static int xmem_readdir(struct vnode *vp, struct uio *uiop, struct cred *cred, int *eofp) { struct xmemnode *xp = (struct xmemnode *)VTOXN(vp); struct xdirent *xdp; int error; register struct dirent64 *dp; register ulong_t offset; register ulong_t total_bytes_wanted; register long outcount = 0; register long bufsize; int reclen; caddr_t outbuf; if (uiop->uio_loffset >= MAXOFF_T) { if (eofp) *eofp = 1; return (0); } /* * assuming system call has already called xmem_rwlock */ ASSERT(RW_READ_HELD(&xp->xn_rwlock)); if (uiop->uio_iovcnt != 1) return (EINVAL); if (vp->v_type != VDIR) return (ENOTDIR); /* * There's a window here where someone could have removed * all the entries in the directory after we put a hold on the * vnode but before we grabbed the rwlock. Just return unless * there are still references to the current file in which case panic. */ if (xp->xn_dir == NULL) { if (xp->xn_nlink) cmn_err(CE_PANIC, "empty directory 0x%p", (void *)xp); return (0); } /* * Get space for multiple directory entries */ total_bytes_wanted = uiop->uio_iov->iov_len; bufsize = total_bytes_wanted + sizeof (struct dirent64); outbuf = kmem_alloc(bufsize, KM_SLEEP); dp = (struct dirent64 *)outbuf; offset = 0; xdp = xp->xn_dir; while (xdp) { offset = xdp->xd_offset; if (offset >= uiop->uio_offset) { reclen = (int)DIRENT64_RECLEN(strlen(xdp->xd_name)); if (outcount + reclen > total_bytes_wanted) break; ASSERT(xdp->xd_xmemnode != NULL); /* use strncpy(9f) to zero out uninitialized bytes */ ASSERT(strlen(xdp->xd_name) + 1 <= DIRENT64_NAMELEN(reclen)); (void) strncpy(dp->d_name, xdp->xd_name, DIRENT64_NAMELEN(reclen)); dp->d_reclen = (ushort_t)reclen; dp->d_ino = (ino64_t)xdp->xd_xmemnode->xn_nodeid; dp->d_off = (offset_t)xdp->xd_offset + 1; dp = (struct dirent64 *) ((uintptr_t)dp + dp->d_reclen); outcount += reclen; ASSERT(outcount <= bufsize); } xdp = xdp->xd_next; } error = uiomove(outbuf, outcount, UIO_READ, uiop); if (!error) { /* If we reached the end of the list our offset */ /* should now be just past the end. */ if (!xdp) { offset += 1; if (eofp) *eofp = 1; } else if (eofp) *eofp = 0; uiop->uio_offset = offset; } gethrestime(&xp->xn_atime); kmem_free(outbuf, bufsize); return (error); }
/*ARGSUSED*/ static int bootfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp, caller_context_t *ct, int flags) { bootfs_node_t *bnp = (bootfs_node_t *)vp->v_data; dirent64_t *dp; void *buf; ulong_t bsize, brem; offset_t coff, roff; int dlen, ret; bootfs_node_t *dnp; boolean_t first = B_TRUE; if (uiop->uio_loffset >= MAXOFF_T) { if (eofp != NULL) *eofp = 1; return (0); } if (uiop->uio_iovcnt != 1) return (EINVAL); if (!(uiop->uio_iov->iov_len > 0)) return (EINVAL); if (vp->v_type != VDIR) return (ENOTDIR); roff = uiop->uio_loffset; coff = 0; brem = bsize = uiop->uio_iov->iov_len; buf = kmem_alloc(bsize, KM_SLEEP); dp = buf; /* * Recall that offsets here are done based on the name of the dirent * excluding the null terminator. Therefore `.` is always at 0, `..` is * always at 1, and then the first real dirent is at 3. This offset is * what's actually stored when we update the offset in the structure. */ if (roff == 0) { dlen = DIRENT64_RECLEN(1); if (first == B_TRUE) { if (dlen > brem) { kmem_free(buf, bsize); return (EINVAL); } first = B_FALSE; } dp->d_ino = (ino64_t)bnp->bvn_attr.va_nodeid; dp->d_off = 0; dp->d_reclen = (ushort_t)dlen; (void) strncpy(dp->d_name, ".", DIRENT64_NAMELEN(dlen)); dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen); brem -= dlen; } if (roff <= 1) { dlen = DIRENT64_RECLEN(2); if (first == B_TRUE) { if (dlen > brem) { kmem_free(buf, bsize); return (EINVAL); } first = B_FALSE; } dp->d_ino = (ino64_t)bnp->bvn_parent->bvn_attr.va_nodeid; dp->d_off = 1; dp->d_reclen = (ushort_t)dlen; (void) strncpy(dp->d_name, "..", DIRENT64_NAMELEN(dlen)); dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen); brem -= dlen; } coff = 3; for (dnp = avl_first(&bnp->bvn_dir); dnp != NULL; dnp = AVL_NEXT(&bnp->bvn_dir, dnp)) { size_t nlen = strlen(dnp->bvn_name); if (roff > coff) { coff += nlen; continue; } dlen = DIRENT64_RECLEN(nlen); if (dlen > brem) { if (first == B_TRUE) { kmem_free(buf, bsize); return (EINVAL); } break; } first = B_FALSE; dp->d_ino = (ino64_t)dnp->bvn_attr.va_nodeid; dp->d_off = coff; dp->d_reclen = (ushort_t)dlen; (void) strncpy(dp->d_name, dnp->bvn_name, DIRENT64_NAMELEN(dlen)); dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen); brem -= dlen; coff += nlen; } ret = uiomove(buf, (bsize - brem), UIO_READ, uiop); if (ret == 0) { if (dnp == NULL) { coff++; if (eofp != NULL) *eofp = 1; } else if (eofp != NULL) { *eofp = 0; } uiop->uio_loffset = coff; } gethrestime(&bnp->bvn_attr.va_atime); kmem_free(buf, bsize); return (ret); }
/* 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); }
/* * If DV_BUILD is set, we call into nexus driver to do a BUS_CONFIG_ALL. * Otherwise, simply return cached dv_node's. Hotplug code always call * devfs_clean() to invalid the dv_node cache. */ static int devfs_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp) { struct dv_node *ddv, *dv; struct dirent64 *de, *bufp; offset_t diroff; offset_t soff; size_t reclen, movesz; int error; struct vattr va; size_t bufsz; ddv = VTODV(dvp); dcmn_err2(("devfs_readdir %s: offset %lld len %ld\n", ddv->dv_name, uiop->uio_loffset, uiop->uio_iov->iov_len)); ASSERT(ddv->dv_attr || ddv->dv_attrvp); ASSERT(RW_READ_HELD(&ddv->dv_contents)); if (uiop->uio_loffset >= MAXOFF_T) { if (eofp) *eofp = 1; return (0); } if (uiop->uio_iovcnt != 1) return (EINVAL); if (dvp->v_type != VDIR) return (ENOTDIR); /* Load the initial contents */ if (ddv->dv_flags & DV_BUILD) { if (!rw_tryupgrade(&ddv->dv_contents)) { rw_exit(&ddv->dv_contents); rw_enter(&ddv->dv_contents, RW_WRITER); } /* recheck and fill */ if (ddv->dv_flags & DV_BUILD) dv_filldir(ddv); rw_downgrade(&ddv->dv_contents); } soff = uiop->uio_offset; bufsz = uiop->uio_iov->iov_len; de = bufp = kmem_alloc(bufsz, KM_SLEEP); movesz = 0; dv = (struct dv_node *)-1; /* * Move as many entries into the uio structure as it will take. * Special case "." and "..". */ diroff = 0; if (soff == 0) { /* . */ reclen = DIRENT64_RECLEN(strlen(".")); if ((movesz + reclen) > bufsz) goto full; de->d_ino = (ino64_t)ddv->dv_ino; de->d_off = (off64_t)diroff + 1; de->d_reclen = (ushort_t)reclen; /* use strncpy(9f) to zero out uninitialized bytes */ (void) strncpy(de->d_name, ".", DIRENT64_NAMELEN(reclen)); movesz += reclen; de = (dirent64_t *)((char *)de + reclen); dcmn_err3(("devfs_readdir: A: diroff %lld, soff %lld: '%s' " "reclen %lu\n", diroff, soff, ".", reclen)); } diroff++; if (soff <= 1) { /* .. */ reclen = DIRENT64_RECLEN(strlen("..")); if ((movesz + reclen) > bufsz) goto full; de->d_ino = (ino64_t)ddv->dv_dotdot->dv_ino; de->d_off = (off64_t)diroff + 1; de->d_reclen = (ushort_t)reclen; /* use strncpy(9f) to zero out uninitialized bytes */ (void) strncpy(de->d_name, "..", DIRENT64_NAMELEN(reclen)); movesz += reclen; de = (dirent64_t *)((char *)de + reclen); dcmn_err3(("devfs_readdir: B: diroff %lld, soff %lld: '%s' " "reclen %lu\n", diroff, soff, "..", reclen)); } diroff++; for (dv = ddv->dv_dot; dv; dv = dv->dv_next, diroff++) { /* * although DDM_INTERNAL_PATH minor nodes are skipped for * readdirs outside the kernel, they still occupy directory * offsets */ if (diroff < soff || ((dv->dv_flags & DV_INTERNAL) && (cred != kcred))) continue; reclen = DIRENT64_RECLEN(strlen(dv->dv_name)); if ((movesz + reclen) > bufsz) { dcmn_err3(("devfs_readdir: C: diroff " "%lld, soff %lld: '%s' reclen %lu\n", diroff, soff, dv->dv_name, reclen)); goto full; } de->d_ino = (ino64_t)dv->dv_ino; de->d_off = (off64_t)diroff + 1; de->d_reclen = (ushort_t)reclen; /* use strncpy(9f) to zero out uninitialized bytes */ ASSERT(strlen(dv->dv_name) + 1 <= DIRENT64_NAMELEN(reclen)); (void) strncpy(de->d_name, dv->dv_name, DIRENT64_NAMELEN(reclen)); movesz += reclen; de = (dirent64_t *)((char *)de + reclen); dcmn_err4(("devfs_readdir: D: diroff " "%lld, soff %lld: '%s' reclen %lu\n", diroff, soff, dv->dv_name, reclen)); } /* the buffer is full, or we exhausted everything */ full: dcmn_err3(("devfs_readdir: moving %lu bytes: " "diroff %lld, soff %lld, dv %p\n", movesz, diroff, soff, (void *)dv)); if ((movesz == 0) && dv) error = EINVAL; /* cannot be represented */ else { error = uiomove(bufp, movesz, UIO_READ, uiop); if (error == 0) { if (eofp) *eofp = dv ? 0 : 1; uiop->uio_offset = diroff; } va.va_mask = AT_ATIME; gethrestime(&va.va_atime); rw_exit(&ddv->dv_contents); (void) devfs_setattr(dvp, &va, 0, cred, NULL); rw_enter(&ddv->dv_contents, RW_READER); } kmem_free(bufp, bufsz); return (error); }