📄 cifssmb.c
字号:
/* * fs/cifs/cifssmb.c * * Copyright (C) International Business Machines Corp., 2002,2003 * Author(s): Steve French (sfrench@us.ibm.com) * * Contains the routines for constructing the SMB PDUs themselves * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ /* These are mostly routines that operate on a pathname, or on a tree id */ /* (mounted volume), but there are eight handle based routines which must be */ /* treated slightly different for reconnection purposes since we never want */ /* to reuse a stale file handle and the caller knows the file handle */#include <linux/fs.h>#include <linux/kernel.h>#include <linux/vfs.h>#include <asm/uaccess.h>#include "cifspdu.h"#include "cifsglob.h"#include "cifsproto.h"#include "cifs_unicode.h"#include "cifs_debug.h"#ifdef CONFIG_CIFS_POSIXstatic struct { int index; char *name;} protocols[] = { {CIFS_PROT, "\2NT LM 0.12"}, {CIFS_PROT, "\2POSIX 2"}, {BAD_PROT, "\2"}};#elsestatic struct { int index; char *name;} protocols[] = { {CIFS_PROT, "\2NT LM 0.12"}, {BAD_PROT, "\2"}};#endif/* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */static void mark_open_files_invalid(struct cifsTconInfo * pTcon){ struct cifsFileInfo *open_file = NULL; struct list_head * tmp; struct list_head * tmp1;/* list all files open on tree connection and mark them invalid */ write_lock(&GlobalSMBSeslock); list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { open_file = list_entry(tmp,struct cifsFileInfo, tlist); if(open_file) { open_file->invalidHandle = TRUE; } } write_unlock(&GlobalSMBSeslock); /* BB Add call to invalidate_inodes(sb) for all superblocks mounted to this tcon */}static intsmb_init(int smb_command, int wct, struct cifsTconInfo *tcon, void **request_buf /* returned */ , void **response_buf /* returned */ ){ int rc = 0; /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for tcp and smb session status done differently for those three - in the calling routine */ if(tcon) { if((tcon->ses) && (tcon->ses->server)){ struct nls_table *nls_codepage; /* Give Demultiplex thread up to 10 seconds to reconnect, should be greater than cifs socket timeout which is 7 seconds */ while(tcon->ses->server->tcpStatus == CifsNeedReconnect) { wait_event_interruptible_timeout(tcon->ses->server->response_q, (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ); if(tcon->ses->server->tcpStatus == CifsNeedReconnect) { /* on "soft" mounts we wait once */ if((tcon->retry == FALSE) || (tcon->ses->status == CifsExiting)) { cFYI(1,("gave up waiting on reconnect in smb_init")); return -EHOSTDOWN; } /* else "hard" mount - keep retrying until process is killed or server comes back up */ } else /* TCP session is reestablished now */ break; } nls_codepage = load_nls_default(); /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); if(tcon->ses->status == CifsNeedReconnect) rc = cifs_setup_session(0, tcon->ses, nls_codepage); if(!rc && (tcon->tidStatus == CifsNeedReconnect)) { mark_open_files_invalid(tcon); rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); up(&tcon->ses->sesSem); if(rc == 0) atomic_inc(&tconInfoReconnectCount); cFYI(1, ("reconnect tcon rc = %d", rc)); /* Removed call to reopen open files here - it is safer (and faster) to reopen files one at a time as needed in read and write */ /* Check if handle based operation so we know whether we can continue or not without returning to caller to reset file handle */ switch(smb_command) { case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: case SMB_COM_CLOSE: case SMB_COM_FIND_CLOSE2: case SMB_COM_LOCKING_ANDX: { unload_nls(nls_codepage); return -EAGAIN; } } } else { up(&tcon->ses->sesSem); } unload_nls(nls_codepage); } else { return -EIO; } } if(rc) return rc; *request_buf = cifs_buf_get(); if (*request_buf == 0) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM; } /* Although the original thought was we needed the response buf for */ /* potential retries of smb operations it turns out we can determine */ /* from the mid flags when the request buffer can be resent without */ /* having to use a second distinct buffer for the response */ *response_buf = *request_buf; header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, wct /*wct */ );#ifdef CONFIG_CIFS_STATS if(tcon != NULL) { atomic_inc(&tcon->num_smbs_sent); }#endif return rc;}intCIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses){ NEGOTIATE_REQ *pSMB; NEGOTIATE_RSP *pSMBr; int rc = 0; int bytes_returned; struct TCP_Server_Info * server; u16 count; if(ses->server) server = ses->server; else { rc = -EIO; return rc; } rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; if (extended_security) pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; count = strlen(protocols[0].name) + 1; strncpy(pSMB->DialectsArray, protocols[0].name, 30); /* null guaranteed to be at end of source and target buffers anyway */ pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc == 0) { server->secMode = pSMBr->SecurityMode; server->secType = NTLM; /* BB override default for NTLMv2 or krb*/ /* one byte - no need to convert this or EncryptionKeyLen from le,*/ server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount); /* probably no need to store and check maxvcs */ server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize), (__u32) CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE); server->maxRw = le32_to_cpu(pSMBr->MaxRawSize); cFYI(0, ("Max buf = %d ", ses->server->maxBuf)); GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); server->capabilities = le32_to_cpu(pSMBr->Capabilities); server->timeZone = le16_to_cpu(pSMBr->ServerTimeZone); /* BB with UTC do we ever need to be using srvr timezone? */ if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { memcpy(server->cryptKey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && (pSMBr->EncryptionKeyLength == 0)) { /* decode security blob */ } else rc = -EIO; /* BB might be helpful to save off the domain of server here */ if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && (server->capabilities & CAP_EXTENDED_SECURITY)) { count = pSMBr->ByteCount; if (count < 16) rc = -EIO; else if (count == 16) { server->secType = RawNTLMSSP; if (server->socketUseCount.counter > 1) { if (memcmp (server->server_GUID, pSMBr->u.extended_response. GUID, 16) != 0) { cFYI(1, ("UID of server does not match previous connection to same ip address")); memcpy(server-> server_GUID, pSMBr->u. extended_response. GUID, 16); } } else memcpy(server->server_GUID, pSMBr->u.extended_response. GUID, 16); } else { rc = decode_negTokenInit(pSMBr->u. extended_response. SecurityBlob, count - 16, &server->secType); } } else server->capabilities &= ~CAP_EXTENDED_SECURITY; if(sign_CIFS_PDUs == FALSE) { if(server->secMode & SECMODE_SIGN_REQUIRED) cERROR(1, ("Server requires /proc/fs/cifs/PacketSigningEnabled")); server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); } else if(sign_CIFS_PDUs == 1) { if((server->secMode & SECMODE_SIGN_REQUIRED) == 0) server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); } } if (pSMB) cifs_buf_release(pSMB); return rc;}intCIFSSMBTDis(const int xid, struct cifsTconInfo *tcon){ struct smb_hdr *smb_buffer; struct smb_hdr *smb_buffer_response; int rc = 0; int length; cFYI(1, ("In tree disconnect")); /* * If last user of the connection and * connection alive - disconnect it * If this is the last connection on the server session disconnect it * (and inside session disconnect we should check if tcp socket needs * to be freed and kernel thread woken up). */ if (tcon) down(&tcon->tconSem); else return -EIO; atomic_dec(&tcon->useCount); if (atomic_read(&tcon->useCount) > 0) { up(&tcon->tconSem); return -EBUSY; } /* 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; } if((tcon->ses == 0) || (tcon->ses->server == 0)) { up(&tcon->tconSem); return -EIO; } rc = smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, (void **) &smb_buffer, (void **) &smb_buffer_response); if (rc) { up(&tcon->tconSem); return rc; } rc = SendReceive(xid, tcon->ses, smb_buffer, smb_buffer_response, &length, 0); if (rc) cFYI(1, (" Tree disconnect failed %d", rc)); if (smb_buffer) cifs_buf_release(smb_buffer); up(&tcon->tconSem); /* No need to return error on this operation if tid invalidated and closed on server already e.g. due to tcp session crashing */ if (rc == -EAGAIN) rc = 0; return rc;}intCIFSSMBLogoff(const int xid, struct cifsSesInfo *ses){ struct smb_hdr *smb_buffer_response; LOGOFF_ANDX_REQ *pSMB; int rc = 0; int length; cFYI(1, ("In SMBLogoff for session disconnect")); if (ses) down(&ses->sesSem); else return -EIO; atomic_dec(&ses->inUse); if (atomic_read(&ses->inUse) > 0) { up(&ses->sesSem); return -EBUSY; } rc = smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL /* no tcon anymore */, (void **) &pSMB, (void **) &smb_buffer_response); if(ses->server) { if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } if (rc) { up(&ses->sesSem); return rc; } pSMB->hdr.Uid = ses->Suid; pSMB->AndXCommand = 0xFF; rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, smb_buffer_response, &length, 0); if (ses->server) { atomic_dec(&ses->server->socketUseCount); if (atomic_read(&ses->server->socketUseCount) == 0) { spin_lock(&GlobalMid_Lock); ses->server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); rc = -ESHUTDOWN; } } if (pSMB) cifs_buf_release(pSMB); up(&ses->sesSem); /* if session dead then we do not need to do ulogoff, since server closed smb session, no sense reporting error */ if (rc == -EAGAIN) rc = 0; return rc;}intCIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, const struct nls_table *nls_codepage){ DELETE_FILE_REQ *pSMB = NULL; DELETE_FILE_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len;DelFileRetry: rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifs_strtoUCS((wchar_t *) pSMB->fileName, fileName, 530 /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -