static int mkentry(struct inodesc *idesc) { LFS_DIRHEADER *dirp = idesc->id_dirp; unsigned namlen; unsigned newreclen, oldreclen; /* figure the length needed for id_name */ namlen = strlen(idesc->id_name); newreclen = LFS_DIRECTSIZ(fs, namlen); /* find the minimum record length for the existing name */ if (lfs_dir_getino(fs, dirp) != 0) oldreclen = LFS_DIRSIZ(fs, dirp); else oldreclen = 0; /* Can we insert here? */ if (lfs_dir_getreclen(fs, dirp) - oldreclen < newreclen) return (KEEPON); /* Divide the record; all but oldreclen goes to the new record */ newreclen = lfs_dir_getreclen(fs, dirp) - oldreclen; lfs_dir_setreclen(fs, dirp, oldreclen); /* advance the pointer to the new record */ dirp = LFS_NEXTDIR(fs, dirp); /* write record; ino to be entered is in id_parent */ lfs_dir_setino(fs, dirp, idesc->id_parent); lfs_dir_setreclen(fs, dirp, newreclen); lfs_dir_settype(fs, dirp, typemap[idesc->id_parent]); lfs_dir_setnamlen(fs, dirp, namlen); lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), idesc->id_name, namlen, newreclen); return (ALTERED | STOP); }
/* * allocate a new directory */ int allocdir(ino_t parent, ino_t request, int mode) { ino_t ino; char *cp; union lfs_dinode *dp; struct ubuf *bp; LFS_DIRHEADER *dirp; struct uvnode *vp; ino = allocino(request, LFS_IFDIR | mode); vp = vget(fs, ino); dp = VTOD(vp); bread(vp, lfs_dino_getdb(fs, dp, 0), lfs_sb_getfsize(fs), 0, &bp); if (bp->b_flags & B_ERROR) { brelse(bp, 0); freeino(ino); return (0); } dirp = (LFS_DIRHEADER *)bp->b_data; /* . */ lfs_dir_setino(fs, dirp, ino); lfs_dir_setreclen(fs, dirp, LFS_DIRECTSIZ(fs, 1)); lfs_dir_settype(fs, dirp, LFS_DT_DIR); lfs_dir_setnamlen(fs, dirp, 1); lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), ".", 1, LFS_DIRECTSIZ(fs, 1)); /* .. */ dirp = LFS_NEXTDIR(fs, dirp); lfs_dir_setino(fs, dirp, parent); lfs_dir_setreclen(fs, dirp, LFS_DIRBLKSIZ - LFS_DIRECTSIZ(fs, 1)); lfs_dir_settype(fs, dirp, LFS_DT_DIR); lfs_dir_setnamlen(fs, dirp, 2); lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), "..", 2, LFS_DIRBLKSIZ - LFS_DIRECTSIZ(fs, 1)); for (cp = &bp->b_data[LFS_DIRBLKSIZ]; cp < &bp->b_data[lfs_sb_getfsize(fs)]; cp += LFS_DIRBLKSIZ) { zerodirblk(cp); } VOP_BWRITE(bp); lfs_dino_setnlink(fs, dp, 2); inodirty(VTOI(vp)); if (ino == ULFS_ROOTINO) { lncntp[ino] = lfs_dino_getnlink(fs, dp); cacheino(dp, ino); return (ino); } if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { freeino(ino); return (0); } cacheino(dp, ino); statemap[ino] = statemap[parent]; if (statemap[ino] == DSTATE) { lncntp[ino] = lfs_dino_getnlink(fs, dp); lncntp[parent]++; } vp = vget(fs, parent); dp = VTOD(vp); lfs_dino_setnlink(fs, dp, lfs_dino_getnlink(fs, dp) + 1); inodirty(VTOI(vp)); return (ino); }
/* * 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; }