]> err.no Git - linux-2.6/commitdiff
[CIFS] Support for legacy servers part 3 - Add support for Open and most
authorSteve French <sfrench@us.ibm.com>
Thu, 25 Aug 2005 06:06:05 +0000 (23:06 -0700)
committerSteve French <sfrench@us.ibm.com>
Thu, 25 Aug 2005 06:06:05 +0000 (23:06 -0700)
of Read support.

Signed-off-by: Steve French <sfrench@us.ibm.com>
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/dir.c
fs/cifs/file.c

index 026c88f486a23f535452bba9935431cba8dd5b50..cf466595b0d400b459dc23f0c18433f00495669d 100644 (file)
@@ -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;
index 28b1ebbd380156796d9397375a75e4fb9fa50e97..c411f2e001aabd641ff406753f5b65cfcfde37f0 100644 (file)
@@ -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);
index 67a6240ff2ba8dc588dff9701498c242f412e7ae..c8ae3ef422baa4372ad0ab4e14d1ab53db2f73ba 100644 (file)
@@ -680,6 +680,146 @@ MkDirRetry:
        return rc;
 }
 
+static __u16 convert_disposition(int disposition)
+{
+       __u16 ofun = 0;
+
+       switch (disposition) {
+               case FILE_SUPERSEDE:
+                       ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
+                       break;
+               case FILE_OPEN:
+                       ofun = SMBOPEN_OAPPEND;
+                       break;
+               case FILE_CREATE:
+                       ofun = SMBOPEN_OCREATE;
+                       break;
+               case FILE_OPEN_IF:
+                       ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
+                       break;
+               case FILE_OVERWRITE:
+                       ofun = SMBOPEN_OTRUNC;
+                       break;
+               case FILE_OVERWRITE_IF:
+                       ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
+                       break;
+               default:
+                       cFYI(1,("unknown disposition %d",disposition));
+                       ofun =  SMBOPEN_OAPPEND; /* regular open */
+       }
+       return ofun;
+}
+
+int
+SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
+           const char *fileName, const int openDisposition,
+           const int access_flags, const int create_options, __u16 * netfid,
+            int *pOplock, FILE_ALL_INFO * pfile_info,
+           const struct nls_table *nls_codepage, int remap)
+{
+       int rc = -EACCES;
+       OPENX_REQ *pSMB = NULL;
+       OPENX_RSP *pSMBr = NULL;
+       int bytes_returned;
+       int name_len;
+       __u16 count;
+
+OldOpenRetry:
+       rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
+                     (void **) &pSMBr);
+       if (rc)
+               return rc;
+
+       pSMB->AndXCommand = 0xFF;       /* none */
+
+       if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+               count = 1;      /* account for one byte pad to word boundary */
+               name_len =
+                  cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
+                                   fileName, PATH_MAX, nls_codepage, remap);
+               name_len++;     /* trailing null */
+               name_len *= 2;
+       } else {                /* BB improve check for buffer overruns BB */
+               count = 0;      /* no pad */
+               name_len = strnlen(fileName, PATH_MAX);
+               name_len++;     /* trailing null */
+               strncpy(pSMB->fileName, fileName, name_len);
+       }
+       if (*pOplock & REQ_OPLOCK)
+               pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
+       else if (*pOplock & REQ_BATCHOPLOCK) {
+               pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
+       }
+       pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
+       /* BB fixme add conversion for access_flags to bits 0 - 2 of mode */
+       /* 0 = read
+          1 = write
+          2 = rw
+          3 = execute
+        */
+       pSMB->Mode = cpu_to_le16(2);
+       pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
+       /* set file as system file if special file such
+          as fifo and server expecting SFU style and
+          no Unix extensions */
+
+        if(create_options & CREATE_OPTION_SPECIAL)
+                pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
+        else
+                pSMB->FileAttributes = cpu_to_le16(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 @@ openRetry:
        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 */
index 5311c50734b0a2056f28cebeda86d9fa5148831e..248ddebd67f4f5b5b5ccacf70428f6fa51bf2562 100644 (file)
@@ -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 {
index 5857d12611e6019a8ab2db69b1cbb2f60d427605..8ae962e7c93f48bdc190fa721acd63e1ae8cce3f 100644 (file)
@@ -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);