aboutsummaryrefslogtreecommitdiff
path: root/fs/cifs/cifssmb.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/cifs/cifssmb.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'fs/cifs/cifssmb.c')
-rw-r--r--fs/cifs/cifssmb.c4186
1 files changed, 4186 insertions, 0 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
new file mode 100644
index 00000000000..df6a619a682
--- /dev/null
+++ b/fs/cifs/cifssmb.c
@@ -0,0 +1,4186 @@
+/*
+ * fs/cifs/cifssmb.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2005
+ * 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 <linux/posix_acl_xattr.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_POSIX
+static struct {
+ int index;
+ char *name;
+} protocols[] = {
+ {CIFS_PROT, "\2NT LM 0.12"},
+ {CIFS_PROT, "\2POSIX 2"},
+ {BAD_PROT, "\2"}
+};
+#else
+static 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 */
+}
+
+/* If the return code is zero, this function must fill in request_buf pointer */
+static int
+small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
+ void **request_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_small_buf_get();
+ if (*request_buf == NULL) {
+ /* BB should we add a retry in here if not a writepage? */
+ return -ENOMEM;
+ }
+
+ header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,wct);
+
+#ifdef CONFIG_CIFS_STATS
+ if(tcon != NULL) {
+ atomic_inc(&tcon->num_smbs_sent);
+ }
+#endif /* CONFIG_CIFS_STATS */
+ return rc;
+}
+
+/* If the return code is zero, this function must fill in request_buf pointer */
+static int
+smb_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 == NULL) {
+ /* 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 /* CONFIG_CIFS_STATS */
+ return rc;
+}
+
+static int validate_t2(struct smb_t2_rsp * pSMB)
+{
+ int rc = -EINVAL;
+ int total_size;
+ char * pBCC;
+
+ /* check for plausible wct, bcc and t2 data and parm sizes */
+ /* check for parm and data offset going beyond end of smb */
+ if(pSMB->hdr.WordCount >= 10) {
+ if((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
+ (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
+ /* check that bcc is at least as big as parms + data */
+ /* check that bcc is less than negotiated smb buffer */
+ total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
+ if(total_size < 512) {
+ total_size+=le16_to_cpu(pSMB->t2_rsp.DataCount);
+ /* BCC le converted in SendReceive */
+ pBCC = (pSMB->hdr.WordCount * 2) + sizeof(struct smb_hdr) +
+ (char *)pSMB;
+ if((total_size <= (*(u16 *)pBCC)) &&
+ (total_size <
+ CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
+ return 0;
+ }
+
+ }
+ }
+ }
+ cifs_dump_mem("Invalid transact2 SMB: ",(char *)pSMB,
+ sizeof(struct smb_t2_rsp) + 16);
+ return rc;
+}
+int
+CIFSSMBNegotiate(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) CIFSMaxBufSize + 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);
+ if(rc == 1) {
+ /* BB Need to fill struct for sessetup here */
+ rc = -EOPNOTSUPP;
+ } else {
+ rc = -EINVAL;
+ }
+ }
+ } 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;
+}
+
+int
+CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response; /* BB removeme BB */
+ 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 == NULL) || (tcon->ses->server == NULL)) {
+ up(&tcon->tconSem);
+ return -EIO;
+ }
+ rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, (void **)&smb_buffer);
+ if (rc) {
+ up(&tcon->tconSem);
+ return rc;
+ } else {
+ smb_buffer_response = smb_buffer; /* BB removeme BB */
+ }
+ 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_small_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;
+}
+
+int
+CIFSSMBLogoff(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 = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
+ if (rc) {
+ up(&ses->sesSem);
+ return rc;
+ }
+
+ smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */
+
+ if(ses->server) {
+ if(ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+ }
+
+ 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_small_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;
+}
+
+int
+CIFSSMBDelFile(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, PATH_MAX
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += name_len + 1;
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Error in RMFile = %d", rc));
+ }
+#ifdef CONFIG_CIFS_STATS
+ else {
+ atomic_inc(&tcon->num_deletes);
+ }
+#endif
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto DelFileRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon,
+ const char *dirName, const struct nls_table *nls_codepage)
+{
+ DELETE_DIRECTORY_REQ *pSMB = NULL;
+ DELETE_DIRECTORY_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In CIFSSMBRmDir"));
+RmDirRetry:
+ rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifs_strtoUCS((wchar_t *) pSMB->DirName, dirName, PATH_MAX
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(dirName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->DirName, dirName, name_len);
+ }
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += name_len + 1;
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Error in RMDir = %d", rc));
+ }
+#ifdef CONFIG_CIFS_STATS
+ else {
+ atomic_inc(&tcon->num_rmdirs);
+ }
+#endif
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto RmDirRetry;
+ return rc;
+}
+
+int
+CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
+ const char *name, const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ CREATE_DIRECTORY_REQ *pSMB = NULL;
+ CREATE_DIRECTORY_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In CIFSSMBMkDir"));
+MkDirRetry:
+ rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifs_strtoUCS((wchar_t *) pSMB->DirName, name, PATH_MAX
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->DirName, name, name_len);
+ }
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += name_len + 1;
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Error in Mkdir = %d", rc));
+ }
+#ifdef CONFIG_CIFS_STATS
+ else {
+ atomic_inc(&tcon->num_mkdirs);
+ }
+#endif
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto MkDirRetry;
+ return rc;
+}
+
+int
+CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, const int openDisposition,
+ const int access_flags, const int create_options, __u16 * netfid,
+ int *pOplock, FILE_ALL_INFO * pfile_info,
+ const struct nls_table *nls_codepage)
+{
+ int rc = -EACCES;
+ OPEN_REQ *pSMB = NULL;
+ OPEN_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len;
+ __u16 count;
+
+openRetry:
+ rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ count = 1; /* account for one byte pad to word boundary */
+ name_len =
+ cifs_strtoUCS((wchar_t *) (pSMB->fileName + 1),
+ fileName, PATH_MAX
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->NameLength = cpu_to_le16(name_len);
+ } else { /* BB improve the check for buffer overruns BB */
+ count = 0; /* no pad */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ pSMB->NameLength = cpu_to_le16(name_len);
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ if (*pOplock & REQ_OPLOCK)
+ pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
+ else if (*pOplock & REQ_BATCHOPLOCK) {
+ pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
+ }
+ pSMB->DesiredAccess = cpu_to_le32(access_flags);
+ pSMB->AllocationSize = 0;
+ pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
+ /* XP does not handle ATTR_POSIX_SEMANTICS */
+ /* but it helps speed up case sensitive checks for other
+ servers such as Samba */
+ if (tcon->ses->capabilities & CAP_UNIX)
+ pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
+
+ /* if ((omode & S_IWUGO) == 0)
+ pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/
+ /* Above line causes problems due to vfs splitting create into two
+ pieces - need to set mode after file created not while it is
+ being created */
+ pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
+ pSMB->CreateDisposition = cpu_to_le32(openDisposition);
+ pSMB->CreateOptions = cpu_to_le32(create_options);
+ pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); /* BB ??*/
+ pSMB->SecurityFlags =
+ SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
+
+ count += name_len;
+ pSMB->hdr.smb_buf_length += count;
+
+ pSMB->ByteCount = cpu_to_le16(count);
+ /* long_op set to 1 to allow for oplock break timeouts */
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 1);
+ if (rc) {
+ cFYI(1, ("Error in Open = %d", rc));
+ } else {
+ *pOplock = pSMBr->OplockLevel; /* one byte no need to le_to_cpu */
+ *netfid = pSMBr->Fid; /* cifs fid stays in le */
+ /* Let caller know file was created so we can set the mode. */
+ /* Do we care about the CreateAction in any other cases? */
+ if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
+ *pOplock |= CIFS_CREATE_ACTION;
+ if(pfile_info) {
+ memcpy((char *)pfile_info,(char *)&pSMBr->CreationTime,
+ 36 /* CreationTime to Attributes */);
+ /* the file_info buf is endian converted by caller */
+ pfile_info->AllocationSize = pSMBr->AllocationSize;
+ pfile_info->EndOfFile = pSMBr->EndOfFile;
+ pfile_info->NumberOfLinks = cpu_to_le32(1);
+ }
+
+#ifdef CONFIG_CIFS_STATS
+ atomic_inc(&tcon->num_opens);
+#endif
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto openRetry;
+ return rc;
+}
+
+/* If no buffer passed in, then caller wants to do the copy
+ as in the case of readpages so the SMB buffer must be
+ freed by the caller */
+
+int
+CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, const unsigned int count,
+ const __u64 lseek, unsigned int *nbytes, char **buf)
+{
+ int rc = -EACCES;
+ READ_REQ *pSMB = NULL;
+ READ_RSP *pSMBr = NULL;
+ char *pReadData = NULL;
+ int bytes_returned;
+
+ cFYI(1,("Reading %d bytes on fid %d",count,netfid));
+
+ *nbytes = 0;
+ rc = smb_init(SMB_COM_READ_ANDX, 12, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
+ pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
+ pSMB->Remaining = 0;
+ pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
+ pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
+ pSMB->ByteCount = 0; /* no need to do le conversion since it is 0 */
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cERROR(1, ("Send error in read = %d", rc));
+ } else {
+ int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
+ data_length = data_length << 16;
+ data_length += le16_to_cpu(pSMBr->DataLength);
+ *nbytes = data_length;
+
+ /*check that DataLength would not go beyond end of SMB */
+ if ((data_length > CIFSMaxBufSize)
+ || (data_length > count)) {
+ cFYI(1,("bad length %d for count %d",data_length,count));
+ rc = -EIO;
+ *nbytes = 0;
+ } else {
+ pReadData =
+ (char *) (&pSMBr->hdr.Protocol) +
+ le16_to_cpu(pSMBr->DataOffset);
+/* if(rc = copy_to_user(buf, pReadData, data_length)) {
+ cERROR(1,("Faulting on read rc = %d",rc));
+ rc = -EFAULT;
+ }*/ /* can not use copy_to_user when using page cache*/
+ if(*buf)
+ memcpy(*buf,pReadData,data_length);
+ }
+ }
+ if(*buf)
+ cifs_buf_release(pSMB);
+ else
+ *buf = (char *)pSMB;
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+ return rc;
+}
+
+int
+CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, const unsigned int count,
+ const __u64 offset, unsigned int *nbytes, const char *buf,
+ const char __user * ubuf, const int long_op)
+{
+ int rc = -EACCES;
+ WRITE_REQ *pSMB = NULL;
+ WRITE_RSP *pSMBr = NULL;
+ int bytes_returned;
+ __u32 bytes_sent;
+ __u16 byte_count;
+
+ /* cFYI(1,("write at %lld %d bytes",offset,count));*/
+ rc = smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
+ pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+ pSMB->Reserved = 0xFFFFFFFF;
+ pSMB->WriteMode = 0;
+ pSMB->Remaining = 0;
+
+ /* Can increase buffer size if buffer is big enough in some cases - ie we
+ can send more if LARGE_WRITE_X capability returned by the server and if
+ our buffer is big enough or if we convert to iovecs on socket writes
+ and eliminate the copy to the CIFS buffer */
+ if(tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
+ bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
+ } else {
+ bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
+ & ~0xFF;
+ }
+
+ if (bytes_sent > count)
+ bytes_sent = count;
+ pSMB->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
+ if(buf)
+ memcpy(pSMB->Data,buf,bytes_sent);
+ else if(ubuf) {
+ if(copy_from_user(pSMB->Data,ubuf,bytes_sent)) {
+ cifs_buf_release(pSMB);
+ return -EFAULT;
+ }
+ } else {
+ /* No buffer */
+ cifs_buf_release(pSMB);
+ return -EINVAL;
+ }
+
+ byte_count = bytes_sent + 1 /* pad */ ; /* BB fix this for sends > 64K */
+ pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
+ pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
+ pSMB->hdr.smb_buf_length += bytes_sent+1;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
+ if (rc) {
+ cFYI(1, ("Send error in write = %d", rc));
+ *nbytes = 0;
+ } else {
+ *nbytes = le16_to_cpu(pSMBr->CountHigh);
+ *nbytes = (*nbytes) << 16;
+ *nbytes += le16_to_cpu(pSMBr->Count);
+ }
+
+ cifs_buf_release(pSMB);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, const unsigned int count,
+ const __u64 offset, unsigned int *nbytes, const char __user *buf,
+ const int long_op)
+{
+ int rc = -EACCES;
+ WRITE_REQ *pSMB = NULL;
+ WRITE_RSP *pSMBr = NULL;
+ /*int bytes_returned;*/
+ unsigned bytes_sent;
+ __u16 byte_count;
+
+ rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMBr = (WRITE_RSP *)pSMB; /* BB removeme BB */
+
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
+ pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+ pSMB->Reserved = 0xFFFFFFFF;
+ pSMB->WriteMode = 0;
+ pSMB->Remaining = 0;
+ bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & ~0xFF;
+ if (bytes_sent > count)
+ bytes_sent = count;
+ pSMB->DataLengthHigh = 0;
+ pSMB->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
+
+ byte_count = bytes_sent + 1 /* pad */ ;
+ pSMB->DataLengthLow = cpu_to_le16(bytes_sent);
+ pSMB->DataLengthHigh = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+/* rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, buf, buflen, &bytes_returned, long_op); */ /* BB fixme BB */
+ if (rc) {
+ cFYI(1, ("Send error in write2 (large write) = %d", rc));
+ *nbytes = 0;
+ } else
+ *nbytes = le16_to_cpu(pSMBr->Count);
+
+ cifs_small_buf_release(pSMB);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+#endif /* CIFS_EXPERIMENTAL */
+
+int
+CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
+ const __u16 smb_file_id, const __u64 len,
+ const __u64 offset, const __u32 numUnlock,
+ const __u32 numLock, const __u8 lockType, const int waitFlag)
+{
+ int rc = 0;
+ LOCK_REQ *pSMB = NULL;
+ LOCK_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int timeout = 0;
+ __u16 count;
+
+ cFYI(1, ("In CIFSSMBLock - timeout %d numLock %d",waitFlag,numLock));
+ rc = smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if(lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
+ timeout = -1; /* no response expected */
+ pSMB->Timeout = 0;
+ } else if (waitFlag == TRUE) {
+ timeout = 3; /* blocking operation, no timeout */
+ pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
+ } else {
+ pSMB->Timeout = 0;
+ }
+
+ pSMB->NumberOfLocks = cpu_to_le16(numLock);
+ pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
+ pSMB->LockType = lockType;
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = smb_file_id; /* netfid stays le */
+
+ if((numLock != 0) || (numUnlock != 0)) {
+ pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
+ /* BB where to store pid high? */
+ pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
+ pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
+ pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
+ pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
+ count = sizeof(LOCKING_ANDX_RANGE);
+ } else {
+ /* oplock break */
+ count = 0;
+ }
+ pSMB->hdr.smb_buf_length += count;
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, timeout);
+
+ if (rc) {
+ cFYI(1, ("Send error in Lock = %d", rc));
+ }
+ cifs_buf_release(pSMB);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+ return rc;
+}
+
+int
+CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
+{
+ int rc = 0;
+ CLOSE_REQ *pSMB = NULL;
+ CLOSE_RSP *pSMBr = NULL;
+ int bytes_returned;
+ cFYI(1, ("In CIFSSMBClose"));
+
+/* do not retry on dead session on close */
+ rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
+ if(rc == -EAGAIN)
+ return 0;
+ if (rc)
+ return rc;
+
+ pSMBr = (CLOSE_RSP *)pSMB; /* BB removeme BB */
+
+ pSMB->FileID = (__u16) smb_file_id;
+ pSMB->LastWriteTime = 0;
+ pSMB->By