Commit a9d02ad4 authored by Steve French's avatar Steve French
Browse files

[CIFS] Support for legacy servers part 3 - Add support for Open and most


of Read support.

Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent e22cb8bc
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
#define SMB_COM_SETATTR               0x09 /* trivial response */
#define SMB_COM_LOCKING_ANDX          0x24 /* trivial response */
#define SMB_COM_COPY                  0x29 /* trivial rsp, fail filename ignrd*/
#define SMB_COM_OPEN_ANDX             0x2D /* Legacy open for old servers */
#define SMB_COM_READ_ANDX             0x2E
#define SMB_COM_WRITE_ANDX            0x2F
#define SMB_COM_TRANSACTION2          0x32
@@ -625,6 +626,7 @@ typedef struct smb_com_findclose_req {
} FINDCLOSE_REQ;

/* OpenFlags */
#define REQ_MORE_INFO      0x00000001  /* legacy (OPEN_AND_X) only */
#define REQ_OPLOCK         0x00000002
#define REQ_BATCHOPLOCK    0x00000004
#define REQ_OPENDIRONLY    0x00000008
@@ -680,6 +682,62 @@ typedef struct smb_com_open_rsp {
	__u16 ByteCount;	/* bct = 0 */
} OPEN_RSP;

/* format of legacy open request */
typedef struct smb_com_openx_req {
	struct smb_hdr	hdr;	/* wct = 15 */
	__u8 AndXCommand;
	__u8 AndXReserved;
	__le16 AndXOffset;
	__le16 OpenFlags;
	__le16 Mode;
	__le16 Sattr; /* search attributes */
	__le16 FileAttributes;  /* dos attrs */
	__le32 CreateTime; /* os2 format */
	__le16 OpenFunction;
	__le32 EndOfFile;
	__le32 Timeout;
	__le32 Reserved;
	__u16  ByteCount;  /* file name follows */
	char   fileName[1];
} OPENX_REQ;

typedef struct smb_com_openx_rsp {
	struct smb_hdr	hdr;	/* wct = 15 */
	__u8 AndXCommand;
	__u8 AndXReserved;
	__le16 AndXOffset;
	__u16  Fid;
	__le16 FileAttributes;
	__le32 LastWriteTime; /* os2 format */
	__le32 EndOfFile;
	__le16 Access;
	__le16 FileType;
	__le16 IPCState;
	__le16 Action;
	__u32  FileId;
	__u16  Reserved;
	__u16  ByteCount;
} OPENX_RSP; 

/* Legacy write request for older servers */
typedef struct smb_com_writex_req {
        struct smb_hdr hdr;     /* wct = 12 */
        __u8 AndXCommand;
        __u8 AndXReserved;
        __le16 AndXOffset;
        __u16 Fid;
        __le32 OffsetLow;
        __u32 Reserved; /* Timeout */
        __le16 WriteMode; /* 1 = write through */
        __le16 Remaining;
        __le16 Reserved2;
        __le16 DataLengthLow;
        __le16 DataOffset;
        __le16 ByteCount;
        __u8 Pad;               /* BB check for whether padded to DWORD boundary and optimum performance here */
        char Data[0];
} WRITEX_REQ;

typedef struct smb_com_write_req {
	struct smb_hdr hdr;	/* wct = 14 */
	__u8 AndXCommand;
@@ -711,6 +769,21 @@ typedef struct smb_com_write_rsp {
	__u16 ByteCount;
} WRITE_RSP;

/* legacy read request for older servers */
typedef struct smb_com_readx_req {
        struct smb_hdr hdr;     /* wct = 10 */
        __u8 AndXCommand;
        __u8 AndXReserved;
        __le16 AndXOffset;
        __u16 Fid;
        __le32 OffsetLow;
        __le16 MaxCount;
        __le16 MinCount;                /* obsolete */
        __le32 Reserved;
        __le16 Remaining;
        __le16 ByteCount;
} READX_REQ;

typedef struct smb_com_read_req {
	struct smb_hdr hdr;	/* wct = 12 */
	__u8 AndXCommand;
+8 −0
Original line number Diff line number Diff line
@@ -218,9 +218,17 @@ extern int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
			const int access_flags, const int omode,
			__u16 * netfid, int *pOplock, FILE_ALL_INFO *,
			const struct nls_table *nls_codepage, int remap);
extern int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
			const char *fileName, const int disposition,
			const int access_flags, const int omode,
			__u16 * netfid, int *pOplock, FILE_ALL_INFO *,
			const struct nls_table *nls_codepage, int remap);
extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon,
			const int smb_file_id);

extern int SMBLegacyRead(const int xid, struct cifsTconInfo *tcon,
			const int netfid, unsigned int count,
			const __u64 lseek, unsigned int *nbytes, char **buf);
extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
			const int netfid, unsigned int count,
			const __u64 lseek, unsigned int *nbytes, char **buf);
+215 −0
Original line number Diff line number Diff line
@@ -680,6 +680,146 @@ CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
	return rc;
}

static __u16 convert_disposition(int disposition)
{
	__u16 ofun = 0;

	switch (disposition) {
		case FILE_SUPERSEDE:
			ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
			break;
		case FILE_OPEN:
			ofun = SMBOPEN_OAPPEND;
			break;
		case FILE_CREATE:
			ofun = SMBOPEN_OCREATE;
			break;
		case FILE_OPEN_IF:
			ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
			break;
		case FILE_OVERWRITE:
			ofun = SMBOPEN_OTRUNC;
			break;
		case FILE_OVERWRITE_IF:
			ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
			break;
		default:
			cFYI(1,("unknown disposition %d",disposition));
			ofun =  SMBOPEN_OAPPEND; /* regular open */
	}
	return ofun;
}

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

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

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

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		count = 1;      /* account for one byte pad to word boundary */
		name_len =
		   cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
				    fileName, PATH_MAX, nls_codepage, remap);
		name_len++;     /* trailing null */
		name_len *= 2;
	} else {                /* BB improve check for buffer overruns BB */
		count = 0;      /* no pad */
		name_len = strnlen(fileName, PATH_MAX);
		name_len++;     /* trailing null */
		strncpy(pSMB->fileName, fileName, name_len);
	}
	if (*pOplock & REQ_OPLOCK)
		pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
	else if (*pOplock & REQ_BATCHOPLOCK) {
		pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
	}
	pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
	/* BB fixme add conversion for access_flags to bits 0 - 2 of mode */
	/* 0 = read
	   1 = write
	   2 = rw
	   3 = execute
        */
	pSMB->Mode = cpu_to_le16(2);
	pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
	/* set file as system file if special file such
	   as fifo and server expecting SFU style and
	   no Unix extensions */

        if(create_options & CREATE_OPTION_SPECIAL)
                pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
        else
                pSMB->FileAttributes = cpu_to_le16(ATTR_NORMAL);

	/* if ((omode & S_IWUGO) == 0)
		pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/
	/*  Above line causes problems due to vfs splitting create into two
	    pieces - need to set mode after file created not while it is
	    being created */

	/* BB FIXME BB */
/*	pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); */
	/* BB FIXME END BB */
	pSMB->OpenFunction = convert_disposition(openDisposition);
	count += name_len;
	pSMB->hdr.smb_buf_length += count;

	pSMB->ByteCount = cpu_to_le16(count);
	/* long_op set to 1 to allow for oplock break timeouts */
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
		         (struct smb_hdr *) pSMBr, &bytes_returned, 1);
	cifs_stats_inc(&tcon->num_opens);
	if (rc) {
		cFYI(1, ("Error in Open = %d", rc));
	} else {
	/* BB verify if wct == 15 */

/*		*pOplock = pSMBr->OplockLevel; */  /* BB take from action field BB */

		*netfid = pSMBr->Fid;   /* cifs fid stays in le */
		/* Let caller know file was created so we can set the mode. */
		/* Do we care about the CreateAction in any other cases? */
	/* BB FIXME BB */
/*		if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
			*pOplock |= CIFS_CREATE_ACTION; */
	/* BB FIXME END */

		if(pfile_info) {
			pfile_info->CreationTime = 0; /* BB convert CreateTime*/
			pfile_info->LastAccessTime = 0; /* BB fixme */
			pfile_info->LastWriteTime = 0; /* BB fixme */
			pfile_info->ChangeTime = 0;  /* BB fixme */
			pfile_info->Attributes = pSMBr->FileAttributes; 
			/* the file_info buf is endian converted by caller */
			pfile_info->AllocationSize = pSMBr->EndOfFile;
			pfile_info->EndOfFile = pSMBr->EndOfFile;
			pfile_info->NumberOfLinks = cpu_to_le32(1);
		}
	}

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

int
CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
	    const char *fileName, const int openDisposition,
@@ -783,6 +923,81 @@ CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
	return rc;
}

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

	cFYI(1,("Legacy read %d bytes fid %d",count,netfid));

	/* field is shorter in legacy read, only 16 bits */
	if(count > 2048)
		count = 2048;  /* BB FIXME make this configurable */

	if(lseek > 0xFFFFFFFF)
		return -EIO; /* can not read that far into file on old server */

	*nbytes = 0;
	rc = smb_init(SMB_COM_READ_ANDX, 10, 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->Remaining = 0;
	pSMB->MaxCount = cpu_to_le16(count);
	pSMB->Reserved = 0; /* Must Be Zero */
	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);
	cifs_stats_inc(&tcon->num_reads);
	if (rc) {
		cERROR(1, ("Send error in legacy read = %d", rc));
	} else {
		int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
		data_length = data_length << 16;
		data_length += le16_to_cpu(pSMBr->DataLength);
		*nbytes = data_length;

		/*check that DataLength would not go beyond end of SMB */
		if ((data_length > CIFSMaxBufSize) || (data_length > count)) {
			cFYI(1,("bad length %d for count %d",data_length,count));
			rc = -EIO;
			*nbytes = 0;
		} else {
			pReadData = (char *) (&pSMBr->hdr.Protocol) +
						le16_to_cpu(pSMBr->DataOffset);
/*                      if(rc = copy_to_user(buf, pReadData, data_length)) {
			 	cERROR(1,("Faulting on read rc = %d",rc));
				rc = -EFAULT;
			}*/ /* can not use copy_to_user when using page cache*/
			if(*buf)
				memcpy(*buf,pReadData,data_length);
		}
	}
	if(*buf)
		cifs_buf_release(pSMB);
	else
		*buf = (char *)pSMB;

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

/* 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 */
+7 −0
Original line number Diff line number Diff line
@@ -184,6 +184,13 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
			 desiredAccess, CREATE_NOT_DIR,
			 &fileHandle, &oplock, buf, cifs_sb->local_nls,
			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
	if(rc == -EIO) {
		/* old server, retry the open legacy style */
		rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
			desiredAccess, CREATE_NOT_DIR,
			&fileHandle, &oplock, buf, cifs_sb->local_nls,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
	} 
	if (rc) {
		cFYI(1, ("cifs_create returned 0x%x ", rc));
	} else {
+27 −2
Original line number Diff line number Diff line
@@ -256,6 +256,13 @@ int cifs_open(struct inode *inode, struct file *file)
			 CREATE_NOT_DIR, &netfid, &oplock, buf,
			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
				 & CIFS_MOUNT_MAP_SPECIAL_CHR);
	if (rc == -EIO) {
		/* Old server, try legacy style OpenX */
		rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
			desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
			cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
				& CIFS_MOUNT_MAP_SPECIAL_CHR);
	}
	if (rc) {
		cFYI(1, ("cifs_open returned 0x%x ", rc));
		goto out;
@@ -1210,7 +1217,12 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
				 open_file->netfid,
				 current_read_size, *poffset,
				 &bytes_read, &smb_read_data);

			if(rc == -EINVAL) {
				rc = SMBLegacyRead(xid, pTcon,
					open_file->netfid,
					current_read_size, *poffset,
					&bytes_read, &smb_read_data);
			}
			pSMBr = (struct smb_com_read_rsp *)smb_read_data;
			if (copy_to_user(current_offset, 
					 smb_read_data + 4 /* RFC1001 hdr */
@@ -1287,6 +1299,12 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
				 open_file->netfid,
				 current_read_size, *poffset,
				 &bytes_read, &current_offset);
			if(rc == -EINVAL) {
				rc = SMBLegacyRead(xid, pTcon,
					open_file->netfid,
					current_read_size, *poffset,
					&bytes_read, &current_offset);
			}
		}
		if (rc || (bytes_read == 0)) {
			if (total_read) {
@@ -1443,7 +1461,14 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
				open_file->netfid,
				read_size, offset,
				&bytes_read, &smb_read_data);
			/* BB need to check return code here */
			if (rc == -EINVAL) {
				rc = SMBLegacyRead(xid, pTcon,
					open_file->netfid,
					read_size, offset,
					&bytes_read, &smb_read_data);
			}

			/* BB more RC checks ? */
			if (rc== -EAGAIN) {
				if (smb_read_data) {
					cifs_buf_release(smb_read_data);