/* * Initialize a completely empty directory block. * (block size is LFS_DIRBLKSIZ) */ static void zerodirblk(void *buf) { LFS_DIRHEADER *dirp; dirp = buf; lfs_dir_setino(fs, dirp, 0); lfs_dir_setreclen(fs, dirp, LFS_DIRBLKSIZ); lfs_dir_settype(fs, dirp, LFS_DT_UNKNOWN); lfs_dir_setnamlen(fs, dirp, 0); lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), "", 0, LFS_DIRBLKSIZ); }
static int chgino(struct inodesc *idesc) { LFS_DIRHEADER *dirp = idesc->id_dirp; int namlen; namlen = lfs_dir_getnamlen(fs, dirp); if (memcmp(lfs_dir_nameptr(fs, dirp), idesc->id_name, namlen + 1)) return (KEEPON); lfs_dir_setino(fs, dirp, idesc->id_parent); lfs_dir_settype(fs, dirp, typemap[idesc->id_parent]); return (ALTERED | STOP); }
/* * Verify that a directory entry is valid. * This is a superset of the checks made in the kernel. */ int dircheck(struct inodesc *idesc, LFS_DIRHEADER *dp) { int size; const char *cp; u_char namlen, type; int spaceleft; spaceleft = LFS_DIRBLKSIZ - (idesc->id_loc % LFS_DIRBLKSIZ); if (lfs_dir_getino(fs, dp) >= maxino || lfs_dir_getreclen(fs, dp) == 0 || lfs_dir_getreclen(fs, dp) > spaceleft || (lfs_dir_getreclen(fs, dp) & 0x3) != 0) { pwarn("ino too large, reclen=0, reclen>space, or reclen&3!=0\n"); pwarn("dp->d_ino = 0x%jx\tdp->d_reclen = 0x%x\n", (uintmax_t)lfs_dir_getino(fs, dp), lfs_dir_getreclen(fs, dp)); pwarn("maxino = %ju\tspaceleft = 0x%x\n", (uintmax_t)maxino, spaceleft); return (0); } if (lfs_dir_getino(fs, dp) == 0) return (1); size = LFS_DIRSIZ(fs, dp); namlen = lfs_dir_getnamlen(fs, dp); type = lfs_dir_gettype(fs, dp); if (lfs_dir_getreclen(fs, dp) < size || idesc->id_filesize < size || /* namlen > MAXNAMLEN || */ type > 15) { printf("reclen<size, filesize<size, namlen too large, or type>15\n"); return (0); } cp = lfs_dir_nameptr(fs, dp); for (size = 0; size < namlen; size++) if (*cp == '\0' || (*cp++ == '/')) { printf("name contains NUL or /\n"); return (0); } if (*cp != '\0') { printf("name size misstated\n"); return (0); } return (1); }
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); }
/* * get next entry in a directory. */ static LFS_DIRHEADER * fsck_readdir(struct uvnode *vp, struct inodesc *idesc) { LFS_DIRHEADER *dp, *ndp; struct ubuf *bp; long size, blksiz, fix, dploc; blksiz = idesc->id_numfrags * lfs_sb_getfsize(fs); bread(vp, idesc->id_lblkno, blksiz, 0, &bp); if (idesc->id_loc % LFS_DIRBLKSIZ == 0 && idesc->id_filesize > 0 && idesc->id_loc < blksiz) { dp = (LFS_DIRHEADER *) (bp->b_data + idesc->id_loc); if (dircheck(idesc, dp)) goto dpok; brelse(bp, 0); if (idesc->id_fix == IGNORE) return (0); fix = dofix(idesc, "DIRECTORY CORRUPTED"); bread(vp, idesc->id_lblkno, blksiz, 0, &bp); dp = (LFS_DIRHEADER *) (bp->b_data + idesc->id_loc); lfs_dir_setino(fs, dp, 0); lfs_dir_settype(fs, dp, LFS_DT_UNKNOWN); lfs_dir_setnamlen(fs, dp, 0); lfs_dir_setreclen(fs, dp, LFS_DIRBLKSIZ); /* for now at least, don't zero the old contents */ /*lfs_copydirname(fs, lfs_dir_nameptr(fs, dp), "", 0, LFS_DIRBLKSIZ);*/ lfs_dir_nameptr(fs, dp)[0] = '\0'; if (fix) VOP_BWRITE(bp); else brelse(bp, 0); idesc->id_loc += LFS_DIRBLKSIZ; idesc->id_filesize -= LFS_DIRBLKSIZ; return (dp); } dpok: if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) { brelse(bp, 0); return NULL; } dploc = idesc->id_loc; dp = (LFS_DIRHEADER *) (bp->b_data + dploc); idesc->id_loc += lfs_dir_getreclen(fs, dp); idesc->id_filesize -= lfs_dir_getreclen(fs, dp); if ((idesc->id_loc % LFS_DIRBLKSIZ) == 0) { brelse(bp, 0); return dp; } ndp = (LFS_DIRHEADER *) (bp->b_data + idesc->id_loc); if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && dircheck(idesc, ndp) == 0) { brelse(bp, 0); size = LFS_DIRBLKSIZ - (idesc->id_loc % LFS_DIRBLKSIZ); idesc->id_loc += size; idesc->id_filesize -= size; if (idesc->id_fix == IGNORE) return 0; fix = dofix(idesc, "DIRECTORY CORRUPTED"); bread(vp, idesc->id_lblkno, blksiz, 0, &bp); dp = (LFS_DIRHEADER *) (bp->b_data + dploc); lfs_dir_setreclen(fs, dp, lfs_dir_getreclen(fs, dp) + size); if (fix) VOP_BWRITE(bp); else brelse(bp, 0); } else brelse(bp, 0); return (dp); }
/* * 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; }