X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=inode.c;h=2398bca4a1096bb20cf4d8cf632b99e8b527c66c;hb=feccd7a8180f3f0f48ca23692ad5f71783163450;hp=be03a52297a71c308a729ad5d6f41720b2c12213;hpb=fe283a4fc32e28c9f83d5e9faec4b8f8375daeb0;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/inode.c b/inode.c index be03a52..2398bca 100644 --- a/inode.c +++ b/inode.c @@ -20,7 +20,7 @@ * Unlike inode.c in kernel, which can use most of the kernel infrastructure * like inode/dentry things, in user-land, we can only use inode number to * do directly operation on extent buffer, which may cause extra searching, - * but should not be a huge problem since progs is less performence sensitive. + * but should not be a huge problem since progs is less performance sensitive. */ #include @@ -28,6 +28,7 @@ #include "transaction.h" #include "disk-io.h" #include "time.h" +#include "messages.h" /* * Find a free inode index for later btrfs_add_link(). @@ -161,7 +162,7 @@ out: */ int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 ino, u64 parent_ino, char *name, int namelen, - u8 type, u64 *index, int add_backref) + u8 type, u64 *index, int add_backref, int ignore_existed) { struct btrfs_path *path; struct btrfs_key key; @@ -184,33 +185,37 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root, } ret = check_dir_conflict(root, name, namelen, parent_ino, ret_index); - if (ret < 0) + if (ret < 0 && !(ignore_existed && ret == -EEXIST)) goto out; /* Add inode ref */ if (add_backref) { ret = btrfs_insert_inode_ref(trans, root, name, namelen, ino, parent_ino, ret_index); - if (ret < 0) + if (ret < 0 && !(ignore_existed && ret == -EEXIST)) goto out; - /* Update nlinks for the inode */ - key.objectid = ino; - key.type = BTRFS_INODE_ITEM_KEY; - key.offset = 0; - ret = btrfs_search_slot(trans, root, &key, path, 1, 1); - if (ret) { - if (ret > 0) - ret = -ENOENT; - goto out; + /* do not update nlinks if existed */ + if (!ret) { + /* Update nlinks for the inode */ + key.objectid = ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + ret = btrfs_search_slot(trans, root, &key, path, 1, 1); + if (ret) { + if (ret > 0) + ret = -ENOENT; + goto out; + } + inode_item = btrfs_item_ptr(path->nodes[0], + path->slots[0], struct btrfs_inode_item); + nlink = btrfs_inode_nlink(path->nodes[0], inode_item); + nlink++; + btrfs_set_inode_nlink(path->nodes[0], inode_item, + nlink); + btrfs_mark_buffer_dirty(path->nodes[0]); + btrfs_release_path(path); } - inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], - struct btrfs_inode_item); - nlink = btrfs_inode_nlink(path->nodes[0], inode_item); - nlink++; - btrfs_set_inode_nlink(path->nodes[0], inode_item, nlink); - btrfs_mark_buffer_dirty(path->nodes[0]); - btrfs_release_path(path); } /* Add dir_item and dir_index */ @@ -262,7 +267,7 @@ int btrfs_add_orphan_item(struct btrfs_trans_handle *trans, * dir_item if any of them exists. * * If an inode's nlink is reduced to 0 and 'add_orphan' is true, it will be - * added to orphan inode and wairing to be deleted by next kernel mount. + * added to orphan inode and waiting to be deleted by next kernel mount. */ int btrfs_unlink(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 ino, u64 parent_ino, u64 index, const char *name, @@ -286,7 +291,7 @@ int btrfs_unlink(struct btrfs_trans_handle *trans, struct btrfs_root *root, /* check the ref and backref exists */ inode_ref = btrfs_lookup_inode_ref(trans, root, path, name, namelen, - ino, parent_ino, index, 0); + ino, parent_ino, 0); if (IS_ERR(inode_ref)) { ret = PTR_ERR(inode_ref); goto out; @@ -472,6 +477,42 @@ int btrfs_new_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root, } /* + * Change inode flags to given value + */ +int btrfs_change_inode_flags(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 ino, u64 flags) +{ + struct btrfs_inode_item *item; + struct btrfs_path *path; + struct btrfs_key key; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + if (ret > 0) { + ret = -ENOENT; + goto out; + } + if (ret < 0) + goto out; + + item = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_item); + btrfs_set_inode_flags(path->nodes[0], item, flags); + btrfs_mark_buffer_dirty(path->nodes[0]); +out: + btrfs_free_path(path); + return ret; +} + +/* * Make a dir under the parent inode 'parent_ino' with 'name' * and 'mode', The owner will be root/root. */ @@ -525,7 +566,7 @@ int btrfs_mkdir(struct btrfs_trans_handle *trans, struct btrfs_root *root, if (ret) goto out; ret = btrfs_add_link(trans, root, ret_ino, parent_ino, name, namelen, - BTRFS_FT_DIR, NULL, 1); + BTRFS_FT_DIR, NULL, 1, 0); if (ret) goto out; out: @@ -534,3 +575,132 @@ out: *ino = ret_ino; return ret; } + +struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root, + const char *base, u64 root_objectid, + bool convert) +{ + struct btrfs_trans_handle *trans; + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *new_root = NULL; + struct btrfs_path path; + struct btrfs_inode_item *inode_item; + struct extent_buffer *leaf; + struct btrfs_key key; + u64 dirid = btrfs_root_dirid(&root->root_item); + u64 index = 2; + char buf[BTRFS_NAME_LEN + 1]; /* for snprintf null */ + int len; + int i; + int ret; + + len = strlen(base); + if (len == 0 || len > BTRFS_NAME_LEN) + return NULL; + + btrfs_init_path(&path); + key.objectid = dirid; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret <= 0) { + error("search for DIR_INDEX dirid %llu failed: %d", + (unsigned long long)dirid, ret); + goto fail; + } + + if (path.slots[0] > 0) { + path.slots[0]--; + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY) + index = key.offset + 1; + } + btrfs_release_path(&path); + + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + error("unable to start transaction"); + goto fail; + } + + key.objectid = dirid; + key.offset = 0; + key.type = BTRFS_INODE_ITEM_KEY; + + ret = btrfs_lookup_inode(trans, root, &path, &key, 1); + if (ret) { + error("search for INODE_ITEM %llu failed: %d", + (unsigned long long)dirid, ret); + goto fail; + } + leaf = path.nodes[0]; + inode_item = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_inode_item); + + key.objectid = root_objectid; + key.offset = (u64)-1; + key.type = BTRFS_ROOT_ITEM_KEY; + + memcpy(buf, base, len); + if (convert) { + for (i = 0; i < 1024; i++) { + ret = btrfs_insert_dir_item(trans, root, buf, len, + dirid, &key, BTRFS_FT_DIR, index); + if (ret != -EEXIST) + break; + len = snprintf(buf, ARRAY_SIZE(buf), "%s%d", base, i); + if (len < 1 || len > BTRFS_NAME_LEN) { + ret = -EINVAL; + break; + } + } + } else { + ret = btrfs_insert_dir_item(trans, root, buf, len, dirid, &key, + BTRFS_FT_DIR, index); + } + if (ret) + goto fail; + + btrfs_set_inode_size(leaf, inode_item, len * 2 + + btrfs_inode_size(leaf, inode_item)); + btrfs_mark_buffer_dirty(leaf); + btrfs_release_path(&path); + + /* add the backref first */ + ret = btrfs_add_root_ref(trans, tree_root, root_objectid, + BTRFS_ROOT_BACKREF_KEY, + root->root_key.objectid, + dirid, index, buf, len); + if (ret) { + error("unable to add root backref for %llu: %d", + root->root_key.objectid, ret); + goto fail; + } + + /* now add the forward ref */ + ret = btrfs_add_root_ref(trans, tree_root, root->root_key.objectid, + BTRFS_ROOT_REF_KEY, root_objectid, + dirid, index, buf, len); + if (ret) { + error("unable to add root ref for %llu: %d", + root->root_key.objectid, ret); + goto fail; + } + + ret = btrfs_commit_transaction(trans, root); + if (ret) { + error("transaction commit failed: %d", ret); + goto fail; + } + + new_root = btrfs_read_fs_root(fs_info, &key); + if (IS_ERR(new_root)) { + error("unable to fs read root: %lu", PTR_ERR(new_root)); + new_root = NULL; + } +fail: + btrfs_init_path(&path); + return new_root; +}