summaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifspdu.h73
-rw-r--r--fs/cifs/cifsproto.h8
-rw-r--r--fs/cifs/cifssmb.c215
-rw-r--r--fs/cifs/dir.c7
-rw-r--r--fs/cifs/file.c29
5 files changed, 330 insertions, 2 deletions
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index 026c88f486a..cf466595b0d 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -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;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 28b1ebbd380..c411f2e001a 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -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);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 67a6240ff2b..c8ae3ef422b 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -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 */
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 5311c50734b..248ddebd67f 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -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 {
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 5857d12611e..8ae962e7c93 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -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);