static int tmpfs_readdir(struct vop_readdir_args *v) { struct vnode *vp = v->a_vp; struct uio *uio = v->a_uio; int *eofflag = v->a_eofflag; off_t **cookies = v->a_cookies; int *ncookies = v->a_ncookies; struct tmpfs_mount *tmp; int error; off_t startoff; off_t cnt = 0; struct tmpfs_node *node; /* This operation only makes sense on directory nodes. */ if (vp->v_type != VDIR) { return ENOTDIR; } tmp = VFS_TO_TMPFS(vp->v_mount); node = VP_TO_TMPFS_DIR(vp); startoff = uio->uio_offset; if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) { error = tmpfs_dir_getdotdent(node, uio); if (error != 0) { TMPFS_NODE_LOCK_SH(node); goto outok; } cnt++; } if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) { /* may lock parent, cannot hold node lock */ error = tmpfs_dir_getdotdotdent(tmp, node, uio); if (error != 0) { TMPFS_NODE_LOCK_SH(node); goto outok; } cnt++; } TMPFS_NODE_LOCK_SH(node); error = tmpfs_dir_getdents(node, uio, &cnt); outok: KKASSERT(error >= -1); if (error == -1) error = 0; if (eofflag != NULL) *eofflag = (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF); /* Update NFS-related variables. */ if (error == 0 && cookies != NULL && ncookies != NULL) { off_t i; off_t off = startoff; struct tmpfs_dirent *de = NULL; *ncookies = cnt; *cookies = kmalloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK); for (i = 0; i < cnt; i++) { KKASSERT(off != TMPFS_DIRCOOKIE_EOF); if (off == TMPFS_DIRCOOKIE_DOT) { off = TMPFS_DIRCOOKIE_DOTDOT; } else { if (off == TMPFS_DIRCOOKIE_DOTDOT) { de = RB_MIN(tmpfs_dirtree_cookie, &node->tn_dir.tn_cookietree); } else if (de != NULL) { de = RB_NEXT(tmpfs_dirtree_cookie, &node->tn_dir.tn_cookietree, de); } else { de = tmpfs_dir_lookupbycookie(node, off); KKASSERT(de != NULL); de = RB_NEXT(tmpfs_dirtree_cookie, &node->tn_dir.tn_cookietree, de); } if (de == NULL) off = TMPFS_DIRCOOKIE_EOF; else off = tmpfs_dircookie(de); } (*cookies)[i] = off; } KKASSERT(uio->uio_offset == off); } TMPFS_NODE_UNLOCK(node); if ((node->tn_status & TMPFS_NODE_ACCESSED) == 0) { TMPFS_NODE_LOCK(node); node->tn_status |= TMPFS_NODE_ACCESSED; TMPFS_NODE_UNLOCK(node); } return error; }
static int tmpfs_readdir(struct vop_readdir_args *v) { struct vnode *vp = v->a_vp; struct uio *uio = v->a_uio; int *eofflag = v->a_eofflag; u_long **cookies = v->a_cookies; int *ncookies = v->a_ncookies; int error; off_t startoff; off_t cnt = 0; struct tmpfs_node *node; /* This operation only makes sense on directory nodes. */ if (vp->v_type != VDIR) return ENOTDIR; node = VP_TO_TMPFS_DIR(vp); startoff = uio->uio_offset; if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) { error = tmpfs_dir_getdotdent(node, uio); if (error != 0) goto outok; cnt++; } if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) { error = tmpfs_dir_getdotdotdent(node, uio); if (error != 0) goto outok; cnt++; } error = tmpfs_dir_getdents(node, uio, &cnt); outok: MPASS(error >= -1); if (error == -1) error = (cnt != 0) ? 0 : EINVAL; if (eofflag != NULL) *eofflag = (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF); /* Update NFS-related variables. */ if (error == 0 && cookies != NULL && ncookies != NULL) { off_t i; off_t off = startoff; struct tmpfs_dirent *de = NULL; *ncookies = cnt; *cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK); for (i = 0; i < cnt; i++) { MPASS(off != TMPFS_DIRCOOKIE_EOF); if (off == TMPFS_DIRCOOKIE_DOT) { off = TMPFS_DIRCOOKIE_DOTDOT; } else { if (off == TMPFS_DIRCOOKIE_DOTDOT) { de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); } else if (de != NULL) { de = TAILQ_NEXT(de, td_entries); } else { de = tmpfs_dir_lookupbycookie(node, off); MPASS(de != NULL); de = TAILQ_NEXT(de, td_entries); } if (de == NULL) off = TMPFS_DIRCOOKIE_EOF; else off = tmpfs_dircookie(de); } (*cookies)[i] = off; } MPASS(uio->uio_offset == off); } return error; }