summaryrefslogtreecommitdiff
path: root/fs/btrfs/super.c
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2007-05-24 08:34:10 -0400
committerDavid Woodhouse <dwmw2@hera.kernel.org>2007-05-24 08:34:10 -0400
commit2b8d99a723a3186a69b5bd941a272dbecae9205b (patch)
treec3f87b82450110d2df8ac03032bd2e21b8984f19 /fs/btrfs/super.c
parente06afa839e726959be2166bec4cb85c117e213f1 (diff)
Btrfs: symlinks and hard links
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/super.c')
-rw-r--r--fs/btrfs/super.c163
1 files changed, 147 insertions, 16 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index f49cad603ee..c1a0adde9b1 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -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;