From 43c36f3cfd4e076e8bc882a2f53619b88be15e2e Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 9 Dec 2014 16:27:26 +0800 Subject: btrfs-progs: Add btrfs_mkdir() function for the incoming 'lost+found' fsck mechanism. With the previous btrfs inode operations patches, now we can use btrfs_mkdir() to create the 'lost+found' dir to do some data salvage in btrfsck. This patch along with previous ones will make data salvage easier. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- inode.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'inode.c') diff --git a/inode.c b/inode.c index 7fbff110..ff1be48c 100644 --- a/inode.c +++ b/inode.c @@ -431,3 +431,105 @@ out: btrfs_free_path(path); return ret; } + +/* Fill inode item with 'mode'. Uid/gid to root/root */ +static void fill_inode_item(struct btrfs_trans_handle *trans, + struct btrfs_inode_item *inode_item, + u32 mode, u32 nlink) +{ + time_t now = time(NULL); + + btrfs_set_stack_inode_generation(inode_item, trans->transid); + btrfs_set_stack_inode_uid(inode_item, 0); + btrfs_set_stack_inode_gid(inode_item, 0); + btrfs_set_stack_inode_size(inode_item, 0); + btrfs_set_stack_inode_mode(inode_item, mode); + btrfs_set_stack_inode_nlink(inode_item, nlink); + btrfs_set_stack_timespec_sec(&inode_item->atime, now); + btrfs_set_stack_timespec_nsec(&inode_item->atime, 0); + btrfs_set_stack_timespec_sec(&inode_item->mtime, now); + btrfs_set_stack_timespec_nsec(&inode_item->mtime, 0); + btrfs_set_stack_timespec_sec(&inode_item->ctime, now); + btrfs_set_stack_timespec_nsec(&inode_item->ctime, 0); +} + +/* + * Unlike kernel btrfs_new_inode(), we only create the INODE_ITEM, without + * its backref. + * The backref is added by btrfs_add_link(). + */ +static int btrfs_new_inode(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 ino, u32 mode) +{ + struct btrfs_inode_item inode_item = {0}; + int ret = 0; + + fill_inode_item(trans, &inode_item, mode, 0); + ret = btrfs_insert_inode(trans, root, ino, &inode_item); + return ret; +} + +/* + * Make a dir under the parent inode 'parent_ino' with 'name' + * and 'mode', The owner will be root/root. + */ +int btrfs_mkdir(struct btrfs_trans_handle *trans, struct btrfs_root *root, + char *name, int namelen, u64 parent_ino, u64 *ino, int mode) +{ + struct btrfs_dir_item *dir_item; + struct btrfs_path *path; + u64 ret_ino = 0; + int ret = 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + if (ino && *ino) + ret_ino = *ino; + + dir_item = btrfs_lookup_dir_item(NULL, root, path, parent_ino, + name, namelen, 0); + if (IS_ERR(dir_item)) { + ret = PTR_ERR(dir_item); + goto out; + } + + if (dir_item) { + struct btrfs_key found_key; + + /* + * Already have conflicting name, check if it is a dir. + * Either way, no need to continue. + */ + btrfs_dir_item_key_to_cpu(path->nodes[0], dir_item, &found_key); + ret_ino = found_key.objectid; + if (btrfs_dir_type(path->nodes[0], dir_item) != BTRFS_FT_DIR) + ret = -EEXIST; + goto out; + } + + if (!ret_ino) + /* + * This is *UNSAFE* if some leaf is corrupted, + * only used as a fallback method. Caller should either + * ensure the fs is OK or pass ino with unused inode number. + */ + ret = btrfs_find_free_objectid(NULL, root, parent_ino, + &ret_ino); + if (ret) + goto out; + ret = btrfs_new_inode(trans, root, ret_ino, mode | S_IFDIR); + if (ret) + goto out; + ret = btrfs_add_link(trans, root, ret_ino, parent_ino, name, namelen, + BTRFS_FT_DIR, NULL, 1); + if (ret) + goto out; +out: + btrfs_free_path(path); + if (ret == 0 && ino) + *ino = ret_ino; + return ret; +} -- cgit v1.2.3