#include <linux/mempool.h>
#include <linux/delay.h>
#include <linux/completion.h>
+#include <linux/kthread.h>
#include <linux/pagevec.h>
+#include <linux/freezer.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include "cifspdu.h"
unsigned retry:1;
unsigned intr:1;
unsigned setuids:1;
+ unsigned override_uid:1;
+ unsigned override_gid:1;
unsigned noperm:1;
unsigned no_psx_acl:1; /* set if posix acl support should be disabled */
unsigned cifs_acl:1;
struct mid_q_entry * mid_entry;
spin_lock(&GlobalMid_Lock);
- if(server->tcpStatus == CifsExiting) {
+ if( kthread_should_stop() ) {
/* the demux thread will exit normally
next time through the loop */
spin_unlock(&GlobalMid_Lock);
spin_unlock(&GlobalMid_Lock);
up(&server->tcpSem);
- while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood))
+ while ( (!kthread_should_stop()) && (server->tcpStatus != CifsGood))
{
try_to_freeze();
if(server->protocolType == IPV6) {
} else {
atomic_inc(&tcpSesReconnectCount);
spin_lock(&GlobalMid_Lock);
- if(server->tcpStatus != CifsExiting)
+ if( !kthread_should_stop() )
server->tcpStatus = CifsGood;
server->sequence_number = 0;
spin_unlock(&GlobalMid_Lock);
int isMultiRsp;
int reconnect;
- daemonize("cifsd");
allow_signal(SIGKILL);
current->flags |= PF_MEMALLOC;
server->tsk = current; /* save process info to wake at shutdown */
GFP_KERNEL);
}
- while (server->tcpStatus != CifsExiting) {
+ while (!kthread_should_stop()) {
if (try_to_freeze())
continue;
if (bigbuf == NULL) {
kernel_recvmsg(csocket, &smb_msg,
&iov, 1, 4, 0 /* BB see socket.h flags */);
- if (server->tcpStatus == CifsExiting) {
+ if ( kthread_should_stop() ) {
break;
} else if (server->tcpStatus == CifsNeedReconnect) {
cFYI(1, ("Reconnect after server stopped responding"));
total_read += length) {
length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
pdu_length - total_read, 0);
- if((server->tcpStatus == CifsExiting) ||
+ if( kthread_should_stop() ||
(length == -EINTR)) {
/* then will exit */
reconnect = 2;
GFP_KERNEL);
}
- complete_and_exit(&cifsd_complete, 0);
return 0;
}
} else if (strnicmp(data, "nouser_xattr",12) == 0) {
vol->no_xattr = 1;
} else if (strnicmp(data, "user", 4) == 0) {
- if (!value || !*value) {
+ if (!value) {
printk(KERN_WARNING
"CIFS: invalid or missing username\n");
return 1; /* needs_arg; */
+ } else if(!*value) {
+ /* null user, ie anonymous, authentication */
+ vol->nullauth = 1;
}
if (strnlen(value, 200) < 200) {
vol->username = value;
}
if ((temp_len = strnlen(value, 300)) < 300) {
vol->UNC = kmalloc(temp_len+1,GFP_KERNEL);
- if(vol->UNC == NULL)
+ if (vol->UNC == NULL)
return 1;
strcpy(vol->UNC,value);
if (strncmp(vol->UNC, "//", 2) == 0) {
return 1; /* needs_arg; */
}
if ((temp_len = strnlen(value, 1024)) < 1024) {
- if(value[0] != '/')
+ if (value[0] != '/')
temp_len++; /* missing leading slash */
vol->prepath = kmalloc(temp_len+1,GFP_KERNEL);
- if(vol->prepath == NULL)
+ if (vol->prepath == NULL)
return 1;
- if(value[0] != '/') {
+ if (value[0] != '/') {
vol->prepath[0] = '/';
strcpy(vol->prepath+1,value);
} else
return 1; /* needs_arg; */
}
if (strnlen(value, 65) < 65) {
- if(strnicmp(value,"default",7))
+ if (strnicmp(value,"default",7))
vol->iocharset = value;
/* if iocharset not set load_nls_default used by caller */
cFYI(1, ("iocharset set to %s",value));
if (value && *value) {
vol->linux_uid =
simple_strtoul(value, &value, 0);
+ vol->override_uid = 1;
}
} else if (strnicmp(data, "gid", 3) == 0) {
if (value && *value) {
vol->linux_gid =
simple_strtoul(value, &value, 0);
+ vol->override_gid = 1;
}
} else if (strnicmp(data, "file_mode", 4) == 0) {
if (value && *value) {
}
/* The string has 16th byte zero still from
set at top of the function */
- if((i==15) && (value[i] != 0))
+ if ((i==15) && (value[i] != 0))
printk(KERN_WARNING "CIFS: netbiosname longer than 15 truncated.\n");
}
} else if (strnicmp(data, "servern", 7) == 0) {
}
/* The string has 16th byte zero still from
set at top of the function */
- if((i==15) && (value[i] != 0))
+ if ((i==15) && (value[i] != 0))
printk(KERN_WARNING "CIFS: server netbiosname longer than 15 truncated.\n");
}
} else if (strnicmp(data, "credentials", 4) == 0) {
printk(KERN_WARNING "CIFS: Unknown mount option %s\n",data);
}
if (vol->UNC == NULL) {
- if(devname == NULL) {
+ if (devname == NULL) {
printk(KERN_WARNING "CIFS: Missing UNC name for mount target\n");
return 1;
}
if ((temp_len = strnlen(devname, 300)) < 300) {
vol->UNC = kmalloc(temp_len+1,GFP_KERNEL);
- if(vol->UNC == NULL)
+ if (vol->UNC == NULL)
return 1;
strcpy(vol->UNC,devname);
if (strncmp(vol->UNC, "//", 2) == 0) {
return rc;
}
+void reset_cifs_unix_caps(int xid, struct cifsTconInfo * tcon,
+ struct super_block * sb, struct smb_vol * vol_info)
+{
+ /* if we are reconnecting then should we check to see if
+ * any requested capabilities changed locally e.g. via
+ * remount but we can not do much about it here
+ * if they have (even if we could detect it by the following)
+ * Perhaps we could add a backpointer to array of sb from tcon
+ * or if we change to make all sb to same share the same
+ * sb as NFS - then we only have one backpointer to sb.
+ * What if we wanted to mount the server share twice once with
+ * and once without posixacls or posix paths? */
+ __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+
+
+ if(!CIFSSMBQFSUnixInfo(xid, tcon)) {
+ __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+
+ /* check for reconnect case in which we do not
+ want to change the mount behavior if we can avoid it */
+ if(vol_info == NULL) {
+ /* turn off POSIX ACL and PATHNAMES if not set
+ originally at mount time */
+ if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
+ cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
+ if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0)
+ cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+
+
+
+
+ }
+
+ cap &= CIFS_UNIX_CAP_MASK;
+ if(vol_info && vol_info->no_psx_acl)
+ cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
+ else if(CIFS_UNIX_POSIX_ACL_CAP & cap) {
+ cFYI(1,("negotiated posix acl support"));
+ if(sb)
+ sb->s_flags |= MS_POSIXACL;
+ }
+
+ if(vol_info && vol_info->posix_paths == 0)
+ cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+ else if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+ cFYI(1,("negotiate posix pathnames"));
+ if(sb)
+ CIFS_SB(sb)->mnt_cifs_flags |=
+ CIFS_MOUNT_POSIX_PATHS;
+ }
+
+ /* We might be setting the path sep back to a different
+ form if we are reconnecting and the server switched its
+ posix path capability for this share */
+ if(sb && (CIFS_SB(sb)->prepathlen > 0))
+ CIFS_SB(sb)->prepath[0] = CIFS_DIR_SEP(CIFS_SB(sb));
+
+ cFYI(1,("Negotiate caps 0x%x",(int)cap));
+#ifdef CONFIG_CIFS_DEBUG2
+ if(cap & CIFS_UNIX_FCNTL_CAP)
+ cFYI(1,("FCNTL cap"));
+ if(cap & CIFS_UNIX_EXTATTR_CAP)
+ cFYI(1,("EXTATTR cap"));
+ if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ cFYI(1,("POSIX path cap"));
+ if(cap & CIFS_UNIX_XATTR_CAP)
+ cFYI(1,("XATTR cap"));
+ if(cap & CIFS_UNIX_POSIX_ACL_CAP)
+ cFYI(1,("POSIX ACL cap"));
+#endif /* CIFS_DEBUG2 */
+ if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
+ cFYI(1,("setting capabilities failed"));
+ }
+ }
+}
+
int
cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
char *mount_data, const char *devname)
return -EINVAL;
}
- if (volume_info.username) {
+ if (volume_info.nullauth) {
+ cFYI(1,("null user"));
+ volume_info.username = NULL;
+ } else if (volume_info.username) {
/* BB fixme parse for domain name here */
cFYI(1, ("Username: %s ", volume_info.username));
-
} else {
cifserror("No username specified");
/* In userspace mount helper we can get user name from alternate
existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr,
NULL /* no ipv6 addr */,
volume_info.username, &srvTcp);
- else if(address_type == AF_INET6)
+ 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 {
+ } else {
kfree(volume_info.UNC);
kfree(volume_info.password);
kfree(volume_info.prepath);
if (srvTcp) {
cFYI(1, ("Existing tcp session with server found"));
} else { /* create socket */
- if(volume_info.port)
+ if (volume_info.port)
sin_server.sin_port = htons(volume_info.port);
else
sin_server.sin_port = 0;
- rc = ipv4_connect(&sin_server,&csocket,
+ if (address_type == AF_INET6) {
+ cFYI(1,("attempting ipv6 connect"));
+ /* BB should we allow ipv6 on port 139? */
+ /* other OS never observed in Wild doing 139 with v6 */
+ rc = ipv6_connect(&sin_server6,&csocket);
+ } else
+ rc = ipv4_connect(&sin_server,&csocket,
volume_info.source_rfc1001_name,
volume_info.target_rfc1001_name);
if (rc < 0) {
cERROR(1,
- ("Error connecting to IPv4 socket. Aborting operation"));
- if(csocket != NULL)
+ ("Error connecting to IPv4 socket. Aborting operation"));
+ if (csocket != NULL)
sock_release(csocket);
kfree(volume_info.UNC);
kfree(volume_info.password);
so no need to spinlock this init of tcpStatus */
srvTcp->tcpStatus = CifsNew;
init_MUTEX(&srvTcp->tcpSem);
- rc = (int)kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp,
- CLONE_FS | CLONE_FILES | CLONE_VM);
- if(rc < 0) {
- rc = -ENOMEM;
+ srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
+ if ( IS_ERR(srvTcp->tsk) ) {
+ rc = PTR_ERR(srvTcp->tsk);
+ cERROR(1,("error %d create cifsd thread", rc));
+ srvTcp->tsk = NULL;
sock_release(csocket);
kfree(volume_info.UNC);
kfree(volume_info.password);
int len = strlen(volume_info.domainname);
pSesInfo->domainName =
kmalloc(len + 1, GFP_KERNEL);
- if(pSesInfo->domainName)
+ if (pSesInfo->domainName)
strcpy(pSesInfo->domainName,
volume_info.domainname);
}
/* BB FIXME need to pass vol->secFlgs BB */
rc = cifs_setup_session(xid,pSesInfo, cifs_sb->local_nls);
up(&pSesInfo->sesSem);
- if(!rc)
+ if (!rc)
atomic_inc(&srvTcp->socketUseCount);
} else
kfree(volume_info.password);
/* search for existing tcon to this server share */
if (!rc) {
- if(volume_info.rsize > CIFSMaxBufSize) {
+ if (volume_info.rsize > CIFSMaxBufSize) {
cERROR(1,("rsize %d too large, using MaxBufSize",
volume_info.rsize));
cifs_sb->rsize = CIFSMaxBufSize;
else /* default */
cifs_sb->rsize = CIFSMaxBufSize;
- if(volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
+ if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
cERROR(1,("wsize %d too large using 4096 instead",
volume_info.wsize));
cifs_sb->wsize = 4096;
- } else if(volume_info.wsize)
+ } else if (volume_info.wsize)
cifs_sb->wsize = volume_info.wsize;
else
cifs_sb->wsize =
conjunction with 52K kvec constraint on arch with 4K
page size */
- if(cifs_sb->rsize < 2048) {
+ 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) {
+ if (cifs_sb->prepath) {
cifs_sb->prepathlen = strlen(cifs_sb->prepath);
cifs_sb->prepath[0] = CIFS_DIR_SEP(cifs_sb);
volume_info.prepath = NULL;
cFYI(1,("file mode: 0x%x dir mode: 0x%x",
cifs_sb->mnt_file_mode,cifs_sb->mnt_dir_mode));
- if(volume_info.noperm)
+ if (volume_info.noperm)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
- if(volume_info.setuids)
+ if (volume_info.setuids)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
- if(volume_info.server_ino)
+ if (volume_info.server_ino)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
- if(volume_info.remap)
+ if (volume_info.remap)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
- if(volume_info.no_xattr)
+ if (volume_info.no_xattr)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
- if(volume_info.sfu_emul)
+ if (volume_info.sfu_emul)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
- if(volume_info.nobrl)
+ if (volume_info.nobrl)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
- if(volume_info.cifs_acl)
+ if (volume_info.cifs_acl)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
-
- if(volume_info.direct_io) {
+ 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.direct_io) {
cFYI(1,("mounting share using direct i/o"));
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
}
if (tcon == NULL)
rc = -ENOMEM;
else {
- /* check for null share name ie connect to dfs root */
+ /* check for null share name ie connecting to
+ * dfs root */
- /* BB check if this works for exactly length three strings */
+ /* 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);
+ "", cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
kfree(volume_info.UNC);
FreeXid(xid);
return -ENODEV;
} 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);
}
}
}
- if(pSesInfo) {
+ if (pSesInfo) {
if (pSesInfo->capabilities & CAP_LARGE_FILES) {
sb->s_maxbytes = (u64) 1 << 63;
} else
sb->s_maxbytes = (u64) 1 << 31; /* 2 GB */
}
+ /* BB FIXME fix time_gran to be larger for LANMAN sessions */
sb->s_time_gran = 100;
/* on error free sesinfo and tcon struct if needed */
if (rc) {
/* if session setup failed, use count is zero but
we still need to free cifsd thread */
- if(atomic_read(&srvTcp->socketUseCount) == 0) {
+ if (atomic_read(&srvTcp->socketUseCount) == 0) {
spin_lock(&GlobalMid_Lock);
srvTcp->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock);
- if(srvTcp->tsk) {
+ if (srvTcp->tsk) {
send_sig(SIGKILL,srvTcp->tsk,1);
- wait_for_completion(&cifsd_complete);
+ kthread_stop(srvTcp->tsk);
}
}
/* If find_unc succeeded then rc == 0 so we can not end */
int temp_rc;
temp_rc = CIFSSMBLogoff(xid, pSesInfo);
/* if the socketUseCount is now zero */
- if((temp_rc == -ESHUTDOWN) &&
- (pSesInfo->server->tsk)) {
+ if ((temp_rc == -ESHUTDOWN) &&
+ (pSesInfo->server) && (pSesInfo->server->tsk)) {
send_sig(SIGKILL,pSesInfo->server->tsk,1);
- wait_for_completion(&cifsd_complete);
+ kthread_stop(pSesInfo->server->tsk);
}
} else
cFYI(1, ("No session or bad tcon"));
/* do not care if following two calls succeed - informational */
CIFSSMBQFSDeviceInfo(xid, tcon);
CIFSSMBQFSAttributeInfo(xid, tcon);
-
- if (tcon->ses->capabilities & CAP_UNIX) {
- if(!CIFSSMBQFSUnixInfo(xid, tcon)) {
- __u64 cap =
- le64_to_cpu(tcon->fsUnixInfo.Capability);
- cap &= CIFS_UNIX_CAP_MASK;
- if(volume_info.no_psx_acl)
- cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
- else if(CIFS_UNIX_POSIX_ACL_CAP & cap) {
- cFYI(1,("negotiated posix acl support"));
- sb->s_flags |= MS_POSIXACL;
- }
-
- if(volume_info.posix_paths == 0)
- cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
- else if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
- cFYI(1,("negotiate posix pathnames"));
- cifs_sb->mnt_cifs_flags |=
- CIFS_MOUNT_POSIX_PATHS;
- }
-
- cFYI(1,("Negotiate caps 0x%x",(int)cap));
-#ifdef CONFIG_CIFS_DEBUG2
- if(cap & CIFS_UNIX_FCNTL_CAP)
- cFYI(1,("FCNTL cap"));
- if(cap & CIFS_UNIX_EXTATTR_CAP)
- cFYI(1,("EXTATTR cap"));
- if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
- cFYI(1,("POSIX path cap"));
- if(cap & CIFS_UNIX_XATTR_CAP)
- cFYI(1,("XATTR cap"));
- if(cap & CIFS_UNIX_POSIX_ACL_CAP)
- cFYI(1,("POSIX ACL cap"));
-#endif /* CIFS_DEBUG2 */
- if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
- cFYI(1,("setting capabilities failed"));
- }
- }
- }
+
+ /* tell server which Unix caps we support */
+ if (tcon->ses->capabilities & CAP_UNIX)
+ reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+
if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
cifs_sb->wsize = min(cifs_sb->wsize,
(tcon->ses->server->maxBuf -
__u16 count;
cFYI(1, ("In sesssetup"));
- if(ses == NULL)
+ if (ses == NULL)
return -EINVAL;
user = ses->userName;
domain = ses->domainName;
*bcc_ptr = 0;
bcc_ptr++;
}
- if(user == NULL)
+ if (user == NULL)
bytes_returned = 0; /* skip null user */
else
bytes_returned =
bcc_ptr += 2 * bytes_returned;
bcc_ptr += 2;
} else {
- if(user != NULL) {
+ if (user != NULL) {
strncpy(bcc_ptr, user, 200);
bcc_ptr += strnlen(user, 200);
}
cFYI(1,("Waking up socket by sending it signal"));
if(cifsd_task) {
send_sig(SIGKILL,cifsd_task,1);
- wait_for_completion(&cifsd_complete);
+ kthread_stop(cifsd_task);
}
rc = 0;
} /* else - we have an smb session