/* * Unlink an inode, which will remove its backref and corresponding dir_index/ * 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 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, int namelen, int add_orphan) { struct btrfs_path *path; struct btrfs_key key; struct btrfs_inode_item *inode_item; struct btrfs_inode_ref *inode_ref; struct btrfs_dir_item *dir_item; u64 inode_size; u32 nlinks; int del_inode_ref = 0; int del_dir_item = 0; int del_dir_index = 0; int ret = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; /* check the ref and backref exists */ inode_ref = btrfs_lookup_inode_ref(trans, root, path, name, namelen, ino, parent_ino, index, 0); if (IS_ERR(inode_ref)) { ret = PTR_ERR(inode_ref); goto out; } if (inode_ref) del_inode_ref = 1; btrfs_release_path(path); dir_item = btrfs_lookup_dir_item(NULL, root, path, parent_ino, name, namelen, 0); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (dir_item) del_dir_item = 1; btrfs_release_path(path); dir_item = btrfs_lookup_dir_index(NULL, root, path, parent_ino, name, namelen, index, 0); /* * Since lookup_dir_index() will return -ENOENT when not found, * we need to do extra check. */ if (IS_ERR(dir_item) && PTR_ERR(dir_item) == -ENOENT) dir_item = NULL; if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (dir_item) del_dir_index = 1; btrfs_release_path(path); if (!del_inode_ref && !del_dir_item && !del_dir_index) { /* All not found, shouldn't happen */ ret = -ENOENT; goto out; } if (del_inode_ref) { /* Only decrease nlink when deleting inode_ref */ 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); nlinks = btrfs_inode_nlink(path->nodes[0], inode_item); if (nlinks > 0) nlinks--; btrfs_set_inode_nlink(path->nodes[0], inode_item, nlinks); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); /* For nlinks == 0, add it to orphan list if needed */ if (nlinks == 0 && add_orphan) { ret = btrfs_add_orphan_item(trans, root, path, ino); if (ret < 0) goto out; btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } ret = btrfs_del_inode_ref(trans, root, name, namelen, ino, parent_ino, &index); if (ret < 0) goto out; } if (del_dir_index) { dir_item = btrfs_lookup_dir_index(trans, root, path, parent_ino, name, namelen, index, -1); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (!dir_item) { ret = -ENOENT; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, dir_item); if (ret) goto out; btrfs_release_path(path); /* Update inode size of the parent inode */ key.objectid = parent_ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) goto out; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); inode_size = btrfs_inode_size(path->nodes[0], inode_item); if (inode_size >= namelen) inode_size -= namelen; btrfs_set_inode_size(path->nodes[0], inode_item, inode_size); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } if (del_dir_item) { dir_item = btrfs_lookup_dir_item(trans, root, path, parent_ino, name, namelen, -1); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (!dir_item) { ret = -ENOENT; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, dir_item); if (ret < 0) goto out; btrfs_release_path(path); /* Update inode size of the parent inode */ key.objectid = parent_ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) goto out; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); inode_size = btrfs_inode_size(path->nodes[0], inode_item); if (inode_size >= namelen) inode_size -= namelen; btrfs_set_inode_size(path->nodes[0], inode_item, inode_size); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } out: btrfs_free_path(path); return ret; }
static int traverse_directory(struct btrfs_trans_handle *trans, struct btrfs_root *root, char *dir_name, struct directory_name_entry *dir_head, int out_fd) { int ret = 0; struct btrfs_inode_item cur_inode; struct btrfs_inode_item *inode_item; int count, i, dir_index_cnt; struct direct **files; struct stat st; struct directory_name_entry *dir_entry, *parent_dir_entry; struct direct *cur_file; ino_t parent_inum, cur_inum; ino_t highest_inum = 0; char *parent_dir_name; struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key root_dir_key; u64 root_dir_inode_size = 0; /* Add list for source directory */ dir_entry = malloc(sizeof(struct directory_name_entry)); dir_entry->dir_name = dir_name; dir_entry->path = strdup(dir_name); parent_inum = highest_inum + BTRFS_FIRST_FREE_OBJECTID; dir_entry->inum = parent_inum; list_add_tail(&dir_entry->list, &dir_head->list); btrfs_init_path(&path); root_dir_key.objectid = btrfs_root_dirid(&root->root_item); root_dir_key.offset = 0; btrfs_set_key_type(&root_dir_key, BTRFS_INODE_ITEM_KEY); ret = btrfs_lookup_inode(trans, root, &path, &root_dir_key, 1); if (ret) { fprintf(stderr, "root dir lookup error\n"); return -1; } leaf = path.nodes[0]; inode_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item); root_dir_inode_size = calculate_dir_inode_size(dir_name); btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size); btrfs_mark_buffer_dirty(leaf); btrfs_release_path(&path); do { parent_dir_entry = list_entry(dir_head->list.next, struct directory_name_entry, list); list_del(&parent_dir_entry->list); parent_inum = parent_dir_entry->inum; parent_dir_name = parent_dir_entry->dir_name; if (chdir(parent_dir_entry->path)) { fprintf(stderr, "chdir error for %s\n", parent_dir_name); goto fail_no_files; } count = scandir(parent_dir_entry->path, &files, directory_select, NULL); if (count == -1) { fprintf(stderr, "scandir for %s failed: %s\n", parent_dir_name, strerror (errno)); goto fail; } for (i = 0; i < count; i++) { cur_file = files[i]; if (lstat(cur_file->d_name, &st) == -1) { fprintf(stderr, "lstat failed for file %s\n", cur_file->d_name); goto fail; } cur_inum = ++highest_inum + BTRFS_FIRST_FREE_OBJECTID; ret = add_directory_items(trans, root, cur_inum, parent_inum, cur_file->d_name, &st, &dir_index_cnt); if (ret) { fprintf(stderr, "add_directory_items failed\n"); goto fail; } ret = add_inode_items(trans, root, &st, cur_file->d_name, cur_inum, parent_inum, dir_index_cnt, &cur_inode); if (ret) { fprintf(stderr, "add_inode_items failed\n"); goto fail; } ret = add_xattr_item(trans, root, cur_inum, cur_file->d_name); if (ret) { fprintf(stderr, "add_xattr_item failed\n"); if(ret != -ENOTSUP) goto fail; } if (S_ISDIR(st.st_mode)) { dir_entry = malloc(sizeof(struct directory_name_entry)); dir_entry->dir_name = cur_file->d_name; dir_entry->path = make_path(parent_dir_entry->path, cur_file->d_name); dir_entry->inum = cur_inum; list_add_tail(&dir_entry->list, &dir_head->list); } else if (S_ISREG(st.st_mode)) { ret = add_file_items(trans, root, &cur_inode, cur_inum, parent_inum, &st, cur_file->d_name, out_fd); if (ret) { fprintf(stderr, "add_file_items failed\n"); goto fail; } } else if (S_ISLNK(st.st_mode)) { ret = add_symbolic_link(trans, root, cur_inum, cur_file->d_name); if (ret) { fprintf(stderr, "add_symbolic_link failed\n"); goto fail; } } } free_namelist(files, count); free(parent_dir_entry->path); free(parent_dir_entry); index_cnt = 2; } while (!list_empty(&dir_head->list)); return 0; fail: free_namelist(files, count); fail_no_files: free(parent_dir_entry->path); free(parent_dir_entry); return -1; }
/* * Add dir_item/index for 'parent_ino' if add_backref is true, also insert a * backref from the ino to parent dir and update the nlink(Kernel version does * not do this thing) * * Currently only supports adding link from an inode to another inode. */ 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) { struct btrfs_path *path; struct btrfs_key key; struct btrfs_inode_item *inode_item; u32 nlink; u64 inode_size; u64 ret_index = 0; int ret = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; if (index && *index) { ret_index = *index; } else { ret = btrfs_find_free_dir_index(root, parent_ino, &ret_index); if (ret < 0) goto out; } ret = check_dir_conflict(root, name, namelen, parent_ino, ret_index); if (ret < 0) 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) 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; } 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 */ key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_insert_dir_item(trans, root, name, namelen, parent_ino, &key, type, ret_index); if (ret < 0) goto out; /* Update inode size of the parent inode */ key.objectid = parent_ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) goto out; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); inode_size = btrfs_inode_size(path->nodes[0], inode_item); inode_size += namelen * 2; btrfs_set_inode_size(path->nodes[0], inode_item, inode_size); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); out: btrfs_free_path(path); if (ret == 0 && index) *index = ret_index; return ret; }