[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>
This commit is contained in:
parent
e22cb8bcb8
commit
a9d02ad490
5 changed files with 330 additions and 2 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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, ¤t_offset);
|
||||
if(rc == -EINVAL) {
|
||||
rc = SMBLegacyRead(xid, pTcon,
|
||||
open_file->netfid,
|
||||
current_read_size, *poffset,
|
||||
&bytes_read, ¤t_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);
|
||||
|
|
Loading…
Reference in a new issue