diff options
Diffstat (limited to 'fs/smb/client/compress.c')
| -rw-r--r-- | fs/smb/client/compress.c | 80 |
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; +} |
