static int mkentry(struct inodesc *idesc) { struct ext2fs_direct *dirp = idesc->id_dirp; struct ext2fs_direct newent; int newlen, oldlen; newent.e2d_type = 0; /* XXX gcc */ newent.e2d_namlen = strlen(idesc->id_name); if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) newent.e2d_type = inot2ext2dt(typemap[idesc->id_parent]); newlen = EXT2FS_DIRSIZ(newent.e2d_namlen); if (dirp->e2d_ino != 0) oldlen = EXT2FS_DIRSIZ(dirp->e2d_namlen); else oldlen = 0; if (fs2h16(dirp->e2d_reclen) - oldlen < newlen) return (KEEPON); newent.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - oldlen); dirp->e2d_reclen = h2fs16(oldlen); dirp = (struct ext2fs_direct *)(((char *)dirp) + oldlen); dirp->e2d_ino = h2fs32(idesc->id_parent); /* ino to be entered is in id_parent */ dirp->e2d_reclen = newent.e2d_reclen; dirp->e2d_namlen = newent.e2d_namlen; dirp->e2d_type = newent.e2d_type; memcpy(dirp->e2d_name, idesc->id_name, (size_t)(dirp->e2d_namlen)); return (ALTERED|STOP); }
static int chgino(struct inodesc *idesc) { struct ext2fs_direct *dirp = idesc->id_dirp; u_int16_t namlen = dirp->e2d_namlen; if (strlen(idesc->id_name) != namlen || strncmp(dirp->e2d_name, idesc->id_name, (int)namlen)) return (KEEPON); dirp->e2d_ino = h2fs32(idesc->id_parent); if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) dirp->e2d_type = inot2ext2dt(typemap[idesc->id_parent]); else dirp->e2d_type = 0; return (ALTERED|STOP); }
/* * Rewrite an existing directory entry to point at the inode * supplied. The parameters describing the directory entry are * set up by a call to namei. */ int ext2fs_dirrewrite(struct inode *dp, struct inode *ip, struct componentname *cnp) { struct buf *bp; struct ext2fs_direct *ep; int error; error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, &bp); if (error != 0) return (error); ep->e2d_ino = h2fs32(ip->i_number); if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); } else { ep->e2d_type = 0; } error = VOP_BWRITE(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); }
/* * Rewrite an existing directory entry to point at the inode * supplied. The parameters describing the directory entry are * set up by a call to namei. */ int ext2fs_dirrewrite(struct inode *dp, const struct ufs_lookup_results *ulr, struct inode *ip, struct componentname *cnp) { struct buf *bp; struct ext2fs_direct *ep; struct vnode *vdp = ITOV(dp); int error; error = ext2fs_blkatoff(vdp, (off_t)ulr->ulr_offset, (void *)&ep, &bp); if (error != 0) return (error); ep->e2d_ino = h2fs32(ip->i_number); if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); } else { ep->e2d_type = 0; } error = VOP_BWRITE(bp->b_vp, bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); }
/* * Write a directory entry after a call to namei, using the parameters * that it left in nameidata. The argument ip is the inode which the new * directory entry will refer to. Dvp is a pointer to the directory to * be written, which was left locked by namei. Remaining parameters * (ulr_offset, ulr_count) indicate how the space for the new * entry is to be obtained. */ int ext2fs_direnter(struct inode *ip, struct vnode *dvp, const struct ufs_lookup_results *ulr, struct componentname *cnp) { struct ext2fs_direct *ep, *nep; struct inode *dp; struct buf *bp; struct ext2fs_direct newdir; struct iovec aiov; struct uio auio; u_int dsize; int error, loc, newentrysize, spacefree; char *dirbuf; struct ufsmount *ump = VFSTOUFS(dvp->v_mount); int dirblksiz = ump->um_dirblksiz; dp = VTOI(dvp); newdir.e2d_ino = h2fs32(ip->i_number); newdir.e2d_namlen = cnp->cn_namelen; if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); } else { newdir.e2d_type = 0; } memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1); newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen); if (ulr->ulr_count == 0) { /* * If ulr_count is 0, then namei could find no * space in the directory. Here, ulr_offset will * be on a directory block boundary and we will write the * new entry into a fresh block. */ if (ulr->ulr_offset & (dirblksiz - 1)) panic("ext2fs_direnter: newblk"); auio.uio_offset = ulr->ulr_offset; newdir.e2d_reclen = h2fs16(dirblksiz); auio.uio_resid = newentrysize; aiov.iov_len = newentrysize; aiov.iov_base = (void *)&newdir; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_WRITE; UIO_SETUP_SYSSPACE(&auio); error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); if (dirblksiz > dvp->v_mount->mnt_stat.f_bsize) /* XXX should grow with balloc() */ panic("ext2fs_direnter: frag size"); else if (!error) { error = ext2fs_setsize(dp, roundup(ext2fs_size(dp), dirblksiz)); if (error) return (error); dp->i_flag |= IN_CHANGE; uvm_vnp_setsize(dvp, ext2fs_size(dp)); } return (error); } /* * If ulr_count is non-zero, then namei found space * for the new entry in the range ulr_offset to * ulr_offset + ulr_count in the directory. * To use this space, we may have to compact the entries located * there, by copying them together towards the beginning of the * block, leaving the free space in one usable chunk at the end. */ /* * Get the block containing the space for the new directory entry. */ if ((error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset, &dirbuf, &bp)) != 0) return (error); /* * Find space for the new entry. In the simple case, the entry at * offset base will have the space. If it does not, then namei * arranged that compacting the region ulr_offset to * ulr_offset + ulr_count would yield the * space. */ ep = (struct ext2fs_direct *)dirbuf; dsize = EXT2FS_DIRSIZ(ep->e2d_namlen); spacefree = fs2h16(ep->e2d_reclen) - dsize; for (loc = fs2h16(ep->e2d_reclen); loc < ulr->ulr_count; ) { nep = (struct ext2fs_direct *)(dirbuf + loc); if (ep->e2d_ino) { /* trim the existing slot */ ep->e2d_reclen = h2fs16(dsize); ep = (struct ext2fs_direct *)((char *)ep + dsize); } else { /* overwrite; nothing there; header is ours */ spacefree += dsize; } dsize = EXT2FS_DIRSIZ(nep->e2d_namlen); spacefree += fs2h16(nep->e2d_reclen) - dsize; loc += fs2h16(nep->e2d_reclen); memcpy((void *)ep, (void *)nep, dsize); } /* * Update the pointer fields in the previous entry (if any), * copy in the new entry, and write out the block. */ if (ep->e2d_ino == 0) { #ifdef DIAGNOSTIC if (spacefree + dsize < newentrysize) panic("ext2fs_direnter: compact1"); #endif newdir.e2d_reclen = h2fs16(spacefree + dsize); } else { #ifdef DIAGNOSTIC if (spacefree < newentrysize) { printf("ext2fs_direnter: compact2 %u %u", (u_int)spacefree, (u_int)newentrysize); panic("ext2fs_direnter: compact2"); } #endif newdir.e2d_reclen = h2fs16(spacefree); ep->e2d_reclen = h2fs16(dsize); ep = (struct ext2fs_direct *)((char *)ep + dsize); } memcpy((void *)ep, (void *)&newdir, (u_int)newentrysize); error = VOP_BWRITE(bp->b_vp, bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; if (!error && ulr->ulr_endoff && ulr->ulr_endoff < ext2fs_size(dp)) error = ext2fs_truncate(dvp, (off_t)ulr->ulr_endoff, IO_SYNC, cnp->cn_cred); return (error); }
static int pass2check(struct inodesc *idesc) { struct ext2fs_direct *dirp = idesc->id_dirp; struct inoinfo *inp; int n, entrysize, ret = 0; struct ext2fs_dinode *dp; char *errmsg; struct ext2fs_direct proto; char namebuf[MAXPATHLEN + 1]; char pathbuf[MAXPATHLEN + 1]; /* * check for "." */ if (idesc->id_entryno != 0) goto chk1; if (letoh32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 1 && dirp->e2d_name[0] == '.') { if (letoh32(dirp->e2d_ino) != idesc->id_number) { direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); dirp->e2d_ino = htole32(idesc->id_number); if (reply("FIX") == 1) ret |= ALTERED; } if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) && (dirp->e2d_type != EXT2_FT_DIR)) { direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); dirp->e2d_type = EXT2_FT_DIR; if (reply("FIX") == 1) ret |= ALTERED; } goto chk1; } direrror(idesc->id_number, "MISSING '.'"); proto.e2d_ino = htole32(idesc->id_number); proto.e2d_namlen = 1; if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) proto.e2d_type = EXT2_FT_DIR; else proto.e2d_type = 0; (void)strlcpy(proto.e2d_name, ".", sizeof proto.e2d_name); entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen); if (letoh32(dirp->e2d_ino) != 0 && strcmp(dirp->e2d_name, "..") != 0) { pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", dirp->e2d_name); } else if (letoh16(dirp->e2d_reclen) < entrysize) { pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); } else if (letoh16(dirp->e2d_reclen) < 2 * entrysize) { proto.e2d_reclen = dirp->e2d_reclen; memcpy(dirp, &proto, (size_t)entrysize); if (reply("FIX") == 1) ret |= ALTERED; } else { n = letoh16(dirp->e2d_reclen) - entrysize; proto.e2d_reclen = htole16(entrysize); memcpy(dirp, &proto, (size_t)entrysize); idesc->id_entryno++; lncntp[letoh32(dirp->e2d_ino)]--; dirp = (struct ext2fs_direct *)((char *)(dirp) + entrysize); memset(dirp, 0, (size_t)n); dirp->e2d_reclen = htole16(n); if (reply("FIX") == 1) ret |= ALTERED; } chk1: if (idesc->id_entryno > 1) goto chk2; inp = getinoinfo(idesc->id_number); proto.e2d_ino = htole32(inp->i_parent); proto.e2d_namlen = 2; if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) proto.e2d_type = EXT2_FT_DIR; else proto.e2d_type = 0; (void)strlcpy(proto.e2d_name, "..", sizeof proto.e2d_name); entrysize = EXT2FS_DIRSIZ(2); if (idesc->id_entryno == 0) { n = EXT2FS_DIRSIZ(dirp->e2d_namlen); if (letoh16(dirp->e2d_reclen) < n + entrysize) goto chk2; proto.e2d_reclen = htole16(letoh16(dirp->e2d_reclen) - n); dirp->e2d_reclen = htole16(n); idesc->id_entryno++; lncntp[letoh32(dirp->e2d_ino)]--; dirp = (struct ext2fs_direct *)((char *)(dirp) + n); memset(dirp, 0, (size_t)letoh16(proto.e2d_reclen)); dirp->e2d_reclen = proto.e2d_reclen; } if (letoh32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 2 && strncmp(dirp->e2d_name, "..", 2) == 0) { inp->i_dotdot = letoh32(dirp->e2d_ino); if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) && dirp->e2d_type != EXT2_FT_DIR) { direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); dirp->e2d_type = EXT2_FT_DIR; if (reply("FIX") == 1) ret |= ALTERED; } goto chk2; } if (letoh32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 1 && strncmp(dirp->e2d_name, ".", 1) != 0) { fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", dirp->e2d_name); inp->i_dotdot = (ino_t)-1; } else if (letoh16(dirp->e2d_reclen) < entrysize) { fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); inp->i_dotdot = (ino_t)-1; } else if (inp->i_parent != 0) { /* * We know the parent, so fix now. */ inp->i_dotdot = inp->i_parent; fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); proto.e2d_reclen = dirp->e2d_reclen; memcpy(dirp, &proto, (size_t)entrysize); if (reply("FIX") == 1) ret |= ALTERED; } idesc->id_entryno++; if (letoh32(dirp->e2d_ino) != 0) lncntp[letoh32(dirp->e2d_ino)]--; return (ret|KEEPON); chk2: if (letoh32(dirp->e2d_ino) == 0) return (ret|KEEPON); if (dirp->e2d_namlen <= 2 && dirp->e2d_name[0] == '.' && idesc->id_entryno >= 2) { if (dirp->e2d_namlen == 1) { direrror(idesc->id_number, "EXTRA '.' ENTRY"); dirp->e2d_ino = 0; if (reply("FIX") == 1) ret |= ALTERED; return (KEEPON | ret); } if (dirp->e2d_name[1] == '.') { direrror(idesc->id_number, "EXTRA '..' ENTRY"); dirp->e2d_ino = 0; if (reply("FIX") == 1) ret |= ALTERED; return (KEEPON | ret); } } idesc->id_entryno++; n = 0; if (letoh32(dirp->e2d_ino) > maxino || (letoh32(dirp->e2d_ino) < EXT2_FIRSTINO && letoh32(dirp->e2d_ino) != EXT2_ROOTINO)) { fileerror(idesc->id_number, letoh32(dirp->e2d_ino), "I OUT OF RANGE"); n = reply("REMOVE"); } else { again: switch (statemap[letoh32(dirp->e2d_ino)]) { case USTATE: if (idesc->id_entryno <= 2) break; fileerror(idesc->id_number, letoh32(dirp->e2d_ino), "UNALLOCATED"); n = reply("REMOVE"); break; case DCLEAR: case FCLEAR: if (idesc->id_entryno <= 2) break; if (statemap[letoh32(dirp->e2d_ino)] == FCLEAR) errmsg = "DUP/BAD"; else if (!preen) errmsg = "ZERO LENGTH DIRECTORY"; else { n = 1; break; } fileerror(idesc->id_number, letoh32(dirp->e2d_ino), errmsg); if ((n = reply("REMOVE")) == 1) break; dp = ginode(letoh32(dirp->e2d_ino)); statemap[letoh32(dirp->e2d_ino)] = (letoh16(dp->e2di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; lncntp[letoh32(dirp->e2d_ino)] = letoh16(dp->e2di_nlink); goto again; case DSTATE: case DFOUND: inp = getinoinfo(letoh32(dirp->e2d_ino)); if (inp->i_parent != 0 && idesc->id_entryno > 2) { getpathname(pathbuf, sizeof pathbuf, idesc->id_number, idesc->id_number); getpathname(namebuf, sizeof namebuf, letoh32(dirp->e2d_ino), letoh32(dirp->e2d_ino)); pwarn("%s %s %s\n", pathbuf, "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", namebuf); if (preen) printf(" (IGNORED)\n"); else if ((n = reply("REMOVE")) == 1) break; } if (idesc->id_entryno > 2) inp->i_parent = idesc->id_number; /* fall through */ case FSTATE: if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) && dirp->e2d_type != inot2ext2dt(typemap[letoh32(dirp->e2d_ino)])) { dirp->e2d_type = inot2ext2dt(typemap[letoh32(dirp->e2d_ino)]); fileerror(idesc->id_number, letoh32(dirp->e2d_ino), "BAD TYPE VALUE"); if (reply("FIX") == 1) ret |= ALTERED; } lncntp[letoh32(dirp->e2d_ino)]--; break; default: errexit("BAD STATE %d FOR INODE I=%llu\n", statemap[letoh32(dirp->e2d_ino)], (unsigned long long)letoh32(dirp->e2d_ino)); } } if (n == 0) return (ret|KEEPON); dirp->e2d_ino = 0; return (ret|KEEPON|ALTERED); }