blob: a53c596e1082fe3c90bc6e93c4736b120ef9d277 [file] [log] [blame]
/*
* 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