static errcode_t orphan_dir_check(ocfs2_filesys *fs, uint16_t new_slots) { errcode_t ret = 0; uint64_t blkno; int i, has_orphan = 0; uint16_t max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; for (i = new_slots ; i < max_slots; ++i) { ret = ocfs2_lookup_system_inode(fs, ORPHAN_DIR_SYSTEM_INODE, i, &blkno); if (ret) { verbosef(VL_APP, "%s while looking up orphan dir for " "slot %u during orphan dir check\n", error_message(ret), i); break; } ret = ocfs2_dir_iterate(fs, blkno, OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL, orphan_iterate, &has_orphan); if (has_orphan) { ret = TUNEFS_ET_ORPHAN_DIR_NOT_EMPTY; verbosef(VL_APP, "Entries found in orphan dir for slot %u\n", i); break; } } return ret; }
static PyObject * fs_dir_iterate (Filesystem *self, PyObject *args, PyObject *kwargs) { errcode_t ret; WalkData wdata; PyObject *py_func, *py_data = NULL, *py_dir = NULL; uint64_t dir; int flags = OCFS2_DIRENT_FLAG_EXCLUDE_DOTS; static char *kwlist[] = { "callback", "data", "dir", "flags", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "O|OOi:dir_iterate", kwlist, &py_func, &py_data, &py_dir, &flags)) return NULL; if (!PyCallable_Check (py_func)) { PyErr_SetString (PyExc_TypeError, "callback must be a callable object"); return NULL; } if (py_dir == NULL || py_dir == Py_None) dir = self->fs->fs_root_blkno; else if (DirEntry_Check (py_dir)) dir = ((DirEntry *) py_dir)->dentry.inode; else if (PyInt_Check (py_dir)) dir = PyInt_AsUnsignedLongMask (py_dir); else if (PyLong_Check (py_dir)) dir = PyLong_AsUnsignedLongLongMask (py_dir); else { PyErr_SetString (PyExc_TypeError, "dir must be DirEntry or integer"); return NULL; } Py_INCREF (py_func); wdata.func = py_func; Py_XINCREF (py_data); wdata.data = py_data; wdata.fs = self; /* XXX: handle errors */ ret = ocfs2_dir_iterate (self->fs, dir, flags, NULL, walk_dirs, &wdata); Py_DECREF (py_func); Py_XDECREF (py_data); Py_INCREF (Py_None); return Py_None; }
static errcode_t remove_slot_entry(ocfs2_filesys *fs, uint16_t removed_slot) { struct remove_slot_ctxt ctxt = { .fs = fs, .removed_slot = removed_slot, .errcode = 0 }; ocfs2_dir_iterate(fs, fs->fs_sysdir_blkno, OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL, remove_slot_iterate, &ctxt); return ctxt.errcode; } static errcode_t decrease_link_count(ocfs2_filesys *fs, uint16_t blkno) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto bail; di = (struct ocfs2_dinode *)buf; if (di->i_links_count > 0) di->i_links_count--; else { ret = OCFS2_ET_INODE_NOT_VALID; goto bail; } ret = ocfs2_write_inode(fs, blkno, buf); bail: if (buf) ocfs2_free(&buf); 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 int walk_tree_func(struct ocfs2_dir_entry *dentry, int offset, int blocksize, char *buf, void *priv_data) { errcode_t ret; int len, oldval; int reti = 0; char *old_path, *path; struct walk_path *wp = priv_data; if (!strncmp(dentry->name, ".", dentry->name_len) || !strncmp(dentry->name, "..", dentry->name_len)) return 0; len = strlen(wp->path); if (len + dentry->name_len > 4095) { fprintf(stderr, "name is too long in %s\n", wp->path); return OCFS2_DIRENT_ABORT; } ret = ocfs2_malloc0(4096, &path); if (ret) { com_err(wp->argv0, ret, "while allocating path memory in %s\n", wp->path); return OCFS2_DIRENT_ABORT; } memcpy(path, wp->path, len); memcpy(path + len, dentry->name, dentry->name_len); if (dentry->file_type == OCFS2_FT_DIR) path[len + dentry->name_len] = '/'; if (wp->check_dups) { ret = ocfs2_bitmap_test(wp->dup_map, dentry->inode, &oldval); if (oldval) { fprintf(stdout, "Dup! %20"PRIu64" %s\n", (uint64_t)dentry->inode, path); } goto out; } oldval = 0; ret = ocfs2_bitmap_set(wp->inode_map, dentry->inode, &oldval); if (ret) { com_err(wp->argv0, ret, "while setting bitmap bit %"PRIu64"\n", (uint64_t)dentry->inode); reti = OCFS2_DIRENT_ABORT; goto out; } if (oldval) { wp->has_dups = 1; ret = ocfs2_bitmap_set(wp->dup_map, dentry->inode, NULL); if (ret) { com_err(wp->argv0, ret, "while setting dup bit %"PRIu64"\n", (uint64_t)dentry->inode); reti = OCFS2_DIRENT_ABORT; goto out; } } if (!wp->quiet) fprintf(stdout, "%20"PRIu64" %s\n", (uint64_t)dentry->inode, path); if (dentry->file_type == OCFS2_FT_DIR) { old_path = wp->path; wp->path = path; ret = ocfs2_dir_iterate(wp->fs, dentry->inode, 0, NULL, walk_tree_func, wp); if (ret) { com_err(wp->argv0, ret, "while walking %s", wp->path); reti = OCFS2_DIRENT_ABORT; } wp->path = old_path; } out: ocfs2_free(&path); return reti; }
int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; char *filename; ocfs2_filesys *fs; struct walk_path wp; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); wp.argv0 = argv[0]; wp.quiet = 0; wp.has_dups = 0; wp.check_dups = 0; if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; if (argc > 2) { if (!strcmp(argv[2], "-q")) wp.quiet = 1; } ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } wp.fs = fs; ret = ocfs2_block_bitmap_new(fs, "Inode bitmap", &wp.inode_map); if (ret) { com_err(argv[0], ret, "while creating the inode bitmap"); goto out_close; } ret = ocfs2_block_bitmap_new(fs, "Duplicate inode bitmap", &wp.dup_map); if (ret) { com_err(argv[0], ret, "while creating the duplicate inode bitmap"); goto out_close; } ocfs2_bitmap_set(wp.inode_map, OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno, NULL); ocfs2_bitmap_set(wp.inode_map, OCFS2_RAW_SB(fs->fs_super)->s_root_blkno, NULL); fprintf(stdout, "Walking system directory...\n"); wp.path = "<system_dir>/"; ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while walking sysdm dir inode %"PRIu64" on \"%s\"\n", blkno, filename); goto out_close; } wp.path = "/"; fprintf(stdout, "Walking root directory...\n"); ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_root_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while walking root inode %"PRIu64" on \"%s\"\n", blkno, filename); goto out_close; } if (wp.has_dups) { fprintf(stdout, "Hardlinks found\n"); wp.check_dups = 1; fprintf(stdout, "Scanning system directory for dups...\n"); wp.path = "<system_dir>/"; ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while dup scanning sysdm dir inode %"PRIu64 " on \"%s\"\n", blkno, filename); goto out_close; } wp.path = "/"; fprintf(stdout, "Scanning root directory for dups...\n"); ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_root_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while dup scanning root inode %"PRIu64 " on \"%s\"\n", blkno, filename); goto out_close; } } out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; }
static errcode_t remove_quota_files(ocfs2_filesys *fs, int type, struct tools_progress *prog) { struct remove_quota_files_ctxt ctxt = { .fs = fs, .type = type, .err = 0, }; ocfs2_dir_iterate(fs, fs->fs_sysdir_blkno, OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL, remove_quota_files_iterate, &ctxt); tools_progress_step(prog, 1); return ctxt.err; } static int enable_usrquota(ocfs2_filesys *fs, int flags) { struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); errcode_t ret; struct tools_progress *prog = NULL; if (OCFS2_HAS_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { verbosef(VL_APP, "User quotas are already enabled; " "nothing to enable\n"); return 0; } if (!tools_interact("Enable user quota feature on device " "\"%s\"? ", fs->fs_devname)) return 0; prog = tools_progress_start("Enabling user quota", "usrquota", 6); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing progress display"); return ret; } tunefs_block_signals(); ret = create_quota_files(fs, USRQUOTA, prog); if (ret) { tcom_err(ret, "while creating user quota files"); goto bail; } OCFS2_SET_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA); ret = ocfs2_write_super(fs); tools_progress_step(prog, 1); bail: tunefs_unblock_signals(); tools_progress_stop(prog); return ret; } static int disable_usrquota(ocfs2_filesys *fs, int flags) { errcode_t ret; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (!OCFS2_HAS_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { verbosef(VL_APP, "User quotas are already disabled; " "nothing to disable\n"); return 0; } if (!tools_interact("Disable user quota feature on device " "\"%s\"? ", fs->fs_devname)) return 0; prog = tools_progress_start("Disabling user quota", "nousrquota", 2); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing progress display"); return ret; } tunefs_block_signals(); ret = remove_quota_files(fs, USRQUOTA, prog); if (ret) { tcom_err(ret, "while removing user quota files"); goto bail; } OCFS2_CLEAR_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA); ret = ocfs2_write_super(fs); tools_progress_step(prog, 1); bail: tunefs_unblock_signals(); tools_progress_stop(prog); return ret; }