static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk) { #define MAXNINDIR (MAXBSIZE / sizeof(uint32_t)) uint32_t idblk[MAXNINDIR]; size_t i; bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), (int)sblock->fs_bsize); if (ind_level <= 0) { if (find_blks32(idblk, sblock->fs_bsize / sizeof(uint32_t), wantedblk)) return 1; } else { ind_level--; for (i = 0; i < sblock->fs_bsize / sizeof(uint32_t); i++) { if (compare_blk32(wantedblk, iswap32(idblk[i]))) { if (founddatablk(iswap32(idblk[i]))) return 1; } if(idblk[i] != 0) if (find_indirblks32(iswap32(idblk[i]), ind_level, wantedblk)) return 1; } } #undef MAXNINDIR return 0; }
/* * Fill in the ufsi struct, as well as the maxino and dev_bsize global * variables. */ struct ufsi * fs_parametrize(void) { static struct ufsi ufsi; spcl.c_flags = iswap32(iswap32(spcl.c_flags) | DR_NEWINODEFMT); ufsi.ufs_dsize = fsbtodb(sblock,sblock->lfs_size); if (sblock->lfs_version == 1) ufsi.ufs_dsize = sblock->lfs_size >> sblock->lfs_blktodb; ufsi.ufs_bsize = sblock->lfs_bsize; ufsi.ufs_bshift = sblock->lfs_bshift; ufsi.ufs_fsize = sblock->lfs_fsize; ufsi.ufs_frag = sblock->lfs_frag; ufsi.ufs_fsatoda = sblock->lfs_fsbtodb; if (sblock->lfs_version == 1) ufsi.ufs_fsatoda = 0; ufsi.ufs_nindir = sblock->lfs_nindir; ufsi.ufs_inopb = sblock->lfs_inopb; ufsi.ufs_maxsymlinklen = sblock->lfs_maxsymlinklen; ufsi.ufs_bmask = ~(sblock->lfs_bmask); ufsi.ufs_qbmask = sblock->lfs_bmask; ufsi.ufs_fmask = ~(sblock->lfs_ffmask); ufsi.ufs_qfmask = sblock->lfs_ffmask; dev_bsize = sblock->lfs_bsize >> sblock->lfs_blktodb; return &ufsi; }
static int dotime(char *name, int32_t *rsec, int32_t *rnsec) { char *p, *val; struct tm t; int32_t sec; int32_t nsec; p = strchr(name, '.'); if (p) { *p = '\0'; nsec = strtoul(++p, &val, 0); if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { warnx("invalid nanoseconds"); goto badformat; } } else nsec = 0; if (strlen(name) != 14) { badformat: warnx("date format: YYYYMMDDHHMMSS[.nsec]"); return 1; } for (p = name; *p; p++) if (*p < '0' || *p > '9') goto badformat; p = name; #define VAL() ((*p++) - '0') t.tm_year = VAL(); t.tm_year = VAL() + t.tm_year * 10; t.tm_year = VAL() + t.tm_year * 10; t.tm_year = VAL() + t.tm_year * 10 - 1900; t.tm_mon = VAL(); t.tm_mon = VAL() + t.tm_mon * 10 - 1; t.tm_mday = VAL(); t.tm_mday = VAL() + t.tm_mday * 10; t.tm_hour = VAL(); t.tm_hour = VAL() + t.tm_hour * 10; t.tm_min = VAL(); t.tm_min = VAL() + t.tm_min * 10; t.tm_sec = VAL(); t.tm_sec = VAL() + t.tm_sec * 10; t.tm_isdst = -1; sec = mktime(&t); if (sec == -1) { warnx("date/time out of range"); return 1; } *rsec = iswap32(sec); *rnsec = iswap32(nsec); return 0; }
static int find_blks32(uint32_t *buf, int size, uint32_t *wantedblk) { int blk; for(blk = 0; blk < size; blk++) { if (buf[blk] == 0) continue; if (compare_blk32(wantedblk, iswap32(buf[blk]))) { if (founddatablk(iswap32(buf[blk]))) return 1; } } return 0; }
static void print_indirblks32(uint32_t blk, int ind_level, uint64_t *blknum) { #define MAXNINDIR (MAXBSIZE / sizeof(int32_t)) const int ptrperblk_shift = sblock->fs_bshift - 2; const int ptrperblk = 1 << ptrperblk_shift; int32_t idblk[MAXNINDIR]; int i; if (blk == 0) { *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level); return; } printf("Indirect block %lld (level %d):\n", (long long)blk, ind_level+1); bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), (int)sblock->fs_bsize); if (ind_level <= 0) { print_blks32(idblk, ptrperblk, blknum); } else { ind_level--; for (i = 0; i < ptrperblk; i++) print_indirblks32(iswap32(idblk[i]), ind_level, blknum); } #undef MAXNINDIR }
static int chinumfunc(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; if (slotcount++ == desired) { dirp->d_ino = iswap32(idesc->id_parent); return STOP | ALTERED | FOUND; } return KEEPON; }
static int scannames(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; printf("slot %d ino %d reclen %d: %s, `%.*s'\n", slot++, iswap32(dirp->d_ino), iswap16(dirp->d_reclen), typename[dirp->d_type], dirp->d_namlen, dirp->d_name); return (KEEPON); }
static void print_blks32(int32_t *buf, int size, uint64_t *blknum) { int chars; char prbuf[CHARS_PER_LINES+1]; int blk; chars = 0; for(blk = 0; blk < size; blk++, (*blknum)++) { if (buf[blk] == 0) continue; snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk])); if ((chars + strlen(prbuf)) > CHARS_PER_LINES) { printf("\n"); chars = 0; } if (chars == 0) printf("%" PRIu64 ": ", *blknum); printf("%s", prbuf); chars += strlen(prbuf); } printf("\n"); }
/* * Scan each entry in a directory block. */ int dirscan(struct inodesc *idesc) { struct direct *dp; struct bufarea *bp; int dsize, n; long blksiz; #if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ char dbuf[DIRBLKSIZ]; #else char dbuf[APPLEUFS_DIRBLKSIZ]; #endif if (idesc->id_type != DATA) errexit("wrong type to dirscan %d", idesc->id_type); if (idesc->id_entryno == 0 && (idesc->id_filesize & (dirblksiz - 1)) != 0) idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz); blksiz = idesc->id_numfrags * sblock->fs_fsize; if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { idesc->id_filesize -= blksiz; return (SKIP); } /* * If we are are swapping byte order in directory entries, just swap * this block and return. */ if (do_dirswap) { int off; bp = getdirblk(idesc->id_blkno, blksiz); for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) { dp = (struct direct *)(bp->b_un.b_buf + off); dp->d_ino = bswap32(dp->d_ino); dp->d_reclen = bswap16(dp->d_reclen); if (!newinofmt) { u_int8_t tmp = dp->d_namlen; dp->d_namlen = dp->d_type; dp->d_type = tmp; } if (dp->d_reclen == 0) break; } dirty(bp); idesc->id_filesize -= blksiz; return (idesc->id_filesize > 0 ? KEEPON : STOP); } idesc->id_loc = 0; for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { dsize = iswap16(dp->d_reclen); if (dsize > sizeof dbuf) dsize = sizeof dbuf; memmove(dbuf, dp, (size_t)dsize); # if (BYTE_ORDER == LITTLE_ENDIAN) if (!newinofmt && !needswap) { # else if (!newinofmt && needswap) { # endif struct direct *tdp = (struct direct *)dbuf; u_char tmp; tmp = tdp->d_namlen; tdp->d_namlen = tdp->d_type; tdp->d_type = tmp; } idesc->id_dirp = (struct direct *)dbuf; if ((n = (*idesc->id_func)(idesc)) & ALTERED) { # if (BYTE_ORDER == LITTLE_ENDIAN) if (!newinofmt && !doinglevel2 && !needswap) { # else if (!newinofmt && !doinglevel2 && needswap) { # endif struct direct *tdp; u_char tmp; tdp = (struct direct *)dbuf; tmp = tdp->d_namlen; tdp->d_namlen = tdp->d_type; tdp->d_type = tmp; } bp = getdirblk(idesc->id_blkno, blksiz); memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, (size_t)dsize); dirty(bp); sbdirty(); } if (n & STOP) return (n); } return (idesc->id_filesize > 0 ? KEEPON : STOP); } /* * get next entry in a directory. */ static struct direct * fsck_readdir(struct inodesc *idesc) { struct direct *dp, *ndp; struct bufarea *bp; long size, blksiz, fix, dploc; blksiz = idesc->id_numfrags * sblock->fs_fsize; bp = getdirblk(idesc->id_blkno, blksiz); if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 && idesc->id_loc < blksiz) { dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); if (dircheck(idesc, dp)) goto dpok; if (idesc->id_fix == IGNORE) return (0); fix = dofix(idesc, "DIRECTORY CORRUPTED"); bp = getdirblk(idesc->id_blkno, blksiz); dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); dp->d_reclen = iswap16(dirblksiz); dp->d_ino = 0; dp->d_type = 0; dp->d_namlen = 0; dp->d_name[0] = '\0'; if (fix) dirty(bp); else markclean = 0; idesc->id_loc += dirblksiz; idesc->id_filesize -= dirblksiz; return (dp); } dpok: if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) return NULL; dploc = idesc->id_loc; dp = (struct direct *)(bp->b_un.b_buf + dploc); idesc->id_loc += iswap16(dp->d_reclen); idesc->id_filesize -= iswap16(dp->d_reclen); if ((idesc->id_loc % dirblksiz) == 0) return (dp); ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && dircheck(idesc, ndp) == 0) { size = dirblksiz - (idesc->id_loc % dirblksiz); idesc->id_loc += size; idesc->id_filesize -= size; if (idesc->id_fix == IGNORE) return (0); fix = dofix(idesc, "DIRECTORY CORRUPTED"); bp = getdirblk(idesc->id_blkno, blksiz); dp = (struct direct *)(bp->b_un.b_buf + dploc); dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size); if (fix) dirty(bp); else markclean = 0; } return (dp); } /* * Verify that a directory entry is valid. * This is a superset of the checks made in the kernel. */ static int dircheck(struct inodesc *idesc, struct direct *dp) { int size; char *cp; u_char namlen, type; int spaceleft; spaceleft = dirblksiz - (idesc->id_loc % dirblksiz); if (iswap32(dp->d_ino) >= maxino || dp->d_reclen == 0 || iswap16(dp->d_reclen) > spaceleft || (iswap16(dp->d_reclen) & 0x3) != 0) return (0); if (dp->d_ino == 0) return (1); size = DIRSIZ(!newinofmt, dp, needswap); # if (BYTE_ORDER == LITTLE_ENDIAN) if (!newinofmt && !needswap) { # else if (!newinofmt && needswap) { # endif type = dp->d_namlen; namlen = dp->d_type; } else { namlen = dp->d_namlen; type = dp->d_type; } if (iswap16(dp->d_reclen) < size || idesc->id_filesize < size || /* namlen > MAXNAMLEN || */ type > 15) return (0); for (cp = dp->d_name, size = 0; size < namlen; size++) if (*cp == '\0' || (*cp++ == '/')) return (0); if (*cp != '\0') return (0); return (1); } void direrror(ino_t ino, const char *errmesg) { fileerror(ino, ino, errmesg); } void fileerror(ino_t cwd, ino_t ino, const char *errmesg) { union dinode *dp; char pathbuf[MAXPATHLEN + 1]; uint16_t mode; pwarn("%s ", errmesg); pinode(ino); printf("\n"); getpathname(pathbuf, sizeof(pathbuf), cwd, ino); if (ino < ROOTINO || ino > maxino) { pfatal("NAME=%s\n", pathbuf); return; } dp = ginode(ino); if (ftypeok(dp)) { mode = DIP(dp, mode); pfatal("%s=%s\n", (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); } else pfatal("NAME=%s\n", pathbuf); } void adjust(struct inodesc *idesc, int lcnt) { union dinode *dp; int16_t nlink; int saveresolved; dp = ginode(idesc->id_number); nlink = iswap16(DIP(dp, nlink)); if (nlink == lcnt) { /* * If we have not hit any unresolved problems, are running * in preen mode, and are on a file system using soft updates, * then just toss any partially allocated files. */ if (resolved && preen && usedsoftdep) { clri(idesc, "UNREF", 1); return; } else { /* * The file system can be marked clean even if * a file is not linked up, but is cleared. * Hence, resolved should not be cleared when * linkup is answered no, but clri is answered yes. */ saveresolved = resolved; if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) { resolved = saveresolved; clri(idesc, "UNREF", 0); return; } /* * Account for the new reference created by linkup(). */ dp = ginode(idesc->id_number); lcnt--; } } if (lcnt != 0) { pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : ((iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? "DIR" : "FILE")); pinode(idesc->id_number); printf(" COUNT %d SHOULD BE %d", nlink, nlink - lcnt); if (preen || usedsoftdep) { if (lcnt < 0) { printf("\n"); pfatal("LINK COUNT INCREASING"); } if (preen) printf(" (ADJUSTED)\n"); } if (preen || reply("ADJUST") == 1) { DIP_SET(dp, nlink, iswap16(nlink - lcnt)); inodirty(); } else markclean = 0; } } static int mkentry(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; struct direct newent; int newlen, oldlen; newent.d_namlen = strlen(idesc->id_name); newlen = DIRSIZ(0, &newent, 0); if (dirp->d_ino != 0) oldlen = DIRSIZ(0, dirp, 0); else oldlen = 0; if (iswap16(dirp->d_reclen) - oldlen < newlen) return (KEEPON); newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen); dirp->d_reclen = iswap16(oldlen); dirp = (struct direct *)(((char *)dirp) + oldlen); /* ino to be entered is in id_parent */ dirp->d_ino = iswap32(idesc->id_parent); dirp->d_reclen = newent.d_reclen; if (newinofmt) dirp->d_type = inoinfo(idesc->id_parent)->ino_type; else dirp->d_type = 0; dirp->d_namlen = newent.d_namlen; memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); # if (BYTE_ORDER == LITTLE_ENDIAN) /* * If the entry was split, dirscan() will only reverse the byte * order of the original entry, and not the new one, before * writing it back out. So, we reverse the byte order here if * necessary. */ if (oldlen != 0 && !newinofmt && !doinglevel2 && !needswap) { # else if (oldlen != 0 && !newinofmt && !doinglevel2 && needswap) { # endif u_char tmp; tmp = dirp->d_namlen; dirp->d_namlen = dirp->d_type; dirp->d_type = tmp; } return (ALTERED|STOP); } static int chgino(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) return (KEEPON); dirp->d_ino = iswap32(idesc->id_parent); if (newinofmt) dirp->d_type = inoinfo(idesc->id_parent)->ino_type; else dirp->d_type = 0; return (ALTERED|STOP); } int linkup(ino_t orphan, ino_t parentdir, char *name) { union dinode *dp; int lostdir; ino_t oldlfdir; struct inodesc idesc; char tempname[BUFSIZ]; int16_t nlink; uint16_t mode; memset(&idesc, 0, sizeof(struct inodesc)); dp = ginode(orphan); mode = iswap16(DIP(dp, mode)); nlink = iswap16(DIP(dp, nlink)); lostdir = (mode & IFMT) == IFDIR; pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); pinode(orphan); if (preen && DIP(dp, size) == 0) return (0); if (preen) printf(" (RECONNECTED)\n"); else if (reply("RECONNECT") == 0) { markclean = 0; return (0); } if (parentdir != 0) inoinfo(parentdir)->ino_linkcnt++; if (lfdir == 0) { dp = ginode(ROOTINO); idesc.id_name = lfname; idesc.id_type = DATA; idesc.id_func = findino; idesc.id_number = ROOTINO; if ((ckinode(dp, &idesc) & FOUND) != 0) { lfdir = idesc.id_parent; } else { pwarn("NO lost+found DIRECTORY"); if (preen || reply("CREATE")) { lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); if (lfdir != 0) { if (makeentry(ROOTINO, lfdir, lfname) != 0) { numdirs++; if (preen) printf(" (CREATED)\n"); } else { freedir(lfdir, ROOTINO); lfdir = 0; if (preen) printf("\n"); } } if (lfdir != 0) { reparent(lfdir, ROOTINO); } } } if (lfdir == 0) { pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); markclean = 0; return (0); } } dp = ginode(lfdir); mode = DIP(dp, mode); mode = iswap16(mode); if ((mode & IFMT) != IFDIR) { pfatal("lost+found IS NOT A DIRECTORY"); if (reply("REALLOCATE") == 0) { markclean = 0; return (0); } oldlfdir = lfdir; lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); if (lfdir == 0) { pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); markclean = 0; return (0); } if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); markclean = 0; return (0); } inodirty(); reparent(lfdir, ROOTINO); idesc.id_type = ADDR; idesc.id_func = pass4check; idesc.id_number = oldlfdir; adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); inoinfo(oldlfdir)->ino_linkcnt = 0; dp = ginode(lfdir); } if (inoinfo(lfdir)->ino_state != DFOUND) { pfatal("SORRY. NO lost+found DIRECTORY\n\n"); markclean = 0; return (0); } (void)lftempname(tempname, orphan); if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); printf("\n\n"); markclean = 0; return (0); } inoinfo(orphan)->ino_linkcnt--; if (lostdir) { if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && parentdir != (ino_t)-1) (void)makeentry(orphan, lfdir, ".."); dp = ginode(lfdir); nlink = DIP(dp, nlink); DIP_SET(dp, nlink, iswap16(iswap16(nlink) + 1)); inodirty(); inoinfo(lfdir)->ino_linkcnt++; reparent(orphan, lfdir); pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan); if (parentdir != (ino_t)-1) printf("PARENT WAS I=%llu\n", (unsigned long long)parentdir); if (preen == 0) printf("\n"); } return (1); } /* * fix an entry in a directory. */ int changeino(ino_t dir, const char *name, ino_t newnum) { struct inodesc idesc; memset(&idesc, 0, sizeof(struct inodesc)); idesc.id_type = DATA; idesc.id_func = chgino; idesc.id_number = dir; idesc.id_fix = DONTKNOW; idesc.id_name = name; idesc.id_parent = newnum; /* new value for name */ return (ckinode(ginode(dir), &idesc)); } /* * make an entry in a directory */ int makeentry(ino_t parent, ino_t ino, const char *name) { union dinode *dp; struct inodesc idesc; char pathbuf[MAXPATHLEN + 1]; if (parent < ROOTINO || parent >= maxino || ino < ROOTINO || ino >= maxino) return (0); memset(&idesc, 0, sizeof(struct inodesc)); idesc.id_type = DATA; idesc.id_func = mkentry; idesc.id_number = parent; idesc.id_parent = ino; /* this is the inode to enter */ idesc.id_fix = DONTKNOW; idesc.id_name = name; dp = ginode(parent); if (iswap64(DIP(dp, size)) % dirblksiz) { DIP_SET(dp, size, iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz))); inodirty(); } if ((ckinode(dp, &idesc) & ALTERED) != 0) return (1); getpathname(pathbuf, sizeof(pathbuf), parent, parent); dp = ginode(parent); if (expanddir(dp, pathbuf) == 0) return (0); return (ckinode(dp, &idesc) & ALTERED); } /* * Attempt to expand the size of a directory */ static int expanddir(union dinode *dp, char *name) { daddr_t lastbn, newblk, dirblk; struct bufarea *bp; char *cp; #if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ char firstblk[DIRBLKSIZ]; #else char firstblk[APPLEUFS_DIRBLKSIZ]; #endif struct ufs1_dinode *dp1 = NULL; struct ufs2_dinode *dp2 = NULL; if (is_ufs2) dp2 = &dp->dp2; else dp1 = &dp->dp1; lastbn = lblkno(sblock, iswap64(DIP(dp, size))); if (lastbn >= NDADDR - 1 || DIP(dp, db[lastbn]) == 0 || DIP(dp, size) == 0) return (0); if ((newblk = allocblk(sblock->fs_frag)) == 0) return (0); if (is_ufs2) { dp2->di_db[lastbn + 1] = dp2->di_db[lastbn]; dp2->di_db[lastbn] = iswap64(newblk); dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize); dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) + btodb(sblock->fs_bsize)); dirblk = iswap64(dp2->di_db[lastbn + 1]); } else { dp1->di_db[lastbn + 1] = dp1->di_db[lastbn]; dp1->di_db[lastbn] = iswap32((int32_t)newblk); dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize); dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) + btodb(sblock->fs_bsize)); dirblk = iswap32(dp1->di_db[lastbn + 1]); } bp = getdirblk(dirblk, sblksize(sblock, DIP(dp, size), lastbn + 1)); if (bp->b_errs) goto bad; memmove(firstblk, bp->b_un.b_buf, dirblksiz); bp = getdirblk(newblk, sblock->fs_bsize); if (bp->b_errs) goto bad; memmove(bp->b_un.b_buf, firstblk, dirblksiz); emptydir.dot_reclen = iswap16(dirblksiz); for (cp = &bp->b_un.b_buf[dirblksiz]; cp < &bp->b_un.b_buf[sblock->fs_bsize]; cp += dirblksiz) memmove(cp, &emptydir, sizeof emptydir); dirty(bp); bp = getdirblk(dirblk, sblksize(sblock, DIP(dp, size), lastbn + 1)); if (bp->b_errs) goto bad; memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir); pwarn("NO SPACE LEFT IN %s", name); if (preen) printf(" (EXPANDED)\n"); else if (reply("EXPAND") == 0) goto bad; dirty(bp); inodirty(); return (1); bad: if (is_ufs2) { dp2->di_db[lastbn] = dp2->di_db[lastbn + 1]; dp2->di_db[lastbn + 1] = 0; dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize); dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) - btodb(sblock->fs_bsize)); } else { dp1->di_db[lastbn] = dp1->di_db[lastbn + 1]; dp1->di_db[lastbn + 1] = 0; dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize); dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) - btodb(sblock->fs_bsize)); } freeblk(newblk, sblock->fs_frag); markclean = 0; return (0); } /* * allocate a new directory */ ino_t allocdir(ino_t parent, ino_t request, int mode) { ino_t ino; char *cp; union dinode *dp; struct bufarea *bp; struct inoinfo *inp; struct dirtemplate *dirp; daddr_t dirblk; ino = allocino(request, IFDIR|mode); if (ino < ROOTINO) return 0; dirhead.dot_reclen = iswap16(12); dirhead.dotdot_reclen = iswap16(dirblksiz - 12); odirhead.dot_reclen = iswap16(12); odirhead.dotdot_reclen = iswap16(dirblksiz - 12); odirhead.dot_namlen = iswap16(1); odirhead.dotdot_namlen = iswap16(2); if (newinofmt) dirp = &dirhead; else dirp = (struct dirtemplate *)&odirhead; dirp->dot_ino = iswap32(ino); dirp->dotdot_ino = iswap32(parent); dp = ginode(ino); dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0]) : iswap32(dp->dp1.di_db[0]); bp = getdirblk(dirblk, sblock->fs_fsize); if (bp->b_errs) { freeino(ino); return (0); } memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); emptydir.dot_reclen = iswap16(dirblksiz); for (cp = &bp->b_un.b_buf[dirblksiz]; cp < &bp->b_un.b_buf[sblock->fs_fsize]; cp += dirblksiz) memmove(cp, &emptydir, sizeof emptydir); dirty(bp); DIP_SET(dp, nlink, iswap16(2)); inodirty(); if (ino == ROOTINO) { inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink)); cacheino(dp, ino); return(ino); } if (inoinfo(parent)->ino_state != DSTATE && inoinfo(parent)->ino_state != DFOUND) { freeino(ino); return (0); } cacheino(dp, ino); inp = getinoinfo(ino); inp->i_parent = parent; inp->i_dotdot = parent; inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; if (inoinfo(ino)->ino_state == DSTATE) { inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink)); inoinfo(parent)->ino_linkcnt++; } dp = ginode(parent); DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1)); inodirty(); return (ino); }
int main(int argc, char *argv[]) { ino_t ino; int dirty; union dinode *dp; struct fstab *dt; struct statvfs *mntinfo, fsbuf; char *map, *cp; int ch; int i, anydirskipped, bflag = 0, Tflag = 0, Fflag = 0, honorlevel = 1; int snap_internal = 0; ino_t maxino; time_t tnow, date; int dirc; char *mountpoint; int just_estimate = 0; char labelstr[LBLSIZE]; char buf[MAXPATHLEN], rbuf[MAXPATHLEN]; char *new_time_format; char *snap_backup = NULL; spcl.c_date = 0; (void)time(&tnow); spcl.c_date = tnow; tzset(); /* set up timezone for strftime */ if ((new_time_format = getenv("TIMEFORMAT")) != NULL) time_string = new_time_format; tsize = 0; /* Default later, based on 'c' option for cart tapes */ if ((tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; dumpdates = _PATH_DUMPDATES; temp = _PATH_DTMP; strcpy(labelstr, "none"); /* XXX safe strcpy. */ if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = '0'; timestamp = 0; if (argc < 2) usage(); obsolete(&argc, &argv); while ((ch = getopt(argc, argv, "0123456789aB:b:cd:eFf:h:ik:l:L:nr:s:StT:uWwx:X")) != -1) switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = ch; break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; case 'B': /* blocks per output file */ blocksperfile = numarg("blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("blocks per write", 1L, 1000L); bflag = 1; break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'e': /* eject full tapes */ eflag = 1; break; case 'F': /* files-to-dump is an fs image */ Fflag = 1; break; case 'f': /* output file */ tape = optarg; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; case 'i': /* "true incremental" regardless level */ level = 'i'; trueinc = 1; break; case 'k': readblksize = numarg("read block size", 0, 64) * 1024; break; case 'l': /* autoload after eject full tapes */ eflag = 1; lflag = numarg("timeout (in seconds)", 1, 0); break; case 'L': /* * Note that although there are LBLSIZE characters, * the last must be '\0', so the limit on strlen() * is really LBLSIZE-1. */ if (strlcpy(labelstr, optarg, sizeof(labelstr)) >= sizeof(labelstr)) { msg( "WARNING Label `%s' is larger than limit of %lu characters.\n", optarg, (unsigned long)sizeof(labelstr) - 1); msg("WARNING: Using truncated label `%s'.\n", labelstr); } break; case 'n': /* notify operators */ notify = 1; break; case 'r': /* read cache size */ readcache = numarg("read cache size", 0, 512); break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'S': /* exit after estimating # of tapes */ just_estimate = 1; break; case 't': timestamp = 1; break; case 'T': /* time of last dump */ spcl.c_ddate = unctime(optarg); if (spcl.c_ddate < 0) { (void)fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = '?'; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ case 'x': snap_backup = optarg; break; case 'X': snap_internal = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 1) { (void)fprintf(stderr, "Must specify disk or image, or file list\n"); exit(X_STARTUP); } /* * determine if disk is a subdirectory, and setup appropriately */ getfstab(); /* /etc/fstab snarfed */ disk = NULL; disk_dev = NULL; mountpoint = NULL; dirc = 0; for (i = 0; i < argc; i++) { struct stat sb; int error; error = lstat(argv[i], &sb); if (Fflag || (!error && (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)))) { if (error) quit("Cannot stat %s: %s\n", argv[i], strerror(errno)); disk = argv[i]; multicheck: if (dirc != 0) quit("Can't dump a disk or image at the same time as a file list\n"); break; } if ((dt = fstabsearch(argv[i])) != NULL) { disk = argv[i]; mountpoint = xstrdup(dt->fs_file); goto multicheck; } if (statvfs(argv[i], &fsbuf) == -1) quit("Cannot statvfs %s: %s\n", argv[i], strerror(errno)); disk = fsbuf.f_mntfromname; if (strcmp(argv[i], fsbuf.f_mntonname) == 0) goto multicheck; if (mountpoint == NULL) { mountpoint = xstrdup(fsbuf.f_mntonname); if (uflag) { msg("Ignoring u flag for subdir dump\n"); uflag = 0; } if (level > '0') { msg("Subdir dump is done at level 0\n"); level = '0'; } msg("Dumping sub files/directories from %s\n", mountpoint); } else { if (strcmp(mountpoint, fsbuf.f_mntonname) != 0) quit("%s is not on %s\n", argv[i], mountpoint); } msg("Dumping file/directory %s\n", argv[i]); dirc++; } if (mountpoint) free(mountpoint); if (dirc == 0) { argv++; if (argc != 1) { (void)fprintf(stderr, "Excess arguments to dump:"); while (--argc) (void)fprintf(stderr, " %s", *argv++); (void)fprintf(stderr, "\n"); exit(X_STARTUP); } } if (Tflag && uflag) { (void)fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if ((cp = strchr(tape, ':')) != NULL) { host = tape; /* This is fine, because all the const strings don't have : */ *cp++ = '\0'; tape = cp; #ifdef RDUMP if (rmthost(host) == 0) exit(X_STARTUP); #else (void)fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); #if 0 if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); #endif if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); /* * disk can be either the full special file name, or * the file system name. */ mountpoint = NULL; mntinfo = mntinfosearch(disk); if ((dt = fstabsearch(disk)) != NULL) { if (getfsspecname(buf, sizeof(buf), dt->fs_spec) == NULL) quit("%s (%s)", buf, strerror(errno)); if (getdiskrawname(rbuf, sizeof(rbuf), buf) == NULL) quit("Can't get disk raw name for `%s' (%s)", buf, strerror(errno)); disk = rbuf; mountpoint = dt->fs_file; msg("Found %s on %s in %s\n", disk, mountpoint, _PATH_FSTAB); } else if (mntinfo != NULL) { if (getdiskrawname(rbuf, sizeof(rbuf), mntinfo->f_mntfromname) == NULL) quit("Can't get disk raw name for `%s' (%s)", mntinfo->f_mntfromname, strerror(errno)); disk = rbuf; mountpoint = mntinfo->f_mntonname; msg("Found %s on %s in mount table\n", disk, mountpoint); } if (mountpoint != NULL) { if (dirc != 0) (void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys), "a subset of %s", mountpoint); else (void)strlcpy(spcl.c_filesys, mountpoint, sizeof(spcl.c_filesys)); } else if (Fflag) { (void)strlcpy(spcl.c_filesys, "a file system image", sizeof(spcl.c_filesys)); } else { (void)strlcpy(spcl.c_filesys, "an unlisted file system", sizeof(spcl.c_filesys)); } (void)strlcpy(spcl.c_dev, disk, sizeof(spcl.c_dev)); (void)strlcpy(spcl.c_label, labelstr, sizeof(spcl.c_label)); (void)gethostname(spcl.c_host, sizeof(spcl.c_host)); spcl.c_host[sizeof(spcl.c_host) - 1] = '\0'; if ((snap_backup != NULL || snap_internal) && mntinfo == NULL) { msg("WARNING: Cannot use -x or -X on unmounted file system.\n"); snap_backup = NULL; snap_internal = 0; } #ifdef DUMP_LFS sync(); if (snap_backup != NULL || snap_internal) { if (lfs_wrap_stop(mountpoint) < 0) { msg("Cannot stop writing on %s\n", mountpoint); exit(X_STARTUP); } } if ((diskfd = open(disk, O_RDONLY)) < 0) { msg("Cannot open %s\n", disk); exit(X_STARTUP); } disk_dev = disk; #else /* ! DUMP_LFS */ if (snap_backup != NULL || snap_internal) { diskfd = snap_open(mntinfo->f_mntonname, snap_backup, &tnow, &disk_dev); if (diskfd < 0) { msg("Cannot open snapshot of %s\n", mntinfo->f_mntonname); exit(X_STARTUP); } spcl.c_date = tnow; } else { if ((diskfd = open(disk, O_RDONLY)) < 0) { msg("Cannot open %s\n", disk); exit(X_STARTUP); } disk_dev = disk; } sync(); #endif /* ! DUMP_LFS */ needswap = fs_read_sblock(sblock_buf); /* true incremental is always a level 10 dump */ spcl.c_level = trueinc? iswap32(10): iswap32(level - '0'); spcl.c_type = iswap32(TS_TAPE); spcl.c_date = iswap32(spcl.c_date); spcl.c_ddate = iswap32(spcl.c_ddate); if (!Tflag) getdumptime(); /* /etc/dumpdates snarfed */ date = iswap32(spcl.c_date); msg("Date of this level %c dump: %s", level, spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); date = iswap32(spcl.c_ddate); msg("Date of last level %c dump: %s", lastlevel, spcl.c_ddate == 0 ? "the epoch\n" : ctime(&date)); msg("Dumping "); if (snap_backup != NULL || snap_internal) msgtail("a snapshot of "); if (dirc != 0) msgtail("a subset of "); msgtail("%s (%s) ", disk, spcl.c_filesys); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); msg("Label: %s\n", labelstr); ufsib = fs_parametrize(); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%ld) is not a power of 2", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); maxino = fs_maxino(); mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); usedinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = iswap32(spcl.c_level) < honorlevel; initcache(readcache, readblksize); (void)signal(SIGINFO, statussig); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize, mountpoint, (dirc ? argv : NULL)); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %llu tape blocks.\n", (unsigned long long)tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop */ ) * (1.0 / tsize ); /* tape / 0.1" */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte */ + tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG */ ) * (1.0 / tsize ); /* tape / 0.1" */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %llu tape blocks on %3.2f tape(s).\n", (unsigned long long)tapesize, fetapes); } /* * If the user only wants an estimate of the number of * tapes, exit now. */ if (just_estimate) exit(X_FINOK); /* * Allocate tape buffer. */ if (!alloctape()) quit("can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); (void)time((time_t *)&(tstart_writing)); xferrate = 0; dumpmap(usedinomap, TS_CLRI, maxino - 1); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino); if ((DIP(dp, mode) & IFMT) != IFDIR) continue; (void)dumpino(dp, ino); } msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { int mode; if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino); mode = DIP(dp, mode) & IFMT; if (mode == IFDIR) continue; (void)dumpino(dp, ino); } spcl.c_type = iswap32(TS_END); for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("%lld tape blocks\n",(long long)iswap64(spcl.c_tapea)); else msg("%lld tape blocks on %d volume%s\n", (long long)iswap64(spcl.c_tapea), iswap32(spcl.c_volume), (iswap32(spcl.c_volume) == 1) ? "" : "s"); tnow = do_stats(); date = iswap32(spcl.c_date); msg("Date of this level %c dump: %s", level, spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); msg("Date this dump completed: %s", ctime(&tnow)); msg("Average transfer rate: %d KB/s\n", xferrate / tapeno); putdumptime(); trewind(0); broadcast("DUMP IS DONE!\a\a\n"); #ifdef DUMP_LFS lfs_wrap_go(); #endif /* DUMP_LFS */ msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ exit(X_FINOK); /* XXX: to satisfy gcc */ }
static int pass2check(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; struct inoinfo *inp; struct inostat *info; int n, entrysize, ret = 0; union dinode *dp; const char *errmsg; struct direct proto; char namebuf[MAXPATHLEN + 1]; char pathbuf[MAXPATHLEN + 1]; /* * If converting, set directory entry type. */ if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 && iswap32(dirp->d_ino) < maxino) { dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type; ret |= ALTERED; } /* * check for "." */ if (idesc->id_entryno != 0) goto chk1; if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { if (iswap32(dirp->d_ino) != idesc->id_number) { direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); dirp->d_ino = iswap32(idesc->id_number); if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; } if (newinofmt && dirp->d_type != DT_DIR) { direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); dirp->d_type = DT_DIR; if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; } goto chk1; } direrror(idesc->id_number, "MISSING '.'"); proto.d_ino = iswap32(idesc->id_number); if (newinofmt) proto.d_type = DT_DIR; else proto.d_type = 0; proto.d_namlen = 1; (void)strlcpy(proto.d_name, ".", sizeof(proto.d_name)); # if BYTE_ORDER == LITTLE_ENDIAN if (!newinofmt && !needswap) { # else if (!newinofmt && needswap) { # endif u_char tmp; tmp = proto.d_type; proto.d_type = proto.d_namlen; proto.d_namlen = tmp; } entrysize = DIRSIZ(0, &proto, 0); if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", dirp->d_name); markclean = 0; } else if (iswap16(dirp->d_reclen) < entrysize) { pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); markclean = 0; } else if (iswap16(dirp->d_reclen) < 2 * entrysize) { proto.d_reclen = dirp->d_reclen; memmove(dirp, &proto, (size_t)entrysize); if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; } else { n = iswap16(dirp->d_reclen) - entrysize; proto.d_reclen = iswap16(entrysize); memmove(dirp, &proto, (size_t)entrysize); idesc->id_entryno++; inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; dirp = (struct direct *)((char *)(dirp) + entrysize); memset(dirp, 0, (size_t)n); dirp->d_reclen = iswap16(n); if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; } chk1: if (idesc->id_entryno > 1) goto chk2; inp = getinoinfo(idesc->id_number); proto.d_ino = iswap32(inp->i_parent); if (newinofmt) proto.d_type = DT_DIR; else proto.d_type = 0; proto.d_namlen = 2; (void)strlcpy(proto.d_name, "..", sizeof(proto.d_name)); #if BYTE_ORDER == LITTLE_ENDIAN if (!newinofmt && !needswap) { #else if (!newinofmt && needswap) { #endif u_char tmp; tmp = proto.d_type; proto.d_type = proto.d_namlen; proto.d_namlen = tmp; } entrysize = DIRSIZ(0, &proto, 0); if (idesc->id_entryno == 0) { n = DIRSIZ(0, dirp, 0); if (iswap16(dirp->d_reclen) < n + entrysize) goto chk2; proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n); dirp->d_reclen = iswap16(n); idesc->id_entryno++; inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; dirp = (struct direct *)((char *)(dirp) + n); memset(dirp, 0, (size_t)iswap16(proto.d_reclen)); dirp->d_reclen = proto.d_reclen; } if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { inp->i_dotdot = iswap32(dirp->d_ino); if (newinofmt && dirp->d_type != DT_DIR) { direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); dirp->d_type = DT_DIR; if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; } goto chk2; } if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) { fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", dirp->d_name); inp->i_dotdot = (ino_t)-1; markclean = 0; } else if (iswap16(dirp->d_reclen) < entrysize) { fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); inp->i_dotdot = (ino_t)-1; markclean = 0; } 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.d_reclen = dirp->d_reclen; memmove(dirp, &proto, (size_t)entrysize); if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; } idesc->id_entryno++; if (dirp->d_ino != 0) inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; return (ret|KEEPON); chk2: if (dirp->d_ino == 0) return (ret|KEEPON); if (dirp->d_namlen <= 2 && dirp->d_name[0] == '.' && idesc->id_entryno >= 2) { if (dirp->d_namlen == 1) { direrror(idesc->id_number, "EXTRA '.' ENTRY"); dirp->d_ino = 0; if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; return (KEEPON | ret); } if (dirp->d_name[1] == '.') { direrror(idesc->id_number, "EXTRA '..' ENTRY"); dirp->d_ino = 0; if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; return (KEEPON | ret); } } idesc->id_entryno++; n = 0; if (iswap32(dirp->d_ino) > maxino) { fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); n = reply("REMOVE"); if (n == 0) markclean = 0; } else if (newinofmt && ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) || (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) { fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY"); dirp->d_ino = iswap32(WINO); dirp->d_type = DT_WHT; if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; } else { again: info = inoinfo(iswap32(dirp->d_ino)); switch (info->ino_state) { case USTATE: if (idesc->id_entryno <= 2) break; fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED"); n = reply("REMOVE"); if (n == 0) markclean = 0; break; case DCLEAR: case FCLEAR: if (idesc->id_entryno <= 2) break; if (info->ino_state == FCLEAR) errmsg = "DUP/BAD"; else if (!preen && !usedsoftdep) errmsg = "ZERO LENGTH DIRECTORY"; else { n = 1; break; } fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg); if ((n = reply("REMOVE")) == 1) break; dp = ginode(iswap32(dirp->d_ino)); info->ino_state = (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE; info->ino_linkcnt = iswap16(DIP(dp, nlink)); goto again; case DSTATE: case DFOUND: inp = getinoinfo(iswap32(dirp->d_ino)); if (inp->i_parent != 0 && idesc->id_entryno > 2) { getpathname(pathbuf, sizeof(pathbuf), idesc->id_number, idesc->id_number); getpathname(namebuf, sizeof(namebuf), iswap32(dirp->d_ino), iswap32(dirp->d_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 (newinofmt && dirp->d_type != info->ino_type) { fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD TYPE VALUE"); dirp->d_type = info->ino_type; if (reply("FIX") == 1) ret |= ALTERED; else markclean = 0; } info->ino_linkcnt--; break; default: errexit("BAD STATE %d FOR INODE I=%d", info->ino_state, iswap32(dirp->d_ino)); } } if (n == 0) return (ret|KEEPON); dirp->d_ino = 0; return (ret|KEEPON|ALTERED); } /* * Routine to sort disk blocks. */ static int blksort(const void *arg1, const void *arg2) { return ((*(const struct inoinfo *const *)arg1)->i_blks[0] - (*(const struct inoinfo *const *)arg2)->i_blks[0]); }