From patchwork Mon Dec 6 20:48:42 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josef Bacik X-Patchwork-Id: 379962 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oB6KwZkx030825 for ; Mon, 6 Dec 2010 20:58:35 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752699Ab0LFU6b (ORCPT ); Mon, 6 Dec 2010 15:58:31 -0500 Received: from mx1.redhat.com ([209.132.183.28]:33255 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752388Ab0LFU6a (ORCPT ); Mon, 6 Dec 2010 15:58:30 -0500 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id oB6KwUSG030500 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 6 Dec 2010 15:58:30 -0500 Received: from localhost.localdomain (test1244.test.redhat.com [10.10.10.244]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id oB6KwTWZ015741 for ; Mon, 6 Dec 2010 15:58:29 -0500 From: Josef Bacik To: linux-btrfs@vger.kernel.org Subject: [PATCH] Btrfs: create a unique inode number for all subvol entries Date: Mon, 6 Dec 2010 15:48:42 -0500 Message-Id: <1291668522-7759-1-git-send-email-josef@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Mon, 06 Dec 2010 20:58:36 +0000 (UTC) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 54e4252..ea0662e 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1161,6 +1161,8 @@ struct btrfs_root { #define BTRFS_DIR_LOG_INDEX_KEY 72 #define BTRFS_DIR_ITEM_KEY 84 #define BTRFS_DIR_INDEX_KEY 96 +#define BTRFS_DIR_SUBVOL_KEY 97 + /* * extent data is for file data */ @@ -2320,6 +2322,10 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, const char *name, int name_len, u64 dir, struct btrfs_key *location, u8 type, u64 index); +int btrfs_insert_subvol_dir_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, const char *name, + int name_len, u64 dir, u64 ino, + struct btrfs_key *location, u64 index); struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 dir, diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index f0cad5a..95d498f 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -116,6 +116,119 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans, return ret; } +/** + * btrfs_insert_subvol_dir_item - setup the dir items for a subvol + * + * @trans: transaction handle + * @root: the root of the parent subvol + * @name: name of the subvol + * @name_len: the length of the name + * @dir: the objectid of the parent directory + * @ino: the unique inode number for the parent directory + * @key: the key that the items will point to + * @index: the dir index for readdir purposes + * + * Creates the dir item/dir index pair for the directory containing the subvol. + * This also creates a blank key to hold the made up inode number for the subvol + * in order to give us a unique to the parent subvol inode number. + */ +int btrfs_insert_subvol_dir_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, const char *name, + int name_len, u64 dir, u64 ino, + struct btrfs_key *location, u64 index) +{ + int ret = 0; + int ret2 = 0; + struct btrfs_path *path; + struct btrfs_dir_item *dir_item; + struct extent_buffer *leaf; + unsigned long name_ptr; + unsigned long ino_ptr; + struct btrfs_key key; + struct btrfs_disk_key disk_key; + u32 data_size; + u8 type = BTRFS_FT_DIR; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + path->leave_spinning = 1; + + key.objectid = ino; + key.type = BTRFS_DIR_SUBVOL_KEY; + key.offset = location->objectid; + + ret = btrfs_insert_empty_item(trans, root, path, &key, 0); + if (ret) + goto out; + + btrfs_release_path(root, path); + + key.objectid = dir; + btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); + key.offset = btrfs_name_hash(name, name_len); + + data_size = sizeof(*dir_item) + name_len + sizeof(u64); + dir_item = insert_with_overflow(trans, root, path, &key, data_size, + name, name_len); + if (IS_ERR(dir_item)) { + ret = PTR_ERR(dir_item); + if (ret == -EEXIST) + goto second_insert; + goto out; + } + + leaf = path->nodes[0]; + btrfs_cpu_key_to_disk(&disk_key, location); + btrfs_set_dir_item_key(leaf, dir_item, &disk_key); + btrfs_set_dir_type(leaf, dir_item, type); + btrfs_set_dir_data_len(leaf, dir_item, sizeof(u64)); + btrfs_set_dir_name_len(leaf, dir_item, name_len); + btrfs_set_dir_transid(leaf, dir_item, trans->transid); + name_ptr = (unsigned long)(dir_item + 1); + ino_ptr = name_ptr + name_len; + + write_extent_buffer(leaf, name, name_ptr, name_len); + write_extent_buffer(leaf, &ino, ino_ptr, sizeof(u64)); + btrfs_mark_buffer_dirty(leaf); + +second_insert: + /* FIXME, use some real flag for selecting the extra index */ + if (root == root->fs_info->tree_root) { + ret = 0; + goto out; + } + btrfs_release_path(root, path); + + btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY); + key.offset = index; + dir_item = insert_with_overflow(trans, root, path, &key, data_size, + name, name_len); + if (IS_ERR(dir_item)) { + ret2 = PTR_ERR(dir_item); + goto out; + } + leaf = path->nodes[0]; + btrfs_cpu_key_to_disk(&disk_key, location); + btrfs_set_dir_item_key(leaf, dir_item, &disk_key); + btrfs_set_dir_type(leaf, dir_item, type); + btrfs_set_dir_data_len(leaf, dir_item, sizeof(u64)); + btrfs_set_dir_name_len(leaf, dir_item, name_len); + btrfs_set_dir_transid(leaf, dir_item, trans->transid); + name_ptr = (unsigned long)(dir_item + 1); + ino_ptr = name_ptr + name_len; + write_extent_buffer(leaf, name, name_ptr, name_len); + write_extent_buffer(leaf, &ino, ino_ptr, sizeof(u64)); + btrfs_mark_buffer_dirty(leaf); +out: + btrfs_free_path(path); + if (ret) + return ret; + if (ret2) + return ret2; + return 0; +} + /* * insert a directory item in the tree, doing all the magic for * both indexes. 'dir' indicates which objectid to insert it into, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a6a3e2f..463f816 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2917,6 +2917,7 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, struct btrfs_dir_item *di; struct btrfs_key key; u64 index; + u64 parent_root_ino = 0; int ret; path = btrfs_alloc_path(); @@ -2928,12 +2929,48 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, BUG_ON(!di || IS_ERR(di)); leaf = path->nodes[0]; + + /* + * This subvol has a fake inode number associated with it, make sure we + * get it and delete it. + */ + if (btrfs_dir_data_len(leaf, di) == sizeof(u64)) { + unsigned long data_ptr; + + data_ptr = (unsigned long)(di + 1) + name_len; + read_extent_buffer(leaf, &parent_root_ino, data_ptr, + sizeof(u64)); + } + btrfs_dir_item_key_to_cpu(leaf, di, &key); WARN_ON(key.type != BTRFS_ROOT_ITEM_KEY || key.objectid != objectid); ret = btrfs_delete_one_dir_name(trans, root, path, di); BUG_ON(ret); btrfs_release_path(root, path); + /* Delete the fake ino place holder */ + if (parent_root_ino != 0) { + key.objectid = parent_root_ino; + key.type = BTRFS_DIR_SUBVOL_KEY; + key.offset = objectid; + + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret < 0) { + btrfs_free_path(path); + return ret; + } else if (ret > 0) { + WARN_ON(1); + btrfs_free_path(path); + return -ENOENT; + } + ret = btrfs_del_item(trans, root, path); + if (ret) { + btrfs_free_path(path); + return ret; + } + btrfs_release_path(root, path); + } + ret = btrfs_del_root_ref(trans, root->fs_info->tree_root, objectid, root->root_key.objectid, dir->i_ino, &index, name, name_len); @@ -4254,6 +4291,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent, while (di_cur < di_total) { struct btrfs_key location; + u64 ino; name_len = btrfs_dir_name_len(leaf, di); if (name_len <= sizeof(tmp_name)) { @@ -4279,9 +4317,25 @@ static int btrfs_real_readdir(struct file *filp, void *dirent, over = 0; goto skip; } + + ino = location.objectid; + + /* + * If this is a subvol, check to see if the data len is + * the size of u64, if so it means we have a unique + * inode number for this subvol and we need to use that + * instead. + */ + if (location.type == BTRFS_ROOT_ITEM_KEY && + btrfs_dir_data_len(leaf, di) == sizeof(u64)) { + unsigned long data_ptr; + + data_ptr = (unsigned long)(di + 1) + name_len; + read_extent_buffer(leaf, &ino, data_ptr, + sizeof(u64)); + } over = filldir(dirent, name_ptr, name_len, - found_key.offset, location.objectid, - d_type); + found_key.offset, ino, d_type); skip: if (name_ptr != tmp_name) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f1c9bb4..460ac1e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -238,6 +238,7 @@ static noinline int create_subvol(struct btrfs_root *root, int ret; int err; u64 objectid; + u64 parent_subvol_ino; u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID; u64 index = 0; @@ -248,6 +249,12 @@ static noinline int create_subvol(struct btrfs_root *root, return ret; } + ret = btrfs_find_free_objectid(NULL, root, 0, &parent_subvol_ino); + if (ret) { + dput(parent); + return ret; + } + dir = parent->d_inode; /* @@ -329,9 +336,9 @@ static noinline int create_subvol(struct btrfs_root *root, ret = btrfs_set_inode_index(dir, &index); BUG_ON(ret); - ret = btrfs_insert_dir_item(trans, root, - name, namelen, dir->i_ino, &key, - BTRFS_FT_DIR, index); + ret = btrfs_insert_subvol_dir_item(trans, root, name, namelen, + dir->i_ino, parent_subvol_ino, + &key, index); if (ret) goto fail; diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index f50e931..50b1335 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -910,6 +910,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, u64 to_reserve = 0; u64 index = 0; u64 objectid; + u64 parent_subvol_ino; new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS); if (!new_root_item) { @@ -947,16 +948,27 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, parent_root = BTRFS_I(parent_inode)->root; record_root_in_trans(trans, parent_root); + ret = btrfs_find_free_objectid(trans, parent_root, 0, + &parent_subvol_ino); + if (ret) { + pending->error = ret; + goto fail; + } + /* * insert the directory item */ ret = btrfs_set_inode_index(parent_inode, &index); BUG_ON(ret); - ret = btrfs_insert_dir_item(trans, parent_root, - dentry->d_name.name, dentry->d_name.len, - parent_inode->i_ino, &key, - BTRFS_FT_DIR, index); - BUG_ON(ret); + ret = btrfs_insert_subvol_dir_item(trans, parent_root, + dentry->d_name.name, + dentry->d_name.len, + parent_inode->i_ino, + parent_subvol_ino, &key, index); + if (ret) { + pending->error = ret; + goto fail; + } btrfs_i_size_write(parent_inode, parent_inode->i_size + dentry->d_name.len * 2);