diff options
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r-- | fs/cifs/connect.c | 2138 |
1 files changed, 430 insertions, 1708 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 44f9cce57099..10fe6d6d2dee 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -62,6 +62,9 @@ #include "dfs_cache.h" #endif #include "fs_context.h" +#ifdef CONFIG_CIFS_SWN_UPCALL +#include "cifs_swn.h" +#endif extern mempool_t *cifs_req_poolp; extern bool disable_legacy_dialects; @@ -73,218 +76,10 @@ extern bool disable_legacy_dialects; /* Drop the connection to not overload the server */ #define NUM_STATUS_IO_TIMEOUT 5 -enum { - /* Mount options that take no arguments */ - Opt_user_xattr, Opt_nouser_xattr, - Opt_forceuid, Opt_noforceuid, - Opt_forcegid, Opt_noforcegid, - Opt_noblocksend, Opt_noautotune, Opt_nolease, - Opt_hard, Opt_soft, Opt_perm, Opt_noperm, Opt_nodelete, - Opt_mapposix, Opt_nomapposix, - Opt_mapchars, Opt_nomapchars, Opt_sfu, - Opt_nosfu, Opt_nodfs, Opt_posixpaths, - Opt_noposixpaths, Opt_nounix, Opt_unix, - Opt_nocase, - Opt_brl, Opt_nobrl, - Opt_handlecache, Opt_nohandlecache, - Opt_forcemandatorylock, Opt_setuidfromacl, Opt_setuids, - Opt_nosetuids, Opt_dynperm, Opt_nodynperm, - Opt_nohard, Opt_nosoft, - Opt_nointr, Opt_intr, - Opt_nostrictsync, Opt_strictsync, - Opt_serverino, Opt_noserverino, - Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl, - Opt_acl, Opt_noacl, Opt_locallease, - Opt_sign, Opt_ignore_signature, Opt_seal, Opt_noac, - Opt_fsc, Opt_mfsymlinks, - Opt_multiuser, Opt_sloppy, Opt_nosharesock, - Opt_persistent, Opt_nopersistent, - Opt_resilient, Opt_noresilient, - Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs, - Opt_multichannel, Opt_nomultichannel, - Opt_compress, - - /* Mount options which take numeric value */ - Opt_backupuid, Opt_backupgid, Opt_uid, - Opt_cruid, Opt_gid, Opt_file_mode, - Opt_dirmode, Opt_port, - Opt_min_enc_offload, - Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo, - Opt_echo_interval, Opt_max_credits, Opt_handletimeout, - Opt_snapshot, Opt_max_channels, - - /* Mount options which take string value */ - Opt_user, Opt_pass, Opt_ip, - Opt_domain, Opt_srcaddr, Opt_iocharset, - Opt_netbiosname, Opt_servern, - Opt_ver, Opt_vers, Opt_sec, Opt_cache, - - /* Mount options to be ignored */ - Opt_ignore, - - /* Options which could be blank */ - Opt_blank_pass, - Opt_blank_user, - Opt_blank_ip, - - Opt_err -}; - -static const match_table_t cifs_mount_option_tokens = { - - { Opt_user_xattr, "user_xattr" }, - { Opt_nouser_xattr, "nouser_xattr" }, - { Opt_forceuid, "forceuid" }, - { Opt_noforceuid, "noforceuid" }, - { Opt_forcegid, "forcegid" }, - { Opt_noforcegid, "noforcegid" }, - { Opt_noblocksend, "noblocksend" }, - { Opt_noautotune, "noautotune" }, - { Opt_nolease, "nolease" }, - { Opt_hard, "hard" }, - { Opt_soft, "soft" }, - { Opt_perm, "perm" }, - { Opt_noperm, "noperm" }, - { Opt_nodelete, "nodelete" }, - { Opt_mapchars, "mapchars" }, /* SFU style */ - { Opt_nomapchars, "nomapchars" }, - { Opt_mapposix, "mapposix" }, /* SFM style */ - { Opt_nomapposix, "nomapposix" }, - { Opt_sfu, "sfu" }, - { Opt_nosfu, "nosfu" }, - { Opt_nodfs, "nodfs" }, - { Opt_posixpaths, "posixpaths" }, - { Opt_noposixpaths, "noposixpaths" }, - { Opt_nounix, "nounix" }, - { Opt_nounix, "nolinux" }, - { Opt_nounix, "noposix" }, - { Opt_unix, "unix" }, - { Opt_unix, "linux" }, - { Opt_unix, "posix" }, - { Opt_nocase, "nocase" }, - { Opt_nocase, "ignorecase" }, - { Opt_brl, "brl" }, - { Opt_nobrl, "nobrl" }, - { Opt_handlecache, "handlecache" }, - { Opt_nohandlecache, "nohandlecache" }, - { Opt_nobrl, "nolock" }, - { Opt_forcemandatorylock, "forcemandatorylock" }, - { Opt_forcemandatorylock, "forcemand" }, - { Opt_setuids, "setuids" }, - { Opt_nosetuids, "nosetuids" }, - { Opt_setuidfromacl, "idsfromsid" }, - { Opt_dynperm, "dynperm" }, - { Opt_nodynperm, "nodynperm" }, - { Opt_nohard, "nohard" }, - { Opt_nosoft, "nosoft" }, - { Opt_nointr, "nointr" }, - { Opt_intr, "intr" }, - { Opt_nostrictsync, "nostrictsync" }, - { Opt_strictsync, "strictsync" }, - { Opt_serverino, "serverino" }, - { Opt_noserverino, "noserverino" }, - { Opt_rwpidforward, "rwpidforward" }, - { Opt_modesid, "modefromsid" }, - { Opt_cifsacl, "cifsacl" }, - { Opt_nocifsacl, "nocifsacl" }, - { Opt_acl, "acl" }, - { Opt_noacl, "noacl" }, - { Opt_locallease, "locallease" }, - { Opt_sign, "sign" }, - { Opt_ignore_signature, "signloosely" }, - { Opt_seal, "seal" }, - { Opt_noac, "noac" }, - { Opt_fsc, "fsc" }, - { Opt_mfsymlinks, "mfsymlinks" }, - { Opt_multiuser, "multiuser" }, - { Opt_sloppy, "sloppy" }, - { Opt_nosharesock, "nosharesock" }, - { Opt_persistent, "persistenthandles"}, - { Opt_nopersistent, "nopersistenthandles"}, - { Opt_resilient, "resilienthandles"}, - { Opt_noresilient, "noresilienthandles"}, - { Opt_domainauto, "domainauto"}, - { Opt_rdma, "rdma"}, - { Opt_multichannel, "multichannel" }, - { Opt_nomultichannel, "nomultichannel" }, - - { Opt_backupuid, "backupuid=%s" }, - { Opt_backupgid, "backupgid=%s" }, - { Opt_uid, "uid=%s" }, - { Opt_cruid, "cruid=%s" }, - { Opt_gid, "gid=%s" }, - { Opt_file_mode, "file_mode=%s" }, - { Opt_dirmode, "dirmode=%s" }, - { Opt_dirmode, "dir_mode=%s" }, - { Opt_port, "port=%s" }, - { Opt_min_enc_offload, "esize=%s" }, - { Opt_blocksize, "bsize=%s" }, - { Opt_rsize, "rsize=%s" }, - { Opt_wsize, "wsize=%s" }, - { Opt_actimeo, "actimeo=%s" }, - { Opt_handletimeout, "handletimeout=%s" }, - { Opt_echo_interval, "echo_interval=%s" }, - { Opt_max_credits, "max_credits=%s" }, - { Opt_snapshot, "snapshot=%s" }, - { Opt_max_channels, "max_channels=%s" }, - { Opt_compress, "compress=%s" }, - - { Opt_blank_user, "user=" }, - { Opt_blank_user, "username=" }, - { Opt_user, "user=%s" }, - { Opt_user, "username=%s" }, - { Opt_blank_pass, "pass=" }, - { Opt_blank_pass, "password=" }, - { Opt_pass, "pass=%s" }, - { Opt_pass, "password=%s" }, - { Opt_blank_ip, "ip=" }, - { Opt_blank_ip, "addr=" }, - { Opt_ip, "ip=%s" }, - { Opt_ip, "addr=%s" }, - { Opt_ignore, "unc=%s" }, - { Opt_ignore, "target=%s" }, - { Opt_ignore, "path=%s" }, - { Opt_domain, "dom=%s" }, - { Opt_domain, "domain=%s" }, - { Opt_domain, "workgroup=%s" }, - { Opt_srcaddr, "srcaddr=%s" }, - { Opt_ignore, "prefixpath=%s" }, - { Opt_iocharset, "iocharset=%s" }, - { Opt_netbiosname, "netbiosname=%s" }, - { Opt_servern, "servern=%s" }, - { Opt_ver, "ver=%s" }, - { Opt_vers, "vers=%s" }, - { Opt_sec, "sec=%s" }, - { Opt_cache, "cache=%s" }, - - { Opt_ignore, "cred" }, - { Opt_ignore, "credentials" }, - { Opt_ignore, "cred=%s" }, - { Opt_ignore, "credentials=%s" }, - { Opt_ignore, "guest" }, - { Opt_ignore, "rw" }, - { Opt_ignore, "ro" }, - { Opt_ignore, "suid" }, - { Opt_ignore, "nosuid" }, - { Opt_ignore, "exec" }, - { Opt_ignore, "noexec" }, - { Opt_ignore, "nodev" }, - { Opt_ignore, "noauto" }, - { Opt_ignore, "dev" }, - { Opt_ignore, "mand" }, - { Opt_ignore, "nomand" }, - { Opt_ignore, "relatime" }, - { Opt_ignore, "_netdev" }, - { Opt_rootfs, "rootfs" }, - - { Opt_err, NULL } -}; - static int ip_connect(struct TCP_Server_Info *server); static int generic_ip_connect(struct TCP_Server_Info *server); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); static void cifs_prune_tlinks(struct work_struct *work); -static char *extract_hostname(const char *unc); /* * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may @@ -293,7 +88,7 @@ static char *extract_hostname(const char *unc); * This should be called with server->srv_mutex held. */ #ifdef CONFIG_CIFS_DFS_UPCALL -static int reconn_set_ipaddr(struct TCP_Server_Info *server) +static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server) { int rc; int len; @@ -328,14 +123,7 @@ static int reconn_set_ipaddr(struct TCP_Server_Info *server) return !rc ? -1 : 0; } -#else -static inline int reconn_set_ipaddr(struct TCP_Server_Info *server) -{ - return 0; -} -#endif -#ifdef CONFIG_CIFS_DFS_UPCALL /* These functions must be called with server->srv_mutex held */ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, @@ -343,6 +131,7 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server, struct dfs_cache_tgt_iterator **tgt_it) { const char *name; + int rc; if (!cifs_sb || !cifs_sb->origin_fullpath) return; @@ -366,6 +155,13 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server, cifs_dbg(FYI, "%s: failed to extract hostname from target: %ld\n", __func__, PTR_ERR(server->hostname)); + return; + } + + rc = reconn_set_ipaddr_from_hostname(server); + if (rc) { + cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", + __func__, rc); } } @@ -517,19 +313,25 @@ cifs_reconnect(struct TCP_Server_Info *server) try_to_freeze(); mutex_lock(&server->srv_mutex); + +#ifdef CONFIG_CIFS_SWN_UPCALL + if (server->use_swn_dstaddr) { + server->dstaddr = server->swn_dstaddr; + } else { +#endif + #ifdef CONFIG_CIFS_DFS_UPCALL - /* - * Set up next DFS target server (if any) for reconnect. If DFS - * feature is disabled, then we will retry last server we - * connected to before. - */ - reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it); + /* + * Set up next DFS target server (if any) for reconnect. If DFS + * feature is disabled, then we will retry last server we + * connected to before. + */ + reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it); #endif - rc = reconn_set_ipaddr(server); - if (rc) { - cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", - __func__, rc); + +#ifdef CONFIG_CIFS_SWN_UPCALL } +#endif if (cifs_rdma_enabled(server)) rc = smbd_reconnect(server); @@ -546,6 +348,9 @@ cifs_reconnect(struct TCP_Server_Info *server) if (server->tcpStatus != CifsExiting) server->tcpStatus = CifsNeedNegotiate; spin_unlock(&GlobalMid_Lock); +#ifdef CONFIG_CIFS_SWN_UPCALL + server->use_swn_dstaddr = false; +#endif mutex_unlock(&server->srv_mutex); } } while (server->tcpStatus == CifsNeedReconnect); @@ -610,6 +415,11 @@ cifs_echo_request(struct work_struct *work) cifs_dbg(FYI, "Unable to send echo request to server: %s\n", server->hostname); +#ifdef CONFIG_CIFS_SWN_UPCALL + /* Check witness registrations */ + cifs_swn_check(); +#endif + requeue_echo: queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval); } @@ -1036,6 +846,7 @@ static void smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) { struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer; + int scredits = server->credits; /* * SMB1 does not use credits. @@ -1048,6 +859,13 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) server->credits += le16_to_cpu(shdr->CreditRequest); spin_unlock(&server->req_lock); wake_up(&server->request_q); + + trace_smb3_add_credits(server->CurrentMid, + server->hostname, scredits, + le16_to_cpu(shdr->CreditRequest)); + cifs_server_dbg(FYI, "%s: added %u credits total=%d\n", + __func__, le16_to_cpu(shdr->CreditRequest), + scredits); } } @@ -1232,1051 +1050,9 @@ next_pdu: module_put_and_exit(0); } -/* extract the host portion of the UNC string */ -static char * -extract_hostname(const char *unc) -{ - const char *src; - char *dst, *delim; - unsigned int len; - - /* skip double chars at beginning of string */ - /* BB: check validity of these bytes? */ - if (strlen(unc) < 3) - return ERR_PTR(-EINVAL); - for (src = unc; *src && *src == '\\'; src++) - ; - if (!*src) - return ERR_PTR(-EINVAL); - - /* delimiter between hostname and sharename is always '\\' now */ - delim = strchr(src, '\\'); - if (!delim) - return ERR_PTR(-EINVAL); - - len = delim - src; - dst = kmalloc((len + 1), GFP_KERNEL); - if (dst == NULL) - return ERR_PTR(-ENOMEM); - - memcpy(dst, src, len); - dst[len] = '\0'; - - return dst; -} - -static int get_option_ul(substring_t args[], unsigned long *option) -{ - int rc; - char *string; - - string = match_strdup(args); - if (string == NULL) - return -ENOMEM; - rc = kstrtoul(string, 0, option); - kfree(string); - - return rc; -} - -static int get_option_uid(substring_t args[], kuid_t *result) -{ - unsigned long value; - kuid_t uid; - int rc; - - rc = get_option_ul(args, &value); - if (rc) - return rc; - - uid = make_kuid(current_user_ns(), value); - if (!uid_valid(uid)) - return -EINVAL; - - *result = uid; - return 0; -} - -static int get_option_gid(substring_t args[], kgid_t *result) -{ - unsigned long value; - kgid_t gid; - int rc; - - rc = get_option_ul(args, &value); - if (rc) - return rc; - - gid = make_kgid(current_user_ns(), value); - if (!gid_valid(gid)) - return -EINVAL; - - *result = gid; - return 0; -} - -/* - * Parse a devname into substrings and populate the vol->UNC and vol->prepath - * fields with the result. Returns 0 on success and an error otherwise. - */ -static int -cifs_parse_devname(const char *devname, struct smb_vol *vol) -{ - char *pos; - const char *delims = "/\\"; - size_t len; - - if (unlikely(!devname || !*devname)) { - cifs_dbg(VFS, "Device name not specified\n"); - return -EINVAL; - } - - /* make sure we have a valid UNC double delimiter prefix */ - len = strspn(devname, delims); - if (len != 2) - return -EINVAL; - - /* find delimiter between host and sharename */ - pos = strpbrk(devname + 2, delims); - if (!pos) - return -EINVAL; - - /* skip past delimiter */ - ++pos; - - /* now go until next delimiter or end of string */ - len = strcspn(pos, delims); - - /* move "pos" up to delimiter or NULL */ - pos += len; - vol->UNC = kstrndup(devname, pos - devname, GFP_KERNEL); - if (!vol->UNC) - return -ENOMEM; - - convert_delimiter(vol->UNC, '\\'); - - /* skip any delimiter */ - if (*pos == '/' || *pos == '\\') - pos++; - - /* If pos is NULL then no prepath */ - if (!*pos) - return 0; - - vol->prepath = kstrdup(pos, GFP_KERNEL); - if (!vol->prepath) - return -ENOMEM; - - return 0; -} - -static int -cifs_parse_mount_options(const char *mountdata, const char *devname, - struct smb_vol *vol, bool is_smb3) -{ - char *data, *end; - char *mountdata_copy = NULL, *options; - unsigned int temp_len, i, j; - char separator[2]; - short int override_uid = -1; - short int override_gid = -1; - bool uid_specified = false; - bool gid_specified = false; - bool sloppy = false; - char *invalid = NULL; - char *nodename = utsname()->nodename; - char *string = NULL; - char *tmp_end, *value; - char delim; - bool got_ip = false; - bool got_version = false; - unsigned short port = 0; - struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr; - - separator[0] = ','; - separator[1] = 0; - delim = separator[0]; - - /* ensure we always start with zeroed-out smb_vol */ - memset(vol, 0, sizeof(*vol)); - - /* - * does not have to be perfect mapping since field is - * informational, only used for servers that do not support - * port 445 and it can be overridden at mount time - */ - memset(vol->source_rfc1001_name, 0x20, RFC1001_NAME_LEN); - for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++) - vol->source_rfc1001_name[i] = toupper(nodename[i]); - - vol->source_rfc1001_name[RFC1001_NAME_LEN] = 0; - /* null target name indicates to use *SMBSERVR default called name - if we end up sending RFC1001 session initialize */ - vol->target_rfc1001_name[0] = 0; - vol->cred_uid = current_uid(); - vol->linux_uid = current_uid(); - vol->linux_gid = current_gid(); - vol->bsize = 1024 * 1024; /* can improve cp performance significantly */ - /* - * default to SFM style remapping of seven reserved characters - * unless user overrides it or we negotiate CIFS POSIX where - * it is unnecessary. Can not simultaneously use more than one mapping - * since then readdir could list files that open could not open - */ - vol->remap = true; - - /* default to only allowing write access to owner of the mount */ - vol->dir_mode = vol->file_mode = S_IRUGO | S_IXUGO | S_IWUSR; - - /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */ - /* default is always to request posix paths. */ - vol->posix_paths = 1; - /* default to using server inode numbers where available */ - vol->server_ino = 1; - - /* default is to use strict cifs caching semantics */ - vol->strict_io = true; - - vol->actimeo = CIFS_DEF_ACTIMEO; - - /* Most clients set timeout to 0, allows server to use its default */ - vol->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */ - - /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */ - vol->ops = &smb30_operations; - vol->vals = &smbdefault_values; - - vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT; - - /* default to no multichannel (single server connection) */ - vol->multichannel = false; - vol->max_channels = 1; - - if (!mountdata) - goto cifs_parse_mount_err; - - mountdata_copy = kstrndup(mountdata, PAGE_SIZE, GFP_KERNEL); - if (!mountdata_copy) - goto cifs_parse_mount_err; - - options = mountdata_copy; - end = options + strlen(options); - - if (strncmp(options, "sep=", 4) == 0) { - if (options[4] != 0) { - separator[0] = options[4]; - options += 5; - } else { - cifs_dbg(FYI, "Null separator not allowed\n"); - } - } - vol->backupuid_specified = false; /* no backup intent for a user */ - vol->backupgid_specified = false; /* no backup intent for a group */ - - switch (cifs_parse_devname(devname, vol)) { - case 0: - break; - case -ENOMEM: - cifs_dbg(VFS, "Unable to allocate memory for devname\n"); - goto cifs_parse_mount_err; - case -EINVAL: - cifs_dbg(VFS, "Malformed UNC in devname\n"); - goto cifs_parse_mount_err; - default: - cifs_dbg(VFS, "Unknown error parsing devname\n"); - goto cifs_parse_mount_err; - } - - while ((data = strsep(&options, separator)) != NULL) { - substring_t args[MAX_OPT_ARGS]; - unsigned long option; - int token; - - if (!*data) - continue; - - token = match_token(data, cifs_mount_option_tokens, args); - - switch (token) { - - /* Ingnore the following */ - case Opt_ignore: - break; - - /* Boolean values */ - case Opt_user_xattr: - vol->no_xattr = 0; - break; - case Opt_nouser_xattr: - vol->no_xattr = 1; - break; - case Opt_forceuid: - override_uid = 1; - break; - case Opt_noforceuid: - override_uid = 0; - break; - case Opt_forcegid: - override_gid = 1; - break; - case Opt_noforcegid: - override_gid = 0; - break; - case Opt_noblocksend: - vol->noblocksnd = 1; - break; - case Opt_noautotune: - vol->noautotune = 1; - break; - case Opt_nolease: - vol->no_lease = 1; - break; - case Opt_hard: - vol->retry = 1; - break; - case Opt_soft: - vol->retry = 0; - break; - case Opt_perm: - vol->noperm = 0; - break; - case Opt_noperm: - vol->noperm = 1; - break; - case Opt_nodelete: - vol->nodelete = 1; - break; - case Opt_mapchars: - vol->sfu_remap = true; - vol->remap = false; /* disable SFM mapping */ - break; - case Opt_nomapchars: - vol->sfu_remap = false; - break; - case Opt_mapposix: - vol->remap = true; - vol->sfu_remap = false; /* disable SFU mapping */ - break; - case Opt_nomapposix: - vol->remap = false; - break; - case Opt_sfu: - vol->sfu_emul = 1; - break; - case Opt_nosfu: - vol->sfu_emul = 0; - break; - case Opt_nodfs: - vol->nodfs = 1; - break; - case Opt_rootfs: -#ifdef CONFIG_CIFS_ROOT - vol->rootfs = true; -#endif - break; - case Opt_posixpaths: - vol->posix_paths = 1; - break; - case Opt_noposixpaths: - vol->posix_paths = 0; - break; - case Opt_nounix: - if (vol->linux_ext) - cifs_dbg(VFS, - "conflicting unix mount options\n"); - vol->no_linux_ext = 1; - break; - case Opt_unix: - if (vol->no_linux_ext) - cifs_dbg(VFS, - "conflicting unix mount options\n"); - vol->linux_ext = 1; - break; - case Opt_nocase: - vol->nocase = 1; - break; - case Opt_brl: - vol->nobrl = 0; - break; - case Opt_nobrl: - vol->nobrl = 1; - /* - * turn off mandatory locking in mode - * if remote locking is turned off since the - * local vfs will do advisory - */ - if (vol->file_mode == - (S_IALLUGO & ~(S_ISUID | S_IXGRP))) - vol->file_mode = S_IALLUGO; - break; - case Opt_nohandlecache: - vol->nohandlecache = 1; - break; - case Opt_handlecache: - vol->nohandlecache = 0; - break; - case Opt_forcemandatorylock: - vol->mand_lock = 1; - break; - case Opt_setuids: - vol->setuids = 1; - break; - case Opt_nosetuids: - vol->setuids = 0; - break; - case Opt_setuidfromacl: - vol->setuidfromacl = 1; - break; - case Opt_dynperm: - vol->dynperm = true; - break; - case Opt_nodynperm: - vol->dynperm = false; - break; - case Opt_nohard: - vol->retry = 0; - break; - case Opt_nosoft: - vol->retry = 1; - break; - case Opt_nointr: - vol->intr = 0; - break; - case Opt_intr: - vol->intr = 1; - break; - case Opt_nostrictsync: - vol->nostrictsync = 1; - break; - case Opt_strictsync: - vol->nostrictsync = 0; - break; - case Opt_serverino: - vol->server_ino = 1; - break; - case Opt_noserverino: - vol->server_ino = 0; - break; - case Opt_rwpidforward: - vol->rwpidforward = 1; - break; - case Opt_modesid: - vol->mode_ace = 1; - break; - case Opt_cifsacl: - vol->cifs_acl = 1; - break; - case Opt_nocifsacl: - vol->cifs_acl = 0; - break; - case Opt_acl: - vol->no_psx_acl = 0; - break; - case Opt_noacl: - vol->no_psx_acl = 1; - break; - case Opt_locallease: - vol->local_lease = 1; - break; - case Opt_sign: - vol->sign = true; - break; - case Opt_ignore_signature: - vol->sign = true; - vol->ignore_signature = true; - break; - case Opt_seal: - /* we do not do the following in secFlags because seal - * is a per tree connection (mount) not a per socket - * or per-smb connection option in the protocol - * vol->secFlg |= CIFSSEC_MUST_SEAL; - */ - vol->seal = 1; - break; - case Opt_noac: - pr_warn("Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n"); - break; - case Opt_fsc: -#ifndef CONFIG_CIFS_FSCACHE - cifs_dbg(VFS, "FS-Cache support needs CONFIG_CIFS_FSCACHE kernel config option set\n"); - goto cifs_parse_mount_err; -#endif - vol->fsc = true; - break; - case Opt_mfsymlinks: - vol->mfsymlinks = true; - break; - case Opt_multiuser: - vol->multiuser = true; - break; - case Opt_sloppy: - sloppy = true; - break; - case Opt_nosharesock: - vol->nosharesock = true; - break; - case Opt_nopersistent: - vol->nopersistent = true; - if (vol->persistent) { - cifs_dbg(VFS, - "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_persistent: - vol->persistent = true; - if ((vol->nopersistent) || (vol->resilient)) { - cifs_dbg(VFS, - "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_resilient: - vol->resilient = true; - if (vol->persistent) { - cifs_dbg(VFS, - "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_noresilient: - vol->resilient = false; /* already the default */ - break; - case Opt_domainauto: - vol->domainauto = true; - break; - case Opt_rdma: - vol->rdma = true; - break; - case Opt_multichannel: - vol->multichannel = true; - /* if number of channels not specified, default to 2 */ - if (vol->max_channels < 2) - vol->max_channels = 2; - break; - case Opt_nomultichannel: - vol->multichannel = false; - vol->max_channels = 1; - break; - case Opt_compress: - vol->compression = UNKNOWN_TYPE; - cifs_dbg(VFS, - "SMB3 compression support is experimental\n"); - break; - - /* Numeric Values */ - case Opt_backupuid: - if (get_option_uid(args, &vol->backupuid)) { - cifs_dbg(VFS, "%s: Invalid backupuid value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->backupuid_specified = true; - break; - case Opt_backupgid: - if (get_option_gid(args, &vol->backupgid)) { - cifs_dbg(VFS, "%s: Invalid backupgid value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->backupgid_specified = true; - break; - case Opt_uid: - if (get_option_uid(args, &vol->linux_uid)) { - cifs_dbg(VFS, "%s: Invalid uid value\n", - __func__); - goto cifs_parse_mount_err; - } - uid_specified = true; - break; - case Opt_cruid: - if (get_option_uid(args, &vol->cred_uid)) { - cifs_dbg(VFS, "%s: Invalid cruid value\n", - __func__); - goto cifs_parse_mount_err; - } - break; - case Opt_gid: - if (get_option_gid(args, &vol->linux_gid)) { - cifs_dbg(VFS, "%s: Invalid gid value\n", - __func__); - goto cifs_parse_mount_err; - } - gid_specified = true; - break; - case Opt_file_mode: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid file_mode value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->file_mode = option; - break; - case Opt_dirmode: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid dir_mode value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->dir_mode = option; - break; - case Opt_port: - if (get_option_ul(args, &option) || - option > USHRT_MAX) { - cifs_dbg(VFS, "%s: Invalid port value\n", - __func__); - goto cifs_parse_mount_err; - } - port = (unsigned short)option; - break; - case Opt_min_enc_offload: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "Invalid minimum encrypted read offload size (esize)\n"); - goto cifs_parse_mount_err; - } - vol->min_offload = option; - break; - case Opt_blocksize: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid blocksize value\n", - __func__); - goto cifs_parse_mount_err; - } - /* - * inode blocksize realistically should never need to be - * less than 16K or greater than 16M and default is 1MB. - * Note that small inode block sizes (e.g. 64K) can lead - * to very poor performance of common tools like cp and scp - */ - if ((option < CIFS_MAX_MSGSIZE) || - (option > (4 * SMB3_DEFAULT_IOSIZE))) { - cifs_dbg(VFS, "%s: Invalid blocksize\n", - __func__); - goto cifs_parse_mount_err; - } - vol->bsize = option; - break; - case Opt_rsize: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid rsize value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->rsize = option; - break; - case Opt_wsize: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid wsize value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->wsize = option; - break; - case Opt_actimeo: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid actimeo value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->actimeo = HZ * option; - if (vol->actimeo > CIFS_MAX_ACTIMEO) { - cifs_dbg(VFS, "attribute cache timeout too large\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_handletimeout: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid handletimeout value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->handle_timeout = option; - if (vol->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) { - cifs_dbg(VFS, "Invalid handle cache timeout, longer than 16 minutes\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_echo_interval: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid echo interval value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->echo_interval = option; - break; - case Opt_snapshot: - if (get_option_ul(args, &option)) { - cifs_dbg(VFS, "%s: Invalid snapshot time\n", - __func__); - goto cifs_parse_mount_err; - } - vol->snapshot_time = option; - break; - case Opt_max_credits: - if (get_option_ul(args, &option) || (option < 20) || - (option > 60000)) { - cifs_dbg(VFS, "%s: Invalid max_credits value\n", - __func__); - goto cifs_parse_mount_err; - } - vol->max_credits = option; - break; - case Opt_max_channels: - if (get_option_ul(args, &option) || option < 1 || - option > CIFS_MAX_CHANNELS) { - cifs_dbg(VFS, "%s: Invalid max_channels value, needs to be 1-%d\n", - __func__, CIFS_MAX_CHANNELS); - goto cifs_parse_mount_err; - } - vol->max_channels = option; - break; - - /* String Arguments */ - - case Opt_blank_user: - /* null user, ie. anonymous authentication */ - vol->nullauth = 1; - vol->username = NULL; - break; - case Opt_user: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - if (strnlen(string, CIFS_MAX_USERNAME_LEN) > - CIFS_MAX_USERNAME_LEN) { - pr_warn("username too long\n"); - goto cifs_parse_mount_err; - } - - kfree(vol->username); - vol->username = kstrdup(string, GFP_KERNEL); - if (!vol->username) - goto cifs_parse_mount_err; - break; - case Opt_blank_pass: - /* passwords have to be handled differently - * to allow the character used for deliminator - * to be passed within them - */ - - /* - * Check if this is a case where the password - * starts with a delimiter - */ - tmp_end = strchr(data, '='); - tmp_end++; - if (!(tmp_end < end && tmp_end[1] == delim)) { - /* No it is not. Set the password to NULL */ - kfree_sensitive(vol->password); - vol->password = NULL; - break; - } - fallthrough; /* to Opt_pass below */ - case Opt_pass: - /* Obtain the value string */ - value = strchr(data, '='); - value++; - - /* Set tmp_end to end of the string */ - tmp_end = (char *) value + strlen(value); - - /* Check if following character is the deliminator - * If yes, we have encountered a double deliminator - * reset the NULL character to the deliminator - */ - if (tmp_end < end && tmp_end[1] == delim) { - tmp_end[0] = delim; - - /* Keep iterating until we get to a single - * deliminator OR the end - */ - while ((tmp_end = strchr(tmp_end, delim)) - != NULL && (tmp_end[1] == delim)) { - tmp_end = (char *) &tmp_end[2]; - } - - /* Reset var options to point to next element */ - if (tmp_end) { - tmp_end[0] = '\0'; - options = (char *) &tmp_end[1]; - } else - /* Reached the end of the mount option - * string */ - options = end; - } - - kfree_sensitive(vol->password); - /* Now build new password string */ - temp_len = strlen(value); - vol->password = kzalloc(temp_len+1, GFP_KERNEL); - if (vol->password == NULL) { - pr_warn("no memory for password\n"); - goto cifs_parse_mount_err; - } - - for (i = 0, j = 0; i < temp_len; i++, j++) { - vol->password[j] = value[i]; - if ((value[i] == delim) && - value[i+1] == delim) - /* skip the second deliminator */ - i++; - } - vol->password[j] = '\0'; - break; - case Opt_blank_ip: - /* FIXME: should this be an error instead? */ - got_ip = false; - break; - case Opt_ip: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - if (!cifs_convert_address(dstaddr, string, - strlen(string))) { - pr_err("bad ip= option (%s)\n", string); - goto cifs_parse_mount_err; - } - got_ip = true; - break; - case Opt_domain: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - if (strnlen(string, CIFS_MAX_DOMAINNAME_LEN) - == CIFS_MAX_DOMAINNAME_LEN) { - pr_warn("domain name too long\n"); - goto cifs_parse_mount_err; - } - - kfree(vol->domainname); - vol->domainname = kstrdup(string, GFP_KERNEL); - if (!vol->domainname) { - pr_warn("no memory for domainname\n"); - goto cifs_parse_mount_err; - } - cifs_dbg(FYI, "Domain name set\n"); - break; - case Opt_srcaddr: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - if (!cifs_convert_address( - (struct sockaddr *)&vol->srcaddr, - string, strlen(string))) { - pr_warn("Could not parse srcaddr: %s\n", - string); - goto cifs_parse_mount_err; - } - break; - case Opt_iocharset: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - if (strnlen(string, 1024) >= 65) { - pr_warn("iocharset name too long\n"); - goto cifs_parse_mount_err; - } - - if (strncasecmp(string, "default", 7) != 0) { - kfree(vol->iocharset); - vol->iocharset = kstrdup(string, - GFP_KERNEL); - if (!vol->iocharset) { - pr_warn("no memory for charset\n"); - goto cifs_parse_mount_err; - } - } - /* if iocharset not set then load_nls_default - * is used by caller - */ - cifs_dbg(FYI, "iocharset set to %s\n", string); - break; - case Opt_netbiosname: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - memset(vol->source_rfc1001_name, 0x20, - RFC1001_NAME_LEN); - /* - * FIXME: are there cases in which a comma can - * be valid in workstation netbios name (and - * need special handling)? - */ - for (i = 0; i < RFC1001_NAME_LEN; i++) { - /* don't ucase netbiosname for user */ - if (string[i] == 0) - break; - vol->source_rfc1001_name[i] = string[i]; - } - /* The string has 16th byte zero still from - * set at top of the function - */ - if (i == RFC1001_NAME_LEN && string[i] != 0) - pr_warn("netbiosname longer than 15 truncated\n"); - break; - case Opt_servern: - /* servernetbiosname specified override *SMBSERVER */ - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - /* last byte, type, is 0x20 for servr type */ - memset(vol->target_rfc1001_name, 0x20, - RFC1001_NAME_LEN_WITH_NULL); - - /* BB are there cases in which a comma can be - valid in this workstation netbios name - (and need special handling)? */ - - /* user or mount helper must uppercase the - netbios name */ - for (i = 0; i < 15; i++) { - if (string[i] == 0) - break; - vol->target_rfc1001_name[i] = string[i]; - } - /* The string has 16th byte zero still from - set at top of the function */ - if (i == RFC1001_NAME_LEN && string[i] != 0) - pr_warn("server netbiosname longer than 15 truncated\n"); - break; - case Opt_ver: - /* version of mount userspace tools, not dialect */ - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - /* If interface changes in mount.cifs bump to new ver */ - if (strncasecmp(string, "1", 1) == 0) { - if (strlen(string) > 1) { - pr_warn("Bad mount helper ver=%s. Did you want SMB1 (CIFS) dialect and mean to type vers=1.0 instead?\n", - string); - goto cifs_parse_mount_err; - } - /* This is the default */ - break; - } - /* For all other value, error */ - pr_warn("Invalid mount helper version specified\n"); - goto cifs_parse_mount_err; - case Opt_vers: - /* protocol version (dialect) */ - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - if (cifs_parse_smb_version(string, vol, is_smb3) != 0) - goto cifs_parse_mount_err; - got_version = true; - break; - case Opt_sec: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - if (cifs_parse_security_flavors(string, vol) != 0) - goto cifs_parse_mount_err; - break; - case Opt_cache: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - - if (cifs_parse_cache_flavor(string, vol) != 0) - goto cifs_parse_mount_err; - break; - default: - /* - * An option we don't recognize. Save it off for later - * if we haven't already found one - */ - if (!invalid) - invalid = data; - break; - } - /* Free up any allocated string */ - kfree(string); - string = NULL; - } - - if (!sloppy && invalid) { - pr_err("Unknown mount option \"%s\"\n", invalid); - goto cifs_parse_mount_err; - } - - if (vol->rdma && vol->vals->protocol_id < SMB30_PROT_ID) { - cifs_dbg(VFS, "SMB Direct requires Version >=3.0\n"); - goto cifs_parse_mount_err; - } - -#ifndef CONFIG_KEYS - /* Muliuser mounts require CONFIG_KEYS support */ - if (vol->multiuser) { - cifs_dbg(VFS, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n"); - goto cifs_parse_mount_err; - } -#endif - if (!vol->UNC) { - cifs_dbg(VFS, "CIFS mount error: No usable UNC path provided in device string!\n"); - goto cifs_parse_mount_err; - } - - /* make sure UNC has a share name */ - if (!strchr(vol->UNC + 3, '\\')) { - cifs_dbg(VFS, "Malformed UNC. Unable to find share name.\n"); - goto cifs_parse_mount_err; - } - - if (!got_ip) { - int len; - const char *slash; - - /* No ip= option specified? Try to get it from UNC */ - /* Use the address part of the UNC. */ - slash = strchr(&vol->UNC[2], '\\'); - len = slash - &vol->UNC[2]; - if (!cifs_convert_address(dstaddr, &vol->UNC[2], len)) { - pr_err("Unable to determine destination address\n"); - goto cifs_parse_mount_err; - } - } - - /* set the port that we got earlier */ - cifs_set_port(dstaddr, port); - - if (uid_specified) - vol->override_uid = override_uid; - else if (override_uid == 1) - pr_notice("ignoring forceuid mount option specified with no uid= option\n"); - - if (gid_specified) - vol->override_gid = override_gid; - else if (override_gid == 1) - pr_notice("ignoring forcegid mount option specified with no gid= option\n"); - - if (got_version == false) - 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; - -out_nomem: - pr_warn("Could not allocate temporary buffer\n"); -cifs_parse_mount_err: - kfree(string); - kfree(mountdata_copy); - return 1; -} - -/** Returns true if srcaddr isn't specified and rhs isn't - * specified, or if srcaddr is specified and - * matches the IP address of the rhs argument. +/** + * Returns true if srcaddr isn't specified and rhs isn't specified, or + * if srcaddr is specified and matches the IP address of the rhs argument */ bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs) @@ -2377,14 +1153,14 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr, } static bool -match_security(struct TCP_Server_Info *server, struct smb_vol *vol) +match_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) { /* - * The select_sectype function should either return the vol->sectype + * The select_sectype function should either return the ctx->sectype * that was specified, or "Unspecified" if that sectype was not * compatible with the given NEGOTIATE request. */ - if (server->ops->select_sectype(server, vol->sectype) + if (server->ops->select_sectype(server, ctx->sectype) == Unspecified) return false; @@ -2393,60 +1169,60 @@ match_security(struct TCP_Server_Info *server, struct smb_vol *vol) * global_secflags at this point since if MUST_SIGN is set then * the server->sign had better be too. */ - if (vol->sign && !server->sign) + if (ctx->sign && !server->sign) return false; return true; } -static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) +static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) { - struct sockaddr *addr = (struct sockaddr *)&vol->dstaddr; + struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; - if (vol->nosharesock) + if (ctx->nosharesock) return 0; /* If multidialect negotiation see if existing sessions match one */ - if (strcmp(vol->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { + if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { if (server->vals->protocol_id < SMB30_PROT_ID) return 0; - } else if (strcmp(vol->vals->version_string, + } else if (strcmp(ctx->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { if (server->vals->protocol_id < SMB21_PROT_ID) return 0; - } else if ((server->vals != vol->vals) || (server->ops != vol->ops)) + } else if ((server->vals != ctx->vals) || (server->ops != ctx->ops)) return 0; if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns)) return 0; if (!match_address(server, addr, - (struct sockaddr *)&vol->srcaddr)) + (struct sockaddr *)&ctx->srcaddr)) return 0; if (!match_port(server, addr)) return 0; - if (!match_security(server, vol)) + if (!match_security(server, ctx)) return 0; - if (server->echo_interval != vol->echo_interval * HZ) + if (server->echo_interval != ctx->echo_interval * HZ) return 0; - if (server->rdma != vol->rdma) + if (server->rdma != ctx->rdma) return 0; - if (server->ignore_signature != vol->ignore_signature) + if (server->ignore_signature != ctx->ignore_signature) return 0; - if (server->min_offload != vol->min_offload) + if (server->min_offload != ctx->min_offload) return 0; return 1; } struct TCP_Server_Info * -cifs_find_tcp_session(struct smb_vol *vol) +cifs_find_tcp_session(struct smb3_fs_context *ctx) { struct TCP_Server_Info *server; @@ -2456,7 +1232,7 @@ cifs_find_tcp_session(struct smb_vol *vol) * Skip ses channels since they're only handled in lower layers * (e.g. cifs_send_recv). */ - if (server->is_channel || !match_server(server, vol)) + if (server->is_channel || !match_server(server, ctx)) continue; ++server->srv_count; @@ -2514,15 +1290,15 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) } struct TCP_Server_Info * -cifs_get_tcp_session(struct smb_vol *volume_info) +cifs_get_tcp_session(struct smb3_fs_context *ctx) { struct TCP_Server_Info *tcp_ses = NULL; int rc; - cifs_dbg(FYI, "UNC: %s\n", volume_info->UNC); + cifs_dbg(FYI, "UNC: %s\n", ctx->UNC); /* see if we already have a matching tcp_ses */ - tcp_ses = cifs_find_tcp_session(volume_info); + tcp_ses = cifs_find_tcp_session(ctx); if (tcp_ses) return tcp_ses; @@ -2532,20 +1308,20 @@ cifs_get_tcp_session(struct smb_vol *volume_info) goto out_err; } - tcp_ses->ops = volume_info->ops; - tcp_ses->vals = volume_info->vals; + tcp_ses->ops = ctx->ops; + tcp_ses->vals = ctx->vals; cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns)); - tcp_ses->hostname = extract_hostname(volume_info->UNC); + tcp_ses->hostname = extract_hostname(ctx->UNC); if (IS_ERR(tcp_ses->hostname)) { rc = PTR_ERR(tcp_ses->hostname); goto out_err_crypto_release; } - tcp_ses->noblockcnt = volume_info->rootfs; - tcp_ses->noblocksnd = volume_info->noblocksnd || volume_info->rootfs; - tcp_ses->noautotune = volume_info->noautotune; - tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay; - tcp_ses->rdma = volume_info->rdma; + tcp_ses->noblockcnt = ctx->rootfs; + tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs; + tcp_ses->noautotune = ctx->noautotune; + tcp_ses->tcp_nodelay = ctx->sockopt_tcp_nodelay; + tcp_ses->rdma = ctx->rdma; tcp_ses->in_flight = 0; tcp_ses->max_in_flight = 0; tcp_ses->credits = 1; @@ -2554,26 +1330,26 @@ cifs_get_tcp_session(struct smb_vol *volume_info) INIT_LIST_HEAD(&tcp_ses->pending_mid_q); mutex_init(&tcp_ses->srv_mutex); memcpy(tcp_ses->workstation_RFC1001_name, - volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); memcpy(tcp_ses->server_RFC1001_name, - volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); tcp_ses->session_estab = false; tcp_ses->sequence_number = 0; tcp_ses->reconnect_instance = 1; tcp_ses->lstrp = jiffies; - tcp_ses->compress_algorithm = cpu_to_le16(volume_info->compression); + tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression); spin_lock_init(&tcp_ses->req_lock); INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list); INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server); mutex_init(&tcp_ses->reconnect_mutex); - memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr, + memcpy(&tcp_ses->srcaddr, &ctx->srcaddr, sizeof(tcp_ses->srcaddr)); - memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr, + memcpy(&tcp_ses->dstaddr, &ctx->dstaddr, sizeof(tcp_ses->dstaddr)); - if (volume_info->use_client_guid) - memcpy(tcp_ses->client_guid, volume_info->client_guid, + if (ctx->use_client_guid) + memcpy(tcp_ses->client_guid, ctx->client_guid, SMB2_CLIENT_GUID_SIZE); else generate_random_uuid(tcp_ses->client_guid); @@ -2585,9 +1361,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) tcp_ses->tcpStatus = CifsNew; ++tcp_ses->srv_count; - if (volume_info->echo_interval >= SMB_ECHO_INTERVAL_MIN && - volume_info->echo_interval <= SMB_ECHO_INTERVAL_MAX) - tcp_ses->echo_interval = volume_info->echo_interval * HZ; + if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN && + ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX) + tcp_ses->echo_interval = ctx->echo_interval * HZ; else tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ; if (tcp_ses->rdma) { @@ -2597,7 +1373,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) goto out_err_crypto_release; #endif tcp_ses->smbd_conn = smbd_get_connection( - tcp_ses, (struct sockaddr *)&volume_info->dstaddr); + tcp_ses, (struct sockaddr *)&ctx->dstaddr); if (tcp_ses->smbd_conn) { cifs_dbg(VFS, "RDMA transport established\n"); rc = 0; @@ -2626,11 +1402,11 @@ smbd_connected: module_put(THIS_MODULE); goto out_err_crypto_release; } - tcp_ses->min_offload = volume_info->min_offload; + tcp_ses->min_offload = ctx->min_offload; tcp_ses->tcpStatus = CifsNeedNegotiate; tcp_ses->nr_targets = 1; - tcp_ses->ignore_signature = volume_info->ignore_signature; + tcp_ses->ignore_signature = ctx->ignore_signature; /* thread spawned, put it on the list */ spin_lock(&cifs_tcp_ses_lock); list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); @@ -2659,41 +1435,41 @@ out_err: return ERR_PTR(rc); } -static int match_session(struct cifs_ses *ses, struct smb_vol *vol) +static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) { - if (vol->sectype != Unspecified && - vol->sectype != ses->sectype) + if (ctx->sectype != Unspecified && + ctx->sectype != ses->sectype) return 0; /* * If an existing session is limited to less channels than * requested, it should not be reused */ - if (ses->chan_max < vol->max_channels) + if (ses->chan_max < ctx->max_channels) return 0; switch (ses->sectype) { case Kerberos: - if (!uid_eq(vol->cred_uid, ses->cred_uid)) + if (!uid_eq(ctx->cred_uid, ses->cred_uid)) return 0; break; default: /* NULL username means anonymous session */ if (ses->user_name == NULL) { - if (!vol->nullauth) + if (!ctx->nullauth) return 0; break; } /* anything else takes username/password */ if (strncmp(ses->user_name, - vol->username ? vol->username : "", + ctx->username ? ctx->username : "", CIFS_MAX_USERNAME_LEN)) return 0; - if ((vol->username && strlen(vol->username) != 0) && + if ((ctx->username && strlen(ctx->username) != 0) && ses->password != NULL && strncmp(ses->password, - vol->password ? vol->password : "", + ctx->password ? ctx->password : "", CIFS_MAX_PASSWORD_LEN)) return 0; } @@ -2707,11 +1483,10 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol) * tcon_ipc. The IPC tcon has the same lifetime as the session. */ static int -cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info) +cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) { int rc = 0, xid; struct cifs_tcon *tcon; - struct nls_table *nls_codepage; char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; bool seal = false; struct TCP_Server_Info *server = ses->server; @@ -2720,7 +1495,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info) * If the mount request that resulted in the creation of the * session requires encryption, force IPC to be encrypted too. */ - if (volume_info->seal) { + if (ctx->seal) { if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) seal = true; else { @@ -2736,14 +1511,11 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info) scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname); - /* cannot fail */ - nls_codepage = load_nls_default(); - xid = get_xid(); tcon->ses = ses; tcon->ipc = true; tcon->seal = seal; - rc = server->ops->tree_connect(xid, ses, unc, tcon, nls_codepage); + rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls); free_xid(xid); if (rc) { @@ -2756,7 +1528,6 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info) ses->tcon_ipc = tcon; out: - unload_nls(nls_codepage); return rc; } @@ -2789,7 +1560,7 @@ cifs_free_ipc(struct cifs_ses *ses) } static struct cifs_ses * -cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) +cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) { struct cifs_ses *ses; @@ -2797,7 +1568,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { if (ses->status == CifsExiting) continue; - if (!match_session(ses, vol)) + if (!match_session(ses, ctx)) continue; ++ses->ses_count; spin_unlock(&cifs_tcp_ses_lock); @@ -2861,7 +1632,7 @@ void cifs_put_smb_ses(struct cifs_ses *ses) /* Populate username and pw fields from keyring if possible */ static int -cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) +cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses) { int rc = 0; int is_domain = 0; @@ -2941,32 +1712,32 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) goto out_key_put; } - vol->username = kstrndup(payload, len, GFP_KERNEL); - if (!vol->username) { + ctx->username = kstrndup(payload, len, GFP_KERNEL); + if (!ctx->username) { cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n", len); rc = -ENOMEM; goto out_key_put; } - cifs_dbg(FYI, "%s: username=%s\n", __func__, vol->username); + cifs_dbg(FYI, "%s: username=%s\n", __func__, ctx->username); len = key->datalen - (len + 1); if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) { cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len); rc = -EINVAL; - kfree(vol->username); - vol->username = NULL; + kfree(ctx->username); + ctx->username = NULL; goto out_key_put; } ++delim; - vol->password = kstrndup(delim, len, GFP_KERNEL); - if (!vol->password) { + ctx->password = kstrndup(delim, len, GFP_KERNEL); + if (!ctx->password) { cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n", len); rc = -ENOMEM; - kfree(vol->username); - vol->username = NULL; + kfree(ctx->username); + ctx->username = NULL; goto out_key_put; } @@ -2975,17 +1746,17 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) * for the request. */ if (is_domain && ses->domainName) { - vol->domainname = kstrndup(ses->domainName, + ctx->domainname = kstrndup(ses->domainName, strlen(ses->domainName), GFP_KERNEL); - if (!vol->domainname) { + if (!ctx->domainname) { cifs_dbg(FYI, "Unable to allocate %zd bytes for domain\n", len); rc = -ENOMEM; - kfree(vol->username); - vol->username = NULL; - kfree_sensitive(vol->password); - vol->password = NULL; + kfree(ctx->username); + ctx->username = NULL; + kfree_sensitive(ctx->password); + ctx->password = NULL; goto out_key_put; } } @@ -3000,7 +1771,7 @@ out_err: } #else /* ! CONFIG_KEYS */ static inline int -cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)), +cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)), struct cifs_ses *ses __attribute__((unused))) { return -ENOSYS; @@ -3008,14 +1779,14 @@ cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)), #endif /* CONFIG_KEYS */ /** - * cifs_get_smb_ses - get a session matching @volume_info data from @server + * cifs_get_smb_ses - get a session matching @ctx data from @server * * This function assumes it is being called from cifs_mount() where we * already got a server reference (server refcount +1). See * cifs_get_tcon() for refcount explanations. */ struct cifs_ses * -cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) +cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) { int rc = -ENOMEM; unsigned int xid; @@ -3025,7 +1796,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) xid = get_xid(); - ses = cifs_find_smb_ses(server, volume_info); + ses = cifs_find_smb_ses(server, ctx); if (ses) { cifs_dbg(FYI, "Existing smb sess found (status=%d)\n", ses->status); @@ -3042,7 +1813,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) if (ses->need_reconnect) { cifs_dbg(FYI, "Session needs reconnect\n"); rc = cifs_setup_session(xid, ses, - volume_info->local_nls); + ctx->local_nls); if (rc) { mutex_unlock(&ses->session_mutex); /* problem -- put our reference */ @@ -3071,40 +1842,40 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) else sprintf(ses->serverName, "%pI4", &addr->sin_addr); - if (volume_info->username) { - ses->user_name = kstrdup(volume_info->username, GFP_KERNEL); + if (ctx->username) { + ses->user_name = kstrdup(ctx->username, GFP_KERNEL); if (!ses->user_name) goto get_ses_fail; } - /* volume_info->password freed at unmount */ - if (volume_info->password) { - ses->password = kstrdup(volume_info->password, GFP_KERNEL); + /* ctx->password freed at unmount */ + if (ctx->password) { + ses->password = kstrdup(ctx->password, GFP_KERNEL); if (!ses->password) goto get_ses_fail; } - if (volume_info->domainname) { - ses->domainName = kstrdup(volume_info->domainname, GFP_KERNEL); + if (ctx->domainname) { + ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL); if (!ses->domainName) goto get_ses_fail; } - if (volume_info->domainauto) - ses->domainAuto = volume_info->domainauto; - ses->cred_uid = volume_info->cred_uid; - ses->linux_uid = volume_info->linux_uid; + if (ctx->domainauto) + ses->domainAuto = ctx->domainauto; + ses->cred_uid = ctx->cred_uid; + ses->linux_uid = ctx->linux_uid; - ses->sectype = volume_info->sectype; - ses->sign = volume_info->sign; + ses->sectype = ctx->sectype; + ses->sign = ctx->sign; mutex_lock(&ses->session_mutex); /* add server as first channel */ ses->chans[0].server = server; ses->chan_count = 1; - ses->chan_max = volume_info->multichannel ? volume_info->max_channels:1; + ses->chan_max = ctx->multichannel ? ctx->max_channels:1; rc = cifs_negotiate_protocol(xid, ses); if (!rc) - rc = cifs_setup_session(xid, ses, volume_info->local_nls); + rc = cifs_setup_session(xid, ses, ctx->local_nls); /* each channel uses a different signing key */ memcpy(ses->chans[0].signkey, ses->smb3signingkey, @@ -3121,7 +1892,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) free_xid(xid); - cifs_setup_ipc(ses, volume_info); + cifs_setup_ipc(ses, ctx); return ses; @@ -3131,27 +1902,27 @@ get_ses_fail: return ERR_PTR(rc); } -static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info) +static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) { if (tcon->tidStatus == CifsExiting) return 0; - if (strncmp(tcon->treeName, volume_info->UNC, MAX_TREE_SIZE)) + if (strncmp(tcon->treeName, ctx->UNC, MAX_TREE_SIZE)) return 0; - if (tcon->seal != volume_info->seal) + if (tcon->seal != ctx->seal) return 0; - if (tcon->snapshot_time != volume_info->snapshot_time) + if (tcon->snapshot_time != ctx->snapshot_time) return 0; - if (tcon->handle_timeout != volume_info->handle_timeout) + if (tcon->handle_timeout != ctx->handle_timeout) return 0; - if (tcon->no_lease != volume_info->no_lease) + if (tcon->no_lease != ctx->no_lease) return 0; - if (tcon->nodelete != volume_info->nodelete) + if (tcon->nodelete != ctx->nodelete) return 0; return 1; } static struct cifs_tcon * -cifs_find_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) +cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) { struct list_head *tmp; struct cifs_tcon *tcon; @@ -3163,7 +1934,7 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) if (tcon->dfs_path) continue; #endif - if (!match_tcon(tcon, volume_info)) + if (!match_tcon(tcon, ctx)) continue; ++tcon->tc_count; spin_unlock(&cifs_tcp_ses_lock); @@ -3194,6 +1965,18 @@ cifs_put_tcon(struct cifs_tcon *tcon) return; } +#ifdef CONFIG_CIFS_SWN_UPCALL + if (tcon->use_witness) { + int rc; + + rc = cifs_swn_unregister(tcon); + if (rc < 0) { + cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", + __func__, rc); + } + } +#endif + list_del_init(&tcon->tcon_list); spin_unlock(&cifs_tcp_ses_lock); @@ -3208,7 +1991,7 @@ cifs_put_tcon(struct cifs_tcon *tcon) } /** - * cifs_get_tcon - get a tcon matching @volume_info data from @ses + * cifs_get_tcon - get a tcon matching @ctx data from @ses * * - tcon refcount is the number of mount points using the tcon. * - ses refcount is the number of tcon using the session. @@ -3228,12 +2011,12 @@ cifs_put_tcon(struct cifs_tcon *tcon) * decrement the ses refcount. */ static struct cifs_tcon * -cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) +cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) { int rc, xid; struct cifs_tcon *tcon; - tcon = cifs_find_tcon(ses, volume_info); + tcon = cifs_find_tcon(ses, ctx); if (tcon) { /* * tcon has refcount already incremented but we need to @@ -3255,36 +2038,36 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) goto out_fail; } - if (volume_info->snapshot_time) { + if (ctx->snapshot_time) { if (ses->server->vals->protocol_id == 0) { cifs_dbg(VFS, "Use SMB2 or later for snapshot mount option\n"); rc = -EOPNOTSUPP; goto out_fail; } else - tcon->snapshot_time = volume_info->snapshot_time; + tcon->snapshot_time = ctx->snapshot_time; } - if (volume_info->handle_timeout) { + if (ctx->handle_timeout) { if (ses->server->vals->protocol_id == 0) { cifs_dbg(VFS, "Use SMB2.1 or later for handle timeout option\n"); rc = -EOPNOTSUPP; goto out_fail; } else - tcon->handle_timeout = volume_info->handle_timeout; + tcon->handle_timeout = ctx->handle_timeout; } tcon->ses = ses; - if (volume_info->password) { - tcon->password = kstrdup(volume_info->password, GFP_KERNEL); + if (ctx->password) { + tcon->password = kstrdup(ctx->password, GFP_KERNEL); if (!tcon->password) { rc = -ENOMEM; goto out_fail; } } - if (volume_info->seal) { + if (ctx->seal) { if (ses->server->vals->protocol_id == 0) { cifs_dbg(VFS, "SMB3 or later required for encryption\n"); @@ -3300,7 +2083,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) } } - if (volume_info->linux_ext) { + if (ctx->linux_ext) { if (ses->server->posix_ext_supported) { tcon->posix_extensions = true; pr_warn_once("SMB3.11 POSIX Extensions are experimental\n"); @@ -3316,8 +2099,8 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) * SetFS as we do on SessSetup and reconnect? */ xid = get_xid(); - rc = ses->server->ops->tree_connect(xid, ses, volume_info->UNC, tcon, - volume_info->local_nls); + rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon, + ctx->local_nls); free_xid(xid); cifs_dbg(FYI, "Tcon rc = %d\n", rc); if (rc) @@ -3325,7 +2108,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) tcon->use_persistent = false; /* check if SMB2 or later, CIFS does not support persistent handles */ - if (volume_info->persistent) { + if (ctx->persistent) { if (ses->server->vals->protocol_id == 0) { cifs_dbg(VFS, "SMB3 or later required for persistent handles\n"); @@ -3342,10 +2125,10 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) } } else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) - && (volume_info->nopersistent == false)) { + && (ctx->nopersistent == false)) { cifs_dbg(FYI, "enabling persistent handles\n"); tcon->use_persistent = true; - } else if (volume_info->resilient) { + } else if (ctx->resilient) { if (ses->server->vals->protocol_id == 0) { cifs_dbg(VFS, "SMB2.1 or later required for resilient handles\n"); @@ -3354,23 +2137,52 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) } tcon->use_resilient = true; } +#ifdef CONFIG_CIFS_SWN_UPCALL + tcon->use_witness = false; + if (ctx->witness) { + if (ses->server->vals->protocol_id >= SMB30_PROT_ID) { + if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) { + /* + * Set witness in use flag in first place + * to retry registration in the echo task + */ + tcon->use_witness = true; + /* And try to register immediately */ + rc = cifs_swn_register(tcon); + if (rc < 0) { + cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc); + goto out_fail; + } + } else { + /* TODO: try to extend for non-cluster uses (eg multichannel) */ + cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } else { + cifs_dbg(VFS, "SMB3 or later required for witness option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } +#endif /* If the user really knows what they are doing they can override */ if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) { - if (volume_info->cache_ro) + if (ctx->cache_ro) cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n"); - else if (volume_info->cache_rw) + else if (ctx->cache_rw) cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n"); } - if (volume_info->no_lease) { + if (ctx->no_lease) { if (ses->server->vals->protocol_id == 0) { cifs_dbg(VFS, "SMB2 or later required for nolease option\n"); rc = -EOPNOTSUPP; goto out_fail; } else - tcon->no_lease = volume_info->no_lease; + tcon->no_lease = ctx->no_lease; } /* @@ -3378,14 +2190,14 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) * resources mounted more than once to the same server share the last * value passed in for the retry flag is used. */ - tcon->retry = volume_info->retry; - tcon->nocase = volume_info->nocase; + tcon->retry = ctx->retry; + tcon->nocase = ctx->nocase; if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) - tcon->nohandlecache = volume_info->nohandlecache; + tcon->nohandlecache = ctx->nohandlecache; else - tcon->nohandlecache = 1; - tcon->nodelete = volume_info->nodelete; - tcon->local_lease = volume_info->local_lease; + tcon->nohandlecache = true; + tcon->nodelete = ctx->nodelete; + tcon->local_lease = ctx->local_lease; INIT_LIST_HEAD(&tcon->pending_opens); spin_lock(&cifs_tcp_ses_lock); @@ -3440,23 +2252,24 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) * We want to share sb only if we don't specify an r/wsize or * specified r/wsize is greater than or equal to existing one. */ - if (new->wsize && new->wsize < old->wsize) + if (new->ctx->wsize && new->ctx->wsize < old->ctx->wsize) return 0; - if (new->rsize && new->rsize < old->rsize) + if (new->ctx->rsize && new->ctx->rsize < old->ctx->rsize) return 0; - if (!uid_eq(old->mnt_uid, new->mnt_uid) || !gid_eq(old->mnt_gid, new->mnt_gid)) + if (!uid_eq(old->ctx->linux_uid, new->ctx->linux_uid) || + !gid_eq(old->ctx->linux_gid, new->ctx->linux_gid)) return 0; - if (old->mnt_file_mode != new->mnt_file_mode || - old->mnt_dir_mode != new->mnt_dir_mode) + if (old->ctx->file_mode != new->ctx->file_mode || + old->ctx->dir_mode != new->ctx->dir_mode) return 0; if (strcmp(old->local_nls->charset, new->local_nls->charset)) return 0; - if (old->actimeo != new->actimeo) + if (old->ctx->actimeo != new->ctx->actimeo) return 0; return 1; @@ -3484,7 +2297,7 @@ int cifs_match_super(struct super_block *sb, void *data) { struct cifs_mnt_data *mnt_data = (struct cifs_mnt_data *)data; - struct smb_vol *volume_info; + struct smb3_fs_context *ctx; struct cifs_sb_info *cifs_sb; struct TCP_Server_Info *tcp_srv; struct cifs_ses *ses; @@ -3503,11 +2316,11 @@ cifs_match_super(struct super_block *sb, void *data) ses = tcon->ses; tcp_srv = ses->server; - volume_info = mnt_data->vol; + ctx = mnt_data->ctx; - if (!match_server(tcp_srv, volume_info) || - !match_session(ses, volume_info) || - !match_tcon(tcon, volume_info) || + if (!match_server(tcp_srv, ctx) || + !match_session(ses, ctx) || + !match_tcon(tcon, ctx) || !match_prepath(sb, mnt_data)) { rc = 0; goto out; @@ -3792,9 +2605,10 @@ ip_connect(struct TCP_Server_Info *server) } void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct smb_vol *vol_info) + struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) { - /* if we are reconnecting then should we check to see if + /* + * If we are reconnecting then should we check to see if * any requested capabilities changed locally e.g. via * remount but we can not do much about it here * if they have (even if we could detect it by the following) @@ -3802,18 +2616,19 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, * or if we change to make all sb to same share the same * sb as NFS - then we only have one backpointer to sb. * What if we wanted to mount the server share twice once with - * and once without posixacls or posix paths? */ + * and once without posixacls or posix paths? + */ __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - if (vol_info && vol_info->no_linux_ext) { + if (ctx && ctx->no_linux_ext) { tcon->fsUnixInfo.Capability = 0; tcon->unix_ext = 0; /* Unix Extensions disabled */ cifs_dbg(FYI, "Linux protocol extensions disabled\n"); return; - } else if (vol_info) + } else if (ctx) tcon->unix_ext = 1; /* Unix Extensions supported */ - if (tcon->unix_ext == 0) { + if (!tcon->unix_ext) { cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n"); return; } @@ -3821,11 +2636,15 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, if (!CIFSSMBQFSUnixInfo(xid, tcon)) { __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); - /* check for reconnect case in which we do not - want to change the mount behavior if we can avoid it */ - if (vol_info == NULL) { - /* turn off POSIX ACL and PATHNAMES if not set - originally at mount time */ + /* + * check for reconnect case in which we do not + * want to change the mount behavior if we can avoid it + */ + if (ctx == NULL) { + /* + * turn off POSIX ACL and PATHNAMES if not set + * originally at mount time + */ if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) cap &= ~CIFS_UNIX_POSIX_ACL_CAP; if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { @@ -3842,7 +2661,7 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, cifs_dbg(VFS, "per-share encryption not supported yet\n"); cap &= CIFS_UNIX_CAP_MASK; - if (vol_info && vol_info->no_psx_acl) + if (ctx && ctx->no_psx_acl) cap &= ~CIFS_UNIX_POSIX_ACL_CAP; else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { cifs_dbg(FYI, "negotiated posix acl support\n"); @@ -3851,7 +2670,7 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, CIFS_MOUNT_POSIXACL; } - if (vol_info && vol_info->posix_paths == 0) + if (ctx && ctx->posix_paths == 0) cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { cifs_dbg(FYI, "negotiate posix pathnames\n"); @@ -3882,129 +2701,59 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, cifs_dbg(FYI, "mandatory transport encryption cap\n"); #endif /* CIFS_DEBUG2 */ if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { - if (vol_info == NULL) { + if (ctx == NULL) cifs_dbg(FYI, "resetting capabilities failed\n"); - } else + else cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n"); } } } -int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, - struct cifs_sb_info *cifs_sb) +int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) { + struct smb3_fs_context *ctx = cifs_sb->ctx; + INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); spin_lock_init(&cifs_sb->tlink_tree_lock); cifs_sb->tlink_tree = RB_ROOT; - cifs_sb->bsize = pvolume_info->bsize; - /* - * Temporarily set r/wsize for matching superblock. If we end up using - * new sb then client will later negotiate it downward if needed. - */ - cifs_sb->rsize = pvolume_info->rsize; - cifs_sb->wsize = pvolume_info->wsize; - - cifs_sb->mnt_uid = pvolume_info->linux_uid; - cifs_sb->mnt_gid = pvolume_info->linux_gid; - cifs_sb->mnt_file_mode = pvolume_info->file_mode; - cifs_sb->mnt_dir_mode = pvolume_info->dir_mode; cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n", - cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode); - - cifs_sb->actimeo = pvolume_info->actimeo; - cifs_sb->local_nls = pvolume_info->local_nls; - - if (pvolume_info->nodfs) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_DFS; - if (pvolume_info->noperm) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; - if (pvolume_info->setuids) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; - if (pvolume_info->setuidfromacl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL; - if (pvolume_info->server_ino) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; - if (pvolume_info->remap) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SFM_CHR; - if (pvolume_info->sfu_remap) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; - if (pvolume_info->no_xattr) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; - if (pvolume_info->sfu_emul) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; - if (pvolume_info->nobrl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; - if (pvolume_info->nohandlecache) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_HANDLE_CACHE; - if (pvolume_info->nostrictsync) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOSSYNC; - if (pvolume_info->mand_lock) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL; - if (pvolume_info->rwpidforward) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD; - if (pvolume_info->mode_ace) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MODE_FROM_SID; - if (pvolume_info->cifs_acl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; - if (pvolume_info->backupuid_specified) { - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID; - cifs_sb->mnt_backupuid = pvolume_info->backupuid; - } - if (pvolume_info->backupgid_specified) { - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID; - cifs_sb->mnt_backupgid = pvolume_info->backupgid; - } - if (pvolume_info->override_uid) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; - if (pvolume_info->override_gid) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; - if (pvolume_info->dynperm) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; - if (pvolume_info->fsc) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; - if (pvolume_info->multiuser) - cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | - CIFS_MOUNT_NO_PERM); - if (pvolume_info->strict_io) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO; - if (pvolume_info->direct_io) { - cifs_dbg(FYI, "mounting share using direct i/o\n"); - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; + ctx->file_mode, ctx->dir_mode); + + /* this is needed for ASCII cp to Unicode converts */ + if (ctx->iocharset == NULL) { + /* load_nls_default cannot return null */ + cifs_sb->local_nls = load_nls_default(); + } else { + cifs_sb->local_nls = load_nls(ctx->iocharset); + if (cifs_sb->local_nls == NULL) { + cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n", + ctx->iocharset); + return -ELIBACC; + } } - if (pvolume_info->cache_ro) { + ctx->local_nls = cifs_sb->local_nls; + + smb3_update_mnt_flags(cifs_sb); + + if (ctx->direct_io) + cifs_dbg(FYI, "mounting share using direct i/o\n"); + if (ctx->cache_ro) { cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE; - } else if (pvolume_info->cache_rw) { + } else if (ctx->cache_rw) { cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n"); cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE | CIFS_MOUNT_RW_CACHE); } - if (pvolume_info->mfsymlinks) { - if (pvolume_info->sfu_emul) { - /* - * Our SFU ("Services for Unix" emulation does not allow - * creating symlinks but does allow reading existing SFU - * symlinks (it does allow both creating and reading SFU - * style mknod and FIFOs though). When "mfsymlinks" and - * "sfu" are both enabled at the same time, it allows - * reading both types of symlinks, but will only create - * them with mfsymlinks format. This allows better - * Apple compatibility (probably better for Samba too) - * while still recognizing old Windows style symlinks. - */ - cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n"); - } - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS; - } - if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) + if ((ctx->cifs_acl) && (ctx->dynperm)) cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n"); - if (pvolume_info->prepath) { - cifs_sb->prepath = kstrdup(pvolume_info->prepath, GFP_KERNEL); + if (ctx->prepath) { + cifs_sb->prepath = kstrdup(ctx->prepath, GFP_KERNEL); if (cifs_sb->prepath == NULL) return -ENOMEM; } @@ -4012,26 +2761,6 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, return 0; } -void -cifs_cleanup_volume_info_contents(struct smb_vol *volume_info) -{ - kfree(volume_info->username); - kfree_sensitive(volume_info->password); - kfree(volume_info->UNC); - kfree(volume_info->domainname); - kfree(volume_info->iocharset); - kfree(volume_info->prepath); -} - -void -cifs_cleanup_volume_info(struct smb_vol *volume_info) -{ - if (!volume_info) - return; - cifs_cleanup_volume_info_contents(volume_info); - kfree(volume_info); -} - /* Release all succeed connections */ static inline void mount_put_conns(struct cifs_sb_info *cifs_sb, unsigned int xid, @@ -4051,7 +2780,7 @@ static inline void mount_put_conns(struct cifs_sb_info *cifs_sb, } /* Get connections for tcp, ses and tcon */ -static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, +static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, unsigned int *xid, struct TCP_Server_Info **nserver, struct cifs_ses **nses, struct cifs_tcon **ntcon) @@ -4068,7 +2797,7 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, *xid = get_xid(); /* get a reference to a tcp session */ - server = cifs_get_tcp_session(vol); + server = cifs_get_tcp_session(ctx); if (IS_ERR(server)) { rc = PTR_ERR(server); return rc; @@ -4076,13 +2805,13 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, *nserver = server; - if ((vol->max_credits < 20) || (vol->max_credits > 60000)) + if ((ctx->max_credits < 20) || (ctx->max_credits > 60000)) server->max_credits = SMB2_MAX_CREDITS_AVAILABLE; else - server->max_credits = vol->max_credits; + server->max_credits = ctx->max_credits; /* get a reference to a SMB session */ - ses = cifs_get_smb_ses(server, vol); + ses = cifs_get_smb_ses(server, ctx); if (IS_ERR(ses)) { rc = PTR_ERR(ses); return rc; @@ -4090,14 +2819,14 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, *nses = ses; - if ((vol->persistent == true) && (!(ses->server->capabilities & + if ((ctx->persistent == true) && (!(ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { cifs_server_dbg(VFS, "persistent handles not supported by server\n"); return -EOPNOTSUPP; } /* search for existing tcon to this server share */ - tcon = cifs_get_tcon(ses, vol); + tcon = cifs_get_tcon(ses, ctx); if (IS_ERR(tcon)) { rc = PTR_ERR(tcon); return rc; @@ -4115,7 +2844,7 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, * reset of caps checks mount to see if unix extensions disabled * for just this mount. */ - reset_cifs_unix_caps(*xid, tcon, cifs_sb, vol); + reset_cifs_unix_caps(*xid, tcon, cifs_sb, ctx); if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && (le64_to_cpu(tcon->fsUnixInfo.Capability) & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) @@ -4137,8 +2866,17 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, } } - cifs_sb->wsize = server->ops->negotiate_wsize(tcon, vol); - cifs_sb->rsize = server->ops->negotiate_rsize(tcon, vol); + /* + * Clamp the rsize/wsize mount arguments if they are too big for the server + * and set the rsize/wsize to the negotiated values if not passed in by + * the user on mount + */ + if ((cifs_sb->ctx->wsize == 0) || + (cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) + cifs_sb->ctx->wsize = server->ops->negotiate_wsize(tcon, ctx); + if ((cifs_sb->ctx->rsize == 0) || + (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx))) + cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx); return 0; } @@ -4175,13 +2913,13 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, * exiting connection (tcon) */ static char * -build_unc_path_to_root(const struct smb_vol *vol, +build_unc_path_to_root(const struct smb3_fs_context *ctx, const struct cifs_sb_info *cifs_sb, bool useppath) { char *full_path, *pos; - unsigned int pplen = useppath && vol->prepath ? - strlen(vol->prepath) + 1 : 0; - unsigned int unc_len = strnlen(vol->UNC, MAX_TREE_SIZE + 1); + unsigned int pplen = useppath && ctx->prepath ? + strlen(ctx->prepath) + 1 : 0; + unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); if (unc_len > MAX_TREE_SIZE) return ERR_PTR(-EINVAL); @@ -4190,12 +2928,12 @@ build_unc_path_to_root(const struct smb_vol *vol, if (full_path == NULL) return ERR_PTR(-ENOMEM); - memcpy(full_path, vol->UNC, unc_len); + memcpy(full_path, ctx->UNC, unc_len); pos = full_path + unc_len; if (pplen) { *pos = CIFS_DIR_SEP(cifs_sb); - memcpy(pos + 1, vol->prepath, pplen); + memcpy(pos + 1, ctx->prepath, pplen); pos += pplen; } @@ -4208,8 +2946,7 @@ build_unc_path_to_root(const struct smb_vol *vol, /** * expand_dfs_referral - Perform a dfs referral query and update the cifs_sb * - * - * If a referral is found, cifs_sb->mountdata will be (re-)allocated + * If a referral is found, cifs_sb->ctx->mount_options will be (re-)allocated * to a string containing updated options for the submount. Otherwise it * will be left untouched. * @@ -4218,7 +2955,7 @@ build_unc_path_to_root(const struct smb_vol *vol, */ static int expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, - struct smb_vol *volume_info, struct cifs_sb_info *cifs_sb, + struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, char *ref_path) { int rc; @@ -4228,7 +2965,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) return -EREMOTE; - full_path = build_unc_path_to_root(volume_info, cifs_sb, true); + full_path = build_unc_path_to_root(ctx, cifs_sb, true); if (IS_ERR(full_path)) return PTR_ERR(full_path); @@ -4237,7 +2974,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, if (!rc) { char *fake_devname = NULL; - mdata = cifs_compose_mount_options(cifs_sb->mountdata, + mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, &referral, &fake_devname); free_dfs_info_param(&referral); @@ -4246,13 +2983,11 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, rc = PTR_ERR(mdata); mdata = NULL; } else { - cifs_cleanup_volume_info_contents(volume_info); - rc = cifs_setup_volume_info(volume_info, mdata, - fake_devname, false); + rc = cifs_setup_volume_info(ctx, mdata, fake_devname); } kfree(fake_devname); - kfree(cifs_sb->mountdata); - cifs_sb->mountdata = mdata; + kfree(cifs_sb->ctx->mount_options); + cifs_sb->ctx->mount_options = mdata; } kfree(full_path); return rc; @@ -4270,7 +3005,7 @@ static inline int get_next_dfs_tgt(const char *path, } static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it, - struct smb_vol *fake_vol, struct smb_vol *vol) + struct smb3_fs_context *fake_ctx, struct smb3_fs_context *ctx) { const char *tgt = dfs_cache_get_tgt_name(tgt_it); int len = strlen(tgt) + 2; @@ -4281,29 +3016,30 @@ static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it, return -ENOMEM; scnprintf(new_unc, len, "\\%s", tgt); - kfree(vol->UNC); - vol->UNC = new_unc; + kfree(ctx->UNC); + ctx->UNC = new_unc; - if (fake_vol->prepath) { - kfree(vol->prepath); - vol->prepath = fake_vol->prepath; - fake_vol->prepath = NULL; + if (fake_ctx->prepath) { + kfree(ctx->prepath); + ctx->prepath = fake_ctx->prepath; + fake_ctx->prepath = NULL; } - memcpy(&vol->dstaddr, &fake_vol->dstaddr, sizeof(vol->dstaddr)); + memcpy(&ctx->dstaddr, &fake_ctx->dstaddr, sizeof(ctx->dstaddr)); return 0; } static int setup_dfs_tgt_conn(const char *path, const char *full_path, const struct dfs_cache_tgt_iterator *tgt_it, - struct cifs_sb_info *cifs_sb, struct smb_vol *vol, unsigned int *xid, - struct TCP_Server_Info **server, struct cifs_ses **ses, - struct cifs_tcon **tcon) + struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx, + unsigned int *xid, struct TCP_Server_Info **server, + struct cifs_ses **ses, struct cifs_tcon **tcon) { int rc; struct dfs_info3_param ref = {0}; - char *mdata = NULL, *fake_devname = NULL; - struct smb_vol fake_vol = {NULL}; + char *mdata = NULL; + struct smb3_fs_context fake_ctx = {NULL}; + char *fake_devname = NULL; cifs_dbg(FYI, "%s: dfs path: %s\n", __func__, path); @@ -4311,45 +3047,45 @@ static int setup_dfs_tgt_conn(const char *path, const char *full_path, if (rc) return rc; - mdata = cifs_compose_mount_options(cifs_sb->mountdata, full_path + 1, &ref, &fake_devname); + mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, + full_path + 1, &ref, + &fake_devname); free_dfs_info_param(&ref); if (IS_ERR(mdata)) { rc = PTR_ERR(mdata); mdata = NULL; - } else { - cifs_dbg(FYI, "%s: fake_devname: %s\n", __func__, fake_devname); - rc = cifs_setup_volume_info(&fake_vol, mdata, fake_devname, - false); - } + } else + rc = cifs_setup_volume_info(&fake_ctx, mdata, fake_devname); + kfree(mdata); kfree(fake_devname); if (!rc) { /* - * We use a 'fake_vol' here because we need pass it down to the + * We use a 'fake_ctx' here because we need pass it down to the * mount_{get,put} functions to test connection against new DFS * targets. */ mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon); - rc = mount_get_conns(&fake_vol, cifs_sb, xid, server, ses, + rc = mount_get_conns(&fake_ctx, cifs_sb, xid, server, ses, tcon); if (!rc || (*server && *ses)) { /* * We were able to connect to new target server. - * Update current volume info with new target server. + * Update current context with new target server. */ - rc = update_vol_info(tgt_it, &fake_vol, vol); + rc = update_vol_info(tgt_it, &fake_ctx, ctx); } } - cifs_cleanup_volume_info_contents(&fake_vol); + smb3_cleanup_fs_context_contents(&fake_ctx); return rc; } static int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb, - struct smb_vol *vol, struct cifs_ses *root_ses, unsigned int *xid, - struct TCP_Server_Info **server, struct cifs_ses **ses, - struct cifs_tcon **tcon) + struct smb3_fs_context *ctx, struct cifs_ses *root_ses, + unsigned int *xid, struct TCP_Server_Info **server, + struct cifs_ses **ses, struct cifs_tcon **tcon) { int rc; struct dfs_cache_tgt_list tgt_list; @@ -4368,7 +3104,7 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_ if (rc) break; /* Connect to next DFS target */ - rc = setup_dfs_tgt_conn(path, full_path, tgt_it, cifs_sb, vol, xid, server, ses, + rc = setup_dfs_tgt_conn(path, full_path, tgt_it, cifs_sb, ctx, xid, server, ses, tcon); if (!rc || (*server && *ses)) break; @@ -4388,22 +3124,35 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_ } #endif +/* TODO: all callers to this are broken. We are not parsing mount_options here + * we should pass a clone of the original context? + */ int -cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, - const char *devname, bool is_smb3) +cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname) { int rc = 0; - if (cifs_parse_mount_options(mount_data, devname, volume_info, is_smb3)) - return -EINVAL; + smb3_parse_devname(devname, ctx); + + if (mntopts) { + char *ip; - if (volume_info->nullauth) { + cifs_dbg(FYI, "%s: mntopts=%s\n", __func__, mntopts); + rc = smb3_parse_opt(mntopts, "ip", &ip); + if (!rc && !cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, + strlen(ip))) { + cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__); + return -EINVAL; + } + } + + if (ctx->nullauth) { cifs_dbg(FYI, "Anonymous login\n"); - kfree(volume_info->username); - volume_info->username = NULL; - } else if (volume_info->username) { + kfree(ctx->username); + ctx->username = NULL; + } else if (ctx->username) { /* BB fixme parse for domain name here */ - cifs_dbg(FYI, "Username: %s\n", volume_info->username); + cifs_dbg(FYI, "Username: %s\n", ctx->username); } else { cifs_dbg(VFS, "No username specified\n"); /* In userspace mount helper we can get user name from alternate @@ -4411,41 +3160,9 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, return -EINVAL; } - /* this is needed for ASCII cp to Unicode converts */ - if (volume_info->iocharset == NULL) { - /* load_nls_default cannot return null */ - volume_info->local_nls = load_nls_default(); - } else { - volume_info->local_nls = load_nls(volume_info->iocharset); - if (volume_info->local_nls == NULL) { - cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n", - volume_info->iocharset); - return -ELIBACC; - } - } - return rc; } -struct smb_vol * -cifs_get_volume_info(char *mount_data, const char *devname, bool is_smb3) -{ - int rc; - struct smb_vol *volume_info; - - volume_info = kmalloc(sizeof(struct smb_vol), GFP_KERNEL); - if (!volume_info) - return ERR_PTR(-ENOMEM); - - rc = cifs_setup_volume_info(volume_info, mount_data, devname, is_smb3); - if (rc) { - cifs_cleanup_volume_info(volume_info); - volume_info = ERR_PTR(rc); - } - - return volume_info; -} - static int cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, unsigned int xid, @@ -4497,7 +3214,7 @@ 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. */ -static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, +static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx, const unsigned int xid, struct TCP_Server_Info *server, struct cifs_tcon *tcon) @@ -4511,7 +3228,7 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, /* * cifs_build_path_to_root works only when we have a valid tcon */ - full_path = cifs_build_path_to_root(vol, cifs_sb, tcon, + full_path = cifs_build_path_to_root(ctx, cifs_sb, tcon, tcon->Flags & SMB_SHARE_IS_IN_DFS); if (full_path == NULL) return -ENOMEM; @@ -4560,7 +3277,7 @@ static void put_root_ses(struct cifs_ses *ses) } /* Check if a path component is remote and then update @dfs_path accordingly */ -static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, +static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx, const unsigned int xid, struct TCP_Server_Info *server, struct cifs_tcon *tcon, char **dfs_path) { @@ -4571,7 +3288,7 @@ static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, int added_treename = tcon->Flags & SMB_SHARE_IS_IN_DFS; int skip = added_treename; - path = cifs_build_path_to_root(vol, cifs_sb, tcon, added_treename); + path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename); if (!path) return -ENOMEM; @@ -4602,17 +3319,17 @@ static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, *s = 0; rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, path); if (rc && rc == -EREMOTE) { - struct smb_vol v = {NULL}; + struct smb3_fs_context v = {NULL}; /* if @path contains a tree name, skip it in the prefix path */ if (added_treename) { - rc = cifs_parse_devname(path, &v); + rc = smb3_parse_devname(path, &v); if (rc) break; rc = -EREMOTE; npath = build_unc_path_to_root(&v, cifs_sb, true); - cifs_cleanup_volume_info_contents(&v); + smb3_cleanup_fs_context_contents(&v); } else { - v.UNC = vol->UNC; + v.UNC = ctx->UNC; v.prepath = path + 1; npath = build_unc_path_to_root(&v, cifs_sb, true); } @@ -4630,7 +3347,7 @@ static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, return rc; } -int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) +int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) { int rc = 0; unsigned int xid; @@ -4642,7 +3359,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) char *oldmnt = NULL; char *mntdata = NULL; - rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); + rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); /* * Unconditionally try to get an DFS referral (even cached) to determine whether it is an * DFS mount. @@ -4650,26 +3367,27 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem * to respond with PATH_NOT_COVERED to requests that include the prefix. */ - if (dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), vol->UNC + 1, NULL, + if (dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ctx->UNC + 1, NULL, NULL)) { /* No DFS referral was returned. Looks like a regular share. */ if (rc) goto error; /* Check if it is fully accessible and then mount it */ - rc = is_path_remote(cifs_sb, vol, xid, server, tcon); + rc = is_path_remote(cifs_sb, ctx, xid, server, tcon); if (!rc) goto out; if (rc != -EREMOTE) goto error; } /* Save mount options */ - mntdata = kstrndup(cifs_sb->mountdata, strlen(cifs_sb->mountdata), GFP_KERNEL); + mntdata = kstrndup(cifs_sb->ctx->mount_options, + strlen(cifs_sb->ctx->mount_options), GFP_KERNEL); if (!mntdata) { rc = -ENOMEM; goto error; } /* Get path of DFS root */ - ref_path = build_unc_path_to_root(vol, cifs_sb, false); + ref_path = build_unc_path_to_root(ctx, cifs_sb, false); if (IS_ERR(ref_path)) { rc = PTR_ERR(ref_path); ref_path = NULL; @@ -4680,25 +3398,25 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) do { /* Save full path of last DFS path we used to resolve final target server */ kfree(full_path); - full_path = build_unc_path_to_root(vol, cifs_sb, !!count); + full_path = build_unc_path_to_root(ctx, cifs_sb, !!count); if (IS_ERR(full_path)) { rc = PTR_ERR(full_path); full_path = NULL; break; } /* Chase referral */ - oldmnt = cifs_sb->mountdata; - rc = expand_dfs_referral(xid, root_ses, vol, cifs_sb, ref_path + 1); + oldmnt = cifs_sb->ctx->mount_options; + rc = expand_dfs_referral(xid, root_ses, ctx, cifs_sb, ref_path + 1); if (rc) break; /* Connect to new DFS target only if we were redirected */ - if (oldmnt != cifs_sb->mountdata) { + if (oldmnt != cifs_sb->ctx->mount_options) { mount_put_conns(cifs_sb, xid, server, ses, tcon); - rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); + rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); } if (rc && !server && !ses) { /* Failed to connect. Try to connect to other targets in the referral. */ - rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, vol, root_ses, &xid, + rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, ctx, root_ses, &xid, &server, &ses, &tcon); } if (rc == -EACCES || rc == -EOPNOTSUPP || !server || !ses) @@ -4711,7 +3429,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) set_root_ses(cifs_sb, ses, &root_ses); } /* Check for remaining path components and then continue chasing them (-EREMOTE) */ - rc = check_dfs_prepath(cifs_sb, vol, xid, server, tcon, &ref_path); + rc = check_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path); /* Prevent recursion on broken link referrals */ if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS) rc = -ELOOP; @@ -4742,8 +3460,8 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) tcon->remap = cifs_remap(cifs_sb); spin_unlock(&cifs_tcp_ses_lock); - /* Add original volume information for DFS cache to be used when refreshing referrals */ - rc = dfs_cache_add_vol(mntdata, vol, cifs_sb->origin_fullpath); + /* Add original context for DFS cache to be used when refreshing referrals */ + rc = dfs_cache_add_vol(mntdata, ctx, cifs_sb->origin_fullpath); if (rc) goto error; /* @@ -4758,12 +3476,12 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) */ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; kfree(cifs_sb->prepath); - cifs_sb->prepath = vol->prepath; - vol->prepath = NULL; + cifs_sb->prepath = ctx->prepath; + ctx->prepath = NULL; out: free_xid(xid); - cifs_try_adding_channels(ses); + cifs_try_adding_channels(cifs_sb, ses); return mount_setup_tlink(cifs_sb, ses, tcon); error: @@ -4776,7 +3494,7 @@ error: return rc; } #else -int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) +int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) { int rc = 0; unsigned int xid; @@ -4784,12 +3502,12 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) struct cifs_tcon *tcon; struct TCP_Server_Info *server; - rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); + rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); if (rc) goto error; if (tcon) { - rc = is_path_remote(cifs_sb, vol, xid, server, tcon); + rc = is_path_remote(cifs_sb, ctx, xid, server, tcon); if (rc == -EREMOTE) rc = -EOPNOTSUPP; if (rc) @@ -4970,9 +3688,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, static void delayed_free(struct rcu_head *p) { - struct cifs_sb_info *sbi = container_of(p, struct cifs_sb_info, rcu); - unload_nls(sbi->local_nls); - kfree(sbi); + struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, rcu); + + unload_nls(cifs_sb->local_nls); + smb3_cleanup_fs_context(cifs_sb->ctx); + kfree(cifs_sb); } void @@ -4997,7 +3717,6 @@ cifs_umount(struct cifs_sb_info *cifs_sb) } spin_unlock(&cifs_sb->tlink_tree_lock); - kfree(cifs_sb->mountdata); kfree(cifs_sb->prepath); #ifdef CONFIG_CIFS_DFS_UPCALL dfs_cache_del_vol(cifs_sb->origin_fullpath); @@ -5041,7 +3760,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, if (!ses->binding) { ses->capabilities = server->capabilities; - if (linuxExtEnabled == 0) + if (!linuxExtEnabled) ses->capabilities &= (~server->vals->cap_unix); if (ses->auth_key.response) { @@ -5066,15 +3785,15 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, } static int -cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) +cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) { - vol->sectype = ses->sectype; + ctx->sectype = ses->sectype; /* krb5 is special, since we don't need username or pw */ - if (vol->sectype == Kerberos) + if (ctx->sectype == Kerberos) return 0; - return cifs_set_cifscreds(vol, ses); + return cifs_set_cifscreds(ctx, ses); } static struct cifs_tcon * @@ -5084,31 +3803,34 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_ses *ses; struct cifs_tcon *tcon = NULL; - struct smb_vol *vol_info; + struct smb3_fs_context *ctx; - vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); - if (vol_info == NULL) + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (ctx == NULL) return ERR_PTR(-ENOMEM); - vol_info->local_nls = cifs_sb->local_nls; - vol_info->linux_uid = fsuid; - vol_info->cred_uid = fsuid; - vol_info->UNC = master_tcon->treeName; - vol_info->retry = master_tcon->retry; - vol_info->nocase = master_tcon->nocase; - vol_info->nohandlecache = master_tcon->nohandlecache; - vol_info->local_lease = master_tcon->local_lease; - vol_info->no_lease = master_tcon->no_lease; - vol_info->resilient = master_tcon->use_resilient; - vol_info->persistent = master_tcon->use_persistent; - vol_info->handle_timeout = master_tcon->handle_timeout; - vol_info->no_linux_ext = !master_tcon->unix_ext; - vol_info->linux_ext = master_tcon->posix_extensions; - vol_info->sectype = master_tcon->ses->sectype; - vol_info->sign = master_tcon->ses->sign; - vol_info->seal = master_tcon->seal; - - rc = cifs_set_vol_auth(vol_info, master_tcon->ses); + ctx->local_nls = cifs_sb->local_nls; + ctx->linux_uid = fsuid; + ctx->cred_uid = fsuid; + ctx->UNC = master_tcon->treeName; + ctx->retry = master_tcon->retry; + ctx->nocase = master_tcon->nocase; + ctx->nohandlecache = master_tcon->nohandlecache; + ctx->local_lease = master_tcon->local_lease; + ctx->no_lease = master_tcon->no_lease; + ctx->resilient = master_tcon->use_resilient; + ctx->persistent = master_tcon->use_persistent; + ctx->handle_timeout = master_tcon->handle_timeout; + ctx->no_linux_ext = !master_tcon->unix_ext; + ctx->linux_ext = master_tcon->posix_extensions; + ctx->sectype = master_tcon->ses->sectype; + ctx->sign = master_tcon->ses->sign; + ctx->seal = master_tcon->seal; +#ifdef CONFIG_CIFS_SWN_UPCALL + ctx->witness = master_tcon->use_witness; +#endif + + rc = cifs_set_vol_auth(ctx, master_tcon->ses); if (rc) { tcon = ERR_PTR(rc); goto out; @@ -5119,26 +3841,26 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) ++master_tcon->ses->server->srv_count; spin_unlock(&cifs_tcp_ses_lock); - ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); + ses = cifs_get_smb_ses(master_tcon->ses->server, ctx); if (IS_ERR(ses)) { tcon = (struct cifs_tcon *)ses; cifs_put_tcp_session(master_tcon->ses->server, 0); goto out; } - tcon = cifs_get_tcon(ses, vol_info); + tcon = cifs_get_tcon(ses, ctx); if (IS_ERR(tcon)) { cifs_put_smb_ses(ses); goto out; } if (cap_unix(ses)) - reset_cifs_unix_caps(0, tcon, NULL, vol_info); + reset_cifs_unix_caps(0, tcon, NULL, ctx); out: - kfree(vol_info->username); - kfree_sensitive(vol_info->password); - kfree(vol_info); + kfree(ctx->username); + kfree_sensitive(ctx->password); + kfree(ctx); return tcon; } |