/* * Vnode op for reading directories. * * Convert the on-disk entries to <sys/dirent.h> entries. * the problem is that the conversion will blow up some entries by four bytes, * so it can't be done in place. This is too bad. Right now the conversion is * done entry by entry, the converted entry is sent via uiomove. * * XXX allocate a buffer, convert as many entries as possible, then send * the whole buffer to uiomove */ int ext2fs_readdir(void *v) { struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; kauth_cred_t a_cred; int **a_eofflag; off_t **a_cookies; int ncookies; } */ *ap = v; struct uio *uio = ap->a_uio; int error; size_t e2fs_count, readcnt; struct vnode *vp = ap->a_vp; struct m_ext2fs *fs = VTOI(vp)->i_e2fs; struct ext2fs_direct *dp; struct dirent *dstd; struct uio auio; struct iovec aiov; void *dirbuf; off_t off = uio->uio_offset; off_t *cookies = NULL; int nc = 0, ncookies = 0; int e2d_reclen; if (vp->v_type != VDIR) return (ENOTDIR); e2fs_count = uio->uio_resid; /* Make sure we don't return partial entries. */ e2fs_count -= (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize -1); if (e2fs_count <= 0) return (EINVAL); auio = *uio; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_len = e2fs_count; auio.uio_resid = e2fs_count; UIO_SETUP_SYSSPACE(&auio); dirbuf = kmem_alloc(e2fs_count, KM_SLEEP); dstd = kmem_zalloc(sizeof(struct dirent), KM_SLEEP); if (ap->a_ncookies) { nc = e2fs_count / _DIRENT_MINSIZE((struct dirent *)0); ncookies = nc; cookies = malloc(sizeof (off_t) * ncookies, M_TEMP, M_WAITOK); *ap->a_cookies = cookies; } aiov.iov_base = dirbuf; error = UFS_BUFRD(ap->a_vp, &auio, 0, ap->a_cred); if (error == 0) { readcnt = e2fs_count - auio.uio_resid; for (dp = (struct ext2fs_direct *)dirbuf; (char *)dp < (char *)dirbuf + readcnt; ) { e2d_reclen = fs2h16(dp->e2d_reclen); if (e2d_reclen == 0) { error = EIO; break; } ext2fs_dirconv2ffs(dp, dstd); if(dstd->d_reclen > uio->uio_resid) { break; } error = uiomove(dstd, dstd->d_reclen, uio); if (error != 0) { break; } off = off + e2d_reclen; if (cookies != NULL) { *cookies++ = off; if (--ncookies <= 0){ break; /* out of cookies */ } } /* advance dp */ dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen); } /* we need to correct uio_offset */ uio->uio_offset = off; } kmem_free(dirbuf, e2fs_count); kmem_free(dstd, sizeof(*dstd)); *ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset; if (ap->a_ncookies) { if (error) { free(*ap->a_cookies, M_TEMP); *ap->a_ncookies = 0; *ap->a_cookies = NULL; } else *ap->a_ncookies = nc - ncookies; } return (error); }
/* * Vnode op for readdir */ int filecore_readdir(void *v) { struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; kauth_cred_t a_cred; int *a_eofflag; off_t **a_cookies; int *a_ncookies; } */ *ap = v; struct uio *uio = ap->a_uio; struct vnode *vdp = ap->a_vp; struct filecore_node *dp; struct filecore_mnt *fcmp; struct buf *bp = NULL; struct dirent *de; struct filecore_direntry *dep = NULL; int error = 0; off_t *cookies = NULL; int ncookies = 0; int i; off_t uiooff; dp = VTOI(vdp); if ((dp->i_dirent.attr & FILECORE_ATTR_DIR) == 0) return (ENOTDIR); if (uio->uio_offset % FILECORE_DIRENT_SIZE != 0) return (EINVAL); i = uio->uio_offset / FILECORE_DIRENT_SIZE; uiooff = uio->uio_offset; *ap->a_eofflag = 0; fcmp = dp->i_mnt; error = filecore_dbread(dp, &bp); if (error) { return error; } if (ap->a_ncookies == NULL) cookies = NULL; else { *ap->a_ncookies = 0; ncookies = uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0); cookies = malloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK); } de = kmem_zalloc(sizeof(struct dirent), KM_SLEEP); for (; ; i++) { switch (i) { case 0: /* Fake the '.' entry */ de->d_fileno = dp->i_number; de->d_type = DT_DIR; de->d_namlen = 1; strlcpy(de->d_name, ".", sizeof(de->d_name)); break; case 1: /* Fake the '..' entry */ de->d_fileno = filecore_getparent(dp); de->d_type = DT_DIR; de->d_namlen = 2; strlcpy(de->d_name, "..", sizeof(de->d_name)); break; default: de->d_fileno = dp->i_dirent.addr + ((i - 2) << FILECORE_INO_INDEX); dep = fcdirentry(bp->b_data, i - 2); if (dep->attr & FILECORE_ATTR_DIR) de->d_type = DT_DIR; else de->d_type = DT_REG; if (filecore_fn2unix(dep->name, de->d_name, /*###346 [cc] warning: passing arg 3 of `filecore_fn2unix' from incompatible pointer type%%%*/ &de->d_namlen)) { *ap->a_eofflag = 1; goto out; } break; } de->d_reclen = _DIRENT_SIZE(de); if (uio->uio_resid < de->d_reclen) goto out; error = uiomove(de, de->d_reclen, uio); if (error) goto out; uiooff += FILECORE_DIRENT_SIZE; if (cookies) { *cookies++ = i*FILECORE_DIRENT_SIZE; (*ap->a_ncookies)++; if (--ncookies == 0) goto out; } } out: if (cookies) { *ap->a_cookies = cookies; if (error) { free(cookies, M_TEMP); *ap->a_ncookies = 0; *ap->a_cookies = NULL; } } uio->uio_offset = uiooff; #ifdef FILECORE_DEBUG_BR printf("brelse(%p) vn3\n", bp); #endif brelse (bp, 0); kmem_free(de, sizeof(*de)); return (error); }
/* * Vnode op for reading directories. * * This routine handles converting from the on-disk directory format * "struct direct" to the in-memory format "struct dirent" as well as * byte swapping the entries if necessary. */ int ufs_readdir(void *v) { struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; kauth_cred_t a_cred; int *a_eofflag; off_t **a_cookies; int *ncookies; } */ *ap = v; struct vnode *vp = ap->a_vp; struct direct *cdp, *ecdp; struct dirent *ndp; char *cdbuf, *ndbuf, *endp; struct uio auio, *uio; struct iovec aiov; int error; size_t count, ccount, rcount, cdbufsz, ndbufsz; off_t off, *ccp; off_t startoff; size_t skipbytes; struct ufsmount *ump = VFSTOUFS(vp->v_mount); int nswap = UFS_MPNEEDSWAP(ump); #if BYTE_ORDER == LITTLE_ENDIAN int needswap = ump->um_maxsymlinklen <= 0 && nswap == 0; #else int needswap = ump->um_maxsymlinklen <= 0 && nswap != 0; #endif uio = ap->a_uio; count = uio->uio_resid; rcount = count - ((uio->uio_offset + count) & (ump->um_dirblksiz - 1)); if (rcount < _DIRENT_MINSIZE(cdp) || count < _DIRENT_MINSIZE(ndp)) return EINVAL; startoff = uio->uio_offset & ~(ump->um_dirblksiz - 1); skipbytes = uio->uio_offset - startoff; rcount += skipbytes; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = startoff; auio.uio_resid = rcount; UIO_SETUP_SYSSPACE(&auio); auio.uio_rw = UIO_READ; cdbufsz = rcount; cdbuf = kmem_alloc(cdbufsz, KM_SLEEP); aiov.iov_base = cdbuf; aiov.iov_len = rcount; error = VOP_READ(vp, &auio, 0, ap->a_cred); if (error != 0) { kmem_free(cdbuf, cdbufsz); return error; } rcount -= auio.uio_resid; cdp = (struct direct *)(void *)cdbuf; ecdp = (struct direct *)(void *)&cdbuf[rcount]; ndbufsz = count; ndbuf = kmem_alloc(ndbufsz, KM_SLEEP); ndp = (struct dirent *)(void *)ndbuf; endp = &ndbuf[count]; off = uio->uio_offset; if (ap->a_cookies) { ccount = rcount / _DIRENT_RECLEN(cdp, 1); ccp = *(ap->a_cookies) = malloc(ccount * sizeof(*ccp), M_TEMP, M_WAITOK); } else { /* XXX: GCC */ ccount = 0; ccp = NULL; } while (cdp < ecdp) { cdp->d_reclen = ufs_rw16(cdp->d_reclen, nswap); if (skipbytes > 0) { if (cdp->d_reclen <= skipbytes) { skipbytes -= cdp->d_reclen; cdp = _DIRENT_NEXT(cdp); continue; } /* * invalid cookie. */ error = EINVAL; goto out; } if (cdp->d_reclen == 0) { struct dirent *ondp = ndp; ndp->d_reclen = _DIRENT_MINSIZE(ndp); ndp = _DIRENT_NEXT(ndp); ondp->d_reclen = 0; cdp = ecdp; break; } if (needswap) { ndp->d_type = cdp->d_namlen; ndp->d_namlen = cdp->d_type; } else { ndp->d_type = cdp->d_type; ndp->d_namlen = cdp->d_namlen; } ndp->d_reclen = _DIRENT_RECLEN(ndp, ndp->d_namlen); if ((char *)(void *)ndp + ndp->d_reclen + _DIRENT_MINSIZE(ndp) > endp) break; ndp->d_fileno = ufs_rw32(cdp->d_ino, nswap); (void)memcpy(ndp->d_name, cdp->d_name, ndp->d_namlen); memset(&ndp->d_name[ndp->d_namlen], 0, ndp->d_reclen - _DIRENT_NAMEOFF(ndp) - ndp->d_namlen); off += cdp->d_reclen; if (ap->a_cookies) { KASSERT(ccp - *(ap->a_cookies) < ccount); *(ccp++) = off; } ndp = _DIRENT_NEXT(ndp); cdp = _DIRENT_NEXT(cdp); } count = ((char *)(void *)ndp - ndbuf); error = uiomove(ndbuf, count, uio); out: if (ap->a_cookies) { if (error) { free(*(ap->a_cookies), M_TEMP); *(ap->a_cookies) = NULL; *(ap->a_ncookies) = 0; } else { *ap->a_ncookies = ccp - *(ap->a_cookies); } } uio->uio_offset = off; kmem_free(ndbuf, ndbufsz); kmem_free(cdbuf, cdbufsz); *ap->a_eofflag = VTOI(vp)->i_size <= uio->uio_offset; return error; }
/* * Vnode op for readdir */ int cd9660_readdir(void *v) { struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; kauth_cred_t a_cred; int *a_eofflag; off_t **a_cookies; int *a_ncookies; } */ *ap = v; struct uio *uio = ap->a_uio; struct isoreaddir *idp; struct vnode *vdp = ap->a_vp; struct iso_node *dp; struct iso_mnt *imp; struct buf *bp = NULL; struct iso_directory_record *ep; int entryoffsetinblock; doff_t endsearch; u_long bmask; int error = 0; int reclen; u_short namelen; off_t *cookies = NULL; int ncookies = 0; if (vdp->v_type != VDIR) return (ENOTDIR); dp = VTOI(vdp); imp = dp->i_mnt; bmask = imp->im_bmask; idp = (struct isoreaddir *)malloc(sizeof(*idp), M_TEMP, M_WAITOK); idp->saveent.d_namlen = idp->assocent.d_namlen = 0; /* * XXX * Is it worth trying to figure out the type? */ idp->saveent.d_type = idp->assocent.d_type = idp->current.d_type = DT_UNKNOWN; idp->uio = uio; if (ap->a_ncookies == NULL) idp->cookies = NULL; else { ncookies = uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0); cookies = malloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK); idp->cookies = cookies; idp->ncookies = ncookies; } idp->eofflag = 1; idp->curroff = uio->uio_offset; if ((entryoffsetinblock = idp->curroff & bmask) && (error = cd9660_blkatoff(vdp, (off_t)idp->curroff, NULL, &bp))) { free(idp, M_TEMP); return (error); } endsearch = dp->i_size; while (idp->curroff < endsearch) { /* * If offset is on a block boundary, * read the next directory block. * Release previous if it exists. */ if ((idp->curroff & bmask) == 0) { if (bp != NULL) brelse(bp, 0); error = cd9660_blkatoff(vdp, (off_t)idp->curroff, NULL, &bp); if (error) break; entryoffsetinblock = 0; } /* * Get pointer to next entry. */ KASSERT(bp != NULL); ep = (struct iso_directory_record *) ((char *)bp->b_data + entryoffsetinblock); reclen = isonum_711(ep->length); if (reclen == 0) { /* skip to next block, if any */ idp->curroff = (idp->curroff & ~bmask) + imp->logical_block_size; continue; } if (reclen < ISO_DIRECTORY_RECORD_SIZE) { error = EINVAL; /* illegal entry, stop */ break; } if (entryoffsetinblock + reclen > imp->logical_block_size) { error = EINVAL; /* illegal directory, so stop looking */ break; } idp->current.d_namlen = isonum_711(ep->name_len); if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) { error = EINVAL; /* illegal entry, stop */ break; } if (isonum_711(ep->flags)&2) idp->current.d_fileno = isodirino(ep, imp); else idp->current.d_fileno = dbtob(bp->b_blkno) + entryoffsetinblock; idp->curroff += reclen; switch (imp->iso_ftype) { case ISO_FTYPE_RRIP: cd9660_rrip_getname(ep, idp->current.d_name, &namelen, &idp->current.d_fileno, imp); idp->current.d_namlen = (u_char)namelen; if (idp->current.d_namlen) error = iso_uiodir(idp, &idp->current, idp->curroff); break; default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 */ isofntrans(ep->name, idp->current.d_namlen, idp->current.d_name, &namelen, imp->iso_ftype == ISO_FTYPE_9660, (imp->im_flags & ISOFSMNT_NOCASETRANS) == 0, isonum_711(ep->flags)&4, imp->im_joliet_level); switch (idp->current.d_name[0]) { case 0: idp->current.d_name[0] = '.'; idp->current.d_namlen = 1; error = iso_uiodir(idp, &idp->current, idp->curroff); break; case 1: strlcpy(idp->current.d_name, "..", sizeof(idp->current.d_name)); idp->current.d_namlen = 2; error = iso_uiodir(idp, &idp->current, idp->curroff); break; default: idp->current.d_namlen = (u_char)namelen; if (imp->iso_ftype == ISO_FTYPE_DEFAULT) error = iso_shipdir(idp); else error = iso_uiodir(idp, &idp->current, idp->curroff); break; } } if (error) break; entryoffsetinblock += reclen; } if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) { idp->current.d_namlen = 0; error = iso_shipdir(idp); } if (error < 0) error = 0; if (ap->a_ncookies != NULL) { if (error) free(cookies, M_TEMP); else { /* * Work out the number of cookies actually used. */ *ap->a_ncookies = ncookies - idp->ncookies; *ap->a_cookies = cookies; } } if (bp) brelse(bp, 0); uio->uio_offset = idp->uio_off; *ap->a_eofflag = idp->eofflag; free(idp, M_TEMP); return (error); }
/* * Vnode op for reading directories. * * This routine handles converting from the on-disk directory format * "struct lfs_direct" to the in-memory format "struct dirent" as well as * byte swapping the entries if necessary. */ int ulfs_readdir(void *v) { struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; kauth_cred_t a_cred; int *a_eofflag; off_t **a_cookies; int *ncookies; } */ *ap = v; struct vnode *vp = ap->a_vp; LFS_DIRHEADER *cdp, *ecdp; struct dirent *ndp; char *cdbuf, *ndbuf, *endp; struct uio auio, *uio; struct iovec aiov; int error; size_t count, ccount, rcount, cdbufsz, ndbufsz; off_t off, *ccp; off_t startoff; size_t skipbytes; struct ulfsmount *ump = VFSTOULFS(vp->v_mount); struct lfs *fs = ump->um_lfs; uio = ap->a_uio; count = uio->uio_resid; rcount = count - ((uio->uio_offset + count) & (fs->um_dirblksiz - 1)); if (rcount < LFS_DIRECTSIZ(fs, 0) || count < _DIRENT_MINSIZE(ndp)) return EINVAL; startoff = uio->uio_offset & ~(fs->um_dirblksiz - 1); skipbytes = uio->uio_offset - startoff; rcount += skipbytes; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = startoff; auio.uio_resid = rcount; UIO_SETUP_SYSSPACE(&auio); auio.uio_rw = UIO_READ; cdbufsz = rcount; cdbuf = kmem_alloc(cdbufsz, KM_SLEEP); aiov.iov_base = cdbuf; aiov.iov_len = rcount; error = VOP_READ(vp, &auio, 0, ap->a_cred); if (error != 0) { kmem_free(cdbuf, cdbufsz); return error; } rcount -= auio.uio_resid; cdp = (LFS_DIRHEADER *)(void *)cdbuf; ecdp = (LFS_DIRHEADER *)(void *)&cdbuf[rcount]; ndbufsz = count; ndbuf = kmem_alloc(ndbufsz, KM_SLEEP); ndp = (struct dirent *)(void *)ndbuf; endp = &ndbuf[count]; off = uio->uio_offset; if (ap->a_cookies) { ccount = rcount / _DIRENT_RECLEN(ndp, 1); ccp = *(ap->a_cookies) = malloc(ccount * sizeof(*ccp), M_TEMP, M_WAITOK); } else { /* XXX: GCC */ ccount = 0; ccp = NULL; } while (cdp < ecdp) { if (skipbytes > 0) { if (lfs_dir_getreclen(fs, cdp) <= skipbytes) { skipbytes -= lfs_dir_getreclen(fs, cdp); cdp = LFS_NEXTDIR(fs, cdp); continue; } /* * invalid cookie. */ error = EINVAL; goto out; } if (lfs_dir_getreclen(fs, cdp) == 0) { struct dirent *ondp = ndp; ndp->d_reclen = _DIRENT_MINSIZE(ndp); ndp = _DIRENT_NEXT(ndp); ondp->d_reclen = 0; cdp = ecdp; break; } ndp->d_type = lfs_dir_gettype(fs, cdp); ndp->d_namlen = lfs_dir_getnamlen(fs, cdp); ndp->d_reclen = _DIRENT_RECLEN(ndp, ndp->d_namlen); if ((char *)(void *)ndp + ndp->d_reclen + _DIRENT_MINSIZE(ndp) > endp) break; ndp->d_fileno = lfs_dir_getino(fs, cdp); (void)memcpy(ndp->d_name, lfs_dir_nameptr(fs, cdp), ndp->d_namlen); memset(&ndp->d_name[ndp->d_namlen], 0, ndp->d_reclen - _DIRENT_NAMEOFF(ndp) - ndp->d_namlen); off += lfs_dir_getreclen(fs, cdp); if (ap->a_cookies) { KASSERT(ccp - *(ap->a_cookies) < ccount); *(ccp++) = off; } ndp = _DIRENT_NEXT(ndp); cdp = LFS_NEXTDIR(fs, cdp); } count = ((char *)(void *)ndp - ndbuf); error = uiomove(ndbuf, count, uio); out: if (ap->a_cookies) { if (error) { free(*(ap->a_cookies), M_TEMP); *(ap->a_cookies) = NULL; *(ap->a_ncookies) = 0; } else { *ap->a_ncookies = ccp - *(ap->a_cookies); } } uio->uio_offset = off; kmem_free(ndbuf, ndbufsz); kmem_free(cdbuf, cdbufsz); *ap->a_eofflag = VTOI(vp)->i_size <= uio->uio_offset; return error; }