Commit 4e14e833 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
  prevent cifs_writepages() from skipping unwritten pages
  Fixed parsing of mount options when doing DFS submount
  [CIFS] Fix check for tcon seal setting and fix oops on failed mount from earlier patch
  [CIFS] Fix build break
  cifs: reinstate sharing of tree connections
  [CIFS] minor cleanup to cifs_mount
  cifs: reinstate sharing of SMB sessions sans races
  cifs: disable sharing session and tcon and add new TCP sharing code
  [CIFS] clean up server protocol handling
  [CIFS] remove unused list, add new cifs sock list to prepare for mount/umount fix
  [CIFS] Fix cifs reconnection flags
  [CIFS] Can't rely on iov length and base when kernel_recvmsg returns error
parents 65ecc14a b066a48c
...@@ -107,12 +107,13 @@ void cifs_dump_mids(struct TCP_Server_Info *server) ...@@ -107,12 +107,13 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static int cifs_debug_data_proc_show(struct seq_file *m, void *v) static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
{ {
struct list_head *tmp; struct list_head *tmp1, *tmp2, *tmp3;
struct list_head *tmp1;
struct mid_q_entry *mid_entry; struct mid_q_entry *mid_entry;
struct TCP_Server_Info *server;
struct cifsSesInfo *ses; struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
int i; int i, j;
__u32 dev_type;
seq_puts(m, seq_puts(m,
"Display Internal CIFS Data Structures for Debugging\n" "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) ...@@ -122,46 +123,78 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "Servers:"); seq_printf(m, "Servers:");
i = 0; i = 0;
read_lock(&GlobalSMBSeslock); read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &GlobalSMBSessionList) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list);
i++; i++;
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); list_for_each(tmp2, &server->smb_ses_list) {
if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || ses = list_entry(tmp2, struct cifsSesInfo,
(ses->serverNOS == NULL)) { smb_ses_list);
seq_printf(m, "\nentry for %s not fully " if ((ses->serverDomain == NULL) ||
"displayed\n\t", ses->serverName); (ses->serverOS == NULL) ||
} else { (ses->serverNOS == NULL)) {
seq_printf(m, seq_printf(m, "\n%d) entry for %s not fully "
"\n%d) Name: %s Domain: %s Mounts: %d OS:" "displayed\n\t", i, ses->serverName);
" %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB" } 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", " session status: %d\t",
i, ses->serverName, ses->serverDomain, i, ses->serverName, ses->serverDomain,
atomic_read(&ses->inUse), ses->ses_count, ses->serverOS, ses->serverNOS,
ses->serverOS, ses->serverNOS,
ses->capabilities, ses->status); ses->capabilities, ses->status);
} }
if (ses->server) {
seq_printf(m, "TCP status: %d\n\tLocal Users To " seq_printf(m, "TCP status: %d\n\tLocal Users To "
"Server: %d SecMode: 0x%x Req On Wire: %d", "Server: %d SecMode: 0x%x Req On Wire: %d",
ses->server->tcpStatus, server->tcpStatus, server->srv_count,
atomic_read(&ses->server->socketUseCount), server->secMode,
ses->server->secMode, atomic_read(&server->inFlight));
atomic_read(&ses->server->inFlight));
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
seq_printf(m, " In Send: %d In MaxReq Wait: %d", seq_printf(m, " In Send: %d In MaxReq Wait: %d",
atomic_read(&ses->server->inSend), atomic_read(&server->inSend),
atomic_read(&ses->server->num_waiters)); atomic_read(&server->num_waiters));
#endif #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); spin_lock(&GlobalMid_Lock);
list_for_each(tmp1, &ses->server->pending_mid_q) { list_for_each(tmp3, &server->pending_mid_q) {
mid_entry = list_entry(tmp1, struct mid_entry = list_entry(tmp3, struct mid_q_entry,
mid_q_entry,
qhead); qhead);
seq_printf(m, "State: %d com: %d pid:" seq_printf(m, "\tState: %d com: %d pid:"
" %d tsk: %p mid %d\n", " %d tsk: %p mid %d\n",
mid_entry->midState, mid_entry->midState,
(int)mid_entry->command, (int)mid_entry->command,
...@@ -171,44 +204,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -171,44 +204,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
} }
spin_unlock(&GlobalMid_Lock); 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'); seq_putc(m, '\n');
/* BB add code to dump additional info such as TCP session info now */ /* 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, ...@@ -234,7 +231,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
{ {
char c; char c;
int rc; int rc;
struct list_head *tmp; struct list_head *tmp1, *tmp2, *tmp3;
struct TCP_Server_Info *server;
struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
rc = get_user(c, buffer); rc = get_user(c, buffer);
...@@ -242,33 +241,42 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -242,33 +241,42 @@ static ssize_t cifs_stats_proc_write(struct file *file,
return rc; return rc;
if (c == '1' || c == 'y' || c == 'Y' || c == '0') { if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
read_lock(&GlobalSMBSeslock);
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
atomic_set(&totBufAllocCount, 0); atomic_set(&totBufAllocCount, 0);
atomic_set(&totSmBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0);
#endif /* CONFIG_CIFS_STATS2 */ #endif /* CONFIG_CIFS_STATS2 */
list_for_each(tmp, &GlobalTreeConnectionList) { read_lock(&cifs_tcp_ses_lock);
tcon = list_entry(tmp, struct cifsTconInfo, list_for_each(tmp1, &cifs_tcp_ses_list) {
cifsConnectionList); server = list_entry(tmp1, struct TCP_Server_Info,
atomic_set(&tcon->num_smbs_sent, 0); tcp_ses_list);
atomic_set(&tcon->num_writes, 0); list_for_each(tmp2, &server->smb_ses_list) {
atomic_set(&tcon->num_reads, 0); ses = list_entry(tmp2, struct cifsSesInfo,
atomic_set(&tcon->num_oplock_brks, 0); smb_ses_list);
atomic_set(&tcon->num_opens, 0); list_for_each(tmp3, &ses->tcon_list) {
atomic_set(&tcon->num_closes, 0); tcon = list_entry(tmp3,
atomic_set(&tcon->num_deletes, 0); struct cifsTconInfo,
atomic_set(&tcon->num_mkdirs, 0); tcon_list);
atomic_set(&tcon->num_rmdirs, 0); atomic_set(&tcon->num_smbs_sent, 0);
atomic_set(&tcon->num_renames, 0); atomic_set(&tcon->num_writes, 0);
atomic_set(&tcon->num_t2renames, 0); atomic_set(&tcon->num_reads, 0);
atomic_set(&tcon->num_ffirst, 0); atomic_set(&tcon->num_oplock_brks, 0);
atomic_set(&tcon->num_fnext, 0); atomic_set(&tcon->num_opens, 0);
atomic_set(&tcon->num_fclose, 0); atomic_set(&tcon->num_closes, 0);
atomic_set(&tcon->num_hardlinks, 0); atomic_set(&tcon->num_deletes, 0);
atomic_set(&tcon->num_symlinks, 0); atomic_set(&tcon->num_mkdirs, 0);
atomic_set(&tcon->num_locks, 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; return count;
...@@ -277,7 +285,9 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -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) static int cifs_stats_proc_show(struct seq_file *m, void *v)
{ {
int i; int i;
struct list_head *tmp; struct list_head *tmp1, *tmp2, *tmp3;
struct TCP_Server_Info *server;
struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
seq_printf(m, seq_printf(m,
...@@ -306,44 +316,55 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) ...@@ -306,44 +316,55 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
GlobalCurrentXid, GlobalMaxActiveXid); GlobalCurrentXid, GlobalMaxActiveXid);
i = 0; i = 0;
read_lock(&GlobalSMBSeslock); read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &GlobalTreeConnectionList) { list_for_each(tmp1, &cifs_tcp_ses_list) {
i++; server = list_entry(tmp1, struct TCP_Server_Info,
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); tcp_ses_list);
seq_printf(m, "\n%d) %s", i, tcon->treeName); list_for_each(tmp2, &server->smb_ses_list) {
if (tcon->tidStatus == CifsNeedReconnect) ses = list_entry(tmp2, struct cifsSesInfo,
seq_puts(m, "\tDISCONNECTED "); smb_ses_list);
seq_printf(m, "\nSMBs: %d Oplock Breaks: %d", list_for_each(tmp3, &ses->tcon_list) {
atomic_read(&tcon->num_smbs_sent), tcon = list_entry(tmp3,
atomic_read(&tcon->num_oplock_brks)); struct cifsTconInfo,
seq_printf(m, "\nReads: %d Bytes: %lld", tcon_list);
atomic_read(&tcon->num_reads), i++;
(long long)(tcon->bytes_read)); seq_printf(m, "\n%d) %s", i, tcon->treeName);
seq_printf(m, "\nWrites: %d Bytes: %lld", if (tcon->need_reconnect)
atomic_read(&tcon->num_writes), seq_puts(m, "\tDISCONNECTED ");
(long long)(tcon->bytes_written)); seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
seq_printf(m, atomic_read(&tcon->num_smbs_sent),
"\nLocks: %d HardLinks: %d Symlinks: %d", atomic_read(&tcon->num_oplock_brks));
atomic_read(&tcon->num_locks), seq_printf(m, "\nReads: %d Bytes: %lld",
atomic_read(&tcon->num_hardlinks), atomic_read(&tcon->num_reads),
atomic_read(&tcon->num_symlinks)); (long long)(tcon->bytes_read));
seq_printf(m, "\nWrites: %d Bytes: %lld",
seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", atomic_read(&tcon->num_writes),
atomic_read(&tcon->num_opens), (long long)(tcon->bytes_written));
atomic_read(&tcon->num_closes), seq_printf(m, "\nLocks: %d HardLinks: %d "
atomic_read(&tcon->num_deletes)); "Symlinks: %d",
seq_printf(m, "\nMkdirs: %d Rmdirs: %d", atomic_read(&tcon->num_locks),
atomic_read(&tcon->num_mkdirs), atomic_read(&tcon->num_hardlinks),
atomic_read(&tcon->num_rmdirs)); atomic_read(&tcon->num_symlinks));
seq_printf(m, "\nRenames: %d T2 Renames %d", seq_printf(m, "\nOpens: %d Closes: %d"
atomic_read(&tcon->num_renames), "Deletes: %d",
atomic_read(&tcon->num_t2renames)); atomic_read(&tcon->num_opens),
seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", atomic_read(&tcon->num_closes),
atomic_read(&tcon->num_ffirst), atomic_read(&tcon->num_deletes));
atomic_read(&tcon->num_fnext), seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
atomic_read(&tcon->num_fclose)); 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'); seq_putc(m, '\n');
return 0; return 0;
......
...@@ -106,7 +106,8 @@ static char *cifs_get_share_name(const char *node_name) ...@@ -106,7 +106,8 @@ static char *cifs_get_share_name(const char *node_name)
/** /**
* compose_mount_options - creates mount options for refferral * compose_mount_options - creates mount options for refferral
* @sb_mountdata: parent/root DFS mount options (template) * @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 * @devname: pointer for saving device name
* *
* creates mount options for submount based on template options sb_mountdata * creates mount options for submount based on template options sb_mountdata
...@@ -116,7 +117,8 @@ static char *cifs_get_share_name(const char *node_name) ...@@ -116,7 +117,8 @@ static char *cifs_get_share_name(const char *node_name)
* Caller is responcible for freeing retunrned value if it is not error. * Caller is responcible for freeing retunrned value if it is not error.
*/ */
static char *compose_mount_options(const char *sb_mountdata, static char *compose_mount_options(const char *sb_mountdata,
const char *ref_unc, struct dentry *dentry,
const struct dfs_info3_param *ref,
char **devname) char **devname)
{ {
int rc; int rc;
...@@ -126,11 +128,12 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -126,11 +128,12 @@ static char *compose_mount_options(const char *sb_mountdata,
char *srvIP = NULL; char *srvIP = NULL;
char sep = ','; char sep = ',';
int off, noff; int off, noff;
char *fullpath;
if (sb_mountdata == NULL) if (sb_mountdata == NULL)
return ERR_PTR(-EINVAL); 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); rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
if (rc != 0) { if (rc != 0) {
cERROR(1, ("%s: Failed to resolve server part of %s to IP", cERROR(1, ("%s: Failed to resolve server part of %s to IP",
...@@ -138,7 +141,12 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -138,7 +141,12 @@ static char *compose_mount_options(const char *sb_mountdata,
mountdata = ERR_PTR(rc); mountdata = ERR_PTR(rc);
goto compose_mount_options_out; goto compose_mount_options_out;
} }
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); mountdata = kzalloc(md_len+1, GFP_KERNEL);
if (mountdata == NULL) { if (mountdata == NULL) {
mountdata = ERR_PTR(-ENOMEM); mountdata = ERR_PTR(-ENOMEM);
...@@ -152,41 +160,56 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -152,41 +160,56 @@ static char *compose_mount_options(const char *sb_mountdata,
strncpy(mountdata, sb_mountdata, 5); strncpy(mountdata, sb_mountdata, 5);
off += 5; off += 5;
} }
while ((tkn_e = strchr(sb_mountdata+off, sep))) {
noff = (tkn_e - (sb_mountdata+off)) + 1; do {
if (strnicmp(sb_mountdata+off, "unc=", 4) == 0) { 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; off += noff;
continue; continue;
} }
if (strnicmp(sb_mountdata+off, "ip=", 3) == 0) { if (strnicmp(sb_mountdata + off, "ip=", 3) == 0) {
off += noff; off += noff;
continue; continue;
} }
if (strnicmp(sb_mountdata+off, "prefixpath=", 3) == 0) { if (strnicmp(sb_mountdata + off, "prefixpath=", 11) == 0) {
off += noff; off += noff;
continue; continue;
} }
strncat(mountdata, sb_mountdata+off, noff); strncat(mountdata, sb_mountdata + off, noff);
off += noff; off += noff;
} } while (tkn_e);
strcat(mountdata, sb_mountdata+off); strcat(mountdata, sb_mountdata + off);
mountdata[md_len] = '\0'; mountdata[md_len] = '\0';
/* copy new IP and ref share name */ /* 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, srvIP);
strcat(mountdata, ",unc="); strncat(mountdata, &sep, 1);
strcat(mountdata, "unc=");
strcat(mountdata, *devname); strcat(mountdata, *devname);
/* find & copy prefixpath */ /* find & copy prefixpath */
tkn_e = strchr(ref_unc+2, '\\'); tkn_e = strchr(ref->node_name + 2, '\\');
if (tkn_e) { if (tkn_e == NULL) /* invalid unc, missing share name*/
tkn_e = strchr(tkn_e+1, '\\'); goto compose_mount_options_out;
if (tkn_e) {
strcat(mountdata, ",prefixpath="); fullpath = build_path_from_dentry(dentry);
strcat(mountdata, tkn_e+1); 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));
} }
kfree(fullpath);
/*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/ /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/
/*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/ /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/
...@@ -198,7 +221,7 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -198,7 +221,7 @@ static char *compose_mount_options(const char *sb_mountdata,
static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, 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 cifs_sb_info *cifs_sb;
struct vfsmount *mnt; struct vfsmount *mnt;
...@@ -207,7 +230,7 @@ static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, ...@@ -207,7 +230,7 @@ static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent,
cifs_sb = CIFS_SB(dentry->d_inode->i_sb); cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
mountdata = compose_mount_options(cifs_sb->mountdata, mountdata = compose_mount_options(cifs_sb->mountdata,
ref_unc, &devname); dentry, ref, &devname);
if (IS_ERR(mountdata)) if (IS_ERR(mountdata))
return (struct vfsmount *)mountdata; return (struct vfsmount *)mountdata;
...@@ -310,7 +333,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) ...@@ -310,7 +333,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
} }
mnt = cifs_dfs_do_refmount(nd->path.mnt, mnt = cifs_dfs_do_refmount(nd->path.mnt,
nd->path.dentry, nd->path.dentry,
referrals[i].node_name); referrals + i);
cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p",
__func__, __func__,
referrals[i].node_name, mnt)); referrals[i].node_name, mnt));
......
...@@ -73,8 +73,8 @@ struct key_type cifs_spnego_key_type = { ...@@ -73,8 +73,8 @@ struct key_type cifs_spnego_key_type = {
* strlen(";sec=ntlmsspi") */ * strlen(";sec=ntlmsspi") */
#define MAX_MECH_STR_LEN 13 #define MAX_MECH_STR_LEN 13
/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */ /* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/128 */
#define MAX_IPV6_ADDR_LEN 42 #define MAX_IPV6_ADDR_LEN 43
/* strlen of "host=" */ /* strlen of "host=" */
#define HOST_KEY_LEN 5 #define HOST_KEY_LEN 5
......
...@@ -514,10 +514,11 @@ static void cifs_umount_begin(struct super_block *sb) ...@@ -514,10 +514,11 @@ static void cifs_umount_begin(struct super_block *sb)
tcon = cifs_sb->tcon; tcon = cifs_sb->tcon;
if (tcon == NULL) if (tcon == NULL)
return; return;
down(&tcon->tconSem);
if (atomic_read(&tcon->useCount) == 1) read_lock(&cifs_tcp_ses_lock);
if (tcon->tc_count == 1)
tcon->tidStatus = CifsExiting; tcon->tidStatus = CifsExiting;
up(&tcon->tconSem); read_unlock(&cifs_tcp_ses_lock);
/* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
/* cancel_notify_requests(tcon); */ /* cancel_notify_requests(tcon); */
...@@ -1013,7 +1014,7 @@ static int cifs_oplock_thread(void *dummyarg) ...@@ -1013,7 +1014,7 @@ static int cifs_oplock_thread(void *dummyarg)
not bother sending an oplock release if session not bother sending an oplock release if session
to server still is disconnected since oplock to server still is disconnected since oplock
already released by the server in that case */ already released by the server in that case */
if (pTcon->tidStatus != CifsNeedReconnect) { if (!pTcon->need_reconnect) {
rc = CIFSSMBLock(0, pTcon, netfid, rc = CIFSSMBLock(0, pTcon, netfid,
0 /* len */ , 0 /* offset */, 0, 0 /* len */ , 0 /* offset */, 0,
0, LOCKING_ANDX_OPLOCK_RELEASE, 0, LOCKING_ANDX_OPLOCK_RELEASE,
...@@ -1031,24 +1032,24 @@ static int cifs_oplock_thread(void *dummyarg) ...@@ -1031,24 +1032,24 @@ static int cifs_oplock_thread(void *dummyarg)
static int cifs_dnotify_thread(void *dummyarg) static int cifs_dnotify_thread(void *dummyarg)
{ {
struct list_head *tmp; struct list_head *tmp;
struct cifsSesInfo *ses; struct TCP_Server_Info *server;
do { do {
if (try_to_freeze()) if (try_to_freeze())
continue; continue;
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(15*HZ); schedule_timeout(15*HZ);
read_lock(&GlobalSMBSeslock);
/* check if any stuck requests that need /* check if any stuck requests that need
to be woken up and wakeq so the to be woken up and wakeq so the
thread can wake up and error out */ thread can wake up and error out */
list_for_each(tmp, &GlobalSMBSessionList) { read_lock(&cifs_tcp_ses_lock);
ses = list_entry(tmp, struct cifsSesInfo, list_for_each(tmp, &cifs_tcp_ses_list) {
cifsSessionList); server = list_entry(tmp, struct TCP_Server_Info,
if (ses->server && atomic_read(&ses->server->inFlight)) tcp_ses_list);
wake_up_all(&ses->server->response_q); if (atomic_read(&server->inFlight))
wake_up_all(&server->response_q);
} }
read_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock);
} while (!kthread_should_stop()); } while (!kthread_should_stop());
return 0; return 0;
...@@ -1059,9 +1060,7 @@ init_cifs(void) ...@@ -1059,9 +1060,7 @@ init_cifs(void)
{ {
int rc = 0; int rc = 0;
cifs_proc_init(); cifs_proc_init();
/* INIT_LIST_HEAD(&GlobalServerList);*/ /* BB not implemented yet */ INIT_LIST_HEAD(&cifs_tcp_ses_list);
INIT_LIST_HEAD(&GlobalSMBSessionList);
INIT_LIST_HEAD(&GlobalTreeConnectionList);
INIT_LIST_HEAD(&GlobalOplock_Q); INIT_LIST_HEAD(&GlobalOplock_Q);
#ifdef CONFIG_CIFS_EXPERIMENTAL #ifdef CONFIG_CIFS_EXPERIMENTAL
INIT_LIST_HEAD(&GlobalDnotifyReqList); INIT_LIST_HEAD(&GlobalDnotifyReqList);
...@@ -1089,6 +1088,7 @@ init_cifs(void) ...@@ -1089,6 +1088,7 @@ init_cifs(void)
GlobalMaxActiveXid = 0; GlobalMaxActiveXid = 0;
memset(Local_System_Name, 0, 15); memset(Local_System_Name, 0, 15);
rwlock_init(&GlobalSMBSeslock); rwlock_init(&GlobalSMBSeslock);
rwlock_init(&cifs_tcp_ses_lock);
spin_lock_init(&GlobalMid_Lock); spin_lock_init(&GlobalMid_Lock);
if (cifs_max_pending < 2) { if (cifs_max_pending < 2) {
......
...@@ -85,8 +85,7 @@ enum securityEnum { ...@@ -85,8 +85,7 @@ enum securityEnum {
}; };
enum protocolEnum { enum protocolEnum {
IPV4 = 0, TCP = 0,
IPV6,
SCTP SCTP
/* Netbios frames protocol not supported at this time */ /* Netbios frames protocol not supported at this time */
}; };
...@@ -122,6 +121,9 @@ struct cifs_cred { ...@@ -122,6 +121,9 @@ struct cifs_cred {
*/ */
struct TCP_Server_Info { 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 */ /* 15 character server name + 0x20 16th byte indicating type = srv */
char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2];
...@@ -143,7 +145,6 @@ struct TCP_Server_Info { ...@@ -143,7 +145,6 @@ struct TCP_Server_Info {
bool svlocal:1; /* local server or remote */ bool svlocal:1; /* local server or remote */
bool noblocksnd; /* use blocking sendmsg */ bool noblocksnd; /* use blocking sendmsg */
bool noautotune; /* do not autotune send buf sizes */ 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 */ atomic_t inFlight; /* number of requests on the wire to server */
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
atomic_t inSend; /* requests trying to send */ atomic_t inSend; /* requests trying to send */
...@@ -194,13 +195,14 @@ struct cifsUidInfo { ...@@ -194,13 +195,14 @@ struct cifsUidInfo {
* Session structure. One of these for each uid session with a particular host * Session structure. One of these for each uid session with a particular host
*/ */
struct cifsSesInfo { struct cifsSesInfo {
struct list_head cifsSessionList; struct list_head smb_ses_list;
struct list_head tcon_list;
struct semaphore sesSem; struct semaphore sesSem;
#if 0 #if 0
struct cifsUidInfo *uidInfo; /* pointer to user info */ struct cifsUidInfo *uidInfo; /* pointer to user info */
#endif #endif
struct TCP_Server_Info *server; /* pointer to server info */ 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; enum statusEnum status;
unsigned overrideSecFlg; /* if non-zero override global sec flags */ unsigned overrideSecFlg; /* if non-zero override global sec flags */
__u16 ipc_tid; /* special tid for connection to IPC share */ __u16 ipc_tid; /* special tid for connection to IPC share */
...@@ -216,6 +218,7 @@ struct cifsSesInfo { ...@@ -216,6 +218,7 @@ struct cifsSesInfo {
char userName[MAX_USERNAME_SIZE + 1]; char userName[MAX_USERNAME_SIZE + 1];
char *domainName; char *domainName;
char *password; char *password;
bool need_reconnect:1; /* connection reset, uid now invalid */
}; };
/* no more than one of the following three session flags may be set */ /* no more than one of the following three session flags may be set */
#define CIFS_SES_NT4 1 #define CIFS_SES_NT4 1
...@@ -230,16 +233,15 @@ struct cifsSesInfo { ...@@ -230,16 +233,15 @@ struct cifsSesInfo {
* session * session
*/ */
struct cifsTconInfo { struct cifsTconInfo {
struct list_head cifsConnectionList; struct list_head tcon_list;
int tc_count;
struct list_head openFileList; struct list_head openFileList;
struct semaphore tconSem;
struct cifsSesInfo *ses; /* pointer to session associated with */ struct cifsSesInfo *ses; /* pointer to session associated with */
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
char *nativeFileSystem; char *nativeFileSystem;
__u16 tid; /* The 2 byte tree id */ __u16 tid; /* The 2 byte tree id */
__u16 Flags; /* optional support bits */ __u16 Flags; /* optional support bits */
enum statusEnum tidStatus; enum statusEnum tidStatus;
atomic_t useCount; /* how many explicit/implicit mounts to share */
#ifdef CONFIG_CIFS_STATS #ifdef CONFIG_CIFS_STATS
atomic_t num_smbs_sent; atomic_t num_smbs_sent;
atomic_t num_writes; atomic_t num_writes;
...@@ -288,6 +290,7 @@ struct cifsTconInfo { ...@@ -288,6 +290,7 @@ struct cifsTconInfo {
bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol
for this mount even if server would support */ for this mount even if server would support */
bool local_lease:1; /* check leases (only) on local system not remote */ 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)? */ /* BB add field for back pointer to sb struct(s)? */
}; };
...@@ -588,21 +591,21 @@ require use of the stronger protocol */ ...@@ -588,21 +591,21 @@ require use of the stronger protocol */
#endif #endif
/* /*
* The list of servers that did not respond with NT LM 0.12. * the list of TCP_Server_Info structures, ie each of the sockets
* This list helps improve performance and eliminate the messages indicating * connecting our client to a distinct server (ip address), is
* that we had a communications error talking to the server in this list. * 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 list_head cifs_tcp_ses_list;
/* GLOBAL_EXTERN struct servers_not_supported *NotSuppList; */
/* /*
* 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 */ GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
GLOBAL_EXTERN struct list_head GlobalOplock_Q; GLOBAL_EXTERN struct list_head GlobalOplock_Q;
......
...@@ -190,10 +190,10 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, ...@@ -190,10 +190,10 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
/* need to prevent multiple threads trying to /* need to prevent multiple threads trying to
simultaneously reconnect the same SMB session */ simultaneously reconnect the same SMB session */
down(&tcon->ses->sesSem); down(&tcon->ses->sesSem);
if (tcon->ses->status == CifsNeedReconnect) if (tcon->ses->need_reconnect)
rc = cifs_setup_session(0, tcon->ses, rc = cifs_setup_session(0, tcon->ses,
nls_codepage); nls_codepage);
if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { if (!rc && (tcon->need_reconnect)) {
mark_open_files_invalid(tcon); mark_open_files_invalid(tcon);
rc = CIFSTCon(0, tcon->ses, tcon->treeName, rc = CIFSTCon(0, tcon->ses, tcon->treeName,
tcon, nls_codepage); tcon, nls_codepage);
...@@ -295,7 +295,7 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, ...@@ -295,7 +295,7 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
check for tcp and smb session status done differently check for tcp and smb session status done differently
for those three - in the calling routine */ for those three - in the calling routine */
if (tcon) { if (tcon) {
if (tcon->tidStatus == CifsExiting) { if (tcon->need_reconnect) {
/* only tree disconnect, open, and write, /* only tree disconnect, open, and write,
(and ulogoff which does not have tcon) (and ulogoff which does not have tcon)
are allowed as we start force umount */ are allowed as we start force umount */
...@@ -337,10 +337,10 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, ...@@ -337,10 +337,10 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
/* need to prevent multiple threads trying to /* need to prevent multiple threads trying to
simultaneously reconnect the same SMB session */ simultaneously reconnect the same SMB session */
down(&tcon->ses->sesSem); down(&tcon->ses->sesSem);
if (tcon->ses->status == CifsNeedReconnect) if (tcon->ses->need_reconnect)
rc = cifs_setup_session(0, tcon->ses, rc = cifs_setup_session(0, tcon->ses,
nls_codepage); nls_codepage);
if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { if (!rc && (tcon->need_reconnect)) {
mark_open_files_invalid(tcon); mark_open_files_invalid(tcon);
rc = CIFSTCon(0, tcon->ses, tcon->treeName, rc = CIFSTCon(0, tcon->ses, tcon->treeName,
tcon, nls_codepage); tcon, nls_codepage);
...@@ -664,8 +664,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) ...@@ -664,8 +664,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
rc = -EIO; rc = -EIO;
goto neg_err_exit; goto neg_err_exit;
} }
read_lock(&cifs_tcp_ses_lock);
if (server->socketUseCount.counter > 1) { if (server->srv_count > 1) {
read_unlock(&cifs_tcp_ses_lock);
if (memcmp(server->server_GUID, if (memcmp(server->server_GUID,
pSMBr->u.extended_response. pSMBr->u.extended_response.
GUID, 16) != 0) { GUID, 16) != 0) {
...@@ -674,9 +675,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) ...@@ -674,9 +675,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
pSMBr->u.extended_response.GUID, pSMBr->u.extended_response.GUID,
16); 16);
} }
} else } else {
read_unlock(&cifs_tcp_ses_lock);
memcpy(server->server_GUID, memcpy(server->server_GUID,
pSMBr->u.extended_response.GUID, 16); pSMBr->u.extended_response.GUID, 16);
}
if (count == 16) { if (count == 16) {
server->secType = RawNTLMSSP; server->secType = RawNTLMSSP;
...@@ -739,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) ...@@ -739,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
int rc = 0; int rc = 0;
cFYI(1, ("In tree disconnect")); 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); /* BB: do we need to check this? These should never be NULL. */
if (atomic_read(&tcon->useCount) > 0) { if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
up(&tcon->tconSem); return -EIO;
return -EBUSY;
}
/* No need to return error on this operation if tid invalidated and /*
closed on server already e.g. due to tcp session crashing */ * No need to return error on this operation if tid invalidated and
if (tcon->tidStatus == CifsNeedReconnect) { * closed on server already e.g. due to tcp session crashing. Also,
up(&tcon->tconSem); * the tcon is no longer on the list, so no need to take lock before
* checking this.
*/
if (tcon->need_reconnect)
return 0; 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, rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
(void **)&smb_buffer); (void **)&smb_buffer);
if (rc) { if (rc)
up(&tcon->tconSem);
return rc; return rc;
}
rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
if (rc) if (rc)
cFYI(1, ("Tree disconnect failed %d", rc)); cFYI(1, ("Tree disconnect failed %d", rc));
up(&tcon->tconSem);
/* No need to return error on this operation if tid invalidated and /* 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) if (rc == -EAGAIN)
rc = 0; rc = 0;
...@@ -796,43 +780,36 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) ...@@ -796,43 +780,36 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
int rc = 0; int rc = 0;
cFYI(1, ("In SMBLogoff for session disconnect")); 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; return -EIO;
atomic_dec(&ses->inUse); down(&ses->sesSem);
if (atomic_read(&ses->inUse) > 0) { if (ses->need_reconnect)
up(&ses->sesSem); goto session_already_dead; /* no need to send SMBlogoff if uid
return -EBUSY; already closed due to reconnect */
}
rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
if (rc) { if (rc) {
up(&ses->sesSem); up(&ses->sesSem);
return rc; 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)) (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
}
pSMB->hdr.Uid = ses->Suid; pSMB->hdr.Uid = ses->Suid;
pSMB->AndXCommand = 0xFF; pSMB->AndXCommand = 0xFF;
rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
if (ses->server) { session_already_dead:
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;
}
}
up(&ses->sesSem); up(&ses->sesSem);
/* if session dead then we do not need to do ulogoff, /* if session dead then we do not need to do ulogoff,
...@@ -3922,6 +3899,27 @@ CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, ...@@ -3922,6 +3899,27 @@ CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
return rc; 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 /* parses DFS refferal V3 structure
* caller is responsible for freeing target_nodes * caller is responsible for freeing target_nodes
* returns: * returns:
...@@ -3932,7 +3930,8 @@ static int ...@@ -3932,7 +3930,8 @@ static int
parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
unsigned int *num_of_nodes, unsigned int *num_of_nodes,
struct dfs_info3_param **target_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; int i, rc = 0;
char *data_end; char *data_end;
...@@ -3983,7 +3982,17 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, ...@@ -3983,7 +3982,17 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
struct dfs_info3_param *node = (*target_nodes)+i; struct dfs_info3_param *node = (*target_nodes)+i;
node->flags = le16_to_cpu(pSMBr->DFSFlags); node->flags = le16_to_cpu(pSMBr->DFSFlags);
node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); if (is_unicode) {
__le16 *tmp = kmalloc(strlen(searchName)*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->server_type = le16_to_cpu(ref->ServerType);
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
...@@ -4116,7 +4125,8 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, ...@@ -4116,7 +4125,8 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
/* parse returned result into more usable form */ /* parse returned result into more usable form */
rc = parse_DFS_referrals(pSMBr, num_of_nodes, rc = parse_DFS_referrals(pSMBr, num_of_nodes,
target_nodes, nls_codepage); target_nodes, nls_codepage, remap,
searchName);
GetDFSRefExit: GetDFSRefExit:
cifs_buf_release(pSMB); cifs_buf_release(pSMB);
......
...@@ -124,7 +124,7 @@ static int ...@@ -124,7 +124,7 @@ static int
cifs_reconnect(struct TCP_Server_Info *server) cifs_reconnect(struct TCP_Server_Info *server)
{ {
int rc = 0; int rc = 0;
struct list_head *tmp; struct list_head *tmp, *tmp2;
struct cifsSesInfo *ses; struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct mid_q_entry *mid_entry; struct mid_q_entry *mid_entry;
...@@ -144,23 +144,17 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -144,23 +144,17 @@ cifs_reconnect(struct TCP_Server_Info *server)
/* before reconnecting the tcp session, mark the smb session (uid) /* before reconnecting the tcp session, mark the smb session (uid)
and the tid bad so they are not used until reconnected */ and the tid bad so they are not used until reconnected */
read_lock(&GlobalSMBSeslock); read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &GlobalSMBSessionList) { list_for_each(tmp, &server->smb_ses_list) {
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
if (ses->server) { ses->need_reconnect = true;
if (ses->server == server) { ses->ipc_tid = 0;
ses->status = CifsNeedReconnect; list_for_each(tmp2, &ses->tcon_list) {
ses->ipc_tid = 0; 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 */ /* do not want to be sending data on a socket we are freeing */
down(&server->tcpSem); down(&server->tcpSem);
if (server->ssocket) { if (server->ssocket) {
...@@ -193,7 +187,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -193,7 +187,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
while ((server->tcpStatus != CifsExiting) && while ((server->tcpStatus != CifsExiting) &&
(server->tcpStatus != CifsGood)) { (server->tcpStatus != CifsGood)) {
try_to_freeze(); try_to_freeze();
if (server->protocolType == IPV6) { if (server->addr.sockAddr6.sin6_family == AF_INET6) {
rc = ipv6_connect(&server->addr.sockAddr6, rc = ipv6_connect(&server->addr.sockAddr6,
&server->ssocket, server->noautotune); &server->ssocket, server->noautotune);
} else { } else {
...@@ -417,9 +411,14 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -417,9 +411,14 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
msleep(1); /* minimum sleep to prevent looping msleep(1); /* minimum sleep to prevent looping
allowing socket to clear and app threads to set allowing socket to clear and app threads to set
tcpStatus CifsNeedReconnect if server hung */ 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; goto incomplete_rcv;
else } else
continue; continue;
} else if (length <= 0) { } else if (length <= 0) {
if (server->tcpStatus == CifsNew) { if (server->tcpStatus == CifsNew) {
...@@ -654,6 +653,11 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -654,6 +653,11 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
} }
} /* end while !EXITING */ } /* 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); spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting; server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -686,29 +690,29 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -686,29 +690,29 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
if (smallbuf) /* no sense logging a debug message if NULL */ if (smallbuf) /* no sense logging a debug message if NULL */
cifs_small_buf_release(smallbuf); 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)) { if (list_empty(&server->pending_mid_q)) {
/* loop through server session structures attached to this and /* loop through server session structures attached to this and
mark them dead */ mark them dead */
list_for_each(tmp, &GlobalSMBSessionList) { list_for_each(tmp, &server->smb_ses_list) {
ses = ses = list_entry(tmp, struct cifsSesInfo,
list_entry(tmp, struct cifsSesInfo, smb_ses_list);
cifsSessionList); ses->status = CifsExiting;
if (ses->server == server) { ses->server = NULL;
ses->status = CifsExiting;
ses->server = NULL;
}
} }
read_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock);
} else { } else {
/* although we can not zero the server struct pointer yet, /* although we can not zero the server struct pointer yet,
since there are active requests which may depnd on them, since there are active requests which may depnd on them,
mark the corresponding SMB sessions as exiting too */ 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, ses = list_entry(tmp, struct cifsSesInfo,
cifsSessionList); smb_ses_list);
if (ses->server == server) ses->status = CifsExiting;
ses->status = CifsExiting;
} }
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
...@@ -723,7 +727,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -723,7 +727,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
} }
} }
spin_unlock(&GlobalMid_Lock); 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 */ /* 1/8th of sec is more than enough time for them to exit */
msleep(125); msleep(125);
} }
...@@ -745,14 +749,13 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -745,14 +749,13 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
if there are any pointing to this (e.g if there are any pointing to this (e.g
if a crazy root user tried to kill cifsd if a crazy root user tried to kill cifsd
kernel thread explicitly this might happen) */ kernel thread explicitly this might happen) */
write_lock(&GlobalSMBSeslock); /* BB: This shouldn't be necessary, see above */
list_for_each(tmp, &GlobalSMBSessionList) { read_lock(&cifs_tcp_ses_lock);
ses = list_entry(tmp, struct cifsSesInfo, list_for_each(tmp, &server->smb_ses_list) {
cifsSessionList); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
if (ses->server == server) ses->server = NULL;
ses->server = NULL;
} }
write_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock);
kfree(server->hostname); kfree(server->hostname);
task_to_wake = xchg(&server->tsk, NULL); task_to_wake = xchg(&server->tsk, NULL);
...@@ -1352,94 +1355,158 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1352,94 +1355,158 @@ cifs_parse_mount_options(char *options, const char *devname,
return 0; return 0;
} }
static struct cifsSesInfo * static struct TCP_Server_Info *
cifs_find_tcp_session(struct in_addr *target_ip_addr, cifs_find_tcp_session(struct sockaddr *addr)
struct in6_addr *target_ip6_addr,
char *userName, struct TCP_Server_Info **psrvTcp)
{ {
struct list_head *tmp; struct list_head *tmp;
struct cifsSesInfo *ses; struct TCP_Server_Info *server;
struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
*psrvTcp = NULL; 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); if (addr->sa_family == AF_INET &&
list_for_each(tmp, &GlobalSMBSessionList) { (addr4->sin_addr.s_addr !=
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); server->addr.sockAddr.sin_addr.s_addr))
if (!ses->server) continue;
else if (addr->sa_family == AF_INET6 &&
memcmp(&server->addr.sockAddr6.sin6_addr,
&addr6->sin6_addr, sizeof(addr6->sin6_addr)))
continue; continue;
if (target_ip_addr && ++server->srv_count;
ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr) write_unlock(&cifs_tcp_ses_lock);
continue; cFYI(1, ("Existing tcp session with server found"));
else if (target_ip6_addr && return server;
memcmp(&ses->server->addr.sockAddr6.sin6_addr, }
target_ip6_addr, sizeof(*target_ip6_addr))) write_unlock(&cifs_tcp_ses_lock);
continue; return NULL;
/* BB lock server and tcp session; increment use count here?? */ }
/* found a match on the TCP session */ static void
*psrvTcp = ses->server; cifs_put_tcp_session(struct TCP_Server_Info *server)
{
struct task_struct *task;
/* BB check if reconnection needed */ write_lock(&cifs_tcp_ses_lock);
if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) { if (--server->srv_count > 0) {
read_unlock(&GlobalSMBSeslock); write_unlock(&cifs_tcp_ses_lock);
/* Found exact match on both TCP and return;
SMB sessions */
return ses;
}
/* else tcp and smb sessions need reconnection */
} }
read_unlock(&GlobalSMBSeslock);
return NULL; list_del_init(&server->tcp_ses_list);
write_unlock(&cifs_tcp_ses_lock);
spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock);
task = xchg(&server->tsk, NULL);
if (task)
force_sig(SIGKILL, task);
} }
static struct cifsTconInfo * static struct cifsSesInfo *
find_unc(__be32 new_target_ip_addr, char *uncName, char *userName) cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
{ {
struct list_head *tmp; struct list_head *tmp;
struct cifsTconInfo *tcon; struct cifsSesInfo *ses;
__be32 old_ip;
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalTreeConnectionList) { write_lock(&cifs_tcp_ses_lock);
cFYI(1, ("Next tcon")); list_for_each(tmp, &server->smb_ses_list) {
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
if (!tcon->ses || !tcon->ses->server) if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
continue; continue;
old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr; ++ses->ses_count;
cFYI(1, ("old ip addr: %x == new ip %x ?", write_unlock(&cifs_tcp_ses_lock);
old_ip, new_target_ip_addr)); return ses;
}
write_unlock(&cifs_tcp_ses_lock);
return NULL;
}
if (old_ip != new_target_ip_addr) static void
continue; 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? */ write_lock(&cifs_tcp_ses_lock);
/* found a match on the TCP session */ if (--ses->ses_count > 0) {
/* BB check if reconnection needed */ write_unlock(&cifs_tcp_ses_lock);
cFYI(1, ("IP match, old UNC: %s new: %s", return;
tcon->treeName, uncName)); }
if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE)) list_del_init(&ses->smb_ses_list);
continue; write_unlock(&cifs_tcp_ses_lock);
cFYI(1, ("and old usr: %s new: %s", if (ses->status == CifsGood) {
tcon->treeName, uncName)); xid = GetXid();
CIFSSMBLogoff(xid, ses);
_FreeXid(xid);
}
sesInfoFree(ses);
cifs_put_tcp_session(server);
}
if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE)) static struct cifsTconInfo *
cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
{
struct list_head *tmp;
struct cifsTconInfo *tcon;
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; continue;
/* matched smb session (user name) */ ++tcon->tc_count;
read_unlock(&GlobalSMBSeslock); write_unlock(&cifs_tcp_ses_lock);
return tcon; return tcon;
} }
write_unlock(&cifs_tcp_ses_lock);
read_unlock(&GlobalSMBSeslock);
return NULL; 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 int
get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
const struct nls_table *nls_codepage, unsigned int *pnum_referrals, const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
...@@ -1876,14 +1943,90 @@ convert_delimiter(char *path, char delim) ...@@ -1876,14 +1943,90 @@ convert_delimiter(char *path, char delim)
} }
} }
static void static void setup_cifs_sb(struct smb_vol *pvolume_info,
kill_cifsd(struct TCP_Server_Info *server) struct cifs_sb_info *cifs_sb)
{ {
struct task_struct *task; if (pvolume_info->rsize > CIFSMaxBufSize) {
cERROR(1, ("rsize %d too large, using MaxBufSize",
task = xchg(&server->tsk, NULL); pvolume_info->rsize));
if (task) cifs_sb->rsize = CIFSMaxBufSize;
force_sig(SIGKILL, task); } 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->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 int
...@@ -1892,13 +2035,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -1892,13 +2035,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
{ {
int rc = 0; int rc = 0;
int xid; int xid;
int address_type = AF_INET;
struct socket *csocket = NULL; struct socket *csocket = NULL;
struct sockaddr_in sin_server; struct sockaddr addr;
struct sockaddr_in6 sin_server6; struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
struct smb_vol volume_info; struct smb_vol volume_info;
struct cifsSesInfo *pSesInfo = NULL; struct cifsSesInfo *pSesInfo = NULL;
struct cifsSesInfo *existingCifsSes = NULL;
struct cifsTconInfo *tcon = NULL; struct cifsTconInfo *tcon = NULL;
struct TCP_Server_Info *srvTcp = NULL; struct TCP_Server_Info *srvTcp = NULL;
...@@ -1906,6 +2048,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -1906,6 +2048,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
memset(&addr, 0, sizeof(struct sockaddr));
memset(&volume_info, 0, sizeof(struct smb_vol)); 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; rc = -EINVAL;
...@@ -1928,16 +2071,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -1928,16 +2071,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
if (volume_info.UNCip && volume_info.UNC) { if (volume_info.UNCip && volume_info.UNC) {
rc = cifs_inet_pton(AF_INET, volume_info.UNCip, rc = cifs_inet_pton(AF_INET, volume_info.UNCip,
&sin_server.sin_addr.s_addr); &sin_server->sin_addr.s_addr);
if (rc <= 0) { if (rc <= 0) {
/* not ipv4 address, try ipv6 */ /* not ipv4 address, try ipv6 */
rc = cifs_inet_pton(AF_INET6, volume_info.UNCip, rc = cifs_inet_pton(AF_INET6, volume_info.UNCip,
&sin_server6.sin6_addr.in6_u); &sin_server6->sin6_addr.in6_u);
if (rc > 0) if (rc > 0)
address_type = AF_INET6; addr.sa_family = AF_INET6;
} else { } else {
address_type = AF_INET; addr.sa_family = AF_INET;
} }
if (rc <= 0) { if (rc <= 0) {
...@@ -1977,41 +2120,25 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -1977,41 +2120,25 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
} }
} }
if (address_type == AF_INET) srvTcp = cifs_find_tcp_session(&addr);
existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr, if (!srvTcp) { /* create socket */
NULL /* no ipv6 addr */, if (addr.sa_family == AF_INET6) {
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;
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")); cFYI(1, ("attempting ipv6 connect"));
/* BB should we allow ipv6 on port 139? */ /* BB should we allow ipv6 on port 139? */
/* other OS never observed in Wild doing 139 with v6 */ /* other OS never observed in Wild doing 139 with v6 */
rc = ipv6_connect(&sin_server6, &csocket, sin_server6->sin6_port = htons(volume_info.port);
rc = ipv6_connect(sin_server6, &csocket,
volume_info.noblocksnd); volume_info.noblocksnd);
} else } else {
rc = ipv4_connect(&sin_server, &csocket, sin_server->sin_port = htons(volume_info.port);
rc = ipv4_connect(sin_server, &csocket,
volume_info.source_rfc1001_name, volume_info.source_rfc1001_name,
volume_info.target_rfc1001_name, volume_info.target_rfc1001_name,
volume_info.noblocksnd, volume_info.noblocksnd,
volume_info.noautotune); volume_info.noautotune);
}
if (rc < 0) { if (rc < 0) {
cERROR(1, ("Error connecting to IPv4 socket. " cERROR(1, ("Error connecting to socket. "
"Aborting operation")); "Aborting operation"));
if (csocket != NULL) if (csocket != NULL)
sock_release(csocket); sock_release(csocket);
...@@ -2026,12 +2153,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2026,12 +2153,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
} else { } else {
srvTcp->noblocksnd = volume_info.noblocksnd; srvTcp->noblocksnd = volume_info.noblocksnd;
srvTcp->noautotune = volume_info.noautotune; srvTcp->noautotune = volume_info.noautotune;
memcpy(&srvTcp->addr.sockAddr, &sin_server, if (addr.sa_family == AF_INET6)
sizeof(struct sockaddr_in)); memcpy(&srvTcp->addr.sockAddr6, sin_server6,
sizeof(struct sockaddr_in6));
else
memcpy(&srvTcp->addr.sockAddr, sin_server,
sizeof(struct sockaddr_in));
atomic_set(&srvTcp->inFlight, 0); atomic_set(&srvTcp->inFlight, 0);
/* BB Add code for ipv6 case too */ /* BB Add code for ipv6 case too */
srvTcp->ssocket = csocket; srvTcp->ssocket = csocket;
srvTcp->protocolType = IPV4;
srvTcp->hostname = extract_hostname(volume_info.UNC); srvTcp->hostname = extract_hostname(volume_info.UNC);
if (IS_ERR(srvTcp->hostname)) { if (IS_ERR(srvTcp->hostname)) {
rc = PTR_ERR(srvTcp->hostname); rc = PTR_ERR(srvTcp->hostname);
...@@ -2061,15 +2191,28 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2061,15 +2191,28 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
memcpy(srvTcp->server_RFC1001_name, memcpy(srvTcp->server_RFC1001_name,
volume_info.target_rfc1001_name, 16); volume_info.target_rfc1001_name, 16);
srvTcp->sequence_number = 0; srvTcp->sequence_number = 0;
INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
INIT_LIST_HEAD(&srvTcp->smb_ses_list);
++srvTcp->srv_count;
write_lock(&cifs_tcp_ses_lock);
list_add(&srvTcp->tcp_ses_list,
&cifs_tcp_ses_list);
write_unlock(&cifs_tcp_ses_lock);
} }
} }
if (existingCifsSes) { pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
pSesInfo = existingCifsSes; if (pSesInfo) {
cFYI(1, ("Existing smb sess found (status=%d)", cFYI(1, ("Existing smb sess found (status=%d)",
pSesInfo->status)); 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); down(&pSesInfo->sesSem);
if (pSesInfo->status == CifsNeedReconnect) { if (pSesInfo->need_reconnect) {
cFYI(1, ("Session needs reconnect")); cFYI(1, ("Session needs reconnect"));
rc = cifs_setup_session(xid, pSesInfo, rc = cifs_setup_session(xid, pSesInfo,
cifs_sb->local_nls); cifs_sb->local_nls);
...@@ -2078,187 +2221,101 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2078,187 +2221,101 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
} else if (!rc) { } else if (!rc) {
cFYI(1, ("Existing smb sess not found")); cFYI(1, ("Existing smb sess not found"));
pSesInfo = sesInfoAlloc(); pSesInfo = sesInfoAlloc();
if (pSesInfo == NULL) if (pSesInfo == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
else { goto mount_fail_check;
pSesInfo->server = srvTcp; }
sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
NIPQUAD(sin_server.sin_addr.s_addr)); /* new SMB session uses our srvTcp ref */
} pSesInfo->server = srvTcp;
sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
NIPQUAD(sin_server->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 = 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);
}
pSesInfo->linux_uid = volume_info.linux_uid;
pSesInfo->overrideSecFlg = volume_info.secFlg;
down(&pSesInfo->sesSem);
if (!rc) { /* BB FIXME need to pass vol->secFlgs BB */
/* volume_info.password freed at unmount */ rc = cifs_setup_session(xid, pSesInfo,
if (volume_info.password) { cifs_sb->local_nls);
pSesInfo->password = volume_info.password; up(&pSesInfo->sesSem);
/* 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);
}
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);
}
} }
/* search for existing tcon to this server share */ /* search for existing tcon to this server share */
if (!rc) { if (!rc) {
if (volume_info.rsize > CIFSMaxBufSize) { setup_cifs_sb(&volume_info, cifs_sb);
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;
}
if ((volume_info.cifs_acl) && (volume_info.dynperm)) tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
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);
if (tcon) { if (tcon) {
cFYI(1, ("Found match on UNC path")); cFYI(1, ("Found match on UNC path"));
/* we can have only one retry value for a connection /* existing tcon already has a reference */
to a share so for resources mounted more than once cifs_put_smb_ses(pSesInfo);
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) if (tcon->seal != volume_info.seal)
cERROR(1, ("transport encryption setting " cERROR(1, ("transport encryption setting "
"conflicts with existing tid")); "conflicts with existing tid"));
} else { } else {
tcon = tconInfoAlloc(); tcon = tconInfoAlloc();
if (tcon == NULL) if (tcon == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
else { goto mount_fail_check;
/* check for null share name ie connecting to }
* dfs root */ tcon->ses = pSesInfo;
/* BB check if this works for exactly length /* check for null share name ie connect to dfs root */
* three strings */ if ((strchr(volume_info.UNC + 3, '\\') == NULL)
if ((strchr(volume_info.UNC + 3, '\\') == NULL) && (strchr(volume_info.UNC + 3, '/') == NULL)) {
&& (strchr(volume_info.UNC + 3, '/') == /* rc = connect_to_dfs_path(...) */
NULL)) { cFYI(1, ("DFS root not supported"));
/* rc = connect_to_dfs_path(xid, pSesInfo, rc = -ENODEV;
"", cifs_sb->local_nls, goto mount_fail_check;
cifs_sb->mnt_cifs_flags & } else {
CIFS_MOUNT_MAP_SPECIAL_CHR);*/ /* BB Do we need to wrap sesSem around
cFYI(1, ("DFS root not supported")); * this TCon call and Unix SetFS as
rc = -ENODEV; * we do on SessSetup and reconnect? */
goto out; rc = CIFSTCon(xid, pSesInfo, volume_info.UNC,
} else { tcon, cifs_sb->local_nls);
/* BB Do we need to wrap sesSem around cFYI(1, ("CIFS Tcon rc = %d", rc));
* this TCon call and Unix SetFS as if (volume_info.nodfs) {
* we do on SessSetup and reconnect? */ tcon->Flags &= ~SMB_SHARE_IS_IN_DFS;
rc = CIFSTCon(xid, pSesInfo, cFYI(1, ("DFS disabled (%d)",
volume_info.UNC, tcon->Flags));
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) {
atomic_inc(&pSesInfo->inUse);
tcon->retry = volume_info.retry;
tcon->nocase = volume_info.nocase;
tcon->seal = volume_info.seal;
} }
} }
} 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) {
if (pSesInfo->capabilities & CAP_LARGE_FILES) { if (pSesInfo->capabilities & CAP_LARGE_FILES) {
...@@ -2270,80 +2327,49 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2270,80 +2327,49 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* BB FIXME fix time_gran to be larger for LANMAN sessions */ /* BB FIXME fix time_gran to be larger for LANMAN sessions */
sb->s_time_gran = 100; 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 (rc) {
/* if session setup failed, use count is zero but /* If find_unc succeeded then rc == 0 so we can not end */
we still need to free cifsd thread */ /* up accidently freeing someone elses tcon struct */
if (atomic_read(&srvTcp->socketUseCount) == 0) { if (tcon)
spin_lock(&GlobalMid_Lock); cifs_put_tcon(tcon);
srvTcp->tcpStatus = CifsExiting; else if (pSesInfo)
spin_unlock(&GlobalMid_Lock); cifs_put_smb_ses(pSesInfo);
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);
else else
tcon->unix_ext = 0; /* server does not support them */ cifs_put_tcp_session(srvTcp);
goto out;
}
cifs_sb->tcon = tcon;
/* convert forward to back slashes in prepath here if needed */ /* do not care if following two calls succeed - informational */
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) if (!tcon->ipc) {
convert_delimiter(cifs_sb->prepath, CIFSSMBQFSDeviceInfo(xid, tcon);
CIFS_DIR_SEP(cifs_sb)); CIFSSMBQFSAttributeInfo(xid, tcon);
}
if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { /* tell server which Unix caps we support */
cifs_sb->rsize = 1024 * 127; if (tcon->ses->capabilities & CAP_UNIX)
cFYI(DBG2, /* reset of caps checks mount to see if unix extensions
("no very large read support, rsize now 127K")); disabled for just this mount */
} reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) else
cifs_sb->wsize = min(cifs_sb->wsize, tcon->unix_ext = 0; /* server does not support them */
(tcon->ses->server->maxBuf -
MAX_CIFS_HDR_SIZE)); /* convert forward to back slashes in prepath here if needed */
if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
cifs_sb->rsize = min(cifs_sb->rsize, convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
(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 (in which case it is not needed anymore) but when new sesion is created
...@@ -3513,6 +3539,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, ...@@ -3513,6 +3539,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
/* above now done in SendReceive */ /* above now done in SendReceive */
if ((rc == 0) && (tcon != NULL)) { if ((rc == 0) && (tcon != NULL)) {
tcon->tidStatus = CifsGood; tcon->tidStatus = CifsGood;
tcon->need_reconnect = false;
tcon->tid = smb_buffer_response->Tid; tcon->tid = smb_buffer_response->Tid;
bcc_ptr = pByteArea(smb_buffer_response); bcc_ptr = pByteArea(smb_buffer_response);
length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
...@@ -3584,48 +3611,17 @@ int ...@@ -3584,48 +3611,17 @@ int
cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
{ {
int rc = 0; int rc = 0;
int xid;
struct cifsSesInfo *ses = NULL;
char *tmp; char *tmp;
xid = GetXid(); if (cifs_sb->tcon)
cifs_put_tcon(cifs_sb->tcon);
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"));
}
cifs_sb->tcon = NULL; cifs_sb->tcon = NULL;
tmp = cifs_sb->prepath; tmp = cifs_sb->prepath;
cifs_sb->prepathlen = 0; cifs_sb->prepathlen = 0;
cifs_sb->prepath = NULL; cifs_sb->prepath = NULL;
kfree(tmp); kfree(tmp);
if (ses)
sesInfoFree(ses);
FreeXid(xid);
return rc; return rc;
} }
...@@ -3741,6 +3737,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, ...@@ -3741,6 +3737,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
cFYI(1, ("CIFS Session Established successfully")); cFYI(1, ("CIFS Session Established successfully"));
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
pSesInfo->status = CifsGood; pSesInfo->status = CifsGood;
pSesInfo->need_reconnect = false;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
} }
......
...@@ -493,7 +493,7 @@ int cifs_close(struct inode *inode, struct file *file) ...@@ -493,7 +493,7 @@ int cifs_close(struct inode *inode, struct file *file)
if (pTcon) { if (pTcon) {
/* no sense reconnecting to close a file that is /* no sense reconnecting to close a file that is
already closed */ already closed */
if (pTcon->tidStatus != CifsNeedReconnect) { if (!pTcon->need_reconnect) {
timeout = 2; timeout = 2;
while ((atomic_read(&pSMBFile->wrtPending) != 0) while ((atomic_read(&pSMBFile->wrtPending) != 0)
&& (timeout <= 2048)) { && (timeout <= 2048)) {
...@@ -1404,7 +1404,10 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1404,7 +1404,10 @@ static int cifs_writepages(struct address_space *mapping,
if ((wbc->nr_to_write -= n_iov) <= 0) if ((wbc->nr_to_write -= n_iov) <= 0)
done = 1; done = 1;
index = next; index = next;
} } else
/* Need to re-find the pages we skipped */
index = pvec.pages[0]->index + 1;
pagevec_release(&pvec); pagevec_release(&pvec);
} }
if (!scanned && !done) { if (!scanned && !done) {
......
...@@ -75,12 +75,12 @@ sesInfoAlloc(void) ...@@ -75,12 +75,12 @@ sesInfoAlloc(void)
ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
if (ret_buf) { if (ret_buf) {
write_lock(&GlobalSMBSeslock);
atomic_inc(&sesInfoAllocCount); atomic_inc(&sesInfoAllocCount);
ret_buf->status = CifsNew; 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); init_MUTEX(&ret_buf->sesSem);
write_unlock(&GlobalSMBSeslock);
} }
return ret_buf; return ret_buf;
} }
...@@ -93,10 +93,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free) ...@@ -93,10 +93,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
return; return;
} }
write_lock(&GlobalSMBSeslock);
atomic_dec(&sesInfoAllocCount); atomic_dec(&sesInfoAllocCount);
list_del(&buf_to_free->cifsSessionList);
write_unlock(&GlobalSMBSeslock);
kfree(buf_to_free->serverOS); kfree(buf_to_free->serverOS);
kfree(buf_to_free->serverDomain); kfree(buf_to_free->serverDomain);
kfree(buf_to_free->serverNOS); kfree(buf_to_free->serverNOS);
...@@ -111,17 +108,14 @@ tconInfoAlloc(void) ...@@ -111,17 +108,14 @@ tconInfoAlloc(void)
struct cifsTconInfo *ret_buf; struct cifsTconInfo *ret_buf;
ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL);
if (ret_buf) { if (ret_buf) {
write_lock(&GlobalSMBSeslock);
atomic_inc(&tconInfoAllocCount); atomic_inc(&tconInfoAllocCount);
list_add(&ret_buf->cifsConnectionList,
&GlobalTreeConnectionList);
ret_buf->tidStatus = CifsNew; ret_buf->tidStatus = CifsNew;
++ret_buf->tc_count;
INIT_LIST_HEAD(&ret_buf->openFileList); INIT_LIST_HEAD(&ret_buf->openFileList);
init_MUTEX(&ret_buf->tconSem); INIT_LIST_HEAD(&ret_buf->tcon_list);
#ifdef CONFIG_CIFS_STATS #ifdef CONFIG_CIFS_STATS
spin_lock_init(&ret_buf->stat_lock); spin_lock_init(&ret_buf->stat_lock);
#endif #endif
write_unlock(&GlobalSMBSeslock);
} }
return ret_buf; return ret_buf;
} }
...@@ -133,10 +127,7 @@ tconInfoFree(struct cifsTconInfo *buf_to_free) ...@@ -133,10 +127,7 @@ tconInfoFree(struct cifsTconInfo *buf_to_free)
cFYI(1, ("Null buffer passed to tconInfoFree")); cFYI(1, ("Null buffer passed to tconInfoFree"));
return; return;
} }
write_lock(&GlobalSMBSeslock);
atomic_dec(&tconInfoAllocCount); atomic_dec(&tconInfoAllocCount);
list_del(&buf_to_free->cifsConnectionList);
write_unlock(&GlobalSMBSeslock);
kfree(buf_to_free->nativeFileSystem); kfree(buf_to_free->nativeFileSystem);
kfree(buf_to_free); kfree(buf_to_free);
} }
...@@ -350,9 +341,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , ...@@ -350,9 +341,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
if (current->fsuid != treeCon->ses->linux_uid) { if (current->fsuid != treeCon->ses->linux_uid) {
cFYI(1, ("Multiuser mode and UID " cFYI(1, ("Multiuser mode and UID "
"did not match tcon uid")); "did not match tcon uid"));
read_lock(&GlobalSMBSeslock); read_lock(&cifs_tcp_ses_lock);
list_for_each(temp_item, &GlobalSMBSessionList) { list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList); ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
if (ses->linux_uid == current->fsuid) { if (ses->linux_uid == current->fsuid) {
if (ses->server == treeCon->ses->server) { if (ses->server == treeCon->ses->server) {
cFYI(1, ("found matching uid substitute right smb_uid")); cFYI(1, ("found matching uid substitute right smb_uid"));
...@@ -364,7 +355,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , ...@@ -364,7 +355,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
} }
} }
} }
read_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock);
} }
} }
} }
...@@ -497,9 +488,10 @@ bool ...@@ -497,9 +488,10 @@ bool
is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) 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 smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
struct list_head *tmp; struct list_head *tmp, *tmp1, *tmp2;
struct list_head *tmp1; struct cifsSesInfo *ses;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct cifsInodeInfo *pCifsInode;
struct cifsFileInfo *netfile; struct cifsFileInfo *netfile;
cFYI(1, ("Checking for oplock break or dnotify response")); cFYI(1, ("Checking for oplock break or dnotify response"));
...@@ -554,42 +546,42 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -554,42 +546,42 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
return false; return false;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
read_lock(&GlobalSMBSeslock); read_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &GlobalTreeConnectionList) { list_for_each(tmp, &srv->smb_ses_list) {
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) { 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); cifs_stats_inc(&tcon->num_oplock_brks);
list_for_each(tmp1, &tcon->openFileList) { list_for_each(tmp2, &tcon->openFileList) {
netfile = list_entry(tmp1, struct cifsFileInfo, netfile = list_entry(tmp2, struct cifsFileInfo,
tlist); tlist);
if (pSMB->Fid == netfile->netfid) { if (pSMB->Fid != netfile->netfid)
struct cifsInodeInfo *pCifsInode; continue;
read_unlock(&GlobalSMBSeslock);
cFYI(1, read_unlock(&cifs_tcp_ses_lock);
("file id match, oplock break")); cFYI(1, ("file id match, oplock break"));
pCifsInode = pCifsInode = CIFS_I(netfile->pInode);
CIFS_I(netfile->pInode); pCifsInode->clientCanCacheAll = false;
pCifsInode->clientCanCacheAll = false; if (pSMB->OplockLevel == 0)
if (pSMB->OplockLevel == 0) pCifsInode->clientCanCacheRead = false;
pCifsInode->clientCanCacheRead pCifsInode->oplockPending = true;
= false; AllocOplockQEntry(netfile->pInode,
pCifsInode->oplockPending = true; netfile->netfid, tcon);
AllocOplockQEntry(netfile->pInode, cFYI(1, ("about to wake up oplock thread"));
netfile->netfid, if (oplockThread)
tcon); wake_up_process(oplockThread);
cFYI(1,
("about to wake up oplock thread")); return true;
if (oplockThread)
wake_up_process(oplockThread);
return true;
}
} }
read_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock);
cFYI(1, ("No matching file for oplock break")); cFYI(1, ("No matching file for oplock break"));
return true; return true;
} }
} }
read_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock);
cFYI(1, ("Can not process oplock break for non-existent connection")); cFYI(1, ("Can not process oplock break for non-existent connection"));
return true; return true;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment