Btrfs: rename
authorChris Mason <chris.mason@oracle.com>
Wed, 23 May 2007 19:44:28 +0000 (15:44 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Wed, 23 May 2007 19:44:28 +0000 (15:44 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/TODO
fs/btrfs/ctree.h
fs/btrfs/dir-item.c
fs/btrfs/super.c

index f6df246..861e34a 100644 (file)
@@ -8,6 +8,7 @@
 * Add generation number to key pointer in nodes
 * Add generation number to inode
 * Add ability to switch a block group from data to metadata or vice versa
+* forbid cross subvolume renames and hardlinks
 * Release
 * Do real tree locking
 * Add extent mirroring (backup copies of blocks)
index da12d82..3330004 100644 (file)
@@ -32,6 +32,7 @@ extern struct kmem_cache *btrfs_path_cachep;
 #define BTRFS_CSUM_SIZE 32
 /* four bytes for CRC32 */
 #define BTRFS_CRC32_SIZE 4
+#define BTRFS_EMPTY_DIR_SIZE 6
 
 /*
  * the key defines the order in the tree, and so it also defines (optimal)
index 00a28d9..b408a3d 100644 (file)
@@ -9,7 +9,9 @@ static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
                                                   struct btrfs_root *root,
                                                   struct btrfs_path *path,
                                                   struct btrfs_key *cpu_key,
-                                                  u32 data_size)
+                                                  u32 data_size,
+                                                  const char *name,
+                                                  int name_len)
 {
        int ret;
        char *ptr;
@@ -18,6 +20,10 @@ static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
 
        ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size);
        if (ret == -EEXIST) {
+               struct btrfs_dir_item *di;
+               di = btrfs_match_dir_item_name(root, path, name, name_len);
+               if (di)
+                       return ERR_PTR(-EEXIST);
                ret = btrfs_extend_item(trans, root, path, data_size);
                WARN_ON(ret > 0);
                if (ret)
@@ -37,6 +43,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
                          struct btrfs_key *location, u8 type)
 {
        int ret = 0;
+       int ret2 = 0;
        struct btrfs_path *path;
        struct btrfs_dir_item *dir_item;
        char *name_ptr;
@@ -51,9 +58,12 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
        path = btrfs_alloc_path();
        btrfs_init_path(path);
        data_size = sizeof(*dir_item) + name_len;
-       dir_item = insert_with_overflow(trans, root, path, &key, data_size);
+       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;
        }
 
@@ -66,19 +76,20 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
        btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
        btrfs_mark_buffer_dirty(path->nodes[0]);
 
+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 = location->objectid;
-       dir_item = insert_with_overflow(trans, root, path, &key, data_size);
+       dir_item = insert_with_overflow(trans, root, path, &key, data_size,
+                                       name, name_len);
        if (IS_ERR(dir_item)) {
-               ret = PTR_ERR(dir_item);
+               ret2 = PTR_ERR(dir_item);
                goto out;
        }
        btrfs_cpu_key_to_disk(&dir_item->location, location);
@@ -90,7 +101,11 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
        btrfs_mark_buffer_dirty(path->nodes[0]);
 out:
        btrfs_free_path(path);
-       return ret;
+       if (ret)
+               return ret;
+       if (ret2)
+               return ret2;
+       return 0;
 }
 
 struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
index 0220c82..f49cad6 100644 (file)
@@ -375,6 +375,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
        struct btrfs_path *path;
        struct btrfs_key key;
        struct btrfs_disk_key *found_key;
+       u32 found_type;
        struct btrfs_leaf *leaf;
        struct btrfs_file_extent_item *fi = NULL;
        u64 extent_start = 0;
@@ -386,12 +387,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
        /* FIXME, add redo link to tree so we don't leak on crash */
        key.objectid = inode->i_ino;
        key.offset = (u64)-1;
-       key.flags = 0;
-       /*
-        * use BTRFS_CSUM_ITEM_KEY because it is larger than inline keys
-        * or extent data
-        */
-       btrfs_set_key_type(&key, BTRFS_CSUM_ITEM_KEY);
+       key.flags = (u32)-1;
        while(1) {
                btrfs_init_path(path);
                ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
@@ -405,10 +401,13 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
                reada_truncate(root, path, inode->i_ino);
                leaf = btrfs_buffer_leaf(path->nodes[0]);
                found_key = &leaf->items[path->slots[0]].key;
+               found_type = btrfs_disk_key_type(found_key);
                if (btrfs_disk_key_objectid(found_key) != inode->i_ino)
                        break;
-               if (btrfs_disk_key_type(found_key) != BTRFS_CSUM_ITEM_KEY &&
-                   btrfs_disk_key_type(found_key) != BTRFS_EXTENT_DATA_KEY)
+               if (found_type != BTRFS_CSUM_ITEM_KEY &&
+                   found_type != BTRFS_DIR_ITEM_KEY &&
+                   found_type != BTRFS_DIR_INDEX_KEY &&
+                   found_type != BTRFS_EXTENT_DATA_KEY)
                        break;
                if (btrfs_disk_key_offset(found_key) < inode->i_size)
                        break;
@@ -460,10 +459,8 @@ static void btrfs_delete_inode(struct inode *inode)
        mutex_lock(&root->fs_info->fs_mutex);
        trans = btrfs_start_transaction(root, 1);
        btrfs_set_trans_block_group(trans, inode);
-       if (S_ISREG(inode->i_mode)) {
-               ret = btrfs_truncate_in_trans(trans, root, inode);
-               BUG_ON(ret);
-       }
+       ret = btrfs_truncate_in_trans(trans, root, inode);
+       BUG_ON(ret);
        btrfs_free_inode(trans, root, inode);
        btrfs_end_transaction(trans, root);
        mutex_unlock(&root->fs_info->fs_mutex);
@@ -2504,6 +2501,116 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        return 0;
 }
 
+static int btrfs_rename(struct inode * old_dir, struct dentry *old_dentry,
+                          struct inode * new_dir,struct dentry *new_dentry)
+{
+       struct btrfs_trans_handle *trans;
+       struct btrfs_root *root = BTRFS_I(old_dir)->root;
+       struct inode *new_inode = new_dentry->d_inode;
+       struct inode *old_inode = old_dentry->d_inode;
+       struct timespec ctime = CURRENT_TIME;
+       struct btrfs_path *path;
+       struct btrfs_dir_item *di;
+       int ret;
+
+       if (S_ISDIR(old_inode->i_mode) && new_inode &&
+           new_inode->i_size > BTRFS_EMPTY_DIR_SIZE) {
+               return -ENOTEMPTY;
+       }
+       mutex_lock(&root->fs_info->fs_mutex);
+       trans = btrfs_start_transaction(root, 1);
+       btrfs_set_trans_block_group(trans, new_dir);
+       path = btrfs_alloc_path();
+       if (!path) {
+               ret = -ENOMEM;
+               goto out_fail;
+       }
+
+       old_dentry->d_inode->i_nlink++;
+       old_dir->i_ctime = old_dir->i_mtime = ctime;
+       new_dir->i_ctime = new_dir->i_mtime = ctime;
+       old_inode->i_ctime = ctime;
+       if (S_ISDIR(old_inode->i_mode) && old_dir != new_dir) {
+               struct btrfs_key *location = &BTRFS_I(new_dir)->location;
+               u64 old_parent_oid;
+               di = btrfs_lookup_dir_item(trans, root, path, old_inode->i_ino,
+                                          "..", 2, -1);
+               if (IS_ERR(di)) {
+                       ret = PTR_ERR(di);
+                       goto out_fail;
+               }
+               if (!di) {
+                       ret = -ENOENT;
+                       goto out_fail;
+               }
+               old_parent_oid = btrfs_disk_key_objectid(&di->location);
+               ret = btrfs_del_item(trans, root, path);
+               if (ret) {
+                       ret = -EIO;
+                       goto out_fail;
+               }
+               btrfs_release_path(root, path);
+
+               di = btrfs_lookup_dir_index_item(trans, root, path,
+                                                old_inode->i_ino,
+                                                old_parent_oid,
+                                                "..", 2, -1);
+               if (IS_ERR(di)) {
+                       ret = PTR_ERR(di);
+                       goto out_fail;
+               }
+               if (!di) {
+                       ret = -ENOENT;
+                       goto out_fail;
+               }
+               ret = btrfs_del_item(trans, root, path);
+               if (ret) {
+                       ret = -EIO;
+                       goto out_fail;
+               }
+               btrfs_release_path(root, path);
+
+               ret = btrfs_insert_dir_item(trans, root, "..", 2,
+                                           old_inode->i_ino, location, 0);
+               if (ret)
+                       goto out_fail;
+       }
+
+
+       ret = btrfs_add_link(trans, new_dentry, old_inode);
+       if (ret == -EEXIST && new_inode)
+               ret = 0;
+       else if (ret)
+               goto out_fail;
+
+       ret = btrfs_unlink_trans(trans, root, old_dir, old_dentry);
+       if (ret)
+               goto out_fail;
+
+       if (new_inode) {
+               new_inode->i_ctime = CURRENT_TIME;
+               di = btrfs_lookup_dir_index_item(trans, root, path,
+                                                new_dir->i_ino,
+                                                new_inode->i_ino,
+                                                new_dentry->d_name.name,
+                                                new_dentry->d_name.len, -1);
+               if (di && !IS_ERR(di)) {
+                       btrfs_del_item(trans, root, path);
+                       btrfs_release_path(root, path);
+               }
+               if (S_ISDIR(new_inode->i_mode))
+                       clear_nlink(new_inode);
+               else
+                       drop_nlink(new_inode);
+               btrfs_update_inode(trans, root, new_inode);
+       }
+out_fail:
+       btrfs_free_path(path);
+       btrfs_end_transaction(trans, root);
+       mutex_unlock(&root->fs_info->fs_mutex);
+       return ret;
+}
+
 static struct file_system_type btrfs_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "btrfs",
@@ -2531,6 +2638,7 @@ static struct inode_operations btrfs_dir_inode_operations = {
        .unlink         = btrfs_unlink,
        .mkdir          = btrfs_mkdir,
        .rmdir          = btrfs_rmdir,
+       .rename         = btrfs_rename,
 };
 
 static struct inode_operations btrfs_dir_ro_inode_operations = {