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