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.c50
1 files changed, 50 insertions, 0 deletions
diff --git a/fs/smb/client/compress.c b/fs/smb/client/compress.c
index 12311b538689..6ddd21a5e19d 100644
--- a/fs/smb/client/compress.c
+++ b/fs/smb/client/compress.c
@@ -17,6 +17,8 @@
#include <linux/uio.h>
#include "../common/smb2pdu.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
#include "compress/lz77.h"
#include "compress.h"
@@ -78,3 +80,51 @@ err_free:
return ret;
}
+
+int smb_decompress(const void *src, size_t src_len, void *dst, size_t *dst_len)
+{
+ const struct smb2_compression_hdr *hdr;
+ size_t buf_len, data_len;
+ int ret;
+
+ hdr = src;
+ if (hdr->CompressionAlgorithm != SMB3_COMPRESS_LZ77)
+ return -EIO;
+
+ buf_len = le32_to_cpu(hdr->Offset);
+ data_len = le32_to_cpu(hdr->OriginalCompressedSegmentSize);
+
+ /*
+ * Copy uncompressed data from the beginning of the payload.
+ * The remainder is all compressed data.
+ */
+ src += SMB_COMPRESS_HDR_LEN;
+ memcpy(dst, src, buf_len);
+ src += buf_len;
+ src_len -= SMB_COMPRESS_HDR_LEN + buf_len;
+ *dst_len -= buf_len;
+
+ ret = lz77_decompress(src, src_len, dst + buf_len, dst_len);
+ if (ret)
+ return ret;
+
+ if (*dst_len != data_len) {
+ cifs_dbg(VFS, "decompressed size mismatch: got %zu, expected %zu\n",
+ *dst_len, data_len);
+ return -ECONNRESET;
+ }
+
+ if (((struct smb2_hdr *)dst)->ProtocolId != SMB2_PROTO_NUMBER) {
+ cifs_dbg(VFS, "decompressed buffer is not an SMB2 message: ProtocolId 0x%x\n",
+ *(__le32 *)dst);
+ return -ECONNRESET;
+ }
+
+ /*
+ * @dst_len contains only the decompressed data size, add back
+ * the previously copied uncompressed size
+ */
+ *dst_len += buf_len;
+
+ return 0;
+}