Browse Source

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

Signed-off-by: Steve French <sfrench@us.ibm.com>

Steve French 19 years ago
parent
commit
a9d02ad490
5 changed files with 330 additions and 2 deletions
  1. 73 0
      fs/cifs/cifspdu.h
  2. 8 0
      fs/cifs/cifsproto.h
  3. 215 0
      fs/cifs/cifssmb.c
  4. 7 0
      fs/cifs/dir.c
  5. 27 2
      fs/cifs/file.c

+ 73 - 0
fs/cifs/cifspdu.h

@@ -40,6 +40,7 @@
 #define SMB_COM_SETATTR               0x09 /* trivial response */
 #define SMB_COM_SETATTR               0x09 /* trivial response */
 #define SMB_COM_LOCKING_ANDX          0x24 /* trivial response */
 #define SMB_COM_LOCKING_ANDX          0x24 /* trivial response */
 #define SMB_COM_COPY                  0x29 /* trivial rsp, fail filename ignrd*/
 #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_READ_ANDX             0x2E
 #define SMB_COM_WRITE_ANDX            0x2F
 #define SMB_COM_WRITE_ANDX            0x2F
 #define SMB_COM_TRANSACTION2          0x32
 #define SMB_COM_TRANSACTION2          0x32
@@ -625,6 +626,7 @@ typedef struct smb_com_findclose_req {
 } FINDCLOSE_REQ;
 } FINDCLOSE_REQ;
 
 
 /* OpenFlags */
 /* OpenFlags */
+#define REQ_MORE_INFO      0x00000001  /* legacy (OPEN_AND_X) only */
 #define REQ_OPLOCK         0x00000002
 #define REQ_OPLOCK         0x00000002
 #define REQ_BATCHOPLOCK    0x00000004
 #define REQ_BATCHOPLOCK    0x00000004
 #define REQ_OPENDIRONLY    0x00000008
 #define REQ_OPENDIRONLY    0x00000008
@@ -680,6 +682,62 @@ typedef struct smb_com_open_rsp {
 	__u16 ByteCount;	/* bct = 0 */
 	__u16 ByteCount;	/* bct = 0 */
 } OPEN_RSP;
 } 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 {
 typedef struct smb_com_write_req {
 	struct smb_hdr hdr;	/* wct = 14 */
 	struct smb_hdr hdr;	/* wct = 14 */
 	__u8 AndXCommand;
 	__u8 AndXCommand;
@@ -711,6 +769,21 @@ typedef struct smb_com_write_rsp {
 	__u16 ByteCount;
 	__u16 ByteCount;
 } WRITE_RSP;
 } 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 {
 typedef struct smb_com_read_req {
 	struct smb_hdr hdr;	/* wct = 12 */
 	struct smb_hdr hdr;	/* wct = 12 */
 	__u8 AndXCommand;
 	__u8 AndXCommand;

+ 8 - 0
fs/cifs/cifsproto.h

@@ -218,9 +218,17 @@ extern int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
 			const int access_flags, const int omode,
 			const int access_flags, const int omode,
 			__u16 * netfid, int *pOplock, FILE_ALL_INFO *,
 			__u16 * netfid, int *pOplock, FILE_ALL_INFO *,
 			const struct nls_table *nls_codepage, int remap);
 			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,
 extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon,
 			const int smb_file_id);
 			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,
 extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
 			const int netfid, unsigned int count,
 			const int netfid, unsigned int count,
 			const __u64 lseek, unsigned int *nbytes, char **buf);
 			const __u64 lseek, unsigned int *nbytes, char **buf);

+ 215 - 0
fs/cifs/cifssmb.c

@@ -680,6 +680,146 @@ MkDirRetry:
 	return rc;
 	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
 int
 CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
 CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
 	    const char *fileName, const int openDisposition,
 	    const char *fileName, const int openDisposition,
@@ -783,6 +923,81 @@ openRetry:
 	return rc;
 	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
 /* If no buffer passed in, then caller wants to do the copy
 	as in the case of readpages so the SMB buffer must be
 	as in the case of readpages so the SMB buffer must be
 	freed by the caller */
 	freed by the caller */

+ 7 - 0
fs/cifs/dir.c

@@ -184,6 +184,13 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
 			 desiredAccess, CREATE_NOT_DIR,
 			 desiredAccess, CREATE_NOT_DIR,
 			 &fileHandle, &oplock, buf, cifs_sb->local_nls,
 			 &fileHandle, &oplock, buf, cifs_sb->local_nls,
 			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 			 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) {
 	if (rc) {
 		cFYI(1, ("cifs_create returned 0x%x ", rc));
 		cFYI(1, ("cifs_create returned 0x%x ", rc));
 	} else {
 	} else {

+ 27 - 2
fs/cifs/file.c

@@ -256,6 +256,13 @@ int cifs_open(struct inode *inode, struct file *file)
 			 CREATE_NOT_DIR, &netfid, &oplock, buf,
 			 CREATE_NOT_DIR, &netfid, &oplock, buf,
 			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
 			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
 				 & CIFS_MOUNT_MAP_SPECIAL_CHR);
 				 & 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) {
 	if (rc) {
 		cFYI(1, ("cifs_open returned 0x%x ", rc));
 		cFYI(1, ("cifs_open returned 0x%x ", rc));
 		goto out;
 		goto out;
@@ -1210,7 +1217,12 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
 				 open_file->netfid,
 				 open_file->netfid,
 				 current_read_size, *poffset,
 				 current_read_size, *poffset,
 				 &bytes_read, &smb_read_data);
 				 &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;
 			pSMBr = (struct smb_com_read_rsp *)smb_read_data;
 			if (copy_to_user(current_offset, 
 			if (copy_to_user(current_offset, 
 					 smb_read_data + 4 /* RFC1001 hdr */
 					 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,
 				 open_file->netfid,
 				 current_read_size, *poffset,
 				 current_read_size, *poffset,
 				 &bytes_read, &current_offset);
 				 &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 (rc || (bytes_read == 0)) {
 			if (total_read) {
 			if (total_read) {
@@ -1443,7 +1461,14 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
 				open_file->netfid,
 				open_file->netfid,
 				read_size, offset,
 				read_size, offset,
 				&bytes_read, &smb_read_data);
 				&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 (rc== -EAGAIN) {
 				if (smb_read_data) {
 				if (smb_read_data) {
 					cifs_buf_release(smb_read_data);
 					cifs_buf_release(smb_read_data);