Yan Zheng
2008-Jan-07 08:07 UTC
[Btrfs-devel][PATCH]Add rollback support for the converter
Hello, This patch adds rollback support for the converter, the converter can roll back a conversion if the image file haven't been modified. In addition, I rearrange some codes in convert.c and add a few comments. Regards YZ --- diff -r 12138d4beeb0 convert.c --- a/convert.c Fri Jan 04 11:29:55 2008 -0500 +++ b/convert.c Mon Jan 07 23:35:25 2008 +0800 @@ -33,6 +33,7 @@ #include "ctree.h" #include "disk-io.h" #include "transaction.h" +#include "crc32c.h" #include "utils.h" #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" @@ -43,7 +44,7 @@ * Open Ext2fs in readonly mode, read block allocation bitmap and * inode bitmap into memory. */ -static int open_ext2fs(char *name, ext2_filsys *ret_fs) +static int open_ext2fs(const char *name, ext2_filsys *ret_fs) { int mnt_flags; errcode_t ret; @@ -88,9 +89,6 @@ static int close_ext2fs(ext2_filsys fs) return 0; } -/* - * Stupid algorithm, search forward starting from the first free block. - */ static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret) { blk_t block; @@ -115,6 +113,7 @@ static int custom_alloc_extent(struct bt { ext2_filsys fs = (ext2_filsys)root->fs_info->priv_data; u32 blocksize = fs->blocksize; + u64 first = 0; u64 block; u64 bytenr; int ret; @@ -127,6 +126,12 @@ static int custom_alloc_extent(struct bt if (ret) return ret; + /* all free blocks are pinned */ + if (first == block) + return -ENOSPC; + if (first == 0) + first = block; + bytenr = block * blocksize; if (!test_range_bit(&root->fs_info->pinned_extents, bytenr, bytenr + blocksize - 1, EXTENT_DIRTY, 0)) @@ -167,7 +172,7 @@ struct dir_iterate_data { struct btrfs_root *root; struct btrfs_inode_item *inode; u64 objectid; - u32 parent; + u64 parent; int errcode; }; @@ -281,10 +286,9 @@ fail: } /* - * record a single file extent. do all required works: - * 1. insert a btrfs_file_extent_item into fs tree. - * 2. compute checksum and insert btrfs_csum_item into fs tree. - * 3. insert extent item and extent backref into extent tree. + * Record a file extent. Do all the required works, such as inserting + * file extent item, inserting extent item and backref item into extent + * tree and updating block accounting. */ static int record_file_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, @@ -446,8 +450,7 @@ static int create_file_extents(struct bt static int create_file_extents(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, struct btrfs_inode_item *btrfs_inode, - ext2_filsys ext2_fs, ext2_ino_t ext2_ino, - struct ext2_inode *ext2_inode) + ext2_filsys ext2_fs, ext2_ino_t ext2_ino) { int ret; char *buffer = NULL; @@ -455,6 +458,7 @@ static int create_file_extents(struct bt u32 last_block; u32 sectorsize = root->sectorsize; u64 inode_size = btrfs_stack_inode_size(btrfs_inode); + u32 inode_flags = btrfs_stack_inode_flags(btrfs_inode); struct blk_iterate_data data = { .trans = trans, .root = root, @@ -466,6 +470,9 @@ static int create_file_extents(struct bt .checksum = 1, .errcode = 0, }; + + if (inode_flags & BTRFS_INODE_NODATASUM) + data.checksum = 0; err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY, NULL, __block_iterate_proc, &data); @@ -495,7 +502,7 @@ static int create_file_extents(struct bt } else if (data.num_blocks > 0) { ret = record_file_blocks(trans, root, objectid, btrfs_inode, data.first_block, data.disk_block, - data.num_blocks, 1); + data.num_blocks, data.checksum); if (ret) goto fail; } @@ -504,7 +511,7 @@ static int create_file_extents(struct bt if (last_block > data.first_block) { ret = record_file_blocks(trans, root, objectid, btrfs_inode, data.first_block, 0, last_block - - data.first_block, 1); + data.first_block, data.checksum); } fail: if (buffer) @@ -527,8 +534,8 @@ static int create_symbol_link(struct btr if (ext2fs_inode_data_blocks(ext2_fs, ext2_inode)) { btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1); - ret = create_file_extents(trans, root, objectid, btrfs_inode, - ext2_fs, ext2_ino, ext2_inode); + ret = create_file_extents(trans, root, objectid, + btrfs_inode, ext2_fs, ext2_ino); btrfs_set_stack_inode_size(btrfs_inode, inode_size); return ret; } @@ -558,7 +565,7 @@ static int copy_inode_item(struct btrfs_ static int copy_inode_item(struct btrfs_inode_item *dst, struct ext2_inode *src) { - btrfs_set_stack_inode_generation(dst, 0); + btrfs_set_stack_inode_generation(dst, 1); btrfs_set_stack_inode_size(dst, src->i_size); btrfs_set_stack_inode_nblocks(dst, src->i_blocks); btrfs_set_stack_inode_block_group(dst, 0); @@ -602,7 +609,8 @@ static int copy_inode_item(struct btrfs_ */ static int copy_single_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, - ext2_filsys ext2_fs, ext2_ino_t ext2_ino) + ext2_filsys ext2_fs, ext2_ino_t ext2_ino, + int datacsum) { int ret; errcode_t err; @@ -621,20 +629,24 @@ static int copy_single_inode(struct btrf } copy_inode_item(&btrfs_inode, &ext2_inode); - - ret = 0; + if (!datacsum && S_ISREG(ext2_inode.i_mode)) { + u32 flags = btrfs_stack_inode_flags(&btrfs_inode) | + BTRFS_INODE_NODATASUM; + btrfs_set_stack_inode_flags(&btrfs_inode, flags); + } + switch (ext2_inode.i_mode & S_IFMT) { case S_IFREG: ret = create_file_extents(trans, root, objectid, &btrfs_inode, - ext2_fs, ext2_ino, &ext2_inode); + ext2_fs, ext2_ino); + break; + case S_IFDIR: + ret = create_dir_entries(trans, root, objectid, &btrfs_inode, + ext2_fs, ext2_ino); break; case S_IFLNK: ret = create_symbol_link(trans, root, objectid, &btrfs_inode, ext2_fs, ext2_ino, &ext2_inode); - break; - case S_IFDIR: - ret = create_dir_entries(trans, root, objectid, - &btrfs_inode, ext2_fs, ext2_ino); break; default: ret = 0; @@ -679,7 +691,8 @@ fail: /* * scan ext2's inode bitmap and copy all used inode. */ -static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs) +static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs, + int datacsum) { int ret; ext2_ino_t ext2_ino; @@ -699,7 +712,7 @@ static int copy_inodes(struct btrfs_root continue; objectid = ext2_ino + INO_OFFSET; ret = copy_single_inode(trans, root, objectid, - ext2_fs, ext2_ino); + ext2_fs, ext2_ino, datacsum); if (ret) return ret; } @@ -716,7 +729,7 @@ static int copy_inodes(struct btrfs_root return ret; } -static int lookup_extent_ref(struct btrfs_trans_handle *trans, +static int lookup_extent_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num_bytes) { @@ -726,8 +739,8 @@ static int lookup_extent_ref(struct btrf btrfs_init_path(&path); key.objectid = bytenr; - key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = num_bytes; + btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path, 0, 0); btrfs_release_path(root, &path); @@ -738,6 +751,9 @@ static int lookup_extent_ref(struct btrf * Construct a range of ext2fs image file. * scan block allocation bitmap, find all blocks used by the ext2fs * in this range and create file extents that point to these blocks. + * + * Note: Before calling the function, no file extent points to blocks + * in this range */ static int create_image_file_range(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, @@ -758,7 +774,7 @@ static int create_image_file_range(struc .first_block = block, .disk_block = block, .num_blocks = 0, - .checksum = 1, + .checksum = 0, .errcode = 0, }; @@ -767,7 +783,7 @@ static int create_image_file_range(struc continue; /* the bit may be set by us, check extent tree */ bytenr = (u64)block * blocksize; - ret = lookup_extent_ref(trans, root, bytenr, blocksize); + ret = lookup_extent_item(trans, root, bytenr, blocksize); if (ret < 0) goto fail; if (ret == 0) @@ -783,7 +799,7 @@ static int create_image_file_range(struc if (data.num_blocks > 0) { ret = record_file_blocks(trans, root, objectid, inode, data.first_block, data.disk_block, - data.num_blocks, 1); + data.num_blocks, 0); if (ret) return ret; data.first_block += data.num_blocks; @@ -791,7 +807,7 @@ static int create_image_file_range(struc if (last_block > data.first_block) { ret = record_file_blocks(trans, root, objectid, inode, data.first_block, 0, last_block - - data.first_block, 1); + data.first_block, 0); if (ret) return ret; } @@ -802,8 +818,8 @@ fail: /* * Create the ext2fs image file. */ -static int create_ext2_image(struct btrfs_root *root, char *name, - int namelen, ext2_filsys ext2_fs) +static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs, + const char *name) { int ret; struct btrfs_key key; @@ -837,7 +853,8 @@ static int create_ext2_image(struct btrf btrfs_set_stack_inode_size(&btrfs_inode, total_bytes); btrfs_set_stack_inode_nlink(&btrfs_inode, 1); btrfs_set_stack_inode_nblocks(&btrfs_inode, 0); - btrfs_set_stack_inode_mode(&btrfs_inode, S_IFREG | 0444); + btrfs_set_stack_inode_mode(&btrfs_inode, S_IFREG | 0400); + btrfs_set_stack_inode_flags(&btrfs_inode, BTRFS_INODE_NODATASUM); btrfs_init_path(&path); trans = btrfs_start_transaction(root, 1); @@ -862,7 +879,7 @@ static int create_ext2_image(struct btrf goto fail; ret = record_file_extent(trans, root, objectid, &btrfs_inode, last_byte, - new_block, sectorsize, 1); + new_block, sectorsize, 0); if (ret) goto fail; } @@ -902,6 +919,11 @@ again: continue; } + /* + * Check backref to distinguish extent items for normal + * files (files that correspond to files in Ext2fs) from + * extent items for ctree blocks. + */ bytenr = key.objectid; num_bytes = key.offset; file_extent = 0; @@ -944,7 +966,7 @@ again: goto fail; } ret = record_file_extent(trans, root, objectid, &btrfs_inode, - bytenr, bytenr, num_bytes, 1); + bytenr, bytenr, num_bytes, 0); if (ret) goto fail; last_byte = bytenr + num_bytes; @@ -958,6 +980,14 @@ again: goto fail; } + /* + * otime isn't used currently, so we can store some data in it. + * These data are used by do_rollback to check whether the image + * file have been modified. + */ + btrfs_set_stack_timespec_sec(&btrfs_inode.otime, trans->transid); + btrfs_set_stack_timespec_nsec(&btrfs_inode.otime, + total_bytes / sectorsize); ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); if (ret) goto fail; @@ -965,12 +995,13 @@ again: location.objectid = objectid; location.offset = 0; btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY); - ret = btrfs_insert_dir_item(trans, root, name, namelen, + ret = btrfs_insert_dir_item(trans, root, name, strlen(name), btrfs_root_dirid(&root->root_item), &location, EXT2_FT_REG_FILE); if (ret) goto fail; - ret = btrfs_insert_inode_ref(trans, root, name, namelen, objectid, + ret = btrfs_insert_inode_ref(trans, root, name, strlen(name), + objectid, btrfs_root_dirid(&root->root_item)); if (ret) goto fail; @@ -981,8 +1012,7 @@ fail: return ret; } -struct btrfs_root *create_subvol(struct btrfs_root *root, - char *name, int namelen) +struct btrfs_root *create_subvol(struct btrfs_root *root, const char *name) { int ret; u64 objectid; @@ -1017,12 +1047,13 @@ struct btrfs_root *create_subvol(struct if (ret) goto fail; location.offset = (u64)-1; - ret = btrfs_insert_dir_item(trans, tree_root, name, namelen, + ret = btrfs_insert_dir_item(trans, tree_root, name, strlen(name), btrfs_super_root_dir(&fs_info->super_copy), &location, BTRFS_FT_DIR); if (ret) goto fail; - ret = btrfs_insert_inode_ref(trans, tree_root, name, namelen, objectid, + ret = btrfs_insert_inode_ref(trans, tree_root, name, strlen(name), + objectid, btrfs_super_root_dir(&fs_info->super_copy)); if (ret) goto fail; @@ -1030,9 +1061,8 @@ struct btrfs_root *create_subvol(struct BUG_ON(ret); new_root = btrfs_read_fs_root(fs_info, &location); - if (!new_root) { - return NULL; - } + if (!new_root || IS_ERR(new_root)) + goto fail; trans = btrfs_start_transaction(new_root, 1); BUG_ON(!trans); @@ -1145,38 +1175,69 @@ err: } /* - * Migrate super block to it's default position. - * This function is dangerous, it modifies a block used by ext2fs. + * Migrate super block to it's default position and zero 0 ~ 16k */ -static int migrate_super_block(struct btrfs_root *root, u64 sb_offset) -{ - int ret = -1; - struct extent_buffer *old_eb; - struct extent_buffer *sb_buffer; - struct btrfs_trans_handle *trans; - - BUG_ON(sb_offset != BTRFS_SUPER_INFO_OFFSET); - sb_buffer = read_tree_block(root, sb_offset, 512); - if (!sb_buffer) - goto fail; - memset_extent_buffer(sb_buffer, 0, 0, sb_buffer->len); - old_eb = root->fs_info->sb_buffer; - root->fs_info->sb_buffer = sb_buffer; - btrfs_set_super_bytenr(&root->fs_info->super_copy, sb_offset); - trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); - ret = btrfs_free_extent(trans, root, old_eb->start, root->leafsize, - 0, 0, 0, 0, 1); - if (ret) - goto fail; - ret = btrfs_commit_transaction(trans, root); - free_extent_buffer(old_eb); - BUG_ON(ret); +static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) +{ + int ret; + char *buf; + u64 bytenr; + u32 crc = ~(u32)0; + u32 len = 512 - BTRFS_CSUM_SIZE; + struct btrfs_super_block *super; + + ret = fsync(fd); + if (ret) + goto fail; + + BUG_ON(sectorsize < sizeof(super)); + buf = malloc(sectorsize); + if (!buf) + return -ENOMEM; + ret = pread(fd, buf, sectorsize, old_bytenr); + if (ret != sectorsize) + goto fail; + + super = (struct btrfs_super_block *)buf; + BUG_ON(btrfs_super_bytenr(super) != old_bytenr); + btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET); + + crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len); + crc = ~cpu_to_le32(crc); + memcpy(super->csum, &crc, BTRFS_CRC32_SIZE); + + ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET); + if (ret < 0) + goto fail; + /* How to handle this case? */ + BUG_ON(ret != sectorsize); + + ret = fsync(fd); + if (ret) + goto fail; + + memset(buf, 0, sectorsize); + for (bytenr = 0; bytenr < BTRFS_SUPER_INFO_OFFSET; ) { + len = BTRFS_SUPER_INFO_OFFSET - bytenr; + if (len > sectorsize) + len = sectorsize; + ret = pwrite(fd, buf, len, bytenr); + if (ret != len) { + fprintf(stderr, "unable to zero fill device\n"); + break; + } + bytenr += len; + } + ret = 0; + fsync(fd); fail: + free(buf); + if (ret > 0) + ret = -1; return ret; } -int main(int argc, char *argv[]) +int do_convert(const char *devname, int datacsum) { int i, fd, ret; u32 blocksize; @@ -1185,15 +1246,11 @@ int main(int argc, char *argv[]) u64 super_bytenr; ext2_filsys ext2_fs; struct btrfs_root *root; - struct btrfs_root *snap_root; - - if (argc != 2) { - fprintf(stderr, "usage: %s device\n", argv[0]); - exit(1); - } - ret = open_ext2fs(argv[1], &ext2_fs); + struct btrfs_root *ext2_root; + + ret = open_ext2fs(devname, &ext2_fs); if (ret) { - fprintf(stderr, "failed to open the Ext2fs\n"); + fprintf(stderr, "unable to open the Ext2fs\n"); goto fail; } blocksize = ext2_fs->blocksize; @@ -1204,7 +1261,7 @@ int main(int argc, char *argv[]) } if (!(ext2_fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) { - fprintf(stderr, "missing filetype feature\n"); + fprintf(stderr, "filetype feature is missing\n"); goto fail; } for (i = 0; i < 4; i++) { @@ -1217,63 +1274,309 @@ int main(int argc, char *argv[]) } super_bytenr = blocks[0]; - fd = open(argv[1], O_RDWR); + fd = open(devname, O_RDWR); if (fd < 0) { - fprintf(stderr, "unable to open %s\n", argv[1]); + fprintf(stderr, "unable to open %s\n", devname); goto fail; } ret = make_btrfs(fd, blocks, total_bytes, blocksize, blocksize, blocksize, blocksize); if (ret) { - fprintf(stderr, "failed to create ctree\n"); + fprintf(stderr, "unable to create initial ctree\n"); goto fail; } root = open_ctree_fd(fd, super_bytenr); if (!root) { - fprintf(stderr, "failed to open ctree\n"); - goto fail; - } - + fprintf(stderr, "unable to open ctree\n"); + goto fail; + } + fd = dup(fd); + if (fd < 0) { + fprintf(stderr, "unable to duplicate file descriptor\n"); + goto fail; + } root->fs_info->priv_data = ext2_fs; root->fs_info->extent_ops = &extent_ops; ret = init_btrfs(root); if (ret) { - fprintf(stderr, "failed to setup the root tree\n"); - goto fail; - } - snap_root = create_subvol(root, "ext2_saved", 10); - if (!snap_root) { - fprintf(stderr, "failed to create subvol\n"); + fprintf(stderr, "unable to setup the root tree\n"); + goto fail; + } + ext2_root = create_subvol(root, "ext2_saved"); + if (!ext2_root) { + fprintf(stderr, "unable to create subvol\n"); goto fail; } printf("creating btrfs metadata.\n"); - ret = copy_inodes(root, ext2_fs); + ret = copy_inodes(root, ext2_fs, datacsum); if (ret) { fprintf(stderr, "error during copy_inodes %d\n", ret); goto fail; } printf("creating ext2fs image file.\n"); - ret = create_ext2_image(snap_root, "image", 5, ext2_fs); + ret = create_ext2_image(ext2_root, ext2_fs, "image"); if (ret) { - fprintf(stderr, "failed to create fs image\n"); - goto fail; - } - ret = migrate_super_block(root, BTRFS_SUPER_INFO_OFFSET); - if (ret) { - fprintf(stderr, "failed to update super block\n"); - goto fail; - } - btrfs_free_fs_root(snap_root->fs_info, snap_root); + fprintf(stderr, "error during create_ext2_image %d\n", ret); + goto fail; + } + btrfs_free_fs_root(ext2_root->fs_info, ext2_root); ret = close_ctree(root); if (ret) { fprintf(stderr, "error during close_ctree %d\n", ret); goto fail; } close_ext2fs(ext2_fs); + + /* finally migrate super block to its default postion */ + ret = migrate_super_block(fd, super_bytenr, blocksize); + if (ret) { + fprintf(stderr, "unable to migrate super block\n"); + goto fail; + } + close(fd); printf("conversion complete.\n"); - exit(0); + return 0; fail: - fprintf(stderr, "conversion abort.\n"); + fprintf(stderr, "conversion aborted.\n"); + return -1; +} + +int do_rollback(const char *devname, int force) +{ + int fd; + int ret; + int modified = 0; + struct btrfs_root *root; + struct btrfs_root *ext2_root; + struct btrfs_dir_item *dir; + struct btrfs_inode_item *inode; + struct btrfs_file_extent_item *fi; + struct btrfs_inode_timespec *tspec; + struct extent_buffer *leaf; + struct btrfs_key key; + struct btrfs_path path; + char *buf; + char *name; + u64 bytenr; + u64 num_bytes; + u64 root_dir; + u64 objectid; + u64 offset; + u64 first_free; + u64 last_trans; + u64 total_bytes; + + fd = open(devname, O_RDWR); + if (fd < 0) { + fprintf(stderr, "unable to open %s\n", devname); + goto fail; + } + root = open_ctree_fd(fd, 0); + if (!root) { + fprintf(stderr, "unable to open ctree\n"); + goto fail; + } + fd = dup(fd); + if (fd < 0) { + fprintf(stderr, "unable to duplicate file descriptor\n"); + goto fail; + } + + first_free = BTRFS_SUPER_INFO_OFFSET + root->sectorsize * 2 - 1; + first_free &= ~((u64)root->sectorsize - 1); + buf = malloc(first_free); + if (!buf) { + fprintf(stderr, "unable to allocate memory\n"); + goto fail; + } + + btrfs_init_path(&path); + name = "ext2_saved"; + root_dir = btrfs_super_root_dir(&root->fs_info->super_copy); + dir = btrfs_lookup_dir_item(NULL, root->fs_info->tree_root, &path, + root_dir, name, strlen(name), 0); + if (!dir || IS_ERR(dir)) { + fprintf(stderr, "unable to find subvol %s\n", name); + goto fail; + } + leaf = path.nodes[0]; + btrfs_dir_item_key_to_cpu(leaf, dir, &key); + btrfs_release_path(root->fs_info->tree_root, &path); + + ext2_root = btrfs_read_fs_root(root->fs_info, &key); + if (!ext2_root || IS_ERR(ext2_root)) { + fprintf(stderr, "unable to open subvol %s\n", name); + goto fail; + } + + name = "image"; + root_dir = btrfs_root_dirid(&root->root_item); + dir = btrfs_lookup_dir_item(NULL, ext2_root, &path, + root_dir, name, strlen(name), 0); + if (!dir || IS_ERR(dir)) { + fprintf(stderr, "unable to find file %s\n", name); + goto fail; + } + leaf = path.nodes[0]; + btrfs_dir_item_key_to_cpu(leaf, dir, &key); + btrfs_release_path(ext2_root, &path); + + objectid = key.objectid; + + ret = btrfs_lookup_inode(NULL, ext2_root, &path, &key, 0); + if (ret) { + fprintf(stderr, "unable to find inode item\n"); + goto fail; + } + leaf = path.nodes[0]; + inode = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item); + tspec = btrfs_inode_otime(inode); + /* + * get image file size and transaction id stored in 'otime' field. + * see comments in create_ext2_image. + */ + last_trans = btrfs_timespec_sec(leaf, tspec); + total_bytes = btrfs_timespec_nsec(leaf, tspec); + total_bytes *= root->sectorsize; + btrfs_release_path(ext2_root, &path); + if (total_bytes != btrfs_inode_size(leaf, inode)) { + fprintf(stderr, "image file size mismatch\n"); + goto fail; + } + + key.objectid = objectid; + key.offset = 0; + btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); + ret = btrfs_search_slot(NULL, ext2_root, &key, &path, 0, 0); + if (ret != 0) { + fprintf(stderr, "unable to find first file extent\n"); + goto fail; + } + + for (offset = 0; offset < total_bytes; ) { + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, &path); + if (ret != 0) + break; + continue; + } + + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.objectid != objectid || key.offset != offset || + btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) + break; + + fi = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_file_extent_item); + if (btrfs_file_extent_generation(leaf, fi) > last_trans) { + modified = 1; + break; + } + if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) + break; + + if (offset >= first_free) + goto next; + + bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + if (bytenr == 0) + break; + bytenr += btrfs_file_extent_offset(leaf, fi); + num_bytes = btrfs_file_extent_num_bytes(leaf, fi); + if (num_bytes > first_free - offset) + num_bytes = first_free - offset; + + ret = pread(fd, buf + offset, num_bytes, bytenr); + if (ret != num_bytes) { + fprintf(stderr, "unable to read required data\n"); + btrfs_release_path(ext2_root, &path); + goto fail; + } +next: + offset += btrfs_file_extent_num_bytes(leaf, fi); + path.slots[0]++; + } + btrfs_release_path(ext2_root, &path); + + if (modified) { + fprintf(stderr, "image file has been modified\n"); + goto fail; + } + if (offset < total_bytes) { + fprintf(stderr, "unable to check all file extents\n"); + goto fail; + } + + btrfs_free_fs_root(ext2_root->fs_info, ext2_root); + ret = close_ctree(root); + if (ret) { + fprintf(stderr, "error during close_ctree %d\n", ret); + goto fail; + } + + ret = pwrite(fd, buf, first_free, 0); + if (ret < 0) { + fprintf(stderr, "error during pwrite %d\n", ret); + goto fail; + } + /* How to handle this case? */ + BUG_ON(ret != first_free); + ret = fsync(fd); + if (ret) { + fprintf(stderr, "error during fsync %d\n", ret); + goto fail; + } + close(fd); + free(buf); + printf("rollback complete.\n"); + return 0; +fail: + fprintf(stderr, "rollback aborted.\n"); + return -1; +} + +static void print_usage(void) +{ + printf("usage: btrfs-convert [-d] [-r] device\n"); + printf("\t-d disable data checksum\n"); + printf("\t-r roll back to ext2fs\n"); exit(1); } + +int main(int argc, char *argv[]) +{ + int ret; + int datacsum = 1; + int rollback = 0; + char *file; + + while(1) { + int c = getopt(argc, argv, "dr"); + if (c < 0) + break; + switch(c) { + case 'd': + datacsum = 0; + break; + case 'r': + rollback = 1; + break; + default: + print_usage(); + } + } + argc = argc - optind; + if (argc == 1) { + file = argv[optind]; + } else { + print_usage(); + } + if (rollback) { + ret = do_rollback(file, 0); + } else { + ret = do_convert(file, datacsum); + } + return ret; +} diff -r 12138d4beeb0 disk-io.c --- a/disk-io.c Fri Jan 04 11:29:55 2008 -0500 +++ b/disk-io.c Mon Jan 07 23:35:25 2008 +0800 @@ -417,7 +417,13 @@ struct btrfs_root *open_ctree_fd(int fp, read_extent_buffer(fs_info->sb_buffer, fs_info->fsid, (unsigned long)btrfs_super_fsid(fs_info->sb_buffer), BTRFS_FSID_SIZE); + disk_super = &fs_info->super_copy; + if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC, + sizeof(disk_super->magic))) { + printk("No valid btrfs found\n"); + BUG_ON(1); + } nodesize = btrfs_super_nodesize(disk_super); leafsize = btrfs_super_leafsize(disk_super); diff -r 12138d4beeb0 utils.c --- a/utils.c Fri Jan 04 11:29:55 2008 -0500 +++ b/utils.c Mon Jan 07 23:35:25 2008 +0800 @@ -34,7 +34,6 @@ #include "utils.h" static u64 reference_root_table[4] = { - [0] = 0, [1] = BTRFS_ROOT_TREE_OBJECTID, [2] = BTRFS_EXTENT_TREE_OBJECTID, [3] = BTRFS_FS_TREE_OBJECTID, @@ -143,11 +142,10 @@ int make_btrfs(int fd, u64 blocks[4], u6 btrfs_set_extent_refs(buf, extent_item, 1); nritems++; - for (i = 0; i < 4; i++) { - if (blocks[i] < first_free) { - BUG_ON(i > 0); - continue; - } + for (i = 1; i < 4; i++) { + BUG_ON(blocks[i] < first_free); + BUG_ON(blocks[i] < blocks[i - 1]); + /* create extent item */ itemoff = itemoff - sizeof(struct btrfs_extent_item); btrfs_set_disk_key_objectid(&disk_key, blocks[i]);
Chris Mason
2008-Jan-08 13:05 UTC
[Btrfs-devel][PATCH]Add rollback support for the converter
On Tue, 8 Jan 2008 00:06:43 +0800 "Yan Zheng" <yanzheng@21cn.com> wrote:> Hello, > > This patch adds rollback support for the converter, the converter can > roll back a conversion if the image file haven't been modified. In > addition, I rearrange some codes in convert.c and add a few comments.Nice, and much faster too. I have this queued up with a large number of changes that includes all the patches you've sent so far. -chris
Reasonably Related Threads
- [PATCH 4/4] btrfs-convert: split into convert/.
- [PATCH] Progs: update convert for uninitialized block groups
- btrfs-convert crashes
- PULL: Properly cast and avoid compiler warnings, fixes build on alpha and ia64.
- [LLVMdev] Dominator error inserting instructions into BasicBlock