From 7ff8d45c9dccf0744404d6fe44468ede7c1b9533 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 14 Oct 2013 00:44:19 -0500 Subject: Fix corrupt SMB2 ioctl requests We were off by one calculating the length of ioctls in some cases because the protocol specification for SMB2 ioctl includes a mininum one byte payload but not all SMB2 ioctl requests actually have a data buffer to send. We were also not zeroing out the return buffer (in case of error this is helpful). Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index edccb525246..6e186861123 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1137,6 +1137,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, cifs_dbg(FYI, "SMB2 IOCTL\n"); + *out_data = NULL; /* zero out returned data len, in case of error */ if (plen) *plen = 0; @@ -1182,11 +1183,23 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, req->Flags = 0; iov[0].iov_base = (char *)req; - /* 4 for rfc1002 length field */ - iov[0].iov_len = get_rfc1002_length(req) + 4; - if (indatalen) - inc_rfc1001_len(req, indatalen); + /* + * If no input data, the size of ioctl struct in + * protocol spec still includes a 1 byte data buffer, + * but if input data passed to ioctl, we do not + * want to double count this, so we do not send + * the dummy one byte of data in iovec[0] if sending + * input data (in iovec[1]). We also must add 4 bytes + * in first iovec to allow for rfc1002 length field. + */ + + if (indatalen) { + iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; + inc_rfc1001_len(req, indatalen - 1); + } else + iov[0].iov_len = get_rfc1002_length(req) + 4; + rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; -- cgit v1.2.3-70-g09d2 From 64a5cfa6db94c5abba2cafe77aca077dd1e3283b Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 14 Oct 2013 15:31:32 -0500 Subject: Allow setting per-file compression via SMB2/3 Allow cifs/smb2/smb3 to return whether or not a file is compressed via lsattr, and allow SMB2/SMB3 to set the per-file compression flag ("chattr +c filename" on an smb3 mount). Windows users often set the compressed flag (it can be done from the desktop and file manager). David Disseldorp has patches to Samba server to support this (at least on btrfs) which are complementary to this Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/ioctl.c | 55 +++++++++++++++++++++++++++++++++++------------------ fs/cifs/smb2ops.c | 11 +++++++++++ fs/cifs/smb2pdu.c | 27 ++++++++++++++++++++++++++ fs/cifs/smb2pdu.h | 9 ++++++++- fs/cifs/smb2proto.h | 2 ++ 6 files changed, 87 insertions(+), 19 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 52b6f6c26bf..06e8947fc37 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -278,6 +278,8 @@ struct smb_version_operations { /* set attributes */ int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *, const unsigned int); + int (*set_compression)(const unsigned int, struct cifs_tcon *, + struct cifsFileInfo *); /* check if we can send an echo or nor */ bool (*can_echo)(struct TCP_Server_Info *); /* send echo request */ diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 3e084558585..029867078af 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -3,7 +3,7 @@ * * vfs operations that deal with io control * - * Copyright (C) International Business Machines Corp., 2005,2007 + * Copyright (C) International Business Machines Corp., 2005,2013 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -34,13 +34,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) int rc = -ENOTTY; /* strange error - but the precedent */ unsigned int xid; struct cifs_sb_info *cifs_sb; -#ifdef CONFIG_CIFS_POSIX struct cifsFileInfo *pSMBFile = filep->private_data; struct cifs_tcon *tcon; __u64 ExtAttrBits = 0; __u64 ExtAttrMask = 0; __u64 caps; -#endif /* CONFIG_CIFS_POSIX */ xid = get_xid(); @@ -49,12 +47,12 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) cifs_sb = CIFS_SB(inode->i_sb); switch (command) { -#ifdef CONFIG_CIFS_POSIX case FS_IOC_GETFLAGS: if (pSMBFile == NULL) break; tcon = tlink_tcon(pSMBFile->tlink); caps = le64_to_cpu(tcon->fsUnixInfo.Capability); +#ifdef CONFIG_CIFS_POSIX if (CIFS_UNIX_EXTATTR_CAP & caps) { rc = CIFSGetExtAttr(xid, tcon, pSMBFile->fid.netfid, @@ -63,29 +61,50 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, (int __user *)arg); + if (rc != EOPNOTSUPP) + break; + } +#endif /* CONFIG_CIFS_POSIX */ + rc = 0; + if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { + /* add in the compressed bit */ + ExtAttrBits = FS_COMPR_FL; + rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, + (int __user *)arg); } break; - case FS_IOC_SETFLAGS: if (pSMBFile == NULL) break; tcon = tlink_tcon(pSMBFile->tlink); caps = le64_to_cpu(tcon->fsUnixInfo.Capability); - if (CIFS_UNIX_EXTATTR_CAP & caps) { - if (get_user(ExtAttrBits, (int __user *)arg)) { - rc = -EFAULT; - break; - } - /* - * rc = CIFSGetExtAttr(xid, tcon, - * pSMBFile->fid.netfid, - * extAttrBits, - * &ExtAttrMask); - */ + + if (get_user(ExtAttrBits, (int __user *)arg)) { + rc = -EFAULT; + break; + } + + /* + * if (CIFS_UNIX_EXTATTR_CAP & caps) + * rc = CIFSSetExtAttr(xid, tcon, + * pSMBFile->fid.netfid, + * extAttrBits, + * &ExtAttrMask); + * if (rc != EOPNOTSUPP) + * break; + */ + + /* Currently only flag we can set is compressed flag */ + if ((ExtAttrBits & FS_COMPR_FL) == 0) + break; + + /* Try to set compress flag */ + if (tcon->ses->server->ops->set_compression) { + rc = tcon->ses->server->ops->set_compression( + xid, tcon, pSMBFile); + cifs_dbg(FYI, "set compress flag rc %d\n", rc); } - cifs_dbg(FYI, "set flags not implemented yet\n"); break; -#endif /* CONFIG_CIFS_POSIX */ default: cifs_dbg(FYI, "unsupported ioctl\n"); break; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 861b3321414..b96bacce955 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -445,6 +445,14 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, cfile->fid.volatile_fid, cfile->pid, &eof); } +static int +smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid); +} + static int smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, const char *path, struct cifs_sb_info *cifs_sb, @@ -874,6 +882,7 @@ struct smb_version_operations smb20_operations = { .set_path_size = smb2_set_path_size, .set_file_size = smb2_set_file_size, .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, .mkdir = smb2_mkdir, .mkdir_setinfo = smb2_mkdir_setinfo, .rmdir = smb2_rmdir, @@ -945,6 +954,7 @@ struct smb_version_operations smb21_operations = { .set_path_size = smb2_set_path_size, .set_file_size = smb2_set_file_size, .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, .mkdir = smb2_mkdir, .mkdir_setinfo = smb2_mkdir_setinfo, .rmdir = smb2_rmdir, @@ -1017,6 +1027,7 @@ struct smb_version_operations smb30_operations = { .set_path_size = smb2_set_path_size, .set_file_size = smb2_set_file_size, .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, .mkdir = smb2_mkdir, .mkdir_setinfo = smb2_mkdir_setinfo, .rmdir = smb2_rmdir, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 6e186861123..bbafa12e83b 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1247,6 +1247,33 @@ ioctl_exit: return rc; } +/* + * Individual callers to ioctl worker function follow + */ + +int +SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + int rc; + char *res_key = NULL; + struct compress_ioctl fsctl_input; + char *ret_data = NULL; + + fsctl_input.CompressionState = + __constant_cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + + rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, + FSCTL_SET_COMPRESSION, true /* is_fsctl */, + (char *)&fsctl_input /* data input */, + 2 /* in data len */, &ret_data /* out data */, NULL); + + cifs_dbg(FYI, "set compression rc %d\n", rc); + kfree(res_key); + + return rc; +} + int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index b83d0118a75..c7c3c8294d1 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -569,6 +569,13 @@ struct network_interface_info_ioctl_rsp { #define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ +struct compress_ioctl { + __le16 CompressionState; +} __packed; + +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_DEFAULT 0x0001 +#define COMPRESSION_FORMAT_LZNT1 0x0002 struct smb2_ioctl_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 57 */ @@ -584,7 +591,7 @@ struct smb2_ioctl_req { __le32 MaxOutputResponse; __le32 Flags; __u32 Reserved2; - char Buffer[0]; + __u8 Buffer[0]; } __packed; struct smb2_ioctl_rsp { diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index e3fb4801ee9..3cf22e39420 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -142,6 +142,8 @@ extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, FILE_BASIC_INFO *buf); +extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid); extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, const u64 persistent_fid, const u64 volatile_fid, const __u8 oplock_level); -- cgit v1.2.3-70-g09d2 From 34f626406c09dd45878ce75170abab342985ec24 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 9 Oct 2013 02:07:00 -0500 Subject: Query file system attributes from server on SMB2, not just cifs, mounts Currently SMB2 and SMB3 mounts do not query the file system attributes from the server at mount time as is done for cifs. These can be useful for debugging. Signed-off-by: Steve French --- fs/cifs/cifspdu.h | 3 +++ fs/cifs/smb2ops.c | 28 ++++++++++++++++++++++++++++ fs/cifs/smb2pdu.c | 42 ++++++++++++++++++++++++++++++++++++++++-- fs/cifs/smb2proto.h | 2 ++ 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 08f9dfb1a89..d40bd77a239 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -2215,6 +2215,9 @@ typedef struct { __le32 DeviceCharacteristics; } __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */ +/* minimum includes first three fields, and empty FS Name */ +#define MIN_FS_ATTR_INFO_SIZE 12 + typedef struct { __le32 Attributes; __le32 MaxPathNameComponentLength; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index b96bacce955..a53e2053ae9 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -209,6 +209,31 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return rsize; } +static void +smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + + oparms.tcon = tcon; + oparms.desired_access = FILE_READ_ATTRIBUTES; + oparms.disposition = FILE_OPEN; + oparms.create_options = 0; + oparms.fid = &fid; + oparms.reconnect = false; + + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); + if (rc) + return; + + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + return; +} + static int smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path) @@ -873,6 +898,7 @@ struct smb_version_operations smb20_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, .is_path_accessible = smb2_is_path_accessible, .can_echo = smb2_can_echo, .echo = SMB2_echo, @@ -945,6 +971,7 @@ struct smb_version_operations smb21_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, .is_path_accessible = smb2_is_path_accessible, .can_echo = smb2_can_echo, .echo = SMB2_echo, @@ -1018,6 +1045,7 @@ struct smb_version_operations smb30_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, .is_path_accessible = smb2_is_path_accessible, .can_echo = smb2_can_echo, .echo = SMB2_echo, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index bbafa12e83b..df12cf8bd97 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2339,7 +2339,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0); if (rc) { cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto qinf_exit; + goto qfsinf_exit; } rsp = (struct smb2_query_info_rsp *)iov.iov_base; @@ -2351,7 +2351,45 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, if (!rc) copy_fs_info_to_kstatfs(info, fsdata); -qinf_exit: +qfsinf_exit: + free_rsp_buf(resp_buftype, iov.iov_base); + return rc; +} + +int +SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + unsigned int rsp_len, offset; + + rc = build_qfs_info_req(&iov, tcon, SMB_QUERY_FS_ATTRIBUTE_INFO, + sizeof(FILE_SYSTEM_ATTRIBUTE_INFO), + persistent_fid, volatile_fid); + if (rc) + return rc; + + rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qfsattr_exit; + } + rsp = (struct smb2_query_info_rsp *)iov.iov_base; + + rsp_len = le32_to_cpu(rsp->OutputBufferLength); + offset = le16_to_cpu(rsp->OutputBufferOffset); + rc = validate_buf(offset, rsp_len, &rsp->hdr, MIN_FS_ATTR_INFO_SIZE); + if (!rc) { + memcpy(&tcon->fsAttrInfo, 4 /* RFC1001 len */ + offset + + (char *)&rsp->hdr, min_t(unsigned int, + rsp_len, sizeof(FILE_SYSTEM_ATTRIBUTE_INFO))); + } + +qfsattr_exit: free_rsp_buf(resp_buftype, iov.iov_base); return rc; } diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 3cf22e39420..68dc00d5fb1 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -150,6 +150,8 @@ extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id, struct kstatfs *FSData); +extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id); extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, const __u64 length, const __u64 offset, -- cgit v1.2.3-70-g09d2 From 944d6f1a5b8f42a780a65378e5f52bea3ae0ce07 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 16 Oct 2013 09:09:49 -0600 Subject: cifs: Remove redundant multiplex identifier check from check_smb_hdr() The only call site for check_smb_header() assigns 'mid' from the SMB packet, which is then checked again in check_smb_header(). This seems like redundant redundancy. Reviewed-by: Jeff Layton Signed-off-by: Tim Gardner Signed-off-by: Steve French --- fs/cifs/misc.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 138a011633f..298e31e3bdc 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -278,7 +278,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , } static int -check_smb_hdr(struct smb_hdr *smb, __u16 mid) +check_smb_hdr(struct smb_hdr *smb) { /* does it have the right SMB "signature" ? */ if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { @@ -287,13 +287,6 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid) return 1; } - /* Make sure that message ids match */ - if (mid != smb->Mid) { - cifs_dbg(VFS, "Mids do not match. received=%u expected=%u\n", - smb->Mid, mid); - return 1; - } - /* if it's a response then accept */ if (smb->Flags & SMBFLG_RESPONSE) return 0; @@ -310,7 +303,6 @@ int checkSMB(char *buf, unsigned int total_read) { struct smb_hdr *smb = (struct smb_hdr *)buf; - __u16 mid = smb->Mid; __u32 rfclen = be32_to_cpu(smb->smb_buf_length); __u32 clc_len; /* calculated length */ cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", @@ -348,7 +340,7 @@ checkSMB(char *buf, unsigned int total_read) } /* otherwise, there is enough to get to the BCC */ - if (check_smb_hdr(smb, mid)) + if (check_smb_hdr(smb)) return -EIO; clc_len = smbCalcSize(smb); -- cgit v1.2.3-70-g09d2 From 3d378d3fd82a759d59c60d89b4559bf325d7e668 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Sat, 2 Nov 2013 12:50:34 -0500 Subject: cifs: Make big endian multiplex ID sequences monotonic on the wire The multiplex identifier (MID) in the SMB header is only ever used by the client, in conjunction with PID, to match responses from the server. As such, the endianess of the MID is not important. However, When tracing packet sequences on the wire, protocol analyzers such as wireshark display MID as little endian. It is much more informative for the on-the-wire MID sequences to match debug information emitted by the CIFS driver. Therefore, one should write and read MID in the SMB header assuming it is always little endian. Observed from wireshark during the protocol negotiation and session setup: Multiplex ID: 256 Multiplex ID: 256 Multiplex ID: 512 Multiplex ID: 512 Multiplex ID: 768 Multiplex ID: 768 After this patch on-the-wire MID values begin at 1 and increase monotonically. Introduce get_next_mid64() for the internal consumers that use the full 64 bit multiplex identifier. Introduce the helpers get_mid() and compare_mid() to make the endian translation clear. Reviewed-by: Jeff Layton Signed-off-by: Tim Gardner Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 25 ++++++++++++++++++++++++- fs/cifs/cifspdu.h | 2 +- fs/cifs/misc.c | 10 ++++++---- fs/cifs/smb1ops.c | 4 ++-- fs/cifs/smb2transport.c | 2 +- fs/cifs/transport.c | 2 +- 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 06e8947fc37..670da1e55be 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -622,11 +622,34 @@ set_credits(struct TCP_Server_Info *server, const int val) } static inline __u64 -get_next_mid(struct TCP_Server_Info *server) +get_next_mid64(struct TCP_Server_Info *server) { return server->ops->get_next_mid(server); } +static inline __le16 +get_next_mid(struct TCP_Server_Info *server) +{ + __u16 mid = get_next_mid64(server); + /* + * The value in the SMB header should be little endian for easy + * on-the-wire decoding. + */ + return cpu_to_le16(mid); +} + +static inline __u16 +get_mid(const struct smb_hdr *smb) +{ + return le16_to_cpu(smb->Mid); +} + +static inline bool +compare_mid(__u16 mid, const struct smb_hdr *smb) +{ + return mid == le16_to_cpu(smb->Mid); +} + /* * When the server supports very large reads and writes via POSIX extensions, * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index d40bd77a239..f80f98f5ef9 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -428,7 +428,7 @@ struct smb_hdr { __u16 Tid; __le16 Pid; __u16 Uid; - __u16 Mid; + __le16 Mid; __u8 WordCount; } __attribute__((packed)); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 298e31e3bdc..2f9f3790679 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -295,7 +295,8 @@ check_smb_hdr(struct smb_hdr *smb) if (smb->Command == SMB_COM_LOCKING_ANDX) return 0; - cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", smb->Mid); + cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", + get_mid(smb)); return 1; } @@ -351,6 +352,7 @@ checkSMB(char *buf, unsigned int total_read) } if (4 + rfclen != clc_len) { + __u16 mid = get_mid(smb); /* check if bcc wrapped around for large read responses */ if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { /* check if lengths match mod 64K */ @@ -358,11 +360,11 @@ checkSMB(char *buf, unsigned int total_read) return 0; /* bcc wrapped */ } cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", - clc_len, 4 + rfclen, smb->Mid); + clc_len, 4 + rfclen, mid); if (4 + rfclen < clc_len) { cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", - rfclen, smb->Mid); + rfclen, mid); return -EIO; } else if (rfclen > clc_len + 512) { /* @@ -375,7 +377,7 @@ checkSMB(char *buf, unsigned int total_read) * data to 512 bytes. */ cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", - rfclen, smb->Mid); + rfclen, mid); return -EIO; } } diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 8233b174de3..09ef8f322c2 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -67,7 +67,7 @@ send_nt_cancel(struct TCP_Server_Info *server, void *buf, mutex_unlock(&server->srv_mutex); cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n", - in_buf->Mid, rc); + get_mid(in_buf), rc); return rc; } @@ -101,7 +101,7 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer) spin_lock(&GlobalMid_Lock); list_for_each_entry(mid, &server->pending_mid_q, qhead) { - if (mid->mid == buf->Mid && + if (compare_mid(mid->mid, buf) && mid->mid_state == MID_REQUEST_SUBMITTED && le16_to_cpu(mid->command) == buf->Command) { spin_unlock(&GlobalMid_Lock); diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 340abca3aa5..c523617eade 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -466,7 +466,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) static inline void smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr) { - hdr->MessageId = get_next_mid(server); + hdr->MessageId = get_next_mid64(server); } static struct mid_q_entry * diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 800b938e406..0ee6d249ef6 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -58,7 +58,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) return temp; else { memset(temp, 0, sizeof(struct mid_q_entry)); - temp->mid = smb_buffer->Mid; /* always LE */ + temp->mid = get_mid(smb_buffer); temp->pid = current->pid; temp->command = cpu_to_le16(smb_buffer->Command); cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); -- cgit v1.2.3-70-g09d2 From 7f48558e6489d032b1584b0cc9ac4bb11072c034 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Sat, 12 Oct 2013 10:06:03 -0500 Subject: cifs: Send a logoff request before removing a smb session Send a smb session logoff request before removing smb session off of the list. On a signed smb session, remvoing a session off of the list before sending a logoff request results in server returning an error for lack of smb signature. Never seen an error during smb logoff, so as per MS-SMB2 3.2.5.1, not sure how an error during logoff should be retried. So for now, if a server returns an error to a logoff request, log the error and remove the session off of the list. Signed-off-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 25 ++++++++++++++++++++----- fs/cifs/smb2transport.c | 10 ++++++++-- fs/cifs/transport.c | 11 +++++++++-- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a279ffc0bc2..62a55147400 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2242,6 +2242,8 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + if (ses->status == CifsExiting) + continue; if (!match_session(ses, vol)) continue; ++ses->ses_count; @@ -2255,24 +2257,37 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) static void cifs_put_smb_ses(struct cifs_ses *ses) { - unsigned int xid; + unsigned int rc, xid; struct TCP_Server_Info *server = ses->server; cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); + spin_lock(&cifs_tcp_ses_lock); + if (ses->status == CifsExiting) { + spin_unlock(&cifs_tcp_ses_lock); + return; + } if (--ses->ses_count > 0) { spin_unlock(&cifs_tcp_ses_lock); return; } - - list_del_init(&ses->smb_ses_list); + if (ses->status == CifsGood) + ses->status = CifsExiting; spin_unlock(&cifs_tcp_ses_lock); - if (ses->status == CifsGood && server->ops->logoff) { + if (ses->status == CifsExiting && server->ops->logoff) { xid = get_xid(); - server->ops->logoff(xid, ses); + rc = server->ops->logoff(xid, ses); + if (rc) + cifs_dbg(VFS, "%s: Session Logoff failure rc=%d\n", + __func__, rc); _free_xid(xid); } + + spin_lock(&cifs_tcp_ses_lock); + list_del_init(&ses->smb_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + sesInfoFree(ses); cifs_put_tcp_session(server); } diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index c523617eade..59c748ce872 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -516,13 +516,19 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf, return -EAGAIN; } - if (ses->status != CifsGood) { - /* check if SMB2 session is bad because we are setting it up */ + if (ses->status == CifsNew) { if ((buf->Command != SMB2_SESSION_SETUP) && (buf->Command != SMB2_NEGOTIATE)) return -EAGAIN; /* else ok - we are setting up session */ } + + if (ses->status == CifsExiting) { + if (buf->Command != SMB2_LOGOFF) + return -EAGAIN; + /* else ok - we are shutting down the session */ + } + *mid = smb2_mid_entry_alloc(buf, ses->server); if (*mid == NULL) return -ENOMEM; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 0ee6d249ef6..b3757095284 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -431,13 +431,20 @@ static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, return -EAGAIN; } - if (ses->status != CifsGood) { - /* check if SMB session is bad because we are setting it up */ + if (ses->status == CifsNew) { if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) return -EAGAIN; /* else ok - we are setting up session */ } + + if (ses->status == CifsExiting) { + /* check if SMB session is bad because we are setting it up */ + if (in_buf->Command != SMB_COM_LOGOFF_ANDX) + return -EAGAIN; + /* else ok - we are shutting down session */ + } + *ppmidQ = AllocMidQEntry(in_buf, ses->server); if (*ppmidQ == NULL) return -ENOMEM; -- cgit v1.2.3-70-g09d2 From 2167114c6ea6e76fd84e368bae5389d37dd156aa Mon Sep 17 00:00:00 2001 From: Steven French Date: Wed, 9 Oct 2013 13:36:35 -0500 Subject: Query device characteristics at mount time from server on SMB2/3 not just on cifs mounts Currently SMB2 and SMB3 mounts do not query the device information at mount time from the server as is done for cifs. These can be useful for debugging. This is a minor patch, that extends the previous one (which added ability to query file system attributes at mount time - this returns the device characteristics - also via in /proc/fs/cifs/DebugData) Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 5 ++++- fs/cifs/smb2pdu.c | 31 +++++++++++++++++++++++-------- fs/cifs/smb2proto.h | 2 +- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index a53e2053ae9..79084d67f3f 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -229,7 +229,10 @@ smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) if (rc) return; - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_ATTRIBUTE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_DEVICE_INFORMATION); SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); return; } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index df12cf8bd97..7887cf50e5f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2358,17 +2358,27 @@ qfsinf_exit: int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid) + u64 persistent_fid, u64 volatile_fid, int level) { struct smb2_query_info_rsp *rsp = NULL; struct kvec iov; int rc = 0; - int resp_buftype; + int resp_buftype, max_len, min_len; struct cifs_ses *ses = tcon->ses; unsigned int rsp_len, offset; - rc = build_qfs_info_req(&iov, tcon, SMB_QUERY_FS_ATTRIBUTE_INFO, - sizeof(FILE_SYSTEM_ATTRIBUTE_INFO), + if (level == FS_DEVICE_INFORMATION) { + max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); + min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); + } else if (level == FS_ATTRIBUTE_INFORMATION) { + max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); + min_len = MIN_FS_ATTR_INFO_SIZE; + } else { + cifs_dbg(FYI, "Invalid qfsinfo level %d", level); + return -EINVAL; + } + + rc = build_qfs_info_req(&iov, tcon, level, max_len, persistent_fid, volatile_fid); if (rc) return rc; @@ -2382,12 +2392,17 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, rsp_len = le32_to_cpu(rsp->OutputBufferLength); offset = le16_to_cpu(rsp->OutputBufferOffset); - rc = validate_buf(offset, rsp_len, &rsp->hdr, MIN_FS_ATTR_INFO_SIZE); - if (!rc) { + rc = validate_buf(offset, rsp_len, &rsp->hdr, min_len); + if (rc) + goto qfsattr_exit; + + if (level == FS_ATTRIBUTE_INFORMATION) memcpy(&tcon->fsAttrInfo, 4 /* RFC1001 len */ + offset + (char *)&rsp->hdr, min_t(unsigned int, - rsp_len, sizeof(FILE_SYSTEM_ATTRIBUTE_INFO))); - } + rsp_len, max_len)); + else if (level == FS_DEVICE_INFORMATION) + memcpy(&tcon->fsDevInfo, 4 /* RFC1001 len */ + offset + + (char *)&rsp->hdr, sizeof(FILE_SYSTEM_DEVICE_INFO)); qfsattr_exit: free_rsp_buf(resp_buftype, iov.iov_base); diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 68dc00d5fb1..313813e4c19 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -151,7 +151,7 @@ extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id, struct kstatfs *FSData); extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id); + u64 persistent_file_id, u64 volatile_file_id, int lvl); extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, const __u64 length, const __u64 offset, -- cgit v1.2.3-70-g09d2 From af6a12ea8d4bb39a87527835b943bde4215897e5 Mon Sep 17 00:00:00 2001 From: Steven French Date: Wed, 9 Oct 2013 20:55:53 -0500 Subject: Query File System Alignment In SMB3 it is now possible to query the file system alignment info, and the preferred (for performance) sector size and whether the underlying disk has no seek penalty (like SSD). Query this information at mount time for SMB3, and make it visible in /proc/fs/cifs/DebugData for debugging purposes. This alignment information and preferred sector size info will be helpful for the copy offload patches to setup the right chunks in the CopyChunk requests. Presumably the knowledge that the underlying disk is SSD could also help us make better readahead and writebehind decisions (something to look at in the future). Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/smb2ops.c | 44 +++++++++++++++++++++++++++++++++++++++++++- fs/cifs/smb2pdu.c | 12 +++++++++++- fs/cifs/smb2pdu.h | 22 ++++++++++++++++++++-- 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 670da1e55be..26b1c1dc93f 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -853,6 +853,8 @@ struct cifs_tcon { __u32 maximal_access; __u32 vol_serial_number; __le64 vol_create_time; + __u32 ss_flags; /* sector size flags */ + __u32 perf_sector_size; /* best sector size for perf */ #endif /* CONFIG_CIFS_SMB2 */ #ifdef CONFIG_CIFS_FSCACHE u64 resource_id; /* server resource id */ diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 79084d67f3f..25759c89619 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -209,6 +209,36 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return rsize; } +static void +smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + + oparms.tcon = tcon; + oparms.desired_access = FILE_READ_ATTRIBUTES; + oparms.disposition = FILE_OPEN; + oparms.create_options = 0; + oparms.fid = &fid; + oparms.reconnect = false; + + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); + if (rc) + return; + + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_ATTRIBUTE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_DEVICE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + return; +} + static void smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) { @@ -332,7 +362,19 @@ smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) seq_puts(m, " ASYMMETRIC,"); if (tcon->capabilities == 0) seq_puts(m, " None"); + if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) + seq_puts(m, " Aligned,"); + if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) + seq_puts(m, " Partition Aligned,"); + if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) + seq_puts(m, " SSD,"); + if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) + seq_puts(m, " TRIM-support,"); + seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); + if (tcon->perf_sector_size) + seq_printf(m, "\tOptimal sector size: 0x%x", + tcon->perf_sector_size); } static void @@ -1048,7 +1090,7 @@ struct smb_version_operations smb30_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb2_qfs_tcon, + .qfs_tcon = smb3_qfs_tcon, .is_path_accessible = smb2_is_path_accessible, .can_echo = smb2_can_echo, .echo = SMB2_echo, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 7887cf50e5f..8ab05b0d677 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2373,8 +2373,11 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, } else if (level == FS_ATTRIBUTE_INFORMATION) { max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); min_len = MIN_FS_ATTR_INFO_SIZE; + } else if (level == FS_SECTOR_SIZE_INFORMATION) { + max_len = sizeof(struct smb3_fs_ss_info); + min_len = sizeof(struct smb3_fs_ss_info); } else { - cifs_dbg(FYI, "Invalid qfsinfo level %d", level); + cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); return -EINVAL; } @@ -2403,6 +2406,13 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, else if (level == FS_DEVICE_INFORMATION) memcpy(&tcon->fsDevInfo, 4 /* RFC1001 len */ + offset + (char *)&rsp->hdr, sizeof(FILE_SYSTEM_DEVICE_INFO)); + else if (level == FS_SECTOR_SIZE_INFORMATION) { + struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) + (4 /* RFC1001 len */ + offset + (char *)&rsp->hdr); + tcon->ss_flags = le32_to_cpu(ss_info->Flags); + tcon->perf_sector_size = + le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); + } qfsattr_exit: free_rsp_buf(resp_buftype, iov.iov_base); diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index c7c3c8294d1..7e44f18cc16 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -877,14 +877,16 @@ struct smb2_lease_ack { /* File System Information Classes */ #define FS_VOLUME_INFORMATION 1 /* Query */ -#define FS_LABEL_INFORMATION 2 /* Set */ +#define FS_LABEL_INFORMATION 2 /* Local only */ #define FS_SIZE_INFORMATION 3 /* Query */ #define FS_DEVICE_INFORMATION 4 /* Query */ #define FS_ATTRIBUTE_INFORMATION 5 /* Query */ #define FS_CONTROL_INFORMATION 6 /* Query, Set */ #define FS_FULL_SIZE_INFORMATION 7 /* Query */ #define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ -#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ +#define FS_DRIVER_PATH_INFORMATION 9 /* Local only */ +#define FS_VOLUME_FLAGS_INFORMATION 10 /* Local only */ +#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ struct smb2_fs_full_size_info { __le64 TotalAllocationUnits; @@ -894,6 +896,22 @@ struct smb2_fs_full_size_info { __le32 BytesPerSector; } __packed; +#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 + +/* sector size info struct */ +struct smb3_fs_ss_info { + __le32 LogicalBytesPerSector; + __le32 PhysicalBytesPerSectorForAtomicity; + __le32 PhysicalBytesPerSectorForPerf; + __le32 FileSystemEffectivePhysicalBytesPerSectorForAtomicity; + __le32 Flags; + __le32 ByteOffsetForSectorAlignment; + __le32 ByteOffsetForPartitionAlignment; +} __packed; + /* partial list of QUERY INFO levels */ #define FILE_DIRECTORY_INFORMATION 1 #define FILE_FULL_DIRECTORY_INFORMATION 2 -- cgit v1.2.3-70-g09d2 From c7f508a99bf229963915e79a603e0618d1d2ba76 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 14 Oct 2013 15:27:32 -0500 Subject: Allow setting per-file compression via CIFS protocol An earlier patch allowed setting the per-file compression flag "chattr +c filename" on an smb2 or smb3 mount, and also allowed lsattr to return whether a file on a cifs, or smb2/smb3 mount was compressed. This patch extends the ability to set the per-file compression flag to the cifs protocol, which uses a somewhat different IOCTL mechanism than SMB2, although the payload (the flags stored in the compression_state) are the same. Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifspdu.h | 29 ++++++++++++++++++++++++++++ fs/cifs/cifsproto.h | 2 ++ fs/cifs/cifssmb.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb1ops.c | 8 ++++++++ fs/cifs/smb2pdu.h | 5 +---- 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index f80f98f5ef9..9e5ee34de98 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1352,6 +1352,35 @@ typedef struct smb_com_transaction_ioctl_req { __u8 Data[1]; } __attribute__((packed)) TRANSACT_IOCTL_REQ; +typedef struct smb_com_transaction_compr_ioctl_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + __le32 FunctionCode; + __u16 Fid; + __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ + __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ + __le16 ByteCount; + __u8 Pad[3]; + __le16 compression_state; /* See below for valid flags */ +} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ; + +/* compression state flags */ +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_DEFAULT 0x0001 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + typedef struct smb_com_transaction_ioctl_rsp { struct smb_hdr hdr; /* wct = 19 */ __u8 Reserved[3]; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index b5ec2a268f5..aa339762034 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -360,6 +360,8 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, char **symlinkinfo, const struct nls_table *nls_codepage); +extern int CIFSSMB_set_compression(const unsigned int xid, + struct cifs_tcon *tcon, __u16 fid); extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, const char *fileName, const int disposition, const int access_flags, const int omode, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index ccd31ab815d..93b29474714 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3199,6 +3199,60 @@ qreparse_out: return rc; } +int +CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid) +{ + int rc = 0; + int bytes_returned; + struct smb_com_transaction_compr_ioctl_req *pSMB; + struct smb_com_transaction_ioctl_rsp *pSMBr; + + cifs_dbg(FYI, "Set compression for %u\n", fid); + rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + + pSMB->TotalParameterCount = 0; + pSMB->TotalDataCount = __constant_cpu_to_le32(2); + pSMB->MaxParameterCount = 0; + pSMB->MaxDataCount = 0; + pSMB->MaxSetupCount = 4; + pSMB->Reserved = 0; + pSMB->ParameterOffset = 0; + pSMB->DataCount = __constant_cpu_to_le32(2); + pSMB->DataOffset = + cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req, + compression_state) - 4); /* 84 */ + pSMB->SetupCount = 4; + pSMB->SubCommand = __constant_cpu_to_le16(NT_TRANSACT_IOCTL); + pSMB->ParameterCount = 0; + pSMB->FunctionCode = __constant_cpu_to_le32(FSCTL_SET_COMPRESSION); + pSMB->IsFsctl = 1; /* FSCTL */ + pSMB->IsRootFlag = 0; + pSMB->Fid = fid; /* file handle always le */ + /* 3 byte pad, followed by 2 byte compress state */ + pSMB->ByteCount = __constant_cpu_to_le16(5); + inc_rfc1001_len(pSMB, 5); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc); + + cifs_buf_release(pSMB); + + /* + * Note: On -EAGAIN error only caller can retry on handle based calls + * since file handle passed in no longer valid. + */ + return rc; +} + + #ifdef CONFIG_CIFS_POSIX /*Convert an Access Control Entry from wire format to local POSIX xattr format*/ diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 09ef8f322c2..384cffe4285 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -806,6 +806,13 @@ out: return rc; } +static int +cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid); +} + static int cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, const char *path, struct cifs_sb_info *cifs_sb, @@ -956,6 +963,7 @@ struct smb_version_operations smb1_operations = { .set_path_size = CIFSSMBSetEOF, .set_file_size = CIFSSMBSetFileSize, .set_file_info = smb_set_file_info, + .set_compression = cifs_set_compression, .echo = CIFSSMBEcho, .mkdir = CIFSSMBMkDir, .mkdir_setinfo = cifs_mkdir_setinfo, diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 7e44f18cc16..6183b1b7550 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -570,12 +570,9 @@ struct network_interface_info_ioctl_rsp { #define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ struct compress_ioctl { - __le16 CompressionState; + __le16 CompressionState; /* See cifspdu.h for possible flag values */ } __packed; -#define COMPRESSION_FORMAT_NONE 0x0000 -#define COMPRESSION_FORMAT_DEFAULT 0x0001 -#define COMPRESSION_FORMAT_LZNT1 0x0002 struct smb2_ioctl_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 57 */ -- cgit v1.2.3-70-g09d2 From f10d9ba405ed250d647d6d810f8fc5ff2f62f370 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 13 Oct 2013 22:32:30 -0500 Subject: Fix unused variable warning when CIFS POSIX disabled Fix unused variable warning when CONFIG_CIFS_POSIX disabled. fs/cifs/ioctl.c: In function 'cifs_ioctl': >> fs/cifs/ioctl.c:40:8: warning: unused variable 'ExtAttrMask' [-Wunused-variable] __u64 ExtAttrMask = 0; ^ Pointed out by 0-DAY kernel build testing backend Signed-off-by: Steve French --- fs/cifs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 029867078af..ba54bf6ab11 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -37,7 +37,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) struct cifsFileInfo *pSMBFile = filep->private_data; struct cifs_tcon *tcon; __u64 ExtAttrBits = 0; - __u64 ExtAttrMask = 0; __u64 caps; xid = get_xid(); @@ -54,6 +53,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) caps = le64_to_cpu(tcon->fsUnixInfo.Capability); #ifdef CONFIG_CIFS_POSIX if (CIFS_UNIX_EXTATTR_CAP & caps) { + __u64 ExtAttrMask = 0; rc = CIFSGetExtAttr(xid, tcon, pSMBFile->fid.netfid, &ExtAttrBits, &ExtAttrMask); -- cgit v1.2.3-70-g09d2 From c481e9feee78c6ce1ba0a1c8c892049f6514f6cf Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 14 Oct 2013 01:21:53 -0500 Subject: Query network adapter info at mount time for debugging When CONFIG_CIFS_STATS2 enabled query adapter info for debugging It is easy now in SMB3 to query the information about the server's network interfaces (and at least Windows 8 and above do this, if not other clients) there are some useful pieces of information you can get including: - all of the network interfaces that the server advertises (not just the one you are mounting over), and with SMB3 supporting multichannel this helps with more than just failover (also aggregating multiple sockets under one mount) - whether the adapter supports RSS (useful to know if you want to estimate whether setting up two or more socket connections to the same address is going to be faster due to RSS offload in the adapter) - whether the server supports RDMA - whether the server has IPv6 interfaces (if you connected over IPv4 but prefer IPv6 e.g.) - what the link speed is (you might want to reconnect over a higher speed interface if available) (Of course we could also rerequest this on every mount cheaplly to the same server, as Windows apparently does, so we can update the adapter info on new mounts, and also on every reconnect if the network interface drops temporarily - so we don't have to rely on info from the first mount to this server) It is trivial to request this information - and certainly will be useful when we get to the point of doing multichannel (and eventually RDMA), but some of this (linkspeed etc.) info may help for debugging in the meantime. Enable this request when CONFIG_CIFS_STATS2 is on (only for smb3 mounts since it is an SMB3 or later ioctl). Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 25759c89619..c571be8cb76 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -209,6 +209,32 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return rsize; } +#ifdef CONFIG_CIFS_STATS2 +static int +SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) +{ + int rc; + unsigned int ret_data_len = 0; + struct network_interface_info_ioctl_rsp *out_buf; + + rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, + FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, + NULL /* no data input */, 0 /* no data input */, + (char **)&out_buf, &ret_data_len); + + if ((rc == 0) && (ret_data_len > 0)) { + /* Dump info on first interface */ + cifs_dbg(FYI, "Adapter Capability 0x%x\t", + le32_to_cpu(out_buf->Capability)); + cifs_dbg(FYI, "Link Speed %lld\n", + le64_to_cpu(out_buf->LinkSpeed)); + } else + cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); + + return rc; +} +#endif /* STATS2 */ + static void smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) { @@ -229,6 +255,10 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) if (rc) return; +#ifdef CONFIG_CIFS_STATS2 + SMB3_request_interfaces(xid, tcon); +#endif /* STATS2 */ + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, FS_ATTRIBUTE_INFORMATION); SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, -- cgit v1.2.3-70-g09d2