From 0c10be57b72901464d4bb9ea7b5383c42b08bcae Mon Sep 17 00:00:00 2001 From: Nitin A Kamble Date: Thu, 9 Jun 2011 18:12:12 -0700 Subject: btrfs-tools: new recipe for tools to operate on btrfs images Added these patches to the recipe to get it to work as desired: Upstream patches from tmp & for-dragonn branches of repository http://git.kernel.org/?p=linux/kernel/git/mason/btrfs-progs-unstable.git Also included the debian patches. Nitin's patch to make the makefile cross friendly And Xin Zhong's patch to improve mkfs.btrfs (From OE-Core rev: 57a2140dc9673c65fea088fbd024fbd002e6937b) Signed-off-by: Nitin A Kamble Signed-off-by: Richard Purdie --- ...-Add-new-feature-to-mkfs.btrfs-to-make-fi.patch | 1108 ++++++++++++++++++++ 1 file changed, 1108 insertions(+) create mode 100644 meta/recipes-devtools/btrfs-tools/btrfs-tools/upstream-tmp/0013-btrfs-progs-Add-new-feature-to-mkfs.btrfs-to-make-fi.patch (limited to 'meta/recipes-devtools/btrfs-tools/btrfs-tools/upstream-tmp/0013-btrfs-progs-Add-new-feature-to-mkfs.btrfs-to-make-fi.patch') diff --git a/meta/recipes-devtools/btrfs-tools/btrfs-tools/upstream-tmp/0013-btrfs-progs-Add-new-feature-to-mkfs.btrfs-to-make-fi.patch b/meta/recipes-devtools/btrfs-tools/btrfs-tools/upstream-tmp/0013-btrfs-progs-Add-new-feature-to-mkfs.btrfs-to-make-fi.patch new file mode 100644 index 0000000000..215106edb8 --- /dev/null +++ b/meta/recipes-devtools/btrfs-tools/btrfs-tools/upstream-tmp/0013-btrfs-progs-Add-new-feature-to-mkfs.btrfs-to-make-fi.patch @@ -0,0 +1,1108 @@ +Upstream-Status: Inappropriate [Backport] +From e3736c698e8b490bea1375576b718a2de6e89603 Mon Sep 17 00:00:00 2001 +From: Donggeun Kim +Date: Thu, 8 Jul 2010 09:17:59 +0000 +Subject: [PATCH 13/15] btrfs-progs: Add new feature to mkfs.btrfs to make file system image file from source directory + +Changes from V1 to V2: +- support extended attributes +- move btrfs_alloc_data_chunk function to volumes.c +- fix an execution error when additional useless parameters are specified +- fix traverse_directory function so that the insertion functions for the common items are invoked in a single point + +The extended attributes is implemented through llistxattr and getxattr function calls. + +Thanks + +Signed-off-by: Donggeun Kim +Signed-off-by: Kyungmin Park +Signed-off-by: Chris Mason +--- + mkfs.c | 864 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + volumes.c | 104 ++++++++ + volumes.h | 3 + + 3 files changed, 947 insertions(+), 24 deletions(-) + +diff --git a/mkfs.c b/mkfs.c +index 04de93a..57c88f9 100644 +--- a/mkfs.c ++++ b/mkfs.c +@@ -29,12 +29,14 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include + #include ++#include + #include "kerncompat.h" + #include "ctree.h" + #include "disk-io.h" +@@ -43,6 +45,15 @@ + #include "utils.h" + #include "version.h" + ++static u64 index_cnt = 2; ++ ++struct directory_name_entry { ++ char *dir_name; ++ char *path; ++ ino_t inum; ++ struct list_head list; ++}; ++ + static u64 parse_size(char *s) + { + int len = strlen(s); +@@ -298,6 +309,7 @@ static void print_usage(void) + fprintf(stderr, "\t -M --mixed mix metadata and data together\n"); + fprintf(stderr, "\t -n --nodesize size of btree nodes\n"); + fprintf(stderr, "\t -s --sectorsize min block allocation\n"); ++ fprintf(stderr, "\t -r --rootdir the source directory\n"); + fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); + exit(1); + } +@@ -355,9 +367,768 @@ static struct option long_options[] = { + { "sectorsize", 1, NULL, 's' }, + { "data", 1, NULL, 'd' }, + { "version", 0, NULL, 'V' }, ++ { "rootdir", 1, NULL, 'r' }, + { 0, 0, 0, 0} + }; + ++static int add_directory_items(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, u64 objectid, ++ ino_t parent_inum, const char *name, ++ struct stat *st, int *dir_index_cnt) ++{ ++ int ret; ++ int name_len; ++ struct btrfs_key location; ++ u8 filetype = 0; ++ ++ name_len = strlen(name); ++ ++ location.objectid = objectid; ++ location.offset = 0; ++ btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); ++ ++ if (S_ISDIR(st->st_mode)) ++ filetype = BTRFS_FT_DIR; ++ if (S_ISREG(st->st_mode)) ++ filetype = BTRFS_FT_REG_FILE; ++ if (S_ISLNK(st->st_mode)) ++ filetype = BTRFS_FT_SYMLINK; ++ ++ ret = btrfs_insert_dir_item(trans, root, name, name_len, ++ parent_inum, &location, ++ filetype, index_cnt); ++ ++ *dir_index_cnt = index_cnt; ++ index_cnt++; ++ ++ return ret; ++} ++ ++static int fill_inode_item(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, ++ struct btrfs_inode_item *dst, struct stat *src) ++{ ++ u64 blocks = 0; ++ u64 sectorsize = root->sectorsize; ++ ++ btrfs_set_stack_inode_generation(dst, trans->transid); ++ btrfs_set_stack_inode_size(dst, src->st_size); ++ btrfs_set_stack_inode_nbytes(dst, 0); ++ btrfs_set_stack_inode_block_group(dst, 0); ++ btrfs_set_stack_inode_nlink(dst, src->st_nlink); ++ btrfs_set_stack_inode_uid(dst, src->st_uid); ++ btrfs_set_stack_inode_gid(dst, src->st_gid); ++ btrfs_set_stack_inode_mode(dst, src->st_mode); ++ btrfs_set_stack_inode_rdev(dst, 0); ++ btrfs_set_stack_inode_flags(dst, 0); ++ btrfs_set_stack_timespec_sec(&dst->atime, src->st_atime); ++ btrfs_set_stack_timespec_nsec(&dst->atime, 0); ++ btrfs_set_stack_timespec_sec(&dst->ctime, src->st_ctime); ++ btrfs_set_stack_timespec_nsec(&dst->ctime, 0); ++ btrfs_set_stack_timespec_sec(&dst->mtime, src->st_mtime); ++ btrfs_set_stack_timespec_nsec(&dst->mtime, 0); ++ btrfs_set_stack_timespec_sec(&dst->otime, 0); ++ btrfs_set_stack_timespec_nsec(&dst->otime, 0); ++ ++ if (S_ISDIR(src->st_mode)) { ++ btrfs_set_stack_inode_size(dst, 0); ++ btrfs_set_stack_inode_nlink(dst, 1); ++ } ++ if (S_ISREG(src->st_mode)) { ++ btrfs_set_stack_inode_size(dst, (u64)src->st_size); ++ if (src->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) ++ btrfs_set_stack_inode_nbytes(dst, src->st_size); ++ else { ++ blocks = src->st_size / sectorsize; ++ if (src->st_size % sectorsize) ++ blocks += 1; ++ blocks *= sectorsize; ++ btrfs_set_stack_inode_nbytes(dst, blocks); ++ } ++ } ++ if (S_ISLNK(src->st_mode)) ++ btrfs_set_stack_inode_nbytes(dst, src->st_size + 1); ++ ++ return 0; ++} ++ ++static int directory_select(const struct direct *entry) ++{ ++ if ((strncmp(entry->d_name, ".", entry->d_reclen) == 0) || ++ (strncmp(entry->d_name, "..", entry->d_reclen) == 0)) ++ return 0; ++ else ++ return 1; ++} ++ ++static u64 calculate_dir_inode_size(char *dirname) ++{ ++ int count, i; ++ struct direct **files, *cur_file; ++ u64 dir_inode_size = 0; ++ ++ count = scandir(dirname, &files, directory_select, NULL); ++ ++ for (i = 0; i < count; i++) { ++ cur_file = files[i]; ++ dir_inode_size += strlen(cur_file->d_name); ++ } ++ ++ dir_inode_size *= 2; ++ return dir_inode_size; ++} ++ ++static int add_inode_items(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, ++ struct stat *st, char *name, ++ u64 self_objectid, ino_t parent_inum, ++ int dir_index_cnt, struct btrfs_inode_item *inode_ret) ++{ ++ int ret; ++ struct btrfs_key inode_key; ++ struct btrfs_inode_item btrfs_inode; ++ u64 objectid; ++ u64 inode_size = 0; ++ int name_len; ++ ++ name_len = strlen(name); ++ fill_inode_item(trans, root, &btrfs_inode, st); ++ objectid = self_objectid; ++ ++ if (S_ISDIR(st->st_mode)) { ++ inode_size = calculate_dir_inode_size(name); ++ btrfs_set_stack_inode_size(&btrfs_inode, inode_size); ++ } ++ ++ inode_key.objectid = objectid; ++ inode_key.offset = 0; ++ btrfs_set_key_type(&inode_key, BTRFS_INODE_ITEM_KEY); ++ ++ ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); ++ if (ret) ++ goto fail; ++ ++ ret = btrfs_insert_inode_ref(trans, root, name, name_len, ++ objectid, parent_inum, dir_index_cnt); ++ if (ret) ++ goto fail; ++ ++ *inode_ret = btrfs_inode; ++fail: ++ return ret; ++} ++ ++static int add_xattr_item(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, u64 objectid, ++ const char *file_name) ++{ ++ int ret; ++ int cur_name_len; ++ char xattr_list[XATTR_LIST_MAX]; ++ char *cur_name; ++ char cur_value[XATTR_SIZE_MAX]; ++ char delimiter = '\0'; ++ char *next_location = xattr_list; ++ ++ ret = llistxattr(file_name, xattr_list, XATTR_LIST_MAX); ++ if (ret < 0) { ++ fprintf(stderr, "get a list of xattr failed for %s\n", ++ file_name); ++ return ret; ++ } ++ if (ret == 0) ++ return ret; ++ ++ cur_name = strtok(xattr_list, &delimiter); ++ while (cur_name != NULL) { ++ cur_name_len = strlen(cur_name); ++ next_location += cur_name_len + 1; ++ ++ ret = getxattr(file_name, cur_name, cur_value, XATTR_SIZE_MAX); ++ if (ret < 0) { ++ fprintf(stderr, "get a xattr value failed for %s\n", ++ cur_name); ++ } ++ ++ ret = btrfs_insert_xattr_item(trans, root, cur_name, ++ cur_name_len, cur_value, ++ ret, objectid); ++ if (ret) { ++ fprintf(stderr, "insert a xattr item failed for %s\n", ++ file_name); ++ } ++ ++ cur_name = strtok(next_location, &delimiter); ++ } ++ ++ return ret; ++} ++ ++static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes, ++ u64 hint_byte, struct btrfs_key *ins) ++{ ++ u64 start; ++ u64 end; ++ u64 last = hint_byte; ++ int ret; ++ int wrapped = 0; ++ struct btrfs_block_group_cache *cache; ++ ++ while (1) { ++ ret = find_first_extent_bit(&root->fs_info->free_space_cache, ++ last, &start, &end, EXTENT_DIRTY); ++ if (ret) { ++ if (wrapped++ == 0) { ++ last = 0; ++ continue; ++ } else { ++ goto fail; ++ } ++ } ++ ++ start = max(last, start); ++ last = end + 1; ++ if (last - start < num_bytes) ++ continue; ++ ++ last = start + num_bytes; ++ if (test_range_bit(&root->fs_info->pinned_extents, ++ start, last - 1, EXTENT_DIRTY, 0)) ++ continue; ++ ++ cache = btrfs_lookup_block_group(root->fs_info, start); ++ BUG_ON(!cache); ++ if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM || ++ last > cache->key.objectid + cache->key.offset) { ++ last = cache->key.objectid + cache->key.offset; ++ continue; ++ } ++ ++ if (cache->flags & (BTRFS_BLOCK_GROUP_SYSTEM | ++ BTRFS_BLOCK_GROUP_METADATA)) { ++ last = cache->key.objectid + cache->key.offset; ++ continue; ++ } ++ ++ clear_extent_dirty(&root->fs_info->free_space_cache, ++ start, start + num_bytes - 1, 0); ++ ++ ins->objectid = start; ++ ins->offset = num_bytes; ++ ins->type = BTRFS_EXTENT_ITEM_KEY; ++ return 0; ++ } ++fail: ++ fprintf(stderr, "not enough free space\n"); ++ return -ENOSPC; ++} ++ ++static int record_file_extent(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, u64 objectid, ++ struct btrfs_inode_item *inode, ++ u64 file_pos, u64 disk_bytenr, ++ u64 num_bytes) ++{ ++ int ret; ++ struct btrfs_fs_info *info = root->fs_info; ++ struct btrfs_root *extent_root = info->extent_root; ++ struct extent_buffer *leaf; ++ struct btrfs_file_extent_item *fi; ++ struct btrfs_key ins_key; ++ struct btrfs_path path; ++ struct btrfs_extent_item *ei; ++ ++ btrfs_init_path(&path); ++ ++ ins_key.objectid = objectid; ++ ins_key.offset = 0; ++ btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY); ++ ret = btrfs_insert_empty_item(trans, root, &path, &ins_key, ++ sizeof(*fi)); ++ if (ret) ++ goto fail; ++ leaf = path.nodes[0]; ++ fi = btrfs_item_ptr(leaf, path.slots[0], ++ struct btrfs_file_extent_item); ++ btrfs_set_file_extent_generation(leaf, fi, trans->transid); ++ btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); ++ btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr); ++ btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes); ++ btrfs_set_file_extent_offset(leaf, fi, 0); ++ btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes); ++ btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes); ++ btrfs_set_file_extent_compression(leaf, fi, 0); ++ btrfs_set_file_extent_encryption(leaf, fi, 0); ++ btrfs_set_file_extent_other_encoding(leaf, fi, 0); ++ btrfs_mark_buffer_dirty(leaf); ++ ++ btrfs_release_path(root, &path); ++ ++ ins_key.objectid = disk_bytenr; ++ ins_key.offset = num_bytes; ++ ins_key.type = BTRFS_EXTENT_ITEM_KEY; ++ ++ ret = btrfs_insert_empty_item(trans, extent_root, &path, ++ &ins_key, sizeof(*ei)); ++ if (ret == 0) { ++ leaf = path.nodes[0]; ++ ei = btrfs_item_ptr(leaf, path.slots[0], ++ struct btrfs_extent_item); ++ ++ btrfs_set_extent_refs(leaf, ei, 0); ++ btrfs_set_extent_generation(leaf, ei, trans->transid); ++ btrfs_set_extent_flags(leaf, ei, BTRFS_EXTENT_FLAG_DATA); ++ ++ btrfs_mark_buffer_dirty(leaf); ++ ret = btrfs_update_block_group(trans, root, disk_bytenr, ++ num_bytes, 1, 0); ++ if (ret) ++ goto fail; ++ } else if (ret != -EEXIST) { ++ goto fail; ++ } ++ ++ ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0, ++ root->root_key.objectid, ++ objectid, 0); ++fail: ++ btrfs_release_path(root, &path); ++ return ret; ++} ++ ++static int add_symbolic_link(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, ++ u64 objectid, const char *path_name) ++{ ++ int ret; ++ u64 sectorsize = root->sectorsize; ++ char *buf = malloc(sectorsize); ++ ++ ret = readlink(path_name, buf, sectorsize); ++ if (ret <= 0) { ++ fprintf(stderr, "readlink failed for %s\n", path_name); ++ goto fail; ++ } ++ if (ret > sectorsize) { ++ fprintf(stderr, "symlink too long for %s", path_name); ++ ret = -1; ++ goto fail; ++ } ++ ret = btrfs_insert_inline_extent(trans, root, objectid, 0, ++ buf, ret + 1); ++fail: ++ free(buf); ++ return ret; ++} ++ ++static int add_file_items(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, ++ struct btrfs_inode_item *btrfs_inode, u64 objectid, ++ ino_t parent_inum, struct stat *st, ++ const char *path_name, int out_fd) ++{ ++ int ret; ++ ssize_t ret_read; ++ u64 bytes_read = 0; ++ char *buffer = NULL; ++ struct btrfs_key key; ++ int blocks; ++ u32 sectorsize = root->sectorsize; ++ u64 first_block = 0; ++ u64 num_blocks = 0; ++ int fd; ++ ++ fd = open(path_name, O_RDONLY); ++ if (fd == -1) { ++ fprintf(stderr, "%s open failed\n", path_name); ++ goto end; ++ } ++ ++ blocks = st->st_size / sectorsize; ++ if (st->st_size % sectorsize) ++ blocks += 1; ++ ++ if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) { ++ buffer = malloc(st->st_size); ++ ret_read = pread64(fd, buffer, st->st_size, bytes_read); ++ if (ret_read == -1) { ++ fprintf(stderr, "%s read failed\n", path_name); ++ goto end; ++ } ++ ++ ret = btrfs_insert_inline_extent(trans, root, objectid, 0, ++ buffer, st->st_size); ++ goto end; ++ } ++ ++ ret = custom_alloc_extent(root, blocks * sectorsize, 0, &key); ++ if (ret) ++ goto end; ++ ++ first_block = key.objectid; ++ bytes_read = 0; ++ buffer = malloc(sectorsize); ++ ++ do { ++ memset(buffer, 0, sectorsize); ++ ret_read = pread64(fd, buffer, sectorsize, bytes_read); ++ if (ret_read == -1) { ++ fprintf(stderr, "%s read failed\n", path_name); ++ goto end; ++ } ++ ++ ret = pwrite64(out_fd, buffer, sectorsize, ++ first_block + bytes_read); ++ if (ret != sectorsize) { ++ fprintf(stderr, "output file write failed\n"); ++ goto end; ++ } ++ ++ /* checksum for file data */ ++ ret = btrfs_csum_file_block(trans, root->fs_info->csum_root, ++ first_block + (blocks * sectorsize), ++ first_block + bytes_read, ++ buffer, sectorsize); ++ if (ret) { ++ fprintf(stderr, "%s checksum failed\n", path_name); ++ goto end; ++ } ++ ++ bytes_read += ret_read; ++ num_blocks++; ++ } while (ret_read == sectorsize); ++ ++ if (num_blocks > 0) { ++ ret = record_file_extent(trans, root, objectid, btrfs_inode, ++ first_block, first_block, ++ blocks * sectorsize); ++ if (ret) ++ goto end; ++ } ++ ++end: ++ if (buffer) ++ free(buffer); ++ close(fd); ++ return ret; ++} ++ ++static char *make_path(char *dir, char *name) ++{ ++ char *path; ++ ++ path = malloc(strlen(dir) + strlen(name) + 2); ++ if (!path) ++ return NULL; ++ strcpy(path, dir); ++ if (dir[strlen(dir) - 1] != '/') ++ strcat(path, "/"); ++ strcat(path, name); ++ return path; ++} ++ ++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 = malloc(strlen(dir_name) + 1); ++ strcpy(dir_entry->path, 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"); ++ goto fail; ++ } ++ ++ 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(root, &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; ++ } ++ ++ count = scandir(parent_dir_entry->path, &files, ++ directory_select, NULL); ++ ++ 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"); ++ 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(parent_dir_entry->path); ++ free(parent_dir_entry); ++ ++ index_cnt = 2; ++ ++ } while (!list_empty(&dir_head->list)); ++ ++ return 0; ++fail: ++ free(parent_dir_entry->path); ++ free(parent_dir_entry); ++ return -1; ++} ++ ++static int open_target(char *output_name) ++{ ++ int output_fd; ++ output_fd = open(output_name, O_CREAT | O_RDWR | O_TRUNC, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ ++ return output_fd; ++} ++ ++static int create_chunks(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, u64 num_of_meta_chunks, ++ u64 size_of_data) ++{ ++ u64 chunk_start; ++ u64 chunk_size; ++ u64 meta_type = BTRFS_BLOCK_GROUP_METADATA; ++ u64 data_type = BTRFS_BLOCK_GROUP_DATA; ++ u64 minimum_data_chunk_size = 64 * 1024 * 1024; ++ u64 i; ++ int ret; ++ ++ for (i = 0; i < num_of_meta_chunks; i++) { ++ ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, ++ &chunk_start, &chunk_size, meta_type); ++ BUG_ON(ret); ++ ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0, ++ meta_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, ++ chunk_start, chunk_size); ++ BUG_ON(ret); ++ set_extent_dirty(&root->fs_info->free_space_cache, ++ chunk_start, chunk_start + chunk_size - 1, 0); ++ } ++ ++ if (size_of_data < minimum_data_chunk_size) ++ size_of_data = minimum_data_chunk_size; ++ ret = btrfs_alloc_data_chunk(trans, root->fs_info->extent_root, ++ &chunk_start, size_of_data, data_type); ++ BUG_ON(ret); ++ ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0, ++ data_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, ++ chunk_start, size_of_data); ++ BUG_ON(ret); ++ set_extent_dirty(&root->fs_info->free_space_cache, ++ chunk_start, chunk_start + size_of_data - 1, 0); ++ return ret; ++} ++ ++static int make_image(char *source_dir, struct btrfs_root *root, int out_fd) ++{ ++ int ret; ++ struct btrfs_trans_handle *trans; ++ ++ struct stat root_st; ++ int root_len; ++ ++ struct directory_name_entry dir_head; ++ ++ ret = lstat(source_dir, &root_st); ++ if (ret) { ++ fprintf(stderr, "unable to lstat the %s\n", source_dir); ++ goto fail; ++ } ++ ++ root_len = strlen(source_dir); ++ ++ INIT_LIST_HEAD(&dir_head.list); ++ ++ trans = btrfs_start_transaction(root, 1); ++ ret = traverse_directory(trans, root, source_dir, &dir_head, out_fd); ++ if (ret) { ++ fprintf(stderr, "unable to traverse_directory\n"); ++ goto fail; ++ } ++ btrfs_commit_transaction(trans, root); ++ ++ printf("Making image is completed.\n"); ++ return 0; ++fail: ++ fprintf(stderr, "Making image is aborted.\n"); ++ return -1; ++} ++ ++static u64 size_sourcedir(char *dir_name, u64 sectorsize, ++ u64 *num_of_meta_chunks_ret, u64 *size_of_data_ret) ++{ ++ u64 dir_size = 0; ++ u64 total_size = 0; ++ int ret; ++ char command[1024]; ++ char path[512]; ++ char *file_name = "temp_file"; ++ FILE *file; ++ u64 minimum_data_size = 256 * 1024 * 1024; /* 256MB */ ++ u64 default_chunk_size = 8 * 1024 * 1024; /* 8MB */ ++ u64 allocated_meta_size = 8 * 1024 * 1024; /* 8MB */ ++ u64 allocated_total_size = 20 * 1024 * 1024; /* 20MB */ ++ u64 num_of_meta_chunks = 0; ++ u64 num_of_allocated_meta_chunks = ++ allocated_meta_size / default_chunk_size; ++ ++ ret = sprintf(command, "du -B 4096 -s "); ++ if (ret < 0) { ++ fprintf(stderr, "error executing sprintf for du command\n"); ++ return -1; ++ } ++ strcat(command, dir_name); ++ strcat(command, " > "); ++ strcat(command, file_name); ++ ret = system(command); ++ ++ file = fopen(file_name, "r"); ++ ret = fscanf(file, "%lld %s\n", &dir_size, path); ++ fclose(file); ++ remove(file_name); ++ ++ dir_size *= sectorsize; ++ *size_of_data_ret = dir_size; ++ ++ num_of_meta_chunks = (dir_size / 2) / default_chunk_size; ++ if (((dir_size / 2) % default_chunk_size) != 0) ++ num_of_meta_chunks++; ++ if (num_of_meta_chunks <= num_of_allocated_meta_chunks) ++ num_of_meta_chunks = 0; ++ else ++ num_of_meta_chunks -= num_of_allocated_meta_chunks; ++ ++ total_size = allocated_total_size + dir_size + ++ (num_of_meta_chunks * default_chunk_size); ++ ++ *num_of_meta_chunks_ret = num_of_meta_chunks; ++ ++ if (total_size < minimum_data_size) ++ total_size = minimum_data_size; ++ ++ return total_size; ++} ++ ++static int zero_output_file(int out_fd, u64 size, u32 sectorsize) ++{ ++ int len = sectorsize; ++ int loop_num = size / sectorsize; ++ u64 location = 0; ++ char *buf = malloc(len); ++ int ret = 0, i; ++ ssize_t written; ++ ++ if (!buf) ++ return -ENOMEM; ++ memset(buf, 0, len); ++ for (i = 0; i < loop_num; i++) { ++ written = pwrite64(out_fd, buf, len, location); ++ if (written != len) ++ ret = -EIO; ++ location += sectorsize; ++ } ++ free(buf); ++ return ret; ++} ++ + int main(int ac, char **av) + { + char *file; +@@ -385,9 +1156,15 @@ int main(int ac, char **av) + int data_profile_opt = 0; + int metadata_profile_opt = 0; + ++ char *source_dir = NULL; ++ int source_dir_set = 0; ++ char *output = "output.img"; ++ u64 num_of_meta_chunks = 0; ++ u64 size_of_data = 0; ++ + while(1) { + int c; +- c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:VM", long_options, ++ c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:r:VM", long_options, + &option_index); + if (c < 0) + break; +@@ -430,6 +1207,10 @@ int main(int ac, char **av) + case 'V': + print_version(); + break; ++ case 'r': ++ source_dir = optarg; ++ source_dir_set = 1; ++ break; + default: + print_usage(); + } +@@ -443,6 +1224,8 @@ int main(int ac, char **av) + fprintf(stderr, "Illegal nodesize %u\n", nodesize); + exit(1); + } ++ if (source_dir_set) ++ ac++; + ac = ac - optind; + if (ac == 0) + print_usage(); +@@ -450,28 +1233,47 @@ int main(int ac, char **av) + printf("\nWARNING! - %s IS EXPERIMENTAL\n", BTRFS_BUILD_VERSION); + printf("WARNING! - see http://btrfs.wiki.kernel.org before using\n\n"); + +- file = av[optind++]; +- ret = check_mounted(file); +- if (ret < 0) { +- fprintf(stderr, "error checking %s mount status\n", file); +- exit(1); +- } +- if (ret == 1) { +- fprintf(stderr, "%s is mounted\n", file); +- exit(1); +- } +- ac--; +- fd = open(file, O_RDWR); +- if (fd < 0) { +- fprintf(stderr, "unable to open %s\n", file); +- exit(1); ++ if (source_dir == 0) { ++ file = av[optind++]; ++ ret = check_mounted(file); ++ if (ret < 0) { ++ fprintf(stderr, "error checking %s mount status\n", file); ++ exit(1); ++ } ++ if (ret == 1) { ++ fprintf(stderr, "%s is mounted\n", file); ++ exit(1); ++ } ++ ac--; ++ fd = open(file, O_RDWR); ++ if (fd < 0) { ++ fprintf(stderr, "unable to open %s\n", file); ++ exit(1); ++ } ++ first_fd = fd; ++ first_file = file; ++ ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count, &mixed); ++ if (block_count == 0) ++ block_count = dev_block_count; ++ } else { ++ ac = 0; ++ fd = open_target(output); ++ if (fd < 0) { ++ fprintf(stderr, "unable to open the %s\n", file); ++ exit(1); ++ } ++ ++ file = output; ++ first_fd = fd; ++ first_file = file; ++ block_count = size_sourcedir(source_dir, sectorsize, ++ &num_of_meta_chunks, &size_of_data); ++ ret = zero_output_file(fd, block_count, sectorsize); ++ if (ret) { ++ fprintf(stderr, "unable to zero the output file\n"); ++ exit(1); ++ } + } +- first_fd = fd; +- first_file = file; +- ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count, +- &mixed); +- if (block_count == 0) +- block_count = dev_block_count; + if (mixed) { + if (!metadata_profile_opt) + metadata_profile = 0; +@@ -558,9 +1360,11 @@ int main(int ac, char **av) + } + + raid_groups: +- ret = create_raid_groups(trans, root, data_profile, ++ if (!source_dir_set) { ++ ret = create_raid_groups(trans, root, data_profile, + metadata_profile, mixed); +- BUG_ON(ret); ++ BUG_ON(ret); ++ } + + ret = create_data_reloc_tree(trans, root); + BUG_ON(ret); +@@ -580,6 +1384,18 @@ raid_groups: + + printf("%s\n", BTRFS_BUILD_VERSION); + btrfs_commit_transaction(trans, root); ++ ++ if (source_dir_set) { ++ trans = btrfs_start_transaction(root, 1); ++ ret = create_chunks(trans, root, ++ num_of_meta_chunks, size_of_data); ++ BUG_ON(ret); ++ btrfs_commit_transaction(trans, root); ++ ++ ret = make_image(source_dir, root, fd); ++ BUG_ON(ret); ++ } ++ + ret = close_ctree(root); + BUG_ON(ret); + +diff --git a/volumes.c b/volumes.c +index 7671855..4bb77e2 100644 +--- a/volumes.c ++++ b/volumes.c +@@ -857,6 +857,110 @@ again: + return ret; + } + ++int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans, ++ struct btrfs_root *extent_root, u64 *start, ++ u64 num_bytes, u64 type) ++{ ++ u64 dev_offset; ++ struct btrfs_fs_info *info = extent_root->fs_info; ++ struct btrfs_root *chunk_root = extent_root->fs_info->chunk_root; ++ struct btrfs_stripe *stripes; ++ struct btrfs_device *device = NULL; ++ struct btrfs_chunk *chunk; ++ struct list_head *dev_list = &extent_root->fs_info->fs_devices->devices; ++ struct list_head *cur; ++ struct map_lookup *map; ++ u64 physical; ++ u64 calc_size = 8 * 1024 * 1024; ++ int num_stripes = 1; ++ int sub_stripes = 0; ++ int ret; ++ int index; ++ int stripe_len = 64 * 1024; ++ struct btrfs_key key; ++ ++ key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; ++ key.type = BTRFS_CHUNK_ITEM_KEY; ++ ret = find_next_chunk(chunk_root, BTRFS_FIRST_CHUNK_TREE_OBJECTID, ++ &key.offset); ++ if (ret) ++ return ret; ++ ++ chunk = kmalloc(btrfs_chunk_item_size(num_stripes), GFP_NOFS); ++ if (!chunk) ++ return -ENOMEM; ++ ++ map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); ++ if (!map) { ++ kfree(chunk); ++ return -ENOMEM; ++ } ++ ++ stripes = &chunk->stripe; ++ calc_size = num_bytes; ++ ++ index = 0; ++ cur = dev_list->next; ++ device = list_entry(cur, struct btrfs_device, dev_list); ++ ++ while (index < num_stripes) { ++ struct btrfs_stripe *stripe; ++ ++ ret = btrfs_alloc_dev_extent(trans, device, ++ info->chunk_root->root_key.objectid, ++ BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset, ++ calc_size, &dev_offset); ++ BUG_ON(ret); ++ ++ device->bytes_used += calc_size; ++ ret = btrfs_update_device(trans, device); ++ BUG_ON(ret); ++ ++ map->stripes[index].dev = device; ++ map->stripes[index].physical = dev_offset; ++ stripe = stripes + index; ++ btrfs_set_stack_stripe_devid(stripe, device->devid); ++ btrfs_set_stack_stripe_offset(stripe, dev_offset); ++ memcpy(stripe->dev_uuid, device->uuid, BTRFS_UUID_SIZE); ++ physical = dev_offset; ++ index++; ++ } ++ ++ /* key was set above */ ++ btrfs_set_stack_chunk_length(chunk, num_bytes); ++ btrfs_set_stack_chunk_owner(chunk, extent_root->root_key.objectid); ++ btrfs_set_stack_chunk_stripe_len(chunk, stripe_len); ++ btrfs_set_stack_chunk_type(chunk, type); ++ btrfs_set_stack_chunk_num_stripes(chunk, num_stripes); ++ btrfs_set_stack_chunk_io_align(chunk, stripe_len); ++ btrfs_set_stack_chunk_io_width(chunk, stripe_len); ++ btrfs_set_stack_chunk_sector_size(chunk, extent_root->sectorsize); ++ btrfs_set_stack_chunk_sub_stripes(chunk, sub_stripes); ++ map->sector_size = extent_root->sectorsize; ++ map->stripe_len = stripe_len; ++ map->io_align = stripe_len; ++ map->io_width = stripe_len; ++ map->type = type; ++ map->num_stripes = num_stripes; ++ map->sub_stripes = sub_stripes; ++ ++ ret = btrfs_insert_item(trans, chunk_root, &key, chunk, ++ btrfs_chunk_item_size(num_stripes)); ++ BUG_ON(ret); ++ *start = key.offset; ++ ++ map->ce.start = key.offset; ++ map->ce.size = num_bytes; ++ ++ ret = insert_existing_cache_extent( ++ &extent_root->fs_info->mapping_tree.cache_tree, ++ &map->ce); ++ BUG_ON(ret); ++ ++ kfree(chunk); ++ return ret; ++} ++ + void btrfs_mapping_init(struct btrfs_mapping_tree *tree) + { + cache_tree_init(&tree->cache_tree); +diff --git a/volumes.h b/volumes.h +index bb78751..93b0e48 100644 +--- a/volumes.h ++++ b/volumes.h +@@ -107,6 +107,9 @@ int btrfs_read_chunk_tree(struct btrfs_root *root); + int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, + struct btrfs_root *extent_root, u64 *start, + u64 *num_bytes, u64 type); ++int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans, ++ struct btrfs_root *extent_root, u64 *start, ++ u64 num_bytes, u64 type); + int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf); + int btrfs_add_device(struct btrfs_trans_handle *trans, + struct btrfs_root *root, +-- +1.7.2.3 + -- cgit v1.2.3-54-g00ecf