mirror of
https://github.com/adulau/aha.git
synced 2024-12-29 12:16:20 +00:00
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
This commit is contained in:
commit
4e14e833ac
9 changed files with 767 additions and 718 deletions
|
@ -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,77 +123,55 @@ 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,
|
||||||
|
smb_ses_list);
|
||||||
|
if ((ses->serverDomain == NULL) ||
|
||||||
|
(ses->serverOS == NULL) ||
|
||||||
(ses->serverNOS == NULL)) {
|
(ses->serverNOS == NULL)) {
|
||||||
seq_printf(m, "\nentry for %s not fully "
|
seq_printf(m, "\n%d) entry for %s not fully "
|
||||||
"displayed\n\t", ses->serverName);
|
"displayed\n\t", i, ses->serverName);
|
||||||
} else {
|
} else {
|
||||||
seq_printf(m,
|
seq_printf(m,
|
||||||
"\n%d) Name: %s Domain: %s Mounts: %d OS:"
|
"\n%d) Name: %s Domain: %s Uses: %d OS:"
|
||||||
" %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
|
" %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;
|
||||||
spin_lock(&GlobalMid_Lock);
|
list_for_each(tmp3, &ses->tcon_list) {
|
||||||
list_for_each(tmp1, &ses->server->pending_mid_q) {
|
tcon = list_entry(tmp3, struct cifsTconInfo,
|
||||||
mid_entry = list_entry(tmp1, struct
|
tcon_list);
|
||||||
mid_q_entry,
|
++j;
|
||||||
qhead);
|
|
||||||
seq_printf(m, "State: %d com: %d pid:"
|
|
||||||
" %d tsk: %p mid %d\n",
|
|
||||||
mid_entry->midState,
|
|
||||||
(int)mid_entry->command,
|
|
||||||
mid_entry->pid,
|
|
||||||
mid_entry->tsk,
|
|
||||||
mid_entry->mid);
|
|
||||||
}
|
|
||||||
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);
|
dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
|
||||||
seq_printf(m, "\n%d) %s Uses: %d ", i,
|
seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
|
||||||
tcon->treeName, atomic_read(&tcon->useCount));
|
tcon->treeName, tcon->tc_count);
|
||||||
if (tcon->nativeFileSystem) {
|
if (tcon->nativeFileSystem) {
|
||||||
seq_printf(m, "Type: %s ",
|
seq_printf(m, "Type: %s ",
|
||||||
tcon->nativeFileSystem);
|
tcon->nativeFileSystem);
|
||||||
}
|
}
|
||||||
seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
|
seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
|
||||||
"\nPathComponentMax: %d Status: %d",
|
"\nPathComponentMax: %d Status: 0x%d",
|
||||||
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
|
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
|
||||||
le32_to_cpu(tcon->fsAttrInfo.Attributes),
|
le32_to_cpu(tcon->fsAttrInfo.Attributes),
|
||||||
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
|
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
|
||||||
|
@ -204,11 +183,29 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||||
else
|
else
|
||||||
seq_printf(m, " type: %d ", dev_type);
|
seq_printf(m, " type: %d ", dev_type);
|
||||||
|
|
||||||
if (tcon->tidStatus == CifsNeedReconnect)
|
if (tcon->need_reconnect)
|
||||||
seq_puts(m, "\tDISCONNECTED ");
|
seq_puts(m, "\tDISCONNECTED ");
|
||||||
|
seq_putc(m, '\n');
|
||||||
}
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
|
||||||
|
|
||||||
|
seq_puts(m, "\n\tMIDs:\n");
|
||||||
|
|
||||||
|
spin_lock(&GlobalMid_Lock);
|
||||||
|
list_for_each(tmp3, &server->pending_mid_q) {
|
||||||
|
mid_entry = list_entry(tmp3, struct mid_q_entry,
|
||||||
|
qhead);
|
||||||
|
seq_printf(m, "\tState: %d com: %d pid:"
|
||||||
|
" %d tsk: %p mid %d\n",
|
||||||
|
mid_entry->midState,
|
||||||
|
(int)mid_entry->command,
|
||||||
|
mid_entry->pid,
|
||||||
|
mid_entry->tsk,
|
||||||
|
mid_entry->mid);
|
||||||
|
}
|
||||||
|
spin_unlock(&GlobalMid_Lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,
|
||||||
{
|
{
|
||||||
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,14 +241,21 @@ 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,
|
||||||
|
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_smbs_sent, 0);
|
||||||
atomic_set(&tcon->num_writes, 0);
|
atomic_set(&tcon->num_writes, 0);
|
||||||
atomic_set(&tcon->num_reads, 0);
|
atomic_set(&tcon->num_reads, 0);
|
||||||
|
@ -268,7 +274,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
|
||||||
atomic_set(&tcon->num_symlinks, 0);
|
atomic_set(&tcon->num_symlinks, 0);
|
||||||
atomic_set(&tcon->num_locks, 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,
|
||||||
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,12 +316,20 @@ 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) {
|
||||||
|
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++;
|
i++;
|
||||||
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
|
|
||||||
seq_printf(m, "\n%d) %s", i, tcon->treeName);
|
seq_printf(m, "\n%d) %s", i, tcon->treeName);
|
||||||
if (tcon->tidStatus == CifsNeedReconnect)
|
if (tcon->need_reconnect)
|
||||||
seq_puts(m, "\tDISCONNECTED ");
|
seq_puts(m, "\tDISCONNECTED ");
|
||||||
seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
|
seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
|
||||||
atomic_read(&tcon->num_smbs_sent),
|
atomic_read(&tcon->num_smbs_sent),
|
||||||
|
@ -322,13 +340,13 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
|
||||||
seq_printf(m, "\nWrites: %d Bytes: %lld",
|
seq_printf(m, "\nWrites: %d Bytes: %lld",
|
||||||
atomic_read(&tcon->num_writes),
|
atomic_read(&tcon->num_writes),
|
||||||
(long long)(tcon->bytes_written));
|
(long long)(tcon->bytes_written));
|
||||||
seq_printf(m,
|
seq_printf(m, "\nLocks: %d HardLinks: %d "
|
||||||
"\nLocks: %d HardLinks: %d Symlinks: %d",
|
"Symlinks: %d",
|
||||||
atomic_read(&tcon->num_locks),
|
atomic_read(&tcon->num_locks),
|
||||||
atomic_read(&tcon->num_hardlinks),
|
atomic_read(&tcon->num_hardlinks),
|
||||||
atomic_read(&tcon->num_symlinks));
|
atomic_read(&tcon->num_symlinks));
|
||||||
|
seq_printf(m, "\nOpens: %d Closes: %d"
|
||||||
seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d",
|
"Deletes: %d",
|
||||||
atomic_read(&tcon->num_opens),
|
atomic_read(&tcon->num_opens),
|
||||||
atomic_read(&tcon->num_closes),
|
atomic_read(&tcon->num_closes),
|
||||||
atomic_read(&tcon->num_deletes));
|
atomic_read(&tcon->num_deletes));
|
||||||
|
@ -338,12 +356,15 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
|
||||||
seq_printf(m, "\nRenames: %d T2 Renames %d",
|
seq_printf(m, "\nRenames: %d T2 Renames %d",
|
||||||
atomic_read(&tcon->num_renames),
|
atomic_read(&tcon->num_renames),
|
||||||
atomic_read(&tcon->num_t2renames));
|
atomic_read(&tcon->num_t2renames));
|
||||||
seq_printf(m, "\nFindFirst: %d FNext %d FClose %d",
|
seq_printf(m, "\nFindFirst: %d FNext %d "
|
||||||
|
"FClose %d",
|
||||||
atomic_read(&tcon->num_ffirst),
|
atomic_read(&tcon->num_ffirst),
|
||||||
atomic_read(&tcon->num_fnext),
|
atomic_read(&tcon->num_fnext),
|
||||||
atomic_read(&tcon->num_fclose));
|
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)
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
* 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,
|
||||||
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,
|
||||||
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,
|
||||||
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 @@ compose_mount_options_out:
|
||||||
|
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
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 = {
|
||||||
* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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 {
|
||||||
};
|
};
|
||||||
|
|
||||||
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 {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
* 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 {
|
||||||
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 {
|
||||||
* 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 {
|
||||||
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 */
|
||||||
#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,
|
||||||
/* 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,
|
||||||
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,
|
||||||
/* 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)
|
||||||
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)
|
||||||
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,48 +742,29 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
cFYI(1, ("In tree disconnect"));
|
cFYI(1, ("In tree disconnect"));
|
||||||
|
|
||||||
|
/* BB: do we need to check this? These should never be NULL. */
|
||||||
|
if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If last user of the connection and
|
* No need to return error on this operation if tid invalidated and
|
||||||
* connection alive - disconnect it
|
* closed on server already e.g. due to tcp session crashing. Also,
|
||||||
* If this is the last connection on the server session disconnect it
|
* the tcon is no longer on the list, so no need to take lock before
|
||||||
* (and inside session disconnect we should check if tcp socket needs
|
* checking this.
|
||||||
* to be freed and kernel thread woken up).
|
|
||||||
*/
|
*/
|
||||||
if (tcon)
|
if (tcon->need_reconnect)
|
||||||
down(&tcon->tconSem);
|
|
||||||
else
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
atomic_dec(&tcon->useCount);
|
|
||||||
if (atomic_read(&tcon->useCount) > 0) {
|
|
||||||
up(&tcon->tconSem);
|
|
||||||
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 */
|
|
||||||
if (tcon->tidStatus == CifsNeedReconnect) {
|
|
||||||
up(&tcon->tconSem);
|
|
||||||
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)
|
||||||
|
@ -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 @@ GetInodeNumOut:
|
||||||
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
|
||||||
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,
|
||||||
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);
|
||||||
|
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->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 @@ getDFSRetry:
|
||||||
|
|
||||||
/* 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
|
||||||
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)
|
||||||
|
|
||||||
/* 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->status = CifsNeedReconnect;
|
|
||||||
ses->ipc_tid = 0;
|
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 */
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
}
|
|
||||||
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);
|
|
||||||
/* 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)
|
||||||
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 @@ incomplete_rcv:
|
||||||
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 @@ multi_t2_fnd:
|
||||||
}
|
}
|
||||||
} /* 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,28 +690,28 @@ multi_t2_fnd:
|
||||||
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);
|
|
||||||
if (ses->server == server) {
|
|
||||||
ses->status = CifsExiting;
|
ses->status = CifsExiting;
|
||||||
ses->server = NULL;
|
ses->server = NULL;
|
||||||
}
|
}
|
||||||
}
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
read_unlock(&GlobalSMBSeslock);
|
|
||||||
} 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,7 +727,7 @@ multi_t2_fnd:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 @@ multi_t2_fnd:
|
||||||
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,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct TCP_Server_Info *
|
||||||
|
cifs_find_tcp_session(struct sockaddr *addr)
|
||||||
|
{
|
||||||
|
struct list_head *tmp;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
++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);
|
||||||
|
|
||||||
|
spin_lock(&GlobalMid_Lock);
|
||||||
|
server->tcpStatus = CifsExiting;
|
||||||
|
spin_unlock(&GlobalMid_Lock);
|
||||||
|
|
||||||
|
task = xchg(&server->tsk, NULL);
|
||||||
|
if (task)
|
||||||
|
force_sig(SIGKILL, task);
|
||||||
|
}
|
||||||
|
|
||||||
static struct cifsSesInfo *
|
static struct cifsSesInfo *
|
||||||
cifs_find_tcp_session(struct in_addr *target_ip_addr,
|
cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
|
||||||
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 cifsSesInfo *ses;
|
||||||
|
|
||||||
*psrvTcp = NULL;
|
write_lock(&cifs_tcp_ses_lock);
|
||||||
|
list_for_each(tmp, &server->smb_ses_list) {
|
||||||
read_lock(&GlobalSMBSeslock);
|
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
|
||||||
list_for_each(tmp, &GlobalSMBSessionList) {
|
if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
|
||||||
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
|
|
||||||
if (!ses->server)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (target_ip_addr &&
|
++ses->ses_count;
|
||||||
ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr)
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
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?? */
|
|
||||||
|
|
||||||
/* found a match on the TCP session */
|
|
||||||
*psrvTcp = ses->server;
|
|
||||||
|
|
||||||
/* 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;
|
return ses;
|
||||||
}
|
}
|
||||||
/* else tcp and smb sessions need reconnection */
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
}
|
|
||||||
read_unlock(&GlobalSMBSeslock);
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cifs_put_smb_ses(struct cifsSesInfo *ses)
|
||||||
|
{
|
||||||
|
int xid;
|
||||||
|
struct TCP_Server_Info *server = ses->server;
|
||||||
|
|
||||||
|
write_lock(&cifs_tcp_ses_lock);
|
||||||
|
if (--ses->ses_count > 0) {
|
||||||
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
static struct cifsTconInfo *
|
static struct cifsTconInfo *
|
||||||
find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
|
cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
|
||||||
{
|
{
|
||||||
struct list_head *tmp;
|
struct list_head *tmp;
|
||||||
struct cifsTconInfo *tcon;
|
struct cifsTconInfo *tcon;
|
||||||
__be32 old_ip;
|
|
||||||
|
|
||||||
read_lock(&GlobalSMBSeslock);
|
write_lock(&cifs_tcp_ses_lock);
|
||||||
|
list_for_each(tmp, &ses->tcon_list) {
|
||||||
list_for_each(tmp, &GlobalTreeConnectionList) {
|
tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
|
||||||
cFYI(1, ("Next tcon"));
|
if (tcon->tidStatus == CifsExiting)
|
||||||
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
|
continue;
|
||||||
if (!tcon->ses || !tcon->ses->server)
|
if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr;
|
++tcon->tc_count;
|
||||||
cFYI(1, ("old ip addr: %x == new ip %x ?",
|
write_unlock(&cifs_tcp_ses_lock);
|
||||||
old_ip, new_target_ip_addr));
|
|
||||||
|
|
||||||
if (old_ip != new_target_ip_addr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* 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));
|
|
||||||
|
|
||||||
if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cFYI(1, ("and old usr: %s new: %s",
|
|
||||||
tcon->treeName, uncName));
|
|
||||||
|
|
||||||
if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* matched smb session (user name) */
|
|
||||||
read_unlock(&GlobalSMBSeslock);
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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",
|
||||||
|
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;
|
||||||
|
|
||||||
task = xchg(&server->tsk, NULL);
|
if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
|
||||||
if (task)
|
cERROR(1, ("wsize %d too large, using 4096 instead",
|
||||||
force_sig(SIGKILL, task);
|
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
|
|
||||||
/* 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,
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
} 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)
|
||||||
|
memcpy(&srvTcp->addr.sockAddr6, sin_server6,
|
||||||
|
sizeof(struct sockaddr_in6));
|
||||||
|
else
|
||||||
|
memcpy(&srvTcp->addr.sockAddr, sin_server,
|
||||||
sizeof(struct sockaddr_in));
|
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,
|
||||||
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,15 +2221,20 @@ 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rc) {
|
/* 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 */
|
/* volume_info.password freed at unmount */
|
||||||
if (volume_info.password) {
|
if (volume_info.password) {
|
||||||
pSesInfo->password = volume_info.password;
|
pSesInfo->password = volume_info.password;
|
||||||
|
@ -2094,13 +2242,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
||||||
volume_info.password = NULL;
|
volume_info.password = NULL;
|
||||||
}
|
}
|
||||||
if (volume_info.username)
|
if (volume_info.username)
|
||||||
strncpy(pSesInfo->userName,
|
strncpy(pSesInfo->userName, volume_info.username,
|
||||||
volume_info.username,
|
|
||||||
MAX_USERNAME_SIZE);
|
MAX_USERNAME_SIZE);
|
||||||
if (volume_info.domainname) {
|
if (volume_info.domainname) {
|
||||||
int len = strlen(volume_info.domainname);
|
int len = strlen(volume_info.domainname);
|
||||||
pSesInfo->domainName =
|
pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
|
||||||
kmalloc(len + 1, GFP_KERNEL);
|
|
||||||
if (pSesInfo->domainName)
|
if (pSesInfo->domainName)
|
||||||
strcpy(pSesInfo->domainName,
|
strcpy(pSesInfo->domainName,
|
||||||
volume_info.domainname);
|
volume_info.domainname);
|
||||||
|
@ -2108,104 +2254,61 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
||||||
pSesInfo->linux_uid = volume_info.linux_uid;
|
pSesInfo->linux_uid = volume_info.linux_uid;
|
||||||
pSesInfo->overrideSecFlg = volume_info.secFlg;
|
pSesInfo->overrideSecFlg = volume_info.secFlg;
|
||||||
down(&pSesInfo->sesSem);
|
down(&pSesInfo->sesSem);
|
||||||
|
|
||||||
/* BB FIXME need to pass vol->secFlgs BB */
|
/* BB FIXME need to pass vol->secFlgs BB */
|
||||||
rc = cifs_setup_session(xid, pSesInfo,
|
rc = cifs_setup_session(xid, pSesInfo,
|
||||||
cifs_sb->local_nls);
|
cifs_sb->local_nls);
|
||||||
up(&pSesInfo->sesSem);
|
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) {
|
tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
|
||||||
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))
|
|
||||||
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"));
|
||||||
|
/* 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) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto mount_fail_check;
|
||||||
|
}
|
||||||
|
tcon->ses = pSesInfo;
|
||||||
|
|
||||||
|
/* 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
|
/* we can have only one retry value for a connection
|
||||||
to a share so for resources mounted more than once
|
to a share so for resources mounted more than once
|
||||||
to the same server share the last value passed in
|
to the same server share the last value passed in
|
||||||
|
@ -2213,52 +2316,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
||||||
tcon->retry = volume_info.retry;
|
tcon->retry = volume_info.retry;
|
||||||
tcon->nocase = volume_info.nocase;
|
tcon->nocase = volume_info.nocase;
|
||||||
tcon->local_lease = volume_info.local_lease;
|
tcon->local_lease = volume_info.local_lease;
|
||||||
if (tcon->seal != volume_info.seal)
|
|
||||||
cERROR(1, ("transport encryption setting "
|
|
||||||
"conflicts with existing tid"));
|
|
||||||
} else {
|
|
||||||
tcon = tconInfoAlloc();
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!rc) {
|
|
||||||
atomic_inc(&pSesInfo->inUse);
|
|
||||||
tcon->retry = volume_info.retry;
|
|
||||||
tcon->nocase = volume_info.nocase;
|
|
||||||
tcon->seal = volume_info.seal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (pSesInfo) {
|
if (pSesInfo) {
|
||||||
if (pSesInfo->capabilities & CAP_LARGE_FILES) {
|
if (pSesInfo->capabilities & CAP_LARGE_FILES) {
|
||||||
|
@ -2270,46 +2327,20 @@ 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
|
|
||||||
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 find_unc succeeded then rc == 0 so we can not end */
|
||||||
if (tcon) /* up accidently freeing someone elses tcon struct */
|
/* up accidently freeing someone elses tcon struct */
|
||||||
tconInfoFree(tcon);
|
if (tcon)
|
||||||
if (existingCifsSes == NULL) {
|
cifs_put_tcon(tcon);
|
||||||
if (pSesInfo) {
|
else if (pSesInfo)
|
||||||
if ((pSesInfo->server) &&
|
cifs_put_smb_ses(pSesInfo);
|
||||||
(pSesInfo->status == CifsGood)) {
|
else
|
||||||
int temp_rc;
|
cifs_put_tcp_session(srvTcp);
|
||||||
temp_rc = CIFSSMBLogoff(xid, pSesInfo);
|
goto out;
|
||||||
/* 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;
|
cifs_sb->tcon = tcon;
|
||||||
tcon->ses = pSesInfo;
|
|
||||||
|
|
||||||
/* do not care if following two calls succeed - informational */
|
/* do not care if following two calls succeed - informational */
|
||||||
if (!tcon->ipc) {
|
if (!tcon->ipc) {
|
||||||
|
@ -2327,23 +2358,18 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
||||||
|
|
||||||
/* convert forward to back slashes in prepath here if needed */
|
/* convert forward to back slashes in prepath here if needed */
|
||||||
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
|
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
|
||||||
convert_delimiter(cifs_sb->prepath,
|
convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
|
||||||
CIFS_DIR_SEP(cifs_sb));
|
|
||||||
|
|
||||||
if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
|
if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
|
||||||
cifs_sb->rsize = 1024 * 127;
|
cifs_sb->rsize = 1024 * 127;
|
||||||
cFYI(DBG2,
|
cFYI(DBG2, ("no very large read support, rsize now 127K"));
|
||||||
("no very large read support, rsize now 127K"));
|
|
||||||
}
|
}
|
||||||
if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
|
if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
|
||||||
cifs_sb->wsize = min(cifs_sb->wsize,
|
cifs_sb->wsize = min(cifs_sb->wsize,
|
||||||
(tcon->ses->server->maxBuf -
|
(tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
|
||||||
MAX_CIFS_HDR_SIZE));
|
|
||||||
if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
|
if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
|
||||||
cifs_sb->rsize = min(cifs_sb->rsize,
|
cifs_sb->rsize = min(cifs_sb->rsize,
|
||||||
(tcon->ses->server->maxBuf -
|
(tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
|
||||||
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,
|
||||||
/* 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
|
||||||
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,
|
||||||
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)
|
||||||
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 @@ retry:
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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 */ ,
|
||||||
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 */ ,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
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
|
pCifsInode->clientCanCacheRead = false;
|
||||||
= false;
|
|
||||||
pCifsInode->oplockPending = true;
|
pCifsInode->oplockPending = true;
|
||||||
AllocOplockQEntry(netfile->pInode,
|
AllocOplockQEntry(netfile->pInode,
|
||||||
netfile->netfid,
|
netfile->netfid, tcon);
|
||||||
tcon);
|
cFYI(1, ("about to wake up oplock thread"));
|
||||||
cFYI(1,
|
|
||||||
("about to wake up oplock thread"));
|
|
||||||
if (oplockThread)
|
if (oplockThread)
|
||||||
wake_up_process(oplockThread);
|
wake_up_process(oplockThread);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
read_unlock(&cifs_tcp_ses_lock);
|
||||||
read_unlock(&GlobalSMBSeslock);
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue