/*
 *   fs/cifs/cifssmb.c
 *
 *   Copyright (C) International Business Machines  Corp., 2002,2003
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   Contains the routines for constructing the SMB PDUs themselves
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

 /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c   */
 /* These are mostly routines that operate on a pathname, or on a tree id     */
 /* (mounted volume), but there are eight handle based routines which must be */
 /* treated slightly different for reconnection purposes since we never want  */
 /* to reuse a stale file handle and the caller knows the file handle */

#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/vfs.h>
#include <asm/uaccess.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_unicode.h"
#include "cifs_debug.h"

#ifdef CONFIG_CIFS_POSIX
static struct {
	int index;
	char *name;
} protocols[] = {
	{CIFS_PROT, "\2NT LM 0.12"}, 
	{CIFS_PROT, "\2POSIX 2"},
	{BAD_PROT, "\2"}
};
#else
static struct {
	int index;
	char *name;
} protocols[] = {
	{CIFS_PROT, "\2NT LM 0.12"}, 
	{BAD_PROT, "\2"}
};
#endif


/* Mark as invalid, all open files on tree connections since they
   were closed when session to server was lost */
static void mark_open_files_invalid(struct cifsTconInfo * pTcon)
{
	struct cifsFileInfo *open_file = NULL;
	struct list_head * tmp;
	struct list_head * tmp1;

/* list all files open on tree connection and mark them invalid */
	write_lock(&GlobalSMBSeslock);
	list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
		open_file = list_entry(tmp,struct cifsFileInfo, tlist);
		if(open_file) {
			open_file->invalidHandle = TRUE;
		}
	}
	write_unlock(&GlobalSMBSeslock);
	/* BB Add call to invalidate_inodes(sb) for all superblocks mounted to this tcon */
}

static int
smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
	 void **request_buf /* returned */ ,
	 void **response_buf /* returned */ )
{
	int rc = 0;

	/* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
	   check for tcp and smb session status done differently
	   for those three - in the calling routine */
	if(tcon) {
		if((tcon->ses) && (tcon->ses->server)){
			struct nls_table *nls_codepage;
				/* Give Demultiplex thread up to 10 seconds to 
					reconnect, should be greater than cifs socket
					timeout which is 7 seconds */
			while(tcon->ses->server->tcpStatus == CifsNeedReconnect) {
				wait_event_interruptible_timeout(tcon->ses->server->response_q,
					(tcon->ses->server->tcpStatus == CifsGood), 10 * HZ);
				if(tcon->ses->server->tcpStatus == CifsNeedReconnect) {
					/* on "soft" mounts we wait once */
					if((tcon->retry == FALSE) || 
					   (tcon->ses->status == CifsExiting)) {
						cFYI(1,("gave up waiting on reconnect in smb_init"));
						return -EHOSTDOWN;
					} /* else "hard" mount - keep retrying until 
					process is killed or server comes back up */
				} else /* TCP session is reestablished now */
					break;
				 
			}
			
			nls_codepage = load_nls_default();
		/* need to prevent multiple threads trying to
		simultaneously reconnect the same SMB session */
			down(&tcon->ses->sesSem);
			if(tcon->ses->status == CifsNeedReconnect)
				rc = cifs_setup_session(0, tcon->ses, nls_codepage);
			if(!rc && (tcon->tidStatus == CifsNeedReconnect)) {
				mark_open_files_invalid(tcon);
				rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon,
					nls_codepage);
				up(&tcon->ses->sesSem);
				if(rc == 0)
					atomic_inc(&tconInfoReconnectCount);

				cFYI(1, ("reconnect tcon rc = %d", rc));
				/* Removed call to reopen open files here - 
					it is safer (and faster) to reopen files
					one at a time as needed in read and write */

				/* Check if handle based operation so we 
					know whether we can continue or not without
					returning to caller to reset file handle */
				switch(smb_command) {
					case SMB_COM_READ_ANDX:
					case SMB_COM_WRITE_ANDX:
					case SMB_COM_CLOSE:
					case SMB_COM_FIND_CLOSE2:
					case SMB_COM_LOCKING_ANDX: {
						unload_nls(nls_codepage);
						return -EAGAIN;
					}
				}
			} else {
				up(&tcon->ses->sesSem);
			}
			unload_nls(nls_codepage);

		} else {
			return -EIO;
		}
	}
	if(rc)
		return rc;

	*request_buf = cifs_buf_get();
	if (*request_buf == 0) {
		/* BB should we add a retry in here if not a writepage? */
		return -ENOMEM;
	}
    /* Although the original thought was we needed the response buf for  */
    /* potential retries of smb operations it turns out we can determine */
    /* from the mid flags when the request buffer can be resent without  */
    /* having to use a second distinct buffer for the response */
	*response_buf = *request_buf; 

	header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
			wct /*wct */ );

#ifdef CONFIG_CIFS_STATS
        if(tcon != NULL) {
                atomic_inc(&tcon->num_smbs_sent);
        }
#endif
	return rc;
}

int
CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
{
	NEGOTIATE_REQ *pSMB;
	NEGOTIATE_RSP *pSMBr;
	int rc = 0;
	int bytes_returned;
	struct TCP_Server_Info * server;
	u16 count;

	if(ses->server)
		server = ses->server;
	else {
		rc = -EIO;
		return rc;
	}
	rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
		      (void **) &pSMB, (void **) &pSMBr);
	if (rc)
		return rc;

	pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
	if (extended_security)
		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;

	count = strlen(protocols[0].name) + 1;
	strncpy(pSMB->DialectsArray, protocols[0].name, 30);	
    /* null guaranteed to be at end of source and target buffers anyway */

	pSMB->hdr.smb_buf_length += count;
	pSMB->ByteCount = cpu_to_le16(count);

	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc == 0) {
		server->secMode = pSMBr->SecurityMode;	
		server->secType = NTLM; /* BB override default for NTLMv2 or krb*/
		/* one byte - no need to convert this or EncryptionKeyLen from le,*/
		server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
		/* probably no need to store and check maxvcs */
		server->maxBuf =
			min(le32_to_cpu(pSMBr->MaxBufferSize),
			(__u32) CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE);
		server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
		cFYI(0, ("Max buf = %d ", ses->server->maxBuf));
		GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
		server->capabilities = le32_to_cpu(pSMBr->Capabilities);
		server->timeZone = le16_to_cpu(pSMBr->ServerTimeZone);	
        /* BB with UTC do we ever need to be using srvr timezone? */
		if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
			memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
			       CIFS_CRYPTO_KEY_SIZE);
		} else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
			   && (pSMBr->EncryptionKeyLength == 0)) {
			/* decode security blob */
		} else
			rc = -EIO;

		/* BB might be helpful to save off the domain of server here */

		if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && 
			(server->capabilities & CAP_EXTENDED_SECURITY)) {
			count = pSMBr->ByteCount;
			if (count < 16)
				rc = -EIO;
			else if (count == 16) {
				server->secType = RawNTLMSSP;
				if (server->socketUseCount.counter > 1) {
					if (memcmp
						(server->server_GUID,
						pSMBr->u.extended_response.
						GUID, 16) != 0) {
						cFYI(1,
							("UID of server does not match previous connection to same ip address"));
						memcpy(server->
							server_GUID,
							pSMBr->u.
							extended_response.
							GUID, 16);
					}
				} else
					memcpy(server->server_GUID,
					       pSMBr->u.extended_response.
					       GUID, 16);
			} else {
				rc = decode_negTokenInit(pSMBr->u.
							 extended_response.
							 SecurityBlob,
							 count - 16,
							 &server->secType);
			}
		} else
			server->capabilities &= ~CAP_EXTENDED_SECURITY;
		if(sign_CIFS_PDUs == FALSE) {        
			if(server->secMode & SECMODE_SIGN_REQUIRED)
				cERROR(1,
				 ("Server requires /proc/fs/cifs/PacketSigningEnabled"));
			server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
		} else if(sign_CIFS_PDUs == 1) {
			if((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
				server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
		}
				
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	return rc;
}

int
CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
{
	struct smb_hdr *smb_buffer;
	struct smb_hdr *smb_buffer_response;
	int rc = 0;
	int length;

	cFYI(1, ("In tree disconnect"));
	/*
	 *  If last user of the connection and
	 *  connection alive - disconnect it
	 *  If this is the last connection on the server session disconnect it
	 *  (and inside session disconnect we should check if tcp socket needs 
	 *  to be freed and kernel thread woken up).
	 */
	if (tcon)
		down(&tcon->tconSem);
	else
		return -EIO;

	atomic_dec(&tcon->useCount);
	if (atomic_read(&tcon->useCount) > 0) {
		up(&tcon->tconSem);
		return -EBUSY;
	}

	/* No need to return error on this operation if tid invalidated and 
	closed on server already e.g. due to tcp session crashing */
	if(tcon->tidStatus == CifsNeedReconnect) {
		up(&tcon->tconSem);
		return 0;  
	}

	if((tcon->ses == 0) || (tcon->ses->server == 0)) {    
		up(&tcon->tconSem);
		return -EIO;
	}

	rc = smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
		      (void **) &smb_buffer, (void **) &smb_buffer_response);
	if (rc) {
		up(&tcon->tconSem);
		return rc;
	}
	rc = SendReceive(xid, tcon->ses, smb_buffer, smb_buffer_response,
			 &length, 0);
	if (rc)
		cFYI(1, (" Tree disconnect failed %d", rc));

	if (smb_buffer)
		cifs_buf_release(smb_buffer);
	up(&tcon->tconSem);

	/* No need to return error on this operation if tid invalidated and 
	closed on server already e.g. due to tcp session crashing */
	if (rc == -EAGAIN)
		rc = 0;

	return rc;
}

int
CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
{
	struct smb_hdr *smb_buffer_response;
	LOGOFF_ANDX_REQ *pSMB;
	int rc = 0;
	int length;

	cFYI(1, ("In SMBLogoff for session disconnect"));
	if (ses)
		down(&ses->sesSem);
	else
		return -EIO;

	atomic_dec(&ses->inUse);
	if (atomic_read(&ses->inUse) > 0) {
		up(&ses->sesSem);
		return -EBUSY;
	}

	rc = smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL /* no tcon anymore */,
		 (void **) &pSMB, (void **) &smb_buffer_response);
	
	if(ses->server) {
		if(ses->server->secMode & 
		   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
			pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
	}

	if (rc) {
		up(&ses->sesSem);
		return rc;
	}

	pSMB->hdr.Uid = ses->Suid;

	pSMB->AndXCommand = 0xFF;
	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
			 smb_buffer_response, &length, 0);
	if (ses->server) {
		atomic_dec(&ses->server->socketUseCount);
		if (atomic_read(&ses->server->socketUseCount) == 0) {
			spin_lock(&GlobalMid_Lock);
			ses->server->tcpStatus = CifsExiting;
			spin_unlock(&GlobalMid_Lock);
			rc = -ESHUTDOWN;
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	up(&ses->sesSem);

	/* if session dead then we do not need to do ulogoff,
		since server closed smb session, no sense reporting 
		error */
	if (rc == -EAGAIN)
		rc = 0;
	return rc;
}

int
CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon,
	       const char *fileName, const struct nls_table *nls_codepage)
{
	DELETE_FILE_REQ *pSMB = NULL;
	DELETE_FILE_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;

DelFileRetry:
	rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->fileName, fileName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fileName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->fileName, fileName, name_len);
	}
	pSMB->SearchAttributes =
	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
	pSMB->BufferFormat = 0x04;
	pSMB->hdr.smb_buf_length += name_len + 1;
	pSMB->ByteCount = cpu_to_le16(name_len + 1);
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("Error in RMFile = %d", rc));
	} 
#ifdef CONFIG_CIFS_STATS
        else {
		atomic_inc(&tcon->num_deletes);
        }
#endif

	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto DelFileRetry;

	return rc;
}

int
CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon,
	     const char *dirName, const struct nls_table *nls_codepage)
{
	DELETE_DIRECTORY_REQ *pSMB = NULL;
	DELETE_DIRECTORY_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;

	cFYI(1, ("In CIFSSMBRmDir"));
RmDirRetry:
	rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len = cifs_strtoUCS((wchar_t *) pSMB->DirName, dirName, 530
				/* find define for this maxpathcomponent */
				, nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(dirName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->DirName, dirName, name_len);
	}

	pSMB->BufferFormat = 0x04;
	pSMB->hdr.smb_buf_length += name_len + 1;
	pSMB->ByteCount = cpu_to_le16(name_len + 1);
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("Error in RMDir = %d", rc));
	}
#ifdef CONFIG_CIFS_STATS
        else {
		atomic_inc(&tcon->num_rmdirs);
        }
#endif

	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto RmDirRetry;
	return rc;
}

int
CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
	     const char *name, const struct nls_table *nls_codepage)
{
	int rc = 0;
	CREATE_DIRECTORY_REQ *pSMB = NULL;
	CREATE_DIRECTORY_RSP *pSMBr = NULL;
	int bytes_returned;
	int name_len;

	cFYI(1, ("In CIFSSMBMkDir"));
MkDirRetry:
	rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len = cifs_strtoUCS((wchar_t *) pSMB->DirName, name, 530
					 /* find define for this maxpathcomponent */
					 , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(name, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->DirName, name, name_len);
	}

	pSMB->BufferFormat = 0x04;
	pSMB->hdr.smb_buf_length += name_len + 1;
	pSMB->ByteCount = cpu_to_le16(name_len + 1);
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("Error in Mkdir = %d", rc));
	}
#ifdef CONFIG_CIFS_STATS
        else {
		atomic_inc(&tcon->num_mkdirs);
        }
#endif
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto MkDirRetry;
	return rc;
}

int
CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
	    const char *fileName, const int openDisposition,
	    const int access_flags, const int create_options, __u16 * netfid,
	    int *pOplock, FILE_ALL_INFO * pfile_info, 
	    const struct nls_table *nls_codepage)
{
	int rc = -EACCES;
	OPEN_REQ *pSMB = NULL;
	OPEN_RSP *pSMBr = NULL;
	int bytes_returned;
	int name_len;
	__u16 count;

openRetry:
	rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	pSMB->AndXCommand = 0xFF;	/* none */

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		count = 1;	/* account for one byte pad to word boundary */
		name_len =
		    cifs_strtoUCS((wchar_t *) (pSMB->fileName + 1),
				  fileName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
		pSMB->NameLength = cpu_to_le16(name_len);
	} else {		/* BB improve the check for buffer overruns BB */
		count = 0;	/* no pad */
		name_len = strnlen(fileName, 530);
		name_len++;	/* trailing null */
		pSMB->NameLength = cpu_to_le16(name_len);
		strncpy(pSMB->fileName, fileName, name_len);
	}
	if (*pOplock & REQ_OPLOCK)
		pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
	else if (*pOplock & REQ_BATCHOPLOCK) {
		pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
	}
	pSMB->DesiredAccess = cpu_to_le32(access_flags);
	pSMB->AllocationSize = 0;
	pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
	/* XP does not handle ATTR_POSIX_SEMANTICS */
	/* but it helps speed up case sensitive checks for other
	servers such as Samba */
	if (tcon->ses->capabilities & CAP_UNIX)
		pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);

	/* if ((omode & S_IWUGO) == 0)
		pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/
	/*  Above line causes problems due to vfs splitting create into two
		pieces - need to set mode after file created not while it is
		being created */
	pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
	pSMB->CreateDisposition = cpu_to_le32(openDisposition);
	pSMB->CreateOptions = cpu_to_le32(create_options);
	pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);	/* BB ??*/
	pSMB->SecurityFlags =
	    SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;

	count += name_len;
	pSMB->hdr.smb_buf_length += count;

	pSMB->ByteCount = cpu_to_le16(count);
	/* long_op set to 1 to allow for oplock break timeouts */
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 1);
	if (rc) {
		cFYI(1, ("Error in Open = %d", rc));
	} else {
		*pOplock = pSMBr->OplockLevel;	/* one byte no need to le_to_cpu */
		*netfid = pSMBr->Fid;	/* cifs fid stays in le */
		/* Let caller know file was created so we can set the mode. */
		/* Do we care about the CreateAction in any other cases? */
		if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
			*pOplock |= CIFS_CREATE_ACTION; 
		if(pfile_info) {
		    memcpy((char *)pfile_info,(char *)&pSMBr->CreationTime,
			36 /* CreationTime to Attributes */);
		    /* the file_info buf is endian converted by caller */
		    pfile_info->AllocationSize = pSMBr->AllocationSize;
		    pfile_info->EndOfFile = pSMBr->EndOfFile;
		    pfile_info->NumberOfLinks = cpu_to_le32(1);
		}

#ifdef CONFIG_CIFS_STATS
		atomic_inc(&tcon->num_opens);
#endif
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto openRetry;
	return rc;
}

/* If no buffer passed in, then caller wants to do the copy
	as in the case of readpages so the SMB buffer must be
	freed by the caller */

int
CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
	    const int netfid, const unsigned int count,
	    const __u64 lseek, unsigned int *nbytes, char **buf)
{
	int rc = -EACCES;
	READ_REQ *pSMB = NULL;
	READ_RSP *pSMBr = NULL;
	char *pReadData = NULL;
	int bytes_returned;

	*nbytes = 0;
	rc = smb_init(SMB_COM_READ_ANDX, 12, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	/* tcon and ses pointer are checked in smb_init */
	if (tcon->ses->server == NULL)
		return -ECONNABORTED;

	pSMB->AndXCommand = 0xFF;	/* none */
	pSMB->Fid = netfid;
	pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
	pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
	pSMB->Remaining = 0;
	pSMB->MaxCount = cpu_to_le16(count);
	pSMB->MaxCountHigh = 0;
	pSMB->ByteCount = 0;  /* no need to do le conversion since it is 0 */

	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cERROR(1, ("Send error in read = %d", rc));
	} else {
		__u16 data_length = le16_to_cpu(pSMBr->DataLength);
		*nbytes = data_length;
		/*check that DataLength would not go beyond end of SMB */
		if ((data_length > CIFS_MAX_MSGSIZE) 
				|| (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 (pSMB) {
		if(*buf)
			cifs_buf_release(pSMB);
		else
			*buf = (char *)pSMB;
	}

	/* Note: On -EAGAIN error only caller can retry on handle based calls 
		since file handle passed in no longer valid */
	return rc;
}

int
CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
	     const int netfid, const unsigned int count,
	     const __u64 offset, unsigned int *nbytes, const char *buf,
	     const int long_op)
{
	int rc = -EACCES;
	WRITE_REQ *pSMB = NULL;
	WRITE_RSP *pSMBr = NULL;
	int bytes_returned;
	unsigned bytes_sent;
	__u16 byte_count;

	rc = smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;
	/* tcon and ses pointer are checked in smb_init */
	if (tcon->ses->server == NULL)
		return -ECONNABORTED;

	pSMB->AndXCommand = 0xFF;	/* none */
	pSMB->Fid = netfid;
	pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
	pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
	pSMB->Remaining = 0;
	bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & ~0xFF;
	if (bytes_sent > count)
		bytes_sent = count;
	pSMB->DataLengthHigh = 0;
	pSMB->DataOffset =
	    cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4);

	memcpy(pSMB->Data,buf,bytes_sent);

	byte_count = bytes_sent + 1 /* pad */ ;
	pSMB->DataLengthLow = cpu_to_le16(bytes_sent);
	pSMB->DataLengthHigh = 0;
	pSMB->hdr.smb_buf_length += byte_count;
	pSMB->ByteCount = cpu_to_le16(byte_count);

	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
	if (rc) {
		cFYI(1, ("Send error in write = %d", rc));
		*nbytes = 0;
	} else
		*nbytes = le16_to_cpu(pSMBr->Count);

	if (pSMB)
		cifs_buf_release(pSMB);

	/* Note: On -EAGAIN error only caller can retry on handle based calls 
		since file handle passed in no longer valid */

	return rc;
}

int
CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
	    const __u16 smb_file_id, const __u64 len,
	    const __u64 offset, const __u32 numUnlock,
	    const __u32 numLock, const __u8 lockType, const int waitFlag)
{
	int rc = 0;
	LOCK_REQ *pSMB = NULL;
	LOCK_RSP *pSMBr = NULL;
	int bytes_returned;
	int timeout = 0;
	__u16 count;

	cFYI(1, ("In CIFSSMBLock - timeout %d numLock %d",waitFlag,numLock));
	rc = smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if(lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
		timeout = -1; /* no response expected */
		pSMB->Timeout = 0;
	} else if (waitFlag == TRUE) {
		timeout = 3;  /* blocking operation, no timeout */
		pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
	} else {
		pSMB->Timeout = 0;
	}

	pSMB->NumberOfLocks = cpu_to_le16(numLock);
	pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
	pSMB->LockType = lockType;
	pSMB->AndXCommand = 0xFF;	/* none */
	pSMB->Fid = smb_file_id; /* netfid stays le */

	if((numLock != 0) || (numUnlock != 0)) {
		pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
		/* BB where to store pid high? */
		pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
		pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
		pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
		pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
		count = sizeof(LOCKING_ANDX_RANGE);
	} else {
		/* oplock break */
		count = 0;
	}
	pSMB->hdr.smb_buf_length += count;
	pSMB->ByteCount = cpu_to_le16(count);

	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, timeout);

	if (rc) {
		cFYI(1, ("Send error in Lock = %d", rc));
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	/* Note: On -EAGAIN error only caller can retry on handle based calls 
	since file handle passed in no longer valid */
	return rc;
}

int
CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
{
	int rc = 0;
	CLOSE_REQ *pSMB = NULL;
	CLOSE_RSP *pSMBr = NULL;
	int bytes_returned;
	cFYI(1, ("In CIFSSMBClose"));

/* do not retry on dead session on close */
	rc = smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if(rc == -EAGAIN)
		return 0;
	if (rc)
		return rc;

	pSMB->FileID = (__u16) smb_file_id;
	pSMB->LastWriteTime = 0;
	pSMB->ByteCount = 0;
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		if(rc!=-EINTR) {
			/* EINTR is expected when user ctl-c to kill app */
			cERROR(1, ("Send error in Close = %d", rc));
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	/* Since session is dead, file will be closed on server already */
	if(rc == -EAGAIN)
		rc = 0;

	return rc;
}

int
CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
	      const char *fromName, const char *toName,
	      const struct nls_table *nls_codepage)
{
	int rc = 0;
	RENAME_REQ *pSMB = NULL;
	RENAME_RSP *pSMBr = NULL;
	int bytes_returned;
	int name_len, name_len2;
	__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 =
		    cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
		pSMB->OldFileName[name_len] = 0x04;	/* pad */
	/* protocol requires ASCII signature byte on Unicode string */
		pSMB->OldFileName[name_len + 1] = 0x00;
		name_len2 =
		    cifs_strtoUCS((wchar_t *) & pSMB->
				  OldFileName[name_len + 2], toName, 530,
				  nls_codepage);
		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;
		name_len2 *= 2;	/* convert to bytes */
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fromName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->OldFileName, fromName, name_len);
		name_len2 = strnlen(toName, 530);
		name_len2++;	/* trailing null */
		pSMB->OldFileName[name_len] = 0x04;  /* 2nd buffer format */
		strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
		name_len2++;	/* trailing null */
		name_len2++;	/* signature byte */
	}

	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 rename = %d", rc));
	} 

#ifdef CONFIG_CIFS_STATS
	  else {
		atomic_inc(&tcon->num_renames);
	}
#endif

	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto renameRetry;

	return rc;
}

int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, 
		int netfid, char * target_name, const struct nls_table * nls_codepage) 
{
	struct smb_com_transaction2_sfi_req *pSMB  = NULL;
	struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
	struct set_file_rename * rename_info;
	char *data_offset;
	char dummy_string[30];
	int rc = 0;
	int bytes_returned = 0;
	int len_of_str;
	__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 = cifs_strtoUCS((wchar_t *) rename_info->target_name, dummy_string, 24, nls_codepage);
	} else {
		len_of_str = cifs_strtoUCS((wchar_t *) rename_info->target_name, target_name, 530, nls_codepage);
	}
	rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
	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);
	if (rc) {
		cFYI(1,("Send error in Rename (by file handle) = %d", rc));
	}
#ifdef CONFIG_CIFS_STATS
	  else {
		atomic_inc(&pTcon->num_t2renames);
	}
#endif
	if (pSMB)
		cifs_buf_release(pSMB);

	/* Note: On -EAGAIN error only caller can retry on handle based calls
		since file handle passed in no longer valid */

	return rc;
}

int
CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char * fromName, 
            const __u16 target_tid, const char *toName, const int flags,
            const struct nls_table *nls_codepage)
{
	int rc = 0;
	COPY_REQ *pSMB = NULL;
	COPY_RSP *pSMBr = NULL;
	int bytes_returned;
	int name_len, name_len2;
	__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 = cifs_strtoUCS((wchar_t *) pSMB->OldFileName, 
				fromName, 
				530 /* find define for this maxpathcomponent */,
				nls_codepage);
		name_len++;     /* trailing null */
		name_len *= 2;
		pSMB->OldFileName[name_len] = 0x04;     /* pad */
		/* protocol requires ASCII signature byte on Unicode string */
		pSMB->OldFileName[name_len + 1] = 0x00;
		name_len2 = cifs_strtoUCS((wchar_t *) & pSMB->
				OldFileName[name_len + 2], toName, 530,
				nls_codepage);
		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;
		name_len2 *= 2; /* convert to bytes */
	} else {                /* BB improve the check for buffer overruns BB */
		name_len = strnlen(fromName, 530);
		name_len++;     /* trailing null */
		strncpy(pSMB->OldFileName, fromName, name_len);
		name_len2 = strnlen(toName, 530);
		name_len2++;    /* trailing null */
		pSMB->OldFileName[name_len] = 0x04;  /* 2nd buffer format */
		strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
		name_len2++;    /* trailing null */
		name_len2++;    /* signature byte */
	}

	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, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;

	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fromName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, fromName, name_len);
	}
	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, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len_target++;	/* trailing null */
		name_len_target *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len_target = strnlen(toName, 530);
		name_len_target++;	/* trailing null */
		strncpy(data_offset, toName, name_len_target);
	}

	pSMB->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);
	if (rc) {
		cFYI(1,
		     ("Send error in SetPathInfo (create symlink) = %d",
		      rc));
	}

	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto createSymLinkRetry;

	return rc;
}

int
CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
		       const char *fromName, const char *toName,
		       const struct nls_table *nls_codepage)
{
	TRANSACTION2_SPI_REQ *pSMB = NULL;
	TRANSACTION2_SPI_RSP *pSMBr = NULL;
	char *data_offset;
	int name_len;
	int name_len_target;
	int rc = 0;
	int bytes_returned = 0;
	__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 = cifs_strtoUCS((wchar_t *) pSMB->FileName, toName, 530
					 /* find define for this maxpathcomponent */
					 , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;

	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(toName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, toName, name_len);
	}
	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, fromName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len_target++;	/* trailing null */
		name_len_target *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len_target = strnlen(fromName, 530);
		name_len_target++;	/* trailing null */
		strncpy(data_offset, fromName, name_len_target);
	}

	pSMB->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);
	if (rc) {
		cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
	}

	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto createHardLinkRetry;

	return rc;
}

int
CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
		   const char *fromName, const char *toName,
		   const struct nls_table *nls_codepage)
{
	int rc = 0;
	NT_RENAME_REQ *pSMB = NULL;
	RENAME_RSP *pSMBr = NULL;
	int bytes_returned;
	int name_len, name_len2;
	__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 =
		    cifs_strtoUCS((wchar_t *) pSMB->OldFileName, fromName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
		pSMB->OldFileName[name_len] = 0;	/* pad */
		pSMB->OldFileName[name_len + 1] = 0x04; 
		name_len2 =
		    cifs_strtoUCS((wchar_t *) & pSMB->
				  OldFileName[name_len + 2], toName, 530,
				  nls_codepage);
		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;
		name_len2 *= 2;	/* convert to bytes */
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fromName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->OldFileName, fromName, name_len);
		name_len2 = strnlen(toName, 530);
		name_len2++;	/* trailing null */
		pSMB->OldFileName[name_len] = 0x04;	/* 2nd buffer format */
		strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
		name_len2++;	/* trailing null */
		name_len2++;	/* signature byte */
	}

	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);
	if (rc) {
		cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto winCreateHardLinkRetry;

	return rc;
}

int
CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
			const unsigned char *searchName,
			char *symlinkinfo, const int buflen,
			const struct nls_table *nls_codepage)
{
/* SMB_QUERY_FILE_UNIX_LINK */
	TRANSACTION2_QPI_REQ *pSMB = NULL;
	TRANSACTION2_QPI_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	__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, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
	}

	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 */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		__u16 count = le16_to_cpu(pSMBr->DataCount);
		if ((pSMBr->ByteCount < 2) || (data_offset > 512))
		/* BB also check enough total bytes returned */
			rc = -EIO;	/* bad smb */
		else {
			if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
				name_len = UniStrnlen((wchar_t *) ((char *)
					&pSMBr->hdr.Protocol +data_offset),
					min_t(const int, buflen,count) / 2);
				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 */
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto querySymLinkRetry;
	return rc;
}



int
CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
			const unsigned char *searchName,
			char *symlinkinfo, const int buflen,__u16 fid,
			const struct nls_table *nls_codepage)
{
	int rc = 0;
	int bytes_returned;
	int name_len;
	struct smb_com_transaction_ioctl_req * pSMB;
	struct smb_com_transaction_ioctl_rsp * pSMBr;

	cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	pSMB->TotalParameterCount = 0 ;
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le32(2);
	/* BB find exact data count max from sess structure BB */
	pSMB->MaxDataCount = cpu_to_le32(4000);
	pSMB->MaxSetupCount = 4;
	pSMB->Reserved = 0;
	pSMB->ParameterOffset = 0;
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 4;
	pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
	pSMB->IsFsctl = 1; /* FSCTL */
	pSMB->IsRootFlag = 0;
	pSMB->Fid = fid; /* file handle always le */
	pSMB->ByteCount = 0;

	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
	} else {		/* decode response */
		__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)) {
		/* could also validate reparse tag && better check name length */
				struct reparse_data * reparse_buf = (struct reparse_data *)
					((char *)&pSMBr->hdr.Protocol + data_offset);
				if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
					name_len = UniStrnlen((wchar_t *)
							(reparse_buf->LinkNamesBuf + 
							reparse_buf->TargetNameOffset),
							min(buflen/2, reparse_buf->TargetNameLen / 2)); 
					cifs_strfromUCS_le(symlinkinfo,
						(wchar_t *) (reparse_buf->LinkNamesBuf + 
						reparse_buf->TargetNameOffset),
						name_len, nls_codepage);
				} else { /* ASCII names */
					strncpy(symlinkinfo,reparse_buf->LinkNamesBuf + 
						reparse_buf->TargetNameOffset, 
						min_t(const int, buflen, reparse_buf->TargetNameLen));
				}
			} else {
				rc = -EIO;
				cFYI(1,("Invalid return data count on get reparse info ioctl"));
			}
			symlinkinfo[buflen] = 0; /* just in case so the caller
					does not go off the end of the buffer */
			cFYI(1,("readlink result - %s ",symlinkinfo));
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	/* Note: On -EAGAIN error only caller can retry on handle based calls
		since file handle passed in no longer valid */

	return rc;
}

int
CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
		 const unsigned char *searchName,
		 FILE_ALL_INFO * pFindData,
		 const struct nls_table *nls_codepage)
{
/* level 263 SMB_QUERY_FILE_ALL_INFO */
	TRANSACTION2_QPI_REQ *pSMB = NULL;
	TRANSACTION2_QPI_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	__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 =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
	}

	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 */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		/* BB also check enough total bytes returned */
		/* BB we need to improve the validity checking
		of these trans2 responses */
		if ((pSMBr->ByteCount < 40) || (data_offset > 512)) 
			rc = -EIO;	/* bad smb */
		else if (pFindData){
			memcpy((char *) pFindData,
			       (char *) &pSMBr->hdr.Protocol +
			       data_offset, sizeof (FILE_ALL_INFO));
		} else
		    rc = -ENOMEM;
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto QPathInfoRetry;

	return rc;
}

int
CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
		     const unsigned char *searchName,
		     FILE_UNIX_BASIC_INFO * pFindData,
		     const struct nls_table *nls_codepage)
{
/* SMB_QUERY_FILE_UNIX_BASIC */
	TRANSACTION2_QPI_REQ *pSMB = NULL;
	TRANSACTION2_QPI_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned = 0;
	int name_len;
	__u16 params, byte_count;

	cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
UnixQPathInfoRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
	}

	params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */ ;
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le16(2);
	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxDataCount = cpu_to_le16(4000); 
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	pSMB->ParameterOffset = cpu_to_le16(offsetof(
        struct smb_com_transaction2_qpi_req ,InformationLevel) - 4);
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
	byte_count = params + 1 /* pad */ ;
	pSMB->TotalParameterCount = cpu_to_le16(params);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
	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 */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		/* BB also check if enough total bytes returned */
		if ((pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO)) || 
			(data_offset > 512) || 
			(data_offset < sizeof(struct smb_hdr))) {
			cFYI(1,("UnixQPathinfo invalid data offset %d bytes returned %d",
					(int)data_offset,bytes_returned));
			rc = -EIO;	/* bad smb */
		} else {
			memcpy((char *) pFindData,
			       (char *) &pSMBr->hdr.Protocol +
			       data_offset,
			       sizeof (FILE_UNIX_BASIC_INFO));
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto UnixQPathInfoRetry;

	return rc;
}

int
CIFSFindSingle(const int xid, struct cifsTconInfo *tcon,
	       const char *searchName, FILE_ALL_INFO * findData,
	       const struct nls_table *nls_codepage)
{
/* level 257 SMB_ */
	TRANSACTION2_FFIRST_REQ *pSMB = NULL;
	TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	__u16 params, byte_count;

	cFYI(1, ("In FindUnique"));
findUniqueRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
	}

	params = 12 + name_len /* includes null */ ;
	pSMB->TotalDataCount = 0;	/* no EAs */
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(4000);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	pSMB->ParameterOffset = cpu_to_le16(
        offsetof(struct smb_com_transaction2_ffirst_req,InformationLevel) - 4);
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 1;	/* one byte, no need to le convert */
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
	byte_count = params + 1 /* pad */ ;
	pSMB->TotalParameterCount = cpu_to_le16(params);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->SearchAttributes =
	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
			ATTR_DIRECTORY);
	pSMB->SearchCount = cpu_to_le16(16);	/* BB increase */
	pSMB->SearchFlags = cpu_to_le16(1);
	pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO);
	pSMB->SearchStorageType = 0;	/* BB what should we set this to? BB */
	pSMB->hdr.smb_buf_length += 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 FindFileDirInfo = %d", rc));
	} else {		/* decode response */

		/* BB fill in */
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto findUniqueRetry;

	return rc;
}

int
CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
	      const char *searchName, FILE_DIRECTORY_INFO * findData,
	      T2_FFIRST_RSP_PARMS * findParms,
	      const struct nls_table *nls_codepage, int *pUnicodeFlag,
	      int *pUnixFlag)
{
/* level 257 SMB_ */
	TRANSACTION2_FFIRST_REQ *pSMB = NULL;
	TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
	char *response_data;
	int rc = 0;
	int bytes_returned;
	int name_len;
	__u16 params, byte_count;

	cFYI(1, ("In FindFirst"));
findFirstRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
	}

	params = 12 + name_len /* includes null */ ;
	pSMB->TotalDataCount = 0;	/* no EAs */
	pSMB->MaxParameterCount = cpu_to_le16(10);
	pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
					  MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	byte_count = params + 1 /* pad */ ;
	pSMB->TotalParameterCount = cpu_to_le16(params);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->ParameterOffset = cpu_to_le16(offsetof(struct 
        smb_com_transaction2_ffirst_req, SearchAttributes) - 4);
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 1;	/* one byte no need to make endian neutral */
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
	pSMB->SearchAttributes =
	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
			ATTR_DIRECTORY);
	pSMB->SearchCount = cpu_to_le16(CIFS_MAX_MSGSIZE / sizeof (FILE_DIRECTORY_INFO));	/* should this be shrunk even more ? */
	pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);

	/* test for Unix extensions */
	if (tcon->ses->capabilities & CAP_UNIX) {
		pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_UNIX);
		*pUnixFlag = TRUE;
	} else {
		pSMB->InformationLevel =
		    cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO);
		*pUnixFlag = FALSE;
	}
	pSMB->SearchStorageType = 0;	/* BB what should we set this to? It is not clear if it matters BB */
	pSMB->hdr.smb_buf_length += 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) {		/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */
		cFYI(1, ("Error in FindFirst = %d", rc));
	} else {		/* decode response */
		/* BB add safety checks for these memcpys */
		if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
			*pUnicodeFlag = TRUE;
		else
			*pUnicodeFlag = FALSE;
		memcpy(findParms,
		       (char *) &pSMBr->hdr.Protocol +
		       le16_to_cpu(pSMBr->ParameterOffset),
		       sizeof (T2_FFIRST_RSP_PARMS));
		response_data =
		    (char *) &pSMBr->hdr.Protocol +
		    le16_to_cpu(pSMBr->DataOffset);
		memcpy(findData, response_data, le16_to_cpu(pSMBr->DataCount));
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto findFirstRetry;

	return rc;
}

int
CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
		FILE_DIRECTORY_INFO * findData, T2_FNEXT_RSP_PARMS * findParms,
		const __u16 searchHandle, char * resume_file_name, int name_len,
		__u32 resume_key, int *pUnicodeFlag, int *pUnixFlag)
{
/* level 257 SMB_ */
	TRANSACTION2_FNEXT_REQ *pSMB = NULL;
	TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
	char *response_data;
	int rc = 0;
	int bytes_returned;
	__u16 params, byte_count;

	cFYI(1, ("In FindNext"));

	if(resume_file_name == NULL) {
		return -EIO;
	}
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	params = 14;	/* includes 2 bytes of null string, converted to LE below */
	byte_count = 0;
	pSMB->TotalDataCount = 0;	/* no EAs */
	pSMB->MaxParameterCount = cpu_to_le16(8);
	pSMB->MaxDataCount =
	    cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	pSMB->ParameterOffset =  cpu_to_le16(offsetof(
        struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
	pSMB->SearchHandle = searchHandle;	/* always kept as le */
	findParms->SearchCount = 0;	/* set to zero in case of error */
	pSMB->SearchCount =
	    cpu_to_le16(CIFS_MAX_MSGSIZE / sizeof (FILE_DIRECTORY_INFO));
	/* test for Unix extensions */
	if (tcon->ses->capabilities & CAP_UNIX) {
		pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_UNIX);
		*pUnixFlag = TRUE;
	} else {
		pSMB->InformationLevel =
		    cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO);
		*pUnixFlag = FALSE;
	}
	pSMB->ResumeKey = resume_key;
	pSMB->SearchFlags =
	    cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
	/* BB add check to make sure we do not cross end of smb */
	if(name_len < CIFS_MAX_MSGSIZE) {
		memcpy(pSMB->ResumeFileName, resume_file_name, name_len);
		byte_count += name_len;
	}
	params += name_len;
	byte_count = params + 1 /* pad */ ;
	pSMB->TotalParameterCount = cpu_to_le16(params);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	/* BB improve error handling here */
	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) {
		if (rc == -EBADF)
			rc = 0;	/* search probably was closed at end of search above */
		else
			cFYI(1, ("FindNext returned = %d", rc));
	} else {		/* decode response */
		/* BB add safety checks for these memcpys */
		if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
			*pUnicodeFlag = TRUE;
		else
			*pUnicodeFlag = FALSE;
		memcpy(findParms,
		       (char *) &pSMBr->hdr.Protocol +
		       le16_to_cpu(pSMBr->ParameterOffset),
		       sizeof (T2_FNEXT_RSP_PARMS));
		response_data =
		    (char *) &pSMBr->hdr.Protocol +
		    le16_to_cpu(pSMBr->DataOffset);
		memcpy(findData, response_data, le16_to_cpu(pSMBr->DataCount));
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	/* Note: On -EAGAIN error only caller can retry on handle based calls
		since file handle passed in no longer valid */

	return rc;
}

int
CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle)
{
	int rc = 0;
	FINDCLOSE_REQ *pSMB = NULL;
	CLOSE_RSP *pSMBr = NULL;
	int bytes_returned;

	cFYI(1, ("In CIFSSMBFindClose"));
	rc = smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	/* no sense returning error if session restarted
		file handle has been closed */
	if(rc == -EAGAIN)
		return 0;
	if (rc)
		return rc;

	pSMB->FileID = searchHandle;
	pSMB->ByteCount = 0;
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cERROR(1, ("Send error in FindClose = %d", rc));
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	/* Since session is dead, search handle closed on server already */
	if (rc == -EAGAIN)
		rc = 0;

	return rc;
}

int
CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
		const unsigned char *searchName,
		unsigned char **targetUNCs,
		unsigned int *number_of_UNC_in_array,
		const struct nls_table *nls_codepage)
{
/* TRANS2_GET_DFS_REFERRAL */
	TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
	TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
	struct dfs_referral_level_3 * referrals = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	unsigned int i;
	char * temp;
	__u16 params, byte_count;
	*number_of_UNC_in_array = 0;
	*targetUNCs = NULL;

	cFYI(1, ("In GetDFSRefer the path %s", searchName));
	if (ses == NULL)
		return -ENODEV;
getDFSRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	pSMB->hdr.Tid = ses->ipc_tid;
	pSMB->hdr.Uid = ses->Suid;
	if (ses->capabilities & CAP_STATUS32) {
		pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
	}
	if (ses->capabilities & CAP_DFS) {
		pSMB->hdr.Flags2 |= SMBFLG2_DFS;
	}

	if (ses->capabilities & CAP_UNICODE) {
		pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->RequestFileName,
				  searchName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->RequestFileName, searchName, name_len);
	}

	params = 2 /* level */  + name_len /*includes null */ ;
	pSMB->TotalDataCount = 0;
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->MaxParameterCount = 0;
	pSMB->MaxDataCount = cpu_to_le16(4000);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	pSMB->ParameterOffset = cpu_to_le16(offsetof(
        struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
	byte_count = params + 3 /* pad */ ;
	pSMB->ParameterCount = cpu_to_le16(params);
	pSMB->TotalParameterCount = pSMB->ParameterCount;
	pSMB->MaxReferralLevel = cpu_to_le16(3);
	pSMB->hdr.smb_buf_length += byte_count;
	pSMB->ByteCount = cpu_to_le16(byte_count);

	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("Send error in GetDFSRefer = %d", rc));
	} else {		/* decode response */
/* BB Add logic to parse referrals here */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		__u16 data_count = le16_to_cpu(pSMBr->DataCount);
		cFYI(1,
		     ("Decoding GetDFSRefer response.  BCC: %d  Offset %d",
		      pSMBr->ByteCount, data_offset));
		if ((pSMBr->ByteCount < 17) || (data_offset > 512))	/* BB also check enough total bytes returned */
			rc = -EIO;	/* bad smb */
		else {
			referrals = 
			    (struct dfs_referral_level_3 *) 
					(8 /* sizeof start of data block */ +
					data_offset +
					(char *) &pSMBr->hdr.Protocol); 
			cFYI(1,("num_referrals: %d dfs flags: 0x%x ... \nfor referral one refer size: 0x%x srv type: 0x%x refer flags: 0x%x ttl: 0x%x",
				le16_to_cpu(pSMBr->NumberOfReferrals),le16_to_cpu(pSMBr->DFSFlags), le16_to_cpu(referrals->ReferralSize),le16_to_cpu(referrals->ServerType),le16_to_cpu(referrals->ReferralFlags),le16_to_cpu(referrals->TimeToLive)));
			/* BB This field is actually two bytes in from start of
			   data block so we could do safety check that DataBlock
			   begins at address of pSMBr->NumberOfReferrals */
			*number_of_UNC_in_array = le16_to_cpu(pSMBr->NumberOfReferrals);

			/* BB Fix below so can return more than one referral */
			if(*number_of_UNC_in_array > 1)
				*number_of_UNC_in_array = 1;

			/* get the length of the strings describing refs */
			name_len = 0;
			for(i=0;i<*number_of_UNC_in_array;i++) {
				/* make sure that DfsPathOffset not past end */
				__u16 offset = le16_to_cpu(referrals->DfsPathOffset);
				if (offset > data_count) {
					/* if invalid referral, stop here and do 
					not try to copy any more */
					*number_of_UNC_in_array = i;
					break;
				} 
				temp = ((char *)referrals) + offset;

				if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
					name_len += UniStrnlen((wchar_t *)temp,data_count);
				} else {
					name_len += strnlen(temp,data_count);
				}
				referrals++;
				/* BB add check that referral pointer does not fall off end PDU */
				
			}
			/* BB add check for name_len bigger than bcc */
			*targetUNCs = 
				kmalloc(name_len+1+ (*number_of_UNC_in_array),GFP_KERNEL);
			if(*targetUNCs == NULL) {
				rc = -ENOMEM;
				goto GetDFSRefExit;
			}
			/* copy the ref strings */
			referrals =  
			    (struct dfs_referral_level_3 *) 
					(8 /* sizeof data hdr */ +
					data_offset + 
					(char *) &pSMBr->hdr.Protocol);

			for(i=0;i<*number_of_UNC_in_array;i++) {
				temp = ((char *)referrals) + le16_to_cpu(referrals->DfsPathOffset);
				if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
					cifs_strfromUCS_le(*targetUNCs,
						(wchar_t *) temp, name_len, nls_codepage);
				} else {
					strncpy(*targetUNCs,temp,name_len);
				}
				/*  BB update target_uncs pointers */
				referrals++;
			}
			temp = *targetUNCs;
			temp[name_len] = 0;
		}

	}
GetDFSRefExit:
	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto getDFSRetry;

	return rc;
}

int
CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon,
	       struct kstatfs *FSData, const struct nls_table *nls_codepage)
{
/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
	TRANSACTION2_QFSI_REQ *pSMB = NULL;
	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
	FILE_SYSTEM_INFO *response_data;
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, byte_count;

	cFYI(1, ("In QFSInfo"));
QFSInfoRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	params = 2;	/* level */
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(1000);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	byte_count = params + 1 /* pad */ ;
	pSMB->TotalParameterCount = cpu_to_le16(params);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->ParameterOffset = cpu_to_le16(offsetof(
        struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
	pSMB->hdr.smb_buf_length += 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) {
		cERROR(1, ("Send error in QFSInfo = %d", rc));
	} else {		/* decode response */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		cFYI(1,
		     ("Decoding qfsinfo response.  BCC: %d  Offset %d",
		      pSMBr->ByteCount, data_offset));
		if ((pSMBr->ByteCount < 24) || (data_offset > 512))	/* BB also check enough total bytes returned */
			rc = -EIO;	/* bad smb */
		else {
			response_data =
			    (FILE_SYSTEM_INFO
			     *) (((char *) &pSMBr->hdr.Protocol) +
				 data_offset);
			FSData->f_bsize =
			    le32_to_cpu(response_data->BytesPerSector) *
			    le32_to_cpu(response_data->
					SectorsPerAllocationUnit);
			FSData->f_blocks =
			    le64_to_cpu(response_data->TotalAllocationUnits);
			FSData->f_bfree = FSData->f_bavail =
			    le64_to_cpu(response_data->FreeAllocationUnits);
			cFYI(1,
			     ("Blocks: %lld  Free: %lld Block size %ld",
			      (unsigned long long)FSData->f_blocks,
			      (unsigned long long)FSData->f_bfree,
			      FSData->f_bsize));
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto QFSInfoRetry;

	return rc;
}

int
CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon,
			const struct nls_table *nls_codepage)
{
/* level 0x105  SMB_QUERY_FILE_SYSTEM_INFO */
	TRANSACTION2_QFSI_REQ *pSMB = NULL;
	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
	FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, byte_count;

	cFYI(1, ("In QFSAttributeInfo"));
QFSAttributeRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	params = 2;	/* level */
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(1000);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	byte_count = params + 1 /* pad */ ;
	pSMB->TotalParameterCount = cpu_to_le16(params);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->ParameterOffset = cpu_to_le16(offsetof(
        struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
	pSMB->hdr.smb_buf_length += 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) {
		cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
	} else {		/* decode response */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		if ((pSMBr->ByteCount < 13) || (data_offset > 512)) {	/* BB also check enough bytes returned */
			rc = -EIO;	/* bad smb */
		} else {
			response_data =
			    (FILE_SYSTEM_ATTRIBUTE_INFO
			     *) (((char *) &pSMBr->hdr.Protocol) +
				 data_offset);
			memcpy(&tcon->fsAttrInfo, response_data,
			       sizeof (FILE_SYSTEM_ATTRIBUTE_INFO));
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto QFSAttributeRetry;

	return rc;
}

int
CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon,
		     const struct nls_table *nls_codepage)
{
/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
	TRANSACTION2_QFSI_REQ *pSMB = NULL;
	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
	FILE_SYSTEM_DEVICE_INFO *response_data;
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, byte_count;

	cFYI(1, ("In QFSDeviceInfo"));
QFSDeviceRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	params = 2;	/* level */
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(1000);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	byte_count = params + 1 /* pad */ ;
	pSMB->TotalParameterCount = cpu_to_le16(params);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->ParameterOffset = cpu_to_le16(offsetof(
        struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);

	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
	pSMB->hdr.smb_buf_length += 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 QFSDeviceInfo = %d", rc));
	} else {		/* decode response */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		if ((pSMBr->ByteCount < sizeof (FILE_SYSTEM_DEVICE_INFO))
                 || (data_offset > 512))
			rc = -EIO;	/* bad smb */
		else {
			response_data =
			    (FILE_SYSTEM_DEVICE_INFO
			     *) (((char *) &pSMBr->hdr.Protocol) +
				 data_offset);
			memcpy(&tcon->fsDevInfo, response_data,
			       sizeof (FILE_SYSTEM_DEVICE_INFO));
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto QFSDeviceRetry;

	return rc;
}

int
CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon,
		   const struct nls_table *nls_codepage)
{
/* level 0x200  SMB_QUERY_CIFS_UNIX_INFO */
	TRANSACTION2_QFSI_REQ *pSMB = NULL;
	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
	FILE_SYSTEM_UNIX_INFO *response_data;
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, byte_count;

	cFYI(1, ("In QFSUnixInfo"));
QFSUnixRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	params = 2;	/* level */
	pSMB->TotalDataCount = 0;
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(100);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	byte_count = params + 1 /* pad */ ;
	pSMB->ParameterCount = cpu_to_le16(params);
	pSMB->TotalParameterCount = pSMB->ParameterCount;
	pSMB->ParameterOffset = cpu_to_le16(offsetof(struct 
        smb_com_transaction2_qfsi_req, InformationLevel) - 4);
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
	pSMB->hdr.smb_buf_length += 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) {
		cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
	} else {		/* decode response */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		if ((pSMBr->ByteCount < 13) || (data_offset > 512)) {
			rc = -EIO;	/* bad smb */
		} else {
			response_data =
			    (FILE_SYSTEM_UNIX_INFO
			     *) (((char *) &pSMBr->hdr.Protocol) +
				 data_offset);
			memcpy(&tcon->fsUnixInfo, response_data,
			       sizeof (FILE_SYSTEM_UNIX_INFO));
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto QFSUnixRetry;


	return rc;
}

/* We can not use write of zero bytes trick to 
   set file size due to need for large file support.  Also note that 
   this SetPathInfo is preferred to SetFileInfo based method in next 
   routine which is only needed to work around a sharing violation bug
   in Samba which this routine can run into */

int
CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
	      __u64 size, int SetAllocation, const struct nls_table *nls_codepage)
{
	struct smb_com_transaction2_spi_req *pSMB = NULL;
	struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
	struct file_end_of_file_info *parm_data;
	int name_len;
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, byte_count, data_count, param_offset, offset;

	cFYI(1, ("In SetEOF"));
SetEOFRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fileName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, fileName, name_len);
	}
	params = 6 + name_len;
	data_count = sizeof (struct file_end_of_file_info);
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(1000);	/* BB find max SMB size from sess */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	param_offset = offsetof(struct smb_com_transaction2_spi_req,
                                     InformationLevel) - 4;
	offset = param_offset + params;
	if(SetAllocation) {
        	if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
	            pSMB->InformationLevel =
                	cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
        	else
	            pSMB->InformationLevel =
        	        cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
	} else /* Set File Size */  {    
	    if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
		    pSMB->InformationLevel =
		        cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
	    else
		    pSMB->InformationLevel =
		        cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
	}

	parm_data =
	    (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
				       offset);
	pSMB->ParameterOffset = cpu_to_le16(param_offset);
	pSMB->DataOffset = cpu_to_le16(offset);
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
	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;
	parm_data->FileSize = cpu_to_le64(size);
	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, ("SetPathInfo (file size) returned %d", rc));
	}

	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto SetEOFRetry;

	return rc;
}

int
CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, 
                   __u16 fid, __u32 pid_of_opener, int SetAllocation)
{
	struct smb_com_transaction2_sfi_req *pSMB  = NULL;
	struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
	char *data_offset;
	struct file_end_of_file_info *parm_data;
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, param_offset, offset, byte_count, count;

	cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
			(long long)size));
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
    
	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;	

	count = sizeof(struct file_end_of_file_info);
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(1000);	/* BB find max SMB PDU from sess */
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
	byte_count = 3 /* pad */  + params + count;
	pSMB->DataCount = cpu_to_le16(count);
	pSMB->ParameterCount = cpu_to_le16(params);
	pSMB->TotalDataCount = pSMB->DataCount;
	pSMB->TotalParameterCount = pSMB->ParameterCount;
	pSMB->ParameterOffset = cpu_to_le16(param_offset);
	parm_data =
		(struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
			offset);
	pSMB->DataOffset = cpu_to_le16(offset);
	parm_data->FileSize = cpu_to_le64(size);
	pSMB->Fid = fid;
	if(SetAllocation) {
		if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
			pSMB->InformationLevel =
				cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
		else
			pSMB->InformationLevel =
				cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
	} else /* Set File Size */  {    
	    if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
		    pSMB->InformationLevel =
		        cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
	    else
		    pSMB->InformationLevel =
		        cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
	}
	pSMB->Reserved4 = 0;
	pSMB->hdr.smb_buf_length += 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 SetFileInfo (SetFileSize) = %d",
		      rc));
	}

	if (pSMB)
		cifs_buf_release(pSMB);

	/* Note: On -EAGAIN error only caller can retry on handle based calls 
		since file handle passed in no longer valid */

	return rc;
}

int
CIFSSMBSetTimes(const int xid, struct cifsTconInfo *tcon, const char *fileName,
		const FILE_BASIC_INFO * data, 
		const struct nls_table *nls_codepage)
{
	TRANSACTION2_SPI_REQ *pSMB = NULL;
	TRANSACTION2_SPI_RSP *pSMBr = NULL;
	int name_len;
	int rc = 0;
	int bytes_returned = 0;
	char *data_offset;
	__u16 params, param_offset, offset, byte_count, count;

	cFYI(1, ("In SetTimes"));

SetTimesRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fileName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, fileName, name_len);
	}

	params = 6 + name_len;
	count = sizeof (FILE_BASIC_INFO);
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(1000);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	param_offset = offsetof(struct smb_com_transaction2_spi_req,
                                     InformationLevel) - 4;
	offset = param_offset + params;
	data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
	pSMB->ParameterOffset = cpu_to_le16(param_offset);
	pSMB->DataOffset = cpu_to_le16(offset);
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
	byte_count = 3 /* pad */  + params + count;

	pSMB->DataCount = cpu_to_le16(count);
	pSMB->ParameterCount = cpu_to_le16(params);
	pSMB->TotalDataCount = pSMB->DataCount;
	pSMB->TotalParameterCount = pSMB->ParameterCount;
	if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
	else
		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
	pSMB->Reserved4 = 0;
	pSMB->hdr.smb_buf_length += byte_count;
	memcpy(data_offset, data, sizeof (FILE_BASIC_INFO));
	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, ("SetPathInfo (times) returned %d", rc));
	}

	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto SetTimesRetry;

	return rc;
}

int
CIFSSMBSetTimesLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
		FILE_INFO_STANDARD * data, const struct nls_table *nls_codepage)
{
	TRANSACTION2_SPI_REQ *pSMB = NULL;
	TRANSACTION2_SPI_RSP *pSMBr = NULL;
	int name_len;
	int rc = 0;
	int bytes_returned = 0;
	char *data_offset;
	__u16 params, param_offset, count, offset, byte_count;

	cFYI(1, ("In SetTimesLegacy"));

SetTimesRetryLegacy:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fileName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, fileName, name_len);
	}
/* BB fixme - we have to map to FILE_STANDARD_INFO (level 1 info
	in parent function, from the better and ususal FILE_BASIC_INFO */
	params = 6 + name_len;
	count = sizeof (FILE_INFO_STANDARD);
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(1000);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	param_offset = offsetof(struct smb_com_transaction2_spi_req,
                                     InformationLevel) - 4;
	offset = param_offset + params;
	data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
	pSMB->ParameterOffset = cpu_to_le16(param_offset);
	pSMB->DataOffset = cpu_to_le16(offset);
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
	byte_count = 3 /* pad */  + params + count;

	pSMB->DataCount = cpu_to_le16(count);
	pSMB->ParameterCount = cpu_to_le16(params);
	pSMB->TotalDataCount = pSMB->DataCount;
	pSMB->TotalParameterCount = pSMB->ParameterCount;
	/* I doubt that passthrough levels apply to this old
	preNT info level */
/*	if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
	else*/
		pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
	pSMB->Reserved4 = 0;
	pSMB->hdr.smb_buf_length += byte_count;
	memcpy(data_offset, data, sizeof (FILE_INFO_STANDARD));
	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, ("SetPathInfo (times legacy) returned %d", rc));
	}

	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto SetTimesRetryLegacy;

	return rc;
}

int
CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon,
		    char *fileName, __u64 mode, __u64 uid, __u64 gid,
		    dev_t device, const struct nls_table *nls_codepage)
{
	TRANSACTION2_SPI_REQ *pSMB = NULL;
	TRANSACTION2_SPI_RSP *pSMBr = NULL;
	int name_len;
	int rc = 0;
	int bytes_returned = 0;
	FILE_UNIX_BASIC_INFO *data_offset;
	__u16 params, param_offset, offset, count, byte_count;

	cFYI(1, ("In SetUID/GID/Mode"));
setPermsRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fileName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, fileName, name_len);
	}

	params = 6 + name_len;
	count = sizeof (FILE_UNIX_BASIC_INFO);
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = cpu_to_le16(1000);	/* BB find exact max SMB PDU from sess structure BB */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	param_offset = offsetof(struct smb_com_transaction2_spi_req,
                                     InformationLevel) - 4;
	offset = param_offset + params;
	data_offset =
	    (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
				      offset);
	pSMB->DataOffset = cpu_to_le16(offset);
	pSMB->ParameterOffset = cpu_to_le16(param_offset);
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
	byte_count = 3 /* pad */  + params + count;
	pSMB->ParameterCount = cpu_to_le16(params);
	pSMB->DataCount = cpu_to_le16(count);
	pSMB->TotalParameterCount = pSMB->ParameterCount;
	pSMB->TotalDataCount = pSMB->DataCount;
	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
	pSMB->Reserved4 = 0;
	pSMB->hdr.smb_buf_length += byte_count;
	data_offset->Uid = cpu_to_le64(uid);
	data_offset->Gid = cpu_to_le64(gid);
	/* better to leave device as zero when it is  */
	data_offset->DevMajor = cpu_to_le64(MAJOR(device));
	data_offset->DevMinor = cpu_to_le64(MINOR(device));
	data_offset->Permissions = cpu_to_le64(mode);
    
	if(S_ISREG(mode))
		data_offset->Type = cpu_to_le32(UNIX_FILE);
	else if(S_ISDIR(mode))
		data_offset->Type = cpu_to_le32(UNIX_DIR);
	else if(S_ISLNK(mode))
		data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
	else if(S_ISCHR(mode))
		data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
	else if(S_ISBLK(mode))
		data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
	else if(S_ISFIFO(mode))
		data_offset->Type = cpu_to_le32(UNIX_FIFO);
	else if(S_ISSOCK(mode))
		data_offset->Type = cpu_to_le32(UNIX_SOCKET);


	pSMB->ByteCount = cpu_to_le16(byte_count);
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("SetPathInfo (perms) returned %d", rc));
	}

	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto setPermsRetry;
	return rc;
}

int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, 
			const int notify_subdirs, const __u16 netfid,
			__u32 filter, const struct nls_table *nls_codepage)
{
	int rc = 0;
	struct smb_com_transaction_change_notify_req * pSMB = NULL;
	struct smb_com_transaction_change_notify_rsp * pSMBr = NULL;
	int bytes_returned;

	cFYI(1, ("In CIFSSMBNotify for file handle %d",(int)netfid));
	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
                      (void **) &pSMBr);
	if (rc)
		return rc;

	pSMB->TotalParameterCount = 0 ;
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le32(2);
	/* BB find exact data count max from sess structure BB */
	pSMB->MaxDataCount = 0; /* same in little endian or be */
	pSMB->MaxSetupCount = 4;
	pSMB->Reserved = 0;
	pSMB->ParameterOffset = 0;
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 4; /* single byte does not need le conversion */
	pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	if(notify_subdirs)
		pSMB->WatchTree = 1; /* one byte - no le conversion needed */
	pSMB->Reserved2 = 0;
	pSMB->CompletionFilter = cpu_to_le32(filter);
	pSMB->Fid = netfid; /* file handle always le */
	pSMB->ByteCount = 0;

	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			(struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cFYI(1, ("Error in Notify = %d", rc));
	}
	if (pSMB)
		cifs_buf_release(pSMB);
