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"
5760#define ZSTD_BTRFS_DEFAULT_LEVEL 3
5861#define ZSTD_BTRFS_MAX_LEVEL 15
5962
63+ #define LZO_LEN 4
64+
6065static u32 fs_block_size ;
6166
6267/*
@@ -526,6 +531,67 @@ 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+ lzo_uint in_len , out_len ;
552+ size_t new_pos ;
553+ u32 padding ;
554+
555+ in_len = min ((size_t )sectorsize , in_size - (i * sectorsize ));
556+
557+ ret = lzo1x_1_compress (in_buf + (i * sectorsize ), in_len ,
558+ out_buf + out_pos + LZO_LEN , & out_len ,
559+ wrkmem );
560+ if (ret ) {
561+ error ("lzo1x_1_compress returned %i" , ret );
562+ return - EINVAL ;
563+ }
564+
565+ put_unaligned_le32 (out_len , out_buf + out_pos );
566+
567+ new_pos = out_pos + LZO_LEN + out_len ;
568+
569+ /* Make sure that our header doesn't cross a sector boundary. */
570+ if (new_pos / sectorsize != (new_pos + LZO_LEN - 1 ) / sectorsize )
571+ padding = round_up (new_pos , LZO_LEN ) - new_pos ;
572+ else
573+ padding = 0 ;
574+
575+ out_pos += out_len + LZO_LEN + padding ;
576+ total_size += out_len + LZO_LEN + padding ;
577+
578+ /*
579+ * Follow kernel in trying to compress the first three sectors,
580+ * then giving up if the output isn't any smaller.
581+ */
582+ if (i >= 3 && total_size > i * sectorsize )
583+ return - E2BIG ;
584+ }
585+
586+ if (total_size > in_size )
587+ return - E2BIG ;
588+
589+ put_unaligned_le32 (total_size , out_buf );
590+
591+ return total_size ;
592+ }
593+ #endif
594+
529595#if COMPRESSION_ZSTD
530596/*
531597 * Returns the size of the compressed data if successful, -E2BIG if it is
@@ -629,6 +695,7 @@ struct source_descriptor {
629695 u64 size ;
630696 const char * path_name ;
631697 char * comp_buf ;
698+ char * wrkmem ;
632699};
633700
634701static int add_file_item_extent (struct btrfs_trans_handle * trans ,
@@ -684,6 +751,14 @@ static int add_file_item_extent(struct btrfs_trans_handle *trans,
684751 source -> buf , bytes_read ,
685752 source -> comp_buf );
686753 break ;
754+ #if COMPRESSION_LZO
755+ case BTRFS_COMPRESS_LZO :
756+ comp_ret = lzo_compress_extent (sectorsize , source -> buf ,
757+ bytes_read ,
758+ source -> comp_buf ,
759+ source -> wrkmem );
760+ break ;
761+ #endif
687762#if COMPRESSION_ZSTD
688763 case BTRFS_COMPRESS_ZSTD :
689764 comp_ret = zstd_compress_extent (first_sector , sectorsize ,
@@ -748,6 +823,11 @@ static int add_file_item_extent(struct btrfs_trans_handle *trans,
748823 features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD ;
749824 btrfs_set_super_incompat_flags (trans -> fs_info -> super_copy ,
750825 features );
826+ } else if (g_compression == BTRFS_COMPRESS_LZO ) {
827+ features = btrfs_super_incompat_flags (trans -> fs_info -> super_copy );
828+ features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO ;
829+ btrfs_set_super_incompat_flags (trans -> fs_info -> super_copy ,
830+ features );
751831 }
752832 } else {
753833 to_write = round_up (to_read , sectorsize );
@@ -849,6 +929,63 @@ static ssize_t zlib_compress_inline_extent(char *buf, u64 size, char **comp_buf)
849929 return ret ;
850930}
851931
932+ #if COMPRESSION_LZO
933+ static u32 lzo_max_outlen (u32 inlen ) {
934+ /*
935+ * Return the worst-case output length for LZO. Formula comes from
936+ * LZO.FAQ.
937+ */
938+ return inlen + (inlen / 16 ) + 64 + 3 ;
939+ }
940+
941+ /*
942+ * Returns the size of the compressed data if successful, -E2BIG if it is
943+ * incompressible, or an error code.
944+ */
945+ static ssize_t lzo_compress_inline_extent (void * buf , u64 size , char * * comp_buf ,
946+ char * wrkmem )
947+ {
948+ ssize_t ret ;
949+ lzo_uint out_len ;
950+ size_t out_size ;
951+ void * out = NULL ;
952+
953+ out_size = LZO_LEN + LZO_LEN + lzo_max_outlen (size );
954+
955+ out = malloc (out_size );
956+ if (!out ) {
957+ error_msg (ERROR_MSG_MEMORY , NULL );
958+ ret = - ENOMEM ;
959+ goto out ;
960+ }
961+
962+ ret = lzo1x_1_compress (buf , size , out + LZO_LEN + LZO_LEN , & out_len ,
963+ wrkmem );
964+ if (ret ) {
965+ error ("lzo1x_1_compress returned %zi" , ret );
966+ ret = - EINVAL ;
967+ goto out ;
968+ }
969+
970+ if (out_len + LZO_LEN + LZO_LEN >= size ) {
971+ ret = - E2BIG ;
972+ goto out ;
973+ }
974+
975+ put_unaligned_le32 (out_len + LZO_LEN + LZO_LEN , out );
976+ put_unaligned_le32 (out_len , out + LZO_LEN );
977+
978+ * comp_buf = out ;
979+ ret = out_len + LZO_LEN + LZO_LEN ;
980+
981+ out :
982+ if (ret < 0 )
983+ free (out );
984+
985+ return ret ;
986+ }
987+ #endif
988+
852989#if COMPRESSION_ZSTD
853990/*
854991 * Returns the size of the compressed data if successful, -E2BIG if it is
@@ -937,7 +1074,7 @@ static int add_file_items(struct btrfs_trans_handle *trans,
9371074 ssize_t ret_read ;
9381075 u32 sectorsize = fs_info -> sectorsize ;
9391076 u64 file_pos = 0 ;
940- char * buf = NULL , * comp_buf = NULL ;
1077+ char * buf = NULL , * comp_buf = NULL , * wrkmem = NULL ;
9411078 struct source_descriptor source ;
9421079 int fd ;
9431080
@@ -950,6 +1087,20 @@ static int add_file_items(struct btrfs_trans_handle *trans,
9501087 return ret ;
9511088 }
9521089
1090+ if (g_compression == BTRFS_COMPRESS_LZO ) {
1091+ #if COMPRESSION_LZO
1092+ wrkmem = malloc (LZO1X_1_MEM_COMPRESS );
1093+ if (!wrkmem ) {
1094+ ret = - ENOMEM ;
1095+ goto end ;
1096+ }
1097+ #else
1098+ error ("lzo support not compiled in" );
1099+ ret = - EINVAL ;
1100+ goto end ;
1101+ #endif
1102+ }
1103+
9531104 if (st -> st_size <= BTRFS_MAX_INLINE_DATA_SIZE (fs_info ) &&
9541105 st -> st_size < sectorsize ) {
9551106 char * buffer = malloc (st -> st_size );
@@ -972,6 +1123,12 @@ static int add_file_items(struct btrfs_trans_handle *trans,
9721123 ret = zlib_compress_inline_extent (buffer , st -> st_size ,
9731124 & comp_buf );
9741125 break ;
1126+ #if COMPRESSION_LZO
1127+ case BTRFS_COMPRESS_LZO :
1128+ ret = lzo_compress_inline_extent (buffer , st -> st_size ,
1129+ & comp_buf , wrkmem );
1130+ break ;
1131+ #endif
9751132#if COMPRESSION_ZSTD
9761133 case BTRFS_COMPRESS_ZSTD :
9771134 ret = zstd_compress_inline_extent (buffer , st -> st_size ,
@@ -1007,7 +1164,47 @@ static int add_file_items(struct btrfs_trans_handle *trans,
10071164 goto end ;
10081165 }
10091166
1010- if (g_compression != BTRFS_COMPRESS_NONE ) {
1167+ if (g_compression == BTRFS_COMPRESS_LZO ) {
1168+ #if COMPRESSION_LZO
1169+ unsigned int sectors ;
1170+ size_t comp_buf_len ;
1171+
1172+ /*
1173+ * LZO helpfully doesn't provide a way to specify the output
1174+ * buffer size, so we need to allocate for the worst-case
1175+ * scenario to avoid buffer overruns.
1176+ *
1177+ * 4 bytes for the total size
1178+ * And for each sector:
1179+ * - 4 bytes for the compressed sector size
1180+ * - the worst-case output size
1181+ * - 3 bytes for possible padding
1182+ */
1183+
1184+ sectors = BTRFS_MAX_COMPRESSED / sectorsize ;
1185+
1186+ comp_buf_len = LZO_LEN ;
1187+ comp_buf_len += (LZO_LEN + lzo_max_outlen (sectorsize ) +
1188+ LZO_LEN - 1 ) * sectors ;
1189+
1190+ comp_buf = malloc (comp_buf_len );
1191+ if (!comp_buf ) {
1192+ ret = - ENOMEM ;
1193+ goto end ;
1194+ }
1195+
1196+ ret = lzo_init ();
1197+ if (ret ) {
1198+ error ("lzo_init returned %i" , ret );
1199+ ret = - EINVAL ;
1200+ goto end ;
1201+ }
1202+ #else
1203+ error ("lzo support not compiled in" );
1204+ ret = - EINVAL ;
1205+ goto end ;
1206+ #endif
1207+ } else if (g_compression != BTRFS_COMPRESS_NONE ) {
10111208 comp_buf = malloc (BTRFS_MAX_COMPRESSED );
10121209 if (!comp_buf ) {
10131210 ret = - ENOMEM ;
@@ -1020,6 +1217,7 @@ static int add_file_items(struct btrfs_trans_handle *trans,
10201217 source .size = st -> st_size ;
10211218 source .path_name = path_name ;
10221219 source .comp_buf = comp_buf ;
1220+ source .wrkmem = wrkmem ;
10231221
10241222 while (file_pos < st -> st_size ) {
10251223 ret = add_file_item_extent (trans , root , btrfs_inode , objectid ,
@@ -1031,6 +1229,7 @@ static int add_file_items(struct btrfs_trans_handle *trans,
10311229 }
10321230
10331231end :
1232+ free (wrkmem );
10341233 free (comp_buf );
10351234 free (buf );
10361235 close (fd );
@@ -1451,7 +1650,13 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
14511650
14521651 switch (compression ) {
14531652 case BTRFS_COMPRESS_NONE :
1653+ case BTRFS_COMPRESS_LZO :
1654+ #if !COMPRESSION_LZO
1655+ error ("lzo support not compiled in" );
1656+ return - EINVAL ;
1657+ #else
14541658 break ;
1659+ #endif
14551660 case BTRFS_COMPRESS_ZLIB :
14561661 if (compression_level > ZLIB_BTRFS_MAX_LEVEL )
14571662 compression_level = ZLIB_BTRFS_MAX_LEVEL ;
0 commit comments