diff options
Diffstat (limited to 'ufs-fsck')
-rw-r--r-- | ufs-fsck/dir.c | 226 |
1 files changed, 204 insertions, 22 deletions
diff --git a/ufs-fsck/dir.c b/ufs-fsck/dir.c index 150719ca..565c0d3f 100644 --- a/ufs-fsck/dir.c +++ b/ufs-fsck/dir.c @@ -18,16 +18,18 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "fsck.h" /* This routine is used in pass 1 to initialize DIRARRAY and DIRSORTED. Copy information from DP (for number NUMBER) into a newly allocated dirinfo structure and add it to the arrays. */ +void record_directory (struct dinode *dp, ino_t number) { u_int blks; struct dirinfo *dnp; - blks = howmany (dp->di_size, sblock.fs_bsize); + blks = howmany (dp->di_size, sblock->fs_bsize); if (blks > NDADDR) blks = NDADDR * NIADDR; blks *= sizeof (daddr_t); @@ -35,7 +37,7 @@ record_directory (struct dinode *dp, ino_t number) dnp->i_number = number; dnp->i_parent = dnp->i_dotdot = 0; - dnp->i_isize = dnp->di_size; + dnp->i_isize = dnp->i_isize; dnp->i_numblks = blks * sizeof (daddr_t); bcopy (dp->di_db, dnp->i_blks, blks); @@ -81,9 +83,12 @@ validdir (ino_t dir, char *action) pfatal ("CANNOT %s I=%d; BAD BLOCKS\n", action, dir); return 0; - case FILE: + case REG: pfatal ("CANNOT %s I=%d; NOT DIRECTORY\n", action, dir); return 0; + + default: + errexit ("ILLEGAL STATE"); } } @@ -126,12 +131,12 @@ searchdir (ino_t dir, char *name, ino_t *ino) int checkdirblock (daddr_t bno, int nfrags) { - void *buf = alloca (nfrags * sblock.fs_fsize); + void *buf = alloca (nfrags * sblock->fs_fsize); void *bufp; - readblock (fsbtodb (bno), buf, fsbtodb (nfrags)); + readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize); for (bufp = buf; - bufp - buf < nflags * sblock.fs_fsize; + bufp - buf < nfrags * sblock->fs_fsize; bufp += DIRBLKSIZ) { check1block (bufp); @@ -143,7 +148,7 @@ searchdir (ino_t dir, char *name, ino_t *ino) *ino = 0; - if (!validdir (dir)) + if (!validdir (dir, "READ")) return; getinode (dir, &dino); @@ -160,12 +165,13 @@ changeino (ino_t dir, char *name, ino_t ino) { struct dinode dino; int len; + int madechange; /* Scan through a directory block looking for NAME; if we find it then change the inode pointer to point at INO and return 1; if we don't find it then return 0. */ int - check1block (void *bufp) + check1block (void *buf) { struct directory_entry *dp; @@ -174,7 +180,7 @@ changeino (ino_t dir, char *name, ino_t ino) { if (dp->d_reclen == 0 || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ) - return; + return 0; if (dp->d_ino == 0 || dp->d_ino > maxino) continue; if (dp->d_namlen != len) @@ -183,6 +189,7 @@ changeino (ino_t dir, char *name, ino_t ino) continue; dp->d_ino = ino; + madechange = 1; return 1; } return 0; @@ -194,29 +201,204 @@ changeino (ino_t dir, char *name, ino_t ino) int checkdirblock (daddr_t bno, int nfrags) { - void *buf = alloca (nfrags * sblock.fs_fsize); + void *buf = alloca (nfrags * sblock->fs_fsize); void *bufp; - readblock (fsbtodb (bno), buf, fsbtodb (nfrags)); + readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize); for (bufp = buf; - bufp - buf < nflags * sblock.fs_fsize; + bufp - buf < nfrags * sblock->fs_fsize; bufp += DIRBLKSIZ) { if (check1block (bufp)) { - writeblock (fsbtodb (bno), buf, fsbtodb (nfrags)); + writeblock (fsbtodb (sblock, bno), buf, + nfrags * sblock->fs_fsize); return 0; } } return 1; } - if (!validdir (dir)) + if (!validdir (dir, "REWRITE")) return 0; getinode (dir, &dino); len = strlen (name); + madechange = 0; datablocks_iterate (&dino, checkdirblock); + return madechange; +} + +/* Attempt to expand the size of a directory. Return + 1 if we succeeded. */ +static int +expanddir (struct dinode *dp) +{ + daddr_t lastbn, newblk; + char *cp, buf[sblock->fs_bsize]; + + lastbn = lblkno (sblock, dp->di_size); + if (blkoff (sblock, dp->di_size) && lastbn >= NDADDR - 1) + return 0; + else if (!blkoff (sblock, dp->di_size) && lastbn >= NDADDR) + return 0; + else if (blkoff (sblock, dp->di_size) && !dp->di_db[lastbn]) + return 0; + else if (!blkoff (sblock, dp->di_size) && dp->di_db[lastbn]) + return 0; + + newblk = allocblk (sblock->fs_frag); + if (!newblk) + return 0; + + if (blkoff (sblock, dp->di_size)) + dp->di_db[lastbn + 1] = dp->di_db[lastbn]; + dp->di_db[lastbn] = newblk; + dp->di_size += sblock->fs_bsize; + dp->di_blocks += sblock->fs_bsize / DEV_BSIZE; + + for (cp = buf; cp < buf + sblock->fs_bsize; cp += DIRBLKSIZ) + { + struct directory_entry *dir = (struct directory_entry *) cp; + dir->d_ino = 0; + dir->d_reclen = DIRBLKSIZ; + } + + writeblock (fsbtodb (sblock, newblk), buf, sblock->fs_bsize); + return 1; +} + +/* Add a new link into directory DIR with name NAME and target + INO. Return 1 if we succeeded and 0 if we failed. It is + an error to call this routine if NAME is already present + in DIR. */ +int +makeentry (ino_t dir, ino_t ino, char *name) +{ + int len; + struct dinode dino; + int needed; + int madeentry; + + /* Read a directory block and see if it contains room for the + new entry. If so, add it and return 1; otherwise return 0. */ + int + check1block (void *buf) + { + struct directory_entry *dp; + + for (dp = buf; (void *)dp - buf < DIRBLKSIZ; + dp = (struct directory_entry *) ((void *)dp + dp->d_reclen)) + { + if (dp->d_reclen == 0 + || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ) + return 0; + if (dp->d_ino && dp->d_reclen - DIRSIZ (dp->d_namlen) >= needed) + { + struct directory_entry *newdp; + newdp = (struct directory_entry *) + ((void *)dp + DIRSIZ (DIRECT_NAMLEN (dp))); + + newdp->d_reclen = dp->d_reclen - DIRSIZ (DIRECT_NAMLEN (dp)); + DIRECT_NAMLEN (newdp) = len; + newdp->d_ino = ino; + if (direct_symlink_extension) + newdp->d_type = typemap[ino]; + bcopy (name, newdp->d_name, len + 1); + + dp->d_reclen -= newdp->d_reclen; + madeentry = 1; + return 1; + } + else if (!dp->d_ino && dp->d_reclen >= needed) + { + DIRECT_NAMLEN (dp) = len; + dp->d_ino = ino; + if (direct_symlink_extension) + dp->d_type = typemap[ino]; + bcopy (name, dp->d_name, len + 1); + madeentry = 1; + return 1; + } + } + return 0; + } + + /* Read part of a directory and look to see if it + contains NAME. Return 1 if we should keep looking + at more blocks. */ + int + checkdirblock (daddr_t bno, int nfrags) + { + void *buf = alloca (nfrags * sblock->fs_fsize); + void *bufp; + + readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize); + for (bufp = buf; + bufp - buf < nfrags * sblock->fs_fsize; + bufp += DIRBLKSIZ) + { + if (check1block (bufp)) + { + writeblock (fsbtodb (sblock, bno), buf, + nfrags * sblock->fs_fsize); + return 0; + } + } + return 1; + } + + if (!validdir (dir, "MODIFY")) + return 0; + + getinode (dir, &dino); + len = strlen (name); + needed = DIRSIZ (len); + madeentry = 0; + datablocks_iterate (&dino, checkdirblock); + if (!madeentry) + { + /* Attempt to expand the directory. */ + pwarn ("NO SPACE LEFT IN DIR INO=%d", dir); + if (preen || reply ("EXPAND")) + { + if (expanddir (&dino)) + { + if (preen) + printf (" (EXPANDED)"); + datablock_iterate (&dino, checkdirblock); + } + else + pfatal ("CANNOT EXPAND DIRECTORY"); + } + } + return madeentry; +} + +/* Create a directory node whose parent is to be PARENT, whose inode + is REQUEST, and whose mode is to be MODE. If REQUEST is zero, then + allocate any inode. Initialze the contents of the + directory. Return the inode of the new directory. */ +ino_t +allocdir (ino_t parent, ino_t request, mode_t mode) +{ + ino_t ino; + + ino = allocino (request, (mode & IFMT) == IFDIR); + if (!ino) + return 0; + if (!makeentry (ino, ino, ".")) + goto bad; + if (!makeentry (ino, ino, "..")) + goto bad; + + linkfound[ino]++; + linkfound[parent]++; + return ino; + + bad: + freeino (ino); + return 0; } /* Link node INO into lost+found. If PARENT is positive then INO is @@ -226,7 +408,7 @@ void linkup (ino_t ino, ino_t parent) { struct dinode lfdino; - char tempnam[MAXNAMLEN]; + char tempname[MAXNAMLEN]; if (lfdir == 0) { @@ -263,7 +445,7 @@ linkup (ino_t ino, ino_t parent) } getinode (lfdir, &lfdino); - if ((DI_MODE (&lfdino) & IFMT) != IFDIR) + if ((lfdino.di_model & IFMT) != IFDIR) { ino_t oldlfdir; @@ -307,14 +489,14 @@ linkup (ino_t ino, ino_t parent) } linkfound[ino]++; - if (parent >= 0) + if (parent != -1) { /* Reset `..' in ino */ if (parent) { if (!changeino (ino, "..", lfdir)) { - pfatal ("CANNOT ADJUST .. link I=%lu", ino); + pfatal ("CANNOT ADJUST .. link I=%u", ino); return; } /* Forget about link to old parent */ @@ -322,7 +504,7 @@ linkup (ino_t ino, ino_t parent) } else if (!makeentry (ino, lfdir, "..")) { - pfatal ("CANNOT CREAT .. link I=%lu", ino); + pfatal ("CANNOT CREAT .. link I=%u", ino); return; } @@ -333,9 +515,9 @@ linkup (ino_t ino, ino_t parent) lfdino.di_nlink++; write_inode (lfdir, &lfdino); - pwarn ("DIR I=%lu CONNECTED. ", ino); - if (parentdir) - printf ("PARENT WAS I=%lu\n", parentdir); + pwarn ("DIR I=%u CONNECTED. ", ino); + if (parent) + printf ("PARENT WAS I=%u\n", parent); if (!preen) printf ("\n"); } |