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; }
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; }
/* * Helper function for tmpfs_readdir. Returns as much directory entries * as can fit in the uio space. The read starts at uio->uio_offset. * The function returns 0 on success, -1 if there was not enough space * in the uio structure to hold the directory entry or an appropriate * error code if another error happens. * * Caller must hold the node locked (shared ok) */ int tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp) { int error; off_t startcookie; struct tmpfs_dirent *de; TMPFS_VALIDATE_DIR(node); /* * Locate the first directory entry we have to return. We have cached * the last readdir in the node, so use those values if appropriate. * Otherwise do a linear scan to find the requested entry. */ startcookie = uio->uio_offset; KKASSERT(startcookie != TMPFS_DIRCOOKIE_DOT); KKASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT); if (startcookie == TMPFS_DIRCOOKIE_EOF) return 0; de = tmpfs_dir_lookupbycookie(node, startcookie); if (de == NULL) return EINVAL; /* * Read as much entries as possible; i.e., until we reach the end of * the directory or we exhaust uio space. */ do { ino_t d_ino; uint8_t d_type; /* Create a dirent structure representing the current * tmpfs_node and fill it. */ d_ino = de->td_node->tn_id; switch (de->td_node->tn_type) { case VBLK: d_type = DT_BLK; break; case VCHR: d_type = DT_CHR; break; case VDIR: d_type = DT_DIR; break; case VFIFO: d_type = DT_FIFO; break; case VLNK: d_type = DT_LNK; break; case VREG: d_type = DT_REG; break; case VSOCK: d_type = DT_SOCK; break; default: panic("tmpfs_dir_getdents: type %p %d", de->td_node, (int)de->td_node->tn_type); } KKASSERT(de->td_namelen < 256); /* 255 + 1 */ if (vop_write_dirent(&error, uio, d_ino, d_type, de->td_namelen, de->td_name)) { error = -1; break; } (*cntp)++; de = RB_NEXT(tmpfs_dirtree_cookie, node->tn_dir.tn_cookietree, de); } while (error == 0 && uio->uio_resid > 0 && de != NULL); /* Update the offset and cache. */ if (de == NULL) { uio->uio_offset = TMPFS_DIRCOOKIE_EOF; } else { uio->uio_offset = tmpfs_dircookie(de); } return error; }