summaryrefslogtreecommitdiff
path: root/release/src/linux/linux/fs/cifs/cifssmb.c
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/linux/linux/fs/cifs/cifssmb.c')
-rw-r--r--release/src/linux/linux/fs/cifs/cifssmb.c3016
1 files changed, 3016 insertions, 0 deletions
diff --git a/release/src/linux/linux/fs/cifs/cifssmb.c b/release/src/linux/linux/fs/cifs/cifssmb.c
new file mode 100644
index 00000000..ca4dedf7
--- /dev/null
+++ b/release/src/linux/linux/fs/cifs/cifssmb.c
@@ -0,0 +1,3016 @@
+/*
+ * 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_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 */
+}
+
+static int
+smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
+ void **request_buf /* returned */ ,
+ void **response_buf /* returned */ )
+{
+ int rc = 0;
+ int timeout = 10 * HZ;
+
+ /* 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) {
+ while ((tcon->ses->server->tcpStatus != CifsGood) && (timeout > 0)){
+ timeout = interruptible_sleep_on_timeout(&tcon->ses->server->response_q,timeout);
+ }
+ 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;
+}
+
+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;
+
+ 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;
+
+ pSMB->ByteCount = 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 += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ 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) {
+ if (pSMBr->ByteCount < 16)
+ rc = -EIO;
+ else if (pSMBr->ByteCount == 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,
+ pSMBr->ByteCount -
+ 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;
+}
+
+int
+CIFSSMBTDis(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;
+}
+
+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 = smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL /* no tcon anymore */,
+ (void **) &pSMB, (void **) &smb_buffer_response);
+
+ 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;
+}
+
+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, 530
+ /* 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, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
+ pSMB->ByteCount = name_len + 1;
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ 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
+
+ if (pSMB)
+ 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, 530
+ /* 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, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->DirName, dirName, name_len);
+ }
+
+ pSMB->ByteCount = name_len + 1;
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ 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
+
+ if (pSMB)
+ 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, 530
+ /* 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, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->DirName, name, name_len);
+ }
+
+ pSMB->ByteCount = name_len + 1 /* for buf format */ ;
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ 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
+ if (pSMB)
+ 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;
+
+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) {
+ pSMB->ByteCount = 1; /* account for one byte pad to word boundary */
+ name_len =
+ cifs_strtoUCS((wchar_t *) (pSMB->fileName + 1),
+ fileName, 530
+ /* 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 */
+ pSMB->ByteCount = 0; /* no pad */
+ name_len = strnlen(fileName, 530);
+ 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 = 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 |= ATTR_POSIX_SEMANTICS;
+
+ /* if ((omode & S_IWUGO) == 0)
+ pSMB->FileAttributes |= 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->FileAttributes = cpu_to_le32(pSMB->FileAttributes);
+ 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 =
+ cpu_to_le32(SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY);
+
+ pSMB->ByteCount += name_len;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ /* 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
+ }
+ if (pSMB)
+ 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;
+
+ *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);
+ pSMB->MaxCountHigh = 0;
+ 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 {
+ pSMBr->DataLength = le16_to_cpu(pSMBr->DataLength);
+ *nbytes = pSMBr->DataLength;
+ /*check that DataLength would not go beyond end of SMB */
+ if ((pSMBr->DataLength > CIFS_MAX_MSGSIZE)
+ || (pSMBr->DataLength > count)) {
+ cFYI(1,("bad length %d for count %d",pSMBr->DataLength,count));
+ rc = -EIO;
+ *nbytes = 0;
+ } else {
+ pReadData =
+ (char *) (&pSMBr->hdr.Protocol) +
+ le16_to_cpu(pSMBr->DataOffset);
+/* if(rc = copy_to_user(buf, pReadData, pSMBr->DataLength)) {
+ 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,pSMBr->DataLength);
+ }
+ }
+ if (pSMB) {
+ 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 int long_op)
+{
+ int rc = -EACCES;
+ WRITE_REQ *pSMB = NULL;
+ WRITE_RSP *pSMBr = NULL;
+ int bytes_returned;
+
+ 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->Remaining = 0;
+ if (count > ((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00))
+ pSMB->DataLengthLow =
+ (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00;
+ else
+ pSMB->DataLengthLow = count;
+ pSMB->DataLengthHigh = 0;
+ pSMB->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);
+
+ memcpy(pSMB->Data,buf,pSMB->DataLengthLow);
+
+ pSMB->ByteCount += pSMB->DataLengthLow + 1 /* pad */ ;
+ pSMB->DataLengthLow = cpu_to_le16(pSMB->DataLengthLow);
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ 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->Count);
+
+ if (pSMB)
+ 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
+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;
+ __u64 temp;
+
+ 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 = -1; /* blocking - do not time out */
+ } else {
+ pSMB->Timeout = 0;
+ }
+
+ pSMB->NumberOfLocks = cpu_to_le32(numLock);
+ pSMB->NumberOfUnlocks = cpu_to_le32(numUnlock);
+ pSMB->LockType = lockType;
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = smb_file_id; /* netfid stays le */
+
+ if(numLock != 0) {
+ pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
+ /* BB where to store pid high? */
+ temp = cpu_to_le64(len);
+ pSMB->Locks[0].LengthLow = (__u32)(temp & 0xFFFFFFFF);
+ pSMB->Locks[0].LengthHigh = (__u32)(temp>>32);
+ temp = cpu_to_le64(offset);
+ pSMB->Locks[0].OffsetLow = (__u32)(temp & 0xFFFFFFFF);
+ pSMB->Locks[0].OffsetHigh = (__u32)(temp>>32);
+ pSMB->ByteCount = sizeof (LOCKING_ANDX_RANGE);
+ } else {
+ /* oplock break */
+ pSMB->ByteCount = 0;
+ }
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ 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));
+ }
+ if (pSMB)
+ 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 = smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if(rc == -EAGAIN)
+ return 0;
+ if (rc)
+ return rc;
+
+ pSMB->FileID = (__u16) smb_file_id;
+ pSMB->LastWriteTime = 0;
+ pSMB->ByteCount = 0;
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ if(rc!=-EINTR) {
+ /* EINTR is expected when user ctl-c to kill app */
+ cERROR(1, ("Send error in Close = %d", rc));
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ /* Since session is dead, file will be closed on server already */
+ if(rc == -EAGAIN)
+ rc = 0;
+
+ return rc;
+}
+
+int
+CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ RENAME_REQ *pSMB = NULL;
+ RENAME_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+
+ cFYI(1, ("In CIFSSMBRename"));
+renameRetry:
+ rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, 530
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->OldFileName[name_len] = 0x04; /* pad */
+ /* protocol requires ASCII signature byte on Unicode string */
+ pSMB->OldFileName[name_len + 1] = 0x00;
+ name_len2 =
+ cifs_strtoUCS((wchar_t *) & pSMB->
+ OldFileName[name_len + 2], toName, 530,
+ nls_codepage);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, fromName, name_len);
+ name_len2 = strnlen(toName, 530);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ pSMB->ByteCount = 1 /* 1st signature byte */ + name_len + name_len2;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in rename = %d", rc));
+ }
+
+#ifdef CONFIG_CIFS_STATS
+ else {
+ atomic_inc(&tcon->num_renames);
+ }
+#endif
+
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto renameRetry;
+
+ return rc;
+}
+
+int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon,
+ int netfid, char * target_name, const struct nls_table * nls_codepage)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
+ struct set_file_rename * rename_info;
+ char *data_offset;
+ char dummy_string[30];
+ int rc = 0;
+ int bytes_returned = 0;
+ int len_of_str;
+
+ cFYI(1, ("Rename to File by handle"));
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->ParameterCount = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = offsetof(struct smb_com_transaction2_sfi_req,
+ Fid) - 4;
+ pSMB->DataOffset = pSMB->ParameterOffset + pSMB->ParameterCount;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + pSMB->DataOffset;
+ rename_info = (struct set_file_rename *) data_offset;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB PDU from sess */
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ pSMB->ByteCount = 3 /* pad */ + pSMB->ParameterCount;
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(pSMB->ParameterOffset);
+ pSMB->DataOffset = cpu_to_le16(pSMB->DataOffset);
+ /* construct random name ".cifs_tmp<inodenum><mid>" */
+ rename_info->overwrite = cpu_to_le32(1);
+ rename_info->root_fid = 0;
+ /* unicode only call */
+ if(target_name == NULL) {
+ sprintf(dummy_string,"cifs%x",pSMB->hdr.Mid);
+ len_of_str = cifs_strtoUCS((wchar_t *) rename_info->target_name, dummy_string, 24, nls_codepage);
+ } else {
+ len_of_str = cifs_strtoUCS((wchar_t *) rename_info->target_name, target_name, 530, nls_codepage);
+ }
+ rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
+ pSMB->DataCount = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2;
+ pSMB->ByteCount += pSMB->DataCount;
+ pSMB->DataCount = cpu_to_le16(pSMB->DataCount);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->Fid = netfid;
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1,("Send error in Rename (by file handle) = %d", rc));
+ }
+#ifdef CONFIG_CIFS_STATS
+ else {
+ atomic_inc(&pTcon->num_t2renames);
+ }
+#endif
+ if (pSMB)
+ 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
+CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char * fromName,
+ const __u16 target_tid, const char *toName, const int flags,
+ const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ COPY_REQ *pSMB = NULL;
+ COPY_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+
+ cFYI(1, ("In CIFSSMBCopy"));
+copyRetry:
+ rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->Tid2 = target_tid;
+
+ if(flags & COPY_TREE)
+ pSMB->Flags |= COPY_TREE;
+ pSMB->Flags = cpu_to_le16(pSMB->Flags);
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifs_strtoUCS((wchar_t *) pSMB->OldFileName,
+ fromName,
+ 530 /* find define for this maxpathcomponent */,
+ nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->OldFileName[name_len] = 0x04; /* pad */
+ /* protocol requires ASCII signature byte on Unicode string */
+ pSMB->OldFileName[name_len + 1] = 0x00;
+ name_len2 = cifs_strtoUCS((wchar_t *) & pSMB->
+ OldFileName[name_len + 2], toName, 530,
+ nls_codepage);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, fromName, name_len);
+ name_len2 = strnlen(toName, 530);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ pSMB->ByteCount = 1 /* 1st signature byte */ + name_len + name_len2;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in copy = %d with %d files copied",
+ rc, pSMBr->CopyCount));
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto copyRetry;
+
+ return rc;
+}
+
+int
+CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ char *data_offset;
+ int name_len;
+ int name_len_target;
+ int rc = 0;
+ int bytes_returned = 0;
+
+ cFYI(1, ("In Symlink Unix style"));
+createSymLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->FileName, fromName, 530
+ /* 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(fromName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fromName, name_len);
+ }
+ pSMB->ParameterCount = 6 + name_len;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ pSMB->DataOffset = pSMB->ParameterOffset + pSMB->ParameterCount;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + pSMB->DataOffset;
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len_target =
+ cifs_strtoUCS((wchar_t *) data_offset, toName, 530
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len_target++; /* trailing null */
+ name_len_target *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len_target = strnlen(toName, 530);
+ name_len_target++; /* trailing null */
+ strncpy(data_offset, toName, name_len_target);
+ }
+
+ pSMB->DataCount = name_len_target;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max on data count below from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ pSMB->ByteCount = 3 /* pad */ + pSMB->ParameterCount + pSMB->DataCount;
+ pSMB->DataCount = cpu_to_le16(pSMB->DataCount);
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(pSMB->ParameterOffset);
+ pSMB->DataOffset = cpu_to_le16(pSMB->DataOffset);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1,
+ ("Send error in SetPathInfo (create symlink) = %d",
+ rc));
+ }
+
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto createSymLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ char *data_offset;
+ int name_len;
+ int name_len_target;
+ int rc = 0;
+ int bytes_returned = 0;
+
+ cFYI(1, ("In Create Hard link Unix style"));
+createHardLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifs_strtoUCS((wchar_t *) pSMB->FileName, toName, 530
+ /* 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(toName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, toName, name_len);
+ }
+ pSMB->ParameterCount = 6 + name_len;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ pSMB->DataOffset = pSMB->ParameterOffset + pSMB->ParameterCount;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + pSMB->DataOffset;
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len_target =
+ cifs_strtoUCS((wchar_t *) data_offset, fromName, 530
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len_target++; /* trailing null */
+ name_len_target *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len_target = strnlen(fromName, 530);
+ name_len_target++; /* trailing null */
+ strncpy(data_offset, fromName, name_len_target);
+ }
+
+ pSMB->DataCount = name_len_target;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max on data count below from sess*/
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ pSMB->ByteCount = 3 /* pad */ + pSMB->ParameterCount + pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->DataCount = cpu_to_le16(pSMB->DataCount);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterOffset = cpu_to_le16(pSMB->ParameterOffset);
+ pSMB->DataOffset = cpu_to_le16(pSMB->DataOffset);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
+ }
+
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto createHardLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ NT_RENAME_REQ *pSMB = NULL;
+ RENAME_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+
+ cFYI(1, ("In CIFSCreateHardLink"));
+winCreateHardLinkRetry:
+
+ rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+ pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
+ pSMB->ClusterCount = 0;
+
+ pSMB->BufferFormat = 0x04;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, 530
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->OldFileName[name_len] = 0; /* pad */
+ pSMB->OldFileName[name_len + 1] = 0x04;
+ name_len2 =
+ cifs_strtoUCS((wchar_t *) & pSMB->
+ OldFileName[name_len + 2], toName, 530,
+ nls_codepage);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, fromName, name_len);
+ name_len2 = strnlen(toName, 530);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ pSMB->ByteCount = 1 /* string type byte */ + name_len + name_len2;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto winCreateHardLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *symlinkinfo, const int buflen,
+ const struct nls_table *nls_codepage)
+{
+/* SMB_QUERY_FILE_UNIX_LINK */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
+
+querySymLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
+ /* 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(searchName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ pSMB->TotalParameterCount =
+ 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req ,InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ pSMBr->DataCount = le16_to_cpu(pSMBr->DataCount);
+ if ((pSMBr->ByteCount < 2) || (pSMBr->DataOffset > 512))
+ /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ else {
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = UniStrnlen((wchar_t *) ((char *)
+ &pSMBr->hdr.Protocol +pSMBr->DataOffset),
+ min_t(const int, buflen,pSMBr->DataCount) / 2);
+ cifs_strfromUCS_le(symlinkinfo,
+ (wchar_t *) ((char *)&pSMBr->hdr.Protocol +
+ pSMBr->DataOffset),
+ name_len, nls_codepage);
+ } else {
+ strncpy(symlinkinfo,
+ (char *) &pSMBr->hdr.Protocol +
+ pSMBr->DataOffset,
+ min_t(const int, buflen, pSMBr->DataCount));
+ }
+ symlinkinfo[buflen] = 0;
+ /* just in case so calling code does not go off the end of buffer */
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto querySymLinkRetry;
+ return rc;
+}
+
+
+
+int
+CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *symlinkinfo, const int buflen,__u16 fid,
+ const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ struct smb_com_transaction_ioctl_req * pSMB;
+ struct smb_com_transaction_ioctl_rsp * pSMBr;
+
+ cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 0 ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le32(2);
+ /* BB find exact data count max from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le32(4000);
+ pSMB->MaxSetupCount = 4;
+ pSMB->Reserved = 0;
+ pSMB->ParameterOffset = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 4;
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
+ pSMB->IsFsctl = 1; /* FSCTL */
+ pSMB->IsRootFlag = 0;
+ pSMB->Fid = fid; /* file handle always le */
+ pSMB->ByteCount = 0;
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ pSMBr->DataCount = le16_to_cpu(pSMBr->DataCount);
+ if ((pSMBr->ByteCount < 2) || (pSMBr->DataOffset > 512))
+ /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ else {
+ if(pSMBr->DataCount && (pSMBr->DataCount < 2048)) {
+ /* could also validate reparse tag && better check name length */
+ struct reparse_data * reparse_buf = (struct reparse_data *)
+ ((char *)&pSMBr->hdr.Protocol + pSMBr->DataOffset);
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = UniStrnlen((wchar_t *)
+ (reparse_buf->LinkNamesBuf +
+ reparse_buf->TargetNameOffset),
+ min(buflen/2, reparse_buf->TargetNameLen / 2));
+ cifs_strfromUCS_le(symlinkinfo,
+ (wchar_t *) (reparse_buf->LinkNamesBuf +
+ reparse_buf->TargetNameOffset),
+ name_len, nls_codepage);
+ } else { /* ASCII names */
+ strncpy(symlinkinfo,reparse_buf->LinkNamesBuf +
+ reparse_buf->TargetNameOffset,
+ min_t(const int, buflen, reparse_buf->TargetNameLen));
+ }
+ } else {
+ rc = -EIO;
+ cFYI(1,("Invalid return data count on get reparse info ioctl"));
+ }
+ symlinkinfo[buflen] = 0; /* just in case so the caller
+ does not go off the end of the buffer */
+ cFYI(1,("readlink result - %s ",symlinkinfo));
+ }
+ }
+ if (pSMB)
+ 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
+CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ FILE_ALL_INFO * pFindData,
+ const struct nls_table *nls_codepage)
+{
+/* level 263 SMB_QUERY_FILE_ALL_INFO */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In QPathInfo path %s", searchName));
+QPathInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
+ /* 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(searchName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ pSMB->TotalParameterCount = 2 /* level */ + 4 /* reserved */ +
+ name_len /* includes null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req ,InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QPathInfo = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ /* BB also check enough total bytes returned */
+ /* BB we need to improve the validity checking
+ of these trans2 responses */
+ if ((pSMBr->ByteCount < 40) || (pSMBr->DataOffset > 512))
+ rc = -EIO; /* bad smb */
+ else if (pFindData){
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ pSMBr->DataOffset, sizeof (FILE_ALL_INFO));
+ } else
+ rc = -ENOMEM;
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto QPathInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ FILE_UNIX_BASIC_INFO * pFindData,
+ const struct nls_table *nls_codepage)
+{
+/* SMB_QUERY_FILE_UNIX_BASIC */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned = 0;
+ int name_len;
+
+ cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
+UnixQPathInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
+ /* 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(searchName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ pSMB->TotalParameterCount = 2 /* level */ + 4 /* reserved */ +
+ name_len /* includes null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req ,InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QPathInfo = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ /* BB also check if enough total bytes returned */
+ if ((pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO)) ||
+ (pSMBr->DataOffset > 512) ||
+ (pSMBr->DataOffset < sizeof(struct smb_hdr))) {
+ cFYI(1,("UnixQPathinfo invalid data offset %d bytes returned %d",
+ (int)pSMBr->DataOffset,bytes_returned));
+ rc = -EIO; /* bad smb */
+ } else {
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ pSMBr->DataOffset,
+ sizeof (FILE_UNIX_BASIC_INFO));
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto UnixQPathInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSFindSingle(const int xid, struct cifsTconInfo *tcon,
+ const char *searchName, FILE_ALL_INFO * findData,
+ const struct nls_table *nls_codepage)
+{
+/* level 257 SMB_ */
+ TRANSACTION2_FFIRST_REQ *pSMB = NULL;
+ TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In FindUnique"));
+findUniqueRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
+ /* 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(searchName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ pSMB->TotalParameterCount = 12 + name_len /* includes null */ ;
+ pSMB->TotalDataCount = 0; /* no EAs */
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(
+ offsetof(struct smb_com_transaction2_ffirst_req,InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1; /* one byte, no need to le convert */
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalDataCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+ pSMB->SearchCount = cpu_to_le16(16); /* BB increase */
+ pSMB->SearchFlags = cpu_to_le16(1);
+ pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO);
+ pSMB->SearchStorageType = 0; /* BB what should we set this to? BB */
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+
+ if (rc) {
+ cFYI(1, ("Send error in FindFileDirInfo = %d", rc));
+ } else { /* decode response */
+
+ /* BB fill in */
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto findUniqueRetry;
+
+ return rc;
+}
+
+int
+CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
+ const char *searchName, FILE_DIRECTORY_INFO * findData,
+ T2_FFIRST_RSP_PARMS * findParms,
+ const struct nls_table *nls_codepage, int *pUnicodeFlag,
+ int *pUnixFlag)
+{
+/* level 257 SMB_ */
+ TRANSACTION2_FFIRST_REQ *pSMB = NULL;
+ TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
+ char *response_data;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In FindFirst"));
+findFirstRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
+ /* 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(searchName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ pSMB->TotalParameterCount = 12 + name_len /* includes null */ ;
+ pSMB->TotalDataCount = 0; /* no EAs */
+ pSMB->MaxParameterCount = cpu_to_le16(10);
+ pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
+ MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
+ smb_com_transaction2_ffirst_req, SearchAttributes) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1; /* one byte no need to make endian neutral */
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+ pSMB->SearchCount = cpu_to_le16(CIFS_MAX_MSGSIZE / sizeof (FILE_DIRECTORY_INFO)); /* should this be shrunk even more ? */
+ pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
+
+ /* test for Unix extensions */
+ if (tcon->ses->capabilities & CAP_UNIX) {
+ pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_UNIX);
+ *pUnixFlag = TRUE;
+ } else {
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO);
+ *pUnixFlag = FALSE;
+ }
+ pSMB->SearchStorageType = 0; /* BB what should we set this to? It is not clear if it matters BB */
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+
+ if (rc) { /* BB add logic to retry regular search if Unix search rejected unexpectedly by server */
+ cFYI(1, ("Error in FindFirst = %d", rc));
+ } else { /* decode response */
+ /* BB add safety checks for these memcpys */
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ *pUnicodeFlag = TRUE;
+ else
+ *pUnicodeFlag = FALSE;
+ memcpy(findParms,
+ (char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->ParameterOffset),
+ sizeof (T2_FFIRST_RSP_PARMS));
+ /* search handle can stay LE and EAoffset not needed so not converted */
+ findParms->EndofSearch = le16_to_cpu(findParms->EndofSearch);
+ findParms->LastNameOffset =
+ le16_to_cpu(findParms->LastNameOffset);
+ findParms->SearchCount = le16_to_cpu(findParms->SearchCount);
+ response_data =
+ (char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->DataOffset);
+ memcpy(findData, response_data, le16_to_cpu(pSMBr->DataCount));
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto findFirstRetry;
+
+ return rc;
+}
+
+int
+CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
+ FILE_DIRECTORY_INFO * findData, T2_FNEXT_RSP_PARMS * findParms,
+ const __u16 searchHandle, char * resume_file_name, int name_len,
+ __u32 resume_key, int *pUnicodeFlag, int *pUnixFlag)
+{
+/* level 257 SMB_ */
+ TRANSACTION2_FNEXT_REQ *pSMB = NULL;
+ TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
+ char *response_data;
+ int rc = 0;
+ int bytes_returned;
+
+ cFYI(1, ("In FindNext"));
+
+ if(resume_file_name == NULL) {
+ return -EIO;
+ }
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 14; /* includes 2 bytes of null string, converted to LE below */
+ pSMB->TotalDataCount = 0; /* no EAs */
+ pSMB->MaxParameterCount = cpu_to_le16(8);
+ pSMB->MaxDataCount =
+ cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
+ pSMB->SearchHandle = searchHandle; /* always kept as le */
+ findParms->SearchCount = 0; /* set to zero in case of error */
+ pSMB->SearchCount =
+ cpu_to_le16(CIFS_MAX_MSGSIZE / sizeof (FILE_DIRECTORY_INFO));
+ /* test for Unix extensions */
+ if (tcon->ses->capabilities & CAP_UNIX) {
+ pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_UNIX);
+ *pUnixFlag = TRUE;
+ } else {
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO);
+ *pUnixFlag = FALSE;
+ }
+ pSMB->ResumeKey = resume_key;
+ pSMB->SearchFlags =
+ cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
+ /* BB add check to make sure we do not cross end of smb */
+ if(name_len < CIFS_MAX_MSGSIZE) {
+ memcpy(pSMB->ResumeFileName, resume_file_name, name_len);
+ pSMB->ByteCount += name_len;
+ }
+ pSMB->TotalParameterCount += name_len;
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ /* BB improve error handling here */
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+
+ if (rc) {
+ if (rc == -EBADF)
+ rc = 0; /* search probably was closed at end of search above */
+ else
+ cFYI(1, ("FindNext returned = %d", rc));
+ } else { /* decode response */
+ /* BB add safety checks for these memcpys */
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ *pUnicodeFlag = TRUE;
+ else
+ *pUnicodeFlag = FALSE;
+ memcpy(findParms,
+ (char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->ParameterOffset),
+ sizeof (T2_FNEXT_RSP_PARMS));
+ findParms->EndofSearch = le16_to_cpu(findParms->EndofSearch);
+ findParms->LastNameOffset =
+ le16_to_cpu(findParms->LastNameOffset);
+ findParms->SearchCount = le16_to_cpu(findParms->SearchCount);
+ response_data =
+ (char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->DataOffset);
+ memcpy(findData, response_data, le16_to_cpu(pSMBr->DataCount));
+ }
+ if (pSMB)
+ 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
+CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle)
+{
+ int rc = 0;
+ FINDCLOSE_REQ *pSMB = NULL;
+ CLOSE_RSP *pSMBr = NULL;
+ int bytes_returned;
+
+ cFYI(1, ("In CIFSSMBFindClose"));
+ rc = smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ /* no sense returning error if session restarted
+ file handle has been closed */
+ if(rc == -EAGAIN)
+ return 0;
+ if (rc)
+ return rc;
+
+ pSMB->FileID = searchHandle;
+ pSMB->ByteCount = 0;
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cERROR(1, ("Send error in FindClose = %d", rc));
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ /* Since session is dead, search handle closed on server already */
+ if (rc == -EAGAIN)
+ rc = 0;
+
+ return rc;
+}
+
+int
+CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
+ const unsigned char *searchName,
+ unsigned char **targetUNCs,
+ unsigned int *number_of_UNC_in_array,
+ const struct nls_table *nls_codepage)
+{
+/* TRANS2_GET_DFS_REFERRAL */
+ TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
+ TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
+ struct dfs_referral_level_3 * referrals = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ unsigned int i;
+ char * temp;
+ *number_of_UNC_in_array = 0;
+ *targetUNCs = NULL;
+
+ cFYI(1, ("In GetDFSRefer the path %s", searchName));
+ if (ses == NULL)
+ return -ENODEV;
+getDFSRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Tid = ses->ipc_tid;
+ pSMB->hdr.Uid = ses->Suid;
+ if (ses->capabilities & CAP_STATUS32) {
+ pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ pSMB->hdr.Flags2 |= SMBFLG2_DFS;
+ }
+
+ if (ses->capabilities & CAP_UNICODE) {
+ pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->RequestFileName,
+ searchName, 530
+ /* 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(searchName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->RequestFileName, searchName, name_len);
+ }
+
+ pSMB->ParameterCount = 2 /* level */ + name_len /*includes null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->MaxParameterCount = 0;
+ pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
+ pSMB->ByteCount = pSMB->ParameterCount + 3 /* pad */ ;
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->MaxReferralLevel = cpu_to_le16(3);
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in GetDFSRefer = %d", rc));
+ } else { /* decode response */
+/* BB Add logic to parse referrals here */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ pSMBr->DataCount = le16_to_cpu(pSMBr->DataCount);
+ cFYI(1,
+ ("Decoding GetDFSRefer response. BCC: %d Offset %d",
+ pSMBr->ByteCount, pSMBr->DataOffset));
+ if ((pSMBr->ByteCount < 17) || (pSMBr->DataOffset > 512)) /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ else {
+ referrals =
+ (struct dfs_referral_level_3 *)
+ (8 /* sizeof start of data block */ +
+ pSMBr->DataOffset +
+ (char *) &pSMBr->hdr.Protocol);
+ cFYI(1,("num_referrals: %d dfs flags: 0x%x ... \nfor referral one refer size: 0x%x srv type: 0x%x refer flags: 0x%x ttl: 0x%x",pSMBr->NumberOfReferrals,pSMBr->DFSFlags, referrals->ReferralSize,referrals->ServerType,referrals->ReferralFlags,referrals->TimeToLive));
+ /* BB This field is actually two bytes in from start of
+ data block so we could do safety check that DataBlock
+ begins at address of pSMBr->NumberOfReferrals */
+ *number_of_UNC_in_array = le16_to_cpu(pSMBr->NumberOfReferrals);
+
+ /* BB Fix below so can return more than one referral */
+ if(*number_of_UNC_in_array > 1)
+ *number_of_UNC_in_array = 1;
+
+ /* get the length of the strings describing refs */
+ name_len = 0;
+ for(i=0;i<*number_of_UNC_in_array;i++) {
+ /* make sure that DfsPathOffset not past end */
+ referrals->DfsPathOffset = le16_to_cpu(referrals->DfsPathOffset);
+ if(referrals->DfsPathOffset > pSMBr->DataCount) {
+ /* if invalid referral, stop here and do
+ not try to copy any more */
+ *number_of_UNC_in_array = i;
+ break;
+ }
+ temp = ((char *)referrals) + referrals->DfsPathOffset;
+
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len += UniStrnlen((wchar_t *)temp,pSMBr->DataCount);
+ } else {
+ name_len += strnlen(temp,pSMBr->DataCount);
+ }
+ referrals++;
+ /* BB add check that referral pointer does not fall off end PDU */
+
+ }
+ /* BB add check for name_len bigger than bcc */
+ *targetUNCs =
+ kmalloc(name_len+1+ (*number_of_UNC_in_array),GFP_KERNEL);
+ /* copy the ref strings */
+ referrals =
+ (struct dfs_referral_level_3 *)
+ (8 /* sizeof data hdr */ +
+ pSMBr->DataOffset +
+ (char *) &pSMBr->hdr.Protocol);
+
+ for(i=0;i<*number_of_UNC_in_array;i++) {
+ temp = ((char *)referrals) + referrals->DfsPathOffset;
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
+ cifs_strfromUCS_le(*targetUNCs,
+ (wchar_t *) temp, name_len, nls_codepage);
+ } else {
+ strncpy(*targetUNCs,temp,name_len);
+ }
+ /* BB update target_uncs pointers */
+ referrals++;
+ }
+ temp = *targetUNCs;
+ temp[name_len] = 0;
+ }
+
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto getDFSRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon,
+ struct statfs *FSData, const struct nls_table *nls_codepage)
+{
+/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+
+ cFYI(1, ("In QFSInfo"));
+QFSInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cERROR(1, ("Send error in QFSInfo = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ cFYI(1,
+ ("Decoding qfsinfo response. BCC: %d Offset %d",
+ pSMBr->ByteCount, pSMBr->DataOffset));
+ if ((pSMBr->ByteCount < 24) || (pSMBr->DataOffset > 512)) /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ else {
+ response_data =
+ (FILE_SYSTEM_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ pSMBr->DataOffset);
+ FSData->f_bsize =
+ le32_to_cpu(response_data->BytesPerSector) *
+ le32_to_cpu(response_data->
+ SectorsPerAllocationUnit);
+ FSData->f_blocks =
+ le64_to_cpu(response_data->TotalAllocationUnits);
+ FSData->f_bfree = FSData->f_bavail =
+ le64_to_cpu(response_data->FreeAllocationUnits);
+ cFYI(1,
+ ("Blocks: %lld Free: %lld Block size %ld",
+ (unsigned long long)FSData->f_blocks,
+ (unsigned long long)FSData->f_bfree,
+ FSData->f_bsize));
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSAttributeInfo(int xid, struct cifsTconInfo *tcon,
+ const struct nls_table *nls_codepage)
+{
+/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+
+ cFYI(1, ("In QFSAttributeInfo"));
+QFSAttributeRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ if ((pSMBr->ByteCount < 13) || (pSMBr->DataOffset > 512)) { /* BB also check enough bytes returned */
+ rc = -EIO; /* bad smb */
+ } else {
+ response_data =
+ (FILE_SYSTEM_ATTRIBUTE_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ pSMBr->DataOffset);
+ response_data->Attributes = le32_to_cpu(response_data->Attributes);
+ response_data->MaxPathNameComponentLength =
+ le32_to_cpu(response_data->MaxPathNameComponentLength);
+ response_data->FileSystemNameLen =
+ le32_to_cpu(response_data->FileSystemNameLen);
+ memcpy(&tcon->fsAttrInfo, response_data,
+ sizeof (FILE_SYSTEM_ATTRIBUTE_INFO));
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSAttributeRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSDeviceInfo(int xid, struct cifsTconInfo *tcon,
+ const struct nls_table *nls_codepage)
+{
+/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_DEVICE_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+
+ cFYI(1, ("In QFSDeviceInfo"));
+QFSDeviceRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QFSDeviceInfo = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ if ((pSMBr->ByteCount < sizeof (FILE_SYSTEM_DEVICE_INFO))
+ || (pSMBr->DataOffset > 512))
+ rc = -EIO; /* bad smb */
+ else {
+ response_data =
+ (FILE_SYSTEM_DEVICE_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ pSMBr->DataOffset);
+ response_data->DeviceType =
+ le32_to_cpu(response_data->DeviceType);
+ response_data->DeviceCharacteristics =
+ le32_to_cpu(response_data->DeviceCharacteristics);
+ memcpy(&tcon->fsDevInfo, response_data,
+ sizeof (FILE_SYSTEM_DEVICE_INFO));
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSDeviceRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSUnixInfo(int xid, struct cifsTconInfo *tcon,
+ const struct nls_table *nls_codepage)
+{
+/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_UNIX_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+
+ cFYI(1, ("In QFSUnixInfo"));
+QFSUnixRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->ParameterCount = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ByteCount = pSMB->ParameterCount + 1 /* pad */ ;
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
+ smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = cpu_to_le16(pSMBr->DataOffset);
+ if ((pSMBr->ByteCount < 13) || (pSMBr->DataOffset > 512)) {
+ rc = -EIO; /* bad smb */
+ } else {
+ response_data =
+ (FILE_SYSTEM_UNIX_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ pSMBr->DataOffset);
+ response_data->MajorVersionNumber =
+ le16_to_cpu(response_data->MajorVersionNumber);
+ response_data->MinorVersionNumber =
+ le16_to_cpu(response_data->MinorVersionNumber);
+ response_data->Capability =
+ le64_to_cpu(response_data->Capability);
+ memcpy(&tcon->fsUnixInfo, response_data,
+ sizeof (FILE_SYSTEM_UNIX_INFO));
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSUnixRetry;
+
+
+ return rc;
+}
+
+/* We can not use write of zero bytes trick to
+ set file size due to need for large file support. Also note that
+ this SetPathInfo is preferred to SetFileInfo based method in next
+ routine which is only needed to work around a sharing violation bug
+ in Samba which this routine can run into */
+
+int
+CIFSSMBSetEOF(int xid, struct cifsTconInfo *tcon, char *fileName,
+ __u64 size, int SetAllocation, const struct nls_table *nls_codepage)
+{
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ struct file_end_of_file_info *parm_data;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+
+ cFYI(1, ("In SetEOF"));
+SetEOFRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, 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;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+ pSMB->ParameterCount = 6 + name_len;
+ pSMB->DataCount = sizeof (struct file_end_of_file_info);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ pSMB->DataOffset = pSMB->ParameterOffset + pSMB->ParameterCount;
+ if(SetAllocation) {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
+ } else /* Set File Size */ {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
+ }
+
+ parm_data =
+ (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
+ pSMB->DataOffset);
+ pSMB->ParameterOffset = cpu_to_le16(pSMB->ParameterOffset);
+ pSMB->DataOffset = cpu_to_le16(pSMB->DataOffset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ pSMB->ByteCount = 3 /* pad */ + pSMB->ParameterCount + pSMB->DataCount;
+ pSMB->DataCount = cpu_to_le16(pSMB->DataCount);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ parm_data->FileSize = cpu_to_le64(size);
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("SetPathInfo (file size) returned %d", rc));
+ }
+
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetEOFRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
+ __u16 fid, __u32 pid_of_opener, int SetAllocation)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
+ char *data_offset;
+ struct file_end_of_file_info *parm_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u32 tmp;
+
+ cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
+ (long long)size));
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ tmp = cpu_to_le32(pid_of_opener); /* override pid of current process
+ so network fid will be valid */
+ pSMB->hdr.Pid = tmp & 0xFFFF;
+ tmp >>= 16;
+ pSMB->hdr.PidHigh = tmp & 0xFFFF;
+
+ pSMB->ParameterCount = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = offsetof(struct smb_com_transaction2_sfi_req,
+ Fid) - 4;
+ pSMB->DataOffset = pSMB->ParameterOffset + pSMB->ParameterCount;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + pSMB->DataOffset;
+
+ pSMB->DataCount = sizeof(struct file_end_of_file_info);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB PDU from sess */
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ pSMB->ByteCount = 3 /* pad */ + pSMB->ParameterCount + pSMB->DataCount;
+ pSMB->DataCount = cpu_to_le16(pSMB->DataCount);
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(pSMB->ParameterOffset);
+ parm_data =
+ (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
+ pSMB->DataOffset);
+ pSMB->DataOffset = cpu_to_le16(pSMB->DataOffset); /* now safe to change to le */
+ parm_data->FileSize = cpu_to_le64(size);
+ pSMB->Fid = fid;
+ if(SetAllocation) {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
+ } else /* Set File Size */ {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
+ }
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1,
+ ("Send error in SetFileInfo (SetFileSize) = %d",
+ rc));
+ }
+
+ if (pSMB)
+ 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
+CIFSSMBSetTimes(int xid, struct cifsTconInfo *tcon, char *fileName,
+ FILE_BASIC_INFO * data, const struct nls_table *nls_codepage)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ char *data_offset;
+
+ cFYI(1, ("In SetTimes"));
+
+SetTimesRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, 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;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ pSMB->ParameterCount = 6 + name_len;
+ pSMB->DataCount = sizeof (FILE_BASIC_INFO);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ pSMB->DataOffset = pSMB->ParameterOffset + pSMB->ParameterCount;
+ data_offset = (char *) (&pSMB->hdr.Protocol) + pSMB->DataOffset;
+ pSMB->ParameterOffset = cpu_to_le16(pSMB->ParameterOffset);
+ pSMB->DataOffset = cpu_to_le16(pSMB->DataOffset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ pSMB->ByteCount = 3 /* pad */ + pSMB->ParameterCount + pSMB->DataCount;
+
+ pSMB->DataCount = cpu_to_le16(pSMB->DataCount);
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
+ else
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ memcpy(data_offset, data, sizeof (FILE_BASIC_INFO));
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("SetPathInfo (times) returned %d", rc));
+ }
+
+ if (pSMB)
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetTimesRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon,
+ char *fileName, __u64 mode, __u64 uid, __u64 gid,
+ dev_t device, const struct nls_table *nls_codepage)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ FILE_UNIX_BASIC_INFO *data_offset;
+
+ cFYI(1, ("In SetUID/GID/Mode"));
+setPermsRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, 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;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ pSMB->ParameterCount = 6 + name_len;
+ pSMB->DataCount = sizeof (FILE_UNIX_BASIC_INFO);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ pSMB->DataOffset = pSMB->ParameterOffset + pSMB->ParameterCount;
+ data_offset =
+ (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
+ pSMB->DataOffset);
+ pSMB->DataOffset = cpu_to_le16(pSMB->DataOffset);
+ pSMB->ParameterOffset = cpu_to_le16(pSMB->ParameterOffset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ pSMB->ByteCount = 3 /* pad */ + pSMB->ParameterCount + pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(pSMB->ParameterCount);
+ pSMB->DataCount = cpu_to_le16(pSMB->DataCount);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ data_offset->Uid = cpu_to_le64(uid);
+ data_offset->Gid = cpu_to_le64(gid);
+ /* better to leave device as zero when it is */
+ data_offset->DevMajor = cpu_to_le64(MAJOR(device));
+ data_offset->DevMinor = cpu_to_le64(MINOR(device));
+ data_offset->Permissions = cpu_to_le64(mode);
+
+ if(S_ISREG(mode))
+ data_offset->Type = cpu_to_le32(UNIX_FILE);
+ else if(S_ISDIR(mode))
+ data_offset->Type = cpu_to_le32(UNIX_DIR);
+ else if(S_ISLNK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
+ else if(S_ISCHR(mode))
+ data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
+ else if(S_ISBLK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
+ else if(S_ISFIFO(mode))
+ data_offset->Type = cpu_to_le32(UNIX_FIFO);
+ else if(S_ISSOCK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_SOCKET);
+
+
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("SetPathInfo (perms) returned %d", rc));
+ }
+
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto setPermsRetry;
+ return rc;
+}
+
+int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
+ const int notify_subdirs, const __u16 netfid,
+ __u32 filter, const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ struct smb_com_transaction_change_notify_req * pSMB = NULL;
+ struct smb_com_transaction_change_notify_rsp * pSMBr = NULL;
+ int bytes_returned;
+
+ cFYI(1, ("In CIFSSMBNotify for file handle %d",(int)netfid));
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 0 ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le32(2);
+ /* BB find exact data count max from sess structure BB */
+ pSMB->MaxDataCount = 0; /* same in little endian or be */
+ pSMB->MaxSetupCount = 4;
+ pSMB->Reserved = 0;
+ pSMB->ParameterOffset = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 4; /* single byte does not need le conversion */
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ if(notify_subdirs)
+ pSMB->WatchTree = 1; /* one byte - no le conversion needed */
+ pSMB->Reserved2 = 0;
+ pSMB->CompletionFilter = cpu_to_le32(filter);
+ pSMB->Fid = netfid; /* file handle always le */
+ pSMB->ByteCount = 0;
+
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Error in Notify = %d", rc));
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+/* if (rc == -EAGAIN)
+ goto NotifyRetry; */
+ return rc;
+}
+#ifdef CONFIG_CIFS_XATTR
+int
+CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char * EAData, size_t size,
+ const struct nls_table *nls_codepage)
+{
+ /* BB assumes one setup word */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In Query All EAs path %s", searchName));
+QAllEAsRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
+ /* 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(searchName, 530);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ pSMB->TotalParameterCount = 2 /* level */ + 4 /* reserved */ +
+ name_len /* includes null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req ,InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QueryAllEAs = %d", rc));
+ } else { /* decode response */
+ pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
+ /* BB also check enough total bytes returned */
+ /* BB we need to improve the validity checking
+ of these trans2 responses */
+ if ((pSMBr->ByteCount < 4) || (pSMBr->DataOffset > 512))
+ rc = -EIO; /* bad smb */
+ /* else if (pFindData){
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ pSMBr->DataOffset, kl);
+ }*/ else {
+ /* check that length of list is not more than bcc */
+ /* check that each entry does not go beyond length
+ of list */
+ /* check that each element of each entry does not
+ go beyond end of list */
+ struct fealist * ea_response_data;
+ rc = 0;
+ /* validate_trans2_offsets() */
+ /* BB to check if(start of smb + pSMBr->DataOffset > &bcc+ bcc)*/
+ ea_response_data = (struct fealist *)
+ (((char *) &pSMBr->hdr.Protocol) +
+ pSMBr->DataOffset);
+ cFYI(1,("ea length %d",ea_response_data->list_len));
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto QAllEAsRetry;
+
+ return rc;
+}
+#endif