16234f501SNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later
26234f501SNamjae Jeon /*
36234f501SNamjae Jeon * SMB2 compression transform helpers.
46234f501SNamjae Jeon *
56234f501SNamjae Jeon * Copyright (C) 2026 Namjae Jeon <linkinjeon@kernel.org>
66234f501SNamjae Jeon */
76234f501SNamjae Jeon #include <linux/module.h>
86234f501SNamjae Jeon #include <linux/overflow.h>
96234f501SNamjae Jeon #include <linux/string.h>
106234f501SNamjae Jeon #include <linux/unaligned.h>
116234f501SNamjae Jeon
126234f501SNamjae Jeon #include "compress.h"
136234f501SNamjae Jeon #include "lz77.h"
146234f501SNamjae Jeon
156234f501SNamjae Jeon #define SMB2_COMPRESSION_CHAINED_HDR_LEN \
166234f501SNamjae Jeon offsetof(struct smb2_compression_hdr, CompressionAlgorithm)
176234f501SNamjae Jeon #define SMB2_COMPRESSION_PAYLOAD_BASE_LEN \
186234f501SNamjae Jeon (sizeof(struct smb2_compression_payload_hdr) - sizeof(__le32))
196234f501SNamjae Jeon
206234f501SNamjae Jeon /*
216234f501SNamjae Jeon * A NONE payload carries bytes verbatim. Keep both cursors and remaining
226234f501SNamjae Jeon * lengths together so every chained payload handler applies identical bounds
236234f501SNamjae Jeon * accounting.
246234f501SNamjae Jeon */
smb_decompress_none(const u8 ** src,u32 * slen,u8 ** dst,u32 * dlen,u32 len)256234f501SNamjae Jeon static int smb_decompress_none(const u8 **src, u32 *slen, u8 **dst, u32 *dlen,
266234f501SNamjae Jeon u32 len)
276234f501SNamjae Jeon {
286234f501SNamjae Jeon if (len > *slen || len > *dlen)
296234f501SNamjae Jeon return -EINVAL;
306234f501SNamjae Jeon
316234f501SNamjae Jeon memcpy(*dst, *src, len);
326234f501SNamjae Jeon *src += len;
336234f501SNamjae Jeon *slen -= len;
346234f501SNamjae Jeon *dst += len;
356234f501SNamjae Jeon *dlen -= len;
366234f501SNamjae Jeon return 0;
376234f501SNamjae Jeon }
386234f501SNamjae Jeon
396234f501SNamjae Jeon /*
406234f501SNamjae Jeon * Pattern_V1 represents a run of one byte. Its wire payload is always the
416234f501SNamjae Jeon * fixed-size smb2_compression_pattern_v1 structure.
426234f501SNamjae Jeon */
smb_decompress_pattern(const u8 ** src,u32 * slen,u8 ** dst,u32 * dlen,u32 len)436234f501SNamjae Jeon static int smb_decompress_pattern(const u8 **src, u32 *slen, u8 **dst,
446234f501SNamjae Jeon u32 *dlen, u32 len)
456234f501SNamjae Jeon {
466234f501SNamjae Jeon const struct smb2_compression_pattern_v1 *pattern;
476234f501SNamjae Jeon u32 repetitions;
486234f501SNamjae Jeon
496234f501SNamjae Jeon if (len != sizeof(*pattern) || len > *slen)
506234f501SNamjae Jeon return -EINVAL;
516234f501SNamjae Jeon
526234f501SNamjae Jeon pattern = (const struct smb2_compression_pattern_v1 *)*src;
536234f501SNamjae Jeon repetitions = le32_to_cpu(pattern->Repetitions);
546234f501SNamjae Jeon if (repetitions > *dlen)
556234f501SNamjae Jeon return -EINVAL;
566234f501SNamjae Jeon
576234f501SNamjae Jeon memset(*dst, pattern->Pattern, repetitions);
586234f501SNamjae Jeon *src += len;
596234f501SNamjae Jeon *slen -= len;
606234f501SNamjae Jeon *dst += repetitions;
616234f501SNamjae Jeon *dlen -= repetitions;
626234f501SNamjae Jeon return 0;
636234f501SNamjae Jeon }
646234f501SNamjae Jeon
656234f501SNamjae Jeon /*
666234f501SNamjae Jeon * LZ77 payload Length includes the four-byte OriginalPayloadSize field.
676234f501SNamjae Jeon * Consume that field before passing the compressed stream to the raw codec.
686234f501SNamjae Jeon */
smb_decompress_lz77_payload(const u8 ** src,u32 * slen,u8 ** dst,u32 * dlen,u32 len)696234f501SNamjae Jeon static int smb_decompress_lz77_payload(const u8 **src, u32 *slen, u8 **dst,
706234f501SNamjae Jeon u32 *dlen, u32 len)
716234f501SNamjae Jeon {
726234f501SNamjae Jeon u32 orig_size;
736234f501SNamjae Jeon int rc;
746234f501SNamjae Jeon
756234f501SNamjae Jeon if (len < sizeof(__le32) || len > *slen)
766234f501SNamjae Jeon return -EINVAL;
776234f501SNamjae Jeon
786234f501SNamjae Jeon orig_size = get_unaligned_le32(*src);
796234f501SNamjae Jeon if (orig_size > *dlen)
806234f501SNamjae Jeon return -EINVAL;
816234f501SNamjae Jeon
826234f501SNamjae Jeon *src += sizeof(__le32);
836234f501SNamjae Jeon *slen -= sizeof(__le32);
846234f501SNamjae Jeon len -= sizeof(__le32);
856234f501SNamjae Jeon
866234f501SNamjae Jeon rc = smb_lz77_decompress(*src, len, *dst, orig_size);
876234f501SNamjae Jeon if (rc)
886234f501SNamjae Jeon return rc;
896234f501SNamjae Jeon
906234f501SNamjae Jeon *src += len;
916234f501SNamjae Jeon *slen -= len;
926234f501SNamjae Jeon *dst += orig_size;
936234f501SNamjae Jeon *dlen -= orig_size;
946234f501SNamjae Jeon return 0;
956234f501SNamjae Jeon }
966234f501SNamjae Jeon
smb_decompress_chained(__le16 alg,bool allow_chained,bool allow_pattern,const struct smb2_compression_hdr * hdr,u32 slen,void * dst,u32 dlen)976234f501SNamjae Jeon static int smb_decompress_chained(__le16 alg, bool allow_chained,
98*0710dd08SAnatolii Shumak bool allow_pattern,
996234f501SNamjae Jeon const struct smb2_compression_hdr *hdr,
1006234f501SNamjae Jeon u32 slen, void *dst, u32 dlen)
1016234f501SNamjae Jeon {
1026234f501SNamjae Jeon const struct smb2_compression_payload_hdr *payload;
1036234f501SNamjae Jeon const u8 *src = (const u8 *)hdr + SMB2_COMPRESSION_CHAINED_HDR_LEN;
1046234f501SNamjae Jeon u32 orig_size = le32_to_cpu(hdr->OriginalCompressedSegmentSize);
1056234f501SNamjae Jeon u32 remaining = slen - SMB2_COMPRESSION_CHAINED_HDR_LEN;
1066234f501SNamjae Jeon u8 *out = dst;
1076234f501SNamjae Jeon u32 out_remaining = dlen;
1086234f501SNamjae Jeon bool first = true;
1096234f501SNamjae Jeon int rc;
1106234f501SNamjae Jeon
1116234f501SNamjae Jeon if (!allow_chained || orig_size != dlen)
1126234f501SNamjae Jeon return -EINVAL;
1136234f501SNamjae Jeon
1146234f501SNamjae Jeon /*
1156234f501SNamjae Jeon * The chained transform has an eight-byte top-level header. The next
1166234f501SNamjae Jeon * bytes are a sequence of payload headers whose Length fields account
1176234f501SNamjae Jeon * for payload data, including OriginalPayloadSize where applicable.
1186234f501SNamjae Jeon */
1196234f501SNamjae Jeon while (remaining) {
1206234f501SNamjae Jeon __le16 payload_alg;
1216234f501SNamjae Jeon __le16 flags;
1226234f501SNamjae Jeon u32 len;
1236234f501SNamjae Jeon
1246234f501SNamjae Jeon if (remaining < SMB2_COMPRESSION_PAYLOAD_BASE_LEN)
1256234f501SNamjae Jeon return -EINVAL;
1266234f501SNamjae Jeon
1276234f501SNamjae Jeon payload = (const struct smb2_compression_payload_hdr *)src;
1286234f501SNamjae Jeon payload_alg = payload->CompressionAlgorithm;
1296234f501SNamjae Jeon flags = payload->Flags;
1306234f501SNamjae Jeon len = le32_to_cpu(payload->Length);
1316234f501SNamjae Jeon
1326234f501SNamjae Jeon /*
1336234f501SNamjae Jeon * CHAINED marks only the first payload. Requiring NONE on every
1346234f501SNamjae Jeon * later payload rejects ambiguous or independently chained data.
1356234f501SNamjae Jeon */
1366234f501SNamjae Jeon if ((first && flags != cpu_to_le16(SMB2_COMPRESSION_FLAG_CHAINED)) ||
1376234f501SNamjae Jeon (!first && flags != cpu_to_le16(SMB2_COMPRESSION_FLAG_NONE)))
1386234f501SNamjae Jeon return -EINVAL;
1396234f501SNamjae Jeon
1406234f501SNamjae Jeon src += SMB2_COMPRESSION_PAYLOAD_BASE_LEN;
1416234f501SNamjae Jeon remaining -= SMB2_COMPRESSION_PAYLOAD_BASE_LEN;
1426234f501SNamjae Jeon
1436234f501SNamjae Jeon if (payload_alg == SMB3_COMPRESS_NONE) {
1446234f501SNamjae Jeon rc = smb_decompress_none(&src, &remaining, &out,
1456234f501SNamjae Jeon &out_remaining, len);
1466234f501SNamjae Jeon } else if (payload_alg == SMB3_COMPRESS_PATTERN) {
147*0710dd08SAnatolii Shumak if (!allow_pattern)
148*0710dd08SAnatolii Shumak return -EINVAL;
1496234f501SNamjae Jeon rc = smb_decompress_pattern(&src, &remaining, &out,
1506234f501SNamjae Jeon &out_remaining, len);
1516234f501SNamjae Jeon } else if (payload_alg == alg && alg == SMB3_COMPRESS_LZ77) {
1526234f501SNamjae Jeon rc = smb_decompress_lz77_payload(&src, &remaining, &out,
1536234f501SNamjae Jeon &out_remaining, len);
1546234f501SNamjae Jeon } else {
1556234f501SNamjae Jeon return -EINVAL;
1566234f501SNamjae Jeon }
1576234f501SNamjae Jeon if (rc)
1586234f501SNamjae Jeon return rc;
1596234f501SNamjae Jeon first = false;
1606234f501SNamjae Jeon }
1616234f501SNamjae Jeon
1626234f501SNamjae Jeon return out_remaining ? -EINVAL : 0;
1636234f501SNamjae Jeon }
1646234f501SNamjae Jeon
smb_decompress_unchained(__le16 alg,const struct smb2_compression_hdr * hdr,u32 slen,void * dst,u32 dlen)1656234f501SNamjae Jeon static int smb_decompress_unchained(__le16 alg,
1666234f501SNamjae Jeon const struct smb2_compression_hdr *hdr,
1676234f501SNamjae Jeon u32 slen, void *dst, u32 dlen)
1686234f501SNamjae Jeon {
1696234f501SNamjae Jeon u32 orig_size, offset, comp_size;
1706234f501SNamjae Jeon
1716234f501SNamjae Jeon if (hdr->CompressionAlgorithm != alg ||
1726234f501SNamjae Jeon !smb_compress_alg_valid(hdr->CompressionAlgorithm, false))
1736234f501SNamjae Jeon return -EINVAL;
1746234f501SNamjae Jeon
1756234f501SNamjae Jeon orig_size = le32_to_cpu(hdr->OriginalCompressedSegmentSize);
1766234f501SNamjae Jeon offset = le32_to_cpu(hdr->Offset);
1776234f501SNamjae Jeon if (offset > slen - sizeof(*hdr) || offset > dlen ||
1786234f501SNamjae Jeon orig_size > dlen - offset || orig_size + offset != dlen)
1796234f501SNamjae Jeon return -EINVAL;
1806234f501SNamjae Jeon
1816234f501SNamjae Jeon memcpy(dst, (const u8 *)hdr + sizeof(*hdr), offset);
1826234f501SNamjae Jeon comp_size = slen - sizeof(*hdr) - offset;
1836234f501SNamjae Jeon return smb_lz77_decompress((const u8 *)hdr + sizeof(*hdr) + offset,
1846234f501SNamjae Jeon comp_size, (u8 *)dst + offset, orig_size);
1856234f501SNamjae Jeon }
1866234f501SNamjae Jeon
1876234f501SNamjae Jeon /**
1886234f501SNamjae Jeon * smb_compression_decompress() - decode an SMB2 compression transform
1896234f501SNamjae Jeon * @alg: negotiated general-purpose compression algorithm
1906234f501SNamjae Jeon * @allow_chained: whether chained transforms were negotiated
191*0710dd08SAnatolii Shumak * @allow_pattern: whether Pattern_V1 payloads were negotiated
1926234f501SNamjae Jeon * @src: transform header followed by compressed payload data
1936234f501SNamjae Jeon * @slen: total number of bytes available at @src
1946234f501SNamjae Jeon * @dst: output buffer for the reconstructed SMB2 message
1956234f501SNamjae Jeon * @dlen: exact expected size of the reconstructed SMB2 message
1966234f501SNamjae Jeon *
1976234f501SNamjae Jeon * Validate the transform type and negotiated capabilities before dispatching
1986234f501SNamjae Jeon * to the chained or unchained decoder. The caller supplies the expected output
1996234f501SNamjae Jeon * size after applying its transport-specific message size limits.
2006234f501SNamjae Jeon *
2016234f501SNamjae Jeon * Return: 0 on success, otherwise a negative errno.
2026234f501SNamjae Jeon */
smb_compression_decompress(__le16 alg,bool allow_chained,bool allow_pattern,const void * src,u32 slen,void * dst,u32 dlen)2036234f501SNamjae Jeon int smb_compression_decompress(__le16 alg, bool allow_chained,
204*0710dd08SAnatolii Shumak bool allow_pattern, const void *src, u32 slen,
205*0710dd08SAnatolii Shumak void *dst, u32 dlen)
2066234f501SNamjae Jeon {
2076234f501SNamjae Jeon const struct smb2_compression_hdr *hdr = src;
2086234f501SNamjae Jeon
2096234f501SNamjae Jeon if (!src || !dst || slen < sizeof(*hdr) ||
2106234f501SNamjae Jeon hdr->ProtocolId != SMB2_COMPRESSION_TRANSFORM_ID ||
2116234f501SNamjae Jeon alg == SMB3_COMPRESS_NONE)
2126234f501SNamjae Jeon return -EINVAL;
2136234f501SNamjae Jeon
2146234f501SNamjae Jeon if (hdr->Flags == cpu_to_le16(SMB2_COMPRESSION_FLAG_CHAINED))
215*0710dd08SAnatolii Shumak return smb_decompress_chained(alg, allow_chained, allow_pattern,
216*0710dd08SAnatolii Shumak hdr, slen, dst, dlen);
2176234f501SNamjae Jeon
2186234f501SNamjae Jeon if (hdr->Flags != cpu_to_le16(SMB2_COMPRESSION_FLAG_NONE))
2196234f501SNamjae Jeon return -EINVAL;
2206234f501SNamjae Jeon
2216234f501SNamjae Jeon return smb_decompress_unchained(alg, hdr, slen, dst, dlen);
2226234f501SNamjae Jeon }
2236234f501SNamjae Jeon EXPORT_SYMBOL_GPL(smb_compression_decompress);
2246234f501SNamjae Jeon
2256234f501SNamjae Jeon struct smb_compression_builder {
2266234f501SNamjae Jeon u8 *pos;
2276234f501SNamjae Jeon u32 remaining;
2286234f501SNamjae Jeon bool first;
2296234f501SNamjae Jeon };
2306234f501SNamjae Jeon
2316234f501SNamjae Jeon /*
2326234f501SNamjae Jeon * Reserve one chained payload header and initialize its common fields.
2336234f501SNamjae Jeon * OriginalPayloadSize is present only for LZNT1/LZ77/LZ77+Huffman payloads.
2346234f501SNamjae Jeon */
2356234f501SNamjae Jeon static struct smb2_compression_payload_hdr *
smb_compression_add_payload(struct smb_compression_builder * builder,__le16 alg,u32 payload_len,bool orig_size)2366234f501SNamjae Jeon smb_compression_add_payload(struct smb_compression_builder *builder,
2376234f501SNamjae Jeon __le16 alg, u32 payload_len, bool orig_size)
2386234f501SNamjae Jeon {
2396234f501SNamjae Jeon struct smb2_compression_payload_hdr *payload;
2406234f501SNamjae Jeon u32 hdr_len = SMB2_COMPRESSION_PAYLOAD_BASE_LEN;
2416234f501SNamjae Jeon u32 total_len;
2426234f501SNamjae Jeon
2436234f501SNamjae Jeon if (orig_size)
2446234f501SNamjae Jeon hdr_len += sizeof(payload->OriginalPayloadSize);
2456234f501SNamjae Jeon if (check_add_overflow(hdr_len, payload_len, &total_len) ||
2466234f501SNamjae Jeon total_len > builder->remaining)
2476234f501SNamjae Jeon return NULL;
2486234f501SNamjae Jeon
2496234f501SNamjae Jeon payload = (struct smb2_compression_payload_hdr *)builder->pos;
2506234f501SNamjae Jeon payload->CompressionAlgorithm = alg;
2516234f501SNamjae Jeon payload->Flags = cpu_to_le16(builder->first ?
2526234f501SNamjae Jeon SMB2_COMPRESSION_FLAG_CHAINED : SMB2_COMPRESSION_FLAG_NONE);
2536234f501SNamjae Jeon payload->Length = cpu_to_le32(payload_len +
2546234f501SNamjae Jeon (orig_size ? sizeof(payload->OriginalPayloadSize) : 0));
2556234f501SNamjae Jeon
2566234f501SNamjae Jeon builder->pos += hdr_len;
2576234f501SNamjae Jeon builder->remaining -= hdr_len;
2586234f501SNamjae Jeon builder->first = false;
2596234f501SNamjae Jeon return payload;
2606234f501SNamjae Jeon }
2616234f501SNamjae Jeon
smb_compression_add_pattern(struct smb_compression_builder * builder,u8 pattern,u32 repetitions)2626234f501SNamjae Jeon static int smb_compression_add_pattern(struct smb_compression_builder *builder,
2636234f501SNamjae Jeon u8 pattern, u32 repetitions)
2646234f501SNamjae Jeon {
2656234f501SNamjae Jeon struct smb2_compression_pattern_v1 *payload;
2666234f501SNamjae Jeon
2676234f501SNamjae Jeon if (!smb_compression_add_payload(builder, SMB3_COMPRESS_PATTERN,
2686234f501SNamjae Jeon sizeof(*payload), false))
2696234f501SNamjae Jeon return -ENOSPC;
2706234f501SNamjae Jeon
2716234f501SNamjae Jeon payload = (struct smb2_compression_pattern_v1 *)builder->pos;
2726234f501SNamjae Jeon payload->Pattern = pattern;
2736234f501SNamjae Jeon payload->Reserved1 = 0;
2746234f501SNamjae Jeon payload->Reserved2 = 0;
2756234f501SNamjae Jeon payload->Repetitions = cpu_to_le32(repetitions);
2766234f501SNamjae Jeon builder->pos += sizeof(*payload);
2776234f501SNamjae Jeon builder->remaining -= sizeof(*payload);
2786234f501SNamjae Jeon return 0;
2796234f501SNamjae Jeon }
2806234f501SNamjae Jeon
smb_compression_add_none(struct smb_compression_builder * builder,const u8 * src,u32 len)2816234f501SNamjae Jeon static int smb_compression_add_none(struct smb_compression_builder *builder,
2826234f501SNamjae Jeon const u8 *src, u32 len)
2836234f501SNamjae Jeon {
2846234f501SNamjae Jeon if (!smb_compression_add_payload(builder, SMB3_COMPRESS_NONE, len, false))
2856234f501SNamjae Jeon return -ENOSPC;
2866234f501SNamjae Jeon
2876234f501SNamjae Jeon memcpy(builder->pos, src, len);
2886234f501SNamjae Jeon builder->pos += len;
2896234f501SNamjae Jeon builder->remaining -= len;
2906234f501SNamjae Jeon return 0;
2916234f501SNamjae Jeon }
2926234f501SNamjae Jeon
smb_compression_add_lz77(struct smb_compression_builder * builder,const u8 * src,u32 len)2936234f501SNamjae Jeon static int smb_compression_add_lz77(struct smb_compression_builder *builder,
2946234f501SNamjae Jeon const u8 *src, u32 len)
2956234f501SNamjae Jeon {
2966234f501SNamjae Jeon struct smb2_compression_payload_hdr *payload;
2976234f501SNamjae Jeon u32 comp_len;
2986234f501SNamjae Jeon int rc;
2996234f501SNamjae Jeon
3006234f501SNamjae Jeon if (builder->remaining <= sizeof(*payload))
3016234f501SNamjae Jeon return -ENOSPC;
3026234f501SNamjae Jeon
3036234f501SNamjae Jeon comp_len = builder->remaining - sizeof(*payload);
3046234f501SNamjae Jeon payload = smb_compression_add_payload(builder, SMB3_COMPRESS_LZ77,
3056234f501SNamjae Jeon comp_len, true);
3066234f501SNamjae Jeon if (!payload)
3076234f501SNamjae Jeon return -ENOSPC;
3086234f501SNamjae Jeon
3096234f501SNamjae Jeon rc = smb_lz77_compress(src, len, builder->pos, &comp_len);
3106234f501SNamjae Jeon if (rc)
3116234f501SNamjae Jeon return rc;
3126234f501SNamjae Jeon
3136234f501SNamjae Jeon payload->Length = cpu_to_le32(comp_len +
3146234f501SNamjae Jeon sizeof(payload->OriginalPayloadSize));
3156234f501SNamjae Jeon payload->OriginalPayloadSize = cpu_to_le32(len);
3166234f501SNamjae Jeon builder->pos += comp_len;
3176234f501SNamjae Jeon builder->remaining -= comp_len;
3186234f501SNamjae Jeon return 0;
3196234f501SNamjae Jeon }
3206234f501SNamjae Jeon
3216234f501SNamjae Jeon /**
3226234f501SNamjae Jeon * smb_compression_compress_chained() - build a chained SMB2 transform
3236234f501SNamjae Jeon * @alg: negotiated general-purpose compression algorithm
3246234f501SNamjae Jeon * @allow_pattern: whether Pattern_V1 was negotiated
3256234f501SNamjae Jeon * @src: complete uncompressed SMB2 message
3266234f501SNamjae Jeon * @slen: size of @src
3276234f501SNamjae Jeon * @dst: output buffer for the transform
3286234f501SNamjae Jeon * @dlen: input capacity of @dst and output transform size
3296234f501SNamjae Jeon *
3306234f501SNamjae Jeon * Following the algorithm in [MS-SMB2] 3.1.4.4, encode sufficiently long
3316234f501SNamjae Jeon * repeated runs at the front and back as Pattern_V1 payloads. Compress a
3326234f501SNamjae Jeon * middle region larger than 1 KiB with LZ77; smaller middle regions are
3336234f501SNamjae Jeon * represented by a chained NONE payload.
3346234f501SNamjae Jeon *
3356234f501SNamjae Jeon * This helper does not decide whether the final transform is smaller than the
3366234f501SNamjae Jeon * original message. The transport caller owns that policy decision.
3376234f501SNamjae Jeon *
3386234f501SNamjae Jeon * Return: 0 on success, otherwise a negative errno.
3396234f501SNamjae Jeon */
smb_compression_compress_chained(__le16 alg,bool allow_pattern,const void * src,u32 slen,void * dst,u32 * dlen)3406234f501SNamjae Jeon int smb_compression_compress_chained(__le16 alg, bool allow_pattern,
3416234f501SNamjae Jeon const void *src, u32 slen,
3426234f501SNamjae Jeon void *dst, u32 *dlen)
3436234f501SNamjae Jeon {
3446234f501SNamjae Jeon struct smb2_compression_hdr *hdr = dst;
3456234f501SNamjae Jeon struct smb_compression_builder builder;
3466234f501SNamjae Jeon const u8 *input = src;
3476234f501SNamjae Jeon u32 forward = 0, backward = 0, middle_len;
3486234f501SNamjae Jeon int rc;
3496234f501SNamjae Jeon
3506234f501SNamjae Jeon if (!src || !dst || !dlen || alg != SMB3_COMPRESS_LZ77 ||
3516234f501SNamjae Jeon *dlen <= SMB2_COMPRESSION_CHAINED_HDR_LEN || !slen)
3526234f501SNamjae Jeon return -EINVAL;
3536234f501SNamjae Jeon
3546234f501SNamjae Jeon hdr->ProtocolId = SMB2_COMPRESSION_TRANSFORM_ID;
3556234f501SNamjae Jeon hdr->OriginalCompressedSegmentSize = cpu_to_le32(slen);
3566234f501SNamjae Jeon builder.pos = (u8 *)dst + SMB2_COMPRESSION_CHAINED_HDR_LEN;
3576234f501SNamjae Jeon builder.remaining = *dlen - SMB2_COMPRESSION_CHAINED_HDR_LEN;
3586234f501SNamjae Jeon builder.first = true;
3596234f501SNamjae Jeon
3606234f501SNamjae Jeon if (allow_pattern && slen > 32) {
3616234f501SNamjae Jeon for (forward = 1; forward < slen; forward++) {
3626234f501SNamjae Jeon if (input[forward] != input[0])
3636234f501SNamjae Jeon break;
3646234f501SNamjae Jeon }
3656234f501SNamjae Jeon if (forward <= 32)
3666234f501SNamjae Jeon forward = 0;
3676234f501SNamjae Jeon
3686234f501SNamjae Jeon for (backward = 1; backward < slen - forward; backward++) {
3696234f501SNamjae Jeon if (input[slen - backward - 1] != input[slen - 1])
3706234f501SNamjae Jeon break;
3716234f501SNamjae Jeon }
3726234f501SNamjae Jeon if (backward <= 32)
3736234f501SNamjae Jeon backward = 0;
3746234f501SNamjae Jeon }
3756234f501SNamjae Jeon
3766234f501SNamjae Jeon if (forward) {
3776234f501SNamjae Jeon rc = smb_compression_add_pattern(&builder, input[0], forward);
3786234f501SNamjae Jeon if (rc)
3796234f501SNamjae Jeon return rc;
3806234f501SNamjae Jeon }
3816234f501SNamjae Jeon
3826234f501SNamjae Jeon middle_len = slen - forward - backward;
3836234f501SNamjae Jeon if (middle_len > 1024)
3846234f501SNamjae Jeon rc = smb_compression_add_lz77(&builder, input + forward,
3856234f501SNamjae Jeon middle_len);
3866234f501SNamjae Jeon else if (middle_len)
3876234f501SNamjae Jeon rc = smb_compression_add_none(&builder,
3886234f501SNamjae Jeon input + forward, middle_len);
3896234f501SNamjae Jeon else
3906234f501SNamjae Jeon rc = 0;
3916234f501SNamjae Jeon if (rc)
3926234f501SNamjae Jeon return rc;
3936234f501SNamjae Jeon
3946234f501SNamjae Jeon if (backward) {
3956234f501SNamjae Jeon rc = smb_compression_add_pattern(&builder, input[slen - 1],
3966234f501SNamjae Jeon backward);
3976234f501SNamjae Jeon if (rc)
3986234f501SNamjae Jeon return rc;
3996234f501SNamjae Jeon }
4006234f501SNamjae Jeon
4016234f501SNamjae Jeon *dlen = builder.pos - (u8 *)dst;
4026234f501SNamjae Jeon return 0;
4036234f501SNamjae Jeon }
4046234f501SNamjae Jeon EXPORT_SYMBOL_GPL(smb_compression_compress_chained);
405