/*		if (rc == -EAGAIN)
			goto NotifyRetry; */
	return rc;	
}
#ifdef CONFIG_CIFS_XATTR
ssize_t
CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
		 const unsigned char *searchName,
		 char * EAData, size_t buf_size,
		 const struct nls_table *nls_codepage)
{
		/* BB assumes one setup word */
	TRANSACTION2_QPI_REQ *pSMB = NULL;
	TRANSACTION2_QPI_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	struct fea * temp_fea;
	char * temp_ptr;
	__u16 params, byte_count;

	cFYI(1, ("In Query All EAs path %s", searchName));
QAllEAsRetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {	/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
	}

	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_INFO_QUERY_ALL_EAS);
	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 QueryAllEAs = %d", rc));
	} else {		/* decode response */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		/* BB also check enough total bytes returned */
		/* BB we need to improve the validity checking
		of these trans2 responses */
		if ((pSMBr->ByteCount < 4) || (data_offset > 512)) 
			rc = -EIO;	/* bad smb */
	   /* else if (pFindData){
			memcpy((char *) pFindData,
			       (char *) &pSMBr->hdr.Protocol +
			       data_offset, kl);
		}*/ else {
			/* check that length of list is not more than bcc */
			/* check that each entry does not go beyond length
			   of list */
			/* check that each element of each entry does not
			   go beyond end of list */
			struct fealist * ea_response_data;
			rc = 0;
			/* validate_trans2_offsets() */
			/* BB to check if(start of smb + data_offset > &bcc+ bcc)*/
			ea_response_data = (struct fealist *)
				(((char *) &pSMBr->hdr.Protocol) +
				data_offset);
			name_len = le32_to_cpu(ea_response_data->list_len);
			cFYI(1,("ea length %d", name_len));
			if(name_len <= 8) {
			/* returned EA size zeroed at top of function */
				cFYI(1,("empty EA list returned from server"));
			} else {
				/* account for ea list len */
				name_len -= 4;
				temp_fea = ea_response_data->list;
				temp_ptr = (char *)temp_fea;
				while(name_len > 0) {
					__u16 value_len;
					name_len -= 4;
					temp_ptr += 4;
					rc += temp_fea->name_len;
				/* account for prefix user. and trailing null */
					rc = rc + 5 + 1; 
					if(rc<(int)buf_size) {
						memcpy(EAData,"user.",5);
						EAData+=5;
						memcpy(EAData,temp_ptr,temp_fea->name_len);
						EAData+=temp_fea->name_len;
						/* null terminate name */
						*EAData = 0;
						EAData = EAData + 1;
					} else if(buf_size == 0) {
						/* skip copy - calc size only */
					} else {
						/* stop before overrun buffer */
						rc = -ERANGE;
						break;
					}
					name_len -= temp_fea->name_len;
					temp_ptr += temp_fea->name_len;
					/* account for trailing null */
					name_len--;
					temp_ptr++;
					value_len = le16_to_cpu(temp_fea->value_len);
					name_len -= value_len;
					temp_ptr += value_len;
					/* BB check that temp_ptr is still within smb BB*/
				/* no trailing null to account for in value len */
					/* go on to next EA */
					temp_fea = (struct fea *)temp_ptr;
				}
			}
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto QAllEAsRetry;

	return (ssize_t)rc;
}

ssize_t CIFSSMBQueryEA(const int xid,struct cifsTconInfo * tcon,
		const unsigned char * searchName,const unsigned char * ea_name,
		unsigned char * ea_value, size_t buf_size, 
		const struct nls_table *nls_codepage)
{
	TRANSACTION2_QPI_REQ *pSMB = NULL;
	TRANSACTION2_QPI_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	struct fea * temp_fea;
	char * temp_ptr;
	__u16 params, byte_count;

	cFYI(1, ("In Query EA path %s", searchName));
QEARetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {	/* BB improve the check for buffer overruns BB */
		name_len = strnlen(searchName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, searchName, name_len);
	}

	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_INFO_QUERY_ALL_EAS);
	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 EA = %d", rc));
	} else {		/* decode response */
		__u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
		/* BB also check enough total bytes returned */
		/* BB we need to improve the validity checking
		of these trans2 responses */
		if ((pSMBr->ByteCount < 4) || (data_offset > 512)) 
			rc = -EIO;	/* bad smb */
	   /* else if (pFindData){
			memcpy((char *) pFindData,
			       (char *) &pSMBr->hdr.Protocol +
			       data_offset, kl);
		}*/ else {
			/* check that length of list is not more than bcc */
			/* check that each entry does not go beyond length
			   of list */
			/* check that each element of each entry does not
			   go beyond end of list */
			struct fealist * ea_response_data;
			rc = -ENOENT;
			/* validate_trans2_offsets() */
			/* BB to check if(start of smb + data_offset > &bcc+ bcc)*/
			ea_response_data = (struct fealist *)
				(((char *) &pSMBr->hdr.Protocol) +
				data_offset);
			name_len = le32_to_cpu(ea_response_data->list_len);
			cFYI(1,("ea length %d", name_len));
			if(name_len <= 8) {
			/* returned EA size zeroed at top of function */
				cFYI(1,("empty EA list returned from server"));
			} else {
				/* account for ea list len */
				name_len -= 4;
				temp_fea = ea_response_data->list;
				temp_ptr = (char *)temp_fea;
				/* loop through checking if we have a matching
				name and then return the associated value */
				while(name_len > 0) {
					__u16 value_len;
					name_len -= 4;
					temp_ptr += 4;
					value_len = le16_to_cpu(temp_fea->value_len);
				/* BB validate that value_len falls within SMB, 
				even though maximum for name_len is 255 */ 
					if(memcmp(temp_fea->name,ea_name,
						  temp_fea->name_len) == 0) {
						/* found a match */
						rc = value_len;
				/* account for prefix user. and trailing null */
						if(rc<=(int)buf_size) {
							memcpy(ea_value,
								temp_fea->name+temp_fea->name_len+1,
								rc);
							/* ea values, unlike ea names,
							are not null terminated */
						} else if(buf_size == 0) {
						/* skip copy - calc size only */
						} else {
							/* stop before overrun buffer */
							rc = -ERANGE;
						}
						break;
					}
					name_len -= temp_fea->name_len;
					temp_ptr += temp_fea->name_len;
					/* account for trailing null */
					name_len--;
					temp_ptr++;
					name_len -= value_len;
					temp_ptr += value_len;
				/* no trailing null to account for in value len */
					/* go on to next EA */
					temp_fea = (struct fea *)temp_ptr;
				}
			} 
		}
	}
	if (pSMB)
		cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto QEARetry;

	return (ssize_t)rc;
}

int
CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
		const char * ea_name, const void * ea_value, 
		const __u16 ea_value_len, const struct nls_table *nls_codepage)
{
	struct smb_com_transaction2_spi_req *pSMB = NULL;
	struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
	struct fealist *parm_data;
	int name_len;
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, param_offset, byte_count, offset, count;

	cFYI(1, ("In SetEA"));
SetEARetry:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifs_strtoUCS((wchar_t *) pSMB->FileName, fileName, 530
				  /* find define for this maxpathcomponent */
				  , nls_codepage);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else {		/* BB improve the check for buffer overruns BB */
		name_len = strnlen(fileName, 530);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, fileName, name_len);
	}

	params = 6 + name_len;

	/* done calculating parms using name_len of file name,
	now use name_len to calculate length of ea name
	we are going to create in the inode xattrs */
	if(ea_name == NULL)
		name_len = 0;
	else
		name_len = strnlen(ea_name,255);

	count = sizeof(*parm_data) + ea_value_len + name_len + 1;
	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;
	pSMB->InformationLevel =
		cpu_to_le16(SMB_SET_FILE_EA);

	parm_data =
		(struct fealist *) (((char *) &pSMB->hdr.Protocol) +
				       offset);
	pSMB->ParameterOffset = cpu_to_le16(param_offset);
	pSMB->DataOffset = cpu_to_le16(offset);
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
	byte_count = 3 /* pad */  + params + count;
	pSMB->DataCount = cpu_to_le16(count);
	parm_data->list_len = cpu_to_le32(count);
	parm_data->list[0].EA_flags = 0;
	/* we checked above that name len is less than 255 */
	parm_data->list[0].name_len = (__u8)name_len;;
	/* EA names are always ASCII */
	strncpy(parm_data->list[0].name,ea_name,name_len);
	parm_data->list[0].name[name_len] = 0;
	parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
	/* caller ensures that ea_value_len is less than 64K but
	we need to ensure that it fits within the smb */

	/*BB add length check that it would fit in negotiated SMB buffer size BB */
	/* if(ea_value_len > buffer_size - 512 (enough for header)) */
	if(ea_value_len)
		memcpy(parm_data->list[0].name+name_len+1,ea_value,ea_value_len);

	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, ("SetPathInfo (EA) returned %d", rc));
	}

	if (pSMB)
		cifs_buf_release(pSMB);

	if (rc == -EAGAIN)
		goto SetEARetry;

	return rc;
}

#endif
