Skip to content

Commit fe56662

Browse files
maharmstoneadam900710
authored andcommitted
btrfs-progs: mkfs: add lzo to --compress option
Allow --compress to work with lzo. Signed-off-by: Mark Harmstone <maharmstone@fb.com> [ Add extra handling when LZO support is not compiled in ] Signed-off-by: Qu Wenruo <wqu@suse.com>
1 parent 225ecd1 commit fe56662

File tree

3 files changed

+209
-4
lines changed

3 files changed

+209
-4
lines changed

Documentation/mkfs.btrfs.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ OPTIONS
214214
215215
--compress <algo>[:<level>]
216216
Try to compress files when using *--rootdir*. Supported values for *algo* are
217-
*no* (the default), *zlib*, and *zstd*. The optional value *level* is a
217+
*no* (the default), *zlib*, *lzo*, and *zstd*. The optional value *level* is a
218218
compression level, from 1 to 9 for ZLIB and from 1 to 15 for ZSTD.
219219

220220
As with the kernel, :command:`mkfs.btrfs` won't write compressed extents when

mkfs/main.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ static const char * const mkfs_usage[] = {
443443
OPTLINE("-u|--subvol TYPE:SUBDIR", "create SUBDIR as subvolume rather than normal directory, can be specified multiple times"),
444444
OPTLINE("--shrink", "(with --rootdir) shrink the filled filesystem to minimal size"),
445445
OPTLINE("-K|--nodiscard", "do not perform whole device TRIM"),
446-
OPTLINE("--compress ALGO[:LEVEL]", "compression algorithm and level to use; ALGO can be no (default), zlib, zstd"),
446+
OPTLINE("--compress ALGO[:LEVEL]", "compression algorithm and level to use; ALGO can be no (default), zlib, lzo, zstd"),
447447
OPTLINE("-f|--force", "force overwrite of existing filesystem"),
448448
"General:",
449449
OPTLINE("-q|--quiet", "no messages except errors"),
@@ -1296,6 +1296,8 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
12961296

12971297
if (!strncmp(optarg, "zlib", type_size)) {
12981298
compression = BTRFS_COMPRESS_ZLIB;
1299+
} else if (!strncmp(optarg, "lzo", type_size)) {
1300+
compression = BTRFS_COMPRESS_LZO;
12991301
} else if (!strncmp(optarg, "zstd", type_size)) {
13001302
compression = BTRFS_COMPRESS_ZSTD;
13011303
} else {

mkfs/rootdir.c

Lines changed: 205 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
#include <zstd_errors.h>
3333
#endif
3434
#include <zlib.h>
35+
#if COMPRESSION_LZO
36+
#include <lzo/lzo1x.h>
37+
#endif
3538
#include "kernel-lib/sizes.h"
3639
#include "kernel-shared/accessors.h"
3740
#include "kernel-shared/uapi/btrfs_tree.h"
@@ -57,6 +60,8 @@
5760
#define ZSTD_BTRFS_DEFAULT_LEVEL 3
5861
#define ZSTD_BTRFS_MAX_LEVEL 15
5962

63+
#define LZO_LEN 4
64+
6065
static u32 fs_block_size;
6166

6267
/*
@@ -526,6 +531,66 @@ static ssize_t zlib_compress_extent(bool first_sector, u32 sectorsize,
526531
return -E2BIG;
527532
}
528533

534+
#if COMPRESSION_LZO
535+
/*
536+
* Returns the size of the compressed data if successful, -E2BIG if it is
537+
* incompressible, or an error code.
538+
*/
539+
static ssize_t lzo_compress_extent(u32 sectorsize, const void *in_buf,
540+
size_t in_size, void *out_buf, char *wrkmem)
541+
{
542+
int ret;
543+
unsigned int sectors;
544+
u32 total_size, out_pos;
545+
546+
out_pos = LZO_LEN;
547+
total_size = LZO_LEN;
548+
sectors = DIV_ROUND_UP(in_size, sectorsize);
549+
550+
for (unsigned int i = 0; i < sectors; i++) {
551+
size_t in_len, out_len, new_pos;
552+
u32 padding;
553+
554+
in_len = min((size_t)sectorsize, in_size - (i * sectorsize));
555+
556+
ret = lzo1x_1_compress(in_buf + (i * sectorsize), in_len,
557+
out_buf + out_pos + LZO_LEN, &out_len,
558+
wrkmem);
559+
if (ret) {
560+
error("lzo1x_1_compress returned %i", ret);
561+
return -EINVAL;
562+
}
563+
564+
put_unaligned_le32(out_len, out_buf + out_pos);
565+
566+
new_pos = out_pos + LZO_LEN + out_len;
567+
568+
/* Make sure that our header doesn't cross a sector boundary. */
569+
if (new_pos / sectorsize != (new_pos + LZO_LEN - 1) / sectorsize)
570+
padding = round_up(new_pos, LZO_LEN) - new_pos;
571+
else
572+
padding = 0;
573+
574+
out_pos += out_len + LZO_LEN + padding;
575+
total_size += out_len + LZO_LEN + padding;
576+
577+
/*
578+
* Follow kernel in trying to compress the first three sectors,
579+
* then giving up if the output isn't any smaller.
580+
*/
581+
if (i >= 3 && total_size > i * sectorsize)
582+
return -E2BIG;
583+
}
584+
585+
if (total_size > in_size)
586+
return -E2BIG;
587+
588+
put_unaligned_le32(total_size, out_buf);
589+
590+
return total_size;
591+
}
592+
#endif
593+
529594
#if COMPRESSION_ZSTD
530595
/*
531596
* Returns the size of the compressed data if successful, -E2BIG if it is
@@ -629,6 +694,7 @@ struct source_descriptor {
629694
u64 size;
630695
const char *path_name;
631696
char *comp_buf;
697+
char *wrkmem;
632698
};
633699

634700
static int add_file_item_extent(struct btrfs_trans_handle *trans,
@@ -684,6 +750,14 @@ static int add_file_item_extent(struct btrfs_trans_handle *trans,
684750
source->buf, bytes_read,
685751
source->comp_buf);
686752
break;
753+
#if COMPRESSION_LZO
754+
case BTRFS_COMPRESS_LZO:
755+
comp_ret = lzo_compress_extent(sectorsize, source->buf,
756+
bytes_read,
757+
source->comp_buf,
758+
source->wrkmem);
759+
break;
760+
#endif
687761
#if COMPRESSION_ZSTD
688762
case BTRFS_COMPRESS_ZSTD:
689763
comp_ret = zstd_compress_extent(first_sector, sectorsize,
@@ -748,6 +822,11 @@ static int add_file_item_extent(struct btrfs_trans_handle *trans,
748822
features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD;
749823
btrfs_set_super_incompat_flags(trans->fs_info->super_copy,
750824
features);
825+
} else if (g_compression == BTRFS_COMPRESS_LZO) {
826+
features = btrfs_super_incompat_flags(trans->fs_info->super_copy);
827+
features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO;
828+
btrfs_set_super_incompat_flags(trans->fs_info->super_copy,
829+
features);
751830
}
752831
} else {
753832
to_write = round_up(to_read, sectorsize);
@@ -849,6 +928,62 @@ static ssize_t zlib_compress_inline_extent(char *buf, u64 size, char **comp_buf)
849928
return ret;
850929
}
851930

931+
#if COMPRESSION_LZO
932+
static u32 lzo_max_outlen(u32 inlen) {
933+
/*
934+
* Return the worst-case output length for LZO. Formula comes from
935+
* LZO.FAQ.
936+
*/
937+
return inlen + (inlen / 16) + 64 + 3;
938+
}
939+
940+
/*
941+
* Returns the size of the compressed data if successful, -E2BIG if it is
942+
* incompressible, or an error code.
943+
*/
944+
static ssize_t lzo_compress_inline_extent(void *buf, u64 size, char **comp_buf,
945+
char *wrkmem)
946+
{
947+
ssize_t ret;
948+
size_t out_len, out_size;
949+
void *out = NULL;
950+
951+
out_size = LZO_LEN + LZO_LEN + lzo_max_outlen(size);
952+
953+
out = malloc(out_size);
954+
if (!out) {
955+
error_msg(ERROR_MSG_MEMORY, NULL);
956+
ret = -ENOMEM;
957+
goto out;
958+
}
959+
960+
ret = lzo1x_1_compress(buf, size, out + LZO_LEN + LZO_LEN, &out_len,
961+
wrkmem);
962+
if (ret) {
963+
error("lzo1x_1_compress returned %zi", ret);
964+
ret = -EINVAL;
965+
goto out;
966+
}
967+
968+
if (out_len + LZO_LEN + LZO_LEN >= size) {
969+
ret = -E2BIG;
970+
goto out;
971+
}
972+
973+
put_unaligned_le32(out_len + LZO_LEN + LZO_LEN, out);
974+
put_unaligned_le32(out_len, out + LZO_LEN);
975+
976+
*comp_buf = out;
977+
ret = out_len + LZO_LEN + LZO_LEN;
978+
979+
out:
980+
if (ret < 0)
981+
free(out);
982+
983+
return ret;
984+
}
985+
#endif
986+
852987
#if COMPRESSION_ZSTD
853988
/*
854989
* Returns the size of the compressed data if successful, -E2BIG if it is
@@ -937,7 +1072,7 @@ static int add_file_items(struct btrfs_trans_handle *trans,
9371072
ssize_t ret_read;
9381073
u32 sectorsize = fs_info->sectorsize;
9391074
u64 file_pos = 0;
940-
char *buf = NULL, *comp_buf = NULL;
1075+
char *buf = NULL, *comp_buf = NULL, *wrkmem = NULL;
9411076
struct source_descriptor source;
9421077
int fd;
9431078

@@ -950,6 +1085,20 @@ static int add_file_items(struct btrfs_trans_handle *trans,
9501085
return ret;
9511086
}
9521087

1088+
if (g_compression == BTRFS_COMPRESS_LZO) {
1089+
#if COMPRESSION_LZO
1090+
wrkmem = malloc(LZO1X_1_MEM_COMPRESS);
1091+
if (!wrkmem) {
1092+
ret = -ENOMEM;
1093+
goto end;
1094+
}
1095+
#else
1096+
error("lzo support not compiled in");
1097+
ret = -EINVAL;
1098+
goto end;
1099+
#endif
1100+
}
1101+
9531102
if (st->st_size <= BTRFS_MAX_INLINE_DATA_SIZE(fs_info) &&
9541103
st->st_size < sectorsize) {
9551104
char *buffer = malloc(st->st_size);
@@ -972,6 +1121,12 @@ static int add_file_items(struct btrfs_trans_handle *trans,
9721121
ret = zlib_compress_inline_extent(buffer, st->st_size,
9731122
&comp_buf);
9741123
break;
1124+
#if COMPRESSION_LZO
1125+
case BTRFS_COMPRESS_LZO:
1126+
ret = lzo_compress_inline_extent(buffer, st->st_size,
1127+
&comp_buf, wrkmem);
1128+
break;
1129+
#endif
9751130
#if COMPRESSION_ZSTD
9761131
case BTRFS_COMPRESS_ZSTD:
9771132
ret = zstd_compress_inline_extent(buffer, st->st_size,
@@ -1007,7 +1162,47 @@ static int add_file_items(struct btrfs_trans_handle *trans,
10071162
goto end;
10081163
}
10091164

1010-
if (g_compression != BTRFS_COMPRESS_NONE) {
1165+
if (g_compression == BTRFS_COMPRESS_LZO) {
1166+
#if COMPRESSION_LZO
1167+
unsigned int sectors;
1168+
size_t comp_buf_len;
1169+
1170+
/*
1171+
* LZO helpfully doesn't provide a way to specify the output
1172+
* buffer size, so we need to allocate for the worst-case
1173+
* scenario to avoid buffer overruns.
1174+
*
1175+
* 4 bytes for the total size
1176+
* And for each sector:
1177+
* - 4 bytes for the compressed sector size
1178+
* - the worst-case output size
1179+
* - 3 bytes for possible padding
1180+
*/
1181+
1182+
sectors = BTRFS_MAX_COMPRESSED / sectorsize;
1183+
1184+
comp_buf_len = LZO_LEN;
1185+
comp_buf_len += (LZO_LEN + lzo_max_outlen(sectorsize) +
1186+
LZO_LEN - 1) * sectors;
1187+
1188+
comp_buf = malloc(comp_buf_len);
1189+
if (!comp_buf) {
1190+
ret = -ENOMEM;
1191+
goto end;
1192+
}
1193+
1194+
ret = lzo_init();
1195+
if (ret) {
1196+
error("lzo_init returned %i", ret);
1197+
ret = -EINVAL;
1198+
goto end;
1199+
}
1200+
#else
1201+
error("lzo support not compiled in");
1202+
ret = -EINVAL;
1203+
goto end;
1204+
#endif
1205+
} else if (g_compression != BTRFS_COMPRESS_NONE) {
10111206
comp_buf = malloc(BTRFS_MAX_COMPRESSED);
10121207
if (!comp_buf) {
10131208
ret = -ENOMEM;
@@ -1020,6 +1215,7 @@ static int add_file_items(struct btrfs_trans_handle *trans,
10201215
source.size = st->st_size;
10211216
source.path_name = path_name;
10221217
source.comp_buf = comp_buf;
1218+
source.wrkmem = wrkmem;
10231219

10241220
while (file_pos < st->st_size) {
10251221
ret = add_file_item_extent(trans, root, btrfs_inode, objectid,
@@ -1031,6 +1227,7 @@ static int add_file_items(struct btrfs_trans_handle *trans,
10311227
}
10321228

10331229
end:
1230+
free(wrkmem);
10341231
free(comp_buf);
10351232
free(buf);
10361233
close(fd);
@@ -1451,7 +1648,13 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
14511648

14521649
switch (compression) {
14531650
case BTRFS_COMPRESS_NONE:
1651+
case BTRFS_COMPRESS_LZO:
1652+
#if !COMPRESSION_LZO
1653+
error("lzo support not compiled in");
1654+
return -EINVAL;
1655+
#else
14541656
break;
1657+
#endif
14551658
case BTRFS_COMPRESS_ZLIB:
14561659
if (compression_level > ZLIB_BTRFS_MAX_LEVEL)
14571660
compression_level = ZLIB_BTRFS_MAX_LEVEL;

0 commit comments

Comments
 (0)