diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/AUTHORS | 2 | ||||
-rw-r--r-- | fs/cifs/CHANGES | 15 | ||||
-rw-r--r-- | fs/cifs/README | 12 | ||||
-rw-r--r-- | fs/cifs/cifs_debug.c | 277 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 111 | ||||
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 7 | ||||
-rw-r--r-- | fs/cifs/cifs_spnego.c | 10 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 30 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 105 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 64 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 184 | ||||
-rw-r--r-- | fs/cifs/connect.c | 1382 | ||||
-rw-r--r-- | fs/cifs/dir.c | 21 | ||||
-rw-r--r-- | fs/cifs/file.c | 130 | ||||
-rw-r--r-- | fs/cifs/inode.c | 66 | ||||
-rw-r--r-- | fs/cifs/ioctl.c | 2 | ||||
-rw-r--r-- | fs/cifs/misc.c | 106 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 5 | ||||
-rw-r--r-- | fs/cifs/sess.c | 5 | ||||
-rw-r--r-- | fs/cifs/smbdes.c | 5 | ||||
-rw-r--r-- | fs/cifs/smbencrypt.c | 9 | ||||
-rw-r--r-- | fs/cifs/transport.c | 378 |
26 files changed, 1629 insertions, 1309 deletions
diff --git a/fs/cifs/AUTHORS b/fs/cifs/AUTHORS index 9c136d7803d9..7f7fa3c302af 100644 --- a/fs/cifs/AUTHORS +++ b/fs/cifs/AUTHORS @@ -36,7 +36,9 @@ Miklos Szeredi Kazeon team for various fixes especially for 2.4 version. Asser Ferno (Change Notify support) Shaggy (Dave Kleikamp) for inumerable small fs suggestions and some good cleanup +Gunter Kukkukk (testing and suggestions for support of old servers) Igor Mammedov (DFS support) +Jeff Layton (many, many fixes, as well as great work on the cifs Kerberos code) Test case and Bug Report contributors ------------------------------------- diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 8855331b2fba..080703a15f44 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,12 @@ +Version 1.56 +------------ +Add "forcemandatorylock" mount option to allow user to use mandatory +rather than posix (advisory) byte range locks, even though server would +support posix byte range locks. Fix query of root inode when prefixpath +specified and user does not have access to query information about the +top of the share. Fix problem in 2.6.28 resolving DFS paths to +Samba servers (worked to Windows). + Version 1.55 ------------ Various fixes to make delete of open files behavior more predictable @@ -8,7 +17,11 @@ handling fcntl(F_SETLEASE). Convert cifs to using blocking tcp sends, and also let tcp autotune the socket send and receive buffers. This reduces the number of EAGAIN errors returned by TCP/IP in high stress workloads (and the number of retries on socket writes -when sending large SMBWriteX requests). +when sending large SMBWriteX requests). Fix case in which a portion of +data can in some cases not get written to the file on the server before the +file is closed. Fix DFS parsing to properly handle path consumed field, +and to handle certain codepage conversions better. Fix mount and +umount race that can cause oops in mount or umount or reconnect. Version 1.54 ------------ diff --git a/fs/cifs/README b/fs/cifs/README index a439dc1739b3..da4515e3be20 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -463,9 +463,19 @@ A partial list of the supported mount options follows: with cifs style mandatory byte range locks (and most cifs servers do not yet support requesting advisory byte range locks). + forcemandatorylock Even if the server supports posix (advisory) byte range + locking, send only mandatory lock requests. For some + (presumably rare) applications, originally coded for + DOS/Windows, which require Windows style mandatory byte range + locking, they may be able to take advantage of this option, + forcing the cifs client to only send mandatory locks + even if the cifs server would support posix advisory locks. + "forcemand" is accepted as a shorter form of this mount + option. nodfs Disable DFS (global name space support) even if the server claims to support it. This can help work around - a problem with parsing of DFS paths with Samba 3.0.24 server. + a problem with parsing of DFS paths with Samba server + versions 3.0.24 and 3.0.25. remount remount the share (often used to change from ro to rw mounts or vice versa) cifsacl Report mode bits (e.g. on stat) based on the Windows ACL for diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 69a12aae91d3..490e34bbf27a 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -107,12 +107,13 @@ void cifs_dump_mids(struct TCP_Server_Info *server) #ifdef CONFIG_PROC_FS static int cifs_debug_data_proc_show(struct seq_file *m, void *v) { - struct list_head *tmp; - struct list_head *tmp1; + struct list_head *tmp1, *tmp2, *tmp3; struct mid_q_entry *mid_entry; + struct TCP_Server_Info *server; struct cifsSesInfo *ses; struct cifsTconInfo *tcon; - int i; + int i, j; + __u32 dev_type; seq_puts(m, "Display Internal CIFS Data Structures for Debugging\n" @@ -122,46 +123,78 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) seq_printf(m, "Servers:"); i = 0; - read_lock(&GlobalSMBSeslock); - list_for_each(tmp, &GlobalSMBSessionList) { + read_lock(&cifs_tcp_ses_lock); + list_for_each(tmp1, &cifs_tcp_ses_list) { + server = list_entry(tmp1, struct TCP_Server_Info, + tcp_ses_list); i++; - ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); - if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || - (ses->serverNOS == NULL)) { - seq_printf(m, "\nentry for %s not fully " - "displayed\n\t", ses->serverName); - } else { - seq_printf(m, - "\n%d) Name: %s Domain: %s Mounts: %d OS:" - " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB" + list_for_each(tmp2, &server->smb_ses_list) { + ses = list_entry(tmp2, struct cifsSesInfo, + smb_ses_list); + if ((ses->serverDomain == NULL) || + (ses->serverOS == NULL) || + (ses->serverNOS == NULL)) { + seq_printf(m, "\n%d) entry for %s not fully " + "displayed\n\t", i, ses->serverName); + } else { + seq_printf(m, + "\n%d) Name: %s Domain: %s Uses: %d OS:" + " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB" " session status: %d\t", i, ses->serverName, ses->serverDomain, - atomic_read(&ses->inUse), - ses->serverOS, ses->serverNOS, + ses->ses_count, ses->serverOS, ses->serverNOS, ses->capabilities, ses->status); - } - if (ses->server) { + } seq_printf(m, "TCP status: %d\n\tLocal Users To " - "Server: %d SecMode: 0x%x Req On Wire: %d", - ses->server->tcpStatus, - atomic_read(&ses->server->socketUseCount), - ses->server->secMode, - atomic_read(&ses->server->inFlight)); + "Server: %d SecMode: 0x%x Req On Wire: %d", + server->tcpStatus, server->srv_count, + server->secMode, + atomic_read(&server->inFlight)); #ifdef CONFIG_CIFS_STATS2 seq_printf(m, " In Send: %d In MaxReq Wait: %d", - atomic_read(&ses->server->inSend), - atomic_read(&ses->server->num_waiters)); + atomic_read(&server->inSend), + atomic_read(&server->num_waiters)); #endif - seq_puts(m, "\nMIDs:\n"); + seq_puts(m, "\n\tShares:"); + j = 0; + list_for_each(tmp3, &ses->tcon_list) { + tcon = list_entry(tmp3, struct cifsTconInfo, + tcon_list); + ++j; + dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); + seq_printf(m, "\n\t%d) %s Mounts: %d ", j, + tcon->treeName, tcon->tc_count); + if (tcon->nativeFileSystem) { + seq_printf(m, "Type: %s ", + tcon->nativeFileSystem); + } + seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x" + "\nPathComponentMax: %d Status: 0x%d", + le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), + le32_to_cpu(tcon->fsAttrInfo.Attributes), + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), + tcon->tidStatus); + if (dev_type == FILE_DEVICE_DISK) + seq_puts(m, " type: DISK "); + else if (dev_type == FILE_DEVICE_CD_ROM) + seq_puts(m, " type: CDROM "); + else + seq_printf(m, " type: %d ", dev_type); + + if (tcon->need_reconnect) + seq_puts(m, "\tDISCONNECTED "); + seq_putc(m, '\n'); + } + + seq_puts(m, "\n\tMIDs:\n"); spin_lock(&GlobalMid_Lock); - list_for_each(tmp1, &ses->server->pending_mid_q) { - mid_entry = list_entry(tmp1, struct - mid_q_entry, + list_for_each(tmp3, &server->pending_mid_q) { + mid_entry = list_entry(tmp3, struct mid_q_entry, qhead); - seq_printf(m, "State: %d com: %d pid:" + seq_printf(m, "\tState: %d com: %d pid:" " %d tsk: %p mid %d\n", mid_entry->midState, (int)mid_entry->command, @@ -171,44 +204,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) } spin_unlock(&GlobalMid_Lock); } - - } - read_unlock(&GlobalSMBSeslock); - seq_putc(m, '\n'); - - seq_puts(m, "Shares:"); - - i = 0; - read_lock(&GlobalSMBSeslock); - list_for_each(tmp, &GlobalTreeConnectionList) { - __u32 dev_type; - i++; - tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); - dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); - seq_printf(m, "\n%d) %s Uses: %d ", i, - tcon->treeName, atomic_read(&tcon->useCount)); - if (tcon->nativeFileSystem) { - seq_printf(m, "Type: %s ", - tcon->nativeFileSystem); - } - seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x" - "\nPathComponentMax: %d Status: %d", - le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), - le32_to_cpu(tcon->fsAttrInfo.Attributes), - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), - tcon->tidStatus); - if (dev_type == FILE_DEVICE_DISK) - seq_puts(m, " type: DISK "); - else if (dev_type == FILE_DEVICE_CD_ROM) - seq_puts(m, " type: CDROM "); - else - seq_printf(m, " type: %d ", dev_type); - - if (tcon->tidStatus == CifsNeedReconnect) - seq_puts(m, "\tDISCONNECTED "); } - read_unlock(&GlobalSMBSeslock); - + read_unlock(&cifs_tcp_ses_lock); seq_putc(m, '\n'); /* BB add code to dump additional info such as TCP session info now */ @@ -234,7 +231,9 @@ static ssize_t cifs_stats_proc_write(struct file *file, { char c; int rc; - struct list_head *tmp; + struct list_head *tmp1, *tmp2, *tmp3; + struct TCP_Server_Info *server; + struct cifsSesInfo *ses; struct cifsTconInfo *tcon; rc = get_user(c, buffer); @@ -242,33 +241,42 @@ static ssize_t cifs_stats_proc_write(struct file *file, return rc; if (c == '1' || c == 'y' || c == 'Y' || c == '0') { - read_lock(&GlobalSMBSeslock); #ifdef CONFIG_CIFS_STATS2 atomic_set(&totBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0); #endif /* CONFIG_CIFS_STATS2 */ - list_for_each(tmp, &GlobalTreeConnectionList) { - tcon = list_entry(tmp, struct cifsTconInfo, - cifsConnectionList); - atomic_set(&tcon->num_smbs_sent, 0); - atomic_set(&tcon->num_writes, 0); - atomic_set(&tcon->num_reads, 0); - atomic_set(&tcon->num_oplock_brks, 0); - atomic_set(&tcon->num_opens, 0); - atomic_set(&tcon->num_closes, 0); - atomic_set(&tcon->num_deletes, 0); - atomic_set(&tcon->num_mkdirs, 0); - atomic_set(&tcon->num_rmdirs, 0); - atomic_set(&tcon->num_renames, 0); - atomic_set(&tcon->num_t2renames, 0); - atomic_set(&tcon->num_ffirst, 0); - atomic_set(&tcon->num_fnext, 0); - atomic_set(&tcon->num_fclose, 0); - atomic_set(&tcon->num_hardlinks, 0); - atomic_set(&tcon->num_symlinks, 0); - atomic_set(&tcon->num_locks, 0); + read_lock(&cifs_tcp_ses_lock); + list_for_each(tmp1, &cifs_tcp_ses_list) { + server = list_entry(tmp1, struct TCP_Server_Info, + tcp_ses_list); + list_for_each(tmp2, &server->smb_ses_list) { + ses = list_entry(tmp2, struct cifsSesInfo, + smb_ses_list); + list_for_each(tmp3, &ses->tcon_list) { + tcon = list_entry(tmp3, + struct cifsTconInfo, + tcon_list); + atomic_set(&tcon->num_smbs_sent, 0); + atomic_set(&tcon->num_writes, 0); + atomic_set(&tcon->num_reads, 0); + atomic_set(&tcon->num_oplock_brks, 0); + atomic_set(&tcon->num_opens, 0); + atomic_set(&tcon->num_closes, 0); + atomic_set(&tcon->num_deletes, 0); + atomic_set(&tcon->num_mkdirs, 0); + atomic_set(&tcon->num_rmdirs, 0); + atomic_set(&tcon->num_renames, 0); + atomic_set(&tcon->num_t2renames, 0); + atomic_set(&tcon->num_ffirst, 0); + atomic_set(&tcon->num_fnext, 0); + atomic_set(&tcon->num_fclose, 0); + atomic_set(&tcon->num_hardlinks, 0); + atomic_set(&tcon->num_symlinks, 0); + atomic_set(&tcon->num_locks, 0); + } + } } - read_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); } return count; @@ -277,7 +285,9 @@ static ssize_t cifs_stats_proc_write(struct file *file, static int cifs_stats_proc_show(struct seq_file *m, void *v) { int i; - struct list_head *tmp; + struct list_head *tmp1, *tmp2, *tmp3; + struct TCP_Server_Info *server; + struct cifsSesInfo *ses; struct cifsTconInfo *tcon; seq_printf(m, @@ -306,44 +316,55 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) GlobalCurrentXid, GlobalMaxActiveXid); i = 0; - read_lock(&GlobalSMBSeslock); - list_for_each(tmp, &GlobalTreeConnectionList) { - i++; - tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); - seq_printf(m, "\n%d) %s", i, tcon->treeName); - if (tcon->tidStatus == CifsNeedReconnect) - seq_puts(m, "\tDISCONNECTED "); - seq_printf(m, "\nSMBs: %d Oplock Breaks: %d", - atomic_read(&tcon->num_smbs_sent), - atomic_read(&tcon->num_oplock_brks)); - seq_printf(m, "\nReads: %d Bytes: %lld", - atomic_read(&tcon->num_reads), - (long long)(tcon->bytes_read)); - seq_printf(m, "\nWrites: %d Bytes: %lld", - atomic_read(&tcon->num_writes), - (long long)(tcon->bytes_written)); - seq_printf(m, - "\nLocks: %d HardLinks: %d Symlinks: %d", - atomic_read(&tcon->num_locks), - atomic_read(&tcon->num_hardlinks), - atomic_read(&tcon->num_symlinks)); - - seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", - atomic_read(&tcon->num_opens), - atomic_read(&tcon->num_closes), - atomic_read(&tcon->num_deletes)); - seq_printf(m, "\nMkdirs: %d Rmdirs: %d", - atomic_read(&tcon->num_mkdirs), - atomic_read(&tcon->num_rmdirs)); - seq_printf(m, "\nRenames: %d T2 Renames %d", - atomic_read(&tcon->num_renames), - atomic_read(&tcon->num_t2renames)); - seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", - atomic_read(&tcon->num_ffirst), - atomic_read(&tcon->num_fnext), - atomic_read(&tcon->num_fclose)); + read_lock(&cifs_tcp_ses_lock); + list_for_each(tmp1, &cifs_tcp_ses_list) { + server = list_entry(tmp1, struct TCP_Server_Info, + tcp_ses_list); + list_for_each(tmp2, &server->smb_ses_list) { + ses = list_entry(tmp2, struct cifsSesInfo, + smb_ses_list); + list_for_each(tmp3, &ses->tcon_list) { + tcon = list_entry(tmp3, + struct cifsTconInfo, + tcon_list); + i++; + seq_printf(m, "\n%d) %s", i, tcon->treeName); + if (tcon->need_reconnect) + seq_puts(m, "\tDISCONNECTED "); + seq_printf(m, "\nSMBs: %d Oplock Breaks: %d", + atomic_read(&tcon->num_smbs_sent), + atomic_read(&tcon->num_oplock_brks)); + seq_printf(m, "\nReads: %d Bytes: %lld", + atomic_read(&tcon->num_reads), + (long long)(tcon->bytes_read)); + seq_printf(m, "\nWrites: %d Bytes: %lld", + atomic_read(&tcon->num_writes), + (long long)(tcon->bytes_written)); + seq_printf(m, "\nLocks: %d HardLinks: %d " + "Symlinks: %d", + atomic_read(&tcon->num_locks), + atomic_read(&tcon->num_hardlinks), + atomic_read(&tcon->num_symlinks)); + seq_printf(m, "\nOpens: %d Closes: %d" + "Deletes: %d", + atomic_read(&tcon->num_opens), + atomic_read(&tcon->num_closes), + atomic_read(&tcon->num_deletes)); + seq_printf(m, "\nMkdirs: %d Rmdirs: %d", + atomic_read(&tcon->num_mkdirs), + atomic_read(&tcon->num_rmdirs)); + seq_printf(m, "\nRenames: %d T2 Renames %d", + atomic_read(&tcon->num_renames), + atomic_read(&tcon->num_t2renames)); + seq_printf(m, "\nFindFirst: %d FNext %d " + "FClose %d", + atomic_read(&tcon->num_ffirst), + atomic_read(&tcon->num_fnext), + atomic_read(&tcon->num_fclose)); + } + } } - read_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); seq_putc(m, '\n'); return 0; diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index d2c8eef84f3c..85c0a74d034d 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -106,7 +106,8 @@ static char *cifs_get_share_name(const char *node_name) /** * compose_mount_options - creates mount options for refferral * @sb_mountdata: parent/root DFS mount options (template) - * @ref_unc: refferral server UNC + * @dentry: point where we are going to mount + * @ref: server's referral * @devname: pointer for saving device name * * creates mount options for submount based on template options sb_mountdata @@ -116,33 +117,39 @@ static char *cifs_get_share_name(const char *node_name) * Caller is responcible for freeing retunrned value if it is not error. */ static char *compose_mount_options(const char *sb_mountdata, - const char *ref_unc, + struct dentry *dentry, + const struct dfs_info3_param *ref, char **devname) { int rc; - char *mountdata; + char *mountdata = NULL; int md_len; char *tkn_e; char *srvIP = NULL; char sep = ','; int off, noff; + char *fullpath; if (sb_mountdata == NULL) return ERR_PTR(-EINVAL); - *devname = cifs_get_share_name(ref_unc); + *devname = cifs_get_share_name(ref->node_name); rc = dns_resolve_server_name_to_ip(*devname, &srvIP); if (rc != 0) { - cERROR(1, ("%s: Failed to resolve server part of %s to IP", - __func__, *devname)); - mountdata = ERR_PTR(rc); - goto compose_mount_options_out; + cERROR(1, ("%s: Failed to resolve server part of %s to IP: %d", + __func__, *devname, rc));; + goto compose_mount_options_err; } - md_len = strlen(sb_mountdata) + strlen(srvIP) + strlen(ref_unc) + 3; + /* md_len = strlen(...) + 12 for 'sep+prefixpath=' + * assuming that we have 'unc=' and 'ip=' in + * the original sb_mountdata + */ + md_len = strlen(sb_mountdata) + strlen(srvIP) + + strlen(ref->node_name) + 12; mountdata = kzalloc(md_len+1, GFP_KERNEL); if (mountdata == NULL) { - mountdata = ERR_PTR(-ENOMEM); - goto compose_mount_options_out; + rc = -ENOMEM; + goto compose_mount_options_err; } /* copy all options except of unc,ip,prefixpath */ @@ -152,53 +159,87 @@ static char *compose_mount_options(const char *sb_mountdata, strncpy(mountdata, sb_mountdata, 5); off += 5; } - while ((tkn_e = strchr(sb_mountdata+off, sep))) { - noff = (tkn_e - (sb_mountdata+off)) + 1; - if (strnicmp(sb_mountdata+off, "unc=", 4) == 0) { + + do { + tkn_e = strchr(sb_mountdata + off, sep); + if (tkn_e == NULL) + noff = strlen(sb_mountdata + off); + else + noff = tkn_e - (sb_mountdata + off) + 1; + + if (strnicmp(sb_mountdata + off, "unc=", 4) == 0) { off += noff; continue; } - if (strnicmp(sb_mountdata+off, "ip=", 3) == 0) { + if (strnicmp(sb_mountdata + off, "ip=", 3) == 0) { off += noff; continue; } - if (strnicmp(sb_mountdata+off, "prefixpath=", 3) == 0) { + if (strnicmp(sb_mountdata + off, "prefixpath=", 11) == 0) { off += noff; continue; } - strncat(mountdata, sb_mountdata+off, noff); + strncat(mountdata, sb_mountdata + off, noff); off += noff; - } - strcat(mountdata, sb_mountdata+off); + } while (tkn_e); + strcat(mountdata, sb_mountdata + off); mountdata[md_len] = '\0'; /* copy new IP and ref share name */ - strcat(mountdata, ",ip="); + if (mountdata[strlen(mountdata) - 1] != sep) + strncat(mountdata, &sep, 1); + strcat(mountdata, "ip="); strcat(mountdata, srvIP); - strcat(mountdata, ",unc="); + strncat(mountdata, &sep, 1); + strcat(mountdata, "unc="); strcat(mountdata, *devname); /* find & copy prefixpath */ - tkn_e = strchr(ref_unc+2, '\\'); - if (tkn_e) { - tkn_e = strchr(tkn_e+1, '\\'); - if (tkn_e) { - strcat(mountdata, ",prefixpath="); - strcat(mountdata, tkn_e+1); - } + tkn_e = strchr(ref->node_name + 2, '\\'); + if (tkn_e == NULL) { + /* invalid unc, missing share name*/ + rc = -EINVAL; + goto compose_mount_options_err; } + /* + * this function gives us a path with a double backslash prefix. We + * require a single backslash for DFS. Temporarily increment fullpath + * to put it in the proper form and decrement before freeing it. + */ + fullpath = build_path_from_dentry(dentry); + if (!fullpath) { + rc = -ENOMEM; + goto compose_mount_options_err; + } + ++fullpath; + tkn_e = strchr(tkn_e + 1, '\\'); + if (tkn_e || (strlen(fullpath) - ref->path_consumed)) { + strncat(mountdata, &sep, 1); + strcat(mountdata, "prefixpath="); + if (tkn_e) + strcat(mountdata, tkn_e + 1); + strcat(mountdata, fullpath + ref->path_consumed); + } + --fullpath; + kfree(fullpath); + /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/ /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/ compose_mount_options_out: kfree(srvIP); return mountdata; + +compose_mount_options_err: + kfree(mountdata); + mountdata = ERR_PTR(rc); + goto compose_mount_options_out; } static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, - struct dentry *dentry, char *ref_unc) + struct dentry *dentry, const struct dfs_info3_param *ref) { struct cifs_sb_info *cifs_sb; struct vfsmount *mnt; @@ -207,7 +248,7 @@ static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, cifs_sb = CIFS_SB(dentry->d_inode->i_sb); mountdata = compose_mount_options(cifs_sb->mountdata, - ref_unc, &devname); + dentry, ref, &devname); if (IS_ERR(mountdata)) return (struct vfsmount *)mountdata; @@ -286,13 +327,19 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) goto out_err; } + /* + * The MSDFS spec states that paths in DFS referral requests and + * responses must be prefixed by a single '\' character instead of + * the double backslashes usually used in the UNC. This function + * gives us the latter, so we must adjust the result. + */ full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; goto out_err; } - rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls, + rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb->local_nls, &num_referrals, &referrals, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -310,7 +357,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) } mnt = cifs_dfs_do_refmount(nd->path.mnt, nd->path.dentry, - referrals[i].node_name); + referrals + i); cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, referrals[i].node_name, mnt)); diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 877c85409f1f..c4c306f7b06f 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -19,8 +19,8 @@ #define _CIFS_FS_SB_H #define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */ -#define CIFS_MOUNT_SET_UID 2 /* set current->euid in create etc. */ -#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */ +#define CIFS_MOUNT_SET_UID 2 /* set current's euid in create etc. */ +#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */ #define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */ #define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */ #define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */ @@ -30,7 +30,8 @@ #define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */ #define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */ #define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */ -#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */ +#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */ +#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index fcee9298b620..3fd3a9df043a 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -73,8 +73,8 @@ struct key_type cifs_spnego_key_type = { * strlen(";sec=ntlmsspi") */ #define MAX_MECH_STR_LEN 13 -/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */ -#define MAX_IPV6_ADDR_LEN 42 +/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/128 */ +#define MAX_IPV6_ADDR_LEN 43 /* strlen of "host=" */ #define HOST_KEY_LEN 5 @@ -121,11 +121,9 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo) /* add the server address */ if (server->addr.sockAddr.sin_family == AF_INET) - sprintf(dp, "ip4=" NIPQUAD_FMT, - NIPQUAD(server->addr.sockAddr.sin_addr)); + sprintf(dp, "ip4=%pI4", &server->addr.sockAddr.sin_addr); else if (server->addr.sockAddr.sin_family == AF_INET6) - sprintf(dp, "ip6=" NIP6_SEQFMT, - NIP6(server->addr.sockAddr6.sin6_addr)); + sprintf(dp, "ip6=%pi6", &server->addr.sockAddr6.sin6_addr); else goto out; diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index bd5f13d38450..d4839cf0cb2c 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -37,7 +37,7 @@ extern void mdfour(unsigned char *out, unsigned char *in, int n); extern void E_md4hash(const unsigned char *passwd, unsigned char *p16); -extern void SMBencrypt(unsigned char *passwd, unsigned char *c8, +extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24); static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, @@ -280,25 +280,22 @@ int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *ses, } #ifdef CONFIG_CIFS_WEAK_PW_HASH -void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key) +void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, + char *lnm_session_key) { int i; char password_with_pad[CIFS_ENCPWD_SIZE]; - if (ses->server == NULL) - return; - memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); - if (ses->password) - strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE); - - if ((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0) - if (extended_security & CIFSSEC_MAY_PLNTXT) { - memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE); - memcpy(lnm_session_key, password_with_pad, - CIFS_ENCPWD_SIZE); - return; - } + if (password) + strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE); + + if (!encrypt && extended_security & CIFSSEC_MAY_PLNTXT) { + memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE); + memcpy(lnm_session_key, password_with_pad, + CIFS_ENCPWD_SIZE); + return; + } /* calculate old style session key */ /* calling toupper is less broken than repeatedly @@ -314,7 +311,8 @@ void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key) for (i = 0; i < CIFS_ENCPWD_SIZE; i++) password_with_pad[i] = toupper(password_with_pad[i]); - SMBencrypt(password_with_pad, ses->server->cryptKey, lnm_session_key); + SMBencrypt(password_with_pad, cryptkey, lnm_session_key); + /* clear password before we return/free memory */ memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); } diff --git a/fs/cifs/cifsencrypt.h b/fs/cifs/cifsencrypt.h index 152fa2dcfc6c..15d2ec006474 100644 --- a/fs/cifs/cifsencrypt.h +++ b/fs/cifs/cifsencrypt.h @@ -26,7 +26,8 @@ extern void mdfour(unsigned char *out, unsigned char *in, int n); /* smbdes.c */ extern void E_P16(unsigned char *p14, unsigned char *p16); -extern void E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24); +extern void E_P24(unsigned char *p21, const unsigned char *c8, + unsigned char *p24); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index ac5915d61dca..0005a194a75c 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -66,7 +66,9 @@ unsigned int sign_CIFS_PDUs = 1; extern struct task_struct *oplockThread; /* remove sparse warning */ struct task_struct *oplockThread = NULL; /* extern struct task_struct * dnotifyThread; remove sparse warning */ +#ifdef CONFIG_CIFS_EXPERIMENTAL static struct task_struct *dnotifyThread = NULL; +#endif static const struct super_operations cifs_super_ops; unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; module_param(CIFSMaxBufSize, int, 0); @@ -337,39 +339,58 @@ static int cifs_show_options(struct seq_file *s, struct vfsmount *m) { struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *tcon; + struct TCP_Server_Info *server; cifs_sb = CIFS_SB(m->mnt_sb); if (cifs_sb) { - if (cifs_sb->tcon) { -/* BB add prepath to mount options displayed */ + tcon = cifs_sb->tcon; + if (tcon) { seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName); - if (cifs_sb->tcon->ses) { - if (cifs_sb->tcon->ses->userName) + if (tcon->ses) { + if (tcon->ses->userName) seq_printf(s, ",username=%s", - cifs_sb->tcon->ses->userName); - if (cifs_sb->tcon->ses->domainName) + tcon->ses->userName); + if (tcon->ses->domainName) seq_printf(s, ",domain=%s", - cifs_sb->tcon->ses->domainName); + tcon->ses->domainName); + server = tcon->ses->server; + if (server) { + seq_printf(s, ",addr="); + switch (server->addr.sockAddr6. + sin6_family) { + case AF_INET6: + seq_printf(s, "%pI6", + &server->addr.sockAddr6.sin6_addr); + break; + case AF_INET: + seq_printf(s, "%pI4", + &server->addr.sockAddr.sin_addr.s_addr); + break; + } + } } if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) || - !(cifs_sb->tcon->unix_ext)) + !(tcon->unix_ext)) seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) || - !(cifs_sb->tcon->unix_ext)) + !(tcon->unix_ext)) seq_printf(s, ",gid=%d", cifs_sb->mnt_gid); - if (!cifs_sb->tcon->unix_ext) { + if (!tcon->unix_ext) { seq_printf(s, ",file_mode=0%o,dir_mode=0%o", cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode); } - if (cifs_sb->tcon->seal) + if (tcon->seal) seq_printf(s, ",seal"); - if (cifs_sb->tcon->nocase) + if (tcon->nocase) seq_printf(s, ",nocase"); - if (cifs_sb->tcon->retry) + if (tcon->retry) seq_printf(s, ",hard"); } + if (cifs_sb->prepath) + seq_printf(s, ",prepath=%s", cifs_sb->prepath); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) seq_printf(s, ",posixpaths"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) @@ -417,9 +438,8 @@ int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid, xid = GetXid(); if (pTcon) { cFYI(1, ("set type: 0x%x id: %d", quota_type, qid)); - } else { + } else rc = -EIO; - } FreeXid(xid); return rc; @@ -441,9 +461,8 @@ int cifs_xquota_get(struct super_block *sb, int quota_type, qid_t qid, xid = GetXid(); if (pTcon) { cFYI(1, ("set type: 0x%x id: %d", quota_type, qid)); - } else { + } else rc = -EIO; - } FreeXid(xid); return rc; @@ -464,9 +483,8 @@ int cifs_xstate_set(struct super_block *sb, unsigned int flags, int operation) xid = GetXid(); if (pTcon) { cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation)); - } else { + } else rc = -EIO; - } FreeXid(xid); return rc; @@ -479,17 +497,16 @@ int cifs_xstate_get(struct super_block *sb, struct fs_quota_stat *qstats) struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; - if (cifs_sb) { + if (cifs_sb) pTcon = cifs_sb->tcon; - } else { + else return -EIO; - } + xid = GetXid(); if (pTcon) { cFYI(1, ("pqstats %p", qstats)); - } else { + } else rc = -EIO; - } FreeXid(xid); return rc; @@ -514,10 +531,11 @@ static void cifs_umount_begin(struct super_block *sb) tcon = cifs_sb->tcon; if (tcon == NULL) return; - down(&tcon->tconSem); - if (atomic_read(&tcon->useCount) == 1) + + read_lock(&cifs_tcp_ses_lock); + if (tcon->tc_count == 1) tcon->tidStatus = CifsExiting; - up(&tcon->tconSem); + read_unlock(&cifs_tcp_ses_lock); /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ /* cancel_notify_requests(tcon); */ @@ -1013,7 +1031,7 @@ static int cifs_oplock_thread(void *dummyarg) not bother sending an oplock release if session to server still is disconnected since oplock already released by the server in that case */ - if (pTcon->tidStatus != CifsNeedReconnect) { + if (!pTcon->need_reconnect) { rc = CIFSSMBLock(0, pTcon, netfid, 0 /* len */ , 0 /* offset */, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, @@ -1028,40 +1046,40 @@ static int cifs_oplock_thread(void *dummyarg) return 0; } +#ifdef CONFIG_CIFS_EXPERIMENTAL static int cifs_dnotify_thread(void *dummyarg) { struct list_head *tmp; - struct cifsSesInfo *ses; + struct TCP_Server_Info *server; do { if (try_to_freeze()) continue; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(15*HZ); - read_lock(&GlobalSMBSeslock); /* check if any stuck requests that need to be woken up and wakeq so the thread can wake up and error out */ - list_for_each(tmp, &GlobalSMBSessionList) { - ses = list_entry(tmp, struct cifsSesInfo, - cifsSessionList); - if (ses->server && atomic_read(&ses->server->inFlight)) - wake_up_all(&ses->server->response_q); + read_lock(&cifs_tcp_ses_lock); + list_for_each(tmp, &cifs_tcp_ses_list) { + server = list_entry(tmp, struct TCP_Server_Info, + tcp_ses_list); + if (atomic_read(&server->inFlight)) + wake_up_all(&server->response_q); } - read_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); } while (!kthread_should_stop()); return 0; } +#endif static int __init init_cifs(void) { int rc = 0; cifs_proc_init(); -/* INIT_LIST_HEAD(&GlobalServerList);*/ /* BB not implemented yet */ - INIT_LIST_HEAD(&GlobalSMBSessionList); - INIT_LIST_HEAD(&GlobalTreeConnectionList); + INIT_LIST_HEAD(&cifs_tcp_ses_list); INIT_LIST_HEAD(&GlobalOplock_Q); #ifdef CONFIG_CIFS_EXPERIMENTAL INIT_LIST_HEAD(&GlobalDnotifyReqList); @@ -1089,6 +1107,7 @@ init_cifs(void) GlobalMaxActiveXid = 0; memset(Local_System_Name, 0, 15); rwlock_init(&GlobalSMBSeslock); + rwlock_init(&cifs_tcp_ses_lock); spin_lock_init(&GlobalMid_Lock); if (cifs_max_pending < 2) { @@ -1131,16 +1150,20 @@ init_cifs(void) goto out_unregister_dfs_key_type; } +#ifdef CONFIG_CIFS_EXPERIMENTAL dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd"); if (IS_ERR(dnotifyThread)) { rc = PTR_ERR(dnotifyThread); cERROR(1, ("error %d create dnotify thread", rc)); goto out_stop_oplock_thread; } +#endif return 0; +#ifdef CONFIG_CIFS_EXPERIMENTAL out_stop_oplock_thread: +#endif kthread_stop(oplockThread); out_unregister_dfs_key_type: #ifdef CONFIG_CIFS_DFS_UPCALL @@ -1179,8 +1202,10 @@ exit_cifs(void) cifs_destroy_inodecache(); cifs_destroy_mids(); cifs_destroy_request_bufs(); - kthread_stop(oplockThread); +#ifdef CONFIG_CIFS_EXPERIMENTAL kthread_stop(dnotifyThread); +#endif + kthread_stop(oplockThread); } MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>"); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 074de0b5064d..2ce04c73d74e 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -101,5 +101,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.55" +#define CIFS_VERSION "1.56" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1cb1189f24e0..94c1ca0ec953 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -47,7 +47,11 @@ */ #define CIFS_MAX_REQ 50 -#define SERVER_NAME_LENGTH 15 +#define RFC1001_NAME_LEN 15 +#define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1) + +/* currently length of NIP6_FMT */ +#define SERVER_NAME_LENGTH 40 #define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1) /* used to define string lengths for reversing unicode strings */ @@ -85,8 +89,7 @@ enum securityEnum { }; enum protocolEnum { - IPV4 = 0, - IPV6, + TCP = 0, SCTP /* Netbios frames protocol not supported at this time */ }; @@ -122,9 +125,11 @@ struct cifs_cred { */ struct TCP_Server_Info { + struct list_head tcp_ses_list; + struct list_head smb_ses_list; + int srv_count; /* reference counter */ /* 15 character server name + 0x20 16th byte indicating type = srv */ - char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; - char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; + char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; char *hostname; /* hostname portion of UNC string */ struct socket *ssocket; union { @@ -143,14 +148,13 @@ struct TCP_Server_Info { bool svlocal:1; /* local server or remote */ bool noblocksnd; /* use blocking sendmsg */ bool noautotune; /* do not autotune send buf sizes */ - atomic_t socketUseCount; /* number of open cifs sessions on socket */ atomic_t inFlight; /* number of requests on the wire to server */ #ifdef CONFIG_CIFS_STATS2 atomic_t inSend; /* requests trying to send */ atomic_t num_waiters; /* blocked waiting to get in sendrecv */ #endif enum statusEnum tcpStatus; /* what we think the status is */ - struct semaphore tcpSem; + struct mutex srv_mutex; struct task_struct *tsk; char server_GUID[16]; char secMode; @@ -170,7 +174,7 @@ struct TCP_Server_Info { __u16 CurrentMid; /* multiplex id - rotating counter */ char cryptKey[CIFS_CRYPTO_KEY_SIZE]; /* 16th byte of RFC1001 workstation name is always null */ - char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; + char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ struct mac_key mac_signing_key; char ntlmv2_hash[16]; @@ -194,13 +198,14 @@ struct cifsUidInfo { * Session structure. One of these for each uid session with a particular host */ struct cifsSesInfo { - struct list_head cifsSessionList; + struct list_head smb_ses_list; + struct list_head tcon_list; struct semaphore sesSem; #if 0 struct cifsUidInfo *uidInfo; /* pointer to user info */ #endif struct TCP_Server_Info *server; /* pointer to server info */ - atomic_t inUse; /* # of mounts (tree connections) on this ses */ + int ses_count; /* reference counter */ enum statusEnum status; unsigned overrideSecFlg; /* if non-zero override global sec flags */ __u16 ipc_tid; /* special tid for connection to IPC share */ @@ -216,6 +221,7 @@ struct cifsSesInfo { char userName[MAX_USERNAME_SIZE + 1]; char *domainName; char *password; + bool need_reconnect:1; /* connection reset, uid now invalid */ }; /* no more than one of the following three session flags may be set */ #define CIFS_SES_NT4 1 @@ -230,16 +236,16 @@ struct cifsSesInfo { * session */ struct cifsTconInfo { - struct list_head cifsConnectionList; + struct list_head tcon_list; + int tc_count; struct list_head openFileList; - struct semaphore tconSem; struct cifsSesInfo *ses; /* pointer to session associated with */ char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ char *nativeFileSystem; + char *password; /* for share-level security */ __u16 tid; /* The 2 byte tree id */ __u16 Flags; /* optional support bits */ enum statusEnum tidStatus; - atomic_t useCount; /* how many explicit/implicit mounts to share */ #ifdef CONFIG_CIFS_STATS atomic_t num_smbs_sent; atomic_t num_writes; @@ -288,6 +294,7 @@ struct cifsTconInfo { bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol for this mount even if server would support */ bool local_lease:1; /* check leases (only) on local system not remote */ + bool need_reconnect:1; /* connection reset, tid now invalid */ /* BB add field for back pointer to sb struct(s)? */ }; @@ -419,7 +426,6 @@ struct mid_q_entry { unsigned long when_sent; /* time when smb send finished */ unsigned long when_received; /* when demux complete (taken off wire) */ #endif - struct cifsSesInfo *ses; /* smb was sent to this server */ struct task_struct *tsk; /* task waiting for response */ struct smb_hdr *resp_buf; /* response buffer */ int midState; /* wish this were enum but can not pass to wait_event */ @@ -588,22 +594,30 @@ require use of the stronger protocol */ #endif /* - * The list of servers that did not respond with NT LM 0.12. - * This list helps improve performance and eliminate the messages indicating - * that we had a communications error talking to the server in this list. + * the list of TCP_Server_Info structures, ie each of the sockets + * connecting our client to a distinct server (ip address), is + * chained together by cifs_tcp_ses_list. The list of all our SMB + * sessions (and from that the tree connections) can be found + * by iterating over cifs_tcp_ses_list */ -/* Feature not supported */ -/* GLOBAL_EXTERN struct servers_not_supported *NotSuppList; */ +GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; /* - * The following is a hash table of all the users we know about. + * This lock protects the cifs_tcp_ses_list, the list of smb sessions per + * tcp session, and the list of tcon's per smb session. It also protects + * the reference counters for the server, smb session, and tcon. Finally, + * changes to the tcon->tidStatus should be done while holding this lock. */ -GLOBAL_EXTERN struct smbUidInfo *GlobalUidList[UID_HASH]; +GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; -/* GLOBAL_EXTERN struct list_head GlobalServerList; BB not implemented yet */ -GLOBAL_EXTERN struct list_head GlobalSMBSessionList; -GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; -GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ +/* + * This lock protects the cifs_file->llist and cifs_file->flist + * list operations, and updates to some flags (cifs_file->invalidHandle) + * It will be moved to either use the tcon->stat_lock or equivalent later. + * If cifs_tcp_ses_lock and the lock below are both needed to be held, then + * the cifs_tcp_ses_lock must be grabbed first and released last. + */ +GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; GLOBAL_EXTERN struct list_head GlobalOplock_Q; diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index d2a073edd1b8..b4e2e9f0ee3d 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1922,7 +1922,7 @@ typedef struct smb_com_transaction2_get_dfs_refer_req { /* DFS server target type */ #define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */ #define DFS_TYPE_ROOT 0x0001 - + /* Referral Entry Flags */ #define DFS_NAME_LIST_REF 0x0200 diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 6f21ecb85ce5..06f6779988bf 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -39,7 +39,7 @@ extern int smb_send(struct socket *, struct smb_hdr *, unsigned int /* length */ , struct sockaddr *, bool); extern unsigned int _GetXid(void); extern void _FreeXid(unsigned int); -#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current->fsuid)); +#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current_fsuid())); #define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__func__,curr_xid,(int)rc));} extern char *build_path_from_dentry(struct dentry *); extern char *build_wildcard_path_from_dentry(struct dentry *direntry); @@ -330,7 +330,8 @@ extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *); extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, const struct nls_table *); #ifdef CONFIG_CIFS_WEAK_PW_HASH -extern void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key); +extern void calc_lanman_hash(const char *password, const char *cryptkey, + bool encrypt, char *lnm_session_key); #endif /* CIFS_WEAK_PW_HASH */ extern int CIFSSMBCopy(int xid, struct cifsTconInfo *source_tcon, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index d5eac48fc415..552642a507c4 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -190,10 +190,10 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); - if (tcon->ses->status == CifsNeedReconnect) + if (tcon->ses->need_reconnect) rc = cifs_setup_session(0, tcon->ses, nls_codepage); - if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { + if (!rc && (tcon->need_reconnect)) { mark_open_files_invalid(tcon); rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); @@ -337,10 +337,10 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); - if (tcon->ses->status == CifsNeedReconnect) + if (tcon->ses->need_reconnect) rc = cifs_setup_session(0, tcon->ses, nls_codepage); - if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { + if (!rc && (tcon->need_reconnect)) { mark_open_files_invalid(tcon); rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); @@ -664,8 +664,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) rc = -EIO; goto neg_err_exit; } - - if (server->socketUseCount.counter > 1) { + read_lock(&cifs_tcp_ses_lock); + if (server->srv_count > 1) { + read_unlock(&cifs_tcp_ses_lock); if (memcmp(server->server_GUID, pSMBr->u.extended_response. GUID, 16) != 0) { @@ -674,9 +675,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) pSMBr->u.extended_response.GUID, 16); } - } else + } else { + read_unlock(&cifs_tcp_ses_lock); memcpy(server->server_GUID, pSMBr->u.extended_response.GUID, 16); + } if (count == 16) { server->secType = RawNTLMSSP; @@ -739,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) int rc = 0; cFYI(1, ("In tree disconnect")); - /* - * If last user of the connection and - * connection alive - disconnect it - * If this is the last connection on the server session disconnect it - * (and inside session disconnect we should check if tcp socket needs - * to be freed and kernel thread woken up). - */ - if (tcon) - down(&tcon->tconSem); - else - return -EIO; - atomic_dec(&tcon->useCount); - if (atomic_read(&tcon->useCount) > 0) { - up(&tcon->tconSem); - return -EBUSY; - } + /* BB: do we need to check this? These should never be NULL. */ + if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) + return -EIO; - /* No need to return error on this operation if tid invalidated and - closed on server already e.g. due to tcp session crashing */ - if (tcon->tidStatus == CifsNeedReconnect) { - up(&tcon->tconSem); + /* + * No need to return error on this operation if tid invalidated and + * closed on server already e.g. due to tcp session crashing. Also, + * the tcon is no longer on the list, so no need to take lock before + * checking this. + */ + if (tcon->need_reconnect) return 0; - } - if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) { - up(&tcon->tconSem); - return -EIO; - } rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, (void **)&smb_buffer); - if (rc) { - up(&tcon->tconSem); + if (rc) return rc; - } rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); if (rc) cFYI(1, ("Tree disconnect failed %d", rc)); - up(&tcon->tconSem); - /* No need to return error on this operation if tid invalidated and - closed on server already e.g. due to tcp session crashing */ + closed on server already e.g. due to tcp session crashing */ if (rc == -EAGAIN) rc = 0; @@ -796,43 +780,36 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) int rc = 0; cFYI(1, ("In SMBLogoff for session disconnect")); - if (ses) - down(&ses->sesSem); - else + + /* + * BB: do we need to check validity of ses and server? They should + * always be valid since we have an active reference. If not, that + * should probably be a BUG() + */ + if (!ses || !ses->server) return -EIO; - atomic_dec(&ses->inUse); - if (atomic_read(&ses->inUse) > 0) { - up(&ses->sesSem); - return -EBUSY; - } + down(&ses->sesSem); + if (ses->need_reconnect) + goto session_already_dead; /* no need to send SMBlogoff if uid + already closed due to reconnect */ rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); if (rc) { up(&ses->sesSem); return rc; } - if (ses->server) { - pSMB->hdr.Mid = GetNextMid(ses->server); + pSMB->hdr.Mid = GetNextMid(ses->server); - if (ses->server->secMode & + if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - } pSMB->hdr.Uid = ses->Suid; pSMB->AndXCommand = 0xFF; rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); - if (ses->server) { - atomic_dec(&ses->server->socketUseCount); - if (atomic_read(&ses->server->socketUseCount) == 0) { - spin_lock(&GlobalMid_Lock); - ses->server->tcpStatus = CifsExiting; - spin_unlock(&GlobalMid_Lock); - rc = -ESHUTDOWN; - } - } +session_already_dead: up(&ses->sesSem); /* if session dead then we do not need to do ulogoff, @@ -1405,13 +1382,13 @@ openRetry: if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) *pOplock |= CIFS_CREATE_ACTION; if (pfile_info) { - memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime, - 36 /* CreationTime to Attributes */); - /* the file_info buf is endian converted by caller */ - pfile_info->AllocationSize = pSMBr->AllocationSize; - pfile_info->EndOfFile = pSMBr->EndOfFile; - pfile_info->NumberOfLinks = cpu_to_le32(1); - pfile_info->DeletePending = 0; + memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime, + 36 /* CreationTime to Attributes */); + /* the file_info buf is endian converted by caller */ + pfile_info->AllocationSize = pSMBr->AllocationSize; + pfile_info->EndOfFile = pSMBr->EndOfFile; + pfile_info->NumberOfLinks = cpu_to_le32(1); + pfile_info->DeletePending = 0; } } @@ -1437,8 +1414,13 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, cFYI(1, ("Reading %d bytes on fid %d", count, netfid)); if (tcon->ses->capabilities & CAP_LARGE_FILES) wct = 12; - else + else { wct = 10; /* old style read */ + if ((lseek >> 32) > 0) { + /* can not handle this big offset for old */ + return -EIO; + } + } *nbytes = 0; rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB); @@ -1454,8 +1436,6 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); if (wct == 12) pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); - else if ((lseek >> 32) > 0) /* can not handle this big offset for old */ - return -EIO; pSMB->Remaining = 0; pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); @@ -1542,8 +1522,13 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, if (tcon->ses->capabilities & CAP_LARGE_FILES) wct = 14; - else + else { wct = 12; + if ((offset >> 32) > 0) { + /* can not handle big offset for old srv */ + return -EIO; + } + } rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB, (void **) &pSMBr); @@ -1558,8 +1543,6 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); if (wct == 14) pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - else if ((offset >> 32) > 0) /* can not handle big offset for old srv */ - return -EIO; pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; @@ -1581,7 +1564,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->DataOffset = cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); if (buf) - memcpy(pSMB->Data, buf, bytes_sent); + memcpy(pSMB->Data, buf, bytes_sent); else if (ubuf) { if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) { cifs_buf_release(pSMB); @@ -1644,10 +1627,15 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count)); - if (tcon->ses->capabilities & CAP_LARGE_FILES) + if (tcon->ses->capabilities & CAP_LARGE_FILES) { wct = 14; - else + } else { wct = 12; + if ((offset >> 32) > 0) { + /* can not handle big offset for old srv */ + return -EIO; + } + } rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB); if (rc) return rc; @@ -1660,8 +1648,6 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); if (wct == 14) pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - else if ((offset >> 32) > 0) /* can not handle big offset for old srv */ - return -EIO; pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; @@ -1885,10 +1871,6 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, rc = -EIO; /* bad smb */ goto plk_err_exit; } - if (pLockData == NULL) { - rc = -EINVAL; - goto plk_err_exit; - } data_offset = le16_to_cpu(pSMBr->t2.DataOffset); data_count = le16_to_cpu(pSMBr->t2.DataCount); if (data_count < sizeof(struct cifs_posix_lock)) { @@ -3922,6 +3904,27 @@ GetInodeNumOut: return rc; } +/* computes length of UCS string converted to host codepage + * @src: UCS string + * @maxlen: length of the input string in UCS characters + * (not in bytes) + * + * return: size of input string in host codepage + */ +static int hostlen_fromUCS(const __le16 *src, const int maxlen, + const struct nls_table *nls_codepage) { + int i; + int hostlen = 0; + char to[4]; + int charlen; + for (i = 0; (i < maxlen) && src[i]; ++i) { + charlen = nls_codepage->uni2char(le16_to_cpu(src[i]), + to, NLS_MAX_CHARSET_SIZE); + hostlen += charlen > 0 ? charlen : 1; + } + return hostlen; +} + /* parses DFS refferal V3 structure * caller is responsible for freeing target_nodes * returns: @@ -3932,7 +3935,8 @@ static int parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, unsigned int *num_of_nodes, struct dfs_info3_param **target_nodes, - const struct nls_table *nls_codepage) + const struct nls_table *nls_codepage, int remap, + const char *searchName) { int i, rc = 0; char *data_end; @@ -3983,7 +3987,18 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, struct dfs_info3_param *node = (*target_nodes)+i; node->flags = le16_to_cpu(pSMBr->DFSFlags); - node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); + if (is_unicode) { + __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, + GFP_KERNEL); + cifsConvertToUCS((__le16 *) tmp, searchName, + PATH_MAX, nls_codepage, remap); + node->path_consumed = hostlen_fromUCS(tmp, + le16_to_cpu(pSMBr->PathConsumed)/2, + nls_codepage); + kfree(tmp); + } else + node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); + node->server_type = le16_to_cpu(ref->ServerType); node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); @@ -4116,7 +4131,8 @@ getDFSRetry: /* parse returned result into more usable form */ rc = parse_DFS_referrals(pSMBr, num_of_nodes, - target_nodes, nls_codepage); + target_nodes, nls_codepage, remap, + searchName); GetDFSRefExit: cifs_buf_release(pSMB); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e9f9248cb3fe..e9ea394ee075 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -89,6 +89,7 @@ struct smb_vol { bool nullauth:1; /* attempt to authenticate with null user */ bool nocase:1; /* request case insensitive filenames */ bool nobrl:1; /* disable sending byte range locks to srv */ + bool mand_lock:1; /* send mandatory not posix byte range lock reqs */ bool seal:1; /* request transport encryption on share */ bool nodfs:1; /* Do not request DFS, even if available */ bool local_lease:1; /* check leases only on local system, not remote */ @@ -101,30 +102,22 @@ struct smb_vol { char *prepath; }; -static int ipv4_connect(struct sockaddr_in *psin_server, - struct socket **csocket, - char *netb_name, - char *server_netb_name, - bool noblocksnd, - bool nosndbuf); /* ipv6 never set sndbuf size */ -static int ipv6_connect(struct sockaddr_in6 *psin_server, - struct socket **csocket, bool noblocksnd); - - - /* - * cifs tcp session reconnection - * - * mark tcp session as reconnecting so temporarily locked - * mark all smb sessions as reconnecting for tcp session - * reconnect tcp session - * wake up waiters on reconnection? - (not needed currently) - */ +static int ipv4_connect(struct TCP_Server_Info *server); +static int ipv6_connect(struct TCP_Server_Info *server); +/* + * cifs tcp session reconnection + * + * mark tcp session as reconnecting so temporarily locked + * mark all smb sessions as reconnecting for tcp session + * reconnect tcp session + * wake up waiters on reconnection? - (not needed currently) + */ static int cifs_reconnect(struct TCP_Server_Info *server) { int rc = 0; - struct list_head *tmp; + struct list_head *tmp, *tmp2; struct cifsSesInfo *ses; struct cifsTconInfo *tcon; struct mid_q_entry *mid_entry; @@ -144,25 +137,19 @@ cifs_reconnect(struct TCP_Server_Info *server) /* before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they are not used until reconnected */ - read_lock(&GlobalSMBSeslock); - list_for_each(tmp, &GlobalSMBSessionList) { - ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); - if (ses->server) { - if (ses->server == server) { - ses->status = CifsNeedReconnect; - ses->ipc_tid = 0; - } + read_lock(&cifs_tcp_ses_lock); + list_for_each(tmp, &server->smb_ses_list) { + ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); + ses->need_reconnect = true; + ses->ipc_tid = 0; + list_for_each(tmp2, &ses->tcon_list) { + tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list); + tcon->need_reconnect = true; } - /* else tcp and smb sessions need reconnection */ } - list_for_each(tmp, &GlobalTreeConnectionList) { - tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); - if ((tcon->ses) && (tcon->ses->server == server)) - tcon->tidStatus = CifsNeedReconnect; - } - read_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); /* do not want to be sending data on a socket we are freeing */ - down(&server->tcpSem); + mutex_lock(&server->srv_mutex); if (server->ssocket) { cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state, server->ssocket->flags)); @@ -188,21 +175,15 @@ cifs_reconnect(struct TCP_Server_Info *server) } } spin_unlock(&GlobalMid_Lock); - up(&server->tcpSem); + mutex_unlock(&server->srv_mutex); while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood)) { try_to_freeze(); - if (server->protocolType == IPV6) { - rc = ipv6_connect(&server->addr.sockAddr6, - &server->ssocket, server->noautotune); - } else { - rc = ipv4_connect(&server->addr.sockAddr, - &server->ssocket, - server->workstation_RFC1001_name, - server->server_RFC1001_name, - server->noblocksnd, server->noautotune); - } + if (server->addr.sockAddr6.sin6_family == AF_INET6) + rc = ipv6_connect(server); + else + rc = ipv4_connect(server); if (rc) { cFYI(1, ("reconnect error %d", rc)); msleep(3000); @@ -417,9 +398,14 @@ incomplete_rcv: msleep(1); /* minimum sleep to prevent looping allowing socket to clear and app threads to set tcpStatus CifsNeedReconnect if server hung */ - if (pdu_length < 4) + if (pdu_length < 4) { + iov.iov_base = (4 - pdu_length) + + (char *)smb_buffer; + iov.iov_len = pdu_length; + smb_msg.msg_control = NULL; + smb_msg.msg_controllen = 0; goto incomplete_rcv; - else + } else continue; } else if (length <= 0) { if (server->tcpStatus == CifsNew) { @@ -654,6 +640,11 @@ multi_t2_fnd: } } /* end while !EXITING */ + /* take it off the list, if it's not already */ + write_lock(&cifs_tcp_ses_lock); + list_del_init(&server->tcp_ses_list); + write_unlock(&cifs_tcp_ses_lock); + spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); @@ -686,29 +677,29 @@ multi_t2_fnd: if (smallbuf) /* no sense logging a debug message if NULL */ cifs_small_buf_release(smallbuf); - read_lock(&GlobalSMBSeslock); + /* + * BB: we shouldn't have to do any of this. It shouldn't be + * possible to exit from the thread with active SMB sessions + */ + read_lock(&cifs_tcp_ses_lock); if (list_empty(&server->pending_mid_q)) { /* loop through server session structures attached to this and mark them dead */ - list_for_each(tmp, &GlobalSMBSessionList) { - ses = - list_entry(tmp, struct cifsSesInfo, - cifsSessionList); - if (ses->server == server) { - ses->status = CifsExiting; - ses->server = NULL; - } + list_for_each(tmp, &server->smb_ses_list) { + ses = list_entry(tmp, struct cifsSesInfo, + smb_ses_list); + ses->status = CifsExiting; + ses->server = NULL; } - read_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); } else { /* although we can not zero the server struct pointer yet, since there are active requests which may depnd on them, mark the corresponding SMB sessions as exiting too */ - list_for_each(tmp, &GlobalSMBSessionList) { + list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, - cifsSessionList); - if (ses->server == server) - ses->status = CifsExiting; + smb_ses_list); + ses->status = CifsExiting; } spin_lock(&GlobalMid_Lock); @@ -723,7 +714,7 @@ multi_t2_fnd: } } spin_unlock(&GlobalMid_Lock); - read_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); /* 1/8th of sec is more than enough time for them to exit */ msleep(125); } @@ -745,14 +736,13 @@ multi_t2_fnd: if there are any pointing to this (e.g if a crazy root user tried to kill cifsd kernel thread explicitly this might happen) */ - write_lock(&GlobalSMBSeslock); - list_for_each(tmp, &GlobalSMBSessionList) { - ses = list_entry(tmp, struct cifsSesInfo, - cifsSessionList); - if (ses->server == server) - ses->server = NULL; + /* BB: This shouldn't be necessary, see above */ + read_lock(&cifs_tcp_ses_lock); + list_for_each(tmp, &server->smb_ses_list) { + ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); + ses->server = NULL; } - write_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); kfree(server->hostname); task_to_wake = xchg(&server->tsk, NULL); @@ -773,7 +763,7 @@ multi_t2_fnd: set_current_state(TASK_RUNNING); } - return 0; + module_put_and_exit(0); } /* extract the host portion of the UNC string */ @@ -833,8 +823,8 @@ cifs_parse_mount_options(char *options, const char *devname, /* 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->linux_uid = current->uid; /* current->euid instead? */ - vol->linux_gid = current->gid; + vol->linux_uid = current_uid(); /* use current_euid() instead? */ + vol->linux_gid = current_gid(); vol->dir_mode = S_IRWXUGO; /* 2767 perms indicate mandatory locking support */ vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP); @@ -1257,6 +1247,17 @@ cifs_parse_mount_options(char *options, const char *devname, if (vol->file_mode == (S_IALLUGO & ~(S_ISUID | S_IXGRP))) vol->file_mode = S_IALLUGO; + } else if (strnicmp(data, "forcemandatorylock", 9) == 0) { + /* will take the shorter form "forcemand" as well */ + /* This mount option will force use of mandatory + (DOS/Windows style) byte range locks, instead of + using posix advisory byte range locks, even if the + Unix extensions are available and posix locks would + be supported otherwise. If Unix extensions are not + negotiated this has no effect since mandatory locks + would be used (mandatory locks is all that those + those servers support) */ + vol->mand_lock = 1; } else if (strnicmp(data, "setuids", 7) == 0) { vol->setuids = 1; } else if (strnicmp(data, "nosetuids", 9) == 0) { @@ -1352,94 +1353,295 @@ cifs_parse_mount_options(char *options, const char *devname, return 0; } -static struct cifsSesInfo * -cifs_find_tcp_session(struct in_addr *target_ip_addr, - struct in6_addr *target_ip6_addr, - char *userName, struct TCP_Server_Info **psrvTcp) +static struct TCP_Server_Info * +cifs_find_tcp_session(struct sockaddr *addr) { struct list_head *tmp; - struct cifsSesInfo *ses; - - *psrvTcp = NULL; + struct TCP_Server_Info *server; + struct sockaddr_in *addr4 = (struct sockaddr_in *) addr; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr; + + write_lock(&cifs_tcp_ses_lock); + list_for_each(tmp, &cifs_tcp_ses_list) { + server = list_entry(tmp, struct TCP_Server_Info, + tcp_ses_list); + /* + * the demux thread can exit on its own while still in CifsNew + * so don't accept any sockets in that state. Since the + * tcpStatus never changes back to CifsNew it's safe to check + * for this without a lock. + */ + if (server->tcpStatus == CifsNew) + continue; - read_lock(&GlobalSMBSeslock); - list_for_each(tmp, &GlobalSMBSessionList) { - ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); - if (!ses->server) + if (addr->sa_family == AF_INET && + (addr4->sin_addr.s_addr != + server->addr.sockAddr.sin_addr.s_addr)) + continue; + else if (addr->sa_family == AF_INET6 && + memcmp(&server->addr.sockAddr6.sin6_addr, + &addr6->sin6_addr, sizeof(addr6->sin6_addr))) continue; - if (target_ip_addr && - ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr) - continue; - else if (target_ip6_addr && - memcmp(&ses->server->addr.sockAddr6.sin6_addr, - target_ip6_addr, sizeof(*target_ip6_addr))) - continue; - /* BB lock server and tcp session; increment use count here?? */ + ++server->srv_count; + write_unlock(&cifs_tcp_ses_lock); + cFYI(1, ("Existing tcp session with server found")); + return server; + } + write_unlock(&cifs_tcp_ses_lock); + return NULL; +} + +static void +cifs_put_tcp_session(struct TCP_Server_Info *server) +{ + struct task_struct *task; + + write_lock(&cifs_tcp_ses_lock); + if (--server->srv_count > 0) { + write_unlock(&cifs_tcp_ses_lock); + return; + } + + list_del_init(&server->tcp_ses_list); + write_unlock(&cifs_tcp_ses_lock); - /* found a match on the TCP session */ - *psrvTcp = ses->server; + spin_lock(&GlobalMid_Lock); + server->tcpStatus = CifsExiting; + spin_unlock(&GlobalMid_Lock); + + task = xchg(&server->tsk, NULL); + if (task) + force_sig(SIGKILL, task); +} - /* BB check if reconnection needed */ - if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) { - read_unlock(&GlobalSMBSeslock); - /* Found exact match on both TCP and - SMB sessions */ - return ses; +static struct TCP_Server_Info * +cifs_get_tcp_session(struct smb_vol *volume_info) +{ + struct TCP_Server_Info *tcp_ses = NULL; + struct sockaddr addr; + struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr; + struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; + int rc; + + memset(&addr, 0, sizeof(struct sockaddr)); + + if (volume_info->UNCip && volume_info->UNC) { + rc = cifs_inet_pton(AF_INET, volume_info->UNCip, + &sin_server->sin_addr.s_addr); + + if (rc <= 0) { + /* not ipv4 address, try ipv6 */ + rc = cifs_inet_pton(AF_INET6, volume_info->UNCip, + &sin_server6->sin6_addr.in6_u); + if (rc > 0) + addr.sa_family = AF_INET6; + } else { + addr.sa_family = AF_INET; } - /* else tcp and smb sessions need reconnection */ + + if (rc <= 0) { + /* we failed translating address */ + rc = -EINVAL; + goto out_err; + } + + cFYI(1, ("UNC: %s ip: %s", volume_info->UNC, + volume_info->UNCip)); + } else if (volume_info->UNCip) { + /* BB using ip addr as tcp_ses name to connect to the + DFS root below */ + cERROR(1, ("Connecting to DFS root not implemented yet")); + rc = -EINVAL; + goto out_err; + } else /* which tcp_sess DFS root would we conect to */ { + cERROR(1, + ("CIFS mount error: No UNC path (e.g. -o " + "unc=//192.168.1.100/public) specified")); + rc = -EINVAL; + goto out_err; } - read_unlock(&GlobalSMBSeslock); - return NULL; + /* see if we already have a matching tcp_ses */ + tcp_ses = cifs_find_tcp_session(&addr); + if (tcp_ses) + return tcp_ses; + + tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); + if (!tcp_ses) { + rc = -ENOMEM; + goto out_err; + } + + tcp_ses->hostname = extract_hostname(volume_info->UNC); + if (IS_ERR(tcp_ses->hostname)) { + rc = PTR_ERR(tcp_ses->hostname); + goto out_err; + } + + tcp_ses->noblocksnd = volume_info->noblocksnd; + tcp_ses->noautotune = volume_info->noautotune; + atomic_set(&tcp_ses->inFlight, 0); + init_waitqueue_head(&tcp_ses->response_q); + init_waitqueue_head(&tcp_ses->request_q); + INIT_LIST_HEAD(&tcp_ses->pending_mid_q); + mutex_init(&tcp_ses->srv_mutex); + memcpy(tcp_ses->workstation_RFC1001_name, + volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + memcpy(tcp_ses->server_RFC1001_name, + volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + tcp_ses->sequence_number = 0; + INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); + INIT_LIST_HEAD(&tcp_ses->smb_ses_list); + + /* + * at this point we are the only ones with the pointer + * to the struct since the kernel thread not created yet + * no need to spinlock this init of tcpStatus or srv_count + */ + tcp_ses->tcpStatus = CifsNew; + ++tcp_ses->srv_count; + + if (addr.sa_family == AF_INET6) { + cFYI(1, ("attempting ipv6 connect")); + /* BB should we allow ipv6 on port 139? */ + /* other OS never observed in Wild doing 139 with v6 */ + memcpy(&tcp_ses->addr.sockAddr6, sin_server6, + sizeof(struct sockaddr_in6)); + sin_server6->sin6_port = htons(volume_info->port); + rc = ipv6_connect(tcp_ses); + } else { + memcpy(&tcp_ses->addr.sockAddr, sin_server, + sizeof(struct sockaddr_in)); + sin_server->sin_port = htons(volume_info->port); + rc = ipv4_connect(tcp_ses); + } + if (rc < 0) { + cERROR(1, ("Error connecting to socket. Aborting operation")); + goto out_err; + } + + /* + * since we're in a cifs function already, we know that + * this will succeed. No need for try_module_get(). + */ + __module_get(THIS_MODULE); + tcp_ses->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, + tcp_ses, "cifsd"); + if (IS_ERR(tcp_ses->tsk)) { + rc = PTR_ERR(tcp_ses->tsk); + cERROR(1, ("error %d create cifsd thread", rc)); + module_put(THIS_MODULE); + goto out_err; + } + + /* thread spawned, put it on the list */ + write_lock(&cifs_tcp_ses_lock); + list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); + write_unlock(&cifs_tcp_ses_lock); + + return tcp_ses; + +out_err: + if (tcp_ses) { + kfree(tcp_ses->hostname); + if (tcp_ses->ssocket) + sock_release(tcp_ses->ssocket); + kfree(tcp_ses); + } + return ERR_PTR(rc); } -static struct cifsTconInfo * -find_unc(__be32 new_target_ip_addr, char *uncName, char *userName) +static struct cifsSesInfo * +cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) { struct list_head *tmp; - struct cifsTconInfo *tcon; - __be32 old_ip; - - read_lock(&GlobalSMBSeslock); + struct cifsSesInfo *ses; - list_for_each(tmp, &GlobalTreeConnectionList) { - cFYI(1, ("Next tcon")); - tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); - if (!tcon->ses || !tcon->ses->server) + write_lock(&cifs_tcp_ses_lock); + list_for_each(tmp, &server->smb_ses_list) { + ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); + if (strncmp(ses->userName, username, MAX_USERNAME_SIZE)) continue; - old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr; - cFYI(1, ("old ip addr: %x == new ip %x ?", - old_ip, new_target_ip_addr)); + ++ses->ses_count; + write_unlock(&cifs_tcp_ses_lock); + return ses; + } + write_unlock(&cifs_tcp_ses_lock); + return NULL; +} - if (old_ip != new_target_ip_addr) - continue; +static void +cifs_put_smb_ses(struct cifsSesInfo *ses) +{ + int xid; + struct TCP_Server_Info *server = ses->server; - /* BB lock tcon, server, tcp session and increment use count? */ - /* found a match on the TCP session */ - /* BB check if reconnection needed */ - cFYI(1, ("IP match, old UNC: %s new: %s", - tcon->treeName, uncName)); + write_lock(&cifs_tcp_ses_lock); + if (--ses->ses_count > 0) { + write_unlock(&cifs_tcp_ses_lock); + return; + } - if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE)) - continue; + list_del_init(&ses->smb_ses_list); + write_unlock(&cifs_tcp_ses_lock); + + if (ses->status == CifsGood) { + xid = GetXid(); + CIFSSMBLogoff(xid, ses); + _FreeXid(xid); + } + sesInfoFree(ses); + cifs_put_tcp_session(server); +} - cFYI(1, ("and old usr: %s new: %s", - tcon->treeName, uncName)); +static struct cifsTconInfo * +cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) +{ + struct list_head *tmp; + struct cifsTconInfo *tcon; - if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE)) + write_lock(&cifs_tcp_ses_lock); + list_for_each(tmp, &ses->tcon_list) { + tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); + if (tcon->tidStatus == CifsExiting) + continue; + if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE)) continue; - /* matched smb session (user name) */ - read_unlock(&GlobalSMBSeslock); + ++tcon->tc_count; + write_unlock(&cifs_tcp_ses_lock); return tcon; } - - read_unlock(&GlobalSMBSeslock); + write_unlock(&cifs_tcp_ses_lock); return NULL; } +static void +cifs_put_tcon(struct cifsTconInfo *tcon) +{ + int xid; + struct cifsSesInfo *ses = tcon->ses; + + write_lock(&cifs_tcp_ses_lock); + if (--tcon->tc_count > 0) { + write_unlock(&cifs_tcp_ses_lock); + return; + } + + list_del_init(&tcon->tcon_list); + write_unlock(&cifs_tcp_ses_lock); + + xid = GetXid(); + CIFSSMBTDis(xid, tcon); + _FreeXid(xid); + + DeleteTconOplockQEntries(tcon); + tconInfoFree(tcon); + cifs_put_smb_ses(ses); +} + int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, const struct nls_table *nls_codepage, unsigned int *pnum_referrals, @@ -1526,93 +1728,96 @@ static void rfc1002mangle(char *target, char *source, unsigned int length) static int -ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char *netbios_name, char *target_name, - bool noblocksnd, bool noautotune) +ipv4_connect(struct TCP_Server_Info *server) { int rc = 0; - int connected = 0; + bool connected = false; __be16 orig_port = 0; + struct socket *socket = server->ssocket; - if (*csocket == NULL) { + if (socket == NULL) { rc = sock_create_kern(PF_INET, SOCK_STREAM, - IPPROTO_TCP, csocket); + IPPROTO_TCP, &socket); if (rc < 0) { cERROR(1, ("Error %d creating socket", rc)); - *csocket = NULL; return rc; - } else { - /* BB other socket options to set KEEPALIVE, NODELAY? */ - cFYI(1, ("Socket created")); - (*csocket)->sk->sk_allocation = GFP_NOFS; - cifs_reclassify_socket4(*csocket); } + + /* BB other socket options to set KEEPALIVE, NODELAY? */ + cFYI(1, ("Socket created")); + server->ssocket = socket; + socket->sk->sk_allocation = GFP_NOFS; + cifs_reclassify_socket4(socket); } - psin_server->sin_family = AF_INET; - if (psin_server->sin_port) { /* user overrode default port */ - rc = (*csocket)->ops->connect(*csocket, - (struct sockaddr *) psin_server, - sizeof(struct sockaddr_in), 0); + /* user overrode default port */ + if (server->addr.sockAddr.sin_port) { + rc = socket->ops->connect(socket, (struct sockaddr *) + &server->addr.sockAddr, + sizeof(struct sockaddr_in), 0); if (rc >= 0) - connected = 1; + connected = true; } if (!connected) { /* save original port so we can retry user specified port later if fall back ports fail this time */ - orig_port = psin_server->sin_port; + orig_port = server->addr.sockAddr.sin_port; /* do not retry on the same port we just failed on */ - if (psin_server->sin_port != htons(CIFS_PORT)) { - psin_server->sin_port = htons(CIFS_PORT); - - rc = (*csocket)->ops->connect(*csocket, - (struct sockaddr *) psin_server, - sizeof(struct sockaddr_in), 0); + if (server->addr.sockAddr.sin_port != htons(CIFS_PORT)) { + server->addr.sockAddr.sin_port = htons(CIFS_PORT); + rc = socket->ops->connect(socket, + (struct sockaddr *) + &server->addr.sockAddr, + sizeof(struct sockaddr_in), 0); if (rc >= 0) - connected = 1; + connected = true; } } if (!connected) { - psin_server->sin_port = htons(RFC1001_PORT); - rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *) - psin_server, + server->addr.sockAddr.sin_port = htons(RFC1001_PORT); + rc = socket->ops->connect(socket, (struct sockaddr *) + &server->addr.sockAddr, sizeof(struct sockaddr_in), 0); if (rc >= 0) - connected = 1; + connected = true; } /* give up here - unless we want to retry on different protocol families some day */ if (!connected) { if (orig_port) - psin_server->sin_port = orig_port; + server->addr.sockAddr.sin_port = orig_port; cFYI(1, ("Error %d connecting to server via ipv4", rc)); - sock_release(*csocket); - *csocket = NULL; + sock_release(socket); + server->ssocket = NULL; return rc; } - /* Eventually check for other socket options to change from - the default. sock_setsockopt not used because it expects - user space buffer */ - cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx", - (*csocket)->sk->sk_sndbuf, - (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo)); - (*csocket)->sk->sk_rcvtimeo = 7 * HZ; - if (!noblocksnd) - (*csocket)->sk->sk_sndtimeo = 3 * HZ; + + + /* + * Eventually check for other socket options to change from + * the default. sock_setsockopt not used because it expects + * user space buffer + */ + socket->sk->sk_rcvtimeo = 7 * HZ; + socket->sk->sk_sndtimeo = 3 * HZ; /* make the bufsizes depend on wsize/rsize and max requests */ - if (noautotune) { - if ((*csocket)->sk->sk_sndbuf < (200 * 1024)) - (*csocket)->sk->sk_sndbuf = 200 * 1024; - if ((*csocket)->sk->sk_rcvbuf < (140 * 1024)) - (*csocket)->sk->sk_rcvbuf = 140 * 1024; + if (server->noautotune) { + if (socket->sk->sk_sndbuf < (200 * 1024)) + socket->sk->sk_sndbuf = 200 * 1024; + if (socket->sk->sk_rcvbuf < (140 * 1024)) + socket->sk->sk_rcvbuf = 140 * 1024; } + cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx", + socket->sk->sk_sndbuf, + socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo)); + /* send RFC1001 sessinit */ - if (psin_server->sin_port == htons(RFC1001_PORT)) { + if (server->addr.sockAddr.sin_port == htons(RFC1001_PORT)) { /* some servers require RFC1001 sessinit before sending negprot - BB check reconnection in case where second sessinit is sent but no second negprot */ @@ -1622,31 +1827,42 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, GFP_KERNEL); if (ses_init_buf) { ses_init_buf->trailer.session_req.called_len = 32; - if (target_name && (target_name[0] != 0)) { - rfc1002mangle(ses_init_buf->trailer.session_req.called_name, - target_name, 16); - } else { - rfc1002mangle(ses_init_buf->trailer.session_req.called_name, - DEFAULT_CIFS_CALLED_NAME, 16); - } + if (server->server_RFC1001_name && + server->server_RFC1001_name[0] != 0) + rfc1002mangle(ses_init_buf->trailer. + session_req.called_name, + server->server_RFC1001_name, + RFC1001_NAME_LEN_WITH_NULL); + else + rfc1002mangle(ses_init_buf->trailer. + session_req.called_name, + DEFAULT_CIFS_CALLED_NAME, + RFC1001_NAME_LEN_WITH_NULL); ses_init_buf->trailer.session_req.calling_len = 32; + /* calling name ends in null (byte 16) from old smb convention. */ - if (netbios_name && (netbios_name[0] != 0)) { - rfc1002mangle(ses_init_buf->trailer.session_req.calling_name, - netbios_name, 16); - } else { - rfc1002mangle(ses_init_buf->trailer.session_req.calling_name, - "LINUX_CIFS_CLNT", 16); - } + if (server->workstation_RFC1001_name && + server->workstation_RFC1001_name[0] != 0) + rfc1002mangle(ses_init_buf->trailer. + session_req.calling_name, + server->workstation_RFC1001_name, + RFC1001_NAME_LEN_WITH_NULL); + else + rfc1002mangle(ses_init_buf->trailer. + session_req.calling_name, + "LINUX_CIFS_CLNT", + RFC1001_NAME_LEN_WITH_NULL); + ses_init_buf->trailer.session_req.scope1 = 0; ses_init_buf->trailer.session_req.scope2 = 0; smb_buf = (struct smb_hdr *)ses_init_buf; /* sizeof RFC1002_SESSION_REQUEST with no scope */ smb_buf->smb_buf_length = 0x81000044; - rc = smb_send(*csocket, smb_buf, 0x44, - (struct sockaddr *)psin_server, noblocksnd); + rc = smb_send(socket, smb_buf, 0x44, + (struct sockaddr *) &server->addr.sockAddr, + server->noblocksnd); kfree(ses_init_buf); msleep(1); /* RFC1001 layer in at least one server requires very short break before negprot @@ -1666,79 +1882,81 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, } static int -ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket, - bool noblocksnd) +ipv6_connect(struct TCP_Server_Info *server) { int rc = 0; - int connected = 0; + bool connected = false; __be16 orig_port = 0; + struct socket *socket = server->ssocket; - if (*csocket == NULL) { + if (socket == NULL) { rc = sock_create_kern(PF_INET6, SOCK_STREAM, - IPPROTO_TCP, csocket); + IPPROTO_TCP, &socket); if (rc < 0) { cERROR(1, ("Error %d creating ipv6 socket", rc)); - *csocket = NULL; + socket = NULL; return rc; - } else { - /* BB other socket options to set KEEPALIVE, NODELAY? */ - cFYI(1, ("ipv6 Socket created")); - (*csocket)->sk->sk_allocation = GFP_NOFS; - cifs_reclassify_socket6(*csocket); } - } - psin_server->sin6_family = AF_INET6; + /* BB other socket options to set KEEPALIVE, NODELAY? */ + cFYI(1, ("ipv6 Socket created")); + server->ssocket = socket; + socket->sk->sk_allocation = GFP_NOFS; + cifs_reclassify_socket6(socket); + } - if (psin_server->sin6_port) { /* user overrode default port */ - rc = (*csocket)->ops->connect(*csocket, - (struct sockaddr *) psin_server, + /* user overrode default port */ + if (server->addr.sockAddr6.sin6_port) { + rc = socket->ops->connect(socket, + (struct sockaddr *) &server->addr.sockAddr6, sizeof(struct sockaddr_in6), 0); if (rc >= 0) - connected = 1; + connected = true; } if (!connected) { /* save original port so we can retry user specified port later if fall back ports fail this time */ - orig_port = psin_server->sin6_port; + orig_port = server->addr.sockAddr6.sin6_port; /* do not retry on the same port we just failed on */ - if (psin_server->sin6_port != htons(CIFS_PORT)) { - psin_server->sin6_port = htons(CIFS_PORT); - - rc = (*csocket)->ops->connect(*csocket, - (struct sockaddr *) psin_server, + if (server->addr.sockAddr6.sin6_port != htons(CIFS_PORT)) { + server->addr.sockAddr6.sin6_port = htons(CIFS_PORT); + rc = socket->ops->connect(socket, (struct sockaddr *) + &server->addr.sockAddr6, sizeof(struct sockaddr_in6), 0); if (rc >= 0) - connected = 1; + connected = true; } } if (!connected) { - psin_server->sin6_port = htons(RFC1001_PORT); - rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *) - psin_server, sizeof(struct sockaddr_in6), 0); + server->addr.sockAddr6.sin6_port = htons(RFC1001_PORT); + rc = socket->ops->connect(socket, (struct sockaddr *) + &server->addr.sockAddr6, + sizeof(struct sockaddr_in6), 0); if (rc >= 0) - connected = 1; + connected = true; } /* give up here - unless we want to retry on different protocol families some day */ if (!connected) { if (orig_port) - psin_server->sin6_port = orig_port; + server->addr.sockAddr6.sin6_port = orig_port; cFYI(1, ("Error %d connecting to server via ipv6", rc)); - sock_release(*csocket); - *csocket = NULL; + sock_release(socket); + server->ssocket = NULL; return rc; } - /* Eventually check for other socket options to change from - the default. sock_setsockopt not used because it expects - user space buffer */ - (*csocket)->sk->sk_rcvtimeo = 7 * HZ; - if (!noblocksnd) - (*csocket)->sk->sk_sndtimeo = 3 * HZ; + /* + * Eventually check for other socket options to change from + * the default. sock_setsockopt not used because it expects + * user space buffer + */ + socket->sk->sk_rcvtimeo = 7 * HZ; + socket->sk->sk_sndtimeo = 3 * HZ; + server->ssocket = socket; return rc; } @@ -1876,14 +2094,92 @@ convert_delimiter(char *path, char delim) } } -static void -kill_cifsd(struct TCP_Server_Info *server) +static void setup_cifs_sb(struct smb_vol *pvolume_info, + struct cifs_sb_info *cifs_sb) { - struct task_struct *task; - - task = xchg(&server->tsk, NULL); - if (task) - force_sig(SIGKILL, task); + if (pvolume_info->rsize > CIFSMaxBufSize) { + cERROR(1, ("rsize %d too large, using MaxBufSize", + pvolume_info->rsize)); + cifs_sb->rsize = CIFSMaxBufSize; + } else if ((pvolume_info->rsize) && + (pvolume_info->rsize <= CIFSMaxBufSize)) + cifs_sb->rsize = pvolume_info->rsize; + else /* default */ + cifs_sb->rsize = CIFSMaxBufSize; + + if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { + cERROR(1, ("wsize %d too large, using 4096 instead", + pvolume_info->wsize)); + cifs_sb->wsize = 4096; + } else if (pvolume_info->wsize) + cifs_sb->wsize = pvolume_info->wsize; + else + cifs_sb->wsize = min_t(const int, + PAGEVEC_SIZE * PAGE_CACHE_SIZE, + 127*1024); + /* old default of CIFSMaxBufSize was too small now + that SMB Write2 can send multiple pages in kvec. + RFC1001 does not describe what happens when frame + bigger than 128K is sent so use that as max in + conjunction with 52K kvec constraint on arch with 4K + page size */ + + if (cifs_sb->rsize < 2048) { + cifs_sb->rsize = 2048; + /* Windows ME may prefer this */ + cFYI(1, ("readsize set to minimum: 2048")); + } + /* calculate prepath */ + cifs_sb->prepath = pvolume_info->prepath; + if (cifs_sb->prepath) { + cifs_sb->prepathlen = strlen(cifs_sb->prepath); + /* we can not convert the / to \ in the path + separators in the prefixpath yet because we do not + know (until reset_cifs_unix_caps is called later) + whether POSIX PATH CAP is available. We normalize + the / to \ after reset_cifs_unix_caps is called */ + pvolume_info->prepath = NULL; + } else + cifs_sb->prepathlen = 0; + 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; + cFYI(1, ("file mode: 0x%x dir mode: 0x%x", + cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); + + 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->server_ino) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; + if (pvolume_info->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->mand_lock) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL; + if (pvolume_info->cifs_acl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; + 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->direct_io) { + cFYI(1, ("mounting share using direct i/o")); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; + } + + if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) + cERROR(1, ("mount option dynperm ignored if cifsacl " + "mount option supported")); } int @@ -1892,32 +2188,30 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, { int rc = 0; int xid; - int address_type = AF_INET; - struct socket *csocket = NULL; - struct sockaddr_in sin_server; - struct sockaddr_in6 sin_server6; - struct smb_vol volume_info; + struct smb_vol *volume_info; struct cifsSesInfo *pSesInfo = NULL; - struct cifsSesInfo *existingCifsSes = NULL; struct cifsTconInfo *tcon = NULL; struct TCP_Server_Info *srvTcp = NULL; xid = GetXid(); -/* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ + volume_info = kzalloc(sizeof(struct smb_vol), GFP_KERNEL); + if (!volume_info) { + rc = -ENOMEM; + goto out; + } - memset(&volume_info, 0, sizeof(struct smb_vol)); - if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { + if (cifs_parse_mount_options(mount_data, devname, volume_info)) { rc = -EINVAL; goto out; } - if (volume_info.nullauth) { + if (volume_info->nullauth) { cFYI(1, ("null user")); - volume_info.username = ""; - } else if (volume_info.username) { + volume_info->username = ""; + } else if (volume_info->username) { /* BB fixme parse for domain name here */ - cFYI(1, ("Username: %s", volume_info.username)); + cFYI(1, ("Username: %s", volume_info->username)); } else { cifserror("No username specified"); /* In userspace mount helper we can get user name from alternate @@ -1926,150 +2220,40 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, goto out; } - if (volume_info.UNCip && volume_info.UNC) { - rc = cifs_inet_pton(AF_INET, volume_info.UNCip, - &sin_server.sin_addr.s_addr); - - if (rc <= 0) { - /* not ipv4 address, try ipv6 */ - rc = cifs_inet_pton(AF_INET6, volume_info.UNCip, - &sin_server6.sin6_addr.in6_u); - if (rc > 0) - address_type = AF_INET6; - } else { - address_type = AF_INET; - } - - if (rc <= 0) { - /* we failed translating address */ - rc = -EINVAL; - goto out; - } - - cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip)); - /* success */ - rc = 0; - } else if (volume_info.UNCip) { - /* BB using ip addr as server name to connect to the - DFS root below */ - cERROR(1, ("Connecting to DFS root not implemented yet")); - rc = -EINVAL; - goto out; - } else /* which servers DFS root would we conect to */ { - cERROR(1, - ("CIFS mount error: No UNC path (e.g. -o " - "unc=//192.168.1.100/public) specified")); - rc = -EINVAL; - goto out; - } /* this is needed for ASCII cp to Unicode converts */ - if (volume_info.iocharset == NULL) { + if (volume_info->iocharset == NULL) { cifs_sb->local_nls = load_nls_default(); /* load_nls_default can not return null */ } else { - cifs_sb->local_nls = load_nls(volume_info.iocharset); + cifs_sb->local_nls = load_nls(volume_info->iocharset); if (cifs_sb->local_nls == NULL) { cERROR(1, ("CIFS mount error: iocharset %s not found", - volume_info.iocharset)); + volume_info->iocharset)); rc = -ELIBACC; goto out; } } - if (address_type == AF_INET) - existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr, - NULL /* no ipv6 addr */, - volume_info.username, &srvTcp); - else if (address_type == AF_INET6) { - cFYI(1, ("looking for ipv6 address")); - existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */, - &sin_server6.sin6_addr, - volume_info.username, &srvTcp); - } else { - rc = -EINVAL; + /* get a reference to a tcp session */ + srvTcp = cifs_get_tcp_session(volume_info); + if (IS_ERR(srvTcp)) { + rc = PTR_ERR(srvTcp); goto out; } - if (srvTcp) { - cFYI(1, ("Existing tcp session with server found")); - } else { /* create socket */ - if (volume_info.port) - sin_server.sin_port = htons(volume_info.port); - else - sin_server.sin_port = 0; - if (address_type == AF_INET6) { - cFYI(1, ("attempting ipv6 connect")); - /* BB should we allow ipv6 on port 139? */ - /* other OS never observed in Wild doing 139 with v6 */ - rc = ipv6_connect(&sin_server6, &csocket, - volume_info.noblocksnd); - } else - rc = ipv4_connect(&sin_server, &csocket, - volume_info.source_rfc1001_name, - volume_info.target_rfc1001_name, - volume_info.noblocksnd, - volume_info.noautotune); - if (rc < 0) { - cERROR(1, ("Error connecting to IPv4 socket. " - "Aborting operation")); - if (csocket != NULL) - sock_release(csocket); - goto out; - } - - srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); - if (!srvTcp) { - rc = -ENOMEM; - sock_release(csocket); - goto out; - } else { - srvTcp->noblocksnd = volume_info.noblocksnd; - srvTcp->noautotune = volume_info.noautotune; - memcpy(&srvTcp->addr.sockAddr, &sin_server, - sizeof(struct sockaddr_in)); - atomic_set(&srvTcp->inFlight, 0); - /* BB Add code for ipv6 case too */ - srvTcp->ssocket = csocket; - srvTcp->protocolType = IPV4; - srvTcp->hostname = extract_hostname(volume_info.UNC); - if (IS_ERR(srvTcp->hostname)) { - rc = PTR_ERR(srvTcp->hostname); - sock_release(csocket); - goto out; - } - init_waitqueue_head(&srvTcp->response_q); - init_waitqueue_head(&srvTcp->request_q); - INIT_LIST_HEAD(&srvTcp->pending_mid_q); - /* at this point we are the only ones with the pointer - to the struct since the kernel thread not created yet - so no need to spinlock this init of tcpStatus */ - srvTcp->tcpStatus = CifsNew; - init_MUTEX(&srvTcp->tcpSem); - srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd"); - if (IS_ERR(srvTcp->tsk)) { - rc = PTR_ERR(srvTcp->tsk); - cERROR(1, ("error %d create cifsd thread", rc)); - srvTcp->tsk = NULL; - sock_release(csocket); - kfree(srvTcp->hostname); - goto out; - } - rc = 0; - memcpy(srvTcp->workstation_RFC1001_name, - volume_info.source_rfc1001_name, 16); - memcpy(srvTcp->server_RFC1001_name, - volume_info.target_rfc1001_name, 16); - srvTcp->sequence_number = 0; - } - } - - if (existingCifsSes) { - pSesInfo = existingCifsSes; + pSesInfo = cifs_find_smb_ses(srvTcp, volume_info->username); + if (pSesInfo) { cFYI(1, ("Existing smb sess found (status=%d)", pSesInfo->status)); + /* + * The existing SMB session already has a reference to srvTcp, + * so we can put back the extra one we got before + */ + cifs_put_tcp_session(srvTcp); + down(&pSesInfo->sesSem); - if (pSesInfo->status == CifsNeedReconnect) { + if (pSesInfo->need_reconnect) { cFYI(1, ("Session needs reconnect")); rc = cifs_setup_session(xid, pSesInfo, cifs_sb->local_nls); @@ -2078,187 +2262,117 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, } else if (!rc) { cFYI(1, ("Existing smb sess not found")); pSesInfo = sesInfoAlloc(); - if (pSesInfo == NULL) + if (pSesInfo == NULL) { rc = -ENOMEM; - else { - pSesInfo->server = srvTcp; - sprintf(pSesInfo->serverName, "%u.%u.%u.%u", - NIPQUAD(sin_server.sin_addr.s_addr)); + goto mount_fail_check; } - if (!rc) { - /* volume_info.password freed at unmount */ - if (volume_info.password) { - pSesInfo->password = volume_info.password; - /* set to NULL to prevent freeing on exit */ - volume_info.password = NULL; - } - if (volume_info.username) - strncpy(pSesInfo->userName, - volume_info.username, - MAX_USERNAME_SIZE); - if (volume_info.domainname) { - int len = strlen(volume_info.domainname); - pSesInfo->domainName = - kmalloc(len + 1, GFP_KERNEL); - if (pSesInfo->domainName) - strcpy(pSesInfo->domainName, - volume_info.domainname); + /* new SMB session uses our srvTcp ref */ + pSesInfo->server = srvTcp; + if (srvTcp->addr.sockAddr6.sin6_family == AF_INET6) + sprintf(pSesInfo->serverName, "%pI6", + &srvTcp->addr.sockAddr6.sin6_addr); + else + sprintf(pSesInfo->serverName, "%pI4", + &srvTcp->addr.sockAddr.sin_addr.s_addr); + + write_lock(&cifs_tcp_ses_lock); + list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list); + write_unlock(&cifs_tcp_ses_lock); + + /* volume_info->password freed at unmount */ + if (volume_info->password) { + pSesInfo->password = kstrdup(volume_info->password, + GFP_KERNEL); + if (!pSesInfo->password) { + rc = -ENOMEM; + goto mount_fail_check; } - pSesInfo->linux_uid = volume_info.linux_uid; - pSesInfo->overrideSecFlg = volume_info.secFlg; - down(&pSesInfo->sesSem); - /* BB FIXME need to pass vol->secFlgs BB */ - rc = cifs_setup_session(xid, pSesInfo, - cifs_sb->local_nls); - up(&pSesInfo->sesSem); - if (!rc) - atomic_inc(&srvTcp->socketUseCount); } + if (volume_info->username) + strncpy(pSesInfo->userName, volume_info->username, + MAX_USERNAME_SIZE); + if (volume_info->domainname) { + int len = strlen(volume_info->domainname); + pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL); + if (pSesInfo->domainName) + strcpy(pSesInfo->domainName, + volume_info->domainname); + } + pSesInfo->linux_uid = volume_info->linux_uid; + pSesInfo->overrideSecFlg = volume_info->secFlg; + down(&pSesInfo->sesSem); + + /* BB FIXME need to pass vol->secFlgs BB */ + rc = cifs_setup_session(xid, pSesInfo, + cifs_sb->local_nls); + up(&pSesInfo->sesSem); } /* search for existing tcon to this server share */ if (!rc) { - if (volume_info.rsize > CIFSMaxBufSize) { - cERROR(1, ("rsize %d too large, using MaxBufSize", - volume_info.rsize)); - cifs_sb->rsize = CIFSMaxBufSize; - } else if ((volume_info.rsize) && - (volume_info.rsize <= CIFSMaxBufSize)) - cifs_sb->rsize = volume_info.rsize; - else /* default */ - cifs_sb->rsize = CIFSMaxBufSize; - - if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { - cERROR(1, ("wsize %d too large, using 4096 instead", - volume_info.wsize)); - cifs_sb->wsize = 4096; - } else if (volume_info.wsize) - cifs_sb->wsize = volume_info.wsize; - else - cifs_sb->wsize = - min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE, - 127*1024); - /* old default of CIFSMaxBufSize was too small now - that SMB Write2 can send multiple pages in kvec. - RFC1001 does not describe what happens when frame - bigger than 128K is sent so use that as max in - conjunction with 52K kvec constraint on arch with 4K - page size */ - - if (cifs_sb->rsize < 2048) { - cifs_sb->rsize = 2048; - /* Windows ME may prefer this */ - cFYI(1, ("readsize set to minimum: 2048")); - } - /* calculate prepath */ - cifs_sb->prepath = volume_info.prepath; - if (cifs_sb->prepath) { - cifs_sb->prepathlen = strlen(cifs_sb->prepath); - /* we can not convert the / to \ in the path - separators in the prefixpath yet because we do not - know (until reset_cifs_unix_caps is called later) - whether POSIX PATH CAP is available. We normalize - the / to \ after reset_cifs_unix_caps is called */ - volume_info.prepath = NULL; - } else - cifs_sb->prepathlen = 0; - cifs_sb->mnt_uid = volume_info.linux_uid; - cifs_sb->mnt_gid = volume_info.linux_gid; - cifs_sb->mnt_file_mode = volume_info.file_mode; - cifs_sb->mnt_dir_mode = volume_info.dir_mode; - cFYI(1, ("file mode: 0x%x dir mode: 0x%x", - cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); - - if (volume_info.noperm) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; - if (volume_info.setuids) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; - if (volume_info.server_ino) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; - if (volume_info.remap) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; - if (volume_info.no_xattr) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; - if (volume_info.sfu_emul) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; - if (volume_info.nobrl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; - if (volume_info.cifs_acl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; - if (volume_info.override_uid) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; - if (volume_info.override_gid) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; - if (volume_info.dynperm) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; - if (volume_info.direct_io) { - cFYI(1, ("mounting share using direct i/o")); - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; - } + setup_cifs_sb(volume_info, cifs_sb); - if ((volume_info.cifs_acl) && (volume_info.dynperm)) - cERROR(1, ("mount option dynperm ignored if cifsacl " - "mount option supported")); - - tcon = - find_unc(sin_server.sin_addr.s_addr, volume_info.UNC, - volume_info.username); + tcon = cifs_find_tcon(pSesInfo, volume_info->UNC); if (tcon) { cFYI(1, ("Found match on UNC path")); - /* we can have only one retry value for a connection - to a share so for 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->local_lease = volume_info.local_lease; - if (tcon->seal != volume_info.seal) + /* existing tcon already has a reference */ + cifs_put_smb_ses(pSesInfo); + if (tcon->seal != volume_info->seal) cERROR(1, ("transport encryption setting " "conflicts with existing tid")); } else { tcon = tconInfoAlloc(); - if (tcon == NULL) + if (tcon == NULL) { rc = -ENOMEM; - else { - /* check for null share name ie connecting to - * dfs root */ - - /* BB check if this works for exactly length - * three strings */ - if ((strchr(volume_info.UNC + 3, '\\') == NULL) - && (strchr(volume_info.UNC + 3, '/') == - NULL)) { -/* rc = connect_to_dfs_path(xid, pSesInfo, - "", cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR);*/ - cFYI(1, ("DFS root not supported")); - rc = -ENODEV; - goto out; - } else { - /* BB Do we need to wrap sesSem around - * this TCon call and Unix SetFS as - * we do on SessSetup and reconnect? */ - rc = CIFSTCon(xid, pSesInfo, - volume_info.UNC, - tcon, cifs_sb->local_nls); - cFYI(1, ("CIFS Tcon rc = %d", rc)); - if (volume_info.nodfs) { - tcon->Flags &= - ~SMB_SHARE_IS_IN_DFS; - cFYI(1, ("DFS disabled (%d)", - tcon->Flags)); - } + goto mount_fail_check; + } + + tcon->ses = pSesInfo; + if (volume_info->password) { + tcon->password = kstrdup(volume_info->password, + GFP_KERNEL); + if (!tcon->password) { + rc = -ENOMEM; + goto mount_fail_check; } - if (!rc) { - atomic_inc(&pSesInfo->inUse); - tcon->retry = volume_info.retry; - tcon->nocase = volume_info.nocase; - tcon->seal = volume_info.seal; + } + + /* check for null share name ie connect to dfs root */ + if ((strchr(volume_info->UNC + 3, '\\') == NULL) + && (strchr(volume_info->UNC + 3, '/') == NULL)) { + /* rc = connect_to_dfs_path(...) */ + cFYI(1, ("DFS root not supported")); + rc = -ENODEV; + goto mount_fail_check; + } else { + /* BB Do we need to wrap sesSem around + * this TCon call and Unix SetFS as + * we do on SessSetup and reconnect? */ + rc = CIFSTCon(xid, pSesInfo, volume_info->UNC, + tcon, cifs_sb->local_nls); + cFYI(1, ("CIFS Tcon rc = %d", rc)); + if (volume_info->nodfs) { + tcon->Flags &= ~SMB_SHARE_IS_IN_DFS; + cFYI(1, ("DFS disabled (%d)", + tcon->Flags)); } } - } + if (rc) + goto mount_fail_check; + tcon->seal = volume_info->seal; + write_lock(&cifs_tcp_ses_lock); + list_add(&tcon->tcon_list, &pSesInfo->tcon_list); + write_unlock(&cifs_tcp_ses_lock); + } + + /* we can have only one retry value for a connection + to a share so for 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->local_lease = volume_info->local_lease; } if (pSesInfo) { if (pSesInfo->capabilities & CAP_LARGE_FILES) { @@ -2270,93 +2384,66 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* BB FIXME fix time_gran to be larger for LANMAN sessions */ sb->s_time_gran = 100; -/* on error free sesinfo and tcon struct if needed */ +mount_fail_check: + /* on error free sesinfo and tcon struct if needed */ if (rc) { - /* if session setup failed, use count is zero but - we still need to free cifsd thread */ - if (atomic_read(&srvTcp->socketUseCount) == 0) { - spin_lock(&GlobalMid_Lock); - srvTcp->tcpStatus = CifsExiting; - spin_unlock(&GlobalMid_Lock); - kill_cifsd(srvTcp); - } - /* If find_unc succeeded then rc == 0 so we can not end */ - if (tcon) /* up accidently freeing someone elses tcon struct */ - tconInfoFree(tcon); - if (existingCifsSes == NULL) { - if (pSesInfo) { - if ((pSesInfo->server) && - (pSesInfo->status == CifsGood)) { - int temp_rc; - temp_rc = CIFSSMBLogoff(xid, pSesInfo); - /* if the socketUseCount is now zero */ - if ((temp_rc == -ESHUTDOWN) && - (pSesInfo->server)) - kill_cifsd(pSesInfo->server); - } else { - cFYI(1, ("No session or bad tcon")); - if (pSesInfo->server) { - spin_lock(&GlobalMid_Lock); - srvTcp->tcpStatus = CifsExiting; - spin_unlock(&GlobalMid_Lock); - kill_cifsd(pSesInfo->server); - } - } - sesInfoFree(pSesInfo); - /* pSesInfo = NULL; */ - } - } - } else { - atomic_inc(&tcon->useCount); - cifs_sb->tcon = tcon; - tcon->ses = pSesInfo; - - /* do not care if following two calls succeed - informational */ - if (!tcon->ipc) { - CIFSSMBQFSDeviceInfo(xid, tcon); - CIFSSMBQFSAttributeInfo(xid, tcon); - } - - /* tell server which Unix caps we support */ - if (tcon->ses->capabilities & CAP_UNIX) - /* reset of caps checks mount to see if unix extensions - disabled for just this mount */ - reset_cifs_unix_caps(xid, tcon, sb, &volume_info); + /* If find_unc succeeded then rc == 0 so we can not end */ + /* up accidently freeing someone elses tcon struct */ + if (tcon) + cifs_put_tcon(tcon); + else if (pSesInfo) + cifs_put_smb_ses(pSesInfo); else - tcon->unix_ext = 0; /* server does not support them */ + cifs_put_tcp_session(srvTcp); + goto out; + } + cifs_sb->tcon = tcon; + + /* do not care if following two calls succeed - informational */ + if (!tcon->ipc) { + CIFSSMBQFSDeviceInfo(xid, tcon); + CIFSSMBQFSAttributeInfo(xid, tcon); + } + + /* tell server which Unix caps we support */ + if (tcon->ses->capabilities & CAP_UNIX) + /* reset of caps checks mount to see if unix extensions + disabled for just this mount */ + reset_cifs_unix_caps(xid, tcon, sb, volume_info); + else + tcon->unix_ext = 0; /* server does not support them */ - /* convert forward to back slashes in prepath here if needed */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) - convert_delimiter(cifs_sb->prepath, - CIFS_DIR_SEP(cifs_sb)); + /* convert forward to back slashes in prepath here if needed */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) + convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); - if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { - cifs_sb->rsize = 1024 * 127; - cFYI(DBG2, - ("no very large read support, rsize now 127K")); - } - if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) - cifs_sb->wsize = min(cifs_sb->wsize, - (tcon->ses->server->maxBuf - - MAX_CIFS_HDR_SIZE)); - if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) - cifs_sb->rsize = min(cifs_sb->rsize, - (tcon->ses->server->maxBuf - - MAX_CIFS_HDR_SIZE)); + if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { + cifs_sb->rsize = 1024 * 127; + cFYI(DBG2, ("no very large read support, rsize now 127K")); } + if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) + cifs_sb->wsize = min(cifs_sb->wsize, + (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); + if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) + cifs_sb->rsize = min(cifs_sb->rsize, + (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); - /* volume_info.password is freed above when existing session found + /* volume_info->password is freed above when existing session found (in which case it is not needed anymore) but when new sesion is created the password ptr is put in the new session structure (in which case the password will be freed at unmount time) */ out: /* zero out password before freeing */ - if (volume_info.password != NULL) { - memset(volume_info.password, 0, strlen(volume_info.password)); - kfree(volume_info.password); + if (volume_info) { + if (volume_info->password != NULL) { + memset(volume_info->password, 0, + strlen(volume_info->password)); + kfree(volume_info->password); + } + kfree(volume_info->UNC); + kfree(volume_info->prepath); + kfree(volume_info); } - kfree(volume_info.UNC); - kfree(volume_info.prepath); FreeXid(xid); return rc; } @@ -2507,7 +2594,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, __u16 action = le16_to_cpu(pSMBr->resp.Action); __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) - cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */ + cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */ ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (little endian) */ cFYI(1, ("UID = %d ", ses->Suid)); @@ -2653,13 +2740,11 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, len)); } } else { - cERROR(1, - (" Security Blob Length extends beyond " + cERROR(1, ("Security Blob Length extends beyond " "end of SMB")); } } else { - cERROR(1, - (" Invalid Word count %d: ", + cERROR(1, ("Invalid Word count %d: ", smb_buffer_response->WordCount)); rc = -EIO; } @@ -2817,7 +2902,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) - cFYI(1, (" Guest login")); + cFYI(1, ("Guest login")); /* Do we want to set anything in SesInfo struct when guest login? */ bcc_ptr = pByteArea(smb_buffer_response); @@ -2825,8 +2910,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr; if (SecurityBlob2->MessageType != NtLmChallenge) { - cFYI(1, - ("Unexpected NTLMSSP message type received %d", + cFYI(1, ("Unexpected NTLMSSP message type received %d", SecurityBlob2->MessageType)); } else if (ses) { ses->Suid = smb_buffer_response->Uid; /* UID left in le format */ @@ -2998,8 +3082,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, cERROR(1, ("No session structure passed in.")); } } else { - cERROR(1, - (" Invalid Word count %d:", + cERROR(1, ("Invalid Word count %d:", smb_buffer_response->WordCount)); rc = -EIO; } @@ -3238,7 +3321,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, __u16 action = le16_to_cpu(pSMBr->resp.Action); __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) - cFYI(1, (" Guest login")); /* BB Should we set anything + cFYI(1, ("Guest login")); /* BB Should we set anything in SesInfo struct ? */ /* if (SecurityBlob2->MessageType != NtLm??) { cFYI("Unexpected message type on auth response is %d")); @@ -3461,12 +3544,14 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, NTLMv2 password here) */ #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((extended_security & CIFSSEC_MAY_LANMAN) && - (ses->server->secType == LANMAN)) - calc_lanman_hash(ses, bcc_ptr); + (ses->server->secType == LANMAN)) + calc_lanman_hash(tcon->password, ses->server->cryptKey, + ses->server->secMode & + SECMODE_PW_ENCRYPT ? true : false, + bcc_ptr); else #endif /* CIFS_WEAK_PW_HASH */ - SMBNTencrypt(ses->password, - ses->server->cryptKey, + SMBNTencrypt(tcon->password, ses->server->cryptKey, bcc_ptr); bcc_ptr += CIFS_SESS_KEY_SIZE; @@ -3513,6 +3598,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, /* above now done in SendReceive */ if ((rc == 0) && (tcon != NULL)) { tcon->tidStatus = CifsGood; + tcon->need_reconnect = false; tcon->tid = smb_buffer_response->Tid; bcc_ptr = pByteArea(smb_buffer_response); length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); @@ -3584,48 +3670,17 @@ int cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) { int rc = 0; - int xid; - struct cifsSesInfo *ses = NULL; char *tmp; - xid = GetXid(); - - if (cifs_sb->tcon) { - ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/ - rc = CIFSSMBTDis(xid, cifs_sb->tcon); - if (rc == -EBUSY) { - FreeXid(xid); - return 0; - } - DeleteTconOplockQEntries(cifs_sb->tcon); - tconInfoFree(cifs_sb->tcon); - if ((ses) && (ses->server)) { - /* save off task so we do not refer to ses later */ - cFYI(1, ("About to do SMBLogoff ")); - rc = CIFSSMBLogoff(xid, ses); - if (rc == -EBUSY) { - FreeXid(xid); - return 0; - } else if (rc == -ESHUTDOWN) { - cFYI(1, ("Waking up socket by sending signal")); - if (ses->server) - kill_cifsd(ses->server); - rc = 0; - } /* else - we have an smb session - left on this socket do not kill cifsd */ - } else - cFYI(1, ("No session or bad tcon")); - } + if (cifs_sb->tcon) + cifs_put_tcon(cifs_sb->tcon); cifs_sb->tcon = NULL; tmp = cifs_sb->prepath; cifs_sb->prepathlen = 0; cifs_sb->prepath = NULL; kfree(tmp); - if (ses) - sesInfoFree(ses); - FreeXid(xid); return rc; } @@ -3741,6 +3796,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, cFYI(1, ("CIFS Session Established successfully")); spin_lock(&GlobalMid_Lock); pSesInfo->status = CifsGood; + pSesInfo->need_reconnect = false; spin_unlock(&GlobalMid_Lock); } diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index e962e75e6f7b..838d9c720a5c 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -235,11 +235,11 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64) current->fsuid; + args.uid = (__u64) current_fsuid(); if (inode->i_mode & S_ISGID) args.gid = (__u64) inode->i_gid; else - args.gid = (__u64) current->fsgid; + args.gid = (__u64) current_fsgid(); } else { args.uid = NO_CHANGE_64; args.gid = NO_CHANGE_64; @@ -271,13 +271,13 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, if ((oplock & CIFS_CREATE_ACTION) && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) { - newinode->i_uid = current->fsuid; + newinode->i_uid = current_fsuid(); if (inode->i_mode & S_ISGID) newinode->i_gid = inode->i_gid; else newinode->i_gid = - current->fsgid; + current_fsgid(); } } } @@ -375,8 +375,8 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, .device = device_number, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64) current->fsuid; - args.gid = (__u64) current->fsgid; + args.uid = (__u64) current_fsuid(); + args.gid = (__u64) current_fsgid(); } else { args.uid = NO_CHANGE_64; args.gid = NO_CHANGE_64; @@ -483,7 +483,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, xid = GetXid(); - cFYI(1, (" parent inode = 0x%p name is: %s and dentry = 0x%p", + cFYI(1, ("parent inode = 0x%p name is: %s and dentry = 0x%p", parent_dir_inode, direntry->d_name.name, direntry)); /* check whether path exists */ @@ -515,12 +515,11 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, } if (direntry->d_inode != NULL) { - cFYI(1, (" non-NULL inode in lookup")); + cFYI(1, ("non-NULL inode in lookup")); } else { - cFYI(1, (" NULL inode in lookup")); + cFYI(1, ("NULL inode in lookup")); } - cFYI(1, - (" Full path: %s inode = 0x%p", full_path, direntry->d_inode)); + cFYI(1, ("Full path: %s inode = 0x%p", full_path, direntry->d_inode)); if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&newInode, full_path, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ead1a3bb0256..b1e1fc6a6e6a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -488,12 +488,13 @@ int cifs_close(struct inode *inode, struct file *file) pTcon = cifs_sb->tcon; if (pSMBFile) { struct cifsLockInfo *li, *tmp; - + write_lock(&GlobalSMBSeslock); pSMBFile->closePend = true; if (pTcon) { /* no sense reconnecting to close a file that is already closed */ - if (pTcon->tidStatus != CifsNeedReconnect) { + if (!pTcon->need_reconnect) { + write_unlock(&GlobalSMBSeslock); timeout = 2; while ((atomic_read(&pSMBFile->wrtPending) != 0) && (timeout <= 2048)) { @@ -510,12 +511,15 @@ int cifs_close(struct inode *inode, struct file *file) timeout *= 4; } if (atomic_read(&pSMBFile->wrtPending)) - cERROR(1, - ("close with pending writes")); - rc = CIFSSMBClose(xid, pTcon, + cERROR(1, ("close with pending write")); + if (!pTcon->need_reconnect && + !pSMBFile->invalidHandle) + rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); - } - } + } else + write_unlock(&GlobalSMBSeslock); + } else + write_unlock(&GlobalSMBSeslock); /* Delete any outstanding lock records. We'll lose them when the file is closed anyway. */ @@ -587,15 +591,18 @@ int cifs_closedir(struct inode *inode, struct file *file) pTcon = cifs_sb->tcon; cFYI(1, ("Freeing private data in close dir")); + write_lock(&GlobalSMBSeslock); if (!pCFileStruct->srch_inf.endOfSearch && !pCFileStruct->invalidHandle) { pCFileStruct->invalidHandle = true; + write_unlock(&GlobalSMBSeslock); rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid); cFYI(1, ("Closing uncompleted readdir with rc %d", rc)); /* not much we can do if it fails anyway, ignore rc */ rc = 0; - } + } else + write_unlock(&GlobalSMBSeslock); ptmp = pCFileStruct->srch_inf.ntwrk_buf_start; if (ptmp) { cFYI(1, ("closedir free smb buf in srch struct")); @@ -637,10 +644,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) __u64 length; bool wait_flag = false; struct cifs_sb_info *cifs_sb; - struct cifsTconInfo *pTcon; + struct cifsTconInfo *tcon; __u16 netfid; __u8 lockType = LOCKING_ANDX_LARGE_FILES; - bool posix_locking; + bool posix_locking = 0; length = 1 + pfLock->fl_end - pfLock->fl_start; rc = -EACCES; @@ -691,7 +698,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) cFYI(1, ("Unknown type of lock")); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - pTcon = cifs_sb->tcon; + tcon = cifs_sb->tcon; if (file->private_data == NULL) { FreeXid(xid); @@ -699,9 +706,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) } netfid = ((struct cifsFileInfo *)file->private_data)->netfid; - posix_locking = (cifs_sb->tcon->ses->capabilities & CAP_UNIX) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability)); - + if ((tcon->ses->capabilities & CAP_UNIX) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + posix_locking = 1; /* BB add code here to normalize offset and length to account for negative length which we can not accept over the wire */ @@ -712,7 +720,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; - rc = CIFSSMBPosixLock(xid, pTcon, netfid, 1 /* get */, + rc = CIFSSMBPosixLock(xid, tcon, netfid, 1 /* get */, length, pfLock, posix_lock_type, wait_flag); FreeXid(xid); @@ -720,10 +728,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) } /* BB we could chain these into one lock request BB */ - rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start, + rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 0, 1, lockType, 0 /* wait flag */ ); if (rc == 0) { - rc = CIFSSMBLock(xid, pTcon, netfid, length, + rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 1 /* numUnlock */ , 0 /* numLock */ , lockType, 0 /* wait flag */ ); @@ -760,7 +768,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) if (numUnlock == 1) posix_lock_type = CIFS_UNLCK; - rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */, + rc = CIFSSMBPosixLock(xid, tcon, netfid, 0 /* set */, length, pfLock, posix_lock_type, wait_flag); } else { @@ -768,7 +776,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) (struct cifsFileInfo *)file->private_data; if (numLock) { - rc = CIFSSMBLock(xid, pTcon, netfid, length, + rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 0, numLock, lockType, wait_flag); @@ -789,7 +797,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) if (pfLock->fl_start <= li->offset && (pfLock->fl_start + length) >= (li->offset + li->length)) { - stored_rc = CIFSSMBLock(xid, pTcon, + stored_rc = CIFSSMBLock(xid, tcon, netfid, li->length, li->offset, 1, 0, li->type, false); @@ -1404,7 +1412,10 @@ retry: if ((wbc->nr_to_write -= n_iov) <= 0) done = 1; index = next; - } + } else + /* Need to re-find the pages we skipped */ + index = pvec.pages[0]->index + 1; + pagevec_release(&pvec); } if (!scanned && !done) { @@ -1465,7 +1476,11 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, cFYI(1, ("write_end for page %p from pos %lld with %d bytes", page, pos, copied)); - if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE) + if (PageChecked(page)) { + if (copied == len) + SetPageUptodate(page); + ClearPageChecked(page); + } else if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE) SetPageUptodate(page); if (!PageUptodate(page)) { @@ -2052,39 +2067,70 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping, { pgoff_t index = pos >> PAGE_CACHE_SHIFT; loff_t offset = pos & (PAGE_CACHE_SIZE - 1); + loff_t page_start = pos & PAGE_MASK; + loff_t i_size; + struct page *page; + int rc = 0; cFYI(1, ("write_begin from %lld len %d", (long long)pos, len)); - *pagep = __grab_cache_page(mapping, index); - if (!*pagep) - return -ENOMEM; - - if (PageUptodate(*pagep)) - return 0; + page = __grab_cache_page(mapping, index); + if (!page) { + rc = -ENOMEM; + goto out; + } - /* If we are writing a full page it will be up to date, - no need to read from the server */ - if (len == PAGE_CACHE_SIZE && flags & AOP_FLAG_UNINTERRUPTIBLE) - return 0; + if (PageUptodate(page)) + goto out; - if ((file->f_flags & O_ACCMODE) != O_WRONLY) { - int rc; + /* + * If we write a full page it will be up to date, no need to read from + * the server. If the write is short, we'll end up doing a sync write + * instead. + */ + if (len == PAGE_CACHE_SIZE) + goto out; - /* might as well read a page, it is fast enough */ - rc = cifs_readpage_worker(file, *pagep, &offset); + /* + * optimize away the read when we have an oplock, and we're not + * expecting to use any of the data we'd be reading in. That + * is, when the page lies beyond the EOF, or straddles the EOF + * and the write will cover all of the existing data. + */ + if (CIFS_I(mapping->host)->clientCanCacheRead) { + i_size = i_size_read(mapping->host); + if (page_start >= i_size || + (offset == 0 && (pos + len) >= i_size)) { + zero_user_segments(page, 0, offset, + offset + len, + PAGE_CACHE_SIZE); + /* + * PageChecked means that the parts of the page + * to which we're not writing are considered up + * to date. Once the data is copied to the + * page, it can be set uptodate. + */ + SetPageChecked(page); + goto out; + } + } - /* we do not need to pass errors back - e.g. if we do not have read access to the file - because cifs_write_end will attempt synchronous writes - -- shaggy */ + if ((file->f_flags & O_ACCMODE) != O_WRONLY) { + /* + * might as well read a page, it is fast enough. If we get + * an error, we don't need to return it. cifs_write_end will + * do a sync write instead since PG_uptodate isn't set. + */ + cifs_readpage_worker(file, page, &page_start); } else { /* we could try using another file handle if there is one - but how would we lock it to prevent close of that handle racing with this read? In any case this will be written out by write_end so is fine */ } - - return 0; +out: + *pagep = page; + return rc; } const struct address_space_operations cifs_addr_ops = { diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ff8c68de4a92..f247da9f4edc 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1,7 +1,7 @@ /* * fs/cifs/inode.c * - * Copyright (C) International Business Machines Corp., 2002,2007 + * Copyright (C) International Business Machines Corp., 2002,2008 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -621,6 +621,47 @@ static const struct inode_operations cifs_ipc_inode_ops = { .lookup = cifs_lookup, }; +static char *build_path_to_root(struct cifs_sb_info *cifs_sb) +{ + int pplen = cifs_sb->prepathlen; + int dfsplen; + char *full_path = NULL; + + /* if no prefix path, simply set path to the root of share to "" */ + if (pplen == 0) { + full_path = kmalloc(1, GFP_KERNEL); + if (full_path) + full_path[0] = 0; + return full_path; + } + + if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) + dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1); + else + dfsplen = 0; + + full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL); + if (full_path == NULL) + return full_path; + + if (dfsplen) { + strncpy(full_path, cifs_sb->tcon->treeName, dfsplen); + /* switch slash direction in prepath depending on whether + * windows or posix style path names + */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { + int i; + for (i = 0; i < dfsplen; i++) { + if (full_path[i] == '\\') + full_path[i] = '/'; + } + } + } + strncpy(full_path + dfsplen, cifs_sb->prepath, pplen); + full_path[dfsplen + pplen] = 0; /* add trailing null */ + return full_path; +} + /* gets root inode */ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) { @@ -628,6 +669,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) struct cifs_sb_info *cifs_sb; struct inode *inode; long rc; + char *full_path; inode = iget_locked(sb, ino); if (!inode) @@ -636,13 +678,17 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) return inode; cifs_sb = CIFS_SB(inode->i_sb); - xid = GetXid(); + full_path = build_path_to_root(cifs_sb); + if (full_path == NULL) + return ERR_PTR(-ENOMEM); + xid = GetXid(); if (cifs_sb->tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid); + rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, + xid); else - rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid, - NULL); + rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb, + xid, NULL); if (rc && cifs_sb->tcon->ipc) { cFYI(1, ("ipc connection - fake read inode")); inode->i_mode |= S_IFDIR; @@ -652,6 +698,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) inode->i_uid = cifs_sb->mnt_uid; inode->i_gid = cifs_sb->mnt_gid; } else if (rc) { + kfree(full_path); _FreeXid(xid); iget_failed(inode); return ERR_PTR(rc); @@ -659,6 +706,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) unlock_new_inode(inode); + kfree(full_path); /* can not call macro FreeXid here since in a void func * TODO: This is no longer true */ @@ -1143,11 +1191,11 @@ mkdir_get_info: .device = 0, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64)current->fsuid; + args.uid = (__u64)current_fsuid(); if (inode->i_mode & S_ISGID) args.gid = (__u64)inode->i_gid; else - args.gid = (__u64)current->fsgid; + args.gid = (__u64)current_fsgid(); } else { args.uid = NO_CHANGE_64; args.gid = NO_CHANGE_64; @@ -1184,13 +1232,13 @@ mkdir_get_info: if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { direntry->d_inode->i_uid = - current->fsuid; + current_fsuid(); if (inode->i_mode & S_ISGID) direntry->d_inode->i_gid = inode->i_gid; else direntry->d_inode->i_gid = - current->fsgid; + current_fsgid(); } } } diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 0088a5b52564..f94650683a00 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -65,7 +65,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) switch (command) { case CIFS_IOC_CHECKUMOUNT: cFYI(1, ("User unmount attempted")); - if (cifs_sb->mnt_uid == current->uid) + if (cifs_sb->mnt_uid == current_uid()) rc = 0; else { rc = -EACCES; diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 88786ba02d27..4c89c572891a 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -75,12 +75,12 @@ sesInfoAlloc(void) ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); if (ret_buf) { - write_lock(&GlobalSMBSeslock); atomic_inc(&sesInfoAllocCount); ret_buf->status = CifsNew; - list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList); + ++ret_buf->ses_count; + INIT_LIST_HEAD(&ret_buf->smb_ses_list); + INIT_LIST_HEAD(&ret_buf->tcon_list); init_MUTEX(&ret_buf->sesSem); - write_unlock(&GlobalSMBSeslock); } return ret_buf; } @@ -93,14 +93,14 @@ sesInfoFree(struct cifsSesInfo *buf_to_free) return; } - write_lock(&GlobalSMBSeslock); atomic_dec(&sesInfoAllocCount); - list_del(&buf_to_free->cifsSessionList); - write_unlock(&GlobalSMBSeslock); kfree(buf_to_free->serverOS); kfree(buf_to_free->serverDomain); kfree(buf_to_free->serverNOS); - kfree(buf_to_free->password); + if (buf_to_free->password) { + memset(buf_to_free->password, 0, strlen(buf_to_free->password)); + kfree(buf_to_free->password); + } kfree(buf_to_free->domainName); kfree(buf_to_free); } @@ -111,17 +111,14 @@ tconInfoAlloc(void) struct cifsTconInfo *ret_buf; ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); if (ret_buf) { - write_lock(&GlobalSMBSeslock); atomic_inc(&tconInfoAllocCount); - list_add(&ret_buf->cifsConnectionList, - &GlobalTreeConnectionList); ret_buf->tidStatus = CifsNew; + ++ret_buf->tc_count; INIT_LIST_HEAD(&ret_buf->openFileList); - init_MUTEX(&ret_buf->tconSem); + INIT_LIST_HEAD(&ret_buf->tcon_list); #ifdef CONFIG_CIFS_STATS spin_lock_init(&ret_buf->stat_lock); #endif - write_unlock(&GlobalSMBSeslock); } return ret_buf; } @@ -133,11 +130,12 @@ tconInfoFree(struct cifsTconInfo *buf_to_free) cFYI(1, ("Null buffer passed to tconInfoFree")); return; } - write_lock(&GlobalSMBSeslock); atomic_dec(&tconInfoAllocCount); - list_del(&buf_to_free->cifsConnectionList); - write_unlock(&GlobalSMBSeslock); kfree(buf_to_free->nativeFileSystem); + if (buf_to_free->password) { + memset(buf_to_free->password, 0, strlen(buf_to_free->password)); + kfree(buf_to_free->password); + } kfree(buf_to_free); } @@ -347,13 +345,13 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , /* BB Add support for establishing new tCon and SMB Session */ /* with userid/password pairs found on the smb session */ /* for other target tcp/ip addresses BB */ - if (current->fsuid != treeCon->ses->linux_uid) { + if (current_fsuid() != treeCon->ses->linux_uid) { cFYI(1, ("Multiuser mode and UID " "did not match tcon uid")); - read_lock(&GlobalSMBSeslock); - list_for_each(temp_item, &GlobalSMBSessionList) { - ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList); - if (ses->linux_uid == current->fsuid) { + read_lock(&cifs_tcp_ses_lock); + list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) { + ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list); + if (ses->linux_uid == current_fsuid()) { if (ses->server == treeCon->ses->server) { cFYI(1, ("found matching uid substitute right smb_uid")); buffer->Uid = ses->Suid; @@ -364,7 +362,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , } } } - read_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); } } } @@ -497,9 +495,10 @@ bool is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) { struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; - struct list_head *tmp; - struct list_head *tmp1; + struct list_head *tmp, *tmp1, *tmp2; + struct cifsSesInfo *ses; struct cifsTconInfo *tcon; + struct cifsInodeInfo *pCifsInode; struct cifsFileInfo *netfile; cFYI(1, ("Checking for oplock break or dnotify response")); @@ -554,42 +553,45 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) return false; /* look up tcon based on tid & uid */ - read_lock(&GlobalSMBSeslock); - list_for_each(tmp, &GlobalTreeConnectionList) { - tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); - if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) { + read_lock(&cifs_tcp_ses_lock); + list_for_each(tmp, &srv->smb_ses_list) { + ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); + list_for_each(tmp1, &ses->tcon_list) { + tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list); + if (tcon->tid != buf->Tid) + continue; + cifs_stats_inc(&tcon->num_oplock_brks); - list_for_each(tmp1, &tcon->openFileList) { - netfile = list_entry(tmp1, struct cifsFileInfo, + write_lock(&GlobalSMBSeslock); + list_for_each(tmp2, &tcon->openFileList) { + netfile = list_entry(tmp2, struct cifsFileInfo, tlist); - if (pSMB->Fid == netfile->netfid) { - struct cifsInodeInfo *pCifsInode; - read_unlock(&GlobalSMBSeslock); - cFYI(1, - ("file id match, oplock break")); - pCifsInode = - CIFS_I(netfile->pInode); - pCifsInode->clientCanCacheAll = false; - if (pSMB->OplockLevel == 0) - pCifsInode->clientCanCacheRead - = false; - pCifsInode->oplockPending = true; - AllocOplockQEntry(netfile->pInode, - netfile->netfid, - tcon); - cFYI(1, - ("about to wake up oplock thread")); - if (oplockThread) - wake_up_process(oplockThread); - return true; - } + if (pSMB->Fid != netfile->netfid) + continue; + + write_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); + cFYI(1, ("file id match, oplock break")); + pCifsInode = CIFS_I(netfile->pInode); + pCifsInode->clientCanCacheAll = false; + if (pSMB->OplockLevel == 0) + pCifsInode->clientCanCacheRead = false; + pCifsInode->oplockPending = true; + AllocOplockQEntry(netfile->pInode, + netfile->netfid, tcon); + cFYI(1, ("about to wake up oplock thread")); + if (oplockThread) + wake_up_process(oplockThread); + + return true; } - read_unlock(&GlobalSMBSeslock); + write_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); cFYI(1, ("No matching file for oplock break")); return true; } } - read_unlock(&GlobalSMBSeslock); + read_unlock(&cifs_tcp_ses_lock); cFYI(1, ("Can not process oplock break for non-existent connection")); return true; } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 58d57299f2a0..9f51f9bf0292 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -741,11 +741,14 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, (index_to_find < first_entry_in_buffer)) { /* close and restart search */ cFYI(1, ("search backing up - close and restart search")); + write_lock(&GlobalSMBSeslock); if (!cifsFile->srch_inf.endOfSearch && !cifsFile->invalidHandle) { cifsFile->invalidHandle = true; + write_unlock(&GlobalSMBSeslock); CIFSFindClose(xid, pTcon, cifsFile->netfid); - } + } else + write_unlock(&GlobalSMBSeslock); if (cifsFile->srch_inf.ntwrk_buf_start) { cFYI(1, ("freeing SMB ff cache buf on search rewind")); if (cifsFile->srch_inf.smallBuf) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 2851d5da0c8c..5f22de7b79a9 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -417,7 +417,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, /* BB calculate hash with password */ /* and copy into bcc */ - calc_lanman_hash(ses, lnm_session_key); + calc_lanman_hash(ses->password, ses->server->cryptKey, + ses->server->secMode & SECMODE_PW_ENCRYPT ? + true : false, lnm_session_key); + ses->flags |= CIFS_SES_LANMAN; memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_SIZE; diff --git a/fs/cifs/smbdes.c b/fs/cifs/smbdes.c index 04943c976f98..224a1f478966 100644 --- a/fs/cifs/smbdes.c +++ b/fs/cifs/smbdes.c @@ -318,7 +318,8 @@ str_to_key(unsigned char *str, unsigned char *key) } static void -smbhash(unsigned char *out, unsigned char *in, unsigned char *key, int forw) +smbhash(unsigned char *out, const unsigned char *in, unsigned char *key, + int forw) { int i; char *outb; /* outb[64] */ @@ -363,7 +364,7 @@ E_P16(unsigned char *p14, unsigned char *p16) } void -E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24) +E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24) { smbhash(p24, c8, p21, 1); smbhash(p24 + 8, c8, p21 + 7, 1); diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index ff3232fa1015..93fb09a99c69 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -49,9 +49,10 @@ /*The following definitions come from libsmb/smbencrypt.c */ -void SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); +void SMBencrypt(unsigned char *passwd, const unsigned char *c8, + unsigned char *p24); void E_md4hash(const unsigned char *passwd, unsigned char *p16); -static void SMBOWFencrypt(unsigned char passwd[16], unsigned char *c8, +static void SMBOWFencrypt(unsigned char passwd[16], const unsigned char *c8, unsigned char p24[24]); void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); @@ -61,7 +62,7 @@ void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); encrypted password into p24 */ /* Note that password must be uppercased and null terminated */ void -SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24) +SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24) { unsigned char p14[15], p21[21]; @@ -212,7 +213,7 @@ ntv2_owf_gen(const unsigned char owf[16], const char *user_n, /* Does the des encryption from the NT or LM MD4 hash. */ static void -SMBOWFencrypt(unsigned char passwd[16], unsigned char *c8, +SMBOWFencrypt(unsigned char passwd[16], const unsigned char *c8, unsigned char p24[24]) { unsigned char p21[21]; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index ff8243a8fe3e..7ebe6599ed3a 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -37,15 +37,11 @@ extern mempool_t *cifs_mid_poolp; extern struct kmem_cache *cifs_oplock_cachep; static struct mid_q_entry * -AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) +AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) { struct mid_q_entry *temp; - if (ses == NULL) { - cERROR(1, ("Null session passed in to AllocMidQEntry")); - return NULL; - } - if (ses->server == NULL) { + if (server == NULL) { cERROR(1, ("Null TCP session in AllocMidQEntry")); return NULL; } @@ -62,12 +58,11 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ /* when mid allocated can be before when sent */ temp->when_alloc = jiffies; - temp->ses = ses; temp->tsk = current; } spin_lock(&GlobalMid_Lock); - list_add_tail(&temp->qhead, &ses->server->pending_mid_q); + list_add_tail(&temp->qhead, &server->pending_mid_q); atomic_inc(&midCount); temp->midState = MID_REQUEST_ALLOCATED; spin_unlock(&GlobalMid_Lock); @@ -349,37 +344,38 @@ static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) if (long_op == CIFS_ASYNC_OP) { /* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); - } else { - spin_lock(&GlobalMid_Lock); - while (1) { - if (atomic_read(&ses->server->inFlight) >= - cifs_max_pending){ - spin_unlock(&GlobalMid_Lock); + return 0; + } + + spin_lock(&GlobalMid_Lock); + while (1) { + if (atomic_read(&ses->server->inFlight) >= + cifs_max_pending){ + spin_unlock(&GlobalMid_Lock); #ifdef CONFIG_CIFS_STATS2 - atomic_inc(&ses->server->num_waiters); + atomic_inc(&ses->server->num_waiters); #endif - wait_event(ses->server->request_q, - atomic_read(&ses->server->inFlight) - < cifs_max_pending); + wait_event(ses->server->request_q, + atomic_read(&ses->server->inFlight) + < cifs_max_pending); #ifdef CONFIG_CIFS_STATS2 - atomic_dec(&ses->server->num_waiters); + atomic_dec(&ses->server->num_waiters); #endif - spin_lock(&GlobalMid_Lock); - } else { - if (ses->server->tcpStatus == CifsExiting) { - spin_unlock(&GlobalMid_Lock); - return -ENOENT; - } - - /* can not count locking commands against total - as they are allowed to block on server */ - - /* update # of requests on the wire to server */ - if (long_op != CIFS_BLOCKING_OP) - atomic_inc(&ses->server->inFlight); + spin_lock(&GlobalMid_Lock); + } else { + if (ses->server->tcpStatus == CifsExiting) { spin_unlock(&GlobalMid_Lock); - break; + return -ENOENT; } + + /* can not count locking commands against total + as they are allowed to block on server */ + + /* update # of requests on the wire to server */ + if (long_op != CIFS_BLOCKING_OP) + atomic_inc(&ses->server->inFlight); + spin_unlock(&GlobalMid_Lock); + break; } } return 0; @@ -390,17 +386,21 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf, { if (ses->server->tcpStatus == CifsExiting) { return -ENOENT; - } else if (ses->server->tcpStatus == CifsNeedReconnect) { + } + + if (ses->server->tcpStatus == CifsNeedReconnect) { cFYI(1, ("tcp session dead - return to caller to retry")); return -EAGAIN; - } else if (ses->status != CifsGood) { + } + + if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) return -EAGAIN; /* else ok - we are setting up session */ } - *ppmidQ = AllocMidQEntry(in_buf, ses); + *ppmidQ = AllocMidQEntry(in_buf, ses->server); if (*ppmidQ == NULL) return -ENOMEM; return 0; @@ -415,11 +415,8 @@ static int wait_for_response(struct cifsSesInfo *ses, for (;;) { curr_timeout = timeout + jiffies; - wait_event(ses->server->response_q, - (!(midQ->midState == MID_REQUEST_SUBMITTED)) || - time_after(jiffies, curr_timeout) || - ((ses->server->tcpStatus != CifsGood) && - (ses->server->tcpStatus != CifsNew))); + wait_event_timeout(ses->server->response_q, + midQ->midState != MID_REQUEST_SUBMITTED, timeout); if (time_after(jiffies, curr_timeout) && (midQ->midState == MID_REQUEST_SUBMITTED) && @@ -521,11 +518,11 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, and avoid races inside tcp sendmsg code that could cause corruption of smb data */ - down(&ses->server->tcpSem); + mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { - up(&ses->server->tcpSem); + mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); @@ -533,6 +530,11 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, return rc; } rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); + if (rc) { + mutex_unlock(&ses->server->srv_mutex); + cifs_small_buf_release(in_buf); + goto out; + } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 @@ -546,7 +548,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, midQ->when_sent = jiffies; #endif - up(&ses->server->tcpSem); + mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); if (rc < 0) @@ -581,10 +583,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, wait_for_response(ses, midQ, timeout, 10 * HZ); spin_lock(&GlobalMid_Lock); - if (midQ->resp_buf) { - spin_unlock(&GlobalMid_Lock); - receive_len = midQ->resp_buf->smb_buf_length; - } else { + + if (midQ->resp_buf == NULL) { cERROR(1, ("No response to cmd %d mid %d", midQ->command, midQ->mid)); if (midQ->midState == MID_REQUEST_SUBMITTED) { @@ -612,53 +612,59 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, return rc; } + spin_unlock(&GlobalMid_Lock); + receive_len = midQ->resp_buf->smb_buf_length; + if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; - } else { /* rcvd frame is ok */ - if (midQ->resp_buf && - (midQ->midState == MID_RESPONSE_RECEIVED)) { - - iov[0].iov_base = (char *)midQ->resp_buf; - if (midQ->largeBuf) - *pRespBufType = CIFS_LARGE_BUFFER; - else - *pRespBufType = CIFS_SMALL_BUFFER; - iov[0].iov_len = receive_len + 4; - - dump_smb(midQ->resp_buf, 80); - /* convert the length into a more usable form */ - if ((receive_len > 24) && - (ses->server->secMode & (SECMODE_SIGN_REQUIRED | - SECMODE_SIGN_ENABLED))) { - rc = cifs_verify_signature(midQ->resp_buf, + goto out; + } + + /* rcvd frame is ok */ + + if (midQ->resp_buf && + (midQ->midState == MID_RESPONSE_RECEIVED)) { + + iov[0].iov_base = (char *)midQ->resp_buf; + if (midQ->largeBuf) + *pRespBufType = CIFS_LARGE_BUFFER; + else + *pRespBufType = CIFS_SMALL_BUFFER; + iov[0].iov_len = receive_len + 4; + + dump_smb(midQ->resp_buf, 80); + /* convert the length into a more usable form */ + if ((receive_len > 24) && + (ses->server->secMode & (SECMODE_SIGN_REQUIRED | + SECMODE_SIGN_ENABLED))) { + rc = cifs_verify_signature(midQ->resp_buf, &ses->server->mac_signing_key, midQ->sequence_number+1); - if (rc) { - cERROR(1, ("Unexpected SMB signature")); - /* BB FIXME add code to kill session */ - } + if (rc) { + cERROR(1, ("Unexpected SMB signature")); + /* BB FIXME add code to kill session */ } - - /* BB special case reconnect tid and uid here? */ - rc = map_smb_to_linux_error(midQ->resp_buf, - flags & CIFS_LOG_ERROR); - - /* convert ByteCount if necessary */ - if (receive_len >= sizeof(struct smb_hdr) - 4 - /* do not count RFC1001 header */ + - (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) - BCC(midQ->resp_buf) = - le16_to_cpu(BCC_LE(midQ->resp_buf)); - if ((flags & CIFS_NO_RESP) == 0) - midQ->resp_buf = NULL; /* mark it so buf will - not be freed by - DeleteMidQEntry */ - } else { - rc = -EIO; - cFYI(1, ("Bad MID state?")); } + + /* BB special case reconnect tid and uid here? */ + rc = map_smb_to_linux_error(midQ->resp_buf, + flags & CIFS_LOG_ERROR); + + /* convert ByteCount if necessary */ + if (receive_len >= sizeof(struct smb_hdr) - 4 + /* do not count RFC1001 header */ + + (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) + BCC(midQ->resp_buf) = + le16_to_cpu(BCC_LE(midQ->resp_buf)); + if ((flags & CIFS_NO_RESP) == 0) + midQ->resp_buf = NULL; /* mark it so buf will + not be freed by + DeleteMidQEntry */ + } else { + rc = -EIO; + cFYI(1, ("Bad MID state?")); } out: @@ -695,6 +701,12 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, to the same server. We may make this configurable later or use ses->maxReq */ + if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cERROR(1, ("Illegal length, greater than maximum frame, %d", + in_buf->smb_buf_length)); + return -EIO; + } + rc = wait_for_free_request(ses, long_op); if (rc) return rc; @@ -703,29 +715,22 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, and avoid races inside tcp sendmsg code that could cause corruption of smb data */ - down(&ses->server->tcpSem); + mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { - up(&ses->server->tcpSem); + mutex_unlock(&ses->server->srv_mutex); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } - if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cERROR(1, ("Illegal length, greater than maximum frame, %d", - in_buf->smb_buf_length)); - DeleteMidQEntry(midQ); - up(&ses->server->tcpSem); - /* Update # of requests on wire to server */ - atomic_dec(&ses->server->inFlight); - wake_up(&ses->server->request_q); - return -EIO; - } - rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); + if (rc) { + mutex_unlock(&ses->server->srv_mutex); + goto out; + } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 @@ -738,7 +743,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif - up(&ses->server->tcpSem); + mutex_unlock(&ses->server->srv_mutex); if (rc < 0) goto out; @@ -772,10 +777,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, wait_for_response(ses, midQ, timeout, 10 * HZ); spin_lock(&GlobalMid_Lock); - if (midQ->resp_buf) { - spin_unlock(&GlobalMid_Lock); - receive_len = midQ->resp_buf->smb_buf_length; - } else { + if (midQ->resp_buf == NULL) { cERROR(1, ("No response for cmd %d mid %d", midQ->command, midQ->mid)); if (midQ->midState == MID_REQUEST_SUBMITTED) { @@ -803,47 +805,52 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, return rc; } + spin_unlock(&GlobalMid_Lock); + receive_len = midQ->resp_buf->smb_buf_length; + if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; - } else { /* rcvd frame is ok */ - - if (midQ->resp_buf && out_buf - && (midQ->midState == MID_RESPONSE_RECEIVED)) { - out_buf->smb_buf_length = receive_len; - memcpy((char *)out_buf + 4, - (char *)midQ->resp_buf + 4, - receive_len); - - dump_smb(out_buf, 92); - /* convert the length into a more usable form */ - if ((receive_len > 24) && - (ses->server->secMode & (SECMODE_SIGN_REQUIRED | - SECMODE_SIGN_ENABLED))) { - rc = cifs_verify_signature(out_buf, + goto out; + } + + /* rcvd frame is ok */ + + if (midQ->resp_buf && out_buf + && (midQ->midState == MID_RESPONSE_RECEIVED)) { + out_buf->smb_buf_length = receive_len; + memcpy((char *)out_buf + 4, + (char *)midQ->resp_buf + 4, + receive_len); + + dump_smb(out_buf, 92); + /* convert the length into a more usable form */ + if ((receive_len > 24) && + (ses->server->secMode & (SECMODE_SIGN_REQUIRED | + SECMODE_SIGN_ENABLED))) { + rc = cifs_verify_signature(out_buf, &ses->server->mac_signing_key, midQ->sequence_number+1); - if (rc) { - cERROR(1, ("Unexpected SMB signature")); - /* BB FIXME add code to kill session */ - } + if (rc) { + cERROR(1, ("Unexpected SMB signature")); + /* BB FIXME add code to kill session */ } + } - *pbytes_returned = out_buf->smb_buf_length; + *pbytes_returned = out_buf->smb_buf_length; - /* BB special case reconnect tid and uid here? */ - rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); + /* BB special case reconnect tid and uid here? */ + rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); - /* convert ByteCount if necessary */ - if (receive_len >= sizeof(struct smb_hdr) - 4 - /* do not count RFC1001 header */ + - (2 * out_buf->WordCount) + 2 /* bcc */ ) - BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); - } else { - rc = -EIO; - cERROR(1, ("Bad MID state?")); - } + /* convert ByteCount if necessary */ + if (receive_len >= sizeof(struct smb_hdr) - 4 + /* do not count RFC1001 header */ + + (2 * out_buf->WordCount) + 2 /* bcc */ ) + BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); + } else { + rc = -EIO; + cERROR(1, ("Bad MID state?")); } out: @@ -866,16 +873,16 @@ send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf, header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0); in_buf->Mid = mid; - down(&ses->server->tcpSem); + mutex_lock(&ses->server->srv_mutex); rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { - up(&ses->server->tcpSem); + mutex_unlock(&ses->server->srv_mutex); return rc; } rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr), ses->server->noblocksnd); - up(&ses->server->tcpSem); + mutex_unlock(&ses->server->srv_mutex); return rc; } @@ -933,6 +940,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, to the same server. We may make this configurable later or use ses->maxReq */ + if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cERROR(1, ("Illegal length, greater than maximum frame, %d", + in_buf->smb_buf_length)); + return -EIO; + } + rc = wait_for_free_request(ses, CIFS_BLOCKING_OP); if (rc) return rc; @@ -941,24 +954,21 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, and avoid races inside tcp sendmsg code that could cause corruption of smb data */ - down(&ses->server->tcpSem); + mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { - up(&ses->server->tcpSem); + mutex_unlock(&ses->server->srv_mutex); return rc; } - if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - up(&ses->server->tcpSem); - cERROR(1, ("Illegal length, greater than maximum frame, %d", - in_buf->smb_buf_length)); + rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); + if (rc) { DeleteMidQEntry(midQ); - return -EIO; + mutex_unlock(&ses->server->srv_mutex); + return rc; } - rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); - midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); @@ -970,7 +980,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif - up(&ses->server->tcpSem); + mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { DeleteMidQEntry(midQ); @@ -1052,44 +1062,48 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; - } else { /* rcvd frame is ok */ - - if (midQ->resp_buf && out_buf - && (midQ->midState == MID_RESPONSE_RECEIVED)) { - out_buf->smb_buf_length = receive_len; - memcpy((char *)out_buf + 4, - (char *)midQ->resp_buf + 4, - receive_len); - - dump_smb(out_buf, 92); - /* convert the length into a more usable form */ - if ((receive_len > 24) && - (ses->server->secMode & (SECMODE_SIGN_REQUIRED | - SECMODE_SIGN_ENABLED))) { - rc = cifs_verify_signature(out_buf, - &ses->server->mac_signing_key, - midQ->sequence_number+1); - if (rc) { - cERROR(1, ("Unexpected SMB signature")); - /* BB FIXME add code to kill session */ - } - } + goto out; + } - *pbytes_returned = out_buf->smb_buf_length; + /* rcvd frame is ok */ - /* BB special case reconnect tid and uid here? */ - rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); + if ((out_buf == NULL) || (midQ->midState != MID_RESPONSE_RECEIVED)) { + rc = -EIO; + cERROR(1, ("Bad MID state?")); + goto out; + } - /* convert ByteCount if necessary */ - if (receive_len >= sizeof(struct smb_hdr) - 4 - /* do not count RFC1001 header */ + - (2 * out_buf->WordCount) + 2 /* bcc */ ) - BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); - } else { - rc = -EIO; - cERROR(1, ("Bad MID state?")); + out_buf->smb_buf_length = receive_len; + memcpy((char *)out_buf + 4, + (char *)midQ->resp_buf + 4, + receive_len); + + dump_smb(out_buf, 92); + /* convert the length into a more usable form */ + if ((receive_len > 24) && + (ses->server->secMode & (SECMODE_SIGN_REQUIRED | + SECMODE_SIGN_ENABLED))) { + rc = cifs_verify_signature(out_buf, + &ses->server->mac_signing_key, + midQ->sequence_number+1); + if (rc) { + cERROR(1, ("Unexpected SMB signature")); + /* BB FIXME add code to kill session */ } } + + *pbytes_returned = out_buf->smb_buf_length; + + /* BB special case reconnect tid and uid here? */ + rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); + + /* convert ByteCount if necessary */ + if (receive_len >= sizeof(struct smb_hdr) - 4 + /* do not count RFC1001 header */ + + (2 * out_buf->WordCount) + 2 /* bcc */ ) + BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); + +out: DeleteMidQEntry(midQ); if (rstart && rc == -EACCES) return -ERESTARTSYS; |