From: Namjae Jeon Date: Wed, 10 Jun 2026 14:40:58 +0000 (+0900) Subject: cifs: negotiate chained SMB2 compression capabilities X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=17e12b1d14134d88d39a7a366391280e6775c2d6;p=thirdparty%2Fkernel%2Flinux.git cifs: negotiate chained SMB2 compression capabilities Advertise LZ77 and Pattern_V1 with chained transform support in the SMB 3.1.1 compression negotiate context. Validate the server's returned algorithm list and flags, then retain the negotiated capabilities for a future compressed transform receive implementation. This patch only negotiates capabilities. It does not request compressed READ responses or add a compressed transform receive path. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 82e0adc1dabd0..a3ac4aa4bbc6f 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -789,6 +789,8 @@ struct TCP_Server_Info { struct { bool requested; /* "compress" mount option set*/ bool enabled; /* actually negotiated with server */ + bool chained; /* chained transforms were negotiated */ + bool pattern; /* Pattern_V1 chained payloads were negotiated */ __le16 alg; /* preferred alg negotiated with server */ } compression; __u16 signing_algorithm; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index fbeb2156ddb6b..a79d8e6091d0b 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -636,10 +636,16 @@ build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt) pneg_ctxt->DataLength = cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) - sizeof(struct smb2_neg_context)); - pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3); + /* + * Pattern_V1 is useful only as part of a chained transform. LZ77 remains + * the preferred general-purpose algorithm selected by this client. + */ + pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(4); + pneg_ctxt->Flags = SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED; pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77; pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF; pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1; + pneg_ctxt->CompressionAlgorithms[3] = SMB3_COMPRESS_PATTERN; } static unsigned int @@ -827,9 +833,12 @@ static void decode_compress_ctx(struct TCP_Server_Info *server, struct smb2_compression_capabilities_context *ctxt) { unsigned int len = le16_to_cpu(ctxt->DataLength); - __le16 alg; + unsigned int count, i; server->compression.enabled = false; + server->compression.chained = false; + server->compression.pattern = false; + server->compression.alg = SMB3_COMPRESS_NONE; /* * Caller checked that DataLength remains within SMB boundary. We still @@ -841,20 +850,37 @@ static void decode_compress_ctx(struct TCP_Server_Info *server, return; } - if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { + count = le16_to_cpu(ctxt->CompressionAlgorithmCount); + if (!count || count > ARRAY_SIZE(ctxt->CompressionAlgorithms) || + len < 8 + count * sizeof(__le16)) { pr_warn_once("invalid SMB3 compress algorithm count\n"); return; } - alg = ctxt->CompressionAlgorithms[0]; - - /* 'NONE' (0) compressor type is never negotiated */ - if (alg == 0 || le16_to_cpu(alg) > 3) { - pr_warn_once("invalid compression algorithm '%u'\n", alg); + if (ctxt->Flags != SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE && + ctxt->Flags != SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED) { + pr_warn_once("invalid SMB3 compression flags\n"); return; } - server->compression.alg = alg; + for (i = 0; i < count; i++) { + /* Record the intersection supported by the shared SMB codec. */ + if (ctxt->CompressionAlgorithms[i] == SMB3_COMPRESS_LZ77) + server->compression.alg = SMB3_COMPRESS_LZ77; + else if (ctxt->CompressionAlgorithms[i] == SMB3_COMPRESS_PATTERN) + server->compression.pattern = true; + } + if (server->compression.alg != SMB3_COMPRESS_LZ77) + return; + + /* + * Pattern_V1 cannot appear in an unchained transform even if a broken + * peer lists it in the algorithm array. + */ + server->compression.chained = + ctxt->Flags == SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED; + if (!server->compression.chained) + server->compression.pattern = false; server->compression.enabled = true; }