| /* |
| * fs/cifs/cifssmb.c |
| * |
| * Copyright (C) International Business Machines Corp., 2002,2005 |
| * Author(s): Steve French (sfrench@us.ibm.com) |
| * |
| * Contains the routines for constructing the SMB PDUs themselves |
| * |
| * This library is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU Lesser General Public License as published |
| * by the Free Software Foundation; either version 2.1 of the License, or |
| * (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| * the GNU Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ |
| /* These are mostly routines that operate on a pathname, or on a tree id */ |
| /* (mounted volume), but there are eight handle based routines which must be */ |
| /* treated slightly different for reconnection purposes since we never want */ |
| /* to reuse a stale file handle and the caller knows the file handle */ |
| |
| #include <linux/fs.h> |
| #include <linux/kernel.h> |
| #include <linux/vfs.h> |
| #include <linux/posix_acl_xattr.h> |
| #include <asm/uaccess.h> |
| #include "cifspdu.h" |
| #include "cifsglob.h" |
| #include "cifsproto.h" |
| #include "cifs_unicode.h" |
| #include "cifs_debug.h" |
| |
| #ifdef CONFIG_CIFS_POSIX |
| static struct { |
| int index; |
| char *name; |
| } protocols[] = { |
| {CIFS_PROT, "\2NT LM 0.12"}, |
| {CIFS_PROT, "\2POSIX 2"}, |
| {BAD_PROT, "\2"} |
| }; |
| #else |
| static struct { |
| int index; |
| char *name; |
| } protocols[] = { |
| {CIFS_PROT, "\2NT LM 0.12"}, |
| {BAD_PROT, "\2"} |
| }; |
| #endif |
| |
| |
| /* Mark as invalid, all open files on tree connections since they |
| were closed when session to server was lost */ |
| static void mark_open_files_invalid(struct cifsTconInfo * pTcon) |
| { |
| struct cifsFileInfo *open_file = NULL; |
| struct list_head * tmp; |
| struct list_head * tmp1; |
| |
| /* list all files open on tree connection and mark them invalid */ |
| write_lock(&GlobalSMBSeslock); |
| list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { |
| open_file = list_entry(tmp,struct cifsFileInfo, tlist); |
| if(open_file) { |
| open_file->invalidHandle = TRUE; |
| } |
| } |
| write_unlock(&GlobalSMBSeslock); |
| /* BB Add call to invalidate_inodes(sb) for all superblocks mounted |
| to this tcon */ |
| } |
| |
| /* If the return code is zero, this function must fill in request_buf pointer */ |
| static int |
| small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, |
| void **request_buf /* returned */) |
| { |
| int rc = 0; |
| |
| /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so |
| check for tcp and smb session status done differently |
| for those three - in the calling routine */ |
| if(tcon) { |
| if((tcon->ses) && (tcon->ses->status != CifsExiting) && |
| (tcon->ses->server)){ |
| struct nls_table *nls_codepage; |
| /* Give Demultiplex thread up to 10 seconds to |
| reconnect, should be greater than cifs socket |
| timeout which is 7 seconds */ |
| while(tcon->ses->server->tcpStatus == CifsNeedReconnect) { |
| wait_event_interruptible_timeout(tcon->ses->server->response_q, |
| (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ); |
| if(tcon->ses->server->tcpStatus == CifsNeedReconnect) { |
| /* on "soft" mounts we wait once */ |
| if((tcon->retry == FALSE) || |
| (tcon->ses->status == CifsExiting)) { |
| cFYI(1,("gave up waiting on reconnect in smb_init")); |
| return -EHOSTDOWN; |
| } /* else "hard" mount - keep retrying |
| until process is killed or server |
| comes back on-line */ |
| } 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); |
| /* BB FIXME add code to check if wsize needs |
| update due to negotiated smb buffer size |
| shrinking */ |
| if(rc == 0) |
| atomic_inc(&tconInfoReconnectCount); |
| |
| cFYI(1, ("reconnect tcon rc = %d", rc)); |
| /* Removed call to reopen open files here - |
| it is safer (and faster) to reopen files |
| one at a time as needed in read and write */ |
| |
| /* Check if handle based operation so we |
| know whether we can continue or not without |
| returning to caller to reset file handle */ |
| switch(smb_command) { |
| case SMB_COM_READ_ANDX: |
| case SMB_COM_WRITE_ANDX: |
| case SMB_COM_CLOSE: |
| case SMB_COM_FIND_CLOSE2: |
| case SMB_COM_LOCKING_ANDX: { |
| unload_nls(nls_codepage); |
| return -EAGAIN; |
| } |
| } |
| } else { |
| up(&tcon->ses->sesSem); |
| } |
| unload_nls(nls_codepage); |
| |
| } else { |
| return -EIO; |
| } |
| } |
| if(rc) |
| return rc; |
| |
| *request_buf = cifs_small_buf_get(); |
| if (*request_buf == NULL) { |
| /* BB should we add a retry in here if not a writepage? */ |
| return -ENOMEM; |
| } |
| |
| header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,wct); |
| |
| if(tcon != NULL) |
| cifs_stats_inc(&tcon->num_smbs_sent); |
| |
| return rc; |
| } |
| |
| /* If the return code is zero, this function must fill in request_buf pointer */ |
| static int |
| smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, |
| void **request_buf /* returned */ , |
| void **response_buf /* returned */ ) |
| { |
| int rc = 0; |
| |
| /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so |
| check for tcp and smb session status done differently |
| for those three - in the calling routine */ |
| if(tcon) { |
| if((tcon->ses) && (tcon->ses->status != CifsExiting) && |
| (tcon->ses->server)){ |
| struct nls_table *nls_codepage; |
| /* Give Demultiplex thread up to 10 seconds to |
| reconnect, should be greater than cifs socket |
| timeout which is 7 seconds */ |
| while(tcon->ses->server->tcpStatus == CifsNeedReconnect) { |
| wait_event_interruptible_timeout(tcon->ses->server->response_q, |
| (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ); |
| if(tcon->ses->server->tcpStatus == |
| CifsNeedReconnect) { |
| /* on "soft" mounts we wait once */ |
| if((tcon->retry == FALSE) || |
| (tcon->ses->status == CifsExiting)) { |
| cFYI(1,("gave up waiting on reconnect in smb_init")); |
| return -EHOSTDOWN; |
| } /* else "hard" mount - keep retrying |
| until process is killed or server |
| comes on-line */ |
| } 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); |
| /* BB FIXME add code to check if wsize needs |
| update due to negotiated smb buffer size |
| shrinking */ |
| if(rc == 0) |
| atomic_inc(&tconInfoReconnectCount); |
| |
| cFYI(1, ("reconnect tcon rc = %d", rc)); |
| /* Removed call to reopen open files here - |
| it is safer (and faster) to reopen files |
| one at a time as needed in read and write */ |
| |
| /* Check if handle based operation so we |
| know whether we can continue or not without |
| returning to caller to reset file handle */ |
| switch(smb_command) { |
| case SMB_COM_READ_ANDX: |
| case SMB_COM_WRITE_ANDX: |
| case SMB_COM_CLOSE: |
| case SMB_COM_FIND_CLOSE2: |
| case SMB_COM_LOCKING_ANDX: { |
| unload_nls(nls_codepage); |
| return -EAGAIN; |
| } |
| } |
| } else { |
| up(&tcon->ses->sesSem); |
| } |
| unload_nls(nls_codepage); |
| |
| } else { |
| return -EIO; |
| } |
| } |
| if(rc) |
| return rc; |
| |
| *request_buf = cifs_buf_get(); |
| if (*request_buf == NULL) { |
| /* BB should we add a retry in here if not a writepage? */ |
| return -ENOMEM; |
| } |
| /* Although the original thought was we needed the response buf for */ |
| /* potential retries of smb operations it turns out we can determine */ |
| /* from the mid flags when the request buffer can be resent without */ |
| /* having to use a second distinct buffer for the response */ |
| *response_buf = *request_buf; |
| |
| header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, |
| wct /*wct */ ); |
| |
| if(tcon != NULL) |
| cifs_stats_inc(&tcon->num_smbs_sent); |
| |
| return rc; |
| } |
| |
| static int validate_t2(struct smb_t2_rsp * pSMB) |
| { |
| int rc = -EINVAL; |
| int total_size; |
| char * pBCC; |
| |
| /* check for plausible wct, bcc and t2 data and parm sizes */ |
| /* check for parm and data offset going beyond end of smb */ |
| if(pSMB->hdr.WordCount >= 10) { |
| if((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) && |
| (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) { |
| /* check that bcc is at least as big as parms + data */ |
| /* check that bcc is less than negotiated smb buffer */ |
| total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount); |
| if(total_size < 512) { |
| total_size+=le16_to_cpu(pSMB->t2_rsp.DataCount); |
| /* BCC le converted in SendReceive */ |
| pBCC = (pSMB->hdr.WordCount * 2) + |
| sizeof(struct smb_hdr) + |
| (char *)pSMB; |
| if((total_size <= (*(u16 *)pBCC)) && |
| (total_size < |
| CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) { |
| return 0; |
| } |
| |
| } |
| } |
| } |
| cifs_dump_mem("Invalid transact2 SMB: ",(char *)pSMB, |
| sizeof(struct smb_t2_rsp) + 16); |
| return rc; |
| } |
| int |
| CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) |
| { |
| NEGOTIATE_REQ *pSMB; |
| NEGOTIATE_RSP *pSMBr; |
| int rc = 0; |
| int bytes_returned; |
| struct TCP_Server_Info * server; |
| u16 count; |
| |
| if(ses->server) |
| server = ses->server; |
| else { |
| rc = -EIO; |
| return rc; |
| } |
| rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , |
| (void **) &pSMB, (void **) &pSMBr); |
| if (rc) |
| return rc; |
| pSMB->hdr.Mid = GetNextMid(server); |
| pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; |
| if (extended_security) |
| pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; |
| |
| count = strlen(protocols[0].name) + 1; |
| strncpy(pSMB->DialectsArray, protocols[0].name, 30); |
| /* null guaranteed to be at end of source and target buffers anyway */ |
| |
| pSMB->hdr.smb_buf_length += count; |
| pSMB->ByteCount = cpu_to_le16(count); |
| |
| rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| if (rc == 0) { |
| server->secMode = pSMBr->SecurityMode; |
| server->secType = NTLM; /* BB override default for |
| NTLMv2 or kerberos v5 */ |
| /* one byte - no need to convert this or EncryptionKeyLen |
| from little endian */ |
| server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount); |
| /* probably no need to store and check maxvcs */ |
| server->maxBuf = |
| min(le32_to_cpu(pSMBr->MaxBufferSize), |
| (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); |
| server->maxRw = le32_to_cpu(pSMBr->MaxRawSize); |
| cFYI(0, ("Max buf = %d ", ses->server->maxBuf)); |
| GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); |
| server->capabilities = le32_to_cpu(pSMBr->Capabilities); |
| server->timeZone = le16_to_cpu(pSMBr->ServerTimeZone); |
| /* BB with UTC do we ever need to be using srvr timezone? */ |
| if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { |
| memcpy(server->cryptKey, pSMBr->u.EncryptionKey, |
| CIFS_CRYPTO_KEY_SIZE); |
| } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) |
| && (pSMBr->EncryptionKeyLength == 0)) { |
| /* decode security blob */ |
| } else |
| rc = -EIO; |
| |
| /* BB might be helpful to save off the domain of server here */ |
| |
| if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && |
| (server->capabilities & CAP_EXTENDED_SECURITY)) { |
| count = pSMBr->ByteCount; |
| if (count < 16) |
| rc = -EIO; |
| else if (count == 16) { |
| server->secType = RawNTLMSSP; |
| if (server->socketUseCount.counter > 1) { |
| if (memcmp |
| (server->server_GUID, |
| pSMBr->u.extended_response. |
| GUID, 16) != 0) { |
| cFYI(1, |
| ("UID of server does not match previous connection to same ip address")); |
| memcpy(server-> |
| server_GUID, |
| pSMBr->u. |
| extended_response. |
| GUID, 16); |
| } |
| } else |
| memcpy(server->server_GUID, |
| pSMBr->u.extended_response. |
| GUID, 16); |
| } else { |
| rc = decode_negTokenInit(pSMBr->u. |
| extended_response. |
| SecurityBlob, |
| count - 16, |
| &server->secType); |
| if(rc == 1) { |
| /* BB Need to fill struct for sessetup here */ |
| rc = -EOPNOTSUPP; |
| } else { |
| rc = -EINVAL; |
| } |
| } |
| } else |
| server->capabilities &= ~CAP_EXTENDED_SECURITY; |
| if(sign_CIFS_PDUs == FALSE) { |
| if(server->secMode & SECMODE_SIGN_REQUIRED) |
| cERROR(1, |
| ("Server requires /proc/fs/cifs/PacketSigningEnabled")); |
| server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); |
| } else if(sign_CIFS_PDUs == 1) { |
| if((server->secMode & SECMODE_SIGN_REQUIRED) == 0) |
| server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); |
| } |
| |
| } |
| |
| cifs_buf_release(pSMB); |
| return rc; |
| } |
| |
| int |
| CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) |
| { |
| struct smb_hdr *smb_buffer; |
| struct smb_hdr *smb_buffer_response; /* BB removeme BB */ |
| int rc = 0; |
| int length; |
| |
| cFYI(1, ("In tree disconnect")); |
| /* |
| * If last user of the connection and |
| * connection alive - disconnect it |
| * If this is the last connection on the server session disconnect it |
| * (and inside session disconnect we should check if tcp socket needs |
| * to be freed and kernel thread woken up). |
| */ |
| if (tcon) |
| down(&tcon->tconSem); |
| else |
| return -EIO; |
| |
| atomic_dec(&tcon->useCount); |
| if (atomic_read(&tcon->useCount) > 0) { |
| up(&tcon->tconSem); |
| return -EBUSY; |
| } |
| |
| /* No need to return error on this operation if tid invalidated and |
| closed on server already e.g. due to tcp session crashing */ |
| if(tcon->tidStatus == CifsNeedReconnect) { |
| up(&tcon->tconSem); |
| return 0; |
| } |
| |
| if((tcon->ses == NULL) || (tcon->ses->server == NULL)) { |
| up(&tcon->tconSem); |
| return -EIO; |
| } |
| rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, |
| (void **)&smb_buffer); |
| if (rc) { |
| up(&tcon->tconSem); |
| return rc; |
| } else { |
| smb_buffer_response = smb_buffer; /* BB removeme BB */ |
| } |
| rc = SendReceive(xid, tcon->ses, smb_buffer, smb_buffer_response, |
| &length, 0); |
| if (rc) |
| cFYI(1, ("Tree disconnect failed %d", rc)); |
| |
| if (smb_buffer) |
| cifs_small_buf_release(smb_buffer); |
| up(&tcon->tconSem); |
| |
| /* No need to return error on this operation if tid invalidated and |
| closed on server already e.g. due to tcp session crashing */ |
| if (rc == -EAGAIN) |
| rc = 0; |
| |
| return rc; |
| } |
| |
| int |
| CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) |
| { |
| struct smb_hdr *smb_buffer_response; |
| LOGOFF_ANDX_REQ *pSMB; |
| int rc = 0; |
| int length; |
| |
| cFYI(1, ("In SMBLogoff for session disconnect")); |
| if (ses) |
| down(&ses->sesSem); |
| else |
| return -EIO; |
| |
| atomic_dec(&ses->inUse); |
| if (atomic_read(&ses->inUse) > 0) { |
| up(&ses->sesSem); |
| return -EBUSY; |
| } |
| rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); |
| if (rc) { |
| up(&ses->sesSem); |
| return rc; |
| } |
| |
| smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */ |
| |
| if(ses->server) { |
| pSMB->hdr.Mid = GetNextMid(ses->server); |
| |
| if(ses->server->secMode & |
| (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) |
| pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; |
| } |
| |
| pSMB->hdr.Uid = ses->Suid; |
| |
| pSMB->AndXCommand = 0xFF; |
| rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, |
| smb_buffer_response, &length, 0); |
| if (ses->server) { |
| atomic_dec(&ses->server->socketUseCount); |
| if (atomic_read(&ses->server->socketUseCount) == 0) { |
| spin_lock(&GlobalMid_Lock); |
| ses->server->tcpStatus = CifsExiting; |
| spin_unlock(&GlobalMid_Lock); |
| rc = -ESHUTDOWN; |
| } |
| } |
| up(&ses->sesSem); |
| cifs_small_buf_release(pSMB); |
| |
| /* 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, int remap) |
| { |
| 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 = |
| cifsConvertToUCS((__le16 *) pSMB->fileName, fileName, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| } else { /* BB improve check for buffer overruns BB */ |
| name_len = strnlen(fileName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->fileName, fileName, name_len); |
| } |
| pSMB->SearchAttributes = |
| cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM); |
| pSMB->BufferFormat = 0x04; |
| pSMB->hdr.smb_buf_length += name_len + 1; |
| pSMB->ByteCount = cpu_to_le16(name_len + 1); |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_deletes); |
| if (rc) { |
| cFYI(1, ("Error in RMFile = %d", rc)); |
| } |
| |
| 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, int remap) |
| { |
| 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 = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| } else { /* BB improve check for buffer overruns BB */ |
| name_len = strnlen(dirName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->DirName, dirName, name_len); |
| } |
| |
| pSMB->BufferFormat = 0x04; |
| pSMB->hdr.smb_buf_length += name_len + 1; |
| pSMB->ByteCount = cpu_to_le16(name_len + 1); |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_rmdirs); |
| if (rc) { |
| cFYI(1, ("Error in RMDir = %d", rc)); |
| } |
| |
| 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 remap) |
| { |
| 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 = cifsConvertToUCS((__le16 *) pSMB->DirName, name, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| } else { /* BB improve check for buffer overruns BB */ |
| name_len = strnlen(name, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->DirName, name, name_len); |
| } |
| |
| pSMB->BufferFormat = 0x04; |
| pSMB->hdr.smb_buf_length += name_len + 1; |
| pSMB->ByteCount = cpu_to_le16(name_len + 1); |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_mkdirs); |
| if (rc) { |
| cFYI(1, ("Error in Mkdir = %d", rc)); |
| } |
| |
| cifs_buf_release(pSMB); |
| if (rc == -EAGAIN) |
| goto MkDirRetry; |
| return rc; |
| } |
| |
| static __u16 convert_disposition(int disposition) |
| { |
| __u16 ofun = 0; |
| |
| switch (disposition) { |
| case FILE_SUPERSEDE: |
| ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; |
| break; |
| case FILE_OPEN: |
| ofun = SMBOPEN_OAPPEND; |
| break; |
| case FILE_CREATE: |
| ofun = SMBOPEN_OCREATE; |
| break; |
| case FILE_OPEN_IF: |
| ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND; |
| break; |
| case FILE_OVERWRITE: |
| ofun = SMBOPEN_OTRUNC; |
| break; |
| case FILE_OVERWRITE_IF: |
| ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; |
| break; |
| default: |
| cFYI(1,("unknown disposition %d",disposition)); |
| ofun = SMBOPEN_OAPPEND; /* regular open */ |
| } |
| return ofun; |
| } |
| |
| int |
| SMBLegacyOpen(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 remap) |
| { |
| int rc = -EACCES; |
| OPENX_REQ *pSMB = NULL; |
| OPENX_RSP *pSMBr = NULL; |
| int bytes_returned; |
| int name_len; |
| __u16 count; |
| |
| OldOpenRetry: |
| rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB, |
| (void **) &pSMBr); |
| if (rc) |
| return rc; |
| |
| pSMB->AndXCommand = 0xFF; /* none */ |
| |
| if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { |
| count = 1; /* account for one byte pad to word boundary */ |
| name_len = |
| cifsConvertToUCS((__le16 *) (pSMB->fileName + 1), |
| fileName, PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| } else { /* BB improve check for buffer overruns BB */ |
| count = 0; /* no pad */ |
| name_len = strnlen(fileName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->fileName, fileName, name_len); |
| } |
| if (*pOplock & REQ_OPLOCK) |
| pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK); |
| else if (*pOplock & REQ_BATCHOPLOCK) { |
| pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK); |
| } |
| pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO); |
| /* BB fixme add conversion for access_flags to bits 0 - 2 of mode */ |
| /* 0 = read |
| 1 = write |
| 2 = rw |
| 3 = execute |
| */ |
| pSMB->Mode = cpu_to_le16(2); |
| pSMB->Mode |= cpu_to_le16(0x40); /* deny none */ |
| /* set file as system file if special file such |
| as fifo and server expecting SFU style and |
| no Unix extensions */ |
| |
| if(create_options & CREATE_OPTION_SPECIAL) |
| pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); |
| else |
| pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); /* BB FIXME */ |
| |
| /* if ((omode & S_IWUGO) == 0) |
| pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/ |
| /* Above line causes problems due to vfs splitting create into two |
| pieces - need to set mode after file created not while it is |
| being created */ |
| |
| /* BB FIXME BB */ |
| /* pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); */ |
| /* BB FIXME END BB */ |
| |
| pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); |
| pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition)); |
| count += name_len; |
| pSMB->hdr.smb_buf_length += count; |
| |
| pSMB->ByteCount = cpu_to_le16(count); |
| /* long_op set to 1 to allow for oplock break timeouts */ |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 1); |
| cifs_stats_inc(&tcon->num_opens); |
| if (rc) { |
| cFYI(1, ("Error in Open = %d", rc)); |
| } else { |
| /* BB verify if wct == 15 */ |
| |
| /* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field BB */ |
| |
| *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? */ |
| /* BB FIXME BB */ |
| /* if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) |
| *pOplock |= CIFS_CREATE_ACTION; */ |
| /* BB FIXME END */ |
| |
| if(pfile_info) { |
| pfile_info->CreationTime = 0; /* BB convert CreateTime*/ |
| pfile_info->LastAccessTime = 0; /* BB fixme */ |
| pfile_info->LastWriteTime = 0; /* BB fixme */ |
| pfile_info->ChangeTime = 0; /* BB fixme */ |
| pfile_info->Attributes = |
| cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes)); |
| /* the file_info buf is endian converted by caller */ |
| pfile_info->AllocationSize = |
| cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile)); |
| pfile_info->EndOfFile = pfile_info->AllocationSize; |
| pfile_info->NumberOfLinks = cpu_to_le32(1); |
| } |
| } |
| |
| cifs_buf_release(pSMB); |
| if (rc == -EAGAIN) |
| goto OldOpenRetry; |
| 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 remap) |
| { |
| int rc = -EACCES; |
| OPEN_REQ *pSMB = NULL; |
| OPEN_RSP *pSMBr = NULL; |
| int bytes_returned; |
| int name_len; |
| __u16 count; |
| |
| openRetry: |
| rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB, |
| (void **) &pSMBr); |
| if (rc) |
| return rc; |
| |
| pSMB->AndXCommand = 0xFF; /* none */ |
| |
| if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { |
| count = 1; /* account for one byte pad to word boundary */ |
| name_len = |
| cifsConvertToUCS((__le16 *) (pSMB->fileName + 1), |
| fileName, PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| pSMB->NameLength = cpu_to_le16(name_len); |
| } else { /* BB improve check for buffer overruns BB */ |
| count = 0; /* no pad */ |
| name_len = strnlen(fileName, PATH_MAX); |
| name_len++; /* trailing null */ |
| pSMB->NameLength = cpu_to_le16(name_len); |
| strncpy(pSMB->fileName, fileName, name_len); |
| } |
| if (*pOplock & REQ_OPLOCK) |
| pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK); |
| else if (*pOplock & REQ_BATCHOPLOCK) { |
| pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); |
| } |
| pSMB->DesiredAccess = cpu_to_le32(access_flags); |
| pSMB->AllocationSize = 0; |
| /* set file as system file if special file such |
| as fifo and server expecting SFU style and |
| no Unix extensions */ |
| if(create_options & CREATE_OPTION_SPECIAL) |
| pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); |
| else |
| pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); |
| /* XP does not handle ATTR_POSIX_SEMANTICS */ |
| /* but it helps speed up case sensitive checks for other |
| servers such as Samba */ |
| if (tcon->ses->capabilities & CAP_UNIX) |
| pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS); |
| |
| /* if ((omode & S_IWUGO) == 0) |
| pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/ |
| /* Above line causes problems due to vfs splitting create into two |
| pieces - need to set mode after file created not while it is |
| being created */ |
| pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); |
| pSMB->CreateDisposition = cpu_to_le32(openDisposition); |
| pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); |
| /* BB Expirement with various impersonation levels and verify */ |
| pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); |
| pSMB->SecurityFlags = |
| SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY; |
| |
| count += name_len; |
| pSMB->hdr.smb_buf_length += count; |
| |
| pSMB->ByteCount = cpu_to_le16(count); |
| /* long_op set to 1 to allow for oplock break timeouts */ |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 1); |
| cifs_stats_inc(&tcon->num_opens); |
| if (rc) { |
| cFYI(1, ("Error in Open = %d", rc)); |
| } else { |
| *pOplock = pSMBr->OplockLevel; /* 1 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); |
| } |
| } |
| |
| 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; |
| int wct; |
| |
| cFYI(1,("Reading %d bytes on fid %d",count,netfid)); |
| if(tcon->ses->capabilities & CAP_LARGE_FILES) |
| wct = 12; |
| else |
| wct = 10; /* old style read */ |
| |
| *nbytes = 0; |
| rc = smb_init(SMB_COM_READ_ANDX, wct, 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); |
| if(wct == 12) |
| pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); |
| else if((lseek >> 32) > 0) /* can not handle this big offset for old */ |
| return -EIO; |
| |
| pSMB->Remaining = 0; |
| pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); |
| pSMB->MaxCountHigh = cpu_to_le32(count >> 16); |
| if(wct == 12) |
| pSMB->ByteCount = 0; /* no need to do le conversion since 0 */ |
| else { |
| /* old style read */ |
| struct smb_com_readx_req * pSMBW = |
| (struct smb_com_readx_req *)pSMB; |
| pSMBW->ByteCount = 0; |
| } |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_reads); |
| if (rc) { |
| cERROR(1, ("Send error in read = %d", rc)); |
| } else { |
| int data_length = le16_to_cpu(pSMBr->DataLengthHigh); |
| data_length = data_length << 16; |
| data_length += le16_to_cpu(pSMBr->DataLength); |
| *nbytes = data_length; |
| |
| /*check that DataLength would not go beyond end of SMB */ |
| if ((data_length > CIFSMaxBufSize) |
| || (data_length > count)) { |
| cFYI(1,("bad length %d for count %d",data_length,count)); |
| rc = -EIO; |
| *nbytes = 0; |
| } else { |
| pReadData = |
| (char *) (&pSMBr->hdr.Protocol) + |
| le16_to_cpu(pSMBr->DataOffset); |
| /* if(rc = copy_to_user(buf, pReadData, data_length)) { |
| cERROR(1,("Faulting on read rc = %d",rc)); |
| rc = -EFAULT; |
| }*/ /* can not use copy_to_user when using page cache*/ |
| if(*buf) |
| memcpy(*buf,pReadData,data_length); |
| } |
| } |
| if(*buf) |
| cifs_buf_release(pSMB); |
| else |
| *buf = (char *)pSMB; |
| |
| /* Note: On -EAGAIN error only caller can retry on handle based calls |
| since file handle passed in no longer valid */ |
| return rc; |
| } |
| |
| int |
| CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, |
| const int netfid, const unsigned int count, |
| const __u64 offset, unsigned int *nbytes, const char *buf, |
| const char __user * ubuf, const int long_op) |
| { |
| int rc = -EACCES; |
| WRITE_REQ *pSMB = NULL; |
| WRITE_RSP *pSMBr = NULL; |
| int bytes_returned, wct; |
| __u32 bytes_sent; |
| __u16 byte_count; |
| |
| /* cFYI(1,("write at %lld %d bytes",offset,count));*/ |
| if(tcon->ses == NULL) |
| return -ECONNABORTED; |
| |
| if(tcon->ses->capabilities & CAP_LARGE_FILES) |
| wct = 14; |
| else |
| wct = 12; |
| |
| rc = smb_init(SMB_COM_WRITE_ANDX, wct, 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); |
| if(wct == 14) |
| pSMB->OffsetHigh = cpu_to_le32(offset >> 32); |
| else if((offset >> 32) > 0) /* can not handle this big offset for old */ |
| return -EIO; |
| |
| pSMB->Reserved = 0xFFFFFFFF; |
| pSMB->WriteMode = 0; |
| pSMB->Remaining = 0; |
| |
| /* Can increase buffer size if buffer is big enough in some cases - ie we |
| can send more if LARGE_WRITE_X capability returned by the server and if |
| our buffer is big enough or if we convert to iovecs on socket writes |
| and eliminate the copy to the CIFS buffer */ |
| if(tcon->ses->capabilities & CAP_LARGE_WRITE_X) { |
| bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); |
| } else { |
| bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) |
| & ~0xFF; |
| } |
| |
| if (bytes_sent > count) |
| bytes_sent = count; |
| pSMB->DataOffset = |
| cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); |
| if(buf) |
| memcpy(pSMB->Data,buf,bytes_sent); |
| else if(ubuf) { |
| if(copy_from_user(pSMB->Data,ubuf,bytes_sent)) { |
| cifs_buf_release(pSMB); |
| return -EFAULT; |
| } |
| } else if (count != 0) { |
| /* No buffer */ |
| cifs_buf_release(pSMB); |
| return -EINVAL; |
| } /* else setting file size with write of zero bytes */ |
| if(wct == 14) |
| byte_count = bytes_sent + 1; /* pad */ |
| else /* wct == 12 */ { |
| byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */ |
| } |
| pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); |
| pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); |
| pSMB->hdr.smb_buf_length += byte_count; |
| |
| if(wct == 14) |
| pSMB->ByteCount = cpu_to_le16(byte_count); |
| else { /* old style write has byte count 4 bytes earlier so 4 bytes pad */ |
| struct smb_com_writex_req * pSMBW = |
| (struct smb_com_writex_req *)pSMB; |
| pSMBW->ByteCount = cpu_to_le16(byte_count); |
| } |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, long_op); |
| cifs_stats_inc(&tcon->num_writes); |
| if (rc) { |
| cFYI(1, ("Send error in write = %d", rc)); |
| *nbytes = 0; |
| } else { |
| *nbytes = le16_to_cpu(pSMBr->CountHigh); |
| *nbytes = (*nbytes) << 16; |
| *nbytes += le16_to_cpu(pSMBr->Count); |
| } |
| |
| cifs_buf_release(pSMB); |
| |
| /* Note: On -EAGAIN error only caller can retry on handle based calls |
| since file handle passed in no longer valid */ |
| |
| return rc; |
| } |
| |
| #ifdef CONFIG_CIFS_EXPERIMENTAL |
| int |
| CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, |
| const int netfid, const unsigned int count, |
| const __u64 offset, unsigned int *nbytes, struct kvec *iov, |
| int n_vec, const int long_op) |
| { |
| int rc = -EACCES; |
| WRITE_REQ *pSMB = NULL; |
| int bytes_returned, wct; |
| int smb_hdr_len; |
| |
| cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ |
| if(tcon->ses->capabilities & CAP_LARGE_FILES) |
| wct = 14; |
| else |
| wct = 12; |
| rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB); |
| 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); |
| if(wct == 14) |
| pSMB->OffsetHigh = cpu_to_le32(offset >> 32); |
| else if((offset >> 32) > 0) /* can not handle this big offset for old */ |
| return -EIO; |
| pSMB->Reserved = 0xFFFFFFFF; |
| pSMB->WriteMode = 0; |
| pSMB->Remaining = 0; |
| |
| pSMB->DataOffset = |
| cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); |
| |
| pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); |
| pSMB->DataLengthHigh = cpu_to_le16(count >> 16); |
| smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ |
| if(wct == 14) |
| pSMB->hdr.smb_buf_length += count+1; |
| else /* wct == 12 */ |
| pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */ |
| if(wct == 14) |
| pSMB->ByteCount = cpu_to_le16(count + 1); |
| else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ { |
| struct smb_com_writex_req * pSMBW = |
| (struct smb_com_writex_req *)pSMB; |
| pSMBW->ByteCount = cpu_to_le16(count + 5); |
| } |
| iov[0].iov_base = pSMB; |
| iov[0].iov_len = smb_hdr_len + 4; |
| |
| rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &bytes_returned, |
| long_op); |
| cifs_stats_inc(&tcon->num_writes); |
| if (rc) { |
| cFYI(1, ("Send error Write2 = %d", rc)); |
| *nbytes = 0; |
| } else { |
| WRITE_RSP * pSMBr = (WRITE_RSP *)pSMB; |
| *nbytes = le16_to_cpu(pSMBr->CountHigh); |
| *nbytes = (*nbytes) << 16; |
| *nbytes += le16_to_cpu(pSMBr->Count); |
| } |
| |
| cifs_small_buf_release(pSMB); |
| |
| /* Note: On -EAGAIN error only caller can retry on handle based calls |
| since file handle passed in no longer valid */ |
| |
| return rc; |
| } |
| |
| |
| #endif /* CIFS_EXPERIMENTAL */ |
| |
| int |
| CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, |
| const __u16 smb_file_id, const __u64 len, |
| const __u64 offset, const __u32 numUnlock, |
| const __u32 numLock, const __u8 lockType, const int waitFlag) |
| { |
| int rc = 0; |
| LOCK_REQ *pSMB = NULL; |
| LOCK_RSP *pSMBr = NULL; |
| int bytes_returned; |
| int timeout = 0; |
| __u16 count; |
| |
| cFYI(1, ("In CIFSSMBLock - timeout %d numLock %d",waitFlag,numLock)); |
| rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); |
| |
| if (rc) |
| return rc; |
| |
| pSMBr = (LOCK_RSP *)pSMB; /* BB removeme BB */ |
| |
| if(lockType == LOCKING_ANDX_OPLOCK_RELEASE) { |
| timeout = -1; /* no response expected */ |
| pSMB->Timeout = 0; |
| } else if (waitFlag == TRUE) { |
| timeout = 3; /* blocking operation, no timeout */ |
| pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */ |
| } else { |
| pSMB->Timeout = 0; |
| } |
| |
| pSMB->NumberOfLocks = cpu_to_le16(numLock); |
| pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock); |
| pSMB->LockType = lockType; |
| pSMB->AndXCommand = 0xFF; /* none */ |
| pSMB->Fid = smb_file_id; /* netfid stays le */ |
| |
| if((numLock != 0) || (numUnlock != 0)) { |
| pSMB->Locks[0].Pid = cpu_to_le16(current->tgid); |
| /* BB where to store pid high? */ |
| pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len); |
| pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32)); |
| pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset); |
| pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32)); |
| count = sizeof(LOCKING_ANDX_RANGE); |
| } else { |
| /* oplock break */ |
| count = 0; |
| } |
| pSMB->hdr.smb_buf_length += count; |
| pSMB->ByteCount = cpu_to_le16(count); |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, timeout); |
| cifs_stats_inc(&tcon->num_locks); |
| if (rc) { |
| cFYI(1, ("Send error in Lock = %d", rc)); |
| } |
| cifs_small_buf_release(pSMB); |
| |
| /* Note: On -EAGAIN error only caller can retry on handle based calls |
| since file handle passed in no longer valid */ |
| return rc; |
| } |
| |
| int |
| CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) |
| { |
| int rc = 0; |
| CLOSE_REQ *pSMB = NULL; |
| CLOSE_RSP *pSMBr = NULL; |
| int bytes_returned; |
| cFYI(1, ("In CIFSSMBClose")); |
| |
| /* do not retry on dead session on close */ |
| rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); |
| if(rc == -EAGAIN) |
| return 0; |
| if (rc) |
| return rc; |
| |
| pSMBr = (CLOSE_RSP *)pSMB; /* BB removeme BB */ |
| |
| pSMB->FileID = (__u16) smb_file_id; |
| pSMB->LastWriteTime = 0; |
| pSMB->ByteCount = 0; |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_closes); |
| if (rc) { |
| if(rc!=-EINTR) { |
| /* EINTR is expected when user ctl-c to kill app */ |
| cERROR(1, ("Send error in Close = %d", rc)); |
| } |
| } |
| |
| cifs_small_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 remap) |
| { |
| int rc = 0; |
| RENAME_REQ *pSMB = NULL; |
| RENAME_RSP *pSMBr = NULL; |
| int bytes_returned; |
| int name_len, name_len2; |
| __u16 count; |
| |
| 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 = |
| cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, |
| PATH_MAX, nls_codepage, remap); |
| 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 = |
| cifsConvertToUCS((__le16 *) &pSMB->OldFileName[name_len + 2], |
| toName, PATH_MAX, nls_codepage, remap); |
| 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, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->OldFileName, fromName, name_len); |
| name_len2 = strnlen(toName, PATH_MAX); |
| 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 */ |
| } |
| |
| count = 1 /* 1st signature byte */ + name_len + name_len2; |
| pSMB->hdr.smb_buf_length += count; |
| pSMB->ByteCount = cpu_to_le16(count); |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_renames); |
| if (rc) { |
| cFYI(1, ("Send error in rename = %d", rc)); |
| } |
| |
| 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, int remap) |
| { |
| 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; |
| __u16 params, param_offset, offset, count, byte_count; |
| |
| cFYI(1, ("Rename to File by handle")); |
| rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB, |
| (void **) &pSMBr); |
| if (rc) |
| return rc; |
| |
| params = 6; |
| pSMB->MaxSetupCount = 0; |
| pSMB->Reserved = 0; |
| pSMB->Flags = 0; |
| pSMB->Timeout = 0; |
| pSMB->Reserved2 = 0; |
| param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; |
| offset = param_offset + params; |
| |
| data_offset = (char *) (&pSMB->hdr.Protocol) + offset; |
| 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); |
| byte_count = 3 /* pad */ + params; |
| pSMB->ParameterCount = cpu_to_le16(params); |
| pSMB->TotalParameterCount = pSMB->ParameterCount; |
| pSMB->ParameterOffset = cpu_to_le16(param_offset); |
| pSMB->DataOffset = cpu_to_le16(offset); |
| /* 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 = cifsConvertToUCS((__le16 *)rename_info->target_name, |
| dummy_string, 24, nls_codepage, remap); |
| } else { |
| len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, |
| target_name, PATH_MAX, nls_codepage, remap); |
| } |
| rename_info->target_name_len = cpu_to_le32(2 * len_of_str); |
| count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2; |
| byte_count += count; |
| pSMB->DataCount = cpu_to_le16(count); |
| 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 += byte_count; |
| pSMB->ByteCount = cpu_to_le16(byte_count); |
| rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&pTcon->num_t2renames); |
| if (rc) { |
| cFYI(1,("Send error in Rename (by file handle) = %d", rc)); |
| } |
| |
| cifs_buf_release(pSMB); |
| |
| /* Note: On -EAGAIN error only caller can retry on handle based calls |
| since file handle passed in no longer valid */ |
| |
| return rc; |
| } |
| |
| int |
| 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 remap) |
| { |
| int rc = 0; |
| COPY_REQ *pSMB = NULL; |
| COPY_RSP *pSMBr = NULL; |
| int bytes_returned; |
| int name_len, name_len2; |
| __u16 count; |
| |
| 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; |
| |
| pSMB->Flags = cpu_to_le16(flags & COPY_TREE); |
| |
| if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { |
| name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName, |
| fromName, PATH_MAX, nls_codepage, |
| remap); |
| 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 = cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], |
| toName, PATH_MAX, nls_codepage, remap); |
| 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, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->OldFileName, fromName, name_len); |
| name_len2 = strnlen(toName, PATH_MAX); |
| 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 */ |
| } |
| |
| count = 1 /* 1st signature byte */ + name_len + name_len2; |
| pSMB->hdr.smb_buf_length += count; |
| pSMB->ByteCount = cpu_to_le16(count); |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| if (rc) { |
| cFYI(1, ("Send error in copy = %d with %d files copied", |
| rc, le16_to_cpu(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; |
| __u16 params, param_offset, offset, byte_count; |
| |
| 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, PATH_MAX |
| /* find define for this maxpathcomponent */ |
| , nls_codepage); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| |
| } else { /* BB improve the check for buffer overruns BB */ |
| name_len = strnlen(fromName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->FileName, fromName, name_len); |
| } |
| params = 6 + name_len; |
| pSMB->MaxSetupCount = 0; |
| pSMB->Reserved = 0; |
| pSMB->Flags = 0; |
| pSMB->Timeout = 0; |
| pSMB->Reserved2 = 0; |
| param_offset = offsetof(struct smb_com_transaction2_spi_req, |
| InformationLevel) - 4; |
| offset = param_offset + params; |
| |
| data_offset = (char *) (&pSMB->hdr.Protocol) + offset; |
| if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { |
| name_len_target = |
| cifs_strtoUCS((wchar_t *) data_offset, toName, PATH_MAX |
| /* 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, PATH_MAX); |
| name_len_target++; /* trailing null */ |
| strncpy(data_offset, toName, 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); |
| byte_count = 3 /* pad */ + params + name_len_target; |
| pSMB->DataCount = cpu_to_le16(name_len_target); |
| pSMB->ParameterCount = cpu_to_le16(params); |
| pSMB->TotalDataCount = pSMB->DataCount; |
| pSMB->TotalParameterCount = pSMB->ParameterCount; |
| pSMB->ParameterOffset = cpu_to_le16(param_offset); |
| pSMB->DataOffset = cpu_to_le16(offset); |
| pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK); |
| pSMB->Reserved4 = 0; |
| pSMB->hdr.smb_buf_length += byte_count; |
| pSMB->ByteCount = cpu_to_le16(byte_count); |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_symlinks); |
| 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, int remap) |
| { |
| 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; |
| __u16 params, param_offset, offset, byte_count; |
| |
| 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 = cifsConvertToUCS((__le16 *) pSMB->FileName, toName, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| |
| } else { /* BB improve the check for buffer overruns BB */ |
| name_len = strnlen(toName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->FileName, toName, name_len); |
| } |
| params = 6 + name_len; |
| pSMB->MaxSetupCount = 0; |
| pSMB->Reserved = 0; |
| pSMB->Flags = 0; |
| pSMB->Timeout = 0; |
| pSMB->Reserved2 = 0; |
| param_offset = offsetof(struct smb_com_transaction2_spi_req, |
| InformationLevel) - 4; |
| offset = param_offset + params; |
| |
| data_offset = (char *) (&pSMB->hdr.Protocol) + offset; |
| if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { |
| name_len_target = |
| cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX, |
| nls_codepage, remap); |
| name_len_target++; /* trailing null */ |
| name_len_target *= 2; |
| } else { /* BB improve the check for buffer overruns BB */ |
| name_len_target = strnlen(fromName, PATH_MAX); |
| name_len_target++; /* trailing null */ |
| strncpy(data_offset, fromName, 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); |
| byte_count = 3 /* pad */ + params + name_len_target; |
| pSMB->ParameterCount = cpu_to_le16(params); |
| pSMB->TotalParameterCount = pSMB->ParameterCount; |
| pSMB->DataCount = cpu_to_le16(name_len_target); |
| pSMB->TotalDataCount = pSMB->DataCount; |
| pSMB->ParameterOffset = cpu_to_le16(param_offset); |
| pSMB->DataOffset = cpu_to_le16(offset); |
| pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK); |
| pSMB->Reserved4 = 0; |
| pSMB->hdr.smb_buf_length += byte_count; |
| pSMB->ByteCount = cpu_to_le16(byte_count); |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_hardlinks); |
| if (rc) { |
| cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc)); |
| } |
| |
| 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 remap) |
| { |
| int rc = 0; |
| NT_RENAME_REQ *pSMB = NULL; |
| RENAME_RSP *pSMBr = NULL; |
| int bytes_returned; |
| int name_len, name_len2; |
| __u16 count; |
| |
| 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 = |
| cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| pSMB->OldFileName[name_len] = 0; /* pad */ |
| pSMB->OldFileName[name_len + 1] = 0x04; |
| name_len2 = |
| cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], |
| toName, PATH_MAX, nls_codepage, remap); |
| 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, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->OldFileName, fromName, name_len); |
| name_len2 = strnlen(toName, PATH_MAX); |
| 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 */ |
| } |
| |
| count = 1 /* string type byte */ + name_len + name_len2; |
| pSMB->hdr.smb_buf_length += count; |
| pSMB->ByteCount = cpu_to_le16(count); |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| cifs_stats_inc(&tcon->num_hardlinks); |
| if (rc) { |
| cFYI(1, ("Send error in hard link (NT rename) = %d", rc)); |
| } |
| 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; |
| __u16 params, byte_count; |
| |
| 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, PATH_MAX |
| /* find define for this maxpathcomponent */ |
| , nls_codepage); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| } else { /* BB improve the check for buffer overruns BB */ |
| name_len = strnlen(searchName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->FileName, searchName, name_len); |
| } |
| |
| params = 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); |
| byte_count = params + 1 /* pad */ ; |
| pSMB->TotalParameterCount = cpu_to_le16(params); |
| pSMB->ParameterCount = pSMB->TotalParameterCount; |
| pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK); |
| pSMB->Reserved4 = 0; |
| pSMB->hdr.smb_buf_length += byte_count; |
| pSMB->ByteCount = cpu_to_le16(byte_count); |
| |
| 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 */ |
| |
| rc = validate_t2((struct smb_t2_rsp *)pSMBr); |
| if (rc || (pSMBr->ByteCount < 2)) |
| /* BB also check enough total bytes returned */ |
| rc = -EIO; /* bad smb */ |
| else { |
| __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| __u16 count = le16_to_cpu(pSMBr->t2.DataCount); |
| |
| if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { |
| name_len = UniStrnlen((wchar_t *) ((char *) |
| &pSMBr->hdr.Protocol +data_offset), |
| min_t(const int, buflen,count) / 2); |
| /* BB FIXME investigate remapping reserved chars here */ |
| cifs_strfromUCS_le(symlinkinfo, |
| (wchar_t *) ((char *)&pSMBr->hdr.Protocol + |
| data_offset), |
| name_len, nls_codepage); |
| } else { |
| strncpy(symlinkinfo, |
| (char *) &pSMBr->hdr.Protocol + |
| data_offset, |
| min_t(const int, buflen, count)); |
| } |
| symlinkinfo[buflen] = 0; |
| /* just in case so calling code does not go off the end of buffer */ |
| } |
| } |
| 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 */ |
| __u32 data_offset = le32_to_cpu(pSMBr->DataOffset); |
| __u32 data_count = le32_to_cpu(pSMBr->DataCount); |
| if ((pSMBr->ByteCount < 2) || (data_offset > 512)) |
| /* BB also check enough total bytes returned */ |
| rc = -EIO; /* bad smb */ |
| else { |
| if(data_count && (data_count < 2048)) { |
| char * end_of_smb = pSMBr->ByteCount + (char *)&pSMBr->ByteCount; |
| |
| struct reparse_data * reparse_buf = (struct reparse_data *) |
| ((char *)&pSMBr->hdr.Protocol + data_offset); |
| if((char*)reparse_buf >= end_of_smb) { |
| rc = -EIO; |
| goto qreparse_out; |
| } |
| if((reparse_buf->LinkNamesBuf + |
| reparse_buf->TargetNameOffset + |
| reparse_buf->TargetNameLen) > |
| end_of_smb) { |
| cFYI(1,("reparse buf extended beyond SMB")); |
| rc = -EIO; |
| goto qreparse_out; |
| } |
| |
| 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)); |
| } |
| } |
| qreparse_out: |
| cifs_buf_release(pSMB); |
| |
| /* Note: On -EAGAIN error only caller can retry on handle based calls |
| since file handle passed in no longer valid */ |
| |
| return rc; |
| } |
| |
| #ifdef CONFIG_CIFS_POSIX |
| |
| /*Convert an Access Control Entry from wire format to local POSIX xattr format*/ |
| static void cifs_convert_ace(posix_acl_xattr_entry * ace, struct cifs_posix_ace * cifs_ace) |
| { |
| /* u8 cifs fields do not need le conversion */ |
| ace->e_perm = (__u16)cifs_ace->cifs_e_perm; |
| ace->e_tag = (__u16)cifs_ace->cifs_e_tag; |
| ace->e_id = (__u32)le64_to_cpu(cifs_ace->cifs_uid); |
| /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */ |
| |
| return; |
| } |
| |
| /* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */ |
| static int cifs_copy_posix_acl(char * trgt,char * src, const int buflen, |
| const int acl_type,const int size_of_data_area) |
| { |
| int size = 0; |
| int i; |
| __u16 count; |
| struct cifs_posix_ace * pACE; |
| struct cifs_posix_acl * cifs_acl = (struct cifs_posix_acl *)src; |
| posix_acl_xattr_header * local_acl = (posix_acl_xattr_header *)trgt; |
| |
| if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) |
| return -EOPNOTSUPP; |
| |
| if(acl_type & ACL_TYPE_ACCESS) { |
| count = le16_to_cpu(cifs_acl->access_entry_count); |
| pACE = &cifs_acl->ace_array[0]; |
| size = sizeof(struct cifs_posix_acl); |
| size += sizeof(struct cifs_posix_ace) * count; |
| /* check if we would go beyond end of SMB */ |
| if(size_of_data_area < size) { |
| cFYI(1,("bad CIFS POSIX ACL size %d vs. %d",size_of_data_area,size)); |
| return -EINVAL; |
| } |
| } else if(acl_type & ACL_TYPE_DEFAULT) { |
| count = le16_to_cpu(cifs_acl->access_entry_count); |
| size = sizeof(struct cifs_posix_acl); |
| size += sizeof(struct cifs_posix_ace) * count; |
| /* skip past access ACEs to get to default ACEs */ |
| pACE = &cifs_acl->ace_array[count]; |
| count = le16_to_cpu(cifs_acl->default_entry_count); |
| size += sizeof(struct cifs_posix_ace) * count; |
| /* check if we would go beyond end of SMB */ |
| if(size_of_data_area < size) |
| return -EINVAL; |
| } else { |
| /* illegal type */ |
| return -EINVAL; |
| } |
| |
| size = posix_acl_xattr_size(count); |
| if((buflen == 0) || (local_acl == NULL)) { |
| /* used to query ACL EA size */ |
| } else if(size > buflen) { |
| return -ERANGE; |
| } else /* buffer big enough */ { |
| local_acl->a_version = POSIX_ACL_XATTR_VERSION; |
| for(i = 0;i < count ;i++) { |
| cifs_convert_ace(&local_acl->a_entries[i],pACE); |
| pACE ++; |
| } |
| } |
| return size; |
| } |
| |
| static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace * cifs_ace, |
| const posix_acl_xattr_entry * local_ace) |
| { |
| __u16 rc = 0; /* 0 = ACL converted ok */ |
| |
| cifs_ace->cifs_e_perm = (__u8)cpu_to_le16(local_ace->e_perm); |
| cifs_ace->cifs_e_tag = (__u8)cpu_to_le16(local_ace->e_tag); |
| /* BB is there a better way to handle the large uid? */ |
| if(local_ace->e_id == -1) { |
| /* Probably no need to le convert -1 on any arch but can not hurt */ |
| cifs_ace->cifs_uid = cpu_to_le64(-1); |
| } else |
| cifs_ace->cifs_uid = (__u64)cpu_to_le32(local_ace->e_id); |
| /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/ |
| return rc; |
| } |
| |
| /* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */ |
| static __u16 ACL_to_cifs_posix(char * parm_data,const char * pACL,const int buflen, |
| const int acl_type) |
| { |
| __u16 rc = 0; |
| struct cifs_posix_acl * cifs_acl = (struct cifs_posix_acl *)parm_data; |
| posix_acl_xattr_header * local_acl = (posix_acl_xattr_header *)pACL; |
| int count; |
| int i; |
| |
| if((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) |
| return 0; |
| |
| count = posix_acl_xattr_count((size_t)buflen); |
| cFYI(1,("setting acl with %d entries from buf of length %d and version of %d", |
| count,buflen,local_acl->a_version)); |
| if(local_acl->a_version != 2) { |
| cFYI(1,("unknown POSIX ACL version %d",local_acl->a_version)); |
| return 0; |
| } |
| cifs_acl->version = cpu_to_le16(1); |
| if(acl_type == ACL_TYPE_ACCESS) |
| cifs_acl->access_entry_count = count; |
| else if(acl_type == ACL_TYPE_DEFAULT) |
| cifs_acl->default_entry_count = count; |
| else { |
| cFYI(1,("unknown ACL type %d",acl_type)); |
| return 0; |
| } |
| for(i=0;i<count;i++) { |
| rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], |
| &local_acl->a_entries[i]); |
| if(rc != 0) { |
| /* ACE not converted */ |
| break; |
| } |
| } |
| if(rc == 0) { |
| rc = (__u16)(count * sizeof(struct cifs_posix_ace)); |
| rc += sizeof(struct cifs_posix_acl); |
| /* BB add check to make sure ACL does not overflow SMB */ |
| } |
| return rc; |
| } |
| |
| int |
| CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, |
| const unsigned char *searchName, |
| char *acl_inf, const int buflen, const int acl_type, |
| const struct nls_table *nls_codepage, int remap) |
| { |
| /* SMB_QUERY_POSIX_ACL */ |
| TRANSACTION2_QPI_REQ *pSMB = NULL; |
| TRANSACTION2_QPI_RSP *pSMBr = NULL; |
| int rc = 0; |
| int bytes_returned; |
| int name_len; |
| __u16 params, byte_count; |
| |
| cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName)); |
| |
| queryAclRetry: |
| rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, |
| (void **) &pSMBr); |
| if (rc) |
| return rc; |
| |
| if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { |
| name_len = |
| cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| pSMB->FileName[name_len] = 0; |
| pSMB->FileName[name_len+1] = 0; |
| } else { /* BB improve the check for buffer overruns BB */ |
| name_len = strnlen(searchName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->FileName, searchName, name_len); |
| } |
| |
| params = 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); |
| byte_count = params + 1 /* pad */ ; |
| pSMB->TotalParameterCount = cpu_to_le16(params); |
| pSMB->ParameterCount = pSMB->TotalParameterCount; |
| pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL); |
| pSMB->Reserved4 = 0; |
| pSMB->hdr.smb_buf_length += byte_count; |
| pSMB->ByteCount = cpu_to_le16(byte_count); |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| if (rc) { |
| cFYI(1, ("Send error in Query POSIX ACL = %d", rc)); |
| } else { |
| /* decode response */ |
| |
| rc = validate_t2((struct smb_t2_rsp *)pSMBr); |
| if (rc || (pSMBr->ByteCount < 2)) |
| /* BB also check enough total bytes returned */ |
| rc = -EIO; /* bad smb */ |
| else { |
| __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| __u16 count = le16_to_cpu(pSMBr->t2.DataCount); |
| rc = cifs_copy_posix_acl(acl_inf, |
| (char *)&pSMBr->hdr.Protocol+data_offset, |
| buflen,acl_type,count); |
| } |
| } |
| cifs_buf_release(pSMB); |
| if (rc == -EAGAIN) |
| goto queryAclRetry; |
| return rc; |
| } |
| |
| int |
| CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, |
| const unsigned char *fileName, |
| const char *local_acl, const int buflen, |
| const int acl_type, |
| const struct nls_table *nls_codepage, int remap) |
| { |
| struct smb_com_transaction2_spi_req *pSMB = NULL; |
| struct smb_com_transaction2_spi_rsp *pSMBr = NULL; |
| char *parm_data; |
| int name_len; |
| int rc = 0; |
| int bytes_returned = 0; |
| __u16 params, byte_count, data_count, param_offset, offset; |
| |
| cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName)); |
| setAclRetry: |
| rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, |
| (void **) &pSMBr); |
| if (rc) |
| return rc; |
| if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { |
| name_len = |
| cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| } else { /* BB improve the check for buffer overruns BB */ |
| name_len = strnlen(fileName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->FileName, fileName, name_len); |
| } |
| params = 6 + name_len; |
| 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; |
| param_offset = offsetof(struct smb_com_transaction2_spi_req, |
| InformationLevel) - 4; |
| offset = param_offset + params; |
| parm_data = ((char *) &pSMB->hdr.Protocol) + offset; |
| pSMB->ParameterOffset = cpu_to_le16(param_offset); |
| |
| /* convert to on the wire format for POSIX ACL */ |
| data_count = ACL_to_cifs_posix(parm_data,local_acl,buflen,acl_type); |
| |
| if(data_count == 0) { |
| rc = -EOPNOTSUPP; |
| goto setACLerrorExit; |
| } |
| pSMB->DataOffset = cpu_to_le16(offset); |
| pSMB->SetupCount = 1; |
| pSMB->Reserved3 = 0; |
| pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); |
| pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL); |
| byte_count = 3 /* pad */ + params + data_count; |
| pSMB->DataCount = cpu_to_le16(data_count); |
| pSMB->TotalDataCount = pSMB->DataCount; |
| pSMB->ParameterCount = cpu_to_le16(params); |
| pSMB->TotalParameterCount = pSMB->ParameterCount; |
| pSMB->Reserved4 = 0; |
| pSMB->hdr.smb_buf_length += byte_count; |
| pSMB->ByteCount = cpu_to_le16(byte_count); |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| if (rc) { |
| cFYI(1, ("Set POSIX ACL returned %d", rc)); |
| } |
| |
| setACLerrorExit: |
| cifs_buf_release(pSMB); |
| if (rc == -EAGAIN) |
| goto setAclRetry; |
| return rc; |
| } |
| |
| /* BB fix tabs in this function FIXME BB */ |
| int |
| CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, |
| const int netfid, __u64 * pExtAttrBits, __u64 *pMask) |
| { |
| int rc = 0; |
| struct smb_t2_qfi_req *pSMB = NULL; |
| struct smb_t2_qfi_rsp *pSMBr = NULL; |
| int bytes_returned; |
| __u16 params, byte_count; |
| |
| cFYI(1,("In GetExtAttr")); |
| if(tcon == NULL) |
| return -ENODEV; |
| |
| GetExtAttrRetry: |
| rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, |
| (void **) &pSMBr); |
| if (rc) |
| return rc; |
| |
| params = 2 /* level */ +2 /* fid */; |
| pSMB->t2.TotalDataCount = 0; |
| pSMB->t2.MaxParameterCount = cpu_to_le16(4); |
| /* BB find exact max data count below from sess structure BB */ |
| pSMB->t2.MaxDataCount = cpu_to_le16(4000); |
| pSMB->t2.MaxSetupCount = 0; |
| pSMB->t2.Reserved = 0; |
| pSMB->t2.Flags = 0; |
| pSMB->t2.Timeout = 0; |
| pSMB->t2.Reserved2 = 0; |
| pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, |
| Fid) - 4); |
| pSMB->t2.DataCount = 0; |
| pSMB->t2.DataOffset = 0; |
| pSMB->t2.SetupCount = 1; |
| pSMB->t2.Reserved3 = 0; |
| pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); |
| byte_count = params + 1 /* pad */ ; |
| pSMB->t2.TotalParameterCount = cpu_to_le16(params); |
| pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; |
| pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS); |
| pSMB->Pad = 0; |
| pSMB->Fid = netfid; |
| pSMB->hdr.smb_buf_length += byte_count; |
| pSMB->t2.ByteCount = cpu_to_le16(byte_count); |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| if (rc) { |
| cFYI(1, ("error %d in GetExtAttr", rc)); |
| } else { |
| /* decode response */ |
| rc = validate_t2((struct smb_t2_rsp *)pSMBr); |
| if (rc || (pSMBr->ByteCount < 2)) |
| /* BB also check enough total bytes returned */ |
| /* If rc should we check for EOPNOSUPP and |
| disable the srvino flag? or in caller? */ |
| rc = -EIO; /* bad smb */ |
| else { |
| __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| __u16 count = le16_to_cpu(pSMBr->t2.DataCount); |
| struct file_chattr_info * pfinfo; |
| /* BB Do we need a cast or hash here ? */ |
| if(count != 16) { |
| cFYI(1, ("Illegal size ret in GetExtAttr")); |
| rc = -EIO; |
| goto GetExtAttrOut; |
| } |
| pfinfo = (struct file_chattr_info *) |
| (data_offset + (char *) &pSMBr->hdr.Protocol); |
| *pExtAttrBits = le64_to_cpu(pfinfo->mode); |
| *pMask = le64_to_cpu(pfinfo->mask); |
| } |
| } |
| GetExtAttrOut: |
| cifs_buf_release(pSMB); |
| if (rc == -EAGAIN) |
| goto GetExtAttrRetry; |
| return rc; |
| } |
| |
| |
| #endif /* CONFIG_POSIX */ |
| |
| /* Legacy Query Path Information call for lookup to old servers such |
| as Win9x/WinME */ |
| int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, |
| const unsigned char *searchName, |
| FILE_ALL_INFO * pFinfo, |
| const struct nls_table *nls_codepage, int remap) |
| { |
| QUERY_INFORMATION_REQ * pSMB; |
| QUERY_INFORMATION_RSP * pSMBr; |
| int rc = 0; |
| int bytes_returned; |
| int name_len; |
| |
| cFYI(1, ("In SMBQPath path %s", searchName)); |
| QInfRetry: |
| rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB, |
| (void **) &pSMBr); |
| if (rc) |
| return rc; |
| |
| if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { |
| name_len = |
| cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| } else { |
| name_len = strnlen(searchName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->FileName, searchName, name_len); |
| } |
| pSMB->BufferFormat = 0x04; |
| name_len++; /* account for buffer type byte */ |
| pSMB->hdr.smb_buf_length += (__u16) name_len; |
| pSMB->ByteCount = cpu_to_le16(name_len); |
| |
| rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
| (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
| if (rc) { |
| cFYI(1, ("Send error in QueryInfo = %d", rc)); |
| } else if (pFinfo) { /* decode response */ |
| memset(pFinfo, 0, sizeof(FILE_ALL_INFO)); |
| pFinfo->AllocationSize = |
| cpu_to_le64(le32_to_cpu(pSMBr->size)); |
| pFinfo->EndOfFile = pFinfo->AllocationSize; |
| pFinfo->Attributes = |
| cpu_to_le32(le16_to_cpu(pSMBr->attr)); |
| } else |
| rc = -EIO; /* bad buffer passed in */ |
| |
| cifs_buf_release(pSMB); |
| |
| if (rc == -EAGAIN) |
| goto QInfRetry; |
| |
| return rc; |
| } |
| |
| |
| |
| |
| int |
| CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, |
| const unsigned char *searchName, |
| FILE_ALL_INFO * pFindData, |
| const struct nls_table *nls_codepage, int remap) |
| { |
| /* 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; |
| __u16 params, byte_count; |
| |
| /* 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 = |
| cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, |
| PATH_MAX, nls_codepage, remap); |
| name_len++; /* trailing null */ |
| name_len *= 2; |
| } else { /* BB improve the check for buffer overruns BB */ |
| name_len = strnlen(searchName, PATH_MAX); |
| name_len++; /* trailing null */ |
| strncpy(pSMB->FileName, searchName, name_len); |
| } |
| |
| params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */ ; |
| 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); |
| byte_count = params + 1 /* pad */ ; |
| pSMB->TotalParameterCount = cpu_to_le16(params); |
| pSMB->ParameterCount = pSMB->TotalParameterCount; |
| pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); |
| pSMB->Reserved4 = 0; |
| pSMB->hdr.smb_buf_length += byte_count; |
| pSMB->ByteCount = cpu_to_le16(byte_count); |
| |
| 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 */ |
| rc = validate_t2((struct smb_t2_rsp *)pSMBr); |
| |
| if (rc || (pSMBr->ByteCount < 40)) |
| rc = -EIO; /* bad smb */ |
| else if (pFindData){ |
| __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| memcpy((char *) pFindData, |
| (char *) &pSMBr->hdr.Protocol + |
| data_offset, sizeof (FILE_ALL_INFO)); |
| } else |
| rc = -ENOMEM; |
| } |
| 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, int remap) |
| { |
| /* 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; |
| __u16 params, byte_count; |
| |
| cFYI(1, ("In QPathInfo (Unix) the path %s", searchName)); |
| UnixQPathInfoRetry: |
| rc |