summaryrefslogtreecommitdiff
path: root/fs/smb
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2024-05-09 15:21:39 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2024-05-12 17:51:52 -0600
commit40afed03b810c78e4ca2d08bdfc645088b782709 (patch)
tree6c308157d0f76b7bacaf0ef36764d52bb9a3a05c /fs/smb
parent40414c6a34081b372e45c7ce5060a6d34779f6ba (diff)
downloadlinux-40afed03b810c78e4ca2d08bdfc645088b782709.tar.gz
linux-40afed03b810c78e4ca2d08bdfc645088b782709.tar.bz2
linux-40afed03b810c78e4ca2d08bdfc645088b782709.zip
smb: client: implement decompression of READ responses
Implement LZ77 decompression for SMB2 READ responses. Since a lot of the code in smb2ops.c for receiving encrypted responses are very similar, add some helpers and/or modify some of those to support decompression as well. No behaviour change is expected. Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
Diffstat (limited to 'fs/smb')
-rw-r--r--fs/smb/client/cifsfs.c12
-rw-r--r--fs/smb/client/cifsglob.h1
-rw-r--r--fs/smb/client/compress.c49
-rw-r--r--fs/smb/client/compress.h21
-rw-r--r--fs/smb/client/compress/lz77.c108
-rw-r--r--fs/smb/client/compress/lz77.h2
-rw-r--r--fs/smb/client/smb2ops.c293
-rw-r--r--fs/smb/client/smb2pdu.c6
8 files changed, 422 insertions, 70 deletions
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index bc160a4eb1cb..6de2ea59e644 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -161,6 +161,7 @@ struct workqueue_struct *fileinfo_put_wq;
struct workqueue_struct *cifsoplockd_wq;
struct workqueue_struct *deferredclose_wq;
struct workqueue_struct *compress_wq;
+struct workqueue_struct *decompress_wq;
__u32 cifs_lock_secret;
/*
@@ -1902,9 +1903,15 @@ init_cifs(void)
goto out_destroy_deferredclose_wq;
}
+ decompress_wq = alloc_workqueue("smb3decompressd", WQ_CPU_INTENSIVE|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
+ if (!decompress_wq) {
+ rc = -ENOMEM;
+ goto out_destroy_compress_wq;
+ }
+
rc = cifs_init_inodecache();
if (rc)
- goto out_destroy_compress_wq;
+ goto out_destroy_decompress_wq;
rc = init_mids();
if (rc)
@@ -1966,6 +1973,8 @@ out_destroy_mids:
destroy_mids();
out_destroy_inodecache:
cifs_destroy_inodecache();
+out_destroy_decompress_wq:
+ destroy_workqueue(decompress_wq);
out_destroy_compress_wq:
destroy_workqueue(compress_wq);
out_destroy_deferredclose_wq:
@@ -2003,6 +2012,7 @@ exit_cifs(void)
cifs_destroy_request_bufs();
destroy_mids();
cifs_destroy_inodecache();
+ destroy_workqueue(decompress_wq);
destroy_workqueue(compress_wq);
destroy_workqueue(deferredclose_wq);
destroy_workqueue(cifsoplockd_wq);
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 3b7a162080ba..c366fae1f669 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -2104,6 +2104,7 @@ extern struct workqueue_struct *fileinfo_put_wq;
extern struct workqueue_struct *cifsoplockd_wq;
extern struct workqueue_struct *deferredclose_wq;
extern struct workqueue_struct *compress_wq;
+extern struct workqueue_struct *decompress_wq;
extern __u32 cifs_lock_secret;
extern mempool_t *cifs_mid_poolp;
diff --git a/fs/smb/client/compress.c b/fs/smb/client/compress.c
index 4efbccbd40bf..de55ddd122a5 100644
--- a/fs/smb/client/compress.c
+++ b/fs/smb/client/compress.c
@@ -20,6 +20,7 @@
#include "../common/smb2pdu.h"
#include "cifsproto.h"
#include "smb2proto.h"
+#include "cifs_debug.h"
#include "compress/lz77.h"
#include "compress.h"
@@ -48,3 +49,51 @@ int smb_compress(void *buf, const void *data, size_t *len)
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;
+}
diff --git a/fs/smb/client/compress.h b/fs/smb/client/compress.h
index 38a4ac1c92dd..ffc712fae45a 100644
--- a/fs/smb/client/compress.h
+++ b/fs/smb/client/compress.h
@@ -25,6 +25,9 @@
/* sizeof(smb2_compression_payload_hdr) - sizeof(OriginalPayloadSize) */
#define SMB_COMPRESS_PAYLOAD_HDR_LEN 8
#define SMB_COMPRESS_MIN_LEN PAGE_SIZE
+#define SMB_DECOMPRESS_MAX_LEN(_srv) \
+ (256 + SMB_COMPRESS_HDR_LEN + \
+ max_t(size_t, (_srv)->maxBuf, max_t(size_t, (_srv)->max_read, (_srv)->max_write)))
struct smb_compress_ctx {
struct TCP_Server_Info *server;
@@ -38,6 +41,7 @@ struct smb_compress_ctx {
#ifdef CONFIG_CIFS_COMPRESSION
int smb_compress(void *buf, const void *data, size_t *len);
+int smb_decompress(const void *src, size_t src_len, void *dst, size_t *dst_len);
/**
* smb_compress_alg_valid() - Validate a compression algorithm.
@@ -63,6 +67,13 @@ static __always_inline int smb_compress_alg_valid(__le16 alg, bool valid_none)
return false;
}
+static __always_inline bool has_compress_hdr(void *buf)
+{
+ struct smb2_compression_hdr *hdr = buf;
+
+ return (hdr->ProtocolId == SMB2_COMPRESSION_TRANSFORM_ID);
+}
+
/**
* should_compress() - Determines if a request (write) or the response to a
* request (read) should be compressed.
@@ -98,9 +109,19 @@ static __always_inline bool should_compress(const struct cifs_tcon *tcon, const
return (shdr->Command == SMB2_READ);
}
+
+static __always_inline size_t decompressed_size(const void *buf)
+{
+ const struct smb2_compression_hdr *hdr = buf;
+
+ return le32_to_cpu(hdr->Offset) +
+ le32_to_cpu(hdr->OriginalCompressedSegmentSize);
+}
#else /* CONFIG_CIFS_COMPRESSION */
#define smb_compress(arg1, arg2, arg3) (-EOPNOTSUPP)
+#define smb_decompress(arg1, arg2, arg3, arg4) (-EOPNOTSUPP)
#define smb_compress_alg_valid(arg1, arg2) (-EOPNOTSUPP)
#define should_compress(arg1, arg2) (false)
+#define decompress_size(arg1) (0)
#endif /* !CONFIG_CIFS_COMPRESSION */
#endif /* _SMB_COMPRESS_H */
diff --git a/fs/smb/client/compress/lz77.c b/fs/smb/client/compress/lz77.c
index 2b8d548f9492..6af0c3e454cd 100644
--- a/fs/smb/client/compress/lz77.c
+++ b/fs/smb/client/compress/lz77.c
@@ -209,3 +209,111 @@ err_free:
return ret;
}
+
+int lz77_decompress(const u8 *src, size_t src_len, u8 *dst, size_t *dst_len)
+{
+ const u8 *srcp = src, *end = src + src_len, *nib = NULL;
+ u8 *dstp = dst, *dst_end = dst + *dst_len;
+ size_t nflags = 0, n, i;
+ long flags;
+
+ *dst_len = 0;
+
+ while (likely(srcp + 2 <= end)) {
+ u32 dist, len;
+ u16 sym;
+
+ if (nflags == 0) {
+ flags = *(u32 *)srcp;
+ srcp += 4;
+ nflags = 32;
+ }
+
+ /* literals */
+ n = flags ? __builtin_clz(flags) : 32;
+ n = lz77_min(n, nflags);
+ nflags -= n;
+ flags <<= n;
+
+ if (dstp + n > dst_end)
+ return -EFAULT;
+
+ if (lz77_copy(dstp, srcp, n))
+ return -EFAULT;
+
+ dstp += n;
+ srcp += n;
+
+ if (nflags == 0)
+ continue;
+
+ /*
+ * This means we've parsed the whole input @src buffer and filled
+ * @dst within its memory bounds.
+ *
+ * However, it's up to callers to determine if the decompressed
+ * buffer and size are according to what they expected to get.
+ */
+ if (unlikely(srcp + 2 > end))
+ break;
+
+ nflags--;
+ flags <<= 1;
+
+ /* match */
+ sym = *(u16 *)srcp;
+ srcp += 2;
+
+ dist = (sym / 8) + 1;
+ len = sym % 8;
+
+ /*
+ * XXX: this section is purposefully not doing any bounds
+ * checking because "performance".
+ *
+ * Let's hope/assume that the checks done above are enough.
+ */
+ if (len == 7) {
+ if (!nib) {
+ nib = srcp;
+ len = *srcp++ % 16;
+ } else {
+ len = *nib >> 4;
+ nib = NULL;
+ }
+
+ if (len == 15) {
+ len = *srcp++;
+ if (len == 255) {
+ len = *(u16 *)srcp;
+ srcp += 2;
+ if (len == 0) {
+ len = *(u32 *)srcp;
+ srcp += 4;
+ }
+
+ if (len < 15 + 7)
+ return -EIO;
+
+ len -= (15 + 7);
+ }
+ len += 15;
+ }
+ len += 7;
+ }
+ len += 3;
+
+ for (i = 0; i < len; i++) {
+ u8 *ref = dstp - dist;
+
+ if (dist > dstp - dst)
+ return -EFAULT;
+
+ *dstp++ = *ref;
+ }
+ }
+
+ *dst_len = dstp - dst;
+
+ return 0;
+}
diff --git a/fs/smb/client/compress/lz77.h b/fs/smb/client/compress/lz77.h
index 57dacadd6a58..e26733a57954 100644
--- a/fs/smb/client/compress/lz77.h
+++ b/fs/smb/client/compress/lz77.h
@@ -280,7 +280,9 @@ static __always_inline unsigned long lz77_min(unsigned long a, unsigned long b)
}
int lz77_compress(const u8 *src, size_t src_len, u8 *dst, size_t *dst_len);
+int lz77_decompress(const u8 *src, size_t src_len, u8 *dst, size_t *dst_len);
#else /* CONFIG_CIFS_COMPRESSION */
#define lz77_compress(arg1, arg2, arg3, arg4) (-EOPNOTSUPP)
+#define lz77_decompress(arg1, arg2, arg3, arg4) (-EOPNOTSUPP)
#endif /* !CONFIG_CIFS_COMPRESSION */
#endif /* _SMB_COMPRESS_LZ77_H */
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index adced44632c9..34ee8ba24c1d 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -4073,16 +4073,6 @@ smb2_dir_needs_close(struct cifsFileInfo *cfile)
return !cfile->invalidHandle;
}
-static __always_inline bool has_compress_hdr(void *buf)
-{
- struct smb2_compression_hdr *hdr = buf;
-
- if (hdr->ProtocolId == SMB2_COMPRESSION_TRANSFORM_ID)
- return true;
-
- return false;
-}
-
static __always_inline struct smb2_hdr *get_shdr(void *buf)
{
struct smb2_hdr *shdr = buf;
@@ -4425,12 +4415,17 @@ err_free:
return rc;
}
+static __always_inline bool has_transform_hdr(const void *buf)
+{
+ const struct smb2_transform_hdr *hdr = buf;
+
+ return (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM);
+}
+
static int
smb3_is_transform_hdr(void *buf)
{
- struct smb2_transform_hdr *trhdr = buf;
-
- return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM;
+ return (has_compress_hdr(buf) || has_transform_hdr(buf));
}
static int
@@ -4640,6 +4635,40 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
return 0;
}
+static void handle_offloaded_read(struct TCP_Server_Info *server, struct mid_q_entry *mid,
+ void *buf, size_t buf_len, void *data, size_t data_len)
+{
+ int ret;
+
+ ret = handle_read_data(server, mid, buf, buf_len, data, data_len, true);
+ if (ret >= 0) {
+ ret = 0;
+#ifdef CONFIG_CIFS_STATS2
+ mid->when_received = jiffies;
+#endif
+ if (server->ops->is_network_name_deleted)
+ server->ops->is_network_name_deleted(buf, server);
+
+ mid->callback(mid);
+ } else {
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus == CifsNeedReconnect) {
+ spin_lock(&server->mid_lock);
+ mid->mid_state = MID_RETRY_NEEDED;
+ spin_unlock(&server->mid_lock);
+ spin_unlock(&server->srv_lock);
+ mid->callback(mid);
+ } else {
+ spin_lock(&server->mid_lock);
+ mid->mid_state = MID_REQUEST_SUBMITTED;
+ mid->mid_flags &= ~(MID_DELETED);
+ list_add_tail(&mid->qhead, &server->pending_mid_q);
+ spin_unlock(&server->mid_lock);
+ spin_unlock(&server->srv_lock);
+ }
+ }
+}
+
struct smb2_decrypt_work {
struct work_struct decrypt;
struct TCP_Server_Info *server;
@@ -4648,7 +4677,6 @@ struct smb2_decrypt_work {
unsigned int len;
};
-
static void smb2_decrypt_offload(struct work_struct *work)
{
struct smb2_decrypt_work *dw = container_of(work,
@@ -4667,44 +4695,14 @@ static void smb2_decrypt_offload(struct work_struct *work)
dw->server->lstrp = jiffies;
mid = smb2_find_dequeue_mid(dw->server, dw->buf);
- if (mid == NULL)
+ if (mid == NULL) {
cifs_dbg(FYI, "mid not found\n");
- else {
- mid->decrypted = true;
- rc = handle_read_data(dw->server, mid, dw->buf,
- dw->server->vals->read_rsp_size,
- &dw->buffer, dw->len,
- true);
- if (rc >= 0) {
-#ifdef CONFIG_CIFS_STATS2
- mid->when_received = jiffies;
-#endif
- if (dw->server->ops->is_network_name_deleted)
- dw->server->ops->is_network_name_deleted(dw->buf,
- dw->server);
-
- mid->callback(mid);
- } else {
- spin_lock(&dw->server->srv_lock);
- if (dw->server->tcpStatus == CifsNeedReconnect) {
- spin_lock(&dw->server->mid_lock);
- mid->mid_state = MID_RETRY_NEEDED;
- spin_unlock(&dw->server->mid_lock);
- spin_unlock(&dw->server->srv_lock);
- mid->callback(mid);
- } else {
- spin_lock(&dw->server->mid_lock);
- mid->mid_state = MID_REQUEST_SUBMITTED;
- mid->mid_flags &= ~(MID_DELETED);
- list_add_tail(&mid->qhead,
- &dw->server->pending_mid_q);
- spin_unlock(&dw->server->mid_lock);
- spin_unlock(&dw->server->srv_lock);
- }
- }
- release_mid(mid);
+ goto free_pages;
}
+ handle_offloaded_read(dw->server, mid, dw->buf, dw->server->vals->read_rsp_size,
+ &dw->buffer, dw->len);
+ release_mid(mid);
free_pages:
cifs_clear_xarray_buffer(&dw->buffer);
cifs_small_buf_release(dw->buf);
@@ -4807,8 +4805,7 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
} else {
cifs_dbg(FYI, "mid found\n");
(*mid)->decrypted = true;
- rc = handle_read_data(server, *mid, buf,
- server->vals->read_rsp_size,
+ rc = handle_read_data(server, *mid, buf, server->vals->read_rsp_size,
&dw->buffer, dw->len, false);
if (rc >= 0) {
if (server->ops->is_network_name_deleted) {
@@ -4923,35 +4920,191 @@ one_more:
return ret;
}
+struct decompress_offload_ctx {
+ struct TCP_Server_Info *server;
+ struct work_struct work;
+ void *buf;
+ size_t len;
+};
+
+static void decompress_thread(struct work_struct *work)
+{
+ struct decompress_offload_ctx *ctx;
+ struct mid_q_entry *mid;
+ size_t dst_len;
+ void *dst;
+ int ret;
+
+ ctx = container_of(work, struct decompress_offload_ctx, work);
+ dst_len = decompressed_size(ctx->buf);
+ dst = kvzalloc(dst_len, GFP_KERNEL);
+ if (!dst) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ ret = smb_decompress(ctx->buf, ctx->len, dst, &dst_len);
+ if (ret)
+ goto err_free;
+
+ ctx->server->lstrp = jiffies;
+ mid = smb2_find_dequeue_mid(ctx->server, dst);
+ if (!mid) {
+ ret = -EIO;
+ goto err_free;
+ }
+
+ handle_offloaded_read(ctx->server, mid, dst, dst_len, NULL, 0);
+ release_mid(mid);
+err_free:
+ kvfree(ctx->buf);
+ kvfree(dst);
+
+ if (ret) {
+ spin_lock(&ctx->server->srv_lock);
+ ctx->server->tcpStatus = CifsNeedReconnect;
+ spin_unlock(&ctx->server->srv_lock);
+ }
+
+ kfree(ctx);
+}
+
+static int receive_compressed(struct TCP_Server_Info *server, char **bufs, struct mid_q_entry **mid,
+ int *num_mids)
+{
+ size_t src_len, dst_len;
+ void *src, *dst;
+ int ret;
+
+ *num_mids = 0;
+ mid[0] = NULL;
+
+ src_len = server->pdu_size;
+ src = kvzalloc(src_len, GFP_KERNEL);
+ if (!src)
+ return -ENOMEM;
+
+ ret = server->total_read;
+ memcpy(src, server->smallbuf, ret);
+ ret = cifs_read_from_socket(server, src + ret, src_len - ret);
+ if (ret < 0)
+ return ret;
+
+ server->total_read += ret;
+
+ dst_len = decompressed_size(src);
+
+ /* offload large decompressions to %decompress_rq */
+ if (src_len > CIFSMaxBufSize + MAX_HEADER_SIZE(server) ||
+ dst_len > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) {
+ struct decompress_offload_ctx *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ kvfree(src);
+ return -ENOMEM;
+ }
+
+ ctx->server = server;
+ ctx->buf = src;
+ ctx->len = src_len;
+ INIT_WORK(&ctx->work, decompress_thread);
+
+ queue_work(decompress_wq, &ctx->work);
+
+ return -EINPROGRESS;
+ }
+
+ dst = cifs_buf_get();
+ ret = smb_decompress(src, src_len, dst, &dst_len);
+ if (ret) {
+ cifs_buf_release(dst);
+ goto err_free;
+ }
+
+ mid[0] = smb2_find_mid(server, dst);
+ if (!mid[0]) {
+ ret = -EIO;
+ goto err_free;
+ }
+
+ *num_mids = 1;
+ server->bigbuf = bufs[0] = dst;
+ server->pdu_size = server->total_read = mid[0]->resp_buf_size = dst_len;
+
+ if (mid[0]->handle)
+ ret = mid[0]->handle(server, mid[0]);
+ else
+ ret = cifs_handle_standard(server, mid[0]);
+err_free:
+ kvfree(src);
+ if (ret) {
+ *num_mids = 0;
+ cifs_buf_release(server->bigbuf);
+ server->bigbuf = (char *)cifs_buf_get();
+ server->large_buf = false;
+ cifs_reconnect(server, true);
+ }
+
+ return ret;
+}
+
static int
smb3_receive_transform(struct TCP_Server_Info *server,
struct mid_q_entry **mids, char **bufs, int *num_mids)
{
char *buf = server->smallbuf;
unsigned int pdu_length = server->pdu_size;
- struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
- unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
-
- if (pdu_length < sizeof(struct smb2_transform_hdr) +
- sizeof(struct smb2_hdr)) {
- cifs_server_dbg(VFS, "Transform message is too small (%u)\n",
- pdu_length);
- cifs_reconnect(server, true);
- return -ECONNABORTED;
+ unsigned int orig_len, pdu_hdr_len = 0;
+ bool compressed = false, encrypted = false;
+
+ if (has_transform_hdr(buf)) {
+ struct smb2_transform_hdr *hdr;
+
+ hdr = (struct smb2_transform_hdr *)buf;
+ orig_len = le32_to_cpu(hdr->OriginalMessageSize);
+ pdu_hdr_len = sizeof(*hdr);
+ if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
+ cifs_server_dbg(VFS, "Transform message is broken\n");
+ goto err_reconnect;
+ }
+ encrypted = true;
+ } else if (has_compress_hdr(buf)) {
+ struct smb2_compression_hdr *hdr;
+
+ hdr = (struct smb2_compression_hdr *)buf;
+ pdu_hdr_len = SMB_COMPRESS_HDR_LEN;
+ orig_len = le32_to_cpu(hdr->OriginalCompressedSegmentSize);
+ if (orig_len > SMB_DECOMPRESS_MAX_LEN(server)) {
+ cifs_server_dbg(VFS, "Uncompressed message is too big (%u, max %lu)\n",
+ orig_len, SMB_DECOMPRESS_MAX_LEN(server));
+ goto err_reconnect;
+ }
+ compressed = true;
+ } else {
+ cifs_server_dbg(VFS, "Invalid ProtocolId 0x%x\n", *(uint32_t *)buf);
+ goto err_reconnect;
}
- if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
- cifs_server_dbg(VFS, "Transform message is broken\n");
- cifs_reconnect(server, true);
- return -ECONNABORTED;
+ if (pdu_length < pdu_hdr_len + sizeof(struct smb2_hdr)) {
+ cifs_server_dbg(VFS, "Transform message is too small (%u)\n", pdu_length);
+ goto err_reconnect;
}
- /* TODO: add support for compounds containing READ. */
- if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) {
- return receive_encrypted_read(server, &mids[0], num_mids);
- }
+ if (compressed)
+ /* TODO: encrypted + compressed */
+ return receive_compressed(server, bufs, mids, num_mids);
+
+ if (encrypted) {
+ /* TODO: add support for compounds containing READ. */
+ if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server))
+ return receive_encrypted_read(server, &mids[0], num_mids);
- return receive_encrypted_standard(server, mids, bufs, num_mids);
+ return receive_encrypted_standard(server, mids, bufs, num_mids);
+ }
+err_reconnect:
+ cifs_reconnect(server, true);
+ return -ECONNABORTED;
}
int
@@ -4973,6 +5126,8 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf,
*noff = le32_to_cpu(t_hdr->OriginalMessageSize);
if (unlikely(check_add_overflow(*noff, sizeof(*t_hdr), noff)))
return -EINVAL;
+ } else if (hdr->ProtocolId == SMB2_COMPRESSION_TRANSFORM_ID) {
+ *noff = 0;
} else {
*noff = le32_to_cpu(hdr->NextCommand);
}
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 8f351a9a73d4..23801b028530 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -4615,6 +4615,12 @@ smb2_async_readv(struct cifs_readdata *rdata)
flags |= CIFS_HAS_CREDITS;
}
+ if (should_compress(io_parms.tcon, buf)) {
+ struct smb2_read_req *req = (struct smb2_read_req *)buf;
+
+ req->Flags |= SMB2_READFLAG_REQUEST_COMPRESSED;
+ }
+
kref_get(&rdata->refcount);
rc = cifs_call_async(server, &rqst,
cifs_readv_receive, smb2_readv_callback,