errcode_t o2fsck_pass3(o2fsck_state *ost) { o2fsck_dir_parent *dp; errcode_t ret = 0; ocfs2_filesys *fs = ost->ost_fs; struct o2fsck_resource_track rt; printf("Pass 3: Checking directory connectivity\n"); o2fsck_init_resource_track(&rt, fs->fs_io); /* these could probably share more code. We might need to treat the * other required directories like root here */ check_root(ost); check_lostfound(ost); dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, ost->ost_fs->fs_root_blkno); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "root inode %"PRIu64" wasn't marked as " "a directory in pass1", ost->ost_fs->fs_root_blkno); goto out; } dp->dp_connected = 1; dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, ost->ost_fs->fs_sysdir_blkno); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "system dir inode %"PRIu64" wasn't " "marked as a directory in pass1", ost->ost_fs->fs_sysdir_blkno); goto out; } dp->dp_connected = 1; for(dp = o2fsck_dir_parent_first(&ost->ost_dir_parents) ; dp; dp = o2fsck_dir_parent_next(dp)) { /* XXX hmm, make sure dir->ino is in the dir map? */ ret = connect_directory(ost, dp); if (ret) goto out; } o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 3", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); out: return ret; }
static errcode_t fix_dirent_linkage(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, int offset, unsigned int *flags) { int expect_dots = expected_dots(ost, dbe, offset); o2fsck_dir_parent *dp; errcode_t ret = 0; int is_dir; /* we already took care of special-casing the dots */ if (expect_dots) goto out; /* we're only checking the linkage if we already found the dir * this inode claims to be pointing to */ ret = ocfs2_bitmap_test(ost->ost_dir_inodes, dirent->inode, &is_dir); if (ret) com_err(whoami, ret, "while checking for inode %"PRIu64" in " "the dir bitmap", (uint64_t)dirent->inode); if (!is_dir) goto out; dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, dirent->inode); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "no dir parents recorded for inode " "%"PRIu64, (uint64_t)dirent->inode); goto out; } /* if no dirents have pointed to this inode yet we record ours * as the first and move on */ if (dp->dp_dirent == 0) { dp->dp_dirent = dbe->e_ino; goto out; } if (prompt(ost, 0, PR_DIR_PARENT_DUP, "Directory inode %"PRIu64" is not the first to " "claim to be the parent of subdir '%.*s' (inode %"PRIu64"). " "Clear this directory entry and leave the previous parent of " "the subdir's inode intact?", dbe->e_ino, dirent->name_len, dirent->name, (uint64_t)dirent->inode)) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; } out: return ret; }
static void fix_dot_dot(o2fsck_state *ost, o2fsck_dir_parent *dir) { errcode_t ret; struct fix_dot_dot_args args = { .ost = ost, .parent = dir->dp_dirent, .fixed = 0, }; ret = ocfs2_dir_iterate(ost->ost_fs, dir->dp_ino, OCFS2_DIRENT_FLAG_INCLUDE_EMPTY, NULL, fix_dot_dot_dirent, &args); if (ret) { com_err("fix_dot_dot", ret, "while iterating through dir " "inode %"PRIu64"'s directory entries.", dir->dp_dirent); /* XXX mark fs invalid */ return; } if (!args.fixed) { fprintf(stderr, "Didn't find a '..' entry to fix.\n"); /* XXX mark fs invalid */ return; } dir->dp_dot_dot = dir->dp_dirent; } /* add a directory entry that points to a given inode in lost+found. */ void o2fsck_reconnect_file(o2fsck_state *ost, uint64_t inode) { static char iname[NAME_MAX + 1]; char name[] = "lost+found"; int namelen = sizeof(name) - 1; o2fsck_dir_parent *dp; errcode_t ret; uint8_t type; int len; if (ost->ost_lostfound_ino == 0) { ret = ocfs2_lookup(ost->ost_fs, ost->ost_fs->fs_root_blkno, name, namelen, NULL, &ost->ost_lostfound_ino); if (ret) { com_err(whoami, ret, "while trying to find the " "/lost+found directory so that inode " "%"PRIu64" could be moved there.", inode); goto out; } } len = snprintf(iname, sizeof(iname), "#%"PRIu64, inode); if (len <= 0) { ret = OCFS2_ET_NO_MEMORY; com_err(whoami, ret, "while trying to build a new file name " "for inode %"PRIu64" to use in /lost+found", inode); goto out; } ret = o2fsck_type_from_dinode(ost, inode, &type); if (ret) goto out; ret = ocfs2_link(ost->ost_fs, ost->ost_lostfound_ino, iname, inode, type); if (ret) { com_err(whoami, ret, "while trying to link inode %"PRIu64" " "into /lost+found", inode); goto out; } /* add another ref to account for this new dirent */ o2fsck_icount_delta(ost->ost_icount_refs, inode, 1); /* if we just added a directory to l+f we need to track that * the new dirent points to the dir. we leave the dot_dot tracking * intact because we didn't change that in the dirblock.. */ if (type == OCFS2_FT_DIR) { dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, inode); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "while looking up the directory " "parent structure for inode %"PRIu64, inode); goto out; } dp->dp_dirent = ost->ost_lostfound_ino; } out: return; } static uint64_t loop_no = 0; static errcode_t connect_directory(o2fsck_state *ost, o2fsck_dir_parent *dir) { o2fsck_dir_parent *dp = dir, *par; errcode_t ret = 0; int fix; verbosef("checking dir inode %"PRIu64" parent %"PRIu64" dot_dot " "%"PRIu64"\n", dir->dp_ino, dp->dp_dirent, dp->dp_dot_dot); loop_no++; while(!dp->dp_connected) { /* we either will ascend to a parent that is connected or * we'll graft the subtree with this directory on to lost * and found. */ dp->dp_connected = 1; /* move on to the parent dir only if it exists and we haven't * already traversed it in this instance of parent walking */ if (dp->dp_dirent) { par = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, dp->dp_dirent); if (par == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "no dir info for parent " "%"PRIu64, dp->dp_dirent); goto out; } if (par->dp_loop_no != loop_no) { par->dp_loop_no = loop_no; dp = par; continue; } } /* ok, we hit an orphan subtree with no parent or are at * the dir in a subtree that is the first to try to reference * a dir in its children */ fix = prompt(ost, PY, PR_DIR_NOT_CONNECTED, "Directory inode %"PRIu64" isn't " "connected to the filesystem. Move it to " "lost+found?", dp->dp_ino); if (fix) o2fsck_reconnect_file(ost, dp->dp_ino); break; } /* * orphan dirs are a magically awesome special case. they have * their i_link_count increased when subdirs are added but * the subdirs '..' entry isn't updated to point to the orphan * dir. we alter our book-keeping to it look like the '..' * was reasonable on disk. */ if (dir->dp_in_orphan_dir) { /* previous '..' entry is garbage */ if (dir->dp_dot_dot) o2fsck_icount_delta(ost->ost_icount_refs, dir->dp_dot_dot, -1); /* pretend '..' pointed to the orphan dir */ dir->dp_dot_dot = dir->dp_dirent; o2fsck_icount_delta(ost->ost_icount_refs, dir->dp_dot_dot, 1); } if (dir->dp_dirent != dir->dp_dot_dot) { fix = prompt(ost, PY, PR_DIR_DOTDOT, "Directory inode %"PRIu64" is " "referenced by a dirent in directory %"PRIu64" " "but its '..' entry points to inode %"PRIu64". " "Fix the '..' entry to reference %"PRIu64"?", dir->dp_ino, dir->dp_dirent, dir->dp_dot_dot, dir->dp_dirent); if (fix) fix_dot_dot(ost, dir); } out: return ret; }
static errcode_t fix_dirent_dots(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, int offset, int left, unsigned int *flags) { int expect_dots = expected_dots(ost, dbe, offset); int changed_len = 0; struct ocfs2_dir_entry *next; uint16_t new_len; errcode_t ret = 0; if (!expect_dots) { if (!dirent->inode || (!dirent_has_dots(dirent, 1) && !dirent_has_dots(dirent, 2))) goto out; if (prompt(ost, PY, PR_DIRENT_DOTTY_DUP, "Duplicate '%.*s' directory entry found, remove " "it?", dirent->name_len, dirent->name)) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; goto out; } } if (!dirent_has_dots(dirent, expect_dots) && prompt(ost, PY, PR_DIRENT_NOT_DOTTY, "The %s directory entry in directory inode " "%"PRIu64" is '%.*s' instead of '%.*s'. Clobber the " "current name with the expected dot name?", expect_dots == 1 ? "first" : "second", dbe->e_ino, dirent->name_len, dirent->name, expect_dots, "..")) { dirent->name_len = expect_dots; memset(dirent->name, '.', expect_dots); dirent->file_type = OCFS2_FT_DIR; changed_len = 1; *flags |= OCFS2_DIRENT_CHANGED; } /* we only record where .. points for now and that ends the * checks for .. */ if (expect_dots == 2) { o2fsck_dir_parent *dp; dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, dbe->e_ino); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "no dir parents for '..' entry " "for inode %"PRIu64, dbe->e_ino); } else dp->dp_dot_dot = dirent->inode; goto out; } if ((dirent->inode != dbe->e_ino) && prompt(ost, PY, PR_DIRENT_DOT_INODE, "The '.' entry in directory inode %"PRIu64" " "points to inode %"PRIu64" instead of itself. Fix " "the '.' entry?", dbe->e_ino, (uint64_t)dirent->inode)) { dirent->inode = dbe->e_ino; *flags |= OCFS2_DIRENT_CHANGED; } /* * we might have slop at the end of this "." dirent. split * it into another seperate dirent if there is enough room and * we've just updated it's name_len or the user says we should. */ new_len = OCFS2_DIR_REC_LEN(dirent->name_len) - dirent->rec_len; if (new_len && (changed_len || prompt(ost, PY, PR_DIRENT_DOT_EXCESS, "The '.' entry in directory inode " "%"PRIu64" is too long. Try to create another " "directory entry from the excess?", dbe->e_ino))) { dirent->rec_len = OCFS2_DIR_REC_LEN(dirent->name_len); next = (struct ocfs2_dir_entry *)((char *)dirent + dirent->rec_len); next->inode = 0; next->name_len = 0; next->rec_len = OCFS2_DIR_REC_LEN(next->rec_len); *flags |= OCFS2_DIRENT_CHANGED; } out: return ret; }