diff --git a/Documentation/mkfs.btrfs.rst b/Documentation/mkfs.btrfs.rst index 58336e82a..77c37553a 100644 --- a/Documentation/mkfs.btrfs.rst +++ b/Documentation/mkfs.btrfs.rst @@ -212,6 +212,15 @@ OPTIONS $ mkfs.btrfs -O list-all +--compress : + Try to compress files when using *--rootdir*. Supported values for *algo* are + *no* (the default) and *zlib*. The optional value *level* is a + compression level, from 1 to 9 for ZLIB. + + As with the kernel, :command:`mkfs.btrfs` won't write compressed extents when + they would be larger than the uncompressed versions, and will mark a file as + `nocompress` if its beginning is found to be incompressible. + -f|--force Forcibly overwrite the block devices when an existing filesystem is detected. By default, :command:`mkfs.btrfs` will utilize *libblkid* to check for any known diff --git a/Makefile b/Makefile index 2f8d2fa08..c8625ae06 100644 --- a/Makefile +++ b/Makefile @@ -740,11 +740,11 @@ btrfsck.static: btrfs.static mkfs.btrfs: $(mkfs_objects) $(objects) libbtrfsutil.a @echo " [LD] $@" - $(Q)$(CC) -o $@ $^ $(LDFLAGS) $(LIBS) + $(Q)$(CC) -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_COMP) mkfs.btrfs.static: $(static_mkfs_objects) $(static_objects) $(static_libbtrfs_objects) @echo " [LD] $@" - $(Q)$(CC) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS) + $(Q)$(CC) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS) $(STATIC_LIBS_COMP) btrfstune: $(tune_objects) $(objects) libbtrfsutil.a @echo " [LD] $@" diff --git a/common/extent-tree-utils.c b/common/extent-tree-utils.c index 9f7e543f3..d3b9ce82e 100644 --- a/common/extent-tree-utils.c +++ b/common/extent-tree-utils.c @@ -285,3 +285,93 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans, } return ret; } + +int btrfs_record_file_extent_comp(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 file_pos, u64 disk_bytenr, + u64 extent_num_bytes, u64 ram_bytes, + enum btrfs_compression_type comp) +{ + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_root *extent_root = btrfs_extent_root(info, disk_bytenr); + struct extent_buffer *leaf; + struct btrfs_file_extent_item *fi; + struct btrfs_key ins_key; + struct btrfs_path *path; + struct btrfs_extent_item *ei; + u64 nbytes; + u64 extent_bytenr; + u64 extent_offset; + int ret = 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ins_key.objectid = disk_bytenr; + ins_key.type = BTRFS_EXTENT_ITEM_KEY; + ins_key.offset = extent_num_bytes; + + 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, disk_bytenr, + extent_num_bytes, 1, 0); + if (ret) + goto fail; + } else if (ret != -EEXIST) { + goto fail; + } + + ret = remove_from_free_space_tree(trans, disk_bytenr, extent_num_bytes); + if (ret) + goto fail; + + btrfs_run_delayed_refs(trans, -1); + extent_bytenr = disk_bytenr; + extent_offset = 0; + + btrfs_release_path(path); + ins_key.objectid = objectid; + ins_key.type = BTRFS_EXTENT_DATA_KEY; + ins_key.offset = file_pos; + 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, extent_bytenr); + btrfs_set_file_extent_disk_num_bytes(leaf, fi, extent_num_bytes); + btrfs_set_file_extent_offset(leaf, fi, extent_offset); + btrfs_set_file_extent_num_bytes(leaf, fi, ram_bytes); + btrfs_set_file_extent_ram_bytes(leaf, fi, ram_bytes); + btrfs_set_file_extent_compression(leaf, fi, comp); + btrfs_set_file_extent_encryption(leaf, fi, 0); + btrfs_set_file_extent_other_encoding(leaf, fi, 0); + btrfs_mark_buffer_dirty(leaf); + + nbytes = btrfs_stack_inode_nbytes(inode) + ram_bytes; + btrfs_set_stack_inode_nbytes(inode, nbytes); + btrfs_release_path(path); + + ret = btrfs_inc_extent_ref(trans, extent_bytenr, extent_num_bytes, + 0, root->root_key.objectid, objectid, + file_pos - extent_offset); + +fail: + btrfs_free_path(path); + return ret; +} diff --git a/common/extent-tree-utils.h b/common/extent-tree-utils.h index f03d9c438..ead41facc 100644 --- a/common/extent-tree-utils.h +++ b/common/extent-tree-utils.h @@ -19,6 +19,7 @@ #include "kerncompat.h" #include "kernel-lib/bitops.h" +#include "kernel-shared/compression.h" struct btrfs_inode_item; struct btrfs_path; @@ -32,5 +33,11 @@ int btrfs_record_file_extent(struct btrfs_trans_handle *trans, struct btrfs_inode_item *inode, u64 file_pos, u64 disk_bytenr, u64 num_bytes); +int btrfs_record_file_extent_comp(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, u64 ram_bytes, + enum btrfs_compression_type comp); #endif diff --git a/convert/source-ext2.c b/convert/source-ext2.c index d06f90a98..a3eb2da5c 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -457,7 +457,8 @@ static int ext2_create_file_extents(struct btrfs_trans_handle *trans, if (num_bytes > inode_size) num_bytes = inode_size; ret = btrfs_insert_inline_extent(trans, root, objectid, - 0, buffer, num_bytes); + 0, buffer, num_bytes, + 0, num_bytes); if (ret) goto fail; nbytes = btrfs_stack_inode_nbytes(btrfs_inode) + num_bytes; @@ -506,7 +507,8 @@ static int ext2_create_symlink(struct btrfs_trans_handle *trans, pathname = (char *)&(ext2_inode->i_block[0]); BUG_ON(pathname[inode_size] != 0); ret = btrfs_insert_inline_extent(trans, root, objectid, 0, - pathname, inode_size); + pathname, inode_size, + 0, inode_size); btrfs_set_stack_inode_nbytes(btrfs_inode, inode_size); return ret; } diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index 3475b1527..974f84c15 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -384,7 +384,8 @@ static int reiserfs_convert_tail(struct btrfs_trans_handle *trans, length, offset, convert_flags); ret = btrfs_insert_inline_extent(trans, root, objectid, - offset, body, length); + offset, body, length, + 0, length); if (ret) return ret; @@ -544,7 +545,7 @@ static int reiserfs_copy_symlink(struct btrfs_trans_handle *trans, goto fail; } ret = btrfs_insert_inline_extent(trans, root, objectid, 0, - symlink, len); + symlink, len, 0, len); btrfs_set_stack_inode_nbytes(btrfs_inode, len); fail: pathrelse(&path); diff --git a/kernel-shared/file-item.c b/kernel-shared/file-item.c index eb9024022..9ef7ad2d1 100644 --- a/kernel-shared/file-item.c +++ b/kernel-shared/file-item.c @@ -87,7 +87,9 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, - u64 offset, const char *buffer, size_t size) + u64 offset, const char *buffer, size_t size, + enum btrfs_compression_type comp, + u64 ram_bytes) { struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_key key; @@ -123,8 +125,8 @@ int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, struct btrfs_file_extent_item); btrfs_set_file_extent_generation(leaf, ei, trans->transid); btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE); - btrfs_set_file_extent_ram_bytes(leaf, ei, size); - btrfs_set_file_extent_compression(leaf, ei, 0); + btrfs_set_file_extent_ram_bytes(leaf, ei, ram_bytes); + btrfs_set_file_extent_compression(leaf, ei, comp); btrfs_set_file_extent_encryption(leaf, ei, 0); btrfs_set_file_extent_other_encoding(leaf, ei, 0); diff --git a/kernel-shared/file-item.h b/kernel-shared/file-item.h index 2c1e17c99..4ea341328 100644 --- a/kernel-shared/file-item.h +++ b/kernel-shared/file-item.h @@ -10,6 +10,7 @@ #include "kernel-shared/ctree.h" #include "kernel-shared/uapi/btrfs_tree.h" #include "kernel-shared/accessors.h" +#include "kernel-shared/compression.h" struct bio; struct inode; @@ -91,7 +92,8 @@ int btrfs_csum_file_block(struct btrfs_trans_handle *trans, u64 logical, u64 csum_objectid, u32 csum_type, const char *data); int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, - u64 offset, const char *buffer, size_t size); + u64 offset, const char *buffer, size_t size, + enum btrfs_compression_type comp, u64 ram_bytes); /* * For symlink we allow up to PATH_MAX - 1 (PATH_MAX includes the terminating NUL, * but fs doesn't store that terminating NUL). diff --git a/mkfs/main.c b/mkfs/main.c index c9dbd234c..ffd7cb427 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -443,6 +443,7 @@ static const char * const mkfs_usage[] = { OPTLINE("-u|--subvol TYPE:SUBDIR", "create SUBDIR as subvolume rather than normal directory, can be specified multiple times"), OPTLINE("--shrink", "(with --rootdir) shrink the filled filesystem to minimal size"), OPTLINE("-K|--nodiscard", "do not perform whole device TRIM"), + OPTLINE("--compress ALGO:LEVEL", "compression algorithm and level to use; ALGO can be no (default), zlib"), OPTLINE("-f|--force", "force overwrite of existing filesystem"), "General:", OPTLINE("-q|--quiet", "no messages except errors"), @@ -1058,6 +1059,8 @@ int BOX_MAIN(mkfs)(int argc, char **argv) char *source_dir = NULL; struct rootdir_subvol *rds; bool has_default_subvol = false; + enum btrfs_compression_type compression = BTRFS_COMPRESS_NONE; + u64 compression_level = 0; LIST_HEAD(subvols); cpu_detect_flags(); @@ -1072,6 +1075,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv) GETOPT_VAL_CHECKSUM, GETOPT_VAL_GLOBAL_ROOTS, GETOPT_VAL_DEVICE_UUID, + GETOPT_VAL_COMPRESS, }; static const struct option long_options[] = { { "byte-count", required_argument, NULL, 'b' }, @@ -1099,6 +1103,8 @@ int BOX_MAIN(mkfs)(int argc, char **argv) { "quiet", 0, NULL, 'q' }, { "verbose", 0, NULL, 'v' }, { "shrink", no_argument, NULL, GETOPT_VAL_SHRINK }, + { "compress", required_argument, NULL, + GETOPT_VAL_COMPRESS }, #if EXPERIMENTAL { "param", required_argument, NULL, GETOPT_VAL_PARAM }, { "num-global-roots", required_argument, NULL, GETOPT_VAL_GLOBAL_ROOTS }, @@ -1272,6 +1278,42 @@ int BOX_MAIN(mkfs)(int argc, char **argv) case 'q': bconf_be_quiet(); break; + case GETOPT_VAL_COMPRESS: { + char *colon; + size_t type_size; + + if (!strcmp(optarg, "no")) { + compression = BTRFS_COMPRESS_NONE; + break; + } + + colon = strstr(optarg, ":"); + + if (colon) + type_size = colon - optarg; + else + type_size = strlen(optarg); + + if (!strncmp(optarg, "zlib", type_size)) { + compression = BTRFS_COMPRESS_ZLIB; + } else if (!strncmp(optarg, "lzo", type_size)) { + compression = BTRFS_COMPRESS_LZO; + } else if (!strncmp(optarg, "zstd", type_size)) { + compression = BTRFS_COMPRESS_ZSTD; + } else { + error("unrecognized compression type %s", + optarg); + ret = 1; + goto error; + } + + if (colon) + compression_level = arg_strtou64(colon + 1); + else + compression_level = 0; + + break; + } case GETOPT_VAL_DEVICE_UUID: strncpy_null(dev_uuid, optarg, BTRFS_UUID_UNPARSED_SIZE); break; @@ -1953,7 +1995,8 @@ int BOX_MAIN(mkfs)(int argc, char **argv) } ret = btrfs_mkfs_fill_dir(trans, source_dir, root, - &subvols); + &subvols, compression, + compression_level); if (ret) { error("error while filling filesystem: %d", ret); btrfs_abort_transaction(trans, ret); diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c index 704f59e76..fd06bb478 100644 --- a/mkfs/rootdir.c +++ b/mkfs/rootdir.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "kernel-lib/sizes.h" #include "kernel-shared/accessors.h" #include "kernel-shared/uapi/btrfs_tree.h" @@ -45,6 +46,9 @@ #include "common/rbtree-utils.h" #include "mkfs/rootdir.h" +#define ZLIB_BTRFS_DEFAULT_LEVEL 3 +#define ZLIB_BTRFS_MAX_LEVEL 9 + static u32 fs_block_size; /* @@ -144,6 +148,8 @@ static struct btrfs_trans_handle *g_trans = NULL; static struct list_head *g_subvols; static u64 next_subvol_id = BTRFS_FIRST_FREE_OBJECTID; static u64 default_subvol_id; +static enum btrfs_compression_type g_compression; +static u64 g_compression_level; static inline struct inode_entry *rootdir_path_last(struct rootdir_path *path) { @@ -351,7 +357,8 @@ static int add_symbolic_link(struct btrfs_trans_handle *trans, buf[ret] = '\0'; /* readlink does not do it for us */ nbytes = ret + 1; - ret = btrfs_insert_inline_extent(trans, root, objectid, 0, buf, nbytes); + ret = btrfs_insert_inline_extent(trans, root, objectid, 0, buf, nbytes, + 0, nbytes); if (ret < 0) { errno = -ret; error("failed to insert inline extent for %s: %m", path_name); @@ -362,6 +369,75 @@ static int add_symbolic_link(struct btrfs_trans_handle *trans, return ret; } +static ssize_t zlib_compress_extent(struct btrfs_inode_item *btrfs_inode, + u32 sectorsize, const void *in_buf, + size_t in_size, void *out_buf) +{ + int ret; + z_stream strm; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + ret = deflateInit(&strm, g_compression_level); + if (ret != Z_OK) { + error("deflateInit failed: %s", strm.msg); + return -EINVAL; + } + + strm.next_out = out_buf; + strm.avail_out = BTRFS_MAX_COMPRESSED; + strm.next_in = (void *)in_buf; + strm.avail_in = in_size; + + /* Try to compress the first sector - if it would be larger, mark the + * inode as nocompress and return. */ + if (!(btrfs_stack_inode_flags(btrfs_inode) & BTRFS_INODE_COMPRESS)) { + strm.avail_in = sectorsize; + + ret = deflate(&strm, Z_SYNC_FLUSH); + + if (ret != Z_OK) { + error("deflate failed: %s", strm.msg); + return -EINVAL; + } + + if (strm.avail_out < BTRFS_MAX_COMPRESSED - sectorsize) { + u64 flags; + + flags = btrfs_stack_inode_flags(btrfs_inode); + flags |= BTRFS_INODE_NOCOMPRESS; + btrfs_set_stack_inode_flags(btrfs_inode, flags); + + return 0; + } + + strm.avail_in += in_size - sectorsize; + } + + ret = deflate(&strm, Z_FINISH); + + if (ret != Z_OK && ret != Z_STREAM_END) { + error("deflate failed: %s", strm.msg); + return -EINVAL; + } + + if (ret == Z_STREAM_END && + out_buf + BTRFS_MAX_COMPRESSED - (void *)strm.next_out > sectorsize) { + return (void *)strm.next_out - out_buf; + } + + if (!(btrfs_stack_inode_flags(btrfs_inode) & BTRFS_INODE_COMPRESS)) { + u64 flags; + + flags = btrfs_stack_inode_flags(btrfs_inode); + flags |= BTRFS_INODE_NOCOMPRESS; + btrfs_set_stack_inode_flags(btrfs_inode, flags); + } + + return 0; +} + /* * keep our extent size at 1MB max, this makes it easier to work * inside the tiny block groups created during mkfs @@ -372,14 +448,23 @@ static int read_and_write_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_inode_item *btrfs_inode, u64 objectid, int fd, u64 file_pos, char *buf, - u64 size, const char *path_name) + u64 size, const char *path_name, + char *comp_buf) { int ret; u32 sectorsize = root->fs_info->sectorsize; u64 bytes_read, first_block, to_read, to_write; struct btrfs_key key; + u64 buf_size; + char *write_buf; + bool do_comp = g_compression != BTRFS_COMPRESS_NONE; + ssize_t comp_ret; + + if (btrfs_stack_inode_flags(btrfs_inode) & BTRFS_INODE_NOCOMPRESS) + do_comp = false; - to_read = min(file_pos + MAX_EXTENT_SIZE, size) - file_pos; + buf_size = do_comp ? BTRFS_MAX_COMPRESSED : MAX_EXTENT_SIZE; + to_read = min(file_pos + buf_size, size) - file_pos; bytes_read = 0; @@ -398,8 +483,65 @@ static int read_and_write_extent(struct btrfs_trans_handle *trans, bytes_read += ret_read; } - to_write = round_up(to_read, sectorsize); - memset(buf + to_read, 0, to_write - to_read); + if (bytes_read <= sectorsize) + do_comp = false; + + if (do_comp) { + switch (g_compression) { + case BTRFS_COMPRESS_ZLIB: + comp_ret = zlib_compress_extent(btrfs_inode, sectorsize, + buf, bytes_read, + comp_buf); + break; + default: + comp_ret = -EINVAL; + break; + } + + if (comp_ret < 0) + return comp_ret; + else if (comp_ret == 0) + do_comp = false; + + /* If we've just marked this inode nocompress, increase the + * buffer size and read the rest of the extent. */ + if (btrfs_stack_inode_flags(btrfs_inode) & BTRFS_INODE_NOCOMPRESS) { + buf_size = MAX_EXTENT_SIZE; + to_read = min(file_pos + buf_size, size) - file_pos; + + while (bytes_read < to_read) { + ssize_t ret_read; + + ret_read = pread(fd, buf + bytes_read, + to_read - bytes_read, + file_pos + bytes_read); + if (ret_read == -1) { + error("cannot read %s at offset %llu length %llu: %m", + path_name, file_pos + bytes_read, + to_read - bytes_read); + return -errno; + } + + bytes_read += ret_read; + } + } + } + + if (do_comp) { + u64 flags; + + to_write = round_up(comp_ret, sectorsize); + write_buf = comp_buf; + memset(write_buf + comp_ret, 0, to_write - comp_ret); + + flags = btrfs_stack_inode_flags(btrfs_inode); + flags |= BTRFS_INODE_COMPRESS; + btrfs_set_stack_inode_flags(btrfs_inode, flags); + } else { + to_write = round_up(to_read, sectorsize); + write_buf = buf; + memset(write_buf + to_read, 0, to_write - to_read); + } ret = btrfs_reserve_extent(trans, root, to_write, 0, 0, (u64)-1, &key, 1); @@ -408,7 +550,7 @@ static int read_and_write_extent(struct btrfs_trans_handle *trans, first_block = key.objectid; - ret = write_data_to_disk(root->fs_info, buf, first_block, + ret = write_data_to_disk(root->fs_info, write_buf, first_block, to_write); if (ret) { error("failed to write %s", path_name); @@ -419,19 +561,77 @@ static int read_and_write_extent(struct btrfs_trans_handle *trans, ret = btrfs_csum_file_block(trans, first_block + (i * sectorsize), BTRFS_EXTENT_CSUM_OBJECTID, root->fs_info->csum_type, - buf + (i * sectorsize)); + write_buf + (i * sectorsize)); if (ret) return ret; } - ret = btrfs_record_file_extent(trans, root, objectid, btrfs_inode, - file_pos, first_block, to_write); + if (do_comp) { + ret = btrfs_record_file_extent_comp(trans, root, objectid, btrfs_inode, + file_pos, first_block, to_write, + round_up(to_read, sectorsize), g_compression); + } else { + ret = btrfs_record_file_extent(trans, root, objectid, btrfs_inode, + file_pos, first_block, to_write); + } + if (ret) return ret; return to_read; } +static int zlib_compress_inline_extent(char *buf, u64 size, char **comp_buf, + u64 *comp_size) +{ + int zlib_ret, ret; + z_stream strm; + char *out = NULL; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + zlib_ret = deflateInit(&strm, g_compression_level); + if (zlib_ret != Z_OK) { + error("deflateInit failed: %s", strm.msg); + return -EINVAL; + } + + out = malloc(size); + if (!out) { + error_msg(ERROR_MSG_MEMORY, NULL); + ret = -ENOMEM; + goto out; + } + + strm.next_out = (Bytef *)out; + strm.avail_out = size; + strm.next_in = (Bytef *)buf; + strm.avail_in = size; + + zlib_ret = deflate(&strm, Z_FINISH); + + if (zlib_ret != Z_OK && zlib_ret != Z_STREAM_END) { + error("deflate failed: %s", strm.msg); + ret = -EINVAL; + goto out; + } + + if (zlib_ret == Z_STREAM_END && strm.avail_out > 0) { + ret = 1; + *comp_buf = out; + *comp_size = size - strm.avail_out; + } else { + ret = 0; + } + +out: + if (ret != 1) + free(out); + + return ret; +} + static int add_file_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_inode_item *btrfs_inode, u64 objectid, @@ -442,7 +642,7 @@ static int add_file_items(struct btrfs_trans_handle *trans, ssize_t ret_read; u32 sectorsize = fs_info->sectorsize; u64 file_pos = 0; - char *buf = NULL; + char *buf = NULL, *comp_buf = NULL; int fd; if (st->st_size == 0) @@ -457,6 +657,7 @@ static int add_file_items(struct btrfs_trans_handle *trans, if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(fs_info) && st->st_size < sectorsize) { char *buffer = malloc(st->st_size); + u64 comp_size; if (!buffer) { ret = -ENOMEM; @@ -471,8 +672,29 @@ static int add_file_items(struct btrfs_trans_handle *trans, goto end; } - ret = btrfs_insert_inline_extent(trans, root, objectid, 0, - buffer, st->st_size); + switch (g_compression) { + case BTRFS_COMPRESS_ZLIB: + ret = zlib_compress_inline_extent(buffer, st->st_size, + &comp_buf, &comp_size); + if (ret < 0) + goto end; + break; + default: + ret = 0; + break; + } + + if (ret == 1) { + ret = btrfs_insert_inline_extent(trans, root, objectid, + 0, comp_buf, comp_size, + g_compression, + st->st_size); + } else { + ret = btrfs_insert_inline_extent(trans, root, objectid, + 0, buffer, st->st_size, + 0, st->st_size); + } + free(buffer); /* Update the inode nbytes for inline extents. */ btrfs_set_stack_inode_nbytes(btrfs_inode, st->st_size); @@ -485,10 +707,18 @@ static int add_file_items(struct btrfs_trans_handle *trans, goto end; } + if (g_compression != BTRFS_COMPRESS_NONE) { + comp_buf = malloc(BTRFS_MAX_COMPRESSED); + if (!comp_buf) { + ret = -ENOMEM; + goto end; + } + } + while (file_pos < st->st_size) { ret = read_and_write_extent(trans, root, btrfs_inode, objectid, fd, file_pos, buf, st->st_size, - path_name); + path_name, comp_buf); if (ret < 0) break; @@ -496,6 +726,7 @@ static int add_file_items(struct btrfs_trans_handle *trans, } end: + free(comp_buf); free(buf); close(fd); return ret; @@ -900,7 +1131,9 @@ static int set_default_subvolume(struct btrfs_trans_handle *trans) } int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir, - struct btrfs_root *root, struct list_head *subvols) + struct btrfs_root *root, struct list_head *subvols, + enum btrfs_compression_type compression, + u64 compression_level) { int ret; struct stat root_st; @@ -911,8 +1144,24 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir return -errno; } + switch (compression) { + case BTRFS_COMPRESS_NONE: + break; + case BTRFS_COMPRESS_ZLIB: + if (compression_level > ZLIB_BTRFS_MAX_LEVEL) + compression_level = ZLIB_BTRFS_MAX_LEVEL; + else if (compression_level == 0) + compression_level = ZLIB_BTRFS_DEFAULT_LEVEL; + break; + default: + error("unsupported compression type"); + return -EINVAL; + } + g_trans = trans; g_subvols = subvols; + g_compression = compression; + g_compression_level = compression_level; INIT_LIST_HEAD(¤t_path.inode_list); ret = nftw(source_dir, ftw_add_inode, 32, FTW_PHYS); diff --git a/mkfs/rootdir.h b/mkfs/rootdir.h index 9db6ff4de..4891ffa74 100644 --- a/mkfs/rootdir.h +++ b/mkfs/rootdir.h @@ -22,6 +22,7 @@ #define __BTRFS_MKFS_ROOTDIR_H__ #include "kerncompat.h" +#include "kernel-shared/compression.h" #include #include @@ -37,7 +38,9 @@ struct rootdir_subvol { }; int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir, - struct btrfs_root *root, struct list_head *subvols); + struct btrfs_root *root, struct list_head *subvols, + enum btrfs_compression_type compression, + u64 compression_level); u64 btrfs_mkfs_size_dir(const char *dir_name, u32 sectorsize, u64 min_dev_size, u64 meta_profile, u64 data_profile); int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret,