Commit 2aa60962 authored by Steve French's avatar Steve French

Fix locking of global smb list. Add missing rc checks.

parent ecf2c214
......@@ -79,18 +79,20 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
buf += length;
i = 0;
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalSMBSessionList) {
i++;
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
length =
sprintf(buf,
"\n%d) Name: %s Domain: %s HowManyMounts: %d LocalUsersToSameServer: %d\n\t ServerOS: %s ServerNOS: %s Capabilities: 0x%x ",
i, ses->serverName, ses->serverDomain,
atomic_read(&ses->inUse),
atomic_read(&ses->server->socketUseCount),
"\n%d) Name: %s Domain: %s HowManyMounts: %d ServerOS: %s ServerNOS: %s Capabilities: 0x%x\n",
i, ses->serverName, ses->serverDomain, atomic_read(&ses->inUse),
ses->serverOS, ses->serverNOS, ses->capabilities);
buf += length;
if(ses->server)
buf += sprintf(buf, "\tLocal Users To Same Server: %d ",atomic_read(&ses->server->socketUseCount));
}
read_unlock(&GlobalSMBSeslock);
sprintf(buf, "\n");
buf++;
printk("\nTotal Buffer so far: %s\n", buf_start);
......@@ -99,6 +101,7 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
buf += length;
i = 0;
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalTreeConnectionList) {
i++;
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
......@@ -122,6 +125,7 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
tcon->fsDevInfo.DeviceType);
buf += length;
}
read_unlock(&GlobalSMBSeslock);
length = sprintf(buf, "\n");
buf += length;
*eof = 1;
......@@ -262,10 +266,13 @@ cifs_proc_clean(void)
remove_proc_entry("DebugData", proc_fs_cifs);
remove_proc_entry("cifsFYI", proc_fs_cifs);
remove_proc_entry("TraceSMB", proc_fs_cifs);
remove_proc_entry("MaxSimultaneousOps", proc_fs_cifs);
remove_proc_entry("SimultaneousOps", proc_fs_cifs);
remove_proc_entry("TotalOps", proc_fs_cifs);
remove_proc_entry("MultiuserMount", proc_fs_cifs);
remove_proc_entry("oplockEnabled", proc_fs_cifs);
remove_proc_entry("NTLMV2Enabled",proc_fs_cifs);
remove_proc_entry("ExtendedSecurity",proc_fs_cifs);
remove_proc_entry("PacketSigningEnabled",proc_fs_cifs);
remove_proc_entry("cifs", proc_root_fs);
}
......
......@@ -66,6 +66,8 @@ cifs_read_super(struct super_block *sb, void *data, char *devname, int silent)
sb->s_fs_info = kmalloc(sizeof(struct cifs_sb_info),GFP_KERNEL);
cifs_sb = CIFS_SB(sb);
if(cifs_sb == NULL)
return -ENOMEM;
cifs_sb->local_nls = load_nls_default(); /* needed for ASCII cp to Unicode converts */
rc = cifs_mount(sb, cifs_sb, data, devname);
......@@ -97,9 +99,8 @@ cifs_read_super(struct super_block *sb, void *data, char *devname, int silent)
if (inode)
iput(inode);
/* rc = cifs_umount(sb); BB is CIFS unmount routine needed? */
if (rc) {
cERROR(1, ("cifs_umount failed with return code %d\n", rc));
cERROR(1, ("cifs_mount failed with no root inode"));
}
out_mount_failed:
if(cifs_sb)
......@@ -114,15 +115,18 @@ cifs_put_super(struct super_block *sb)
struct cifs_sb_info *cifs_sb;
cFYI(1, ("In cifs_put_super\n"));
schedule_timeout(HZ*4);
cifs_sb = CIFS_SB(sb);
if(cifs_sb == NULL) {
cFYI(1,("\nEmpty cifs superblock info passed to unmount"));
return;
}
rc = cifs_umount(sb, cifs_sb);
if (rc) {
cERROR(1, ("cifs_umount failed with return code %d\n", rc));
}
if(cifs_sb) {
unload_nls(cifs_sb->local_nls);
kfree(cifs_sb);
}
return;
}
......@@ -387,8 +391,11 @@ init_cifs(void)
atomic_set(&tconInfoAllocCount, 0);
atomic_set(&bufAllocCount, 0);
atomic_set(&midCount, 0);
GlobalCurrentXid = 0;
GlobalTotalActiveXid = 0;
GlobalMaxActiveXid = 0;
GlobalSMBSeslock = RW_LOCK_UNLOCKED;
GlobalMid_Lock = RW_LOCK_UNLOCKED;
rc = cifs_init_inodecache();
if (!rc) {
......
......@@ -292,13 +292,10 @@ GLOBAL_EXTERN struct servers_not_supported *NotSuppList; /*@z4a */
*/
GLOBAL_EXTERN struct smbUidInfo *GlobalUidList[UID_HASH];
GLOBAL_EXTERN struct list_head GlobalServerList; /* BB this one is not implemented yet */
GLOBAL_EXTERN struct list_head GlobalServerList; /* BB not implemented yet */
GLOBAL_EXTERN struct list_head GlobalSMBSessionList;
GLOBAL_EXTERN struct list_head GlobalTreeConnectionList;
/*
* Global list of free SMB structures
*/
GLOBAL_EXTERN void *GlobalFreeSMB;
GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
/*
* Global transaction id (XID) information
......@@ -306,7 +303,8 @@ GLOBAL_EXTERN void *GlobalFreeSMB;
GLOBAL_EXTERN unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */
GLOBAL_EXTERN unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */
GLOBAL_EXTERN unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */
GLOBAL_EXTERN rwlock_t GlobalMid_Lock; /* protects above and list operations */
/* on midQ entries */
GLOBAL_EXTERN char Local_System_Name[15];
/*
......@@ -330,4 +328,3 @@ GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent
GLOBAL_EXTERN unsigned int ntlmv2_support; /* better optional password hash */
GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */
......@@ -505,7 +505,8 @@ CIFSSMBRead(const int xid, const struct cifsTconInfo *tcon,
pReadData =
(char *) (&pSMBr->hdr.Protocol) +
le16_to_cpu(pSMBr->DataOffset);
copy_to_user(buf, pReadData, pSMBr->DataLength);
if(copy_to_user(buf, pReadData, pSMBr->DataLength))
rc = -EFAULT;
}
}
......@@ -544,7 +545,13 @@ CIFSSMBWrite(const int xid, const struct cifsTconInfo *tcon,
pSMB->DataLengthHigh = 0;
pSMB->DataOffset =
cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
copy_from_user(pSMB->Data, buf, pSMB->DataLengthLow);
if(copy_from_user(pSMB->Data, buf, pSMB->DataLengthLow)) {
buf_release(pSMB);
return -EFAULT;
}
pSMB->ByteCount += pSMB->DataLengthLow + 1 /* pad */ ;
pSMB->DataLengthLow = cpu_to_le16(pSMB->DataLengthLow);
......
......@@ -219,6 +219,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
}
task_to_wake = NULL;
read_lock(&GlobalMid_Lock);
list_for_each(tmp, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct
mid_q_entry,
......@@ -234,7 +235,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
MID_RESPONSE_RECEIVED;
}
}
read_unlock(&GlobalMid_Lock);
if (task_to_wake) {
smb_buffer = NULL; /* will be freed by users thread after he is done */
wake_up_process(task_to_wake);
......@@ -262,6 +263,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
set_fs(temp_fs);
if (smb_buffer) /* buffer usually freed in free_mid - need to free it on error or exit */
buf_release(smb_buffer);
read_lock(&GlobalSMBSeslock);
if (list_empty(&server->pending_mid_q)) {
/* loop through server session structures attached to this and mark them dead */
list_for_each(tmp, &GlobalSMBSessionList) {
......@@ -279,6 +282,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
cERROR(1, ("\nThere are still active MIDs in queue and we are exiting but we can not delete mid_q_entries or TCP_Server_Info structure due to pending requests MEMORY LEAK!!\n ")); /* BB wake up waitors, and/or wait and/or free stale mids and try again? BB */
/* BB Need to fix bug in error path above - perhaps wait until smb requests
time out and then free the tcp per server struct BB */
read_unlock(&GlobalSMBSeslock);
cFYI(1, ("\nAbout to exit from demultiplex thread\n"));
return 0;
......@@ -421,7 +426,7 @@ find_tcp_session(__u32 new_target_ip_addr,
struct cifsSesInfo *ses;
*psrvTcp = NULL;
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalSMBSessionList) {
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
if (ses->server) {
......@@ -432,12 +437,15 @@ find_tcp_session(__u32 new_target_ip_addr,
/* BB check if reconnection needed */
if (strncmp
(ses->userName, userName,
MAX_USERNAME_SIZE) == 0)
MAX_USERNAME_SIZE) == 0){
read_unlock(&GlobalSMBSeslock);
return ses; /* found exact match on both tcp and SMB sessions */
}
}
}
/* else tcp and smb sessions need reconnection */
}
read_unlock(&GlobalSMBSeslock);
return NULL;
}
......@@ -447,6 +455,7 @@ find_unc(__u32 new_target_ip_addr, char *uncName, char *userName)
struct list_head *tmp;
struct cifsTconInfo *tcon;
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalTreeConnectionList) {
cFYI(1, ("\nNext tcon - "));
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
......@@ -473,13 +482,16 @@ find_unc(__u32 new_target_ip_addr, char *uncName, char *userName)
if (strncmp
(tcon->ses->userName,
userName,
MAX_USERNAME_SIZE) == 0)
MAX_USERNAME_SIZE) == 0) {
read_unlock(&GlobalSMBSeslock);
return tcon;/* also matched user (smb session)*/
}
}
}
}
}
}
read_unlock(&GlobalSMBSeslock);
return NULL;
}
......@@ -616,7 +628,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
xid = GetXid();
cFYI(0, ("\nEntering cifs_mount. Xid: %d with: %s\n", xid, mount_data));
parse_mount_options(mount_data, devname, &volume_info);
if(parse_mount_options(mount_data, devname, &volume_info)) {
FreeXid(xid);
return -EINVAL;
}
if (volume_info.username) {
cFYI(1, ("\nUsername: %s ", volume_info.username));
......@@ -634,7 +650,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
cERROR(1,
("\nCIFS mount error: No UNC path (e.g. -o unc=//192.168.1.100/public) specified "));
FreeXid(xid);
return -ENODEV;
return -EINVAL;
}
/* BB add support to use the multiuser_mount flag BB */
existingCifsSes =
......@@ -2202,6 +2218,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
struct cifsSesInfo *ses = NULL;
xid = GetXid();
if (cifs_sb->tcon) {
ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
rc = CIFSSMBTDis(xid, cifs_sb->tcon);
......@@ -2218,15 +2235,10 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
FreeXid(xid);
return 0;
}
/* wake_up_process(ses->server->tsk);*/ /* was worth a try */
schedule_timeout(HZ / 4); /* give captive thread time to exit */
if((ses->server) && (ses->server->ssocket)) {
cFYI(1,("\nWaking up socket by sending it signal "));
send_sig(SIGINT,ses->server->tsk,1);
/* No luck figuring out a better way to_close socket */
/*ses->server->ssocket->sk->prot->close(ses->server->ssocket->sk,0);*/
/* ses->server->ssocket = NULL; */ /* serialize better */
/* sock_wake_async(ses->server->ssocket,3,POLL_HUP); */
}
} else
cFYI(1, ("\nNo session or bad tcon"));
......
......@@ -28,7 +28,6 @@
extern kmem_cache_t *cifs_req_cachep;
static DECLARE_MUTEX(GlobalMid_Sem); /* also protects XID globals */
__u16 GlobalMid; /* multiplex id - rotating counter */
/* The xid serves as a useful identifier for each incoming vfs request,
......@@ -42,21 +41,21 @@ _GetXid(void)
{
unsigned int xid;
down(&GlobalMid_Sem);
write_lock(&GlobalMid_Lock);
GlobalTotalActiveXid++;
if (GlobalTotalActiveXid > GlobalMaxActiveXid)
GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */
xid = GlobalCurrentXid++;
up(&GlobalMid_Sem);
write_unlock(&GlobalMid_Lock);
return xid;
}
void
_FreeXid(unsigned int xid)
{
down(&GlobalMid_Sem);
write_lock(&GlobalMid_Lock);
GlobalTotalActiveXid--;
up(&GlobalMid_Sem);
write_unlock(&GlobalMid_Lock);
}
struct cifsSesInfo *
......@@ -69,9 +68,11 @@ sesInfoAlloc(void)
GFP_KERNEL);
if (ret_buf) {
memset(ret_buf, 0, sizeof (struct cifsSesInfo));
write_lock(&GlobalSMBSeslock);
atomic_inc(&sesInfoAllocCount);
list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList);
init_MUTEX(&ret_buf->sesSem);
write_unlock(&GlobalSMBSeslock);
}
return ret_buf;
}
......@@ -84,8 +85,10 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
return;
}
write_lock(&GlobalSMBSeslock);
atomic_dec(&sesInfoAllocCount);
list_del(&buf_to_free->cifsSessionList);
write_unlock(&GlobalSMBSeslock);
if (buf_to_free->serverOS)
kfree(buf_to_free->serverOS);
if (buf_to_free->serverDomain)
......@@ -104,11 +107,13 @@ tconInfoAlloc(void)
GFP_KERNEL);
if (ret_buf) {
memset(ret_buf, 0, sizeof (struct cifsTconInfo));
write_lock(&GlobalSMBSeslock);
atomic_inc(&tconInfoAllocCount);
list_add(&ret_buf->cifsConnectionList,
&GlobalTreeConnectionList);
INIT_LIST_HEAD(&ret_buf->openFileList);
init_MUTEX(&ret_buf->tconSem);
write_unlock(&GlobalSMBSeslock);
}
return ret_buf;
}
......@@ -120,9 +125,10 @@ tconInfoFree(struct cifsTconInfo *buf_to_free)
cFYI(1, ("\nNull buffer passed to tconInfoFree"));
return;
}
write_lock(&GlobalSMBSeslock);
atomic_dec(&tconInfoAllocCount);
list_del(&buf_to_free->cifsConnectionList);
write_unlock(&GlobalSMBSeslock);
if (buf_to_free->nativeFileSystem)
kfree(buf_to_free->nativeFileSystem);
kfree(buf_to_free);
......@@ -203,9 +209,10 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
buffer->Pid = tmp & 0xFFFF;
tmp >>= 16;
buffer->PidHigh = tmp & 0xFFFF;
down(&GlobalMid_Sem);
write_lock(&GlobalMid_Lock);
GlobalMid++;
buffer->Mid = GlobalMid;
write_unlock(&GlobalMid_Lock);
if (treeCon) {
buffer->Tid = treeCon->tid;
if (treeCon->ses) {
......@@ -220,11 +227,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
buffer->Flags2 |= SMBFLG2_DFS;
if(treeCon->ses->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
}
/* endian conversion of flags is now done just before sending */
up(&GlobalMid_Sem);
buffer->WordCount = (char) word_count;
return;
}
......@@ -234,7 +239,8 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid)
{
/* Make sure that this really is an SMB, that it is a response,
and that the message ids match */
if ((*(unsigned int *) smb->Protocol == cpu_to_le32(0x424d53ff)) && (mid == smb->Mid)) {
if ((*(unsigned int *) smb->Protocol == cpu_to_le32(0x424d53ff)) &&
(mid == smb->Mid)) {
if(smb->Flags & SMBFLG_RESPONSE)
return 0;
else {
......@@ -269,7 +275,7 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length)
cERROR(1, ("\n Length less than 2 + sizeof smb_hdr "));
if ((length >= sizeof (struct smb_hdr) - 1)
&& (smb->Status.CifsError != 0))
return 0; /* this is ok - some error cases do not return wct and bcc */
return 0; /* some error cases do not return wct and bcc */
}
if (4 + ntohl(smb->smb_buf_length) >
......@@ -299,6 +305,8 @@ int
is_valid_oplock_break(struct smb_hdr *buf)
{
struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)buf;
struct list_head *tmp;
struct cifsTconInfo *tcon;
/* could add check for smb response flag 0x80 */
cFYI(1,("\nChecking for oplock break"));
......@@ -313,14 +321,24 @@ is_valid_oplock_break(struct smb_hdr *buf)
if(!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
return FALSE;
/* look up tcon based on tid & uid */
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalTreeConnectionList) {
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
if (tcon->tid == buf->Tid)
if(tcon->ses->Suid == buf->Uid) {
/* BB Add following logic:
1) look up tcon based on tid & uid
2) look up inode from tcon->openFileList->file->f_dentry->d_inode
3) flush dirty pages and cached byte range locks and mark inode
4) depending on break type change to r/o caching or no caching
5) send oplock break response to server */
cFYI(1,("\nNeed to process oplock break "));
read_unlock(&GlobalSMBSeslock);
cFYI(1,("\nFound matching connection, process oplock break"));
return TRUE;
}
}
read_unlock(&GlobalSMBSeslock);
cFYI(1,("\nProcessing oplock break for non-existent connection"));
return TRUE;
}
......
......@@ -59,9 +59,11 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
temp->tsk = current;
}
if (ses->status == CifsGood) {
write_lock(&GlobalMid_Lock);
list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
atomic_inc(&midCount);
temp->midState = MID_REQUEST_ALLOCATED;
write_unlock(&GlobalMid_Lock);
} else { /* BB add reconnect code here BB */
cERROR(1,
......@@ -77,11 +79,13 @@ void
DeleteMidQEntry(struct mid_q_entry *midEntry)
{
/* BB add spinlock to protect midq for each session BB */
write_lock(&GlobalMid_Lock);
midEntry->midState = MID_FREE;
buf_release(midEntry->resp_buf);
list_del(&midEntry->qhead);
kmem_cache_free(cifs_mid_cachep, midEntry);
atomic_dec(&midCount);
write_unlock(&GlobalMid_Lock);
buf_release(midEntry->resp_buf);
kmem_cache_free(cifs_mid_cachep, midEntry);
}
int
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment