/* * Do the pass1 inode-level checks on inode INO, which has already * been loaded into SFI. Note that sfi_type has already been * validated. * * Returns nonzero if SFI has been modified and needs to be written * back. */ static int pass1_inode(uint32_t ino, struct sfs_dinode *sfi, int alreadychanged) { int changed = alreadychanged; int isdir = sfi->sfi_type == SFS_TYPE_DIR; if (inode_add(ino, sfi->sfi_type)) { /* Already been here. */ assert(changed == 0); return 1; } bitmap_blockinuse(ino, B_INODE, ino); if (checkzeroed(sfi->sfi_waste, sizeof(sfi->sfi_waste))) { warnx("Inode %lu: sfi_waste section not zeroed (fixed)", (unsigned long) ino); setbadness(EXIT_RECOV); changed = 1; } if (check_inode_blocks(ino, sfi, isdir)) { changed = 1; } if (changed) { sfs_writeinode(ino, sfi); } return 0; }
static int check_dir(uint32_t ino, uint32_t parentino, const char *pathsofar) { struct sfs_inode sfi; struct sfs_dir *direntries; int *sortvector; uint32_t dirsize, ndirentries, maxdirentries, subdircount, i; int ichanged=0, dchanged=0, dotseen=0, dotdotseen=0; diskread(&sfi, ino); swapinode(&sfi); if (remember_dir(ino, pathsofar)) { /* crosslinked dir */ return 1; } bitmap_mark(ino, B_INODE, ino); count_dirs++; if (sfi.sfi_size % sizeof(struct sfs_dir) != 0) { setbadness(EXIT_RECOV); warnx("Directory /%s has illegal size %lu (fixed)", pathsofar, (unsigned long) sfi.sfi_size); sfi.sfi_size = SFS_ROUNDUP(sfi.sfi_size, sizeof(struct sfs_dir)); ichanged = 1; } if (check_inode_blocks(ino, &sfi, 1)) { ichanged = 1; } ndirentries = sfi.sfi_size/sizeof(struct sfs_dir); maxdirentries = SFS_ROUNDUP(ndirentries, SFS_BLOCKSIZE/sizeof(struct sfs_dir)); dirsize = maxdirentries * sizeof(struct sfs_dir); direntries = domalloc(dirsize); sortvector = domalloc(ndirentries * sizeof(int)); dirread(&sfi, direntries, ndirentries); for (i=ndirentries; i<maxdirentries; i++) { direntries[i].sfd_ino = SFS_NOINO; bzero(direntries[i].sfd_name, sizeof(direntries[i].sfd_name)); } for (i=0; i<ndirentries; i++) { if (check_dir_entry(pathsofar, i, &direntries[i])) { dchanged = 1; } sortvector[i] = i; } sortdir(sortvector, direntries, ndirentries); /* don't use ndirentries-1 here in case ndirentries == 0 */ for (i=0; i+1<ndirentries; i++) { struct sfs_dir *d1 = &direntries[sortvector[i]]; struct sfs_dir *d2 = &direntries[sortvector[i+1]]; assert(d1 != d2); if (d1->sfd_ino == SFS_NOINO) { continue; } if (!strcmp(d1->sfd_name, d2->sfd_name)) { if (d1->sfd_ino == d2->sfd_ino) { setbadness(EXIT_RECOV); warnx("Directory /%s: Duplicate entries for " "%s (merged)", pathsofar, d1->sfd_name); d1->sfd_ino = SFS_NOINO; d1->sfd_name[0] = 0; } else { snprintf(d1->sfd_name, sizeof(d1->sfd_name), "FSCK.%lu.%lu", (unsigned long) d1->sfd_ino, (unsigned long) uniquecounter++); setbadness(EXIT_RECOV); warnx("Directory /%s: Duplicate names %s " "(one renamed: %s)", pathsofar, d2->sfd_name, d1->sfd_name); } dchanged = 1; } } for (i=0; i<ndirentries; i++) { if (!strcmp(direntries[i].sfd_name, ".")) { if (direntries[i].sfd_ino != ino) { setbadness(EXIT_RECOV); warnx("Directory /%s: Incorrect `.' entry " "(fixed)", pathsofar); direntries[i].sfd_ino = ino; dchanged = 1; } assert(dotseen==0); /* due to duplicate checking */ dotseen = 1; } else if (!strcmp(direntries[i].sfd_name, "..")) { if (direntries[i].sfd_ino != parentino) { setbadness(EXIT_RECOV); warnx("Directory /%s: Incorrect `..' entry " "(fixed)", pathsofar); direntries[i].sfd_ino = parentino; dchanged = 1; } assert(dotdotseen==0); /* due to duplicate checking */ dotdotseen = 1; } } if (!dotseen) { if (dir_tryadd(direntries, ndirentries, ".", ino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `.' entry (added)", pathsofar); dchanged = 1; } else if (dir_tryadd(direntries, maxdirentries, ".", ino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `.' entry (added)", pathsofar); ndirentries++; dchanged = 1; sfi.sfi_size += sizeof(struct sfs_dir); ichanged = 1; } else { setbadness(EXIT_UNRECOV); warnx("Directory /%s: No `.' entry (NOT FIXED)", pathsofar); } } if (!dotdotseen) { if (dir_tryadd(direntries, ndirentries, "..", parentino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `..' entry (added)", pathsofar); dchanged = 1; } else if (dir_tryadd(direntries, maxdirentries, "..", parentino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `..' entry (added)", pathsofar); ndirentries++; dchanged = 1; sfi.sfi_size += sizeof(struct sfs_dir); ichanged = 1; } else { setbadness(EXIT_UNRECOV); warnx("Directory /%s: No `..' entry (NOT FIXED)", pathsofar); } } subdircount=0; for (i=0; i<ndirentries; i++) { if (!strcmp(direntries[i].sfd_name, ".")) { /* nothing */ } else if (!strcmp(direntries[i].sfd_name, "..")) { /* nothing */ } else if (direntries[i].sfd_ino == SFS_NOINO) { /* nothing */ } else { char path[strlen(pathsofar)+SFS_NAMELEN+1]; struct sfs_inode subsfi; diskread(&subsfi, direntries[i].sfd_ino); swapinode(&subsfi); snprintf(path, sizeof(path), "%s/%s", pathsofar, direntries[i].sfd_name); switch (subsfi.sfi_type) { case SFS_TYPE_FILE: if (check_inode_blocks(direntries[i].sfd_ino, &subsfi, 0)) { swapinode(&subsfi); diskwrite(&subsfi, direntries[i].sfd_ino); } observe_filelink(direntries[i].sfd_ino); break; case SFS_TYPE_DIR: if (check_dir(direntries[i].sfd_ino, ino, path)) { setbadness(EXIT_RECOV); warnx("Directory /%s: Crosslink to " "other directory (removed)", path); direntries[i].sfd_ino = SFS_NOINO; direntries[i].sfd_name[0] = 0; dchanged = 1; } else { subdircount++; } break; default: setbadness(EXIT_RECOV); warnx("Object /%s: Invalid inode type " "(removed)", path); direntries[i].sfd_ino = SFS_NOINO; direntries[i].sfd_name[0] = 0; dchanged = 1; break; } } } if (sfi.sfi_linkcount != subdircount+2) { setbadness(EXIT_RECOV); warnx("Directory /%s: Link count %lu should be %lu (fixed)", pathsofar, (unsigned long) sfi.sfi_linkcount, (unsigned long) subdircount+2); sfi.sfi_linkcount = subdircount+2; ichanged = 1; } if (dchanged) { dirwrite(&sfi, direntries, ndirentries); } if (ichanged) { swapinode(&sfi); diskwrite(&sfi, ino); } free(direntries); free(sortvector); return 0; }