/* ARGSUSED */ static int zfsctl_shares_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, caller_context_t *ct, int flags) { zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data; znode_t *dzp; int error; ZFS_ENTER(zfsvfs); if (zfsvfs->z_shares_dir == 0) { ZFS_EXIT(zfsvfs); return (ENOTSUP); } if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) { error = VOP_READDIR(ZTOV(dzp), uiop, cr, eofp, ct, flags); VN_RELE(ZTOV(dzp)); } else { *eofp = 1; error = ENOENT; } ZFS_EXIT(zfsvfs); return (error); }
static void walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *)) { char *nm; int eof, error; struct iovec iov; struct uio uio; struct dirent64 *dp; dirent64_t *dbuf; size_t dbuflen, dlen; ASSERT(dvp); dlen = 4096; dbuf = kmem_zalloc(dlen, KM_SLEEP); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_fmode = 0; uio.uio_extflg = UIO_COPY_CACHED; uio.uio_loffset = 0; uio.uio_llimit = MAXOFFSET_T; eof = 0; error = 0; while (!error && !eof) { uio.uio_resid = dlen; iov.iov_base = (char *)dbuf; iov.iov_len = dlen; (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0); VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); dbuflen = dlen - uio.uio_resid; if (error || dbuflen == 0) break; for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { nm = dp->d_name; if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) continue; if (callback(nm, arg) == WALK_DIR_TERMINATE) goto end; } } end: kmem_free(dbuf, dlen); }
int RUMP_VOP_READDIR(struct vnode *vp, struct uio *uio, struct kauth_cred *cred, int *eofflag, off_t **cookies, int *ncookies) { int error; rump_schedule(); error = VOP_READDIR(vp, uio, cred, eofflag, cookies, ncookies); rump_unschedule(); return error; }
int getdents64(int fd, void *buf, size_t count) { vnode_t *vp; file_t *fp; struct uio auio; struct iovec aiov; register int error; int sink; if (count < sizeof (struct dirent64)) return (set_errno(EINVAL)); /* * Don't let the user overcommit kernel resources. */ if (count > MAXGETDENTS_SIZE) count = MAXGETDENTS_SIZE; if ((fp = getf(fd)) == NULL) return (set_errno(EBADF)); vp = fp->f_vnode; if (vp->v_type != VDIR) { releasef(fd); return (set_errno(ENOTDIR)); } aiov.iov_base = buf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_loffset = fp->f_offset; auio.uio_segflg = UIO_USERSPACE; auio.uio_resid = count; auio.uio_fmode = 0; auio.uio_extflg = UIO_COPY_CACHED; (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0); VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); if (error) { releasef(fd); return (set_errno(error)); } count = count - auio.uio_resid; fp->f_offset = auio.uio_loffset; releasef(fd); return (count); }
/* * smb_vop_readdir() * * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries. * The directory entries are returned in an fs-independent format by the * underlying file system. That is, the "page" of information returned is * not literally stored on-disk in the format returned. * If the file system supports extended directory entries (has features * VFSFT_DIRENTFLAGS), set V_RDDIR_ENTFLAGS to cause the buffer to be * filled with edirent_t structures, instead of dirent64_t structures. * If the file system supports access based enumeration (abe), set * V_RDDIR_ACCFILTER to filter directory entries based on user cred. */ int smb_vop_readdir(vnode_t *vp, uint32_t offset, void *buf, int *count, int *eof, uint32_t rddir_flag, cred_t *cr) { int error = 0; int flags = 0; int rdirent_size; struct uio auio; struct iovec aiov; if (vp->v_type != VDIR) return (ENOTDIR); if (vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS)) { flags |= V_RDDIR_ENTFLAGS; rdirent_size = sizeof (edirent_t); } else { rdirent_size = sizeof (dirent64_t); } if (*count < rdirent_size) return (EINVAL); if (rddir_flag & SMB_ABE) flags |= V_RDDIR_ACCFILTER; aiov.iov_base = buf; aiov.iov_len = *count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_loffset = (uint64_t)offset; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = *count; auio.uio_fmode = 0; (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, flags); VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); if (error == 0) *count = *count - auio.uio_resid; return (error); }
/* reset the file position */ file_p->f_pos = (loff_t)uios.uio_offset; dir_ctx->pos = (loff_t) uios.uio_offset; } return err; } #else /* LINUX_VERSION_CODE > KERNEL_VERSION(3,10,0) */ int vnode_fop_readdir( FILE_T *file_p, void *dirent_p, filldir_t filldir_func ) { uio_t uios; INODE_T *inode; DENT_T *dentry; int err; CALL_DATA_T cd; struct readdir_ctx ctx; dentry = file_p->f_dentry; inode = dentry->d_inode; ASSERT(MDKI_INOISMVFS(inode)); ctx.file = file_p; ctx.done = FALSE; BZERO(&uios, sizeof(uios)); uios.uio_offset = (loff_t)file_p->f_pos; /* This value cannot be larger than value in the view_v4_procinfo table * It is used for maximum size of the return data. */ uios.uio_resid = MVFS_LINUX_MAXRPCDATA; uios.uio_buff = dirent_p; uios.uio_func = filldir_func; mdki_linux_init_call_data(&cd); err = VOP_READDIR(ITOV(inode), &uios, &cd, NULL, &ctx); err = mdki_errno_unix_to_linux(err); mdki_linux_destroy_call_data(&cd); if (!ctx.done) /* reset the file position */ file_p->f_pos = (loff_t)uios.uio_offset; return err; }
static int VMBlockReaddir(struct vnode *vp, // IN: Vnode of directory to read struct uio *uiop, // IN: User's read request struct cred *cr, // IN: Credentials of caller int *eofp // OUT: Indicates we are done #if OS_VFS_VERSION >= 5 , caller_context_t *ctx // IN: Caller's context , int flags // IN: flags #endif ) { VMBlockMountInfo *mip; Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockReaddir: entry\n"); mip = (VMBlockMountInfo *)vp->v_vfsp->vfs_data; return VOP_READDIR(mip->redirectVnode, uiop, cr, eofp #if OS_VFS_VERSION >= 5 , ctx, flags #endif ); }
STATIC int xfs_file_readdir( struct file *filp, void *dirent, filldir_t filldir) { int error = 0; vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode); uio_t uio; iovec_t iov; int eof = 0; caddr_t read_buf; int namelen, size = 0; size_t rlen = PAGE_CACHE_SIZE; xfs_off_t start_offset, curr_offset; xfs_dirent_t *dbp = NULL; /* Try fairly hard to get memory */ do { if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL))) break; rlen >>= 1; } while (rlen >= 1024); if (read_buf == NULL) return -ENOMEM; uio.uio_iov = &iov; uio.uio_segflg = UIO_SYSSPACE; curr_offset = filp->f_pos; if (filp->f_pos != 0x7fffffff) uio.uio_offset = filp->f_pos; else uio.uio_offset = 0xffffffff; while (!eof) { uio.uio_resid = iov.iov_len = rlen; iov.iov_base = read_buf; uio.uio_iovcnt = 1; start_offset = uio.uio_offset; VOP_READDIR(vp, &uio, NULL, &eof, error); if ((uio.uio_offset == start_offset) || error) { size = 0; break; } size = rlen - uio.uio_resid; dbp = (xfs_dirent_t *)read_buf; while (size > 0) { namelen = strlen(dbp->d_name); if (filldir(dirent, dbp->d_name, namelen, (loff_t) curr_offset & 0x7fffffff, (ino_t) dbp->d_ino, DT_UNKNOWN)) { goto done; } size -= dbp->d_reclen; curr_offset = (loff_t)dbp->d_off /* & 0x7fffffff */; dbp = (xfs_dirent_t *)((char *)dbp + dbp->d_reclen); } } done: if (!error) { if (size == 0) filp->f_pos = uio.uio_offset & 0x7fffffff; else if (dbp) filp->f_pos = curr_offset; } kfree(read_buf); return -error; }
/* * Find the entry in the directory corresponding to the target vnode. */ int dirfindvp(vnode_t *vrootp, vnode_t *dvp, vnode_t *tvp, cred_t *cr, char *dbuf, size_t dlen, dirent64_t **rdp) { size_t dbuflen; struct iovec iov; struct uio uio; int error; int eof; vnode_t *cmpvp; struct dirent64 *dp; pathname_t pnp; ASSERT(dvp->v_type == VDIR); /* * This is necessary because of the strange semantics of VOP_LOOKUP(). */ bzero(&pnp, sizeof (pnp)); eof = 0; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_fmode = 0; uio.uio_extflg = UIO_COPY_CACHED; uio.uio_loffset = 0; if ((error = VOP_ACCESS(dvp, VREAD, 0, cr, NULL)) != 0) return (error); while (!eof) { uio.uio_resid = dlen; iov.iov_base = dbuf; iov.iov_len = dlen; (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); error = VOP_READDIR(dvp, &uio, cr, &eof, NULL, 0); VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); dbuflen = dlen - uio.uio_resid; if (error || dbuflen == 0) break; dp = (dirent64_t *)dbuf; while ((intptr_t)dp < (intptr_t)dbuf + dbuflen) { /* * Ignore '.' and '..' entries */ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen); continue; } error = VOP_LOOKUP(dvp, dp->d_name, &cmpvp, &pnp, 0, vrootp, cr, NULL, NULL, NULL); /* * We only want to bail out if there was an error other * than ENOENT. Otherwise, it could be that someone * just removed an entry since the readdir() call, and * the entry we want is further on in the directory. */ if (error == 0) { if (vnode_match(tvp, cmpvp, cr)) { VN_RELE(cmpvp); *rdp = dp; return (0); } VN_RELE(cmpvp); } else if (error != ENOENT) { return (error); } dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen); } } /* * Something strange has happened, this directory does not contain the * specified vnode. This should never happen in the normal case, since * we ensured that dvp is the parent of vp. This is possible in some * rare conditions (races and the special .zfs directory). */ if (error == 0) { error = VOP_LOOKUP(dvp, ".zfs", &cmpvp, &pnp, 0, vrootp, cr, NULL, NULL, NULL); if (error == 0) { if (vnode_match(tvp, cmpvp, cr)) { (void) strcpy(dp->d_name, ".zfs"); dp->d_reclen = strlen(".zfs"); dp->d_off = 2; dp->d_ino = 1; *rdp = dp; } else { error = ENOENT; } VN_RELE(cmpvp); } } return (error); }
static int xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp, caller_context_t *ct, int flags) { vnode_t *pvp; int error; int local_eof; int reset_off = 0; int has_xattrs = 0; if (eofp == NULL) { eofp = &local_eof; } *eofp = 0; /* * See if there is a real extended attribute directory. */ error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct); if (error == 0) { has_xattrs = 1; } /* * Start by reading up the static entries. */ if (uiop->uio_loffset == 0) { ino64_t pino, ino; offset_t off; gfs_dir_t *dp = dvp->v_data; gfs_readdir_state_t gstate; if (has_xattrs) { /* * If there is a real xattr dir, skip . and .. * in the GFS dir. We'll pick them up below * when we call into the underlying fs. */ uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET; } error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino); if (error == 0) { error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1, uiop, pino, ino, flags); } if (error) { return (error); } while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 && !*eofp) { if (off >= 0 && off < dp->gfsd_nstatic) { int eflags; /* * Check to see if this sysattr set name has a * case-insensitive conflict with a real xattr * name. */ eflags = 0; if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) { error = readdir_xattr_casecmp(pvp, dp->gfsd_static[off].gfse_name, cr, ct, &eflags); if (error) break; } ino = dp->gfsd_inode(dvp, off); error = gfs_readdir_emit(&gstate, uiop, off, ino, dp->gfsd_static[off].gfse_name, eflags); if (error) break; } else { *eofp = 1; } } error = gfs_readdir_fini(&gstate, error, eofp, *eofp); if (error) { return (error); } /* * We must read all of the static entries in the first * call. Otherwise we won't know if uio_loffset in a * subsequent call refers to the static entries or to those * in an underlying fs. */ if (*eofp == 0) return (EINVAL); reset_off = 1; } if (!has_xattrs) { *eofp = 1; return (0); } *eofp = 0; if (reset_off) { uiop->uio_loffset = 0; } (void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL); error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags); VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL); return (error); }
int vn_readdir(file_t *fp, char *bf, int segflg, u_int count, int *done, struct lwp *l, off_t **cookies, int *ncookies) { struct vnode *vp = (struct vnode *)fp->f_data; struct iovec aiov; struct uio auio; int error, eofflag; /* Limit the size on any kernel buffers used by VOP_READDIR */ count = min(MAXBSIZE, count); unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = bf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; if (segflg == UIO_SYSSPACE) { UIO_SETUP_SYSSPACE(&auio); } else { KASSERT(l == curlwp); auio.uio_vmspace = l->l_proc->p_vmspace; } auio.uio_resid = count; vn_lock(vp, LK_SHARED | LK_RETRY); auio.uio_offset = fp->f_offset; error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, cookies, ncookies); mutex_enter(&fp->f_lock); fp->f_offset = auio.uio_offset; mutex_exit(&fp->f_lock); VOP_UNLOCK(vp); if (error) return (error); if (count == auio.uio_resid && vn_union_readdir_hook) { struct vnode *ovp = vp; error = (*vn_union_readdir_hook)(&vp, fp, l); if (error) return (error); if (vp != ovp) goto unionread; } if (count == auio.uio_resid && (vp->v_vflag & VV_ROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; vref(vp); mutex_enter(&fp->f_lock); fp->f_data = vp; fp->f_offset = 0; mutex_exit(&fp->f_lock); vrele(tvp); goto unionread; } *done = count - auio.uio_resid; return error; }
/* Find parent vnode of *lvpp, return in *uvpp */ int vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp, char *bufp, struct proc *p) { int eofflag, tries, dirbuflen, len, reclen, error = 0; off_t off; struct uio uio; struct iovec iov; char *dirbuf = NULL; ino_t fileno; struct vattr va; struct vnode *uvp = NULL; struct vnode *lvp = *lvpp; struct componentname cn; tries = 0; /* * If we want the filename, get some info we need while the * current directory is still locked. */ if (bufp != NULL) { error = VOP_GETATTR(lvp, &va, p->p_ucred, p); if (error) { vput(lvp); *lvpp = NULL; *uvpp = NULL; return (error); } } cn.cn_nameiop = LOOKUP; cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; cn.cn_proc = p; cn.cn_cred = p->p_ucred; cn.cn_pnbuf = NULL; cn.cn_nameptr = ".."; cn.cn_namelen = 2; cn.cn_consume = 0; /* Get parent vnode using lookup of '..' */ error = VOP_LOOKUP(lvp, uvpp, &cn); if (error) { vput(lvp); *lvpp = NULL; *uvpp = NULL; return (error); } uvp = *uvpp; /* If we don't care about the pathname, we're done */ if (bufp == NULL) { vrele(lvp); *lvpp = NULL; return (0); } fileno = va.va_fileid; dirbuflen = DIRBLKSIZ; if (dirbuflen < va.va_blocksize) dirbuflen = va.va_blocksize; dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK); off = 0; do { char *cpos; struct dirent *dp; iov.iov_base = dirbuf; iov.iov_len = dirbuflen; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = off; uio.uio_resid = dirbuflen; uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = UIO_READ; uio.uio_procp = p; eofflag = 0; /* Call VOP_READDIR of parent */ error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag); off = uio.uio_offset; /* Try again if NFS tosses its cookies */ if (error == EINVAL && tries < 3) { tries++; off = 0; continue; } else if (error) { goto out; /* Old userland getcwd() behaviour */ } cpos = dirbuf; tries = 0; /* Scan directory page looking for matching vnode */ for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) { dp = (struct dirent *)cpos; reclen = dp->d_reclen; /* Check for malformed directory */ if (reclen < DIRENT_RECSIZE(1)) { error = EINVAL; goto out; } if (dp->d_fileno == fileno) { char *bp = *bpp; bp -= dp->d_namlen; if (bp <= bufp) { error = ERANGE; goto out; } memmove(bp, dp->d_name, dp->d_namlen); error = 0; *bpp = bp; goto out; } cpos += reclen; } } while (!eofflag); error = ENOENT; out: vrele(lvp); *lvpp = NULL; free(dirbuf, M_TEMP); return (error); }
static int unionfs_readdir(void *v) { struct vop_readdir_args *ap = v; int error; int eofflag; int locked; struct unionfs_node *unp; struct unionfs_node_status *unsp; struct uio *uio; struct vnode *uvp; struct vnode *lvp; struct vattr va; int ncookies_bk; off_t *cookies_bk; UNIONFS_INTERNAL_DEBUG("unionfs_readdir: enter\n"); error = 0; eofflag = 0; locked = 0; unp = VTOUNIONFS(ap->a_vp); uio = ap->a_uio; uvp = unp->un_uppervp; lvp = unp->un_lowervp; ncookies_bk = 0; cookies_bk = NULL; if (ap->a_vp->v_type != VDIR) return (ENOTDIR); /* check opaque */ if (uvp != NULLVP && lvp != NULLVP) { if ((error = VOP_GETATTR(uvp, &va, ap->a_cred)) != 0) goto unionfs_readdir_exit; if (va.va_flags & OPAQUE) lvp = NULLVP; } /* check the open count. unionfs needs to open before readdir. */ VOP_UNLOCK(ap->a_vp); vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY); unionfs_get_node_status(unp, &unsp); if ((uvp != NULLVP && unsp->uns_upper_opencnt <= 0) || (lvp != NULLVP && unsp->uns_lower_opencnt <= 0)) { unionfs_tryrem_node_status(unp, unsp); error = EBADF; } if (error != 0) goto unionfs_readdir_exit; /* upper only */ if (uvp != NULLVP && lvp == NULLVP) { error = VOP_READDIR(uvp, uio, ap->a_cred, ap->a_eofflag, ap->a_cookies, ap->a_ncookies); unsp->uns_readdir_status = 0; goto unionfs_readdir_exit; } /* lower only */ if (uvp == NULLVP && lvp != NULLVP) { error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag, ap->a_cookies, ap->a_ncookies); unsp->uns_readdir_status = 2; goto unionfs_readdir_exit; } /* * readdir upper and lower */ KASSERT(uvp != NULLVP); KASSERT(lvp != NULLVP); if (uio->uio_offset == 0) unsp->uns_readdir_status = 0; if (unsp->uns_readdir_status == 0) { /* read upper */ error = VOP_READDIR(uvp, uio, ap->a_cred, &eofflag, ap->a_cookies, ap->a_ncookies); if (error != 0 || eofflag == 0) goto unionfs_readdir_exit; unsp->uns_readdir_status = 1; /* * ufs(and other fs) needs size of uio_resid larger than * DIRBLKSIZ. * size of DIRBLKSIZ equals DEV_BSIZE. * (see: ufs/ufs/ufs_vnops.c ufs_readdir func , ufs/ufs/dir.h) */ if (uio->uio_resid <= (uio->uio_resid & (DEV_BSIZE -1))) goto unionfs_readdir_exit; /* * backup cookies * It prepares to readdir in lower. */ if (ap->a_ncookies != NULL) { ncookies_bk = *(ap->a_ncookies); *(ap->a_ncookies) = 0; } if (ap->a_cookies != NULL) { cookies_bk = *(ap->a_cookies); *(ap->a_cookies) = NULL; } } /* initialize for readdir in lower */ if (unsp->uns_readdir_status == 1) { unsp->uns_readdir_status = 2; uio->uio_offset = 0; } if (lvp == NULLVP) { error = EBADF; goto unionfs_readdir_exit; } /* read lower */ error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag, ap->a_cookies, ap->a_ncookies); if (cookies_bk != NULL) { /* merge cookies */ int size; off_t *newcookies, *pos; size = *(ap->a_ncookies) + ncookies_bk; newcookies = (off_t *) malloc(size * sizeof(off_t), M_TEMP, M_WAITOK); pos = newcookies; memcpy(pos, cookies_bk, ncookies_bk * sizeof(off_t)); pos += ncookies_bk * sizeof(off_t); memcpy(pos, *(ap->a_cookies), *(ap->a_ncookies) * sizeof(off_t)); free(cookies_bk, M_TEMP); free(*(ap->a_cookies), M_TEMP); *(ap->a_ncookies) = size; *(ap->a_cookies) = newcookies; } unionfs_readdir_exit: if (error != 0 && ap->a_eofflag != NULL) *(ap->a_eofflag) = 1; UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error); return (error); }
int readdir_with_callback(struct file *fp, off_t *off, u_long nbytes, int (*appendfunc)(void *, struct dirent *), void *arg) { struct dirent *bdp; caddr_t inp, buf; int buflen; struct uio auio; struct iovec aiov; int eofflag = 0; int error, len, reclen; off_t newoff = *off; struct vnode *vp; struct vattr va; if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) return (EINVAL); if ((error = VOP_GETATTR(vp, &va, fp->f_cred, curproc)) != 0) return (error); buflen = min(MAXBSIZE, nbytes); buflen = max(buflen, va.va_blocksize); buf = malloc(buflen, M_TEMP, M_WAITOK); error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curproc); if (error) { free(buf, M_TEMP, 0); return (error); } again: aiov.iov_base = buf; aiov.iov_len = buflen; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = curproc; auio.uio_resid = buflen; auio.uio_offset = newoff; error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag); *off = auio.uio_offset; if (error) goto out; if ((len = buflen - auio.uio_resid) <= 0) goto eof; inp = buf; for (; len > 0; len -= reclen, inp += reclen) { bdp = (struct dirent *)inp; reclen = bdp->d_reclen; if (len < reclen) break; if (reclen & 3) { error = EFAULT; goto out; } /* Skip holes */ if (bdp->d_fileno != 0) { if ((error = (*appendfunc) (arg, bdp)) != 0) { if (error == ENOMEM) error = 0; break; } } } if (len <= 0 && !eofflag) goto again; eof: out: VOP_UNLOCK(vp, 0, curproc); free(buf, M_TEMP, 0); return (error); }
/* * Native 32-bit system call for non-large-file applications. */ int getdents32(int fd, void *buf, size_t count) { vnode_t *vp; file_t *fp; struct uio auio; struct iovec aiov; register int error; int sink; char *newbuf; char *obuf; int bufsize; int osize, nsize; struct dirent64 *dp; struct dirent32 *op; if (count < sizeof (struct dirent32)) return (set_errno(EINVAL)); if ((fp = getf(fd)) == NULL) return (set_errno(EBADF)); vp = fp->f_vnode; if (vp->v_type != VDIR) { releasef(fd); return (set_errno(ENOTDIR)); } /* * Don't let the user overcommit kernel resources. */ if (count > MAXGETDENTS_SIZE) count = MAXGETDENTS_SIZE; bufsize = count; newbuf = kmem_alloc(bufsize, KM_SLEEP); obuf = kmem_alloc(bufsize, KM_SLEEP); aiov.iov_base = newbuf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_loffset = fp->f_offset; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = count; auio.uio_fmode = 0; auio.uio_extflg = UIO_COPY_CACHED; (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0); VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); if (error) goto out; count = count - auio.uio_resid; fp->f_offset = auio.uio_loffset; dp = (struct dirent64 *)newbuf; op = (struct dirent32 *)obuf; osize = 0; nsize = 0; while (nsize < count) { uint32_t reclen, namlen; /* * This check ensures that the 64 bit d_ino and d_off * fields will fit into their 32 bit equivalents. * * Although d_off is a signed value, the check is done * against the full 32 bits because certain file systems, * NFS for one, allow directory cookies to use the full * 32 bits. We use uint64_t because there is no exact * unsigned analog to the off64_t type of dp->d_off. */ if (dp->d_ino > (ino64_t)UINT32_MAX || dp->d_off > (uint64_t)UINT32_MAX) { error = EOVERFLOW; goto out; } op->d_ino = (ino32_t)dp->d_ino; op->d_off = (off32_t)dp->d_off; namlen = strlen(dp->d_name); reclen = DIRENT32_RECLEN(namlen); op->d_reclen = (uint16_t)reclen; /* use strncpy(9f) to zero out uninitialized bytes */ (void) strncpy(op->d_name, dp->d_name, DIRENT32_NAMELEN(reclen)); nsize += (uint_t)dp->d_reclen; osize += (uint_t)op->d_reclen; dp = (struct dirent64 *)((char *)dp + (uint_t)dp->d_reclen); op = (struct dirent32 *)((char *)op + (uint_t)op->d_reclen); } ASSERT(osize <= count); ASSERT((char *)op <= (char *)obuf + bufsize); ASSERT((char *)dp <= (char *)newbuf + bufsize); if ((error = copyout(obuf, buf, osize)) < 0) error = EFAULT; out: kmem_free(newbuf, bufsize); kmem_free(obuf, bufsize); if (error) { releasef(fd); return (set_errno(error)); } releasef(fd); return (osize); }
/* * Read a block of directory entries in a file system independent format. */ int compat_43_sys_getdirentries(struct lwp *l, const struct compat_43_sys_getdirentries_args *uap, register_t *retval) { /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; char *dirbuf; size_t count = min(MAXBSIZE, (size_t)SCARG(uap, count)); int error, eofflag, readcnt; long loff; /* fd_getvnode() will use the descriptor for us */ if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { error = EBADF; goto out; } vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) { error = EINVAL; goto out; } aiov.iov_base = SCARG(uap, buf); aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_resid = count; KASSERT(l == curlwp); auio.uio_vmspace = curproc->p_vmspace; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); loff = auio.uio_offset = fp->f_offset; # if (BYTE_ORDER != LITTLE_ENDIAN) if ((vp->v_mount->mnt_iflag & IMNT_DTYPE) == 0) { error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, (off_t **)0, (int *)0); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kiov.iov_len = count; dirbuf = malloc(count, M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; UIO_SETUP_SYSSPACE(&kuio); error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, (off_t **)0, (int *)0); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = count - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } free(dirbuf, M_TEMP); } VOP_UNLOCK(vp); if (error) goto out; if ((count == auio.uio_resid) && (vp->v_vflag & VV_ROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; vref(vp); fp->f_data = (void *) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } error = copyout((void *)&loff, (void *)SCARG(uap, basep), sizeof(long)); *retval = count - auio.uio_resid; out: fd_putfile(SCARG(uap, fd)); return (error); }
/* * Read a block of directory entries in a file system independent format. */ int compat_43_sys_getdirentries(struct lwp *l, const struct compat_43_sys_getdirentries_args *uap, register_t *retval) { /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ struct dirent *bdp; struct vnode *vp; char *inp, *tbuf; /* Current-format */ int len, reclen; /* Current-format */ char *outp; /* Dirent12-format */ int resid, old_reclen = 0; /* Dirent12-format */ struct file *fp; struct uio auio; struct iovec aiov; struct dirent43 idb; off_t off; /* true file offset */ int buflen, error, eofflag, nbytes; struct vattr va; off_t *cookiebuf = NULL, *cookie; int ncookies; long loff; /* fd_getvnode() will use the descriptor for us */ if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { error = EBADF; goto out1; } vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) { error = ENOTDIR; goto out1; } vn_lock(vp, LK_SHARED | LK_RETRY); error = VOP_GETATTR(vp, &va, l->l_cred); VOP_UNLOCK(vp); if (error) goto out1; loff = fp->f_offset; nbytes = SCARG(uap, count); buflen = min(MAXBSIZE, nbytes); if (buflen < va.va_blocksize) buflen = va.va_blocksize; tbuf = malloc(buflen, M_TEMP, M_WAITOK); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); off = fp->f_offset; again: aiov.iov_base = tbuf; aiov.iov_len = buflen; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_resid = buflen; auio.uio_offset = off; UIO_SETUP_SYSSPACE(&auio); /* * First we read into the malloc'ed buffer, then * we massage it into user space, one record at a time. */ error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, &ncookies); if (error) goto out; inp = tbuf; outp = SCARG(uap, buf); resid = nbytes; if ((len = buflen - auio.uio_resid) == 0) goto eof; for (cookie = cookiebuf; len > 0; len -= reclen) { bdp = (struct dirent *)inp; reclen = bdp->d_reclen; if (reclen & 3) panic(__func__); if (bdp->d_fileno == 0) { inp += reclen; /* it is a hole; squish it out */ if (cookie) off = *cookie++; else off += reclen; continue; } old_reclen = _DIRENT_RECLEN(&idb, bdp->d_namlen); if (reclen > len || resid < old_reclen) { /* entry too big for buffer, so just stop */ outp++; break; } /* * Massage in place to make a Dirent12-shaped dirent (otherwise * we have to worry about touching user memory outside of * the copyout() call). */ idb.d_fileno = (uint32_t)bdp->d_fileno; idb.d_reclen = (uint16_t)old_reclen; idb.d_namlen = (uint16_t)bdp->d_namlen; strcpy(idb.d_name, bdp->d_name); if ((error = copyout(&idb, outp, old_reclen))) goto out; /* advance past this real entry */ inp += reclen; if (cookie) off = *cookie++; /* each entry points to itself */ else off += reclen; /* advance output past Dirent12-shaped entry */ outp += old_reclen; resid -= old_reclen; } /* if we squished out the whole block, try again */ if (outp == SCARG(uap, buf)) { if (cookiebuf) free(cookiebuf, M_TEMP); cookiebuf = NULL; goto again; } fp->f_offset = off; /* update the vnode offset */ eof: *retval = nbytes - resid; out: VOP_UNLOCK(vp); if (cookiebuf) free(cookiebuf, M_TEMP); free(tbuf, M_TEMP); out1: fd_putfile(SCARG(uap, fd)); if (error) return error; return copyout(&loff, SCARG(uap, basep), sizeof(long)); }
STATIC int linvfs_readdir( struct file *filp, void *dirent, filldir_t filldir) { int error = 0; vnode_t *vp; uio_t uio; iovec_t iov; int eof = 0; cred_t cred; /* Temporary cred workaround */ caddr_t read_buf; int namelen, size = 0; size_t rlen = PAGE_CACHE_SIZE << 2; off_t start_offset; dirent_t *dbp = NULL; vp = LINVFS_GET_VP(filp->f_dentry->d_inode); ASSERT(vp); /* Try fairly hard to get memory */ do { if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL))) break; rlen >>= 1; } while (rlen >= 1024); if (read_buf == NULL) return -ENOMEM; uio.uio_iov = &iov; uio.uio_fmode = filp->f_mode; uio.uio_segflg = UIO_SYSSPACE; uio.uio_offset = filp->f_pos; while (!eof) { uio.uio_resid = iov.iov_len = rlen; iov.iov_base = read_buf; uio.uio_iovcnt = 1; start_offset = uio.uio_offset; VOP_READDIR(vp, &uio, &cred, &eof, error); if ((uio.uio_offset == start_offset) || error) { size = 0; break; } size = rlen - uio.uio_resid; dbp = (dirent_t *)read_buf; while (size > 0) { namelen = strlen(dbp->d_name); if (filldir(dirent, dbp->d_name, namelen, (off_t) dbp->d_off, (linux_ino_t) dbp->d_ino, DT_UNKNOWN)) { goto done; } size -= dbp->d_reclen; dbp = nextdp(dbp); } } done: if (!error) { if (size == 0) filp->f_pos = uio.uio_offset; else if (dbp) filp->f_pos = dbp->d_off; } kfree(read_buf); return -error; }
static int zfsfuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { vnode_t *vp = ((file_info_t *)(uintptr_t) fi->fh)->vp; ASSERT(vp != NULL); ASSERT(VTOZ(vp) != NULL); ASSERT(VTOZ(vp)->z_id == ino); if(vp->v_type != VDIR) return ENOTDIR; vfs_t *vfs = (vfs_t *) fuse_req_userdata(req); zfsvfs_t *zfsvfs = vfs->vfs_data; char *outbuf = kmem_alloc(size, KM_NOSLEEP); if(outbuf == NULL) return ENOMEM; ZFS_ENTER(zfsvfs); cred_t cred; zfsfuse_getcred(req, &cred); union { char buf[DIRENT64_RECLEN(MAXNAMELEN)]; struct dirent64 dirent; } entry; struct stat fstat = { 0 }; iovec_t iovec; uio_t uio; uio.uio_iov = &iovec; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_fmode = 0; uio.uio_llimit = RLIM64_INFINITY; int eofp = 0; int outbuf_off = 0; int outbuf_resid = size; off_t next = off; int error; for(;;) { iovec.iov_base = entry.buf; iovec.iov_len = sizeof(entry.buf); uio.uio_resid = iovec.iov_len; uio.uio_loffset = next; error = VOP_READDIR(vp, &uio, &cred, &eofp, NULL, 0); if(error) goto out; /* No more directory entries */ if(iovec.iov_base == entry.buf) break; fstat.st_ino = entry.dirent.d_ino; fstat.st_mode = 0; int dsize = fuse_dirent_size(strlen(entry.dirent.d_name)); if(dsize > outbuf_resid) break; fuse_add_dirent(outbuf + outbuf_off, entry.dirent.d_name, &fstat, entry.dirent.d_off); outbuf_off += dsize; outbuf_resid -= dsize; next = entry.dirent.d_off; } out: ZFS_EXIT(zfsvfs); if(!error) fuse_reply_buf(req, outbuf, outbuf_off); kmem_free(outbuf, size); return error; }
/* * Linux 'readdir' call. This code is mostly taken from the * SunOS getdents call (see compat/sunos/sunos_misc.c), though * an attempt has been made to keep it a little cleaner. * * The d_off field contains the offset of the next valid entry, * unless the older Linux getdents(2), which used to have it set * to the offset of the entry itself. This function also doesn't * need to deal with the old count == 1 glibc problem. * * Read in BSD-style entries, convert them, and copy them out. * * Note that this doesn't handle union-mounted filesystems. */ int linux_sys_getdents64(struct lwp *l, const struct linux_sys_getdents64_args *uap, register_t *retval) { /* { syscallarg(int) fd; syscallarg(struct linux_dirent64 *) dent; syscallarg(unsigned int) count; } */ struct dirent *bdp; struct vnode *vp; char *inp, *tbuf; /* BSD-format */ int len, reclen; /* BSD-format */ char *outp; /* Linux-format */ int resid, linux_reclen = 0; /* Linux-format */ file_t *fp; struct uio auio; struct iovec aiov; struct linux_dirent64 idb; off_t off; /* true file offset */ int buflen, error, eofflag, nbytes; struct vattr va; off_t *cookiebuf = NULL, *cookie; int ncookies; /* fd_getvnode() will use the descriptor for us */ if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { error = EBADF; goto out1; } vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) { error = EINVAL; goto out1; } if ((error = VOP_GETATTR(vp, &va, l->l_cred))) goto out1; nbytes = SCARG(uap, count); buflen = min(MAXBSIZE, nbytes); if (buflen < va.va_blocksize) buflen = va.va_blocksize; tbuf = malloc(buflen, M_TEMP, M_WAITOK); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); off = fp->f_offset; again: aiov.iov_base = tbuf; aiov.iov_len = buflen; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_resid = buflen; auio.uio_offset = off; UIO_SETUP_SYSSPACE(&auio); /* * First we read into the malloc'ed buffer, then * we massage it into user space, one record at a time. */ error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, &ncookies); if (error) goto out; inp = tbuf; outp = (void *)SCARG(uap, dent); resid = nbytes; if ((len = buflen - auio.uio_resid) == 0) goto eof; for (cookie = cookiebuf; len > 0; len -= reclen) { bdp = (struct dirent *)inp; reclen = bdp->d_reclen; if (reclen & 3) panic("linux_readdir"); if (bdp->d_fileno == 0) { inp += reclen; /* it is a hole; squish it out */ if (cookie) off = *cookie++; else off += reclen; continue; } linux_reclen = LINUX_RECLEN(&idb, bdp->d_namlen); if (reclen > len || resid < linux_reclen) { /* entry too big for buffer, so just stop */ outp++; break; } if (cookie) off = *cookie++; /* each entry points to next */ else off += reclen; /* * Massage in place to make a Linux-shaped dirent (otherwise * we have to worry about touching user memory outside of * the copyout() call). */ idb.d_ino = bdp->d_fileno; idb.d_type = bdp->d_type; idb.d_off = off; idb.d_reclen = (u_short)linux_reclen; strcpy(idb.d_name, bdp->d_name); if ((error = copyout((void *)&idb, outp, linux_reclen))) goto out; /* advance past this real entry */ inp += reclen; /* advance output past Linux-shaped entry */ outp += linux_reclen; resid -= linux_reclen; } /* if we squished out the whole block, try again */ if (outp == (void *)SCARG(uap, dent)) { if (cookiebuf) free(cookiebuf, M_TEMP); cookiebuf = NULL; goto again; } fp->f_offset = off; /* update the vnode offset */ eof: *retval = nbytes - resid; out: VOP_UNLOCK(vp, 0); if (cookiebuf) free(cookiebuf, M_TEMP); free(tbuf, M_TEMP); out1: fd_putfile(SCARG(uap, fd)); return error; }