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,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
634700static 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
10331229end :
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