Btrfs: symlinks and hard links
authorChris Mason <chris.mason@oracle.com>
Thu, 24 May 2007 12:34:10 +0000 (08:34 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Thu, 24 May 2007 12:34:10 +0000 (08:34 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/super.c

index f49cad6..c1a0add 100644 (file)
@@ -26,11 +26,13 @@ struct btrfs_iget_args {
 #define BTRFS_SUPER_MAGIC 0x9123682E
 
 static struct inode_operations btrfs_dir_inode_operations;
+static struct inode_operations btrfs_symlink_inode_operations;
 static struct inode_operations btrfs_dir_ro_inode_operations;
 static struct super_operations btrfs_super_ops;
 static struct file_operations btrfs_dir_file_operations;
 static struct inode_operations btrfs_file_inode_operations;
 static struct address_space_operations btrfs_aops;
+static struct address_space_operations btrfs_symlink_aops;
 static struct file_operations btrfs_file_operations;
 
 static void btrfs_read_locked_inode(struct inode *inode)
@@ -103,7 +105,8 @@ static void btrfs_read_locked_inode(struct inode *inode)
                        inode->i_op = &btrfs_dir_inode_operations;
                break;
        case S_IFLNK:
-               // inode->i_op = &page_symlink_inode_operations;
+               inode->i_op = &btrfs_symlink_inode_operations;
+               inode->i_mapping->a_ops = &btrfs_symlink_aops;
                break;
        }
        return;
@@ -940,6 +943,41 @@ out_unlock:
        return err;
 }
 
+static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
+                     struct dentry *dentry)
+{
+       struct btrfs_trans_handle *trans;
+       struct btrfs_root *root = BTRFS_I(dir)->root;
+       struct inode *inode = old_dentry->d_inode;
+       int err;
+       int drop_inode = 0;
+
+       if (inode->i_nlink == 0)
+               return -ENOENT;
+
+       inc_nlink(inode);
+       mutex_lock(&root->fs_info->fs_mutex);
+       trans = btrfs_start_transaction(root, 1);
+       btrfs_set_trans_block_group(trans, dir);
+       atomic_inc(&inode->i_count);
+       err = btrfs_add_nondir(trans, dentry, inode);
+       if (err)
+               drop_inode = 1;
+       dir->i_sb->s_dirt = 1;
+       btrfs_update_inode_block_group(trans, dir);
+       btrfs_update_inode(trans, root, inode);
+
+       btrfs_end_transaction(trans, root);
+       mutex_unlock(&root->fs_info->fs_mutex);
+
+       if (drop_inode) {
+               inode_dec_link_count(inode);
+               iput(inode);
+       }
+       btrfs_btree_balance_dirty(root);
+       return err;
+}
+
 static int btrfs_make_empty_dir(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root,
                                u64 objectid, u64 dirid)
@@ -2577,33 +2615,25 @@ static int btrfs_rename(struct inode * old_dir, struct dentry *old_dentry,
        }
 
 
-       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);
-               }
+               ret = btrfs_unlink_trans(trans, root, new_dir, new_dentry);
+               if (ret)
+                       goto out_fail;
                if (S_ISDIR(new_inode->i_mode))
                        clear_nlink(new_inode);
                else
                        drop_nlink(new_inode);
                btrfs_update_inode(trans, root, new_inode);
        }
+       ret = btrfs_add_link(trans, new_dentry, old_inode);
+       if (ret)
+               goto out_fail;
+
 out_fail:
        btrfs_free_path(path);
        btrfs_end_transaction(trans, root);
@@ -2611,6 +2641,94 @@ out_fail:
        return ret;
 }
 
+static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
+                        const char *symname)
+{
+       struct btrfs_trans_handle *trans;
+       struct btrfs_root *root = BTRFS_I(dir)->root;
+       struct btrfs_path *path;
+       struct btrfs_key key;
+       struct inode *inode;
+       int err;
+       int drop_inode = 0;
+       u64 objectid;
+       int name_len;
+       int datasize;
+       char *ptr;
+       struct btrfs_file_extent_item *ei;
+
+       name_len = strlen(symname) + 1;
+       if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(root))
+               return -ENAMETOOLONG;
+       mutex_lock(&root->fs_info->fs_mutex);
+       trans = btrfs_start_transaction(root, 1);
+       btrfs_set_trans_block_group(trans, dir);
+
+       err = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid);
+       if (err) {
+               err = -ENOSPC;
+               goto out_unlock;
+       }
+
+       inode = btrfs_new_inode(trans, root, objectid,
+                               BTRFS_I(dir)->block_group, S_IFLNK|S_IRWXUGO);
+       err = PTR_ERR(inode);
+       if (IS_ERR(inode))
+               goto out_unlock;
+
+       btrfs_set_trans_block_group(trans, inode);
+       err = btrfs_add_nondir(trans, dentry, inode);
+       if (err)
+               drop_inode = 1;
+       else {
+               inode->i_mapping->a_ops = &btrfs_aops;
+               inode->i_fop = &btrfs_file_operations;
+               inode->i_op = &btrfs_file_inode_operations;
+       }
+       dir->i_sb->s_dirt = 1;
+       btrfs_update_inode_block_group(trans, inode);
+       btrfs_update_inode_block_group(trans, dir);
+       if (drop_inode)
+               goto out_unlock;
+
+       path = btrfs_alloc_path();
+       BUG_ON(!path);
+       key.objectid = inode->i_ino;
+       key.offset = 0;
+       key.flags = 0;
+       btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
+       datasize = btrfs_file_extent_calc_inline_size(name_len);
+       err = btrfs_insert_empty_item(trans, root, path, &key,
+                                     datasize);
+       BUG_ON(err);
+       ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]),
+              path->slots[0], struct btrfs_file_extent_item);
+       btrfs_set_file_extent_generation(ei, trans->transid);
+       btrfs_set_file_extent_type(ei,
+                                  BTRFS_FILE_EXTENT_INLINE);
+       ptr = btrfs_file_extent_inline_start(ei);
+       btrfs_memcpy(root, path->nodes[0]->b_data,
+                    ptr, symname, name_len);
+       mark_buffer_dirty(path->nodes[0]);
+       btrfs_free_path(path);
+       inode->i_op = &btrfs_symlink_inode_operations;
+       inode->i_mapping->a_ops = &btrfs_symlink_aops;
+       inode->i_size = name_len - 1;
+       btrfs_update_inode(trans, root, inode);
+       err = 0;
+
+out_unlock:
+       btrfs_end_transaction(trans, root);
+       mutex_unlock(&root->fs_info->fs_mutex);
+
+       if (drop_inode) {
+               inode_dec_link_count(inode);
+               iput(inode);
+       }
+       btrfs_btree_balance_dirty(root);
+       return err;
+}
+
 static struct file_system_type btrfs_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "btrfs",
@@ -2636,9 +2754,11 @@ static struct inode_operations btrfs_dir_inode_operations = {
        .lookup         = btrfs_lookup,
        .create         = btrfs_create,
        .unlink         = btrfs_unlink,
+       .link           = btrfs_link,
        .mkdir          = btrfs_mkdir,
        .rmdir          = btrfs_rmdir,
        .rename         = btrfs_rename,
+       .symlink        = btrfs_symlink,
 };
 
 static struct inode_operations btrfs_dir_ro_inode_operations = {
@@ -2660,6 +2780,11 @@ static struct address_space_operations btrfs_aops = {
        .commit_write   = btrfs_commit_write,
 };
 
+static struct address_space_operations btrfs_symlink_aops = {
+       .readpage       = btrfs_readpage,
+       .writepage      = btrfs_writepage,
+};
+
 static struct inode_operations btrfs_file_inode_operations = {
        .truncate       = btrfs_truncate,
        .getattr        = btrfs_getattr,
@@ -2676,6 +2801,12 @@ static struct file_operations btrfs_file_operations = {
        .fsync          = btrfs_sync_file,
 };
 
+static struct inode_operations btrfs_symlink_inode_operations = {
+       .readlink       = generic_readlink,
+       .follow_link    = page_follow_link_light,
+       .put_link       = page_put_link,
+};
+
 static int __init init_btrfs_fs(void)
 {
        int err;