summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/TODO1
-rw-r--r--fs/btrfs/ctree.h1
-rw-r--r--fs/btrfs/dir-item.c27
-rw-r--r--fs/btrfs/super.c132
4 files changed, 143 insertions, 18 deletions
diff --git a/fs/btrfs/TODO b/fs/btrfs/TODO
index f6df246f26c3..861e34ae0979 100644
--- a/fs/btrfs/TODO
+++ b/fs/btrfs/TODO
@@ -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)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index da12d8275817..3330004af1a6 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -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)
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 00a28d90fea6..b408a3d20c7b 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -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,
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 0220c82841ad..f49cad603ee8 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -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 = {