diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/Kconfig | 2 | ||||
-rw-r--r-- | fs/cifs/cifs_debug.c | 6 | ||||
-rw-r--r-- | fs/cifs/cifsacl.c | 5 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 8 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 4 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 19 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 22 | ||||
-rw-r--r-- | fs/cifs/connect.c | 89 | ||||
-rw-r--r-- | fs/cifs/dfs_cache.c | 38 | ||||
-rw-r--r-- | fs/cifs/dfs_cache.h | 4 | ||||
-rw-r--r-- | fs/cifs/dir.c | 1 | ||||
-rw-r--r-- | fs/cifs/file.c | 66 | ||||
-rw-r--r-- | fs/cifs/inode.c | 53 | ||||
-rw-r--r-- | fs/cifs/link.c | 4 | ||||
-rw-r--r-- | fs/cifs/misc.c | 80 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 82 | ||||
-rw-r--r-- | fs/cifs/smb2file.c | 9 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 14 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 72 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 202 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 138 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 13 | ||||
-rw-r--r-- | fs/cifs/smb2transport.c | 95 | ||||
-rw-r--r-- | fs/cifs/smbdirect.c | 342 | ||||
-rw-r--r-- | fs/cifs/smbdirect.h | 8 | ||||
-rw-r--r-- | fs/cifs/transport.c | 28 |
28 files changed, 944 insertions, 467 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 22cf04fb32d3..604f65f4b6c5 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -202,7 +202,7 @@ config CIFS_SMB_DIRECT help Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. SMB Direct allows transferring SMB packets over RDMA. If unsure, - say N. + say Y. config CIFS_FSCACHE bool "Provide CIFS client caching support" diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 276e4b5ea8e0..916567d770f5 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -323,10 +323,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) atomic_read(&server->smbd_conn->send_credits), atomic_read(&server->smbd_conn->receive_credits), server->smbd_conn->receive_credit_target); - seq_printf(m, "\nPending send_pending: %x " - "send_payload_pending: %x", - atomic_read(&server->smbd_conn->send_pending), - atomic_read(&server->smbd_conn->send_payload_pending)); + seq_printf(m, "\nPending send_pending: %x ", + atomic_read(&server->smbd_conn->send_pending)); seq_printf(m, "\nReceive buffers count_receive_queue: %x " "count_empty_packet_queue: %x", server->smbd_conn->count_receive_queue, diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 716574aab3b6..ae421634aa42 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -342,7 +342,7 @@ static int sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, struct cifs_fattr *fattr, uint sidtype) { - int rc; + int rc = 0; struct key *sidkey; char *sidstr; const struct cred *saved_cred; @@ -450,11 +450,12 @@ out_revert_creds: * fails then we just fall back to using the mnt_uid/mnt_gid. */ got_valid_id: + rc = 0; if (sidtype == SIDOWNER) fattr->cf_uid = fuid; else fattr->cf_gid = fgid; - return 0; + return rc; } int diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index fa77fe5258b0..c31f362fa098 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -1018,7 +1018,7 @@ struct file_system_type cifs_fs_type = { .name = "cifs", .mount = cifs_do_mount, .kill_sb = cifs_kill_sb, - /* .fs_flags */ + .fs_flags = FS_RENAME_DOES_D_MOVE, }; MODULE_ALIAS_FS("cifs"); @@ -1027,7 +1027,7 @@ static struct file_system_type smb3_fs_type = { .name = "smb3", .mount = smb3_do_mount, .kill_sb = cifs_kill_sb, - /* .fs_flags */ + .fs_flags = FS_RENAME_DOES_D_MOVE, }; MODULE_ALIAS_FS("smb3"); MODULE_ALIAS("smb3"); @@ -1208,6 +1208,10 @@ static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off, { unsigned int xid = get_xid(); ssize_t rc; + struct cifsFileInfo *cfile = dst_file->private_data; + + if (cfile->swapfile) + return -EOPNOTSUPP; rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff, len, flags); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index b87456bae1a1..c9e2e6bbca13 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -156,5 +156,5 @@ extern int cifs_truncate_page(struct address_space *mapping, loff_t from); extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION "2.25" +#define CIFS_VERSION "2.26" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 0d956360e984..05dd3dea684b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -426,7 +426,8 @@ struct smb_version_operations { /* generate new lease key */ void (*new_lease_key)(struct cifs_fid *); int (*generate_signingkey)(struct cifs_ses *); - int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *); + int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *, + bool allocate_crypto); int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, struct cifsFileInfo *src_file); int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, @@ -1312,6 +1313,7 @@ struct cifsFileInfo { struct tcon_link *tlink; unsigned int f_flags; bool invalidHandle:1; /* file closed via session abend */ + bool swapfile:1; bool oplock_break_cancelled:1; unsigned int oplock_epoch; /* epoch from the lease break */ __u32 oplock_level; /* oplock/lease level from the lease break */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 79d842e7240c..593d826820c3 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1021,7 +1021,7 @@ typedef struct smb_com_writex_req { __le16 ByteCount; __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ - char Data[0]; + char Data[]; } __attribute__((packed)) WRITEX_REQ; typedef struct smb_com_write_req { @@ -1041,7 +1041,7 @@ typedef struct smb_com_write_req { __le16 ByteCount; __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ - char Data[0]; + char Data[]; } __attribute__((packed)) WRITE_REQ; typedef struct smb_com_write_rsp { @@ -1306,7 +1306,7 @@ typedef struct smb_com_ntransact_req { /* SetupCount words follow then */ __le16 ByteCount; __u8 Pad[3]; - __u8 Parms[0]; + __u8 Parms[]; } __attribute__((packed)) NTRANSACT_REQ; typedef struct smb_com_ntransact_rsp { @@ -1523,7 +1523,7 @@ struct file_notify_information { __le32 NextEntryOffset; __le32 Action; __le32 FileNameLength; - __u8 FileName[0]; + __u8 FileName[]; } __attribute__((packed)); /* For IO_REPARSE_TAG_SYMLINK */ @@ -1536,7 +1536,7 @@ struct reparse_symlink_data { __le16 PrintNameOffset; __le16 PrintNameLength; __le32 Flags; - char PathBuffer[0]; + char PathBuffer[]; } __attribute__((packed)); /* Flag above */ @@ -1553,7 +1553,7 @@ struct reparse_posix_data { __le16 ReparseDataLength; __u16 Reserved; __le64 InodeType; /* LNK, FIFO, CHR etc. */ - char PathBuffer[0]; + char PathBuffer[]; } __attribute__((packed)); struct cifs_quota_data { @@ -1691,6 +1691,7 @@ struct smb_t2_rsp { #define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 #define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 #define SMB_FIND_FILE_UNIX 0x202 +#define SMB_FIND_FILE_POSIX_INFO 0x064 typedef struct smb_com_transaction2_qpi_req { struct smb_hdr hdr; /* wct = 14+ */ @@ -1761,7 +1762,7 @@ struct set_file_rename { __le32 overwrite; /* 1 = overwrite dest */ __u32 root_fid; /* zero */ __le32 target_name_len; - char target_name[0]; /* Must be unicode */ + char target_name[]; /* Must be unicode */ } __attribute__((packed)); struct smb_com_transaction2_sfi_req { @@ -2450,7 +2451,7 @@ struct cifs_posix_acl { /* access conrol list (ACL) */ __le16 version; __le16 access_entry_count; /* access ACL - count of entries */ __le16 default_entry_count; /* default ACL - count of entries */ - struct cifs_posix_ace ace_array[0]; + struct cifs_posix_ace ace_array[]; /* followed by struct cifs_posix_ace default_ace_arraay[] */ } __attribute__((packed)); /* level 0x204 */ @@ -2756,7 +2757,7 @@ typedef struct file_xattr_info { /* BB do we need another field for flags? BB */ __u32 xattr_name_len; __u32 xattr_value_len; - char xattr_name[0]; + char xattr_name[]; /* followed by xattr_value[xattr_value_len], no pad */ } __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info level 0x205 */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index e5cb681ec138..12a895e02db4 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -602,6 +602,11 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, int resp_buftype, struct cifs_search_info *srch_inf); +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server); +void cifs_put_tcp_super(struct super_block *sb); +int update_super_prepath(struct cifs_tcon *tcon, const char *prefix, + size_t prefix_len); + #ifdef CONFIG_CIFS_DFS_UPCALL static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, const char *old_path, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 6f6fb3606a5d..140efc1a9374 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -162,9 +162,18 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc, for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) { - const char *tgt = dfs_cache_get_tgt_name(it); + const char *share, *prefix; + size_t share_len, prefix_len; - extract_unc_hostname(tgt, &dfs_host, &dfs_host_len); + rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix, + &prefix_len); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse target share %d\n", + __func__, rc); + continue; + } + + extract_unc_hostname(share, &dfs_host, &dfs_host_len); if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { @@ -175,11 +184,13 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc, continue; } - scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt); + scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share); rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc); - if (!rc) + if (!rc) { + rc = update_super_prepath(tcon, prefix, prefix_len); break; + } if (rc == -EREMOTE) break; } @@ -320,7 +331,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) atomic_inc(&tconInfoReconnectCount); /* tell server Unix caps we support */ - if (ses->capabilities & CAP_UNIX) + if (cap_unix(ses)) reset_cifs_unix_caps(0, tcon, NULL, NULL); /* @@ -1591,7 +1602,6 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) if (server->ops->is_session_expired && server->ops->is_session_expired(buf)) { cifs_reconnect(server); - wake_up(&server->response_q); return -1; } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4804d1df8c1c..95b3ab0ca8c0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -21,6 +21,7 @@ #include <linux/fs.h> #include <linux/net.h> #include <linux/string.h> +#include <linux/sched/mm.h> #include <linux/sched/signal.h> #include <linux/list.h> #include <linux/wait.h> @@ -57,7 +58,6 @@ #include "smb2proto.h" #include "smbdirect.h" #include "dns_resolve.h" -#include "cifsfs.h" #ifdef CONFIG_CIFS_DFS_UPCALL #include "dfs_cache.h" #endif @@ -389,54 +389,7 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server) #endif #ifdef CONFIG_CIFS_DFS_UPCALL -struct super_cb_data { - struct TCP_Server_Info *server; - struct super_block *sb; -}; - /* These functions must be called with server->srv_mutex held */ - -static void super_cb(struct super_block *sb, void *arg) -{ - struct super_cb_data *d = arg; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - - if (d->sb) - return; - - cifs_sb = CIFS_SB(sb); - tcon = cifs_sb_master_tcon(cifs_sb); - if (tcon->ses->server == d->server) - d->sb = sb; -} - -static struct super_block *get_tcp_super(struct TCP_Server_Info *server) -{ - struct super_cb_data d = { - .server = server, - .sb = NULL, - }; - - iterate_supers_type(&cifs_fs_type, super_cb, &d); - - if (unlikely(!d.sb)) - return ERR_PTR(-ENOENT); - /* - * Grab an active reference in order to prevent automounts (DFS links) - * of expiring and then freeing up our cifs superblock pointer while - * we're doing failover. - */ - cifs_sb_active(d.sb); - return d.sb; -} - -static inline void put_tcp_super(struct super_block *sb) -{ - if (!IS_ERR_OR_NULL(sb)) - cifs_sb_deactive(sb); -} - static void reconn_inval_dfs_target(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, struct dfs_cache_tgt_list *tgt_list, @@ -508,7 +461,7 @@ cifs_reconnect(struct TCP_Server_Info *server) server->nr_targets = 1; #ifdef CONFIG_CIFS_DFS_UPCALL spin_unlock(&GlobalMid_Lock); - sb = get_tcp_super(server); + sb = cifs_get_tcp_super(server); if (IS_ERR(sb)) { rc = PTR_ERR(sb); cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n", @@ -535,8 +488,9 @@ cifs_reconnect(struct TCP_Server_Info *server) spin_unlock(&GlobalMid_Lock); #ifdef CONFIG_CIFS_DFS_UPCALL dfs_cache_free_tgts(&tgt_list); - put_tcp_super(sb); + cifs_put_tcp_super(sb); #endif + wake_up(&server->response_q); return rc; } else server->tcpStatus = CifsNeedReconnect; @@ -666,11 +620,12 @@ cifs_reconnect(struct TCP_Server_Info *server) } - put_tcp_super(sb); + cifs_put_tcp_super(sb); #endif if (server->tcpStatus == CifsNeedNegotiate) mod_delayed_work(cifsiod_wq, &server->echo, 0); + wake_up(&server->response_q); return rc; } @@ -765,7 +720,6 @@ server_unresponsive(struct TCP_Server_Info *server) cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n", (3 * server->echo_interval) / HZ); cifs_reconnect(server); - wake_up(&server->response_q); return true; } @@ -898,7 +852,6 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type) */ cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT); cifs_reconnect(server); - wake_up(&server->response_q); break; default: cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); @@ -1070,7 +1023,6 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) server->vals->header_preamble_size) { cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); cifs_reconnect(server); - wake_up(&server->response_q); return -ECONNABORTED; } @@ -1118,7 +1070,6 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) if (server->ops->is_session_expired && server->ops->is_session_expired(buf)) { cifs_reconnect(server); - wake_up(&server->response_q); return -1; } @@ -1164,8 +1115,9 @@ cifs_demultiplex_thread(void *p) struct task_struct *task_to_wake = NULL; struct mid_q_entry *mids[MAX_COMPOUND]; char *bufs[MAX_COMPOUND]; + unsigned int noreclaim_flag; - current->flags |= PF_MEMALLOC; + noreclaim_flag = memalloc_noreclaim_save(); cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); length = atomic_inc_return(&tcpSesAllocCount); @@ -1212,7 +1164,6 @@ next_pdu: cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n", server->pdu_size); cifs_reconnect(server); - wake_up(&server->response_q); continue; } @@ -1320,6 +1271,7 @@ next_pdu: set_current_state(TASK_RUNNING); } + memalloc_noreclaim_restore(noreclaim_flag); module_put_and_exit(0); } @@ -1522,6 +1474,9 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3) cifs_dbg(VFS, "vers=1.0 (cifs) not permitted when mounting with smb3\n"); return 1; } + cifs_dbg(VFS, "Use of the less secure dialect vers=1.0 " + "is not recommended unless required for " + "access to very old servers\n"); vol->ops = &smb1_operations; vol->vals = &smb1_values; break; @@ -2517,11 +2472,12 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, pr_notice("CIFS: ignoring forcegid mount option specified with no gid= option.\n"); if (got_version == false) - pr_warn("No dialect specified on mount. Default has changed to " - "a more secure dialect, SMB2.1 or later (e.g. SMB3), from CIFS " - "(SMB1). To use the less secure SMB1 dialect to access " - "old servers which do not support SMB3 (or SMB2.1) specify vers=1.0" - " on mount.\n"); + pr_warn_once("No dialect specified on mount. Default has changed" + " to a more secure dialect, SMB2.1 or later (e.g. " + "SMB3.1.1), from CIFS (SMB1). To use the less secure " + "SMB1 dialect to access old servers which do not " + "support SMB3.1.1 (or even SMB3 or SMB2.1) specify " + "vers=1.0 on mount.\n"); kfree(mountdata_copy); return 0; @@ -4999,6 +4955,15 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) * dentry revalidation to think the dentry are stale (ESTALE). */ cifs_autodisable_serverino(cifs_sb); + /* + * Force the use of prefix path to support failover on DFS paths that + * resolve to targets that have different prefix paths. + */ + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + kfree(cifs_sb->prepath); + cifs_sb->prepath = vol->prepath; + vol->prepath = NULL; + out: free_xid(xid); cifs_try_adding_channels(ses); diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c index 43c1b43a07ec..a67f88bf7ae1 100644 --- a/fs/cifs/dfs_cache.c +++ b/fs/cifs/dfs_cache.c @@ -1260,6 +1260,44 @@ void dfs_cache_del_vol(const char *fullpath) kref_put(&vi->refcnt, vol_release); } +/** + * dfs_cache_get_tgt_share - parse a DFS target + * + * @it: DFS target iterator. + * @share: tree name. + * @share_len: length of tree name. + * @prefix: prefix path. + * @prefix_len: length of prefix path. + * + * Return zero if target was parsed correctly, otherwise non-zero. + */ +int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it, + const char **share, size_t *share_len, + const char **prefix, size_t *prefix_len) +{ + char *s, sep; + + if (!it || !share || !share_len || !prefix || !prefix_len) + return -EINVAL; + + sep = it->it_name[0]; + if (sep != '\\' && sep != '/') + return -EINVAL; + + s = strchr(it->it_name + 1, sep); + if (!s) + return -EINVAL; + + s = strchrnul(s + 1, sep); + + *share = it->it_name; + *share_len = s - it->it_name; + *prefix = *s ? s + 1 : s; + *prefix_len = &it->it_name[strlen(it->it_name)] - *prefix; + + return 0; +} + /* Get all tcons that are within a DFS namespace and can be refreshed */ static void get_tcons(struct TCP_Server_Info *server, struct list_head *head) { diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h index 99ee44f8ad07..bf94d08cfb5a 100644 --- a/fs/cifs/dfs_cache.h +++ b/fs/cifs/dfs_cache.h @@ -49,6 +49,10 @@ extern int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server); extern void dfs_cache_del_vol(const char *fullpath); +extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it, + const char **share, size_t *share_len, + const char **prefix, size_t *prefix_len); + static inline struct dfs_cache_tgt_iterator * dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, struct dfs_cache_tgt_iterator *it) diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 0ef099442f20..36e7b2fd2190 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -555,7 +555,6 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, if (server->ops->close) server->ops->close(xid, tcon, &fid); cifs_del_pending_open(&open); - fput(file); rc = -ENOMEM; } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 3b942ecdd4be..0b1528edebcf 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1169,7 +1169,8 @@ try_again: rc = posix_lock_file(file, flock, NULL); up_write(&cinode->lock_sem); if (rc == FILE_LOCK_DEFERRED) { - rc = wait_event_interruptible(flock->fl_wait, !flock->fl_blocker); + rc = wait_event_interruptible(flock->fl_wait, + list_empty(&flock->fl_blocked_member)); if (!rc) goto try_again; locks_delete_block(flock); @@ -3840,7 +3841,7 @@ again: if (rc == -ENODATA) rc = 0; - ctx->rc = (rc == 0) ? ctx->total_len : rc; + ctx->rc = (rc == 0) ? (ssize_t)ctx->total_len : rc; mutex_unlock(&ctx->aio_mutex); @@ -4807,6 +4808,60 @@ cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter) return -EINVAL; } +static int cifs_swap_activate(struct swap_info_struct *sis, + struct file *swap_file, sector_t *span) +{ + struct cifsFileInfo *cfile = swap_file->private_data; + struct inode *inode = swap_file->f_mapping->host; + unsigned long blocks; + long long isize; + + cifs_dbg(FYI, "swap activate\n"); + + spin_lock(&inode->i_lock); + blocks = inode->i_blocks; + isize = inode->i_size; + spin_unlock(&inode->i_lock); + if (blocks*512 < isize) { + pr_warn("swap activate: swapfile has holes\n"); + return -EINVAL; + } + *span = sis->pages; + + printk_once(KERN_WARNING "Swap support over SMB3 is experimental\n"); + + /* + * TODO: consider adding ACL (or documenting how) to prevent other + * users (on this or other systems) from reading it + */ + + + /* TODO: add sk_set_memalloc(inet) or similar */ + + if (cfile) + cfile->swapfile = true; + /* + * TODO: Since file already open, we can't open with DENY_ALL here + * but we could add call to grab a byte range lock to prevent others + * from reading or writing the file + */ + + return 0; +} + +static void cifs_swap_deactivate(struct file *file) +{ + struct cifsFileInfo *cfile = file->private_data; + + cifs_dbg(FYI, "swap deactivate\n"); + + /* TODO: undo sk_set_memalloc(inet) will eventually be needed */ + + if (cfile) + cfile->swapfile = false; + + /* do we need to unpin (or unlock) the file */ +} const struct address_space_operations cifs_addr_ops = { .readpage = cifs_readpage, @@ -4820,6 +4875,13 @@ const struct address_space_operations cifs_addr_ops = { .direct_IO = cifs_direct_io, .invalidatepage = cifs_invalidate_page, .launder_page = cifs_launder_page, + /* + * TODO: investigate and if useful we could add an cifs_migratePage + * helper (under an CONFIG_MIGRATION) in the future, and also + * investigate and add an is_dirty_writeback helper if needed + */ + .swap_activate = cifs_swap_activate, + .swap_deactivate = cifs_swap_deactivate, }; /* diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 1e8a4b1579db..8fbbdcdad8ff 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1835,6 +1835,8 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, CIFSSMBClose(xid, tcon, fid.netfid); } do_rename_exit: + if (rc == 0) + d_move(from_dentry, to_dentry); cifs_put_tlink(tlink); return rc; } @@ -2024,6 +2026,10 @@ cifs_revalidate_mapping(struct inode *inode) int rc; unsigned long *flags = &CIFS_I(inode)->flags; + /* swapfiles are not supposed to be shared */ + if (IS_SWAPFILE(inode)) + return 0; + rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, TASK_KILLABLE); if (rc) @@ -2148,8 +2154,9 @@ int cifs_getattr(const struct path *path, struct kstat *stat, * We need to be sure that all dirty pages are written and the server * has actual ctime, mtime and file length. */ - if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && - inode->i_mapping->nrpages != 0) { + if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_SIZE)) && + !CIFS_CACHE_READ(CIFS_I(inode)) && + inode->i_mapping && inode->i_mapping->nrpages != 0) { rc = filemap_fdatawait(inode->i_mapping); if (rc) { mapping_set_error(inode->i_mapping, rc); @@ -2157,9 +2164,20 @@ int cifs_getattr(const struct path *path, struct kstat *stat, } } - rc = cifs_revalidate_dentry_attr(dentry); - if (rc) - return rc; + if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_FORCE_SYNC) + CIFS_I(inode)->time = 0; /* force revalidate */ + + /* + * If the caller doesn't require syncing, only sync if + * necessary (e.g. due to earlier truncate or setattr + * invalidating the cached metadata) + */ + if (((flags & AT_STATX_SYNC_TYPE) != AT_STATX_DONT_SYNC) || + (CIFS_I(inode)->time == 0)) { + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + } generic_fillattr(inode, stat); stat->blksize = cifs_sb->bsize; @@ -2191,7 +2209,7 @@ int cifs_getattr(const struct path *path, struct kstat *stat, if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) stat->gid = current_fsgid(); } - return rc; + return 0; } int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, @@ -2516,25 +2534,26 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) /* * Attempt to flush data before changing attributes. We need to do - * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the - * ownership or mode then we may also need to do this. Here, we take - * the safe way out and just do the flush on all setattr requests. If - * the flush returns error, store it to report later and continue. + * this for ATTR_SIZE and ATTR_MTIME. If the flush of the data + * returns error, store it to report later and continue. * * BB: This should be smarter. Why bother flushing pages that * will be truncated anyway? Also, should we error out here if - * the flush returns error? + * the flush returns error? Do we need to check for ATTR_MTIME_SET flag? */ - rc = filemap_write_and_wait(inode->i_mapping); - if (is_interrupt_error(rc)) { - rc = -ERESTARTSYS; - goto cifs_setattr_exit; + if (attrs->ia_valid & (ATTR_MTIME | ATTR_SIZE | ATTR_CTIME)) { + rc = filemap_write_and_wait(inode->i_mapping); + if (is_interrupt_error(rc)) { + rc = -ERESTARTSYS; + goto cifs_setattr_exit; + } + mapping_set_error(inode->i_mapping, rc); } - mapping_set_error(inode->i_mapping, rc); rc = 0; - if (attrs->ia_valid & ATTR_MTIME) { + if ((attrs->ia_valid & ATTR_MTIME) && + !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile); if (!rc) { tcon = tlink_tcon(wfile->tlink); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 852aa00ec729..a25ef35b023e 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -416,7 +416,7 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, } rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL, - NULL); + NULL, NULL); if (rc) goto qmf_out_open_fail; @@ -470,7 +470,7 @@ smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, oparms.reconnect = false; rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, - NULL); + NULL, NULL); if (rc) { kfree(utf16_path); return rc; diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 40ca394fd5de..a456febd4109 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -31,6 +31,7 @@ #include "nterr.h" #include "cifs_unicode.h" #include "smb2pdu.h" +#include "cifsfs.h" extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; @@ -1022,3 +1023,82 @@ int copy_path_name(char *dst, const char *src) name_len++; return name_len; } + +struct super_cb_data { + struct TCP_Server_Info *server; + struct super_block *sb; +}; + +static void super_cb(struct super_block *sb, void *arg) +{ + struct super_cb_data *d = arg; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + + if (d->sb) + return; + + cifs_sb = CIFS_SB(sb); + tcon = cifs_sb_master_tcon(cifs_sb); + if (tcon->ses->server == d->server) + d->sb = sb; +} + +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) +{ + struct super_cb_data d = { + .server = server, + .sb = NULL, + }; + + iterate_supers_type(&cifs_fs_type, super_cb, &d); + + if (unlikely(!d.sb)) + return ERR_PTR(-ENOENT); + /* + * Grab an active reference in order to prevent automounts (DFS links) + * of expiring and then freeing up our cifs superblock pointer while + * we're doing failover. + */ + cifs_sb_active(d.sb); + return d.sb; +} + +void cifs_put_tcp_super(struct super_block *sb) +{ + if (!IS_ERR_OR_NULL(sb)) + cifs_sb_deactive(sb); +} + +int update_super_prepath(struct cifs_tcon *tcon, const char *prefix, + size_t prefix_len) +{ + struct super_block *sb; + struct cifs_sb_info *cifs_sb; + int rc = 0; + + sb = cifs_get_tcp_super(tcon->ses->server); + if (IS_ERR(sb)) + return PTR_ERR(sb); + + cifs_sb = CIFS_SB(sb); + + kfree(cifs_sb->prepath); + + if (*prefix && prefix_len) { + cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC); + if (!cifs_sb->prepath) { + rc = -ENOMEM; + goto out; + } + + convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); + } else + cifs_sb->prepath = NULL; + + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + +out: + cifs_put_tcp_super(sb); + return rc; +} diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index ba9dadf3be24..50f776a8d4ba 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -32,6 +32,7 @@ #include "cifs_debug.h" #include "cifs_fs_sb.h" #include "cifsfs.h" +#include "smb2proto.h" /* * To be safe - for UCS to UTF-8 with strings loaded with the rare long @@ -217,6 +218,60 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) } } +/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */ +static void +cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, + struct cifs_sb_info *cifs_sb) +{ + struct smb2_posix_info_parsed parsed; + + posix_info_parse(info, NULL, &parsed); + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_uniqueid = le64_to_cpu(info->Inode); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime); + + fattr->cf_nlink = le32_to_cpu(info->HardLinks); + fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); + + /* + * Since we set the inode type below we need to mask off + * to avoid strange results if bits set above. + * XXX: why not make server&client use the type bits? + */ + fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT; + + cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o", + le32_to_cpu(info->DeviceId), + le32_to_cpu(info->ReparseTag), + le32_to_cpu(info->Mode)); + + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + } else { + /* + * mark anything that is not a dir as regular + * file. special files should have the REPARSE + * attribute and will be marked as needing revaluation + */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + } + + if (reparse_file_needs_reval(fattr)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + + /* TODO map SIDs */ + fattr->cf_uid = cifs_sb->mnt_uid; + fattr->cf_gid = cifs_sb->mnt_gid; +} + static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) { const FILE_DIRECTORY_INFO *fi = info; @@ -359,6 +414,8 @@ ffirst_retry: /* if (cap_unix(tcon->ses) { */ if (tcon->unix_ext) cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; + else if (tcon->posix_extensions) + cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO; else if ((tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find) == 0) { cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; @@ -451,6 +508,23 @@ struct cifs_dirent { u64 ino; }; +static void cifs_fill_dirent_posix(struct cifs_dirent *de, + const struct smb2_posix_info *info) +{ + struct smb2_posix_info_parsed parsed; + + /* payload should have already been checked at this point */ + if (posix_info_parse(info, NULL, &parsed) < 0) { + cifs_dbg(VFS, "invalid POSIX info payload"); + return; + } + + de->name = parsed.name; + de->namelen = parsed.name_len; + de->resume_key = info->Ignored; + de->ino = le64_to_cpu(info->Inode); +} + static void cifs_fill_dirent_unix(struct cifs_dirent *de, const FILE_UNIX_INFO *info, bool is_unicode) { @@ -511,6 +585,9 @@ static int cifs_fill_dirent(struct cifs_dirent *de, const void *info, memset(de, 0, sizeof(*de)); switch (level) { + case SMB_FIND_FILE_POSIX_INFO: + cifs_fill_dirent_posix(de, info); + break; case SMB_FIND_FILE_UNIX: cifs_fill_dirent_unix(de, info, is_unicode); break; @@ -786,6 +863,11 @@ static int cifs_filldir(char *find_entry, struct file *file, } switch (file_info->srch_inf.info_level) { + case SMB_FIND_FILE_POSIX_INFO: + cifs_posix_to_fattr(&fattr, + (struct smb2_posix_info *)find_entry, + cifs_sb); + break; case SMB_FIND_FILE_UNIX: cifs_unix_basic_to_fattr(&fattr, &((FILE_UNIX_INFO *)find_entry)->basic, diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index afe1f03aabe3..2fa3ba354cc9 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c @@ -62,7 +62,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, - NULL); + NULL, NULL); if (rc) goto out; @@ -152,7 +152,12 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, (li->offset + li->length)) continue; if (current->tgid != li->pid) - continue; + /* + * flock and OFD lock are associated with an open + * file description, not the process. + */ + if (!(flock->fl_flags & (FL_FLOCK | FL_OFDLCK))) + continue; if (cinode->can_cache_brlcks) { /* * We can cache brlock requests - simply remove a lock diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 0511aaf451d4..497afb0b9960 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -766,6 +766,20 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); spin_lock(&cifs_tcp_ses_lock); + if (tcon->tc_count <= 0) { + struct TCP_Server_Info *server = NULL; + + WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); + spin_unlock(&cifs_tcp_ses_lock); + + if (tcon->ses) + server = tcon->ses->server; + + cifs_server_dbg(FYI, "tid=%u: tcon is closing, skipping async close retry of fid %llu %llu\n", + tcon->tid, persistent_fid, volatile_fid); + + return 0; + } tcon->tc_count++; spin_unlock(&cifs_tcp_ses_lock); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index c31e84ee3c39..b36c46f48705 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -328,16 +328,6 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) /* start with specified wsize, or default */ wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE; wsize = min_t(unsigned int, wsize, server->max_write); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (server->rdma) { - if (server->sign) - wsize = min_t(unsigned int, - wsize, server->smbd_conn->max_fragmented_send_size); - else - wsize = min_t(unsigned int, - wsize, server->smbd_conn->max_readwrite_size); - } -#endif if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); @@ -356,8 +346,15 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) #ifdef CONFIG_CIFS_SMB_DIRECT if (server->rdma) { if (server->sign) + /* + * Account for SMB2 data transfer packet header and + * possible encryption header + */ wsize = min_t(unsigned int, - wsize, server->smbd_conn->max_fragmented_send_size); + wsize, + server->smbd_conn->max_fragmented_send_size - + SMB2_READWRITE_PDU_HEADER_SIZE - + sizeof(struct smb2_transform_hdr)); else wsize = min_t(unsigned int, wsize, server->smbd_conn->max_readwrite_size); @@ -378,16 +375,6 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) /* start with specified rsize, or default */ rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE; rsize = min_t(unsigned int, rsize, server->max_read); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (server->rdma) { - if (server->sign) - rsize = min_t(unsigned int, - rsize, server->smbd_conn->max_fragmented_recv_size); - else - rsize = min_t(unsigned int, - rsize, server->smbd_conn->max_readwrite_size); - } -#endif if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); @@ -407,8 +394,15 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) #ifdef CONFIG_CIFS_SMB_DIRECT if (server->rdma) { if (server->sign) + /* + * Account for SMB2 data transfer packet header and + * possible encryption header + */ rsize = min_t(unsigned int, - rsize, server->smbd_conn->max_fragmented_recv_size); + rsize, + server->smbd_conn->max_fragmented_recv_size - + SMB2_READWRITE_PDU_HEADER_SIZE - + sizeof(struct smb2_transform_hdr)); else rsize = min_t(unsigned int, rsize, server->smbd_conn->max_readwrite_size); @@ -794,7 +788,8 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, tcon->crfid.has_lease = true; smb2_parse_contexts(server, o_rsp, &oparms.fid->epoch, - oparms.fid->lease_key, &oplock, NULL); + oparms.fid->lease_key, &oplock, + NULL, NULL); } else goto oshr_exit; @@ -838,7 +833,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, if (no_cached_open) rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, - NULL); + NULL, NULL); else rc = open_shroot(xid, tcon, cifs_sb, &fid); @@ -878,7 +873,8 @@ smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, oparms.fid = &fid; oparms.reconnect = false; - rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL); + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, + NULL, NULL); if (rc) return; @@ -913,7 +909,8 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, oparms.fid = &fid; oparms.reconnect = false; - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL); + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, + NULL); if (rc) { kfree(utf16_path); return rc; @@ -2122,7 +2119,8 @@ smb3_notify(const unsigned int xid, struct file *pfile, oparms.fid = &fid; oparms.reconnect = false; - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL); + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, + NULL); if (rc) goto notify_exit; @@ -2222,6 +2220,8 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, goto qdf_free; } + atomic_inc(&tcon->num_remote_opens); + qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base; if (qd_rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) { trace_smb3_query_dir_done(xid, fid->persistent_fid, @@ -2541,7 +2541,8 @@ smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon, oparms.fid = &fid; oparms.reconnect = false; - rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL); + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, + NULL, NULL); if (rc) return rc; @@ -3026,7 +3027,8 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, oparms.fid = &fid; oparms.reconnect = false; - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL); + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, + NULL); kfree(utf16_path); if (!rc) { rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid, @@ -3084,7 +3086,8 @@ set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen, oparms.fid = &fid; oparms.reconnect = false; - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL); + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, + NULL, NULL); kfree(utf16_path); if (!rc) { rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid, @@ -3246,6 +3249,10 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, * Extending the file */ if ((keep_size == false) && i_size_read(inode) < off + len) { + rc = inode_newsize_ok(inode, off + len); + if (rc) + goto out; + if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) smb2_set_sparse(xid, tcon, cfile, inode, false); @@ -3417,7 +3424,7 @@ static int smb3_fiemap(struct cifs_tcon *tcon, if (rc) goto out; - if (out_data_len < sizeof(struct file_allocated_range_buffer)) { + if (out_data_len && out_data_len < sizeof(struct file_allocated_range_buffer)) { rc = -EINVAL; goto out; } @@ -4149,7 +4156,6 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, if (server->ops->is_session_expired && server->ops->is_session_expired(buf)) { cifs_reconnect(server); - wake_up(&server->response_q); return -1; } @@ -4513,14 +4519,12 @@ smb3_receive_transform(struct TCP_Server_Info *server, cifs_server_dbg(VFS, "Transform message is too small (%u)\n", pdu_length); cifs_reconnect(server); - wake_up(&server->response_q); return -ECONNABORTED; } if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { cifs_server_dbg(VFS, "Transform message is broken\n"); cifs_reconnect(server); - wake_up(&server->response_q); return -ECONNABORTED; } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 28c0be5e69b7..47d3e382ecaa 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -193,9 +193,18 @@ static int __smb2_reconnect(const struct nls_table *nlsc, for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) { - const char *tgt = dfs_cache_get_tgt_name(it); + const char *share, *prefix; + size_t share_len, prefix_len; - extract_unc_hostname(tgt, &dfs_host, &dfs_host_len); + rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix, + &prefix_len); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse target share %d\n", + __func__, rc); + continue; + } + + extract_unc_hostname(share, &dfs_host, &dfs_host_len); if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { @@ -206,11 +215,13 @@ static int __smb2_reconnect(const struct nls_table *nlsc, continue; } - scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt); + scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share); rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc); - if (!rc) + if (!rc) { + rc = update_super_prepath(tcon, prefix, prefix_len); break; + } if (rc == -EREMOTE) break; } @@ -378,7 +389,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) } if (smb2_command != SMB2_INTERNAL_CMD) - queue_delayed_work(cifsiod_wq, &server->reconnect, 0); + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); atomic_inc(&tconInfoReconnectCount); out: @@ -1940,20 +1951,46 @@ parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) } static void -parse_posix_ctxt(struct create_context *cc, struct smb_posix_info *pposix_inf) +parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, + struct create_posix_rsp *posix) { - /* struct smb_posix_info *ppinf = (struct smb_posix_info *)cc; */ + int sid_len; + u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); + u8 *end = beg + le32_to_cpu(cc->DataLength); + u8 *sid; + + memset(posix, 0, sizeof(*posix)); + + posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); + posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); + posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); - /* TODO: Need to add parsing for the context and return */ - printk_once(KERN_WARNING - "SMB3 3.11 POSIX response context not completed yet\n"); + sid = beg + 12; + sid_len = posix_info_sid_size(sid, end); + if (sid_len < 0) { + cifs_dbg(VFS, "bad owner sid in posix create response\n"); + return; + } + memcpy(&posix->owner, sid, sid_len); + + sid = sid + sid_len; + sid_len = posix_info_sid_size(sid, end); + if (sid_len < 0) { + cifs_dbg(VFS, "bad group sid in posix create response\n"); + return; + } + memcpy(&posix->group, sid, sid_len); + + cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n", + posix->nlink, posix->mode, posix->reparse_tag); } void smb2_parse_contexts(struct TCP_Server_Info *server, - struct smb2_create_rsp *rsp, - unsigned int *epoch, char *lease_key, __u8 *oplock, - struct smb2_file_all_info *buf) + struct smb2_create_rsp *rsp, + unsigned int *epoch, char *lease_key, __u8 *oplock, + struct smb2_file_all_info *buf, + struct create_posix_rsp *posix) { char *data_offset; struct create_context *cc; @@ -1983,8 +2020,9 @@ smb2_parse_contexts(struct TCP_Server_Info *server, strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4) == 0) parse_query_id_ctxt(cc, buf); else if ((le16_to_cpu(cc->NameLength) == 16)) { - if (memcmp(name, smb3_create_tag_posix, 16) == 0) - parse_posix_ctxt(cc, NULL); + if (posix && + memcmp(name, smb3_create_tag_posix, 16) == 0) + parse_posix_ctxt(cc, buf, posix); } /* else { cifs_dbg(FYI, "Context not matched with len %d\n", @@ -2709,6 +2747,7 @@ SMB2_open_free(struct smb_rqst *rqst) int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, struct kvec *err_iov, int *buftype) { struct smb_rqst rqst; @@ -2787,7 +2826,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, smb2_parse_contexts(server, rsp, &oparms->fid->epoch, - oparms->fid->lease_key, oplock, buf); + oparms->fid->lease_key, oplock, buf, posix); creat_exit: SMB2_open_free(&rqst); free_rsp_buf(resp_buftype, rsp); @@ -3559,7 +3598,7 @@ SMB2_echo(struct TCP_Server_Info *server) if (server->tcpStatus == CifsNeedNegotiate) { /* No need to send echo on newly established connections */ - queue_delayed_work(cifsiod_wq, &server->reconnect, 0); + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); return rc; } @@ -4286,8 +4325,104 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, return rc; } +int posix_info_sid_size(const void *beg, const void *end) +{ + size_t subauth; + int total; + + if (beg + 1 > end) + return -1; + + subauth = *(u8 *)(beg+1); + if (subauth < 1 || subauth > 15) + return -1; + + total = 1 + 1 + 6 + 4*subauth; + if (beg + total > end) + return -1; + + return total; +} + +int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out) + +{ + int total_len = 0; + int sid_len; + int name_len; + const void *owner_sid; + const void *group_sid; + const void *name; + + /* if no end bound given, assume payload to be correct */ + if (!end) { + const struct smb2_posix_info *p = beg; + + end = beg + le32_to_cpu(p->NextEntryOffset); + /* last element will have a 0 offset, pick a sensible bound */ + if (end == beg) + end += 0xFFFF; + } + + /* check base buf */ + if (beg + sizeof(struct smb2_posix_info) > end) + return -1; + total_len = sizeof(struct smb2_posix_info); + + /* check owner sid */ + owner_sid = beg + total_len; + sid_len = posix_info_sid_size(owner_sid, end); + if (sid_len < 0) + return -1; + total_len += sid_len; + + /* check group sid */ + group_sid = beg + total_len; + sid_len = posix_info_sid_size(group_sid, end); + if (sid_len < 0) + return -1; + total_len += sid_len; + + /* check name len */ + if (beg + total_len + 4 > end) + return -1; + name_len = le32_to_cpu(*(__le32 *)(beg + total_len)); + if (name_len < 1 || name_len > 0xFFFF) + return -1; + total_len += 4; + + /* check name */ + name = beg + total_len; + if (name + name_len > end) + return -1; + total_len += name_len; + + if (out) { + out->base = beg; + out->size = total_len; + out->name_len = name_len; + out->name = name; + memcpy(&out->owner, owner_sid, + posix_info_sid_size(owner_sid, end)); + memcpy(&out->group, group_sid, + posix_info_sid_size(group_sid, end)); + } + return total_len; +} + +static int posix_info_extra_size(const void *beg, const void *end) +{ + int len = posix_info_parse(beg, end, NULL); + + if (len < 0) + return -1; + return len - sizeof(struct smb2_posix_info); +} + static unsigned int -num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size) +num_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry, + size_t size) { int len; unsigned int entrycount = 0; @@ -4311,8 +4446,13 @@ num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size) entryptr = entryptr + next_offset; dir_info = (FILE_DIRECTORY_INFO *)entryptr; - len = le32_to_cpu(dir_info->FileNameLength); - if (entryptr + len < entryptr || + if (infotype == SMB_FIND_FILE_POSIX_INFO) + len = posix_info_extra_size(entryptr, end_of_buf); + else + len = le32_to_cpu(dir_info->FileNameLength); + + if (len < 0 || + entryptr + len < entryptr || entryptr + len > end_of_buf || entryptr + len + size > end_of_buf) { cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", @@ -4362,6 +4502,9 @@ int SMB2_query_directory_init(const unsigned int xid, case SMB_FIND_FILE_ID_FULL_DIR_INFO: req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; break; + case SMB_FIND_FILE_POSIX_INFO: + req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO; + break; default: cifs_tcon_dbg(VFS, "info level %u isn't supported\n", info_level); @@ -4427,6 +4570,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, case SMB_FIND_FILE_ID_FULL_DIR_INFO: info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; break; + case SMB_FIND_FILE_POSIX_INFO: + /* note that posix payload are variable size */ + info_buf_size = sizeof(struct smb2_posix_info); + break; default: cifs_tcon_dbg(VFS, "info level %u isn't supported\n", srch_inf->info_level); @@ -4436,8 +4583,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), le32_to_cpu(rsp->OutputBufferLength), rsp_iov, info_buf_size); - if (rc) + if (rc) { + cifs_tcon_dbg(VFS, "bad info payload"); return rc; + } srch_inf->unicode = true; @@ -4451,9 +4600,14 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, srch_inf->srch_entries_start = srch_inf->last_entry = (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); end_of_smb = rsp_iov->iov_len + (char *)rsp; - srch_inf->entries_in_buffer = - num_entries(srch_inf->srch_entries_start, end_of_smb, - &srch_inf->last_entry, info_buf_size); + + srch_inf->entries_in_buffer = num_entries( + srch_inf->info_level, + srch_inf->srch_entries_start, + end_of_smb, + &srch_inf->last_entry, + info_buf_size); + srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index fa03df130f1a..10acf90f858d 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -91,6 +91,7 @@ #define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) #define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) +#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc) /* * SMB2 Header Definition @@ -119,6 +120,9 @@ struct smb2_sync_hdr { __u8 Signature[16]; } __packed; +/* The total header size for SMB2 read and write */ +#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_sync_hdr)) + struct smb2_sync_pdu { struct smb2_sync_hdr sync_hdr; __le16 StructureSize2; /* size of wct area (varies, request specific) */ @@ -127,16 +131,33 @@ struct smb2_sync_pdu { #define SMB3_AES128CCM_NONCE 11 #define SMB3_AES128GCM_NONCE 12 +/* Transform flags (for 3.0 dialect this flag indicates CCM */ +#define TRANSFORM_FLAG_ENCRYPTED 0x0001 struct smb2_transform_hdr { __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ __u8 Signature[16]; __u8 Nonce[16]; __le32 OriginalMessageSize; __u16 Reserved1; - __le16 Flags; /* EncryptionAlgorithm */ + __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */ __u64 SessionId; } __packed; +/* See MS-SMB2 2.2.42.1 */ +struct compression_playload_header { + __le16 AlgorithmId; + __le16 Reserved; + __le32 Length; +} __packed; + +/* See MS-SMB2 2.2.42.2 */ +struct compression_pattern_payload_v1 { + __le16 Pattern; + __le16 Reserved1; + __le16 Reserved2; + __le32 Repetitions; +} __packed; + /* * SMB2 flag definitions */ @@ -182,7 +203,7 @@ struct smb2_symlink_err_rsp { __le16 PrintNameOffset; __le16 PrintNameLength; __le32 Flags; - __u8 PathBuffer[0]; + __u8 PathBuffer[]; } __packed; /* SMB 3.1.1 and later dialects. See MS-SMB2 section 2.2.2.1 */ @@ -192,6 +213,10 @@ struct smb2_error_context_rsp { __u8 ErrorContextData; /* ErrorDataLength long array */ } __packed; +/* ErrorId values */ +#define SMB2_ERROR_ID_DEFAULT 0x00000000 +#define SMB2_ERROR_ID_SHARE_REDIRECT cpu_to_le32(0x72645253) /* "rdRS" */ + /* Defines for Type field below (see MS-SMB2 2.2.2.2.2.1) */ #define MOVE_DST_IPADDR_V4 cpu_to_le32(0x00000001) #define MOVE_DST_IPADDR_V6 cpu_to_le32(0x00000002) @@ -210,7 +235,7 @@ struct share_redirect_error_context_rsp { __le16 Flags; __le16 TargetType; __le32 IPAddrCount; - struct move_dst_ipaddr IpAddrMoveList[0]; + struct move_dst_ipaddr IpAddrMoveList[]; /* __u8 ResourceName[] */ /* Name of share as counted Unicode string */ } __packed; @@ -307,11 +332,17 @@ struct smb2_encryption_neg_context { #define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) #define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) #define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) +/* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */ +#define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) + +/* Compression Flags */ +#define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000) +#define SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED cpu_to_le32(0x00000001) struct smb2_compression_capabilities_context { __le16 ContextType; /* 3 */ __le16 DataLength; - __u32 Reserved; + __u32 Flags; __le16 CompressionAlgorithmCount; __u16 Padding; __u32 Reserved1; @@ -326,7 +357,7 @@ struct smb2_netname_neg_context { __le16 ContextType; /* 0x100 */ __le16 DataLength; __le32 Reserved; - __le16 NetName[0]; /* hostname of target converted to UCS-2 */ + __le16 NetName[]; /* hostname of target converted to UCS-2 */ } __packed; #define POSIX_CTXT_DATA_LEN 16 @@ -406,7 +437,7 @@ struct smb2_logoff_rsp { struct smb2_tree_connect_req { struct smb2_sync_hdr sync_hdr; __le16 StructureSize; /* Must be 9 */ - __le16 Reserved; /* Flags in SMB3.1.1 */ + __le16 Flags; /* Reserved MBZ for dialects prior to SMB3.1.1 */ __le16 PathOffset; __le16 PathLength; __u8 Buffer[1]; /* variable length */ @@ -421,13 +452,13 @@ struct tree_connect_contexts { __le16 ContextType; __le16 DataLength; __le32 Reserved; - __u8 Data[0]; + __u8 Data[]; } __packed; /* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */ struct smb3_blob_data { __le16 BlobSize; - __u8 BlobData[0]; + __u8 BlobData[]; } __packed; /* Valid values for Attr */ @@ -477,14 +508,14 @@ struct remoted_identity_tcon_context { __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */ __le16 UserClaims; /* offset to BLOB_DATA struct */ __le16 DeviceClaims; /* offset to BLOB_DATA struct */ - __u8 TicketInfo[0]; /* variable length buf - remoted identity data */ + __u8 TicketInfo[]; /* variable length buf - remoted identity data */ } __packed; struct smb2_tree_connect_req_extension { __le32 TreeConnectContextOffset; __le16 TreeConnectContextCount; __u8 Reserved[10]; - __u8 PathName[0]; /* variable sized array */ + __u8 PathName[]; /* variable sized array */ /* followed by array of TreeConnectContexts */ } __packed; @@ -633,7 +664,7 @@ struct smb2_tree_disconnect_rsp { | FILE_WRITE_EA_LE | FILE_WRITE_ATTRIBUTES_LE) #define FILE_EXEC_RIGHTS_LE (FILE_EXECUTE_LE) -/* Impersonation Levels */ +/* Impersonation Levels. See MS-WPO section 9.7 and MSDN-IMPERS */ #define IL_ANONYMOUS cpu_to_le32(0x00000000) #define IL_IDENTIFICATION cpu_to_le32(0x00000001) #define IL_IMPERSONATION cpu_to_le32(0x00000002) @@ -689,7 +720,7 @@ struct smb2_create_req { __le16 NameLength; __le32 CreateContextsOffset; __le32 CreateContextsLength; - __u8 Buffer[0]; + __u8 Buffer[]; } __packed; /* @@ -727,7 +758,7 @@ struct create_context { __le16 Reserved; __le16 DataOffset; __le32 DataLength; - __u8 Buffer[0]; + __u8 Buffer[]; } __packed; #define SMB2_LEASE_READ_CACHING_HE 0x01 @@ -739,7 +770,7 @@ struct create_context { #define SMB2_LEASE_HANDLE_CACHING cpu_to_le32(0x02) #define SMB2_LEASE_WRITE_CACHING cpu_to_le32(0x04) -#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS cpu_to_le32(0x02) +#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS cpu_to_le32(0x00000002) #define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET cpu_to_le32(0x00000004) #define SMB2_LEASE_KEY_SIZE 16 @@ -869,7 +900,7 @@ struct crt_sd_ctxt { struct resume_key_req { char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; __le32 ContextLength; /* MBZ */ - char Context[0]; /* ignored, Windows sets to 4 bytes of zero */ + char Context[]; /* ignored, Windows sets to 4 bytes of zero */ } __packed; /* this goes in the ioctl buffer when doing a copychunk request */ @@ -931,7 +962,7 @@ struct reparse_data_buffer { __le32 ReparseTag; __le16 ReparseDataLength; __u16 Reserved; - __u8 DataBuffer[0]; /* Variable Length */ + __u8 DataBuffer[]; /* Variable Length */ } __packed; struct reparse_guid_data_buffer { @@ -939,7 +970,7 @@ struct reparse_guid_data_buffer { __le16 ReparseDataLength; __u16 Reserved; __u8 ReparseGuid[16]; - __u8 DataBuffer[0]; /* Variable Length */ + __u8 DataBuffer[]; /* Variable Length */ } __packed; struct reparse_mount_point_data_buffer { @@ -950,7 +981,7 @@ struct reparse_mount_point_data_buffer { __le16 SubstituteNameLength; __le16 PrintNameOffset; __le16 PrintNameLength; - __u8 PathBuffer[0]; /* Variable Length */ + __u8 PathBuffer[]; /* Variable Length */ } __packed; #define SYMLINK_FLAG_RELATIVE 0x00000001 @@ -964,7 +995,7 @@ struct reparse_symlink_data_buffer { __le16 PrintNameOffset; __le16 PrintNameLength; __le32 Flags; - __u8 PathBuffer[0]; /* Variable Length */ + __u8 PathBuffer[]; /* Variable Length */ } __packed; /* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */ @@ -1066,7 +1097,7 @@ struct smb2_ioctl_req { __le32 MaxOutputResponse; __le32 Flags; __u32 Reserved2; - __u8 Buffer[0]; + __u8 Buffer[]; } __packed; struct smb2_ioctl_rsp { @@ -1180,7 +1211,7 @@ struct smb2_write_req { __le64 Offset; __u64 PersistentFileId; /* opaque endianness */ __u64 VolatileFileId; /* opaque endianness */ - __le32 Channel; /* Reserved MBZ */ + __le32 Channel; /* MBZ unless SMB3.02 or later */ __le32 RemainingBytes; __le16 WriteChannelInfoOffset; __le16 WriteChannelInfoLength; @@ -1469,7 +1500,7 @@ struct smb3_fs_vol_info { __le32 VolumeLabelLength; /* includes trailing null */ __u8 SupportsObjects; /* True if eg like NTFS, supports objects */ __u8 Reserved; - __u8 VolumeLabel[0]; /* variable len */ + __u8 VolumeLabel[]; /* variable len */ } __packed; /* partial list of QUERY INFO levels */ @@ -1531,7 +1562,7 @@ struct smb2_file_rename_info { /* encoding of request for level 10 */ __u8 Reserved[7]; __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ __le32 FileNameLength; - char FileName[0]; /* New name to be assigned */ + char FileName[]; /* New name to be assigned */ } __packed; /* level 10 Set */ struct smb2_file_link_info { /* encoding of request for level 11 */ @@ -1540,7 +1571,7 @@ struct smb2_file_link_info { /* encoding of request for level 11 */ __u8 Reserved[7]; __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ __le32 FileNameLength; - char FileName[0]; /* Name to be assigned to new link */ + char FileName[]; /* Name to be assigned to new link */ } __packed; /* level 11 Set */ struct smb2_file_full_ea_info { /* encoding of response for level 15 */ @@ -1548,7 +1579,7 @@ struct smb2_file_full_ea_info { /* encoding of response for level 15 */ __u8 flags; __u8 ea_name_length; __le16 ea_value_length; - char ea_data[0]; /* \0 terminated name plus value */ + char ea_data[]; /* \0 terminated name plus value */ } __packed; /* level 15 Set */ /* @@ -1604,11 +1635,56 @@ struct smb2_file_id_information { extern char smb2_padding[7]; /* equivalent of the contents of SMB3.1.1 POSIX open context response */ -struct smb_posix_info { - __le32 nlink; - __le32 reparse_tag; - __le32 mode; - kuid_t uid; - kuid_t gid; +struct create_posix_rsp { + u32 nlink; + u32 reparse_tag; + u32 mode; + struct cifs_sid owner; /* var-sized on the wire */ + struct cifs_sid group; /* var-sized on the wire */ +} __packed; + +/* + * SMB2-only POSIX info level + * + * See posix_info_sid_size(), posix_info_extra_size() and + * posix_info_parse() to help with the handling of this struct. + */ +struct smb2_posix_info { + __le32 NextEntryOffset; + __u32 Ignored; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* + * Parsed version of the above struct. Allows direct access to the + * variable length fields + */ +struct smb2_posix_info_parsed { + const struct smb2_posix_info *base; + size_t size; + struct cifs_sid owner; + struct cifs_sid group; + int name_len; + const u8 *name; }; + #endif /* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index de6388ef344f..087d5f14320b 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -55,9 +55,11 @@ extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid); extern int smb2_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server); + struct TCP_Server_Info *server, + bool allocate_crypto); extern int smb3_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server); + struct TCP_Server_Info *server, + bool allocate_crypto); extern void smb2_echo_request(struct work_struct *work); extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); extern bool smb2_is_valid_oplock_break(char *buffer, @@ -139,6 +141,7 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, struct kvec *err_iov, int *resp_buftype); extern int SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock, struct cifs_open_parms *oparms, @@ -252,7 +255,8 @@ extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, extern void smb2_parse_contexts(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp, unsigned int *epoch, char *lease_key, - __u8 *oplock, struct smb2_file_all_info *buf); + __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix); extern int smb3_encryption_required(const struct cifs_tcon *tcon); extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, struct kvec *iov, unsigned int min_buf_size); @@ -272,4 +276,7 @@ extern int smb2_query_info_compound(const unsigned int xid, u32 class, u32 type, u32 output_len, struct kvec *rsp, int *buftype, struct cifs_sb_info *cifs_sb); +int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out); +int posix_info_sid_size(const void *beg, const void *end); #endif /* _SMB2PROTO_H */ diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 08b703b7a15e..1a6c227ada8f 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -41,14 +41,6 @@ #include "smb2glob.h" static int -smb2_crypto_shash_allocate(struct TCP_Server_Info *server) -{ - return cifs_alloc_hash("hmac(sha256)", - &server->secmech.hmacsha256, - &server->secmech.sdeschmacsha256); -} - -static int smb3_crypto_shash_allocate(struct TCP_Server_Info *server) { struct cifs_secmech *p = &server->secmech; @@ -219,7 +211,8 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) } int -smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) +smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, + bool allocate_crypto) { int rc; unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; @@ -228,6 +221,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; struct cifs_ses *ses; struct shash_desc *shash; + struct crypto_shash *hash; + struct sdesc *sdesc = NULL; struct smb_rqst drqst; ses = smb2_find_smb_ses(server, shdr->SessionId); @@ -239,24 +234,32 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - rc = smb2_crypto_shash_allocate(server); - if (rc) { - cifs_server_dbg(VFS, "%s: sha256 alloc failed\n", __func__); - return rc; + if (allocate_crypto) { + rc = cifs_alloc_hash("hmac(sha256)", &hash, &sdesc); + if (rc) { + cifs_server_dbg(VFS, + "%s: sha256 alloc failed\n", __func__); + return rc; + } + shash = &sdesc->shash; + } else { + hash = server->secmech.hmacsha256; + shash = &server->secmech.sdeschmacsha256->shash; } - rc = crypto_shash_setkey(server->secmech.hmacsha256, - ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); + rc = crypto_shash_setkey(hash, ses->auth_key.response, + SMB2_NTLMV2_SESSKEY_SIZE); if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with response\n", __func__); - return rc; + cifs_server_dbg(VFS, + "%s: Could not update with response\n", + __func__); + goto out; } - shash = &server->secmech.sdeschmacsha256->shash; rc = crypto_shash_init(shash); if (rc) { cifs_server_dbg(VFS, "%s: Could not init sha256", __func__); - return rc; + goto out; } /* @@ -271,9 +274,10 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) rc = crypto_shash_update(shash, iov[0].iov_base, iov[0].iov_len); if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with payload\n", - __func__); - return rc; + cifs_server_dbg(VFS, + "%s: Could not update with payload\n", + __func__); + goto out; } drqst.rq_iov++; drqst.rq_nvec--; @@ -283,6 +287,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) if (!rc) memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); +out: + if (allocate_crypto) + cifs_free_hash(&hash, &sdesc); return rc; } @@ -504,14 +511,17 @@ generate_smb311signingkey(struct cifs_ses *ses) } int -smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) +smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, + bool allocate_crypto) { int rc; unsigned char smb3_signature[SMB2_CMACAES_SIZE]; unsigned char *sigptr = smb3_signature; struct kvec *iov = rqst->rq_iov; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; - struct shash_desc *shash = &server->secmech.sdesccmacaes->shash; + struct shash_desc *shash; + struct crypto_shash *hash; + struct sdesc *sdesc = NULL; struct smb_rqst drqst; u8 key[SMB3_SIGN_KEY_SIZE]; @@ -519,14 +529,24 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) if (rc) return 0; + if (allocate_crypto) { + rc = cifs_alloc_hash("cmac(aes)", &hash, &sdesc); + if (rc) + return rc; + + shash = &sdesc->shash; + } else { + hash = server->secmech.cmacaes; + shash = &server->secmech.sdesccmacaes->shash; + } + memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - rc = crypto_shash_setkey(server->secmech.cmacaes, - key, SMB2_CMACAES_SIZE); + rc = crypto_shash_setkey(hash, key, SMB2_CMACAES_SIZE); if (rc) { cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); - return rc; + goto out; } /* @@ -537,7 +557,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) rc = crypto_shash_init(shash); if (rc) { cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); - return rc; + goto out; } /* @@ -554,7 +574,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) if (rc) { cifs_server_dbg(VFS, "%s: Could not update with payload\n", __func__); - return rc; + goto out; } drqst.rq_iov++; drqst.rq_nvec--; @@ -564,6 +584,9 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) if (!rc) memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); +out: + if (allocate_crypto) + cifs_free_hash(&hash, &sdesc); return rc; } @@ -593,7 +616,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) return 0; } - rc = server->ops->calc_signature(rqst, server); + rc = server->ops->calc_signature(rqst, server, false); return rc; } @@ -602,7 +625,7 @@ int smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) { unsigned int rc; - char server_response_sig[16]; + char server_response_sig[SMB2_SIGNATURE_SIZE]; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; @@ -631,16 +654,16 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); - mutex_lock(&server->srv_mutex); - rc = server->ops->calc_signature(rqst, server); - mutex_unlock(&server->srv_mutex); + rc = server->ops->calc_signature(rqst, server, true); if (rc) return rc; - if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) + if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) { + dump_stack(); + cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n", shdr->Command, shdr->MessageId); return -EACCES; - else + } else return 0; } diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c index 5b1b97e9e0c9..1a5834a5d597 100644 --- a/fs/cifs/smbdirect.c +++ b/fs/cifs/smbdirect.c @@ -284,13 +284,10 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) request->sge[i].length, DMA_TO_DEVICE); - if (request->has_payload) { - if (atomic_dec_and_test(&request->info->send_payload_pending)) - wake_up(&request->info->wait_send_payload_pending); - } else { - if (atomic_dec_and_test(&request->info->send_pending)) - wake_up(&request->info->wait_send_pending); - } + if (atomic_dec_and_test(&request->info->send_pending)) + wake_up(&request->info->wait_send_pending); + + wake_up(&request->info->wait_post_send); mempool_free(request, request->info->request_mempool); } @@ -383,27 +380,6 @@ static bool process_negotiation_response( return true; } -/* - * Check and schedule to send an immediate packet - * This is used to extend credtis to remote peer to keep the transport busy - */ -static void check_and_send_immediate(struct smbd_connection *info) -{ - if (info->transport_status != SMBD_CONNECTED) - return; - - info->send_immediate = true; - - /* - * Promptly send a packet if our peer is running low on receive - * credits - */ - if (atomic_read(&info->receive_credits) < - info->receive_credit_target - 1) - queue_delayed_work( - info->workqueue, &info->send_immediate_work, 0); -} - static void smbd_post_send_credits(struct work_struct *work) { int ret = 0; @@ -453,29 +429,16 @@ static void smbd_post_send_credits(struct work_struct *work) info->new_credits_offered += ret; spin_unlock(&info->lock_new_credits_offered); - atomic_add(ret, &info->receive_credits); - - /* Check if we can post new receive and grant credits to peer */ - check_and_send_immediate(info); -} - -static void smbd_recv_done_work(struct work_struct *work) -{ - struct smbd_connection *info = - container_of(work, struct smbd_connection, recv_done_work); - - /* - * We may have new send credits granted from remote peer - * If any sender is blcoked on lack of credets, unblock it - */ - if (atomic_read(&info->send_credits)) - wake_up_interruptible(&info->wait_send_queue); - - /* - * Check if we need to send something to remote peer to - * grant more credits or respond to KEEP_ALIVE packet - */ - check_and_send_immediate(info); + /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */ + info->send_immediate = true; + if (atomic_read(&info->receive_credits) < + info->receive_credit_target - 1) { + if (info->keep_alive_requested == KEEP_ALIVE_PENDING || + info->send_immediate) { + log_keep_alive(INFO, "send an empty message\n"); + smbd_post_send_empty(info); + } + } } /* Called from softirq, when recv is done */ @@ -546,8 +509,15 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) atomic_dec(&info->receive_credits); info->receive_credit_target = le16_to_cpu(data_transfer->credits_requested); - atomic_add(le16_to_cpu(data_transfer->credits_granted), - &info->send_credits); + if (le16_to_cpu(data_transfer->credits_granted)) { + atomic_add(le16_to_cpu(data_transfer->credits_granted), + &info->send_credits); + /* + * We have new send credits granted from remote peer + * If any sender is waiting for credits, unblock it + */ + wake_up_interruptible(&info->wait_send_queue); + } log_incoming(INFO, "data flags %d data_offset %d " "data_length %d remaining_data_length %d\n", @@ -563,7 +533,6 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) info->keep_alive_requested = KEEP_ALIVE_PENDING; } - queue_work(info->workqueue, &info->recv_done_work); return; default: @@ -756,7 +725,6 @@ static int smbd_post_send_negotiate_req(struct smbd_connection *info) request->sge[0].addr, request->sge[0].length, request->sge[0].lkey); - request->has_payload = false; atomic_inc(&info->send_pending); rc = ib_post_send(info->id->qp, &send_wr, NULL); if (!rc) @@ -813,45 +781,96 @@ static int manage_keep_alive_before_sending(struct smbd_connection *info) return 0; } -/* - * Build and prepare the SMBD packet header - * This function waits for avaialbe send credits and build a SMBD packet - * header. The caller then optional append payload to the packet after - * the header - * intput values - * size: the size of the payload - * remaining_data_length: remaining data to send if this is part of a - * fragmented packet - * output values - * request_out: the request allocated from this function - * return values: 0 on success, otherwise actual error code returned - */ -static int smbd_create_header(struct smbd_connection *info, - int size, int remaining_data_length, - struct smbd_request **request_out) +/* Post the send request */ +static int smbd_post_send(struct smbd_connection *info, + struct smbd_request *request) { + struct ib_send_wr send_wr; + int rc, i; + + for (i = 0; i < request->num_sge; i++) { + log_rdma_send(INFO, + "rdma_request sge[%d] addr=%llu length=%u\n", + i, request->sge[i].addr, request->sge[i].length); + ib_dma_sync_single_for_device( + info->id->device, + request->sge[i].addr, + request->sge[i].length, + DMA_TO_DEVICE); + } + + request->cqe.done = send_done; + + send_wr.next = NULL; + send_wr.wr_cqe = &request->cqe; + send_wr.sg_list = request->sge; + send_wr.num_sge = request->num_sge; + send_wr.opcode = IB_WR_SEND; + send_wr.send_flags = IB_SEND_SIGNALED; + + rc = ib_post_send(info->id->qp, &send_wr, NULL); + if (rc) { + log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); + smbd_disconnect_rdma_connection(info); + rc = -EAGAIN; + } else + /* Reset timer for idle connection after packet is sent */ + mod_delayed_work(info->workqueue, &info->idle_timer_work, + info->keep_alive_interval*HZ); + + return rc; +} + +static int smbd_post_send_sgl(struct smbd_connection *info, + struct scatterlist *sgl, int data_length, int remaining_data_length) +{ + int num_sgs; + int i, rc; + int header_length; struct smbd_request *request; struct smbd_data_transfer *packet; - int header_length; - int rc; + int new_credits; + struct scatterlist *sg; +wait_credit: /* Wait for send credits. A SMBD packet needs one credit */ rc = wait_event_interruptible(info->wait_send_queue, atomic_read(&info->send_credits) > 0 || info->transport_status != SMBD_CONNECTED); if (rc) - return rc; + goto err_wait_credit; if (info->transport_status != SMBD_CONNECTED) { - log_outgoing(ERR, "disconnected not sending\n"); - return -EAGAIN; + log_outgoing(ERR, "disconnected not sending on wait_credit\n"); + rc = -EAGAIN; + goto err_wait_credit; + } + if (unlikely(atomic_dec_return(&info->send_credits) < 0)) { + atomic_inc(&info->send_credits); + goto wait_credit; + } + +wait_send_queue: + wait_event(info->wait_post_send, + atomic_read(&info->send_pending) < info->send_credit_target || + info->transport_status != SMBD_CONNECTED); + + if (info->transport_status != SMBD_CONNECTED) { + log_outgoing(ERR, "disconnected not sending on wait_send_queue\n"); + rc = -EAGAIN; + goto err_wait_send_queue; + } + + if (unlikely(atomic_inc_return(&info->send_pending) > + info->send_credit_target)) { + atomic_dec(&info->send_pending); + goto wait_send_queue; } - atomic_dec(&info->send_credits); request = mempool_alloc(info->request_mempool, GFP_KERNEL); if (!request) { rc = -ENOMEM; - goto err; + goto err_alloc; } request->info = info; @@ -859,8 +878,11 @@ static int smbd_create_header(struct smbd_connection *info, /* Fill in the packet header */ packet = smbd_request_payload(request); packet->credits_requested = cpu_to_le16(info->send_credit_target); - packet->credits_granted = - cpu_to_le16(manage_credits_prior_sending(info)); + + new_credits = manage_credits_prior_sending(info); + atomic_add(new_credits, &info->receive_credits); + packet->credits_granted = cpu_to_le16(new_credits); + info->send_immediate = false; packet->flags = 0; @@ -868,11 +890,11 @@ static int smbd_create_header(struct smbd_connection *info, packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED); packet->reserved = 0; - if (!size) + if (!data_length) packet->data_offset = 0; else packet->data_offset = cpu_to_le32(24); - packet->data_length = cpu_to_le32(size); + packet->data_length = cpu_to_le32(data_length); packet->remaining_data_length = cpu_to_le32(remaining_data_length); packet->padding = 0; @@ -887,7 +909,7 @@ static int smbd_create_header(struct smbd_connection *info, /* Map the packet to DMA */ header_length = sizeof(struct smbd_data_transfer); /* If this is a packet without payload, don't send padding */ - if (!size) + if (!data_length) header_length = offsetof(struct smbd_data_transfer, padding); request->num_sge = 1; @@ -896,102 +918,15 @@ static int smbd_create_header(struct smbd_connection *info, header_length, DMA_TO_DEVICE); if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { - mempool_free(request, info->request_mempool); rc = -EIO; - goto err; + request->sge[0].addr = 0; + goto err_dma; } request->sge[0].length = header_length; request->sge[0].lkey = info->pd->local_dma_lkey; - *request_out = request; - return 0; - -err: - atomic_inc(&info->send_credits); - return rc; -} - -static void smbd_destroy_header(struct smbd_connection *info, - struct smbd_request *request) -{ - - ib_dma_unmap_single(info->id->device, - request->sge[0].addr, - request->sge[0].length, - DMA_TO_DEVICE); - mempool_free(request, info->request_mempool); - atomic_inc(&info->send_credits); -} - -/* Post the send request */ -static int smbd_post_send(struct smbd_connection *info, - struct smbd_request *request, bool has_payload) -{ - struct ib_send_wr send_wr; - int rc, i; - - for (i = 0; i < request->num_sge; i++) { - log_rdma_send(INFO, - "rdma_request sge[%d] addr=%llu length=%u\n", - i, request->sge[i].addr, request->sge[i].length); - ib_dma_sync_single_for_device( - info->id->device, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - } - - request->cqe.done = send_done; - - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; - - if (has_payload) { - request->has_payload = true; - atomic_inc(&info->send_payload_pending); - } else { - request->has_payload = false; - atomic_inc(&info->send_pending); - } - - rc = ib_post_send(info->id->qp, &send_wr, NULL); - if (rc) { - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - if (has_payload) { - if (atomic_dec_and_test(&info->send_payload_pending)) - wake_up(&info->wait_send_payload_pending); - } else { - if (atomic_dec_and_test(&info->send_pending)) - wake_up(&info->wait_send_pending); - } - smbd_disconnect_rdma_connection(info); - rc = -EAGAIN; - } else - /* Reset timer for idle connection after packet is sent */ - mod_delayed_work(info->workqueue, &info->idle_timer_work, - info->keep_alive_interval*HZ); - - return rc; -} - -static int smbd_post_send_sgl(struct smbd_connection *info, - struct scatterlist *sgl, int data_length, int remaining_data_length) -{ - int num_sgs; - int i, rc; - struct smbd_request *request; - struct scatterlist *sg; - - rc = smbd_create_header( - info, data_length, remaining_data_length, &request); - if (rc) - return rc; - + /* Fill in the packet data payload */ num_sgs = sgl ? sg_nents(sgl) : 0; for_each_sg(sgl, sg, num_sgs, i) { request->sge[i+1].addr = @@ -1001,25 +936,41 @@ static int smbd_post_send_sgl(struct smbd_connection *info, info->id->device, request->sge[i+1].addr)) { rc = -EIO; request->sge[i+1].addr = 0; - goto dma_mapping_failure; + goto err_dma; } request->sge[i+1].length = sg->length; request->sge[i+1].lkey = info->pd->local_dma_lkey; request->num_sge++; } - rc = smbd_post_send(info, request, data_length); + rc = smbd_post_send(info, request); if (!rc) return 0; -dma_mapping_failure: - for (i = 1; i < request->num_sge; i++) +err_dma: + for (i = 0; i < request->num_sge; i++) if (request->sge[i].addr) ib_dma_unmap_single(info->id->device, request->sge[i].addr, request->sge[i].length, DMA_TO_DEVICE); - smbd_destroy_header(info, request); + mempool_free(request, info->request_mempool); + + /* roll back receive credits and credits to be offered */ + spin_lock(&info->lock_new_credits_offered); + info->new_credits_offered += new_credits; + spin_unlock(&info->lock_new_credits_offered); + atomic_sub(new_credits, &info->receive_credits); + +err_alloc: + if (atomic_dec_and_test(&info->send_pending)) + wake_up(&info->wait_send_pending); + +err_wait_send_queue: + /* roll back send credits and pending */ + atomic_inc(&info->send_credits); + +err_wait_credit: return rc; } @@ -1341,25 +1292,6 @@ static void destroy_receive_buffers(struct smbd_connection *info) mempool_free(response, info->response_mempool); } -/* - * Check and send an immediate or keep alive packet - * The condition to send those packets are defined in [MS-SMBD] 3.1.1.1 - * Connection.KeepaliveRequested and Connection.SendImmediate - * The idea is to extend credits to server as soon as it becomes available - */ -static void send_immediate_work(struct work_struct *work) -{ - struct smbd_connection *info = container_of( - work, struct smbd_connection, - send_immediate_work.work); - - if (info->keep_alive_requested == KEEP_ALIVE_PENDING || - info->send_immediate) { - log_keep_alive(INFO, "send an empty message\n"); - smbd_post_send_empty(info); - } -} - /* Implement idle connection timer [MS-SMBD] 3.1.6.2 */ static void idle_connection_timer(struct work_struct *work) { @@ -1414,14 +1346,10 @@ void smbd_destroy(struct TCP_Server_Info *server) log_rdma_event(INFO, "cancelling idle timer\n"); cancel_delayed_work_sync(&info->idle_timer_work); - log_rdma_event(INFO, "cancelling send immediate work\n"); - cancel_delayed_work_sync(&info->send_immediate_work); log_rdma_event(INFO, "wait for all send posted to IB to finish\n"); wait_event(info->wait_send_pending, atomic_read(&info->send_pending) == 0); - wait_event(info->wait_send_payload_pending, - atomic_read(&info->send_payload_pending) == 0); /* It's not posssible for upper layer to get to reassembly */ log_rdma_event(INFO, "drain the reassembly queue\n"); @@ -1751,18 +1679,15 @@ static struct smbd_connection *_smbd_get_connection( init_waitqueue_head(&info->wait_send_queue); INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer); - INIT_DELAYED_WORK(&info->send_immediate_work, send_immediate_work); queue_delayed_work(info->workqueue, &info->idle_timer_work, info->keep_alive_interval*HZ); init_waitqueue_head(&info->wait_send_pending); atomic_set(&info->send_pending, 0); - init_waitqueue_head(&info->wait_send_payload_pending); - atomic_set(&info->send_payload_pending, 0); + init_waitqueue_head(&info->wait_post_send); INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work); - INIT_WORK(&info->recv_done_work, smbd_recv_done_work); INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits); info->new_credits_offered = 0; spin_lock_init(&info->lock_new_credits_offered); @@ -2097,8 +2022,7 @@ int smbd_send(struct TCP_Server_Info *server, for (i = 0; i < num_rqst; i++) remaining_data_length += smb_rqst_len(server, &rqst_array[i]); - if (remaining_data_length + sizeof(struct smbd_data_transfer) > - info->max_fragmented_send_size) { + if (remaining_data_length > info->max_fragmented_send_size) { log_write(ERR, "payload size %d > max size %d\n", remaining_data_length, info->max_fragmented_send_size); rc = -EINVAL; @@ -2235,8 +2159,8 @@ done: * that means all the I/Os have been out and we are good to return */ - wait_event(info->wait_send_payload_pending, - atomic_read(&info->send_payload_pending) == 0); + wait_event(info->wait_send_pending, + atomic_read(&info->send_pending) == 0); return rc; } diff --git a/fs/cifs/smbdirect.h b/fs/cifs/smbdirect.h index 6ff880a1e186..a87fca82a796 100644 --- a/fs/cifs/smbdirect.h +++ b/fs/cifs/smbdirect.h @@ -67,7 +67,6 @@ struct smbd_connection { bool negotiate_done; struct work_struct disconnect_work; - struct work_struct recv_done_work; struct work_struct post_send_credits_work; spinlock_t lock_new_credits_offered; @@ -115,8 +114,7 @@ struct smbd_connection { /* Activity accoutning */ atomic_t send_pending; wait_queue_head_t wait_send_pending; - atomic_t send_payload_pending; - wait_queue_head_t wait_send_payload_pending; + wait_queue_head_t wait_post_send; /* Receive queue */ struct list_head receive_queue; @@ -155,7 +153,6 @@ struct smbd_connection { struct workqueue_struct *workqueue; struct delayed_work idle_timer_work; - struct delayed_work send_immediate_work; /* Memory pool for preallocating buffers */ /* request pool for RDMA send */ @@ -235,9 +232,6 @@ struct smbd_request { struct smbd_connection *info; struct ib_cqe cqe; - /* true if this request carries upper layer payload */ - bool has_payload; - /* the SGE entries for this packet */ struct ib_sge sge[SMBDIRECT_MAX_SGE]; int num_sge; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index cb3ee916f527..c97570eb2c18 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -466,7 +466,7 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst, int flags) { struct kvec iov; - struct smb2_transform_hdr tr_hdr; + struct smb2_transform_hdr *tr_hdr; struct smb_rqst cur_rqst[MAX_COMPOUND]; int rc; @@ -476,28 +476,34 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, if (num_rqst > MAX_COMPOUND - 1) return -ENOMEM; - memset(&cur_rqst[0], 0, sizeof(cur_rqst)); - memset(&iov, 0, sizeof(iov)); - memset(&tr_hdr, 0, sizeof(tr_hdr)); - - iov.iov_base = &tr_hdr; - iov.iov_len = sizeof(tr_hdr); - cur_rqst[0].rq_iov = &iov; - cur_rqst[0].rq_nvec = 1; - if (!server->ops->init_transform_rq) { cifs_server_dbg(VFS, "Encryption requested but transform " "callback is missing\n"); return -EIO; } + tr_hdr = kmalloc(sizeof(*tr_hdr), GFP_NOFS); + if (!tr_hdr) + return -ENOMEM; + + memset(&cur_rqst[0], 0, sizeof(cur_rqst)); + memset(&iov, 0, sizeof(iov)); + memset(tr_hdr, 0, sizeof(*tr_hdr)); + + iov.iov_base = tr_hdr; + iov.iov_len = sizeof(*tr_hdr); + cur_rqst[0].rq_iov = &iov; + cur_rqst[0].rq_nvec = 1; + rc = server->ops->init_transform_rq(server, num_rqst + 1, &cur_rqst[0], rqst); if (rc) - return rc; + goto out; rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); +out: + kfree(tr_hdr); return rc; } |