/* ARGSUSED */ static int auto_readdir( vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp, caller_context_t *ct, int flags) { struct autofs_rddirargs rda; autofs_rddirres rd; fnnode_t *fnp = vntofn(vp); fnnode_t *cfnp, *nfnp; dirent64_t *dp; ulong_t offset; ulong_t outcount = 0, count = 0; size_t namelen; ulong_t alloc_count; void *outbuf = NULL; fninfo_t *fnip = vfstofni(vp->v_vfsp); struct iovec *iovp; int error = 0; int reached_max = 0; int myeof = 0; int this_reclen; struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals; AUTOFS_DPRINT((4, "auto_readdir vp=%p offset=%lld\n", (void *)vp, uiop->uio_loffset)); if (eofp != NULL) *eofp = 0; if (uiop->uio_iovcnt != 1) return (EINVAL); iovp = uiop->uio_iov; alloc_count = iovp->iov_len; gethrestime(&fnp->fn_atime); fnp->fn_ref_time = fnp->fn_atime.tv_sec; dp = outbuf = kmem_zalloc(alloc_count, KM_SLEEP); /* * Held when getdents calls VOP_RWLOCK.... */ ASSERT(RW_READ_HELD(&fnp->fn_rwlock)); if (uiop->uio_offset >= AUTOFS_DAEMONCOOKIE) { again: /* * Do readdir of daemon contents only * Drop readers lock and reacquire after reply. */ rw_exit(&fnp->fn_rwlock); bzero(&rd, sizeof (struct autofs_rddirres)); count = 0; rda.rda_map = fnip->fi_map; rda.rda_offset = (uint_t)uiop->uio_offset; rd.rd_rddir.rddir_entries = dp; rda.rda_count = rd.rd_rddir.rddir_size = (uint_t)alloc_count; rda.uid = crgetuid(cred); error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_READDIR, xdr_autofs_rddirargs, &rda, xdr_autofs_rddirres, (void *)&rd, sizeof (autofs_rddirres), TRUE); /* * reacquire previously dropped lock */ rw_enter(&fnp->fn_rwlock, RW_READER); if (!error) { error = rd.rd_status; dp = rd.rd_rddir.rddir_entries; } if (error) { if (error == AUTOFS_SHUTDOWN) { /* * treat as empty directory */ error = 0; myeof = 1; if (eofp) *eofp = 1; } goto done; } if (rd.rd_rddir.rddir_size) { dirent64_t *odp = dp; /* next in output buffer */ dirent64_t *cdp = dp; /* current examined entry */ /* * Check for duplicates here */ do { this_reclen = cdp->d_reclen; if (auto_search(fnp, cdp->d_name, NULL, cred)) { /* * entry not found in kernel list, * include it in readdir output. * * If we are skipping entries. then * we need to copy this entry to the * correct position in the buffer * to be copied out. */ if (cdp != odp) bcopy(cdp, odp, (size_t)this_reclen); odp = nextdp(odp); outcount += this_reclen; } else { /* * Entry was found in the kernel * list. If it is the first entry * in this buffer, then just skip it */ if (odp == dp) { dp = nextdp(dp); odp = dp; } } count += this_reclen; cdp = (struct dirent64 *) ((char *)cdp + this_reclen); } while (count < rd.rd_rddir.rddir_size); if (outcount) error = uiomove(dp, outcount, UIO_READ, uiop); uiop->uio_offset = rd.rd_rddir.rddir_offset; } else { if (rd.rd_rddir.rddir_eof == 0) { /* * alloc_count not large enough for one * directory entry */ error = EINVAL; } } if (rd.rd_rddir.rddir_eof && !error) { myeof = 1; if (eofp) *eofp = 1; } if (!error && !myeof && outcount == 0) { /* * call daemon with new cookie, all previous * elements happened to be duplicates */ dp = outbuf; goto again; } goto done; } if (uiop->uio_offset == 0) { /* * first time: so fudge the . and .. */ this_reclen = DIRENT64_RECLEN(1); if (alloc_count < this_reclen) { error = EINVAL; goto done; } dp->d_ino = (ino64_t)fnp->fn_nodeid; dp->d_off = (off64_t)1; dp->d_reclen = (ushort_t)this_reclen; /* use strncpy(9f) to zero out uninitialized bytes */ (void) strncpy(dp->d_name, ".", DIRENT64_NAMELEN(this_reclen)); outcount += dp->d_reclen; dp = nextdp(dp); this_reclen = DIRENT64_RECLEN(2); if (alloc_count < outcount + this_reclen) { error = EINVAL; goto done; } dp->d_reclen = (ushort_t)this_reclen; dp->d_ino = (ino64_t)fnp->fn_parent->fn_nodeid; dp->d_off = (off64_t)2; /* use strncpy(9f) to zero out uninitialized bytes */ (void) strncpy(dp->d_name, "..", DIRENT64_NAMELEN(this_reclen)); outcount += dp->d_reclen; dp = nextdp(dp); } offset = 2; cfnp = fnp->fn_dirents; while (cfnp != NULL) { nfnp = cfnp->fn_next; offset = cfnp->fn_offset; if ((offset >= uiop->uio_offset) && (!(cfnp->fn_flags & MF_LOOKUP))) { int reclen; /* * include node only if its offset is greater or * equal to the one required and it is not in * transient state (not being looked-up) */ namelen = strlen(cfnp->fn_name); reclen = (int)DIRENT64_RECLEN(namelen); if (outcount + reclen > alloc_count) { reached_max = 1; break; } dp->d_reclen = (ushort_t)reclen; dp->d_ino = (ino64_t)cfnp->fn_nodeid; if (nfnp != NULL) { /* * get the offset of the next element */ dp->d_off = (off64_t)nfnp->fn_offset; } else { /* * This is the last element, make * offset one plus the current */ dp->d_off = (off64_t)cfnp->fn_offset + 1; } /* use strncpy(9f) to zero out uninitialized bytes */ (void) strncpy(dp->d_name, cfnp->fn_name, DIRENT64_NAMELEN(reclen)); outcount += dp->d_reclen; dp = nextdp(dp); } cfnp = nfnp; } if (outcount) error = uiomove(outbuf, outcount, UIO_READ, uiop); if (!error) { if (reached_max) { /* * This entry did not get added to the buffer on this, * call. We need to add it on the next call therefore * set uio_offset to this entry's offset. If there * wasn't enough space for one dirent, return EINVAL. */ uiop->uio_offset = offset; if (outcount == 0) error = EINVAL; } else if (autofs_nobrowse || auto_nobrowse_option(fnip->fi_opts) || (fnip->fi_flags & MF_DIRECT) || (fnp->fn_trigger != NULL) || (((vp->v_flag & VROOT) == 0) && ((fntovn(fnp->fn_parent))->v_flag & VROOT) && (fnp->fn_dirents == NULL))) { /* * done reading directory entries */ uiop->uio_offset = offset + 1; if (eofp) *eofp = 1; } else { /* * Need to get the rest of the entries from the daemon. */ uiop->uio_offset = AUTOFS_DAEMONCOOKIE; } } done: kmem_free(outbuf, alloc_count); AUTOFS_DPRINT((5, "auto_readdir vp=%p offset=%lld eof=%d\n", (void *)vp, uiop->uio_loffset, myeof)); return (error); }
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; }