summaryrefslogtreecommitdiff
path: root/fs/smb/client/compress.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb/client/compress.c')
-rw-r--r--fs/smb/client/compress.c80
1 files changed, 80 insertions, 0 deletions
diff --git a/fs/smb/client/compress.c b/fs/smb/client/compress.c
new file mode 100644
index 000000000000..12311b538689
--- /dev/null
+++ b/fs/smb/client/compress.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024, SUSE LLC
+ *
+ * Authors: Enzo Matsumiya <ematsumiya@suse.de>
+ *
+ * This file implements I/O compression support for SMB2 messages (SMB 3.1.1 only).
+ * See compress/ for implementation details of each algorithm.
+ *
+ * References:
+ * MS-SMB2 "3.1.4.4 Compressing the Message"
+ * MS-SMB2 "3.1.5.3 Decompressing the Chained Message"
+ * MS-XCA - for details of the supported algorithms
+ */
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/uio.h>
+
+#include "../common/smb2pdu.h"
+
+#include "compress/lz77.h"
+#include "compress.h"
+
+int smb_compress(struct smb_rqst *src_rq, struct smb_rqst *dst_rq)
+{
+ struct smb2_compression_hdr *hdr;
+ size_t buf_len, data_len;
+ struct iov_iter tmp = src_rq->rq_iter;
+ void *src, *dst;
+ int ret = - ENOMEM;
+
+ buf_len = src_rq->rq_iov->iov_len;
+ if (WARN(buf_len != sizeof(struct smb2_write_req),
+ "%s: unexpected buf len %zu\n", __func__, buf_len))
+ return -EIO;
+
+ data_len = iov_iter_count(&src_rq->rq_iter);
+ if (data_len < SMB_COMPRESS_MIN_LEN)
+ return -ENODATA;
+
+ src = kvzalloc(data_len, GFP_KERNEL);
+ if (!src)
+ goto err_free;
+
+ if (!copy_from_iter_full(src, data_len, &tmp)) {
+ ret = -EIO;
+ goto err_free;
+ }
+
+ dst_rq->rq_iov->iov_base = kvzalloc(SMB_COMPRESS_HDR_LEN + buf_len + data_len, GFP_KERNEL);
+ if (!dst_rq->rq_iov->iov_base)
+ goto err_free;
+
+ dst = dst_rq->rq_iov->iov_base;
+ hdr = dst;
+ hdr->ProtocolId = SMB2_COMPRESSION_TRANSFORM_ID;
+ hdr->OriginalCompressedSegmentSize = cpu_to_le32(data_len);
+ hdr->Offset = cpu_to_le32(buf_len);
+ hdr->Flags = SMB2_COMPRESSION_FLAG_NONE;
+ hdr->CompressionAlgorithm = SMB3_COMPRESS_LZ77;
+
+ /*
+ * Copy SMB2 header uncompressed to @dst.
+ * Compression header is setup in smb_compress().
+ */
+ memcpy(dst + SMB_COMPRESS_HDR_LEN, src_rq->rq_iov->iov_base, buf_len);
+
+ /* XXX: add other algs here as they're implemented */
+ ret = lz77_compress(src, data_len, dst + SMB_COMPRESS_HDR_LEN + buf_len, &data_len);
+err_free:
+ kvfree(src);
+ if (!ret) {
+ dst_rq->rq_iov->iov_len = SMB_COMPRESS_HDR_LEN + buf_len + data_len;
+ } else {
+ kvfree(dst_rq->rq_iov->iov_base);
+ dst_rq->rq_iov->iov_base = NULL;
+ }
+
+ return ret;
+}