diff options
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r-- | fs/cifs/connect.c | 195 |
1 files changed, 129 insertions, 66 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 42e14f408856..fa29c9aae24b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -97,6 +97,10 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server) if (!server->hostname) return -EINVAL; + /* if server hostname isn't populated, there's nothing to do here */ + if (server->hostname[0] == '\0') + return 0; + len = strlen(server->hostname) + 3; unc = kmalloc(len, GFP_KERNEL); @@ -141,6 +145,25 @@ requeue_resolve: return rc; } +static void smb2_query_server_interfaces(struct work_struct *work) +{ + int rc; + struct cifs_tcon *tcon = container_of(work, + struct cifs_tcon, + query_interfaces.work); + + /* + * query server network interfaces, in case they change + */ + rc = SMB3_request_interfaces(0, tcon); + if (rc) { + cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", + __func__, rc); + } + + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, + (SMB_INTERFACE_POLL_INTERVAL * HZ)); +} static void cifs_resolve_server(struct work_struct *work) { @@ -148,7 +171,7 @@ static void cifs_resolve_server(struct work_struct *work) struct TCP_Server_Info *server = container_of(work, struct TCP_Server_Info, resolve.work); - mutex_lock(&server->srv_mutex); + cifs_server_lock(server); /* * Resolve the hostname again to make sure that IP address is up-to-date. @@ -159,7 +182,7 @@ static void cifs_resolve_server(struct work_struct *work) __func__, rc); } - mutex_unlock(&server->srv_mutex); + cifs_server_unlock(server); } /* @@ -213,7 +236,7 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) { struct TCP_Server_Info *pserver; - struct cifs_ses *ses; + struct cifs_ses *ses, *nses; struct cifs_tcon *tcon; /* @@ -227,7 +250,20 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) { + /* check if iface is still active */ + if (!cifs_chan_is_iface_active(ses, server)) { + /* + * HACK: drop the lock before calling + * cifs_chan_update_iface to avoid deadlock + */ + ses->ses_count++; + spin_unlock(&cifs_tcp_ses_lock); + cifs_chan_update_iface(ses, server); + spin_lock(&cifs_tcp_ses_lock); + ses->ses_count--; + } + spin_lock(&ses->chan_lock); if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) goto next_session; @@ -241,7 +277,7 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) goto next_session; - ses->status = CifsNeedReconnect; + ses->ses_status = SES_NEED_RECON; list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { tcon->need_reconnect = true; @@ -267,7 +303,7 @@ cifs_abort_connection(struct TCP_Server_Info *server) /* do not want to be sending data on a socket we are freeing */ cifs_dbg(FYI, "%s: tearing down socket\n", __func__); - mutex_lock(&server->srv_mutex); + cifs_server_lock(server); if (server->ssocket) { cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state, server->ssocket->flags); @@ -296,7 +332,7 @@ cifs_abort_connection(struct TCP_Server_Info *server) mid->mid_flags |= MID_DELETED; } spin_unlock(&GlobalMid_Lock); - mutex_unlock(&server->srv_mutex); + cifs_server_unlock(server); cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); list_for_each_entry_safe(mid, nmid, &retry_list, qhead) { @@ -306,9 +342,9 @@ cifs_abort_connection(struct TCP_Server_Info *server) } if (cifs_rdma_enabled(server)) { - mutex_lock(&server->srv_mutex); + cifs_server_lock(server); smbd_destroy(server); - mutex_unlock(&server->srv_mutex); + cifs_server_unlock(server); } } @@ -359,7 +395,7 @@ static int __cifs_reconnect(struct TCP_Server_Info *server, do { try_to_freeze(); - mutex_lock(&server->srv_mutex); + cifs_server_lock(server); if (!cifs_swn_set_server_dstaddr(server)) { /* resolve the hostname again to make sure that IP address is up-to-date */ @@ -372,7 +408,7 @@ static int __cifs_reconnect(struct TCP_Server_Info *server, else rc = generic_ip_connect(server); if (rc) { - mutex_unlock(&server->srv_mutex); + cifs_server_unlock(server); cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); msleep(3000); } else { @@ -383,7 +419,7 @@ static int __cifs_reconnect(struct TCP_Server_Info *server, server->tcpStatus = CifsNeedNegotiate; spin_unlock(&cifs_tcp_ses_lock); cifs_swn_reset_server_dstaddr(server); - mutex_unlock(&server->srv_mutex); + cifs_server_unlock(server); mod_delayed_work(cifsiod_wq, &server->reconnect, 0); } } while (server->tcpStatus == CifsNeedReconnect); @@ -488,12 +524,12 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server) do { try_to_freeze(); - mutex_lock(&server->srv_mutex); + cifs_server_lock(server); rc = reconnect_target_unlocked(server, &tl, &target_hint); if (rc) { /* Failed to reconnect socket */ - mutex_unlock(&server->srv_mutex); + cifs_server_unlock(server); cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); msleep(3000); continue; @@ -510,7 +546,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server) server->tcpStatus = CifsNeedNegotiate; spin_unlock(&cifs_tcp_ses_lock); cifs_swn_reset_server_dstaddr(server); - mutex_unlock(&server->srv_mutex); + cifs_server_unlock(server); mod_delayed_work(cifsiod_wq, &server->reconnect, 0); } while (server->tcpStatus == CifsNeedReconnect); @@ -1565,7 +1601,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, init_waitqueue_head(&tcp_ses->response_q); init_waitqueue_head(&tcp_ses->request_q); INIT_LIST_HEAD(&tcp_ses->pending_mid_q); - mutex_init(&tcp_ses->srv_mutex); + mutex_init(&tcp_ses->_srv_mutex); memcpy(tcp_ses->workstation_RFC1001_name, ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); memcpy(tcp_ses->server_RFC1001_name, @@ -1789,7 +1825,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) goto out; } - cifs_dbg(FYI, "IPC tcon rc = %d ipc tid = %d\n", rc, tcon->tid); + cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid); ses->tcon_ipc = tcon; out: @@ -1828,7 +1864,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - if (ses->status == CifsExiting) + if (ses->ses_status == SES_EXITING) continue; if (!match_session(ses, ctx)) continue; @@ -1845,10 +1881,9 @@ void cifs_put_smb_ses(struct cifs_ses *ses) unsigned int rc, xid; unsigned int chan_count; 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) { + if (ses->ses_status == SES_EXITING) { spin_unlock(&cifs_tcp_ses_lock); return; } @@ -1864,13 +1899,13 @@ void cifs_put_smb_ses(struct cifs_ses *ses) /* ses_count can never go negative */ WARN_ON(ses->ses_count < 0); - if (ses->status == CifsGood) - ses->status = CifsExiting; + if (ses->ses_status == SES_GOOD) + ses->ses_status = SES_EXITING; spin_unlock(&cifs_tcp_ses_lock); cifs_free_ipc(ses); - if (ses->status == CifsExiting && server->ops->logoff) { + if (ses->ses_status == SES_EXITING && server->ops->logoff) { xid = get_xid(); rc = server->ops->logoff(xid, ses); if (rc) @@ -1891,9 +1926,11 @@ void cifs_put_smb_ses(struct cifs_ses *ses) int i; for (i = 1; i < chan_count; i++) { - spin_unlock(&ses->chan_lock); + if (ses->chans[i].iface) { + kref_put(&ses->chans[i].iface->refcount, release_iface); + ses->chans[i].iface = NULL; + } cifs_put_tcp_session(ses->chans[i].server, 0); - spin_lock(&ses->chan_lock); ses->chans[i].server = NULL; } } @@ -2037,18 +2074,7 @@ cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses) } } - ctx->workstation_name = kstrdup(ses->workstation_name, GFP_KERNEL); - if (!ctx->workstation_name) { - cifs_dbg(FYI, "Unable to allocate memory for workstation_name\n"); - rc = -ENOMEM; - kfree(ctx->username); - ctx->username = NULL; - kfree_sensitive(ctx->password); - ctx->password = NULL; - kfree(ctx->domainname); - ctx->domainname = NULL; - goto out_key_put; - } + strscpy(ctx->workstation_name, ses->workstation_name, sizeof(ctx->workstation_name)); out_key_put: up_read(&key->sem); @@ -2090,7 +2116,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ses = cifs_find_smb_ses(server, ctx); if (ses) { cifs_dbg(FYI, "Existing smb sess found (status=%d)\n", - ses->status); + ses->ses_status); spin_lock(&ses->chan_lock); if (cifs_chan_needs_reconnect(ses, server)) { @@ -2157,12 +2183,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) if (!ses->domainName) goto get_ses_fail; } - if (ctx->workstation_name) { - ses->workstation_name = kstrdup(ctx->workstation_name, - GFP_KERNEL); - if (!ses->workstation_name) - goto get_ses_fail; - } + + strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name)); + if (ctx->domainauto) ses->domainAuto = ctx->domainauto; ses->cred_uid = ctx->cred_uid; @@ -2281,6 +2304,9 @@ cifs_put_tcon(struct cifs_tcon *tcon) list_del_init(&tcon->tcon_list); spin_unlock(&cifs_tcp_ses_lock); + /* cancel polling of interfaces */ + cancel_delayed_work_sync(&tcon->query_interfaces); + if (tcon->use_witness) { int rc; @@ -2509,6 +2535,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) */ tcon->retry = ctx->retry; tcon->nocase = ctx->nocase; + tcon->broken_sparse_sup = ctx->no_sparse; if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) tcon->nohandlecache = ctx->nohandlecache; else @@ -2517,6 +2544,12 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) tcon->local_lease = ctx->local_lease; INIT_LIST_HEAD(&tcon->pending_opens); + /* schedule query interfaces poll */ + INIT_DELAYED_WORK(&tcon->query_interfaces, + smb2_query_server_interfaces); + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, + (SMB_INTERFACE_POLL_INTERVAL * HZ)); + spin_lock(&cifs_tcp_ses_lock); list_add(&tcon->tcon_list, &ses->tcon_list); spin_unlock(&cifs_tcp_ses_lock); @@ -3420,8 +3453,9 @@ cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, } /* - * Check if path is remote (e.g. a DFS share). Return -EREMOTE if it is, - * otherwise 0. + * Check if path is remote (i.e. a DFS share). + * + * Return -EREMOTE if it is, otherwise 0 or -errno. */ static int is_path_remote(struct mount_ctx *mnt_ctx) { @@ -3432,6 +3466,9 @@ static int is_path_remote(struct mount_ctx *mnt_ctx) struct cifs_tcon *tcon = mnt_ctx->tcon; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; char *full_path; +#ifdef CONFIG_CIFS_DFS_UPCALL + bool nodfs = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS; +#endif if (!server->ops->is_path_accessible) return -EOPNOTSUPP; @@ -3449,14 +3486,20 @@ static int is_path_remote(struct mount_ctx *mnt_ctx) rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, full_path); #ifdef CONFIG_CIFS_DFS_UPCALL + if (nodfs) { + if (rc == -EREMOTE) + rc = -EOPNOTSUPP; + goto out; + } + + /* path *might* exist with non-ASCII characters in DFS root + * try again with full path (only if nodfs is not set) */ if (rc == -ENOENT && is_tcon_dfs(tcon)) rc = cifs_dfs_query_info_nonascii_quirk(xid, tcon, cifs_sb, full_path); #endif - if (rc != 0 && rc != -EREMOTE) { - kfree(full_path); - return rc; - } + if (rc != 0 && rc != -EREMOTE) + goto out; if (rc != -EREMOTE) { rc = cifs_are_all_path_components_accessible(server, xid, tcon, @@ -3468,6 +3511,7 @@ static int is_path_remote(struct mount_ctx *mnt_ctx) } } +out: kfree(full_path); return rc; } @@ -3703,6 +3747,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) if (!isdfs) goto out; + /* proceed as DFS mount */ uuid_gen(&mnt_ctx.mount_id); rc = connect_dfs_root(&mnt_ctx, &tl); dfs_cache_free_tgts(&tl); @@ -3960,7 +4005,7 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, if (rc == 0) { spin_lock(&cifs_tcp_ses_lock); if (server->tcpStatus == CifsInNegotiate) - server->tcpStatus = CifsNeedSessSetup; + server->tcpStatus = CifsGood; else rc = -EHOSTDOWN; spin_unlock(&cifs_tcp_ses_lock); @@ -3980,22 +4025,39 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, struct nls_table *nls_info) { int rc = -ENOSYS; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; + struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; bool is_binding = false; - /* only send once per connect */ spin_lock(&cifs_tcp_ses_lock); - if ((server->tcpStatus != CifsNeedSessSetup) && - (ses->status == CifsGood)) { + if (server->dstaddr.ss_family == AF_INET6) + scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr); + else + scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr); + + if (ses->ses_status != SES_GOOD && + ses->ses_status != SES_NEW && + ses->ses_status != SES_NEED_RECON) { spin_unlock(&cifs_tcp_ses_lock); return 0; } - server->tcpStatus = CifsInSessSetup; - spin_unlock(&cifs_tcp_ses_lock); + /* only send once per connect */ spin_lock(&ses->chan_lock); + if (CIFS_ALL_CHANS_GOOD(ses) || + cifs_chan_in_reconnect(ses, server)) { + spin_unlock(&ses->chan_lock); + spin_unlock(&cifs_tcp_ses_lock); + return 0; + } is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); + cifs_chan_set_in_reconnect(ses, server); spin_unlock(&ses->chan_lock); + if (!is_binding) + ses->ses_status = SES_IN_SETUP; + spin_unlock(&cifs_tcp_ses_lock); + if (!is_binding) { ses->capabilities = server->capabilities; if (!linuxExtEnabled) @@ -4019,20 +4081,21 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, if (rc) { cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc); spin_lock(&cifs_tcp_ses_lock); - if (server->tcpStatus == CifsInSessSetup) - server->tcpStatus = CifsNeedSessSetup; + if (ses->ses_status == SES_IN_SETUP) + ses->ses_status = SES_NEED_RECON; + spin_lock(&ses->chan_lock); + cifs_chan_clear_in_reconnect(ses, server); + spin_unlock(&ses->chan_lock); spin_unlock(&cifs_tcp_ses_lock); } else { spin_lock(&cifs_tcp_ses_lock); - if (server->tcpStatus == CifsInSessSetup) - server->tcpStatus = CifsGood; - /* Even if one channel is active, session is in good state */ - ses->status = CifsGood; - spin_unlock(&cifs_tcp_ses_lock); - + if (ses->ses_status == SES_IN_SETUP) + ses->ses_status = SES_GOOD; spin_lock(&ses->chan_lock); + cifs_chan_clear_in_reconnect(ses, server); cifs_chan_clear_need_reconnect(ses, server); spin_unlock(&ses->chan_lock); + spin_unlock(&cifs_tcp_ses_lock); } return rc; @@ -4497,7 +4560,7 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru /* only send once per connect */ spin_lock(&cifs_tcp_ses_lock); - if (tcon->ses->status != CifsGood || + if (tcon->ses->ses_status != SES_GOOD || (tcon->status != TID_NEW && tcon->status != TID_NEED_TCON)) { spin_unlock(&cifs_tcp_ses_lock); @@ -4565,7 +4628,7 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru /* only send once per connect */ spin_lock(&cifs_tcp_ses_lock); - if (tcon->ses->status != CifsGood || + if (tcon->ses->ses_status != SES_GOOD || (tcon->status != TID_NEW && tcon->status != TID_NEED_TCON)) { spin_unlock(&cifs_tcp_ses_lock); |