Commit 10f3cb41 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French.

* git://git.samba.org/sfrench/cifs-2.6:
  [CIFS] Update CIFS version number to 1.77
  CIFS: Add missed forcemand mount option
  [CIFS] Fix trivial sparse warning with asyn i/o patch
  cifs: handle "sloppy" option appropriately
  cifs: use standard token parser for mount options
  cifs: remove /proc/fs/cifs/OplockEnabled
  cifs: convert cifs_iovec_write to use async writes
  cifs: call cifs_update_eof with i_lock held
  cifs: abstract out function to marshal up the iovec array for async writes
  cifs: fix up get_numpages
  cifs: make cifsFileInfo_get return the cifsFileInfo pointer
  cifs: fix allocation in cifs_write_allocate_pages
  cifs: allow caller to specify completion op when allocating writedata
  cifs: add pid field to cifs_writedata
  cifs: add new cifsiod_wq workqueue
  CIFS: Change mid_q_entry structure fields
  CIFS: Expand CurrentMid field
  CIFS: Separate protocol-specific code from cifs_readv_receive code
  CIFS: Separate protocol-specific code from demultiplex code
  CIFS: Separate protocol-specific code from transport routines
parents 919c8401 867646f2
...@@ -58,15 +58,16 @@ cifs_dump_mem(char *label, void *data, int length) ...@@ -58,15 +58,16 @@ cifs_dump_mem(char *label, void *data, int length)
} }
#ifdef CONFIG_CIFS_DEBUG2 #ifdef CONFIG_CIFS_DEBUG2
void cifs_dump_detail(struct smb_hdr *smb) void cifs_dump_detail(void *buf)
{ {
struct smb_hdr *smb = (struct smb_hdr *)buf;
cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d", cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d",
smb->Command, smb->Status.CifsError, smb->Command, smb->Status.CifsError,
smb->Flags, smb->Flags2, smb->Mid, smb->Pid); smb->Flags, smb->Flags2, smb->Mid, smb->Pid);
cERROR(1, "smb buf %p len %d", smb, smbCalcSize(smb)); cERROR(1, "smb buf %p len %d", smb, smbCalcSize(smb));
} }
void cifs_dump_mids(struct TCP_Server_Info *server) void cifs_dump_mids(struct TCP_Server_Info *server)
{ {
struct list_head *tmp; struct list_head *tmp;
...@@ -79,15 +80,15 @@ void cifs_dump_mids(struct TCP_Server_Info *server) ...@@ -79,15 +80,15 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each(tmp, &server->pending_mid_q) { list_for_each(tmp, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead); mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cERROR(1, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %d", cERROR(1, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu",
mid_entry->midState, mid_entry->mid_state,
(int)mid_entry->command, le16_to_cpu(mid_entry->command),
mid_entry->pid, mid_entry->pid,
mid_entry->callback_data, mid_entry->callback_data,
mid_entry->mid); mid_entry->mid);
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
cERROR(1, "IsLarge: %d buf: %p time rcv: %ld now: %ld", cERROR(1, "IsLarge: %d buf: %p time rcv: %ld now: %ld",
mid_entry->largeBuf, mid_entry->large_buf,
mid_entry->resp_buf, mid_entry->resp_buf,
mid_entry->when_received, mid_entry->when_received,
jiffies); jiffies);
...@@ -217,9 +218,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -217,9 +218,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
mid_entry = list_entry(tmp3, struct mid_q_entry, mid_entry = list_entry(tmp3, struct mid_q_entry,
qhead); qhead);
seq_printf(m, "\tState: %d com: %d pid:" seq_printf(m, "\tState: %d com: %d pid:"
" %d cbdata: %p mid %d\n", " %d cbdata: %p mid %llu\n",
mid_entry->midState, mid_entry->mid_state,
(int)mid_entry->command, le16_to_cpu(mid_entry->command),
mid_entry->pid, mid_entry->pid,
mid_entry->callback_data, mid_entry->callback_data,
mid_entry->mid); mid_entry->mid);
...@@ -417,7 +418,6 @@ static const struct file_operations cifs_stats_proc_fops = { ...@@ -417,7 +418,6 @@ static const struct file_operations cifs_stats_proc_fops = {
static struct proc_dir_entry *proc_fs_cifs; static struct proc_dir_entry *proc_fs_cifs;
static const struct file_operations cifsFYI_proc_fops; static const struct file_operations cifsFYI_proc_fops;
static const struct file_operations cifs_oplock_proc_fops;
static const struct file_operations cifs_lookup_cache_proc_fops; static const struct file_operations cifs_lookup_cache_proc_fops;
static const struct file_operations traceSMB_proc_fops; static const struct file_operations traceSMB_proc_fops;
static const struct file_operations cifs_multiuser_mount_proc_fops; static const struct file_operations cifs_multiuser_mount_proc_fops;
...@@ -438,7 +438,6 @@ cifs_proc_init(void) ...@@ -438,7 +438,6 @@ cifs_proc_init(void)
#endif /* STATS */ #endif /* STATS */
proc_create("cifsFYI", 0, proc_fs_cifs, &cifsFYI_proc_fops); proc_create("cifsFYI", 0, proc_fs_cifs, &cifsFYI_proc_fops);
proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops); proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops);
proc_create("OplockEnabled", 0, proc_fs_cifs, &cifs_oplock_proc_fops);
proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs, proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs,
&cifs_linux_ext_proc_fops); &cifs_linux_ext_proc_fops);
proc_create("MultiuserMount", 0, proc_fs_cifs, proc_create("MultiuserMount", 0, proc_fs_cifs,
...@@ -462,7 +461,6 @@ cifs_proc_clean(void) ...@@ -462,7 +461,6 @@ cifs_proc_clean(void)
remove_proc_entry("Stats", proc_fs_cifs); remove_proc_entry("Stats", proc_fs_cifs);
#endif #endif
remove_proc_entry("MultiuserMount", proc_fs_cifs); remove_proc_entry("MultiuserMount", proc_fs_cifs);
remove_proc_entry("OplockEnabled", proc_fs_cifs);
remove_proc_entry("SecurityFlags", proc_fs_cifs); remove_proc_entry("SecurityFlags", proc_fs_cifs);
remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
...@@ -508,46 +506,6 @@ static const struct file_operations cifsFYI_proc_fops = { ...@@ -508,46 +506,6 @@ static const struct file_operations cifsFYI_proc_fops = {
.write = cifsFYI_proc_write, .write = cifsFYI_proc_write,
}; };
static int cifs_oplock_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "%d\n", enable_oplocks);
return 0;
}
static int cifs_oplock_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, cifs_oplock_proc_show, NULL);
}
static ssize_t cifs_oplock_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos)
{
char c;
int rc;
printk(KERN_WARNING "CIFS: The /proc/fs/cifs/OplockEnabled interface "
"will be removed in kernel version 3.4. Please migrate to "
"using the 'enable_oplocks' module parameter in cifs.ko.\n");
rc = get_user(c, buffer);
if (rc)
return rc;
if (c == '0' || c == 'n' || c == 'N')
enable_oplocks = false;
else if (c == '1' || c == 'y' || c == 'Y')
enable_oplocks = true;
return count;
}
static const struct file_operations cifs_oplock_proc_fops = {
.owner = THIS_MODULE,
.open = cifs_oplock_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = cifs_oplock_proc_write,
};
static int cifs_linux_ext_proc_show(struct seq_file *m, void *v) static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
{ {
seq_printf(m, "%d\n", linuxExtEnabled); seq_printf(m, "%d\n", linuxExtEnabled);
......
...@@ -26,13 +26,13 @@ ...@@ -26,13 +26,13 @@
void cifs_dump_mem(char *label, void *data, int length); void cifs_dump_mem(char *label, void *data, int length);
#ifdef CONFIG_CIFS_DEBUG2 #ifdef CONFIG_CIFS_DEBUG2
#define DBG2 2 #define DBG2 2
void cifs_dump_detail(struct smb_hdr *); void cifs_dump_detail(void *);
void cifs_dump_mids(struct TCP_Server_Info *); void cifs_dump_mids(struct TCP_Server_Info *);
#else #else
#define DBG2 0 #define DBG2 0
#endif #endif
extern int traceSMB; /* flag which enables the function below */ extern int traceSMB; /* flag which enables the function below */
void dump_smb(struct smb_hdr *, int); void dump_smb(void *, int);
#define CIFS_INFO 0x01 #define CIFS_INFO 0x01
#define CIFS_RC 0x02 #define CIFS_RC 0x02
#define CIFS_TIMER 0x04 #define CIFS_TIMER 0x04
......
...@@ -85,6 +85,8 @@ extern mempool_t *cifs_sm_req_poolp; ...@@ -85,6 +85,8 @@ extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp; extern mempool_t *cifs_mid_poolp;
struct workqueue_struct *cifsiod_wq;
static int static int
cifs_read_super(struct super_block *sb) cifs_read_super(struct super_block *sb)
{ {
...@@ -1111,9 +1113,15 @@ init_cifs(void) ...@@ -1111,9 +1113,15 @@ init_cifs(void)
cFYI(1, "cifs_max_pending set to max of %u", CIFS_MAX_REQ); cFYI(1, "cifs_max_pending set to max of %u", CIFS_MAX_REQ);
} }
cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
if (!cifsiod_wq) {
rc = -ENOMEM;
goto out_clean_proc;
}
rc = cifs_fscache_register(); rc = cifs_fscache_register();
if (rc) if (rc)
goto out_clean_proc; goto out_destroy_wq;
rc = cifs_init_inodecache(); rc = cifs_init_inodecache();
if (rc) if (rc)
...@@ -1161,6 +1169,8 @@ init_cifs(void) ...@@ -1161,6 +1169,8 @@ init_cifs(void)
cifs_destroy_inodecache(); cifs_destroy_inodecache();
out_unreg_fscache: out_unreg_fscache:
cifs_fscache_unregister(); cifs_fscache_unregister();
out_destroy_wq:
destroy_workqueue(cifsiod_wq);
out_clean_proc: out_clean_proc:
cifs_proc_clean(); cifs_proc_clean();
return rc; return rc;
...@@ -1183,6 +1193,7 @@ exit_cifs(void) ...@@ -1183,6 +1193,7 @@ exit_cifs(void)
cifs_destroy_mids(); cifs_destroy_mids();
cifs_destroy_inodecache(); cifs_destroy_inodecache();
cifs_fscache_unregister(); cifs_fscache_unregister();
destroy_workqueue(cifsiod_wq);
cifs_proc_clean(); cifs_proc_clean();
} }
......
...@@ -125,5 +125,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); ...@@ -125,5 +125,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops; extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */ #endif /* CONFIG_CIFS_NFSD_EXPORT */
#define CIFS_VERSION "1.76" #define CIFS_VERSION "1.77"
#endif /* _CIFSFS_H */ #endif /* _CIFSFS_H */
...@@ -230,6 +230,12 @@ struct cifs_mnt_data { ...@@ -230,6 +230,12 @@ struct cifs_mnt_data {
int flags; int flags;
}; };
static inline unsigned int
get_rfc1002_length(void *buf)
{
return be32_to_cpu(*((__be32 *)buf));
}
struct TCP_Server_Info { struct TCP_Server_Info {
struct list_head tcp_ses_list; struct list_head tcp_ses_list;
struct list_head smb_ses_list; struct list_head smb_ses_list;
...@@ -276,7 +282,7 @@ struct TCP_Server_Info { ...@@ -276,7 +282,7 @@ struct TCP_Server_Info {
vcnumbers */ vcnumbers */
int capabilities; /* allow selective disabling of caps by smb sess */ int capabilities; /* allow selective disabling of caps by smb sess */
int timeAdj; /* Adjust for difference in server time zone in sec */ int timeAdj; /* Adjust for difference in server time zone in sec */
__u16 CurrentMid; /* multiplex id - rotating counter */ __u64 CurrentMid; /* multiplex id - rotating counter */
char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */
/* 16th byte of RFC1001 workstation name is always null */ /* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
...@@ -335,6 +341,18 @@ has_credits(struct TCP_Server_Info *server, int *credits) ...@@ -335,6 +341,18 @@ has_credits(struct TCP_Server_Info *server, int *credits)
return num > 0; return num > 0;
} }
static inline size_t
header_size(void)
{
return sizeof(struct smb_hdr);
}
static inline size_t
max_header_size(void)
{
return MAX_CIFS_HDR_SIZE;
}
/* /*
* Macros to allow the TCP_Server_Info->net field and related code to drop out * Macros to allow the TCP_Server_Info->net field and related code to drop out
* when CONFIG_NET_NS isn't set. * when CONFIG_NET_NS isn't set.
...@@ -583,9 +601,11 @@ struct cifs_io_parms { ...@@ -583,9 +601,11 @@ struct cifs_io_parms {
* Take a reference on the file private data. Must be called with * Take a reference on the file private data. Must be called with
* cifs_file_list_lock held. * cifs_file_list_lock held.
*/ */
static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file) static inline
struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file)
{ {
++cifs_file->count; ++cifs_file->count;
return cifs_file;
} }
void cifsFileInfo_put(struct cifsFileInfo *cifs_file); void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
...@@ -606,7 +626,7 @@ struct cifsInodeInfo { ...@@ -606,7 +626,7 @@ struct cifsInodeInfo {
bool delete_pending; /* DELETE_ON_CLOSE is set */ bool delete_pending; /* DELETE_ON_CLOSE is set */
bool invalid_mapping; /* pagecache is invalid */ bool invalid_mapping; /* pagecache is invalid */
unsigned long time; /* jiffies of last update of inode */ unsigned long time; /* jiffies of last update of inode */
u64 server_eof; /* current file size on server */ u64 server_eof; /* current file size on server -- protected by i_lock */
u64 uniqueid; /* server inode number */ u64 uniqueid; /* server inode number */
u64 createtime; /* creation time on server */ u64 createtime; /* creation time on server */
#ifdef CONFIG_CIFS_FSCACHE #ifdef CONFIG_CIFS_FSCACHE
...@@ -713,8 +733,8 @@ typedef void (mid_callback_t)(struct mid_q_entry *mid); ...@@ -713,8 +733,8 @@ typedef void (mid_callback_t)(struct mid_q_entry *mid);
/* one of these for every pending CIFS request to the server */ /* one of these for every pending CIFS request to the server */
struct mid_q_entry { struct mid_q_entry {
struct list_head qhead; /* mids waiting on reply from this server */ struct list_head qhead; /* mids waiting on reply from this server */
__u16 mid; /* multiplex id */ __u64 mid; /* multiplex id */
__u16 pid; /* process id */ __u32 pid; /* process id */
__u32 sequence_number; /* for CIFS signing */ __u32 sequence_number; /* for CIFS signing */
unsigned long when_alloc; /* when mid was created */ unsigned long when_alloc; /* when mid was created */
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
...@@ -724,10 +744,10 @@ struct mid_q_entry { ...@@ -724,10 +744,10 @@ struct mid_q_entry {
mid_receive_t *receive; /* call receive callback */ mid_receive_t *receive; /* call receive callback */
mid_callback_t *callback; /* call completion callback */ mid_callback_t *callback; /* call completion callback */
void *callback_data; /* general purpose pointer for callback */ void *callback_data; /* general purpose pointer for callback */
struct smb_hdr *resp_buf; /* pointer to received SMB header */ void *resp_buf; /* pointer to received SMB header */
int midState; /* wish this were enum but can not pass to wait_event */ int mid_state; /* wish this were enum but can not pass to wait_event */
__u8 command; /* smb command code */ __le16 command; /* smb command code */
bool largeBuf:1; /* if valid response, is pointer to large buf */ bool large_buf:1; /* if valid response, is pointer to large buf */
bool multiRsp:1; /* multiple trans2 responses for one request */ bool multiRsp:1; /* multiple trans2 responses for one request */
bool multiEnd:1; /* both received */ bool multiEnd:1; /* both received */
}; };
...@@ -1052,5 +1072,6 @@ GLOBAL_EXTERN spinlock_t gidsidlock; ...@@ -1052,5 +1072,6 @@ GLOBAL_EXTERN spinlock_t gidsidlock;
void cifs_oplock_break(struct work_struct *work); void cifs_oplock_break(struct work_struct *work);
extern const struct slow_work_ops cifs_oplock_break_ops; extern const struct slow_work_ops cifs_oplock_break_ops;
extern struct workqueue_struct *cifsiod_wq;
#endif /* _CIFS_GLOB_H */ #endif /* _CIFS_GLOB_H */
...@@ -77,7 +77,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, ...@@ -77,7 +77,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
struct smb_hdr * /* out */ , struct smb_hdr * /* out */ ,
int * /* bytes returned */ , const int long_op); int * /* bytes returned */ , const int long_op);
extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
struct smb_hdr *in_buf, int flags); char *in_buf, int flags);
extern int cifs_check_receive(struct mid_q_entry *mid, extern int cifs_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error); struct TCP_Server_Info *server, bool log_error);
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
...@@ -91,9 +91,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid, ...@@ -91,9 +91,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
extern void cifs_add_credits(struct TCP_Server_Info *server, extern void cifs_add_credits(struct TCP_Server_Info *server,
const unsigned int add); const unsigned int add);
extern void cifs_set_credits(struct TCP_Server_Info *server, const int val); extern void cifs_set_credits(struct TCP_Server_Info *server, const int val);
extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); extern int checkSMB(char *buf, unsigned int length);
extern bool is_valid_oplock_break(struct smb_hdr *smb, extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
struct TCP_Server_Info *);
extern bool backup_cred(struct cifs_sb_info *); extern bool backup_cred(struct cifs_sb_info *);
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
...@@ -107,7 +106,7 @@ extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); ...@@ -107,7 +106,7 @@ extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
extern int cifs_set_port(struct sockaddr *addr, const unsigned short int port); extern int cifs_set_port(struct sockaddr *addr, const unsigned short int port);
extern int cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len, extern int cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len,
const unsigned short int port); const unsigned short int port);
extern int map_smb_to_linux_error(struct smb_hdr *smb, bool logErr); extern int map_smb_to_linux_error(char *buf, bool logErr);
extern void header_assemble(struct smb_hdr *, char /* command */ , extern void header_assemble(struct smb_hdr *, char /* command */ ,
const struct cifs_tcon *, int /* length of const struct cifs_tcon *, int /* length of
fixed section (word count) in two byte units */); fixed section (word count) in two byte units */);
...@@ -116,7 +115,7 @@ extern int small_smb_init_no_tc(const int smb_cmd, const int wct, ...@@ -116,7 +115,7 @@ extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
void **request_buf); void **request_buf);
extern int CIFS_SessSetup(unsigned int xid, struct cifs_ses *ses, extern int CIFS_SessSetup(unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
extern __u16 GetNextMid(struct TCP_Server_Info *server); extern __u64 GetNextMid(struct TCP_Server_Info *server);
extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
extern u64 cifs_UnixTimeToNT(struct timespec); extern u64 cifs_UnixTimeToNT(struct timespec);
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
...@@ -484,18 +483,25 @@ int cifs_async_readv(struct cifs_readdata *rdata); ...@@ -484,18 +483,25 @@ int cifs_async_readv(struct cifs_readdata *rdata);
/* asynchronous write support */ /* asynchronous write support */
struct cifs_writedata { struct cifs_writedata {
struct kref refcount; struct kref refcount;
struct list_head list;
struct completion done;
enum writeback_sync_modes sync_mode; enum writeback_sync_modes sync_mode;
struct work_struct work; struct work_struct work;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
__u64 offset; __u64 offset;
pid_t pid;
unsigned int bytes; unsigned int bytes;
int result; int result;
void (*marshal_iov) (struct kvec *iov,
struct cifs_writedata *wdata);
unsigned int nr_pages; unsigned int nr_pages;
struct page *pages[1]; struct page *pages[1];
}; };
int cifs_async_writev(struct cifs_writedata *wdata); int cifs_async_writev(struct cifs_writedata *wdata);
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages); void cifs_writev_complete(struct work_struct *work);
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
work_func_t complete);
void cifs_writedata_release(struct kref *refcount); void cifs_writedata_release(struct kref *refcount);
#endif /* _CIFSPROTO_H */ #endif /* _CIFSPROTO_H */
...@@ -696,7 +696,7 @@ CIFSSMBTDis(const int xid, struct cifs_tcon *tcon) ...@@ -696,7 +696,7 @@ CIFSSMBTDis(const int xid, struct cifs_tcon *tcon)
if (rc) if (rc)
return rc; return rc;
rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0);
if (rc) if (rc)
cFYI(1, "Tree disconnect failed %d", rc); cFYI(1, "Tree disconnect failed %d", rc);
...@@ -792,7 +792,7 @@ CIFSSMBLogoff(const int xid, struct cifs_ses *ses) ...@@ -792,7 +792,7 @@ CIFSSMBLogoff(const int xid, struct cifs_ses *ses)
pSMB->hdr.Uid = ses->Suid; pSMB->hdr.Uid = ses->Suid;
pSMB->AndXCommand = 0xFF; pSMB->AndXCommand = 0xFF;
rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0);
session_already_dead: session_already_dead:
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
...@@ -1414,8 +1414,7 @@ cifs_readdata_free(struct cifs_readdata *rdata) ...@@ -1414,8 +1414,7 @@ cifs_readdata_free(struct cifs_readdata *rdata)
static int static int
cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{ {
READ_RSP *rsp = (READ_RSP *)server->smallbuf; unsigned int rfclen = get_rfc1002_length(server->smallbuf);
unsigned int rfclen = be32_to_cpu(rsp->hdr.smb_buf_length);
int remaining = rfclen + 4 - server->total_read; int remaining = rfclen + 4 - server->total_read;
struct cifs_readdata *rdata = mid->callback_data; struct cifs_readdata *rdata = mid->callback_data;
...@@ -1424,7 +1423,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1424,7 +1423,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
length = cifs_read_from_socket(server, server->bigbuf, length = cifs_read_from_socket(server, server->bigbuf,
min_t(unsigned int, remaining, min_t(unsigned int, remaining,
CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)); CIFSMaxBufSize + max_header_size()));
if (length < 0) if (length < 0)
return length; return length;
server->total_read += length; server->total_read += length;
...@@ -1435,19 +1434,40 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1435,19 +1434,40 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return 0; return 0;
} }
static inline size_t
read_rsp_size(void)
{
return sizeof(READ_RSP);
}
static inline unsigned int
read_data_offset(char *buf)
{
READ_RSP *rsp = (READ_RSP *)buf;
return le16_to_cpu(rsp->DataOffset);
}
static inline unsigned int
read_data_length(char *buf)
{
READ_RSP *rsp = (READ_RSP *)buf;
return (le16_to_cpu(rsp->DataLengthHigh) << 16) +
le16_to_cpu(rsp->DataLength);
}
static int static int
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{ {
int length, len; int length, len;
unsigned int data_offset, remaining, data_len; unsigned int data_offset, remaining, data_len;
struct cifs_readdata *rdata = mid->callback_data; struct cifs_readdata *rdata = mid->callback_data;
READ_RSP *rsp = (READ_RSP *)server->smallbuf; char *buf = server->smallbuf;
unsigned int rfclen = be32_to_cpu(rsp->hdr.smb_buf_length) + 4; unsigned int buflen = get_rfc1002_length(buf) + 4;
u64 eof; u64 eof;
pgoff_t eof_index; pgoff_t eof_index;
struct page *page, *tpage; struct page *page, *tpage;
cFYI(1, "%s: mid=%u offset=%llu bytes=%u", __func__, cFYI(1, "%s: mid=%llu offset=%llu bytes=%u", __func__,
mid->mid, rdata->offset, rdata->bytes); mid->mid, rdata->offset, rdata->bytes);
/* /*
...@@ -1455,10 +1475,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1455,10 +1475,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
* can if there's not enough data. At this point, we've read down to * can if there's not enough data. At this point, we've read down to
* the Mid. * the Mid.
*/ */
len = min_t(unsigned int, rfclen, sizeof(*rsp)) - len = min_t(unsigned int, buflen, read_rsp_size()) - header_size() + 1;
sizeof(struct smb_hdr) + 1;
rdata->iov[0].iov_base = server->smallbuf + sizeof(struct smb_hdr) - 1; rdata->iov[0].iov_base = buf + header_size() - 1;
rdata->iov[0].iov_len = len; rdata->iov[0].iov_len = len;
length = cifs_readv_from_socket(server, rdata->iov, 1, len); length = cifs_readv_from_socket(server, rdata->iov, 1, len);
...@@ -1467,7 +1486,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1467,7 +1486,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
server->total_read += length; server->total_read += length;
/* Was the SMB read successful? */ /* Was the SMB read successful? */
rdata->result = map_smb_to_linux_error(&rsp->hdr, false); rdata->result = map_smb_to_linux_error(buf, false);
if (rdata->result != 0) { if (rdata->result != 0) {
cFYI(1, "%s: server returned error %d", __func__, cFYI(1, "%s: server returned error %d", __func__,
rdata->result); rdata->result);
...@@ -1475,14 +1494,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1475,14 +1494,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
} }
/* Is there enough to get to the rest of the READ_RSP header? */ /* Is there enough to get to the rest of the READ_RSP header? */
if (server->total_read < sizeof(READ_RSP)) { if (server->total_read < read_rsp_size()) {
cFYI(1, "%s: server returned short header. got=%u expected=%zu", cFYI(1, "%s: server returned short header. got=%u expected=%zu",
__func__, server->total_read, sizeof(READ_RSP)); __func__, server->total_read, read_rsp_size());
rdata->result = -EIO; rdata->result = -EIO;
return cifs_readv_discard(server, mid); return cifs_readv_discard(server, mid);
} }
data_offset = le16_to_cpu(rsp->DataOffset) + 4; data_offset = read_data_offset(buf) + 4;
if (data_offset < server->total_read) { if (data_offset < server->total_read) {
/* /*
* win2k8 sometimes sends an offset of 0 when the read * win2k8 sometimes sends an offset of 0 when the read
...@@ -1506,7 +1525,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1506,7 +1525,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
len = data_offset - server->total_read; len = data_offset - server->total_read;
if (len > 0) { if (len > 0) {
/* read any junk before data into the rest of smallbuf */ /* read any junk before data into the rest of smallbuf */
rdata->iov[0].iov_base = server->smallbuf + server->total_read; rdata->iov[0].iov_base = buf + server->total_read;
rdata->iov[0].iov_len = len; rdata->iov[0].iov_len = len;
length = cifs_readv_from_socket(server, rdata->iov, 1, len); length = cifs_readv_from_socket(server, rdata->iov, 1, len);
if (length < 0) if (length < 0)
...@@ -1515,15 +1534,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1515,15 +1534,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
} }
/* set up first iov for signature check */ /* set up first iov for signature check */
rdata->iov[0].iov_base = server->smallbuf; rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = server->total_read; rdata->iov[0].iov_len = server->total_read;
cFYI(1, "0: iov_base=%p iov_len=%zu", cFYI(1, "0: iov_base=%p iov_len=%zu",
rdata->iov[0].iov_base, rdata->iov[0].iov_len); rdata->iov[0].iov_base, rdata->iov[0].iov_len);
/* how much data is in the response? */ /* how much data is in the response? */
data_len = le16_to_cpu(rsp->DataLengthHigh) << 16; data_len = read_data_length(buf);
data_len += le16_to_cpu(rsp->DataLength); if (data_offset + data_len > buflen) {
if (data_offset + data_len > rfclen) {
/* data_len is corrupt -- discard frame */ /* data_len is corrupt -- discard frame */
rdata->result = -EIO; rdata->result = -EIO;
return cifs_readv_discard(server, mid); return cifs_readv_discard(server, mid);
...@@ -1602,11 +1620,11 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1602,11 +1620,11 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
rdata->bytes = length; rdata->bytes = length;
cFYI(1, "total_read=%u rfclen=%u remaining=%u", server->total_read, cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read,
rfclen, remaining); buflen, remaining);
/* discard anything left over */ /* discard anything left over */
if (server->total_read < rfclen) if (server->total_read < buflen)
return cifs_readv_discard(server, mid); return cifs_readv_discard(server, mid);
dequeue_mid(mid, false); dequeue_mid(mid, false);
...@@ -1647,10 +1665,10 @@ cifs_readv_callback(struct mid_q_entry *mid) ...@@ -1647,10 +1665,10 @@ cifs_readv_callback(struct mid_q_entry *mid)
struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
cFYI(1, "%s: mid=%u state=%d result=%d bytes=%u", __func__, cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__,
mid->mid, mid->midState, rdata->result, rdata->bytes); mid->mid, mid->mid_state, rdata->result, rdata->bytes);
switch (mid->midState) { switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED: case MID_RESPONSE_RECEIVED:
/* result already set, check signature */ /* result already set, check signature */
if (server->sec_mode & if (server->sec_mode &
...@@ -1671,7 +1689,7 @@ cifs_readv_callback(struct mid_q_entry *mid) ...@@ -1671,7 +1689,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
rdata->result = -EIO; rdata->result = -EIO;
} }
queue_work(system_nrt_wq, &rdata->work); queue_work(cifsiod_wq, &rdata->work);
DeleteMidQEntry(mid); DeleteMidQEntry(mid);
cifs_add_credits(server, 1); cifs_add_credits(server, 1);
} }
...@@ -2017,7 +2035,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata) ...@@ -2017,7 +2035,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
kref_put(&wdata->refcount, cifs_writedata_release); kref_put(&wdata->refcount, cifs_writedata_release);
} }
static void void
cifs_writev_complete(struct work_struct *work) cifs_writev_complete(struct work_struct *work)
{ {
struct cifs_writedata *wdata = container_of(work, struct cifs_writedata *wdata = container_of(work,
...@@ -2026,7 +2044,9 @@ cifs_writev_complete(struct work_struct *work) ...@@ -2026,7 +2044,9 @@ cifs_writev_complete(struct work_struct *work)
int i = 0; int i = 0;
if (wdata->result == 0) { if (wdata->result == 0) {
spin_lock(&inode->i_lock);
cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes); cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes);
spin_unlock(&inode->i_lock);
cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink), cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink),
wdata->bytes); wdata->bytes);
} else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN) } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN)
...@@ -2047,7 +2067,7 @@ cifs_writev_complete(struct work_struct *work) ...@@ -2047,7 +2067,7 @@ cifs_writev_complete(struct work_struct *work)
} }
struct cifs_writedata * struct cifs_writedata *
cifs_writedata_alloc(unsigned int nr_pages) cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)
{ {
struct cifs_writedata *wdata; struct cifs_writedata *wdata;
...@@ -2061,14 +2081,16 @@ cifs_writedata_alloc(unsigned int nr_pages) ...@@ -2061,14 +2081,16 @@ cifs_writedata_alloc(unsigned int nr_pages)
wdata = kzalloc(sizeof(*wdata) + wdata = kzalloc(sizeof(*wdata) +
sizeof(struct page *) * (nr_pages - 1), GFP_NOFS); sizeof(struct page *) * (nr_pages - 1), GFP_NOFS);
if (wdata != NULL) { if (wdata != NULL) {
INIT_WORK(&wdata->work, cifs_writev_complete);
kref_init(&wdata->refcount); kref_init(&wdata->refcount);
INIT_LIST_HEAD(&wdata->list);
init_completion(&wdata->done);
INIT_WORK(&wdata->work, complete);
} }
return wdata; return wdata;
} }
/* /*
* Check the midState and signature on received buffer (if any), and queue the * Check the mid_state and signature on received buffer (if any), and queue the
* workqueue completion task. * workqueue completion task.
*/ */
static void static void
...@@ -2079,7 +2101,7 @@ cifs_writev_callback(struct mid_q_entry *mid) ...@@ -2079,7 +2101,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
unsigned int written; unsigned int written;
WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf; WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
switch (mid->midState) { switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED: case MID_RESPONSE_RECEIVED:
wdata->result = cifs_check_receive(mid, tcon->ses->server, 0); wdata->result = cifs_check_receive(mid, tcon->ses->server, 0);
if (wdata->result != 0) if (wdata->result != 0)
...@@ -2111,7 +2133,7 @@ cifs_writev_callback(struct mid_q_entry *mid) ...@@ -2111,7 +2133,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
break; break;
} }
queue_work(system_nrt_wq, &wdata->work); queue_work(cifsiod_wq, &wdata->work);
DeleteMidQEntry(mid); DeleteMidQEntry(mid);
cifs_add_credits(tcon->ses->server, 1); cifs_add_credits(tcon->ses->server, 1);
} }
...@@ -2124,7 +2146,6 @@ cifs_async_writev(struct cifs_writedata *wdata) ...@@ -2124,7 +2146,6 @@ cifs_async_writev(struct cifs_writedata *wdata)
WRITE_REQ *smb = NULL; WRITE_REQ *smb = NULL;
int wct; int wct;
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
struct inode *inode = wdata->cfile->dentry->d_inode;
struct kvec *iov = NULL; struct kvec *iov = NULL;
if (tcon->ses->capabilities & CAP_LARGE_FILES) { if (tcon->ses->capabilities & CAP_LARGE_FILES) {
...@@ -2148,8 +2169,8 @@ cifs_async_writev(struct cifs_writedata *wdata) ...@@ -2148,8 +2169,8 @@ cifs_async_writev(struct cifs_writedata *wdata)
goto async_writev_out; goto async_writev_out;
} }
smb->hdr.Pid = cpu_to_le16((__u16)wdata->cfile->pid); smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid);
smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->cfile->pid >> 16)); smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16));
smb->AndXCommand = 0xFF; /* none */ smb->AndXCommand = 0xFF; /* none */
smb->Fid = wdata->cfile->netfid; smb->Fid = wdata->cfile->netfid;
...@@ -2167,15 +2188,13 @@ cifs_async_writev(struct cifs_writedata *wdata) ...@@ -2167,15 +2188,13 @@ cifs_async_writev(struct cifs_writedata *wdata)
iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1; iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1;
iov[0].iov_base = smb; iov[0].iov_base = smb;
/* marshal up the pages into iov array */ /*
wdata->bytes = 0; * This function should marshal up the page array into the kvec
for (i = 0; i < wdata->nr_pages; i++) { * array, reserving [0] for the header. It should kmap the pages
iov[i + 1].iov_len = min(inode->i_size - * and set the iov_len properly for each one. It may also set
page_offset(wdata->pages[i]), * wdata->bytes too.
(loff_t)PAGE_CACHE_SIZE); */
iov[i + 1].iov_base = kmap(wdata->pages[i]); wdata->marshal_iov(iov, wdata);
wdata->bytes += iov[i + 1].iov_len;
}
cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes); cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
...@@ -2420,8 +2439,7 @@ CIFSSMBLock(const int xid, struct cifs_tcon *tcon, ...@@ -2420,8 +2439,7 @@ CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
(struct smb_hdr *) pSMB, &bytes_returned); (struct smb_hdr *) pSMB, &bytes_returned);
cifs_small_buf_release(pSMB); cifs_small_buf_release(pSMB);
} else { } else {
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB, rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, timeout);
timeout);
/* SMB buffer freed by function above */ /* SMB buffer freed by function above */
} }
cifs_stats_inc(&tcon->num_locks); cifs_stats_inc(&tcon->num_locks);
...@@ -2588,7 +2606,7 @@ CIFSSMBClose(const int xid, struct cifs_tcon *tcon, int smb_file_id) ...@@ -2588,7 +2606,7 @@ CIFSSMBClose(const int xid, struct cifs_tcon *tcon, int smb_file_id)
pSMB->FileID = (__u16) smb_file_id; pSMB->FileID = (__u16) smb_file_id;
pSMB->LastWriteTime = 0xFFFFFFFF; pSMB->LastWriteTime = 0xFFFFFFFF;
pSMB->ByteCount = 0; pSMB->ByteCount = 0;
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
cifs_stats_inc(&tcon->num_closes); cifs_stats_inc(&tcon->num_closes);
if (rc) { if (rc) {
if (rc != -EINTR) { if (rc != -EINTR) {
...@@ -2617,7 +2635,7 @@ CIFSSMBFlush(const int xid, struct cifs_tcon *tcon, int smb_file_id) ...@@ -2617,7 +2635,7 @@ CIFSSMBFlush(const int xid, struct cifs_tcon *tcon, int smb_file_id)
pSMB->FileID = (__u16) smb_file_id; pSMB->FileID = (__u16) smb_file_id;
pSMB->ByteCount = 0; pSMB->ByteCount = 0;
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
cifs_stats_inc(&tcon->num_flushes); cifs_stats_inc(&tcon->num_flushes);
if (rc) if (rc)
cERROR(1, "Send error in Flush = %d", rc); cERROR(1, "Send error in Flush = %d", rc);
...@@ -4625,7 +4643,7 @@ CIFSFindClose(const int xid, struct cifs_tcon *tcon, ...@@ -4625,7 +4643,7 @@ CIFSFindClose(const int xid, struct cifs_tcon *tcon,
pSMB->FileID = searchHandle; pSMB->FileID = searchHandle;
pSMB->ByteCount = 0; pSMB->ByteCount = 0;
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
if (rc) if (rc)
cERROR(1, "Send error in FindClose = %d", rc); cERROR(1, "Send error in FindClose = %d", rc);
...@@ -5646,7 +5664,7 @@ CIFSSMBSetFileSize(const int xid, struct cifs_tcon *tcon, __u64 size, ...@@ -5646,7 +5664,7 @@ CIFSSMBSetFileSize(const int xid, struct cifs_tcon *tcon, __u64 size,
pSMB->Reserved4 = 0; pSMB->Reserved4 = 0;
inc_rfc1001_len(pSMB, byte_count); inc_rfc1001_len(pSMB, byte_count);
pSMB->ByteCount = cpu_to_le16(byte_count); pSMB->ByteCount = cpu_to_le16(byte_count);
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
if (rc) { if (rc) {
cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc); cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
} }
...@@ -5715,7 +5733,7 @@ CIFSSMBSetFileInfo(const int xid, struct cifs_tcon *tcon, ...@@ -5715,7 +5733,7 @@ CIFSSMBSetFileInfo(const int xid, struct cifs_tcon *tcon,
inc_rfc1001_len(pSMB, byte_count); inc_rfc1001_len(pSMB, byte_count);
pSMB->ByteCount = cpu_to_le16(byte_count); pSMB->ByteCount = cpu_to_le16(byte_count);
memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
if (rc) if (rc)
cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc); cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
...@@ -5774,7 +5792,7 @@ CIFSSMBSetFileDisposition(const int xid, struct cifs_tcon *tcon, ...@@ -5774,7 +5792,7 @@ CIFSSMBSetFileDisposition(const int xid, struct cifs_tcon *tcon,
inc_rfc1001_len(pSMB, byte_count); inc_rfc1001_len(pSMB, byte_count);
pSMB->ByteCount = cpu_to_le16(byte_count); pSMB->ByteCount = cpu_to_le16(byte_count);
*data_offset = delete_file ? 1 : 0; *data_offset = delete_file ? 1 : 0;
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
if (rc) if (rc)
cFYI(1, "Send error in SetFileDisposition = %d", rc); cFYI(1, "Send error in SetFileDisposition = %d", rc);
...@@ -6006,7 +6024,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon, ...@@ -6006,7 +6024,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon,
cifs_fill_unix_set_info(data_offset, args); cifs_fill_unix_set_info(data_offset, args);
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
if (rc) if (rc)
cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc); cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
......
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <keys/user-type.h> #include <keys/user-type.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <linux/parser.h>
#include "cifspdu.h" #include "cifspdu.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "cifsproto.h" #include "cifsproto.h"
...@@ -63,6 +65,193 @@ extern mempool_t *cifs_req_poolp; ...@@ -63,6 +65,193 @@ extern mempool_t *cifs_req_poolp;
#define TLINK_ERROR_EXPIRE (1 * HZ) #define TLINK_ERROR_EXPIRE (1 * HZ)
#define TLINK_IDLE_EXPIRE (600 * HZ) #define TLINK_IDLE_EXPIRE (600 * HZ)
enum {
/* Mount options that take no arguments */
Opt_user_xattr, Opt_nouser_xattr,
Opt_forceuid, Opt_noforceuid,
Opt_noblocksend, Opt_noautotune,
Opt_hard, Opt_soft, Opt_perm, Opt_noperm,
Opt_mapchars, Opt_nomapchars, Opt_sfu,
Opt_nosfu, Opt_nodfs, Opt_posixpaths,
Opt_noposixpaths, Opt_nounix,
Opt_nocase,
Opt_brl, Opt_nobrl,
Opt_forcemandatorylock, Opt_setuids,
Opt_nosetuids, Opt_dynperm, Opt_nodynperm,
Opt_nohard, Opt_nosoft,
Opt_nointr, Opt_intr,
Opt_nostrictsync, Opt_strictsync,
Opt_serverino, Opt_noserverino,
Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
Opt_acl, Opt_noacl, Opt_locallease,
Opt_sign, Opt_seal, Opt_direct,
Opt_strictcache, Opt_noac,
Opt_fsc, Opt_mfsymlinks,
Opt_multiuser, Opt_sloppy,
/* Mount options which take numeric value */
Opt_backupuid, Opt_backupgid, Opt_uid,
Opt_cruid, Opt_gid, Opt_file_mode,
Opt_dirmode, Opt_port,
Opt_rsize, Opt_wsize, Opt_actimeo,
/* Mount options which take string value */
Opt_user, Opt_pass, Opt_ip,
Opt_unc, Opt_domain,
Opt_srcaddr, Opt_prefixpath,
Opt_iocharset, Opt_sockopt,
Opt_netbiosname, Opt_servern,
Opt_ver, Opt_sec,
/* Mount options to be ignored */
Opt_ignore,
/* Options which could be blank */
Opt_blank_pass,
Opt_err
};
static const match_table_t cifs_mount_option_tokens = {
{ Opt_user_xattr, "user_xattr" },
{ Opt_nouser_xattr, "nouser_xattr" },
{ Opt_forceuid, "forceuid" },
{ Opt_noforceuid, "noforceuid" },
{ Opt_noblocksend, "noblocksend" },
{ Opt_noautotune, "noautotune" },
{ Opt_hard, "hard" },
{ Opt_soft, "soft" },
{ Opt_perm, "perm" },
{ Opt_noperm, "noperm" },
{ Opt_mapchars, "mapchars" },
{ Opt_nomapchars, "nomapchars" },
{ Opt_sfu, "sfu" },
{ Opt_nosfu, "nosfu" },
{ Opt_nodfs, "nodfs" },
{ Opt_posixpaths, "posixpaths" },
{ Opt_noposixpaths, "noposixpaths" },
{ Opt_nounix, "nounix" },
{ Opt_nounix, "nolinux" },
{ Opt_nocase, "nocase" },
{ Opt_nocase, "ignorecase" },
{ Opt_brl, "brl" },
{ Opt_nobrl, "nobrl" },
{ Opt_nobrl, "nolock" },
{ Opt_forcemandatorylock, "forcemandatorylock" },
{ Opt_forcemandatorylock, "forcemand" },
{ Opt_setuids, "setuids" },
{ Opt_nosetuids, "nosetuids" },
{ Opt_dynperm, "dynperm" },
{ Opt_nodynperm, "nodynperm" },
{ Opt_nohard, "nohard" },
{ Opt_nosoft, "nosoft" },
{ Opt_nointr, "nointr" },
{ Opt_intr, "intr" },
{ Opt_nostrictsync, "nostrictsync" },
{ Opt_strictsync, "strictsync" },
{ Opt_serverino, "serverino" },
{ Opt_noserverino, "noserverino" },
{ Opt_rwpidforward, "rwpidforward" },
{ Opt_cifsacl, "cifsacl" },
{ Opt_nocifsacl, "nocifsacl" },
{ Opt_acl, "acl" },
{ Opt_noacl, "noacl" },
{ Opt_locallease, "locallease" },
{ Opt_sign, "sign" },
{ Opt_seal, "seal" },
{ Opt_direct, "direct" },
{ Opt_direct, "forceddirectio" },
{ Opt_strictcache, "strictcache" },
{ Opt_noac, "noac" },
{ Opt_fsc, "fsc" },
{ Opt_mfsymlinks, "mfsymlinks" },
{ Opt_multiuser, "multiuser" },
{ Opt_sloppy, "sloppy" },
{ Opt_backupuid, "backupuid=%s" },
{ Opt_backupgid, "backupgid=%s" },
{ Opt_uid, "uid=%s" },
{ Opt_cruid, "cruid=%s" },
{ Opt_gid, "gid=%s" },
{ Opt_file_mode, "file_mode=%s" },
{ Opt_dirmode, "dirmode=%s" },
{ Opt_dirmode, "dir_mode=%s" },
{ Opt_port, "port=%s" },
{ Opt_rsize, "rsize=%s" },
{ Opt_wsize, "wsize=%s" },
{ Opt_actimeo, "actimeo=%s" },
{ Opt_user, "user=%s" },
{ Opt_user, "username=%s" },
{ Opt_blank_pass, "pass=" },
{ Opt_pass, "pass=%s" },
{ Opt_pass, "password=%s" },
{ Opt_ip, "ip=%s" },
{ Opt_ip, "addr=%s" },
{ Opt_unc, "unc=%s" },
{ Opt_unc, "target=%s" },
{ Opt_unc, "path=%s" },
{ Opt_domain, "dom=%s" },
{ Opt_domain, "domain=%s" },
{ Opt_domain, "workgroup=%s" },
{ Opt_srcaddr, "srcaddr=%s" },
{ Opt_prefixpath, "prefixpath=%s" },
{ Opt_iocharset, "iocharset=%s" },
{ Opt_sockopt, "sockopt=%s" },
{ Opt_netbiosname, "netbiosname=%s" },
{ Opt_servern, "servern=%s" },
{ Opt_ver, "ver=%s" },
{ Opt_ver, "vers=%s" },
{ Opt_ver, "version=%s" },
{ Opt_sec, "sec=%s" },
{ Opt_ignore, "cred" },
{ Opt_ignore, "credentials" },
{ Opt_ignore, "guest" },
{ Opt_ignore, "rw" },
{ Opt_ignore, "ro" },
{ Opt_ignore, "suid" },
{ Opt_ignore, "nosuid" },
{ Opt_ignore, "exec" },
{ Opt_ignore, "noexec" },
{ Opt_ignore, "nodev" },
{ Opt_ignore, "noauto" },
{ Opt_ignore, "dev" },
{ Opt_ignore, "mand" },
{ Opt_ignore, "nomand" },
{ Opt_ignore, "_netdev" },
{ Opt_err, NULL }
};
enum {
Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p,
Opt_sec_ntlmsspi, Opt_sec_ntlmssp,
Opt_ntlm, Opt_sec_ntlmi, Opt_sec_ntlmv2i,
Opt_sec_nontlm, Opt_sec_lanman,
Opt_sec_none,
Opt_sec_err
};
static const match_table_t cifs_secflavor_tokens = {
{ Opt_sec_krb5, "krb5" },
{ Opt_sec_krb5i, "krb5i" },
{ Opt_sec_krb5p, "krb5p" },
{ Opt_sec_ntlmsspi, "ntlmsspi" },
{ Opt_sec_ntlmssp, "ntlmssp" },
{ Opt_ntlm, "ntlm" },
{ Opt_sec_ntlmi, "ntlmi" },
{ Opt_sec_ntlmv2i, "ntlmv2i" },
{ Opt_sec_nontlm, "nontlm" },
{ Opt_sec_lanman, "lanman" },
{ Opt_sec_none, "none" },
{ Opt_sec_err, NULL }
};
static int ip_connect(struct TCP_Server_Info *server); static int ip_connect(struct TCP_Server_Info *server);
static int generic_ip_connect(struct TCP_Server_Info *server); static int generic_ip_connect(struct TCP_Server_Info *server);
static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
...@@ -143,8 +332,8 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -143,8 +332,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead); mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
if (mid_entry->midState == MID_REQUEST_SUBMITTED) if (mid_entry->mid_state == MID_REQUEST_SUBMITTED)
mid_entry->midState = MID_RETRY_NEEDED; mid_entry->mid_state = MID_RETRY_NEEDED;
list_move(&mid_entry->qhead, &retry_list); list_move(&mid_entry->qhead, &retry_list);
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -183,8 +372,9 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -183,8 +372,9 @@ cifs_reconnect(struct TCP_Server_Info *server)
-EINVAL = invalid transact2 -EINVAL = invalid transact2
*/ */
static int check2ndT2(struct smb_hdr *pSMB) static int check2ndT2(char *buf)
{ {
struct smb_hdr *pSMB = (struct smb_hdr *)buf;
struct smb_t2_rsp *pSMBt; struct smb_t2_rsp *pSMBt;
int remaining; int remaining;
__u16 total_data_size, data_in_this_rsp; __u16 total_data_size, data_in_this_rsp;
...@@ -224,10 +414,10 @@ static int check2ndT2(struct smb_hdr *pSMB) ...@@ -224,10 +414,10 @@ static int check2ndT2(struct smb_hdr *pSMB)
return remaining; return remaining;
} }
static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) static int coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
{ {
struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf;
struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr;
char *data_area_of_tgt; char *data_area_of_tgt;
char *data_area_of_src; char *data_area_of_src;
int remaining; int remaining;
...@@ -280,23 +470,23 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) ...@@ -280,23 +470,23 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount);
/* fix up the BCC */ /* fix up the BCC */
byte_count = get_bcc(pTargetSMB); byte_count = get_bcc(target_hdr);
byte_count += total_in_src; byte_count += total_in_src;
/* is the result too big for the field? */ /* is the result too big for the field? */
if (byte_count > USHRT_MAX) { if (byte_count > USHRT_MAX) {
cFYI(1, "coalesced BCC too large (%u)", byte_count); cFYI(1, "coalesced BCC too large (%u)", byte_count);
return -EPROTO; return -EPROTO;
} }
put_bcc(byte_count, pTargetSMB); put_bcc(byte_count, target_hdr);
byte_count = be32_to_cpu(pTargetSMB->smb_buf_length); byte_count = be32_to_cpu(target_hdr->smb_buf_length);
byte_count += total_in_src; byte_count += total_in_src;
/* don't allow buffer to overflow */ /* don't allow buffer to overflow */
if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count); cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count);
return -ENOBUFS; return -ENOBUFS;
} }
pTargetSMB->smb_buf_length = cpu_to_be32(byte_count); target_hdr->smb_buf_length = cpu_to_be32(byte_count);
/* copy second buffer into end of first buffer */ /* copy second buffer into end of first buffer */
memcpy(data_area_of_tgt, data_area_of_src, total_in_src); memcpy(data_area_of_tgt, data_area_of_src, total_in_src);
...@@ -334,7 +524,7 @@ cifs_echo_request(struct work_struct *work) ...@@ -334,7 +524,7 @@ cifs_echo_request(struct work_struct *work)
server->hostname); server->hostname);
requeue_echo: requeue_echo:
queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL); queue_delayed_work(cifsiod_wq, &server->echo, SMB_ECHO_INTERVAL);
} }
static bool static bool
...@@ -350,7 +540,7 @@ allocate_buffers(struct TCP_Server_Info *server) ...@@ -350,7 +540,7 @@ allocate_buffers(struct TCP_Server_Info *server)
} }
} else if (server->large_buf) { } else if (server->large_buf) {
/* we are reusing a dirty large buf, clear its start */ /* we are reusing a dirty large buf, clear its start */
memset(server->bigbuf, 0, sizeof(struct smb_hdr)); memset(server->bigbuf, 0, header_size());
} }
if (!server->smallbuf) { if (!server->smallbuf) {
...@@ -364,7 +554,7 @@ allocate_buffers(struct TCP_Server_Info *server) ...@@ -364,7 +554,7 @@ allocate_buffers(struct TCP_Server_Info *server)
/* beginning of smb buffer is cleared in our buf_get */ /* beginning of smb buffer is cleared in our buf_get */
} else { } else {
/* if existing small buf clear beginning */ /* if existing small buf clear beginning */
memset(server->smallbuf, 0, sizeof(struct smb_hdr)); memset(server->smallbuf, 0, header_size());
} }
return true; return true;
...@@ -566,15 +756,16 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type) ...@@ -566,15 +756,16 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
} }
static struct mid_q_entry * static struct mid_q_entry *
find_mid(struct TCP_Server_Info *server, struct smb_hdr *buf) find_mid(struct TCP_Server_Info *server, char *buffer)
{ {
struct smb_hdr *buf = (struct smb_hdr *)buffer;
struct mid_q_entry *mid; struct mid_q_entry *mid;
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) { list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if (mid->mid == buf->Mid && if (mid->mid == buf->Mid &&
mid->midState == MID_REQUEST_SUBMITTED && mid->mid_state == MID_REQUEST_SUBMITTED &&
mid->command == buf->Command) { le16_to_cpu(mid->command) == buf->Command) {
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
return mid; return mid;
} }
...@@ -591,16 +782,16 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed) ...@@ -591,16 +782,16 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
#endif #endif
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (!malformed) if (!malformed)
mid->midState = MID_RESPONSE_RECEIVED; mid->mid_state = MID_RESPONSE_RECEIVED;
else else
mid->midState = MID_RESPONSE_MALFORMED; mid->mid_state = MID_RESPONSE_MALFORMED;
list_del_init(&mid->qhead); list_del_init(&mid->qhead);
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
} }
static void static void
handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
struct smb_hdr *buf, int malformed) char *buf, int malformed)
{ {
if (malformed == 0 && check2ndT2(buf) > 0) { if (malformed == 0 && check2ndT2(buf) > 0) {
mid->multiRsp = true; mid->multiRsp = true;
...@@ -620,13 +811,13 @@ handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, ...@@ -620,13 +811,13 @@ handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
} else { } else {
/* Have first buffer */ /* Have first buffer */
mid->resp_buf = buf; mid->resp_buf = buf;
mid->largeBuf = true; mid->large_buf = true;
server->bigbuf = NULL; server->bigbuf = NULL;
} }
return; return;
} }
mid->resp_buf = buf; mid->resp_buf = buf;
mid->largeBuf = server->large_buf; mid->large_buf = server->large_buf;
/* Was previous buf put in mpx struct for multi-rsp? */ /* Was previous buf put in mpx struct for multi-rsp? */
if (!mid->multiRsp) { if (!mid->multiRsp) {
/* smb buffer will be freed by user thread */ /* smb buffer will be freed by user thread */
...@@ -682,8 +873,8 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) ...@@ -682,8 +873,8 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead); mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cFYI(1, "Clearing mid 0x%x", mid_entry->mid); cFYI(1, "Clearing mid 0x%llx", mid_entry->mid);
mid_entry->midState = MID_SHUTDOWN; mid_entry->mid_state = MID_SHUTDOWN;
list_move(&mid_entry->qhead, &dispose_list); list_move(&mid_entry->qhead, &dispose_list);
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -691,7 +882,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) ...@@ -691,7 +882,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
/* now walk dispose list and issue callbacks */ /* now walk dispose list and issue callbacks */
list_for_each_safe(tmp, tmp2, &dispose_list) { list_for_each_safe(tmp, tmp2, &dispose_list) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead); mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cFYI(1, "Callback mid 0x%x", mid_entry->mid); cFYI(1, "Callback mid 0x%llx", mid_entry->mid);
list_del_init(&mid_entry->qhead); list_del_init(&mid_entry->qhead);
mid_entry->callback(mid_entry); mid_entry->callback(mid_entry);
} }
...@@ -731,11 +922,10 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -731,11 +922,10 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{ {
int length; int length;
char *buf = server->smallbuf; char *buf = server->smallbuf;
struct smb_hdr *smb_buffer = (struct smb_hdr *)buf; unsigned int pdu_length = get_rfc1002_length(buf);
unsigned int pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
/* make sure this will fit in a large buffer */ /* make sure this will fit in a large buffer */
if (pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { if (pdu_length > CIFSMaxBufSize + max_header_size() - 4) {
cERROR(1, "SMB response too long (%u bytes)", cERROR(1, "SMB response too long (%u bytes)",
pdu_length); pdu_length);
cifs_reconnect(server); cifs_reconnect(server);
...@@ -746,20 +936,18 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -746,20 +936,18 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
/* switch to large buffer if too big for a small one */ /* switch to large buffer if too big for a small one */
if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
server->large_buf = true; server->large_buf = true;
memcpy(server->bigbuf, server->smallbuf, server->total_read); memcpy(server->bigbuf, buf, server->total_read);
buf = server->bigbuf; buf = server->bigbuf;
smb_buffer = (struct smb_hdr *)buf;
} }
/* now read the rest */ /* now read the rest */
length = cifs_read_from_socket(server, length = cifs_read_from_socket(server, buf + header_size() - 1,
buf + sizeof(struct smb_hdr) - 1, pdu_length - header_size() + 1 + 4);
pdu_length - sizeof(struct smb_hdr) + 1 + 4);
if (length < 0) if (length < 0)
return length; return length;
server->total_read += length; server->total_read += length;
dump_smb(smb_buffer, server->total_read); dump_smb(buf, server->total_read);
/* /*
* We know that we received enough to get to the MID as we * We know that we received enough to get to the MID as we
...@@ -770,7 +958,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -770,7 +958,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
* 48 bytes is enough to display the header and a little bit * 48 bytes is enough to display the header and a little bit
* into the payload for debugging purposes. * into the payload for debugging purposes.
*/ */
length = checkSMB(smb_buffer, smb_buffer->Mid, server->total_read); length = checkSMB(buf, server->total_read);
if (length != 0) if (length != 0)
cifs_dump_mem("Bad SMB: ", buf, cifs_dump_mem("Bad SMB: ", buf,
min_t(unsigned int, server->total_read, 48)); min_t(unsigned int, server->total_read, 48));
...@@ -778,7 +966,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -778,7 +966,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (!mid) if (!mid)
return length; return length;
handle_mid(mid, server, smb_buffer, length); handle_mid(mid, server, buf, length);
return 0; return 0;
} }
...@@ -789,7 +977,6 @@ cifs_demultiplex_thread(void *p) ...@@ -789,7 +977,6 @@ cifs_demultiplex_thread(void *p)
struct TCP_Server_Info *server = p; struct TCP_Server_Info *server = p;
unsigned int pdu_length; unsigned int pdu_length;
char *buf = NULL; char *buf = NULL;
struct smb_hdr *smb_buffer = NULL;
struct task_struct *task_to_wake = NULL; struct task_struct *task_to_wake = NULL;
struct mid_q_entry *mid_entry; struct mid_q_entry *mid_entry;
...@@ -810,7 +997,6 @@ cifs_demultiplex_thread(void *p) ...@@ -810,7 +997,6 @@ cifs_demultiplex_thread(void *p)
continue; continue;
server->large_buf = false; server->large_buf = false;
smb_buffer = (struct smb_hdr *)server->smallbuf;
buf = server->smallbuf; buf = server->smallbuf;
pdu_length = 4; /* enough to get RFC1001 header */ pdu_length = 4; /* enough to get RFC1001 header */
...@@ -823,14 +1009,14 @@ cifs_demultiplex_thread(void *p) ...@@ -823,14 +1009,14 @@ cifs_demultiplex_thread(void *p)
* The right amount was read from socket - 4 bytes, * The right amount was read from socket - 4 bytes,
* so we can now interpret the length field. * so we can now interpret the length field.
*/ */
pdu_length = be32_to_cpu(smb_buffer->smb_buf_length); pdu_length = get_rfc1002_length(buf);
cFYI(1, "RFC1002 header 0x%x", pdu_length); cFYI(1, "RFC1002 header 0x%x", pdu_length);
if (!is_smb_response(server, buf[0])) if (!is_smb_response(server, buf[0]))
continue; continue;
/* make sure we have enough to get to the MID */ /* make sure we have enough to get to the MID */
if (pdu_length < sizeof(struct smb_hdr) - 1 - 4) { if (pdu_length < header_size() - 1 - 4) {
cERROR(1, "SMB response too short (%u bytes)", cERROR(1, "SMB response too short (%u bytes)",
pdu_length); pdu_length);
cifs_reconnect(server); cifs_reconnect(server);
...@@ -840,12 +1026,12 @@ cifs_demultiplex_thread(void *p) ...@@ -840,12 +1026,12 @@ cifs_demultiplex_thread(void *p)
/* read down to the MID */ /* read down to the MID */
length = cifs_read_from_socket(server, buf + 4, length = cifs_read_from_socket(server, buf + 4,
sizeof(struct smb_hdr) - 1 - 4); header_size() - 1 - 4);
if (length < 0) if (length < 0)
continue; continue;
server->total_read += length; server->total_read += length;
mid_entry = find_mid(server, smb_buffer); mid_entry = find_mid(server, buf);
if (!mid_entry || !mid_entry->receive) if (!mid_entry || !mid_entry->receive)
length = standard_receive3(server, mid_entry); length = standard_receive3(server, mid_entry);
...@@ -855,22 +1041,19 @@ cifs_demultiplex_thread(void *p) ...@@ -855,22 +1041,19 @@ cifs_demultiplex_thread(void *p)
if (length < 0) if (length < 0)
continue; continue;
if (server->large_buf) { if (server->large_buf)
buf = server->bigbuf; buf = server->bigbuf;
smb_buffer = (struct smb_hdr *)buf;
}
server->lstrp = jiffies; server->lstrp = jiffies;
if (mid_entry != NULL) { if (mid_entry != NULL) {
if (!mid_entry->multiRsp || mid_entry->multiEnd) if (!mid_entry->multiRsp || mid_entry->multiEnd)
mid_entry->callback(mid_entry); mid_entry->callback(mid_entry);
} else if (!is_valid_oplock_break(smb_buffer, server)) { } else if (!is_valid_oplock_break(buf, server)) {
cERROR(1, "No task to wake, unknown frame received! " cERROR(1, "No task to wake, unknown frame received! "
"NumMids %d", atomic_read(&midCount)); "NumMids %d", atomic_read(&midCount));
cifs_dump_mem("Received Data is: ", buf, cifs_dump_mem("Received Data is: ", buf, header_size());
sizeof(struct smb_hdr));
#ifdef CONFIG_CIFS_DEBUG2 #ifdef CONFIG_CIFS_DEBUG2
cifs_dump_detail(smb_buffer); cifs_dump_detail(buf);
cifs_dump_mids(server); cifs_dump_mids(server);
#endif /* CIFS_DEBUG2 */ #endif /* CIFS_DEBUG2 */
...@@ -926,23 +1109,95 @@ extract_hostname(const char *unc) ...@@ -926,23 +1109,95 @@ extract_hostname(const char *unc)
return dst; return dst;
} }
static int get_option_ul(substring_t args[], unsigned long *option)
{
int rc;
char *string;
string = match_strdup(args);
if (string == NULL)
return -ENOMEM;
rc = kstrtoul(string, 10, option);
kfree(string);
return rc;
}
static int cifs_parse_security_flavors(char *value,
struct smb_vol *vol)
{
substring_t args[MAX_OPT_ARGS];
switch (match_token(value, cifs_secflavor_tokens, args)) {
case Opt_sec_krb5:
vol->secFlg |= CIFSSEC_MAY_KRB5;
break;
case Opt_sec_krb5i:
vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN;
break;
case Opt_sec_krb5p:
/* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */
cERROR(1, "Krb5 cifs privacy not supported");
break;
case Opt_sec_ntlmssp:
vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
break;
case Opt_sec_ntlmsspi:
vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN;
break;
case Opt_ntlm:
/* ntlm is default so can be turned off too */
vol->secFlg |= CIFSSEC_MAY_NTLM;
break;
case Opt_sec_ntlmi:
vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN;
break;
case Opt_sec_nontlm:
vol->secFlg |= CIFSSEC_MAY_NTLMV2;
break;
case Opt_sec_ntlmv2i:
vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN;
break;
#ifdef CONFIG_CIFS_WEAK_PW_HASH
case Opt_sec_lanman:
vol->secFlg |= CIFSSEC_MAY_LANMAN;
break;
#endif
case Opt_sec_none:
vol->nullauth = 1;
break;
default:
cERROR(1, "bad security option: %s", value);
return 1;
}
return 0;
}
static int static int
cifs_parse_mount_options(const char *mountdata, const char *devname, cifs_parse_mount_options(const char *mountdata, const char *devname,
struct smb_vol *vol) struct smb_vol *vol)
{ {
char *value, *data, *end; char *data, *end;
char *mountdata_copy = NULL, *options; char *mountdata_copy = NULL, *options;
int err;
unsigned int temp_len, i, j; unsigned int temp_len, i, j;
char separator[2]; char separator[2];
short int override_uid = -1; short int override_uid = -1;
short int override_gid = -1; short int override_gid = -1;
bool uid_specified = false; bool uid_specified = false;
bool gid_specified = false; bool gid_specified = false;
bool sloppy = false;
char *invalid = NULL;
char *nodename = utsname()->nodename; char *nodename = utsname()->nodename;
char *string = NULL;
char *tmp_end, *value;
char delim;
separator[0] = ','; separator[0] = ',';
separator[1] = 0; separator[1] = 0;
delim = separator[0];
/* /*
* does not have to be perfect mapping since field is * does not have to be perfect mapping since field is
...@@ -981,6 +1236,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -981,6 +1236,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
options = mountdata_copy; options = mountdata_copy;
end = options + strlen(options); end = options + strlen(options);
if (strncmp(options, "sep=", 4) == 0) { if (strncmp(options, "sep=", 4) == 0) {
if (options[4] != 0) { if (options[4] != 0) {
separator[0] = options[4]; separator[0] = options[4];
...@@ -993,356 +1249,548 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -993,356 +1249,548 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->backupgid_specified = false; /* no backup intent for a group */ vol->backupgid_specified = false; /* no backup intent for a group */
while ((data = strsep(&options, separator)) != NULL) { while ((data = strsep(&options, separator)) != NULL) {
substring_t args[MAX_OPT_ARGS];
unsigned long option;
int token;
if (!*data) if (!*data)
continue; continue;
if ((value = strchr(data, '=')) != NULL)
*value++ = '\0';
/* Have to parse this before we parse for "user" */ token = match_token(data, cifs_mount_option_tokens, args);
if (strnicmp(data, "user_xattr", 10) == 0) {
switch (token) {
/* Ingnore the following */
case Opt_ignore:
break;
/* Boolean values */
case Opt_user_xattr:
vol->no_xattr = 0; vol->no_xattr = 0;
} else if (strnicmp(data, "nouser_xattr", 12) == 0) { break;
case Opt_nouser_xattr:
vol->no_xattr = 1; vol->no_xattr = 1;
} else if (strnicmp(data, "user", 4) == 0) { break;
if (!value) { case Opt_forceuid:
printk(KERN_WARNING override_uid = 1;
"CIFS: invalid or missing username\n"); break;
case Opt_noforceuid:
override_uid = 0;
break;
case Opt_noblocksend:
vol->noblocksnd = 1;
break;
case Opt_noautotune:
vol->noautotune = 1;
break;
case Opt_hard:
vol->retry = 1;
break;
case Opt_soft:
vol->retry = 0;
break;
case Opt_perm:
vol->noperm = 0;
break;
case Opt_noperm:
vol->noperm = 1;
break;
case Opt_mapchars:
vol->remap = 1;
break;
case Opt_nomapchars:
vol->remap = 0;
break;
case Opt_sfu:
vol->sfu_emul = 1;
break;
case Opt_nosfu:
vol->sfu_emul = 0;
break;
case Opt_nodfs:
vol->nodfs = 1;
break;
case Opt_posixpaths:
vol->posix_paths = 1;
break;
case Opt_noposixpaths:
vol->posix_paths = 0;
break;
case Opt_nounix:
vol->no_linux_ext = 1;
break;
case Opt_nocase:
vol->nocase = 1;
break;
case Opt_brl:
vol->nobrl = 0;
break;
case Opt_nobrl:
vol->nobrl = 1;
/*
* turn off mandatory locking in mode
* if remote locking is turned off since the
* local vfs will do advisory
*/
if (vol->file_mode ==
(S_IALLUGO & ~(S_ISUID | S_IXGRP)))
vol->file_mode = S_IALLUGO;
break;
case Opt_forcemandatorylock:
vol->mand_lock = 1;
break;
case Opt_setuids:
vol->setuids = 1;
break;
case Opt_nosetuids:
vol->setuids = 0;
break;
case Opt_dynperm:
vol->dynperm = true;
break;
case Opt_nodynperm:
vol->dynperm = false;
break;
case Opt_nohard:
vol->retry = 0;
break;
case Opt_nosoft:
vol->retry = 1;
break;
case Opt_nointr:
vol->intr = 0;
break;
case Opt_intr:
vol->intr = 1;
break;
case Opt_nostrictsync:
vol->nostrictsync = 1;
break;
case Opt_strictsync:
vol->nostrictsync = 0;
break;
case Opt_serverino:
vol->server_ino = 1;
break;
case Opt_noserverino:
vol->server_ino = 0;
break;
case Opt_rwpidforward:
vol->rwpidforward = 1;
break;
case Opt_cifsacl:
vol->cifs_acl = 1;
break;
case Opt_nocifsacl:
vol->cifs_acl = 0;
break;
case Opt_acl:
vol->no_psx_acl = 0;
break;
case Opt_noacl:
vol->no_psx_acl = 1;
break;
case Opt_locallease:
vol->local_lease = 1;
break;
case Opt_sign:
vol->secFlg |= CIFSSEC_MUST_SIGN;
break;
case Opt_seal:
/* we do not do the following in secFlags because seal
* is a per tree connection (mount) not a per socket
* or per-smb connection option in the protocol
* vol->secFlg |= CIFSSEC_MUST_SEAL;
*/
vol->seal = 1;
break;
case Opt_direct:
vol->direct_io = 1;
break;
case Opt_strictcache:
vol->strict_io = 1;
break;
case Opt_noac:
printk(KERN_WARNING "CIFS: Mount option noac not "
"supported. Instead set "
"/proc/fs/cifs/LookupCacheEnabled to 0\n");
break;
case Opt_fsc:
#ifndef CONFIG_CIFS_FSCACHE
cERROR(1, "FS-Cache support needs CONFIG_CIFS_FSCACHE "
"kernel config option set");
goto cifs_parse_mount_err;
#endif
vol->fsc = true;
break;
case Opt_mfsymlinks:
vol->mfsymlinks = true;
break;
case Opt_multiuser:
vol->multiuser = true;
break;
case Opt_sloppy:
sloppy = true;
break;
/* Numeric Values */
case Opt_backupuid:
if (get_option_ul(args, &option)) {
cERROR(1, "%s: Invalid backupuid value",
__func__);
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} else if (!*value) {
/* null user, ie anonymous, authentication */
vol->nullauth = 1;
} }
if (strnlen(value, MAX_USERNAME_SIZE) < vol->backupuid = option;
MAX_USERNAME_SIZE) { vol->backupuid_specified = true;
vol->username = kstrdup(value, GFP_KERNEL); break;
if (!vol->username) { case Opt_backupgid:
printk(KERN_WARNING "CIFS: no memory " if (get_option_ul(args, &option)) {
"for username\n"); cERROR(1, "%s: Invalid backupgid value",
__func__);
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} else { vol->backupgid = option;
printk(KERN_WARNING "CIFS: username too long\n"); vol->backupgid_specified = true;
break;
case Opt_uid:
if (get_option_ul(args, &option)) {
cERROR(1, "%s: Invalid uid value",
__func__);
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "pass", 4) == 0) { vol->linux_uid = option;
if (!value) { uid_specified = true;
vol->password = NULL; break;
continue; case Opt_cruid:
} else if (value[0] == 0) { if (get_option_ul(args, &option)) {
/* check if string begins with double comma cERROR(1, "%s: Invalid cruid value",
since that would mean the password really __func__);
does start with a comma, and would not goto cifs_parse_mount_err;
indicate an empty string */
if (value[1] != separator[0]) {
vol->password = NULL;
continue;
} }
vol->cred_uid = option;
break;
case Opt_gid:
if (get_option_ul(args, &option)) {
cERROR(1, "%s: Invalid gid value",
__func__);
goto cifs_parse_mount_err;
} }
temp_len = strlen(value); vol->linux_gid = option;
/* removed password length check, NTLM passwords gid_specified = true;
can be arbitrarily long */ break;
case Opt_file_mode:
/* if comma in password, the string will be if (get_option_ul(args, &option)) {
prematurely null terminated. Commas in password are cERROR(1, "%s: Invalid file_mode value",
specified across the cifs mount interface by a double __func__);
comma ie ,, and a comma used as in other cases ie ',' goto cifs_parse_mount_err;
as a parameter delimiter/separator is single and due }
to the strsep above is temporarily zeroed. */ vol->file_mode = option;
/* NB: password legally can have multiple commas and
the only illegal character in a password is null */
if ((value[temp_len] == 0) &&
(value + temp_len < end) &&
(value[temp_len+1] == separator[0])) {
/* reinsert comma */
value[temp_len] = separator[0];
temp_len += 2; /* move after second comma */
while (value[temp_len] != 0) {
if (value[temp_len] == separator[0]) {
if (value[temp_len+1] ==
separator[0]) {
/* skip second comma */
temp_len++;
} else {
/* single comma indicating start
of next parm */
break; break;
case Opt_dirmode:
if (get_option_ul(args, &option)) {
cERROR(1, "%s: Invalid dir_mode value",
__func__);
goto cifs_parse_mount_err;
} }
vol->dir_mode = option;
break;
case Opt_port:
if (get_option_ul(args, &option)) {
cERROR(1, "%s: Invalid port value",
__func__);
goto cifs_parse_mount_err;
} }
temp_len++; vol->port = option;
break;
case Opt_rsize:
if (get_option_ul(args, &option)) {
cERROR(1, "%s: Invalid rsize value",
__func__);
goto cifs_parse_mount_err;
} }
if (value[temp_len] == 0) { vol->rsize = option;
options = NULL; break;
} else { case Opt_wsize:
value[temp_len] = 0; if (get_option_ul(args, &option)) {
/* point option to start of next parm */ cERROR(1, "%s: Invalid wsize value",
options = value + temp_len + 1; __func__);
}
/* go from value to value + temp_len condensing
double commas to singles. Note that this ends up
allocating a few bytes too many, which is ok */
vol->password = kzalloc(temp_len, GFP_KERNEL);
if (vol->password == NULL) {
printk(KERN_WARNING "CIFS: no memory "
"for password\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
for (i = 0, j = 0; i < temp_len; i++, j++) { vol->wsize = option;
vol->password[j] = value[i]; break;
if (value[i] == separator[0] case Opt_actimeo:
&& value[i+1] == separator[0]) { if (get_option_ul(args, &option)) {
/* skip second comma */ cERROR(1, "%s: Invalid actimeo value",
i++; __func__);
goto cifs_parse_mount_err;
} }
vol->actimeo = HZ * option;
if (vol->actimeo > CIFS_MAX_ACTIMEO) {
cERROR(1, "CIFS: attribute cache"
"timeout too large");
goto cifs_parse_mount_err;
} }
vol->password[j] = 0; break;
} else {
/* String Arguments */
case Opt_user:
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
if (!*string) {
/* null user, ie. anonymous authentication */
vol->nullauth = 1;
} else if (strnlen(string, MAX_USERNAME_SIZE) >
MAX_USERNAME_SIZE) {
printk(KERN_WARNING "CIFS: username too long\n");
goto cifs_parse_mount_err;
}
vol->username = kstrdup(string, GFP_KERNEL);
if (!vol->username) {
printk(KERN_WARNING "CIFS: no memory "
"for username\n");
goto cifs_parse_mount_err;
}
break;
case Opt_blank_pass:
vol->password = NULL;
break;
case Opt_pass:
/* passwords have to be handled differently
* to allow the character used for deliminator
* to be passed within them
*/
/* Obtain the value string */
value = strchr(data, '=');
if (value != NULL)
*value++ = '\0';
/* Set tmp_end to end of the string */
tmp_end = (char *) value + strlen(value);
/* Check if following character is the deliminator
* If yes, we have encountered a double deliminator
* reset the NULL character to the deliminator
*/
if (tmp_end < end && tmp_end[1] == delim)
tmp_end[0] = delim;
/* Keep iterating until we get to a single deliminator
* OR the end
*/
while ((tmp_end = strchr(tmp_end, delim)) != NULL &&
(tmp_end[1] == delim)) {
tmp_end = (char *) &tmp_end[2];
}
/* Reset var options to point to next element */
if (tmp_end) {
tmp_end[0] = '\0';
options = (char *) &tmp_end[1];
} else
/* Reached the end of the mount option string */
options = end;
/* Now build new password string */
temp_len = strlen(value);
vol->password = kzalloc(temp_len+1, GFP_KERNEL); vol->password = kzalloc(temp_len+1, GFP_KERNEL);
if (vol->password == NULL) { if (vol->password == NULL) {
printk(KERN_WARNING "CIFS: no memory " printk(KERN_WARNING "CIFS: no memory "
"for password\n"); "for password\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
strcpy(vol->password, value);
for (i = 0, j = 0; i < temp_len; i++, j++) {
vol->password[j] = value[i];
if ((value[i] == delim) &&
value[i+1] == delim)
/* skip the second deliminator */
i++;
} }
} else if (!strnicmp(data, "ip", 2) || vol->password[j] = '\0';
!strnicmp(data, "addr", 4)) { break;
if (!value || !*value) { case Opt_ip:
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
if (!*string) {
vol->UNCip = NULL; vol->UNCip = NULL;
} else if (strnlen(value, INET6_ADDRSTRLEN) < } else if (strnlen(string, INET6_ADDRSTRLEN) >
INET6_ADDRSTRLEN) { INET6_ADDRSTRLEN) {
vol->UNCip = kstrdup(value, GFP_KERNEL);
if (!vol->UNCip) {
printk(KERN_WARNING "CIFS: no memory "
"for UNC IP\n");
goto cifs_parse_mount_err;
}
} else {
printk(KERN_WARNING "CIFS: ip address " printk(KERN_WARNING "CIFS: ip address "
"too long\n"); "too long\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "sec", 3) == 0) { vol->UNCip = kstrdup(string, GFP_KERNEL);
if (!value || !*value) { if (!vol->UNCip) {
cERROR(1, "no security value specified"); printk(KERN_WARNING "CIFS: no memory "
continue; "for UNC IP\n");
} else if (strnicmp(value, "krb5i", 5) == 0) {
vol->secFlg |= CIFSSEC_MAY_KRB5 |
CIFSSEC_MUST_SIGN;
} else if (strnicmp(value, "krb5p", 5) == 0) {
/* vol->secFlg |= CIFSSEC_MUST_SEAL |
CIFSSEC_MAY_KRB5; */
cERROR(1, "Krb5 cifs privacy not supported");
goto cifs_parse_mount_err;
} else if (strnicmp(value, "krb5", 4) == 0) {
vol->secFlg |= CIFSSEC_MAY_KRB5;
} else if (strnicmp(value, "ntlmsspi", 8) == 0) {
vol->secFlg |= CIFSSEC_MAY_NTLMSSP |
CIFSSEC_MUST_SIGN;
} else if (strnicmp(value, "ntlmssp", 7) == 0) {
vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
} else if (strnicmp(value, "ntlmv2i", 7) == 0) {
vol->secFlg |= CIFSSEC_MAY_NTLMV2 |
CIFSSEC_MUST_SIGN;
} else if (strnicmp(value, "ntlmv2", 6) == 0) {
vol->secFlg |= CIFSSEC_MAY_NTLMV2;
} else if (strnicmp(value, "ntlmi", 5) == 0) {
vol->secFlg |= CIFSSEC_MAY_NTLM |
CIFSSEC_MUST_SIGN;
} else if (strnicmp(value, "ntlm", 4) == 0) {
/* ntlm is default so can be turned off too */
vol->secFlg |= CIFSSEC_MAY_NTLM;
} else if (strnicmp(value, "nontlm", 6) == 0) {
/* BB is there a better way to do this? */
vol->secFlg |= CIFSSEC_MAY_NTLMV2;
#ifdef CONFIG_CIFS_WEAK_PW_HASH
} else if (strnicmp(value, "lanman", 6) == 0) {
vol->secFlg |= CIFSSEC_MAY_LANMAN;
#endif
} else if (strnicmp(value, "none", 4) == 0) {
vol->nullauth = 1;
} else {
cERROR(1, "bad security option: %s", value);
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "vers", 3) == 0) { break;
if (!value || !*value) { case Opt_unc:
cERROR(1, "no protocol version specified" string = match_strdup(args);
" after vers= mount option"); if (string == NULL)
} else if ((strnicmp(value, "cifs", 4) == 0) || goto out_nomem;
(strnicmp(value, "1", 1) == 0)) {
/* this is the default */ if (!*string) {
continue;
}
} else if ((strnicmp(data, "unc", 3) == 0)
|| (strnicmp(data, "target", 6) == 0)
|| (strnicmp(data, "path", 4) == 0)) {
if (!value || !*value) {
printk(KERN_WARNING "CIFS: invalid path to " printk(KERN_WARNING "CIFS: invalid path to "
"network resource\n"); "network resource\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
if ((temp_len = strnlen(value, 300)) < 300) {
vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); temp_len = strnlen(string, 300);
if (vol->UNC == NULL) if (temp_len == 300) {
printk(KERN_WARNING "CIFS: UNC name too long\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
strcpy(vol->UNC, value); }
if (strncmp(vol->UNC, "//", 2) == 0) {
if (strncmp(string, "//", 2) == 0) {
vol->UNC[0] = '\\'; vol->UNC[0] = '\\';
vol->UNC[1] = '\\'; vol->UNC[1] = '\\';
} else if (strncmp(vol->UNC, "\\\\", 2) != 0) { } else if (strncmp(string, "\\\\", 2) != 0) {
printk(KERN_WARNING printk(KERN_WARNING "CIFS: UNC Path does not "
"CIFS: UNC Path does not begin " "begin with // or \\\\\n");
"with // or \\\\ \n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} else {
printk(KERN_WARNING "CIFS: UNC name too long\n"); vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
if (vol->UNC == NULL) {
printk(KERN_WARNING "CIFS: no memory "
"for UNC\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} else if ((strnicmp(data, "domain", 3) == 0) strcpy(vol->UNC, string);
|| (strnicmp(data, "workgroup", 5) == 0)) { break;
if (!value || !*value) { case Opt_domain:
printk(KERN_WARNING "CIFS: invalid domain name\n"); string = match_strdup(args);
if (string == NULL)
goto out_nomem;
if (!*string) {
printk(KERN_WARNING "CIFS: invalid domain"
" name\n");
goto cifs_parse_mount_err;
} else if (strnlen(string, 256) == 256) {
printk(KERN_WARNING "CIFS: domain name too"
" long\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
/* BB are there cases in which a comma can be valid in
a domain name and need special handling? */ vol->domainname = kstrdup(string, GFP_KERNEL);
if (strnlen(value, 256) < 256) {
vol->domainname = kstrdup(value, GFP_KERNEL);
if (!vol->domainname) { if (!vol->domainname) {
printk(KERN_WARNING "CIFS: no memory " printk(KERN_WARNING "CIFS: no memory "
"for domainname\n"); "for domainname\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
cFYI(1, "Domain name set"); cFYI(1, "Domain name set");
} else { break;
printk(KERN_WARNING "CIFS: domain name too " case Opt_srcaddr:
"long\n"); string = match_strdup(args);
goto cifs_parse_mount_err; if (string == NULL)
} goto out_nomem;
} else if (strnicmp(data, "srcaddr", 7) == 0) {
vol->srcaddr.ss_family = AF_UNSPEC; if (!*string) {
printk(KERN_WARNING "CIFS: srcaddr value not"
if (!value || !*value) { " specified\n");
printk(KERN_WARNING "CIFS: srcaddr value"
" not specified.\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} } else if (!cifs_convert_address(
i = cifs_convert_address((struct sockaddr *)&vol->srcaddr, (struct sockaddr *)&vol->srcaddr,
value, strlen(value)); string, strlen(string))) {
if (i == 0) {
printk(KERN_WARNING "CIFS: Could not parse" printk(KERN_WARNING "CIFS: Could not parse"
" srcaddr: %s\n", " srcaddr: %s\n", string);
value);
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "prefixpath", 10) == 0) { break;
if (!value || !*value) { case Opt_prefixpath:
printk(KERN_WARNING string = match_strdup(args);
"CIFS: invalid path prefix\n"); if (string == NULL)
goto out_nomem;
if (!*string) {
printk(KERN_WARNING "CIFS: Invalid path"
" prefix\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
if ((temp_len = strnlen(value, 1024)) < 1024) { temp_len = strnlen(string, 1024);
if (value[0] != '/') if (string[0] != '/')
temp_len++; /* missing leading slash */ temp_len++; /* missing leading slash */
if (temp_len > 1024) {
printk(KERN_WARNING "CIFS: prefix too long\n");
goto cifs_parse_mount_err;
}
vol->prepath = kmalloc(temp_len+1, GFP_KERNEL); vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
if (vol->prepath == NULL) if (vol->prepath == NULL) {
printk(KERN_WARNING "CIFS: no memory "
"for path prefix\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
if (value[0] != '/') { }
if (string[0] != '/') {
vol->prepath[0] = '/'; vol->prepath[0] = '/';
strcpy(vol->prepath+1, value); strcpy(vol->prepath+1, string);
} else } else
strcpy(vol->prepath, value); strcpy(vol->prepath, string);
cFYI(1, "prefix path %s", vol->prepath);
} else { break;
printk(KERN_WARNING "CIFS: prefix too long\n"); case Opt_iocharset:
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
if (!*string) {
printk(KERN_WARNING "CIFS: Invalid iocharset"
" specified\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} } else if (strnlen(string, 1024) >= 65) {
} else if (strnicmp(data, "iocharset", 9) == 0) { printk(KERN_WARNING "CIFS: iocharset name "
if (!value || !*value) { "too long.\n");
printk(KERN_WARNING "CIFS: invalid iocharset "
"specified\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
if (strnlen(value, 65) < 65) {
if (strnicmp(value, "default", 7)) {
vol->iocharset = kstrdup(value,
GFP_KERNEL);
if (strnicmp(string, "default", 7) != 0) {
vol->iocharset = kstrdup(string,
GFP_KERNEL);
if (!vol->iocharset) { if (!vol->iocharset) {
printk(KERN_WARNING "CIFS: no " printk(KERN_WARNING "CIFS: no memory"
"memory for" "for charset\n");
"charset\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} }
/* if iocharset not set then load_nls_default /* if iocharset not set then load_nls_default
is used by caller */ * is used by caller
cFYI(1, "iocharset set to %s", value); */
} else { cFYI(1, "iocharset set to %s", string);
printk(KERN_WARNING "CIFS: iocharset name " break;
"too long.\n"); case Opt_sockopt:
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
if (!*string) {
printk(KERN_WARNING "CIFS: No socket option"
" specified\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
} else if (!strnicmp(data, "uid", 3) && value && *value) { if (strnicmp(string, "TCP_NODELAY", 11) == 0)
vol->linux_uid = simple_strtoul(value, &value, 0);
uid_specified = true;
} else if (!strnicmp(data, "cruid", 5) && value && *value) {
vol->cred_uid = simple_strtoul(value, &value, 0);
} else if (!strnicmp(data, "forceuid", 8)) {
override_uid = 1;
} else if (!strnicmp(data, "noforceuid", 10)) {
override_uid = 0;
} else if (!strnicmp(data, "gid", 3) && value && *value) {
vol->linux_gid = simple_strtoul(value, &value, 0);
gid_specified = true;
} else if (!strnicmp(data, "forcegid", 8)) {
override_gid = 1;
} else if (!strnicmp(data, "noforcegid", 10)) {
override_gid = 0;
} else if (strnicmp(data, "file_mode", 4) == 0) {
if (value && *value) {
vol->file_mode =
simple_strtoul(value, &value, 0);
}
} else if (strnicmp(data, "dir_mode", 4) == 0) {
if (value && *value) {
vol->dir_mode =
simple_strtoul(value, &value, 0);
}
} else if (strnicmp(data, "dirmode", 4) == 0) {
if (value && *value) {
vol->dir_mode =
simple_strtoul(value, &value, 0);
}
} else if (strnicmp(data, "port", 4) == 0) {
if (value && *value) {
vol->port =
simple_strtoul(value, &value, 0);
}
} else if (strnicmp(data, "rsize", 5) == 0) {
if (value && *value) {
vol->rsize =
simple_strtoul(value, &value, 0);
}
} else if (strnicmp(data, "wsize", 5) == 0) {
if (value && *value) {
vol->wsize =
simple_strtoul(value, &value, 0);
}
} else if (strnicmp(data, "sockopt", 5) == 0) {
if (!value || !*value) {
cERROR(1, "no socket option specified");
continue;
} else if (strnicmp(value, "TCP_NODELAY", 11) == 0) {
vol->sockopt_tcp_nodelay = 1; vol->sockopt_tcp_nodelay = 1;
break;
case Opt_netbiosname:
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
if (!*string) {
printk(KERN_WARNING "CIFS: Invalid (empty)"
" netbiosname\n");
break;
} }
} else if (strnicmp(data, "netbiosname", 4) == 0) {
if (!value || !*value || (*value == ' ')) {
cFYI(1, "invalid (empty) netbiosname");
} else {
memset(vol->source_rfc1001_name, 0x20, memset(vol->source_rfc1001_name, 0x20,
RFC1001_NAME_LEN); RFC1001_NAME_LEN);
/* /*
...@@ -1352,251 +1800,102 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1352,251 +1800,102 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
*/ */
for (i = 0; i < RFC1001_NAME_LEN; i++) { for (i = 0; i < RFC1001_NAME_LEN; i++) {
/* don't ucase netbiosname for user */ /* don't ucase netbiosname for user */
if (value[i] == 0) if (string[i] == 0)
break; break;
vol->source_rfc1001_name[i] = value[i]; vol->source_rfc1001_name[i] = string[i];
} }
/* The string has 16th byte zero still from /* The string has 16th byte zero still from
set at top of the function */ * set at top of the function
if (i == RFC1001_NAME_LEN && value[i] != 0) */
if (i == RFC1001_NAME_LEN && string[i] != 0)
printk(KERN_WARNING "CIFS: netbiosname" printk(KERN_WARNING "CIFS: netbiosname"
" longer than 15 truncated.\n"); " longer than 15 truncated.\n");
}
} else if (strnicmp(data, "servern", 7) == 0) { break;
case Opt_servern:
/* servernetbiosname specified override *SMBSERVER */ /* servernetbiosname specified override *SMBSERVER */
if (!value || !*value || (*value == ' ')) { string = match_strdup(args);
cFYI(1, "empty server netbiosname specified"); if (string == NULL)
} else { goto out_nomem;
if (!*string) {
printk(KERN_WARNING "CIFS: Empty server"
" netbiosname specified\n");
break;
}
/* last byte, type, is 0x20 for servr type */ /* last byte, type, is 0x20 for servr type */
memset(vol->target_rfc1001_name, 0x20, memset(vol->target_rfc1001_name, 0x20,
RFC1001_NAME_LEN_WITH_NULL); RFC1001_NAME_LEN_WITH_NULL);
for (i = 0; i < 15; i++) {
/* BB are there cases in which a comma can be /* BB are there cases in which a comma can be
valid in this workstation netbios name valid in this workstation netbios name
(and need special handling)? */ (and need special handling)? */
/* user or mount helper must uppercase /* user or mount helper must uppercase the
the netbiosname */ netbios name */
if (value[i] == 0) for (i = 0; i < 15; i++) {
if (string[i] == 0)
break; break;
else vol->target_rfc1001_name[i] = string[i];
vol->target_rfc1001_name[i] =
value[i];
} }
/* The string has 16th byte zero still from /* The string has 16th byte zero still from
set at top of the function */ set at top of the function */
if (i == RFC1001_NAME_LEN && value[i] != 0) if (i == RFC1001_NAME_LEN && string[i] != 0)
printk(KERN_WARNING "CIFS: server net" printk(KERN_WARNING "CIFS: server net"
"biosname longer than 15 truncated.\n"); "biosname longer than 15 truncated.\n");
} break;
} else if (strnicmp(data, "actimeo", 7) == 0) { case Opt_ver:
if (value && *value) { string = match_strdup(args);
vol->actimeo = HZ * simple_strtoul(value, if (string == NULL)
&value, 0); goto out_nomem;
if (vol->actimeo > CIFS_MAX_ACTIMEO) {
cERROR(1, "CIFS: attribute cache" if (!*string) {
"timeout too large"); cERROR(1, "no protocol version specified"
" after vers= mount option");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
if (strnicmp(string, "cifs", 4) == 0 ||
strnicmp(string, "1", 1) == 0) {
/* This is the default */
break;
} }
} else if (strnicmp(data, "credentials", 4) == 0) { /* For all other value, error */
/* ignore */ printk(KERN_WARNING "CIFS: Invalid version"
} else if (strnicmp(data, "version", 3) == 0) { " specified\n");
/* ignore */
} else if (strnicmp(data, "guest", 5) == 0) {
/* ignore */
} else if (strnicmp(data, "rw", 2) == 0 && strlen(data) == 2) {
/* ignore */
} else if (strnicmp(data, "ro", 2) == 0) {
/* ignore */
} else if (strnicmp(data, "noblocksend", 11) == 0) {
vol->noblocksnd = 1;
} else if (strnicmp(data, "noautotune", 10) == 0) {
vol->noautotune = 1;
} else if ((strnicmp(data, "suid", 4) == 0) ||
(strnicmp(data, "nosuid", 6) == 0) ||
(strnicmp(data, "exec", 4) == 0) ||
(strnicmp(data, "noexec", 6) == 0) ||
(strnicmp(data, "nodev", 5) == 0) ||
(strnicmp(data, "noauto", 6) == 0) ||
(strnicmp(data, "dev", 3) == 0)) {
/* The mount tool or mount.cifs helper (if present)
uses these opts to set flags, and the flags are read
by the kernel vfs layer before we get here (ie
before read super) so there is no point trying to
parse these options again and set anything and it
is ok to just ignore them */
continue;
} else if (strnicmp(data, "hard", 4) == 0) {
vol->retry = 1;
} else if (strnicmp(data, "soft", 4) == 0) {
vol->retry = 0;
} else if (strnicmp(data, "perm", 4) == 0) {
vol->noperm = 0;
} else if (strnicmp(data, "noperm", 6) == 0) {
vol->noperm = 1;
} else if (strnicmp(data, "mapchars", 8) == 0) {
vol->remap = 1;
} else if (strnicmp(data, "nomapchars", 10) == 0) {
vol->remap = 0;
} else if (strnicmp(data, "sfu", 3) == 0) {
vol->sfu_emul = 1;
} else if (strnicmp(data, "nosfu", 5) == 0) {
vol->sfu_emul = 0;
} else if (strnicmp(data, "nodfs", 5) == 0) {
vol->nodfs = 1;
} else if (strnicmp(data, "posixpaths", 10) == 0) {
vol->posix_paths = 1;
} else if (strnicmp(data, "noposixpaths", 12) == 0) {
vol->posix_paths = 0;
} else if (strnicmp(data, "nounix", 6) == 0) {
vol->no_linux_ext = 1;
} else if (strnicmp(data, "nolinux", 7) == 0) {
vol->no_linux_ext = 1;
} else if ((strnicmp(data, "nocase", 6) == 0) ||
(strnicmp(data, "ignorecase", 10) == 0)) {
vol->nocase = 1;
} else if (strnicmp(data, "mand", 4) == 0) {
/* ignore */
} else if (strnicmp(data, "nomand", 6) == 0) {
/* ignore */
} else if (strnicmp(data, "_netdev", 7) == 0) {
/* ignore */
} else if (strnicmp(data, "brl", 3) == 0) {
vol->nobrl = 0;
} else if ((strnicmp(data, "nobrl", 5) == 0) ||
(strnicmp(data, "nolock", 6) == 0)) {
vol->nobrl = 1;
/* turn off mandatory locking in mode
if remote locking is turned off since the
local vfs will do advisory */
if (vol->file_mode ==
(S_IALLUGO & ~(S_ISUID | S_IXGRP)))
vol->file_mode = S_IALLUGO;
} else if (strnicmp(data, "forcemandatorylock", 9) == 0) {
/* will take the shorter form "forcemand" as well */
/* This mount option will force use of mandatory
(DOS/Windows style) byte range locks, instead of
using posix advisory byte range locks, even if the
Unix extensions are available and posix locks would
be supported otherwise. If Unix extensions are not
negotiated this has no effect since mandatory locks
would be used (mandatory locks is all that those
those servers support) */
vol->mand_lock = 1;
} else if (strnicmp(data, "setuids", 7) == 0) {
vol->setuids = 1;
} else if (strnicmp(data, "nosetuids", 9) == 0) {
vol->setuids = 0;
} else if (strnicmp(data, "dynperm", 7) == 0) {
vol->dynperm = true;
} else if (strnicmp(data, "nodynperm", 9) == 0) {
vol->dynperm = false;
} else if (strnicmp(data, "nohard", 6) == 0) {
vol->retry = 0;
} else if (strnicmp(data, "nosoft", 6) == 0) {
vol->retry = 1;
} else if (strnicmp(data, "nointr", 6) == 0) {
vol->intr = 0;
} else if (strnicmp(data, "intr", 4) == 0) {
vol->intr = 1;
} else if (strnicmp(data, "nostrictsync", 12) == 0) {
vol->nostrictsync = 1;
} else if (strnicmp(data, "strictsync", 10) == 0) {
vol->nostrictsync = 0;
} else if (strnicmp(data, "serverino", 7) == 0) {
vol->server_ino = 1;
} else if (strnicmp(data, "noserverino", 9) == 0) {
vol->server_ino = 0;
} else if (strnicmp(data, "rwpidforward", 12) == 0) {
vol->rwpidforward = 1;
} else if (strnicmp(data, "cifsacl", 7) == 0) {
vol->cifs_acl = 1;
} else if (strnicmp(data, "nocifsacl", 9) == 0) {
vol->cifs_acl = 0;
} else if (strnicmp(data, "acl", 3) == 0) {
vol->no_psx_acl = 0;
} else if (strnicmp(data, "noacl", 5) == 0) {
vol->no_psx_acl = 1;
} else if (strnicmp(data, "locallease", 6) == 0) {
vol->local_lease = 1;
} else if (strnicmp(data, "sign", 4) == 0) {
vol->secFlg |= CIFSSEC_MUST_SIGN;
} else if (strnicmp(data, "seal", 4) == 0) {
/* we do not do the following in secFlags because seal
is a per tree connection (mount) not a per socket
or per-smb connection option in the protocol */
/* vol->secFlg |= CIFSSEC_MUST_SEAL; */
vol->seal = 1;
} else if (strnicmp(data, "direct", 6) == 0) {
vol->direct_io = 1;
} else if (strnicmp(data, "forcedirectio", 13) == 0) {
vol->direct_io = 1;
} else if (strnicmp(data, "strictcache", 11) == 0) {
vol->strict_io = 1;
} else if (strnicmp(data, "noac", 4) == 0) {
printk(KERN_WARNING "CIFS: Mount option noac not "
"supported. Instead set "
"/proc/fs/cifs/LookupCacheEnabled to 0\n");
} else if (strnicmp(data, "fsc", 3) == 0) {
#ifndef CONFIG_CIFS_FSCACHE
cERROR(1, "FS-Cache support needs CONFIG_CIFS_FSCACHE "
"kernel config option set");
goto cifs_parse_mount_err;
#endif
vol->fsc = true;
} else if (strnicmp(data, "mfsymlinks", 10) == 0) {
vol->mfsymlinks = true;
} else if (strnicmp(data, "multiuser", 8) == 0) {
vol->multiuser = true;
} else if (!strnicmp(data, "backupuid", 9) && value && *value) {
err = kstrtouint(value, 0, &vol->backupuid);
if (err < 0) {
cERROR(1, "%s: Invalid backupuid value",
__func__);
goto cifs_parse_mount_err;
}
vol->backupuid_specified = true;
} else if (!strnicmp(data, "backupgid", 9) && value && *value) {
err = kstrtouint(value, 0, &vol->backupgid);
if (err < 0) {
cERROR(1, "%s: Invalid backupgid value",
__func__);
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
case Opt_sec:
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
if (!*string) {
printk(KERN_WARNING "CIFS: no security flavor"
" specified\n");
break;
} }
vol->backupgid_specified = true;
} else if (cifs_parse_security_flavors(string, vol) != 0)
printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
data);
}
if (vol->UNC == NULL) {
if (devname == NULL) {
printk(KERN_WARNING "CIFS: Missing UNC name for mount "
"target\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
break;
default:
/*
* An option we don't recognize. Save it off for later
* if we haven't already found one
*/
if (!invalid)
invalid = data;
break;
} }
if ((temp_len = strnlen(devname, 300)) < 300) { /* Free up any allocated string */
vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); kfree(string);
if (vol->UNC == NULL) string = NULL;
goto cifs_parse_mount_err;
strcpy(vol->UNC, devname);
if (strncmp(vol->UNC, "//", 2) == 0) {
vol->UNC[0] = '\\';
vol->UNC[1] = '\\';
} else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
printk(KERN_WARNING "CIFS: UNC Path does not "
"begin with // or \\\\ \n");
goto cifs_parse_mount_err;
} }
value = strpbrk(vol->UNC+2, "/\\");
if (value) if (!sloppy && invalid) {
*value = '\\'; printk(KERN_ERR "CIFS: Unknown mount option \"%s\"\n", invalid);
} else {
printk(KERN_WARNING "CIFS: UNC name too long\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
}
#ifndef CONFIG_KEYS #ifndef CONFIG_KEYS
/* Muliuser mounts require CONFIG_KEYS support */ /* Muliuser mounts require CONFIG_KEYS support */
...@@ -1625,7 +1924,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1625,7 +1924,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
kfree(mountdata_copy); kfree(mountdata_copy);
return 0; return 0;
out_nomem:
printk(KERN_WARNING "Could not allocate temporary buffer\n");
cifs_parse_mount_err: cifs_parse_mount_err:
kfree(string);
kfree(mountdata_copy); kfree(mountdata_copy);
return 1; return 1;
} }
...@@ -1977,7 +2279,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -1977,7 +2279,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
cifs_fscache_get_client_cookie(tcp_ses); cifs_fscache_get_client_cookie(tcp_ses);
/* queue echo request delayed work */ /* queue echo request delayed work */
queue_delayed_work(system_nrt_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL); queue_delayed_work(cifsiod_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL);
return tcp_ses; return tcp_ses;
...@@ -3543,7 +3845,7 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) ...@@ -3543,7 +3845,7 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
tlink_rb_insert(&cifs_sb->tlink_tree, tlink); tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
spin_unlock(&cifs_sb->tlink_tree_lock); spin_unlock(&cifs_sb->tlink_tree_lock);
queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
TLINK_IDLE_EXPIRE); TLINK_IDLE_EXPIRE);
mount_fail_check: mount_fail_check:
...@@ -4097,6 +4399,6 @@ cifs_prune_tlinks(struct work_struct *work) ...@@ -4097,6 +4399,6 @@ cifs_prune_tlinks(struct work_struct *work)
} }
spin_unlock(&cifs_sb->tlink_tree_lock); spin_unlock(&cifs_sb->tlink_tree_lock);
queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
TLINK_IDLE_EXPIRE); TLINK_IDLE_EXPIRE);
} }
...@@ -1399,7 +1399,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) ...@@ -1399,7 +1399,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
return rc; return rc;
} }
/* update the file size (if needed) after a write */ /*
* update the file size (if needed) after a write. Should be called with
* the inode->i_lock held
*/
void void
cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written) unsigned int bytes_written)
...@@ -1471,7 +1474,9 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, __u32 pid, ...@@ -1471,7 +1474,9 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, __u32 pid,
return rc; return rc;
} }
} else { } else {
spin_lock(&dentry->d_inode->i_lock);
cifs_update_eof(cifsi, *poffset, bytes_written); cifs_update_eof(cifsi, *poffset, bytes_written);
spin_unlock(&dentry->d_inode->i_lock);
*poffset += bytes_written; *poffset += bytes_written;
} }
} }
...@@ -1648,6 +1653,27 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) ...@@ -1648,6 +1653,27 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
return rc; return rc;
} }
/*
* Marshal up the iov array, reserving the first one for the header. Also,
* set wdata->bytes.
*/
static void
cifs_writepages_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata)
{
int i;
struct inode *inode = wdata->cfile->dentry->d_inode;
loff_t size = i_size_read(inode);
/* marshal up the pages into iov array */
wdata->bytes = 0;
for (i = 0; i < wdata->nr_pages; i++) {
iov[i + 1].iov_len = min(size - page_offset(wdata->pages[i]),
(loff_t)PAGE_CACHE_SIZE);
iov[i + 1].iov_base = kmap(wdata->pages[i]);
wdata->bytes += iov[i + 1].iov_len;
}
}
static int cifs_writepages(struct address_space *mapping, static int cifs_writepages(struct address_space *mapping,
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
...@@ -1684,7 +1710,8 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1684,7 +1710,8 @@ static int cifs_writepages(struct address_space *mapping,
tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1, tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1,
end - index) + 1; end - index) + 1;
wdata = cifs_writedata_alloc((unsigned int)tofind); wdata = cifs_writedata_alloc((unsigned int)tofind,
cifs_writev_complete);
if (!wdata) { if (!wdata) {
rc = -ENOMEM; rc = -ENOMEM;
break; break;
...@@ -1791,6 +1818,7 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1791,6 +1818,7 @@ static int cifs_writepages(struct address_space *mapping,
wdata->sync_mode = wbc->sync_mode; wdata->sync_mode = wbc->sync_mode;
wdata->nr_pages = nr_pages; wdata->nr_pages = nr_pages;
wdata->offset = page_offset(wdata->pages[0]); wdata->offset = page_offset(wdata->pages[0]);
wdata->marshal_iov = cifs_writepages_marshal_iov;
do { do {
if (wdata->cfile != NULL) if (wdata->cfile != NULL)
...@@ -1802,6 +1830,7 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1802,6 +1830,7 @@ static int cifs_writepages(struct address_space *mapping,
rc = -EBADF; rc = -EBADF;
break; break;
} }
wdata->pid = wdata->cfile->pid;
rc = cifs_async_writev(wdata); rc = cifs_async_writev(wdata);
} while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN); } while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);
...@@ -2043,7 +2072,7 @@ cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) ...@@ -2043,7 +2072,7 @@ cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
unsigned long i; unsigned long i;
for (i = 0; i < num_pages; i++) { for (i = 0; i < num_pages; i++) {
pages[i] = alloc_page(__GFP_HIGHMEM); pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
if (!pages[i]) { if (!pages[i]) {
/* /*
* save number of pages we have already allocated and * save number of pages we have already allocated and
...@@ -2051,15 +2080,14 @@ cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) ...@@ -2051,15 +2080,14 @@ cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
*/ */
num_pages = i; num_pages = i;
rc = -ENOMEM; rc = -ENOMEM;
goto error; break;
} }
} }
return rc; if (rc) {
error:
for (i = 0; i < num_pages; i++) for (i = 0; i < num_pages; i++)
put_page(pages[i]); put_page(pages[i]);
}
return rc; return rc;
} }
...@@ -2070,9 +2098,7 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) ...@@ -2070,9 +2098,7 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
size_t clen; size_t clen;
clen = min_t(const size_t, len, wsize); clen = min_t(const size_t, len, wsize);
num_pages = clen / PAGE_CACHE_SIZE; num_pages = DIV_ROUND_UP(clen, PAGE_SIZE);
if (clen % PAGE_CACHE_SIZE)
num_pages++;
if (cur_len) if (cur_len)
*cur_len = clen; *cur_len = clen;
...@@ -2080,24 +2106,79 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) ...@@ -2080,24 +2106,79 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
return num_pages; return num_pages;
} }
static void
cifs_uncached_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata)
{
int i;
size_t bytes = wdata->bytes;
/* marshal up the pages into iov array */
for (i = 0; i < wdata->nr_pages; i++) {
iov[i + 1].iov_len = min_t(size_t, bytes, PAGE_SIZE);
iov[i + 1].iov_base = kmap(wdata->pages[i]);
bytes -= iov[i + 1].iov_len;
}
}
static void
cifs_uncached_writev_complete(struct work_struct *work)
{
int i;
struct cifs_writedata *wdata = container_of(work,
struct cifs_writedata, work);
struct inode *inode = wdata->cfile->dentry->d_inode;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
spin_lock(&inode->i_lock);
cifs_update_eof(cifsi, wdata->offset, wdata->bytes);
if (cifsi->server_eof > inode->i_size)
i_size_write(inode, cifsi->server_eof);
spin_unlock(&inode->i_lock);
complete(&wdata->done);
if (wdata->result != -EAGAIN) {
for (i = 0; i < wdata->nr_pages; i++)
put_page(wdata->pages[i]);
}
kref_put(&wdata->refcount, cifs_writedata_release);
}
/* attempt to send write to server, retry on any -EAGAIN errors */
static int
cifs_uncached_retry_writev(struct cifs_writedata *wdata)
{
int rc;
do {
if (wdata->cfile->invalidHandle) {
rc = cifs_reopen_file(wdata->cfile, false);
if (rc != 0)
continue;
}
rc = cifs_async_writev(wdata);
} while (rc == -EAGAIN);
return rc;
}
static ssize_t static ssize_t
cifs_iovec_write(struct file *file, const struct iovec *iov, cifs_iovec_write(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset) unsigned long nr_segs, loff_t *poffset)
{ {
unsigned int written; unsigned long nr_pages, i;
unsigned long num_pages, npages, i;
size_t copied, len, cur_len; size_t copied, len, cur_len;
ssize_t total_written = 0; ssize_t total_written = 0;
struct kvec *to_send; loff_t offset = *poffset;
struct page **pages;
struct iov_iter it; struct iov_iter it;
struct inode *inode;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifs_tcon *pTcon; struct cifs_tcon *tcon;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifs_io_parms io_parms; struct cifs_writedata *wdata, *tmp;
int xid, rc; struct list_head wdata_list;
__u32 pid; int rc;
pid_t pid;
len = iov_length(iov, nr_segs); len = iov_length(iov, nr_segs);
if (!len) if (!len)
...@@ -2107,103 +2188,103 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, ...@@ -2107,103 +2188,103 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
if (rc) if (rc)
return rc; return rc;
INIT_LIST_HEAD(&wdata_list);
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
num_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL);
if (!pages)
return -ENOMEM;
to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL);
if (!to_send) {
kfree(pages);
return -ENOMEM;
}
rc = cifs_write_allocate_pages(pages, num_pages);
if (rc) {
kfree(pages);
kfree(to_send);
return rc;
}
xid = GetXid();
open_file = file->private_data; open_file = file->private_data;
tcon = tlink_tcon(open_file->tlink);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
pid = open_file->pid; pid = open_file->pid;
else else
pid = current->tgid; pid = current->tgid;
pTcon = tlink_tcon(open_file->tlink);
inode = file->f_path.dentry->d_inode;
iov_iter_init(&it, iov, nr_segs, len, 0); iov_iter_init(&it, iov, nr_segs, len, 0);
npages = num_pages;
do { do {
size_t save_len = cur_len; size_t save_len;
for (i = 0; i < npages; i++) {
copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
copied = iov_iter_copy_from_user(pages[i], &it, 0,
copied);
cur_len -= copied;
iov_iter_advance(&it, copied);
to_send[i+1].iov_base = kmap(pages[i]);
to_send[i+1].iov_len = copied;
}
cur_len = save_len - cur_len; nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
wdata = cifs_writedata_alloc(nr_pages,
cifs_uncached_writev_complete);
if (!wdata) {
rc = -ENOMEM;
break;
}
do { rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
if (open_file->invalidHandle) { if (rc) {
rc = cifs_reopen_file(open_file, false); kfree(wdata);
if (rc != 0)
break; break;
} }
io_parms.netfid = open_file->netfid;
io_parms.pid = pid;
io_parms.tcon = pTcon;
io_parms.offset = *poffset;
io_parms.length = cur_len;
rc = CIFSSMBWrite2(xid, &io_parms, &written, to_send,
npages, 0);
} while (rc == -EAGAIN);
for (i = 0; i < npages; i++) save_len = cur_len;
kunmap(pages[i]); for (i = 0; i < nr_pages; i++) {
copied = min_t(const size_t, cur_len, PAGE_SIZE);
if (written) { copied = iov_iter_copy_from_user(wdata->pages[i], &it,
len -= written; 0, copied);
total_written += written; cur_len -= copied;
cifs_update_eof(CIFS_I(inode), *poffset, written); iov_iter_advance(&it, copied);
*poffset += written; }
} else if (rc < 0) { cur_len = save_len - cur_len;
if (!total_written)
total_written = rc; wdata->sync_mode = WB_SYNC_ALL;
wdata->nr_pages = nr_pages;
wdata->offset = (__u64)offset;
wdata->cfile = cifsFileInfo_get(open_file);
wdata->pid = pid;
wdata->bytes = cur_len;
wdata->marshal_iov = cifs_uncached_marshal_iov;
rc = cifs_uncached_retry_writev(wdata);
if (rc) {
kref_put(&wdata->refcount, cifs_writedata_release);
break; break;
} }
/* get length and number of kvecs of the next write */ list_add_tail(&wdata->list, &wdata_list);
npages = get_numpages(cifs_sb->wsize, len, &cur_len); offset += cur_len;
len -= cur_len;
} while (len > 0); } while (len > 0);
if (total_written > 0) { /*
spin_lock(&inode->i_lock); * If at least one write was successfully sent, then discard any rc
if (*poffset > inode->i_size) * value from the later writes. If the other write succeeds, then
i_size_write(inode, *poffset); * we'll end up returning whatever was written. If it fails, then
spin_unlock(&inode->i_lock); * we'll get a new rc value from that.
*/
if (!list_empty(&wdata_list))
rc = 0;
/*
* Wait for and collect replies for any successful sends in order of
* increasing offset. Once an error is hit or we get a fatal signal
* while waiting, then return without waiting for any more replies.
*/
restart_loop:
list_for_each_entry_safe(wdata, tmp, &wdata_list, list) {
if (!rc) {
/* FIXME: freezable too? */
rc = wait_for_completion_killable(&wdata->done);
if (rc)
rc = -EINTR;
else if (wdata->result)
rc = wdata->result;
else
total_written += wdata->bytes;
/* resend call if it's a retryable error */
if (rc == -EAGAIN) {
rc = cifs_uncached_retry_writev(wdata);
goto restart_loop;
}
}
list_del_init(&wdata->list);
kref_put(&wdata->refcount, cifs_writedata_release);
} }
cifs_stats_bytes_written(pTcon, total_written); if (total_written > 0)
mark_inode_dirty_sync(inode); *poffset += total_written;
for (i = 0; i < num_pages; i++) cifs_stats_bytes_written(tcon, total_written);
put_page(pages[i]); return total_written ? total_written : (ssize_t)rc;
kfree(to_send);
kfree(pages);
FreeXid(xid);
return total_written;
} }
ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
......
...@@ -213,55 +213,62 @@ cifs_small_buf_release(void *buf_to_free) ...@@ -213,55 +213,62 @@ cifs_small_buf_release(void *buf_to_free)
} }
/* /*
Find a free multiplex id (SMB mid). Otherwise there could be * Find a free multiplex id (SMB mid). Otherwise there could be
mid collisions which might cause problems, demultiplexing the * mid collisions which might cause problems, demultiplexing the
wrong response to this request. Multiplex ids could collide if * wrong response to this request. Multiplex ids could collide if
one of a series requests takes much longer than the others, or * one of a series requests takes much longer than the others, or
if a very large number of long lived requests (byte range * if a very large number of long lived requests (byte range
locks or FindNotify requests) are pending. No more than * locks or FindNotify requests) are pending. No more than
64K-1 requests can be outstanding at one time. If no * 64K-1 requests can be outstanding at one time. If no
mids are available, return zero. A future optimization * mids are available, return zero. A future optimization
could make the combination of mids and uid the key we use * could make the combination of mids and uid the key we use
to demultiplex on (rather than mid alone). * to demultiplex on (rather than mid alone).
In addition to the above check, the cifs demultiplex * In addition to the above check, the cifs demultiplex
code already used the command code as a secondary * code already used the command code as a secondary
check of the frame and if signing is negotiated the * check of the frame and if signing is negotiated the
response would be discarded if the mid were the same * response would be discarded if the mid were the same
but the signature was wrong. Since the mid is not put in the * but the signature was wrong. Since the mid is not put in the
pending queue until later (when it is about to be dispatched) * pending queue until later (when it is about to be dispatched)
we do have to limit the number of outstanding requests * we do have to limit the number of outstanding requests
to somewhat less than 64K-1 although it is hard to imagine * to somewhat less than 64K-1 although it is hard to imagine
so many threads being in the vfs at one time. * so many threads being in the vfs at one time.
*/ */
__u16 GetNextMid(struct TCP_Server_Info *server) __u64 GetNextMid(struct TCP_Server_Info *server)
{ {
__u16 mid = 0; __u64 mid = 0;
__u16 last_mid; __u16 last_mid, cur_mid;
bool collision; bool collision;
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
last_mid = server->CurrentMid; /* we do not want to loop forever */
server->CurrentMid++; /* mid is 16 bit only for CIFS/SMB */
/* This nested loop looks more expensive than it is. cur_mid = (__u16)((server->CurrentMid) & 0xffff);
In practice the list of pending requests is short, /* we do not want to loop forever */
fewer than 50, and the mids are likely to be unique last_mid = cur_mid;
on the first pass through the loop unless some request cur_mid++;
takes longer than the 64 thousand requests before it
(and it would also have to have been a request that /*
did not time out) */ * This nested loop looks more expensive than it is.
while (server->CurrentMid != last_mid) { * In practice the list of pending requests is short,
* fewer than 50, and the mids are likely to be unique
* on the first pass through the loop unless some request
* takes longer than the 64 thousand requests before it
* (and it would also have to have been a request that
* did not time out).
*/
while (cur_mid != last_mid) {
struct mid_q_entry *mid_entry; struct mid_q_entry *mid_entry;
unsigned int num_mids; unsigned int num_mids;
collision = false; collision = false;
if (server->CurrentMid == 0) if (cur_mid == 0)
server->CurrentMid++; cur_mid++;
num_mids = 0; num_mids = 0;
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
++num_mids; ++num_mids;
if (mid_entry->mid == server->CurrentMid && if (mid_entry->mid == cur_mid &&
mid_entry->midState == MID_REQUEST_SUBMITTED) { mid_entry->mid_state == MID_REQUEST_SUBMITTED) {
/* This mid is in use, try a different one */ /* This mid is in use, try a different one */
collision = true; collision = true;
break; break;
...@@ -282,10 +289,11 @@ __u16 GetNextMid(struct TCP_Server_Info *server) ...@@ -282,10 +289,11 @@ __u16 GetNextMid(struct TCP_Server_Info *server)
server->tcpStatus = CifsNeedReconnect; server->tcpStatus = CifsNeedReconnect;
if (!collision) { if (!collision) {
mid = server->CurrentMid; mid = (__u64)cur_mid;
server->CurrentMid = mid;
break; break;
} }
server->CurrentMid++; cur_mid++;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
return mid; return mid;
...@@ -420,8 +428,10 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid) ...@@ -420,8 +428,10 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)
} }
int int
checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read) checkSMB(char *buf, unsigned int total_read)
{ {
struct smb_hdr *smb = (struct smb_hdr *)buf;
__u16 mid = smb->Mid;
__u32 rfclen = be32_to_cpu(smb->smb_buf_length); __u32 rfclen = be32_to_cpu(smb->smb_buf_length);
__u32 clc_len; /* calculated length */ __u32 clc_len; /* calculated length */
cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x",
...@@ -502,8 +512,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read) ...@@ -502,8 +512,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read)
} }
bool bool
is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
{ {
struct smb_hdr *buf = (struct smb_hdr *)buffer;
struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
struct list_head *tmp, *tmp1, *tmp2; struct list_head *tmp, *tmp1, *tmp2;
struct cifs_ses *ses; struct cifs_ses *ses;
...@@ -584,7 +595,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -584,7 +595,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
cifs_set_oplock_level(pCifsInode, cifs_set_oplock_level(pCifsInode,
pSMB->OplockLevel ? OPLOCK_READ : 0); pSMB->OplockLevel ? OPLOCK_READ : 0);
queue_work(system_nrt_wq, queue_work(cifsiod_wq,
&netfile->oplock_break); &netfile->oplock_break);
netfile->oplock_break_cancelled = false; netfile->oplock_break_cancelled = false;
...@@ -604,16 +615,15 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -604,16 +615,15 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
} }
void void
dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) dump_smb(void *buf, int smb_buf_length)
{ {
int i, j; int i, j;
char debug_line[17]; char debug_line[17];
unsigned char *buffer; unsigned char *buffer = buf;
if (traceSMB == 0) if (traceSMB == 0)
return; return;
buffer = (unsigned char *) smb_buf;
for (i = 0, j = 0; i < smb_buf_length; i++, j++) { for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
if (i % 8 == 0) { if (i % 8 == 0) {
/* have reached the beginning of line */ /* have reached the beginning of line */
......
...@@ -836,8 +836,9 @@ ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) ...@@ -836,8 +836,9 @@ ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode)
} }
int int
map_smb_to_linux_error(struct smb_hdr *smb, bool logErr) map_smb_to_linux_error(char *buf, bool logErr)
{ {
struct smb_hdr *smb = (struct smb_hdr *)buf;
unsigned int i; unsigned int i;
int rc = -EIO; /* if transport error smb error may not be set */ int rc = -EIO; /* if transport error smb error may not be set */
__u8 smberrclass; __u8 smberrclass;
......
...@@ -60,8 +60,8 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) ...@@ -60,8 +60,8 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
memset(temp, 0, sizeof(struct mid_q_entry)); memset(temp, 0, sizeof(struct mid_q_entry));
temp->mid = smb_buffer->Mid; /* always LE */ temp->mid = smb_buffer->Mid; /* always LE */
temp->pid = current->pid; temp->pid = current->pid;
temp->command = smb_buffer->Command; temp->command = cpu_to_le16(smb_buffer->Command);
cFYI(1, "For smb_command %d", temp->command); cFYI(1, "For smb_command %d", smb_buffer->Command);
/* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
/* when mid allocated can be before when sent */ /* when mid allocated can be before when sent */
temp->when_alloc = jiffies; temp->when_alloc = jiffies;
...@@ -75,7 +75,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) ...@@ -75,7 +75,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
} }
atomic_inc(&midCount); atomic_inc(&midCount);
temp->midState = MID_REQUEST_ALLOCATED; temp->mid_state = MID_REQUEST_ALLOCATED;
return temp; return temp;
} }
...@@ -85,9 +85,9 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) ...@@ -85,9 +85,9 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
unsigned long now; unsigned long now;
#endif #endif
midEntry->midState = MID_FREE; midEntry->mid_state = MID_FREE;
atomic_dec(&midCount); atomic_dec(&midCount);
if (midEntry->largeBuf) if (midEntry->large_buf)
cifs_buf_release(midEntry->resp_buf); cifs_buf_release(midEntry->resp_buf);
else else
cifs_small_buf_release(midEntry->resp_buf); cifs_small_buf_release(midEntry->resp_buf);
...@@ -97,8 +97,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) ...@@ -97,8 +97,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
something is wrong, unless it is quite a slow link or server */ something is wrong, unless it is quite a slow link or server */
if ((now - midEntry->when_alloc) > HZ) { if ((now - midEntry->when_alloc) > HZ) {
if ((cifsFYI & CIFS_TIMER) && if ((cifsFYI & CIFS_TIMER) &&
(midEntry->command != SMB_COM_LOCKING_ANDX)) { (midEntry->command != cpu_to_le16(SMB_COM_LOCKING_ANDX))) {
printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d", printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %llu",
midEntry->command, midEntry->mid); midEntry->command, midEntry->mid);
printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n", printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
now - midEntry->when_alloc, now - midEntry->when_alloc,
...@@ -126,11 +126,11 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) ...@@ -126,11 +126,11 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
int rc = 0; int rc = 0;
int i = 0; int i = 0;
struct msghdr smb_msg; struct msghdr smb_msg;
struct smb_hdr *smb_buffer = iov[0].iov_base; __be32 *buf_len = (__be32 *)(iov[0].iov_base);
unsigned int len = iov[0].iov_len; unsigned int len = iov[0].iov_len;
unsigned int total_len; unsigned int total_len;
int first_vec = 0; int first_vec = 0;
unsigned int smb_buf_length = be32_to_cpu(smb_buffer->smb_buf_length); unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
struct socket *ssocket = server->ssocket; struct socket *ssocket = server->ssocket;
if (ssocket == NULL) if (ssocket == NULL)
...@@ -150,7 +150,7 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) ...@@ -150,7 +150,7 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
total_len += iov[i].iov_len; total_len += iov[i].iov_len;
cFYI(1, "Sending smb: total_len %d", total_len); cFYI(1, "Sending smb: total_len %d", total_len);
dump_smb(smb_buffer, len); dump_smb(iov[0].iov_base, len);
i = 0; i = 0;
while (total_len) { while (total_len) {
...@@ -158,23 +158,23 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) ...@@ -158,23 +158,23 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
n_vec - first_vec, total_len); n_vec - first_vec, total_len);
if ((rc == -ENOSPC) || (rc == -EAGAIN)) { if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
i++; i++;
/* if blocking send we try 3 times, since each can block /*
for 5 seconds. For nonblocking we have to try more * If blocking send we try 3 times, since each can block
but wait increasing amounts of time allowing time for * for 5 seconds. For nonblocking we have to try more
socket to clear. The overall time we wait in either * but wait increasing amounts of time allowing time for
case to send on the socket is about 15 seconds. * socket to clear. The overall time we wait in either
Similarly we wait for 15 seconds for * case to send on the socket is about 15 seconds.
a response from the server in SendReceive[2] * Similarly we wait for 15 seconds for a response from
for the server to send a response back for * the server in SendReceive[2] for the server to send
most types of requests (except SMB Write * a response back for most types of requests (except
past end of file which can be slow, and * SMB Write past end of file which can be slow, and
blocking lock operations). NFS waits slightly longer * blocking lock operations). NFS waits slightly longer
than CIFS, but this can make it take longer for * than CIFS, but this can make it take longer for
nonresponsive servers to be detected and 15 seconds * nonresponsive servers to be detected and 15 seconds
is more than enough time for modern networks to * is more than enough time for modern networks to
send a packet. In most cases if we fail to send * send a packet. In most cases if we fail to send
after the retries we will kill the socket and * after the retries we will kill the socket and
reconnect which may clear the network problem. * reconnect which may clear the network problem.
*/ */
if ((i >= 14) || (!server->noblocksnd && (i > 2))) { if ((i >= 14) || (!server->noblocksnd && (i > 2))) {
cERROR(1, "sends on sock %p stuck for 15 seconds", cERROR(1, "sends on sock %p stuck for 15 seconds",
...@@ -235,9 +235,8 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) ...@@ -235,9 +235,8 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
else else
rc = 0; rc = 0;
/* Don't want to modify the buffer as a /* Don't want to modify the buffer as a side effect of this call. */
side effect of this call. */ *buf_len = cpu_to_be32(smb_buf_length);
smb_buffer->smb_buf_length = cpu_to_be32(smb_buf_length);
return rc; return rc;
} }
...@@ -342,13 +341,40 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) ...@@ -342,13 +341,40 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
int error; int error;
error = wait_event_freezekillable(server->response_q, error = wait_event_freezekillable(server->response_q,
midQ->midState != MID_REQUEST_SUBMITTED); midQ->mid_state != MID_REQUEST_SUBMITTED);
if (error < 0) if (error < 0)
return -ERESTARTSYS; return -ERESTARTSYS;
return 0; return 0;
} }
static int
cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
unsigned int nvec, struct mid_q_entry **ret_mid)
{
int rc;
struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
struct mid_q_entry *mid;
/* enable signing if server requires it */
if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
mid = AllocMidQEntry(hdr, server);
if (mid == NULL)
return -ENOMEM;
/* put it on the pending_mid_q */
spin_lock(&GlobalMid_Lock);
list_add_tail(&mid->qhead, &server->pending_mid_q);
spin_unlock(&GlobalMid_Lock);
rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number);
if (rc)
delete_mid(mid);
*ret_mid = mid;
return rc;
}
/* /*
* Send a SMB request and set the callback function in the mid to handle * Send a SMB request and set the callback function in the mid to handle
...@@ -361,40 +387,24 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, ...@@ -361,40 +387,24 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
{ {
int rc; int rc;
struct mid_q_entry *mid; struct mid_q_entry *mid;
struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
rc = wait_for_free_request(server, ignore_pend ? CIFS_ASYNC_OP : 0); rc = wait_for_free_request(server, ignore_pend ? CIFS_ASYNC_OP : 0);
if (rc) if (rc)
return rc; return rc;
/* enable signing if server requires it */
if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
mutex_lock(&server->srv_mutex); mutex_lock(&server->srv_mutex);
mid = AllocMidQEntry(hdr, server); rc = cifs_setup_async_request(server, iov, nvec, &mid);
if (mid == NULL) { if (rc) {
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
cifs_add_credits(server, 1); cifs_add_credits(server, 1);
wake_up(&server->request_q); wake_up(&server->request_q);
return -ENOMEM; return rc;
}
/* put it on the pending_mid_q */
spin_lock(&GlobalMid_Lock);
list_add_tail(&mid->qhead, &server->pending_mid_q);
spin_unlock(&GlobalMid_Lock);
rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number);
if (rc) {
mutex_unlock(&server->srv_mutex);
goto out_err;
} }
mid->receive = receive; mid->receive = receive;
mid->callback = callback; mid->callback = callback;
mid->callback_data = cbdata; mid->callback_data = cbdata;
mid->midState = MID_REQUEST_SUBMITTED; mid->mid_state = MID_REQUEST_SUBMITTED;
cifs_in_send_inc(server); cifs_in_send_inc(server);
rc = smb_sendv(server, iov, nvec); rc = smb_sendv(server, iov, nvec);
...@@ -424,14 +434,14 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, ...@@ -424,14 +434,14 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
*/ */
int int
SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
struct smb_hdr *in_buf, int flags) char *in_buf, int flags)
{ {
int rc; int rc;
struct kvec iov[1]; struct kvec iov[1];
int resp_buf_type; int resp_buf_type;
iov[0].iov_base = (char *)in_buf; iov[0].iov_base = in_buf;
iov[0].iov_len = be32_to_cpu(in_buf->smb_buf_length) + 4; iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
flags |= CIFS_NO_RESP; flags |= CIFS_NO_RESP;
rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags); rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags);
cFYI(DBG2, "SendRcvNoRsp flags %d rc %d", flags, rc); cFYI(DBG2, "SendRcvNoRsp flags %d rc %d", flags, rc);
...@@ -444,11 +454,11 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) ...@@ -444,11 +454,11 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
{ {
int rc = 0; int rc = 0;
cFYI(1, "%s: cmd=%d mid=%d state=%d", __func__, mid->command, cFYI(1, "%s: cmd=%d mid=%llu state=%d", __func__,
mid->mid, mid->midState); le16_to_cpu(mid->command), mid->mid, mid->mid_state);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
switch (mid->midState) { switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED: case MID_RESPONSE_RECEIVED:
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
return rc; return rc;
...@@ -463,8 +473,8 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) ...@@ -463,8 +473,8 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
break; break;
default: default:
list_del_init(&mid->qhead); list_del_init(&mid->qhead);
cERROR(1, "%s: invalid mid state mid=%d state=%d", __func__, cERROR(1, "%s: invalid mid state mid=%llu state=%d", __func__,
mid->mid, mid->midState); mid->mid, mid->mid_state);
rc = -EIO; rc = -EIO;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -514,7 +524,7 @@ int ...@@ -514,7 +524,7 @@ int
cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
bool log_error) bool log_error)
{ {
unsigned int len = be32_to_cpu(mid->resp_buf->smb_buf_length) + 4; unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
dump_smb(mid->resp_buf, min_t(u32, 92, len)); dump_smb(mid->resp_buf, min_t(u32, 92, len));
...@@ -534,6 +544,24 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, ...@@ -534,6 +544,24 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
return map_smb_to_linux_error(mid->resp_buf, log_error); return map_smb_to_linux_error(mid->resp_buf, log_error);
} }
static int
cifs_setup_request(struct cifs_ses *ses, struct kvec *iov,
unsigned int nvec, struct mid_q_entry **ret_mid)
{
int rc;
struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
struct mid_q_entry *mid;
rc = allocate_mid(ses, hdr, &mid);
if (rc)
return rc;
rc = cifs_sign_smb2(iov, nvec, ses->server, &mid->sequence_number);
if (rc)
delete_mid(mid);
*ret_mid = mid;
return rc;
}
int int
SendReceive2(const unsigned int xid, struct cifs_ses *ses, SendReceive2(const unsigned int xid, struct cifs_ses *ses,
struct kvec *iov, int n_vec, int *pRespBufType /* ret */, struct kvec *iov, int n_vec, int *pRespBufType /* ret */,
...@@ -542,55 +570,53 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, ...@@ -542,55 +570,53 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
int rc = 0; int rc = 0;
int long_op; int long_op;
struct mid_q_entry *midQ; struct mid_q_entry *midQ;
struct smb_hdr *in_buf = iov[0].iov_base; char *buf = iov[0].iov_base;
long_op = flags & CIFS_TIMEOUT_MASK; long_op = flags & CIFS_TIMEOUT_MASK;
*pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
if ((ses == NULL) || (ses->server == NULL)) { if ((ses == NULL) || (ses->server == NULL)) {
cifs_small_buf_release(in_buf); cifs_small_buf_release(buf);
cERROR(1, "Null session"); cERROR(1, "Null session");
return -EIO; return -EIO;
} }
if (ses->server->tcpStatus == CifsExiting) { if (ses->server->tcpStatus == CifsExiting) {
cifs_small_buf_release(in_buf); cifs_small_buf_release(buf);
return -ENOENT; return -ENOENT;
} }
/* Ensure that we do not send more than 50 overlapping requests /*
to the same server. We may make this configurable later or * Ensure that we do not send more than 50 overlapping requests
use ses->maxReq */ * to the same server. We may make this configurable later or
* use ses->maxReq.
*/
rc = wait_for_free_request(ses->server, long_op); rc = wait_for_free_request(ses->server, long_op);
if (rc) { if (rc) {
cifs_small_buf_release(in_buf); cifs_small_buf_release(buf);
return rc; return rc;
} }
/* make sure that we sign in the same order that we send on this socket /*
and avoid races inside tcp sendmsg code that could cause corruption * Make sure that we sign in the same order that we send on this socket
of smb data */ * and avoid races inside tcp sendmsg code that could cause corruption
* of smb data.
*/
mutex_lock(&ses->server->srv_mutex); mutex_lock(&ses->server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ); rc = cifs_setup_request(ses, iov, n_vec, &midQ);
if (rc) { if (rc) {
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&ses->server->srv_mutex);
cifs_small_buf_release(in_buf); cifs_small_buf_release(buf);
/* Update # of requests on wire to server */ /* Update # of requests on wire to server */
cifs_add_credits(ses->server, 1); cifs_add_credits(ses->server, 1);
return rc; return rc;
} }
rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
if (rc) {
mutex_unlock(&ses->server->srv_mutex);
cifs_small_buf_release(in_buf);
goto out;
}
midQ->midState = MID_REQUEST_SUBMITTED; midQ->mid_state = MID_REQUEST_SUBMITTED;
cifs_in_send_inc(ses->server); cifs_in_send_inc(ses->server);
rc = smb_sendv(ses->server, iov, n_vec); rc = smb_sendv(ses->server, iov, n_vec);
cifs_in_send_dec(ses->server); cifs_in_send_dec(ses->server);
...@@ -599,30 +625,30 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, ...@@ -599,30 +625,30 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&ses->server->srv_mutex);
if (rc < 0) { if (rc < 0) {
cifs_small_buf_release(in_buf); cifs_small_buf_release(buf);
goto out; goto out;
} }
if (long_op == CIFS_ASYNC_OP) { if (long_op == CIFS_ASYNC_OP) {
cifs_small_buf_release(in_buf); cifs_small_buf_release(buf);
goto out; goto out;
} }
rc = wait_for_response(ses->server, midQ); rc = wait_for_response(ses->server, midQ);
if (rc != 0) { if (rc != 0) {
send_nt_cancel(ses->server, in_buf, midQ); send_nt_cancel(ses->server, (struct smb_hdr *)buf, midQ);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (midQ->midState == MID_REQUEST_SUBMITTED) { if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
midQ->callback = DeleteMidQEntry; midQ->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
cifs_small_buf_release(in_buf); cifs_small_buf_release(buf);
cifs_add_credits(ses->server, 1); cifs_add_credits(ses->server, 1);
return rc; return rc;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
} }
cifs_small_buf_release(in_buf); cifs_small_buf_release(buf);
rc = cifs_sync_mid_result(midQ, ses->server); rc = cifs_sync_mid_result(midQ, ses->server);
if (rc != 0) { if (rc != 0) {
...@@ -630,15 +656,16 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, ...@@ -630,15 +656,16 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
return rc; return rc;
} }
if (!midQ->resp_buf || midQ->midState != MID_RESPONSE_RECEIVED) { if (!midQ->resp_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) {
rc = -EIO; rc = -EIO;
cFYI(1, "Bad MID state?"); cFYI(1, "Bad MID state?");
goto out; goto out;
} }
iov[0].iov_base = (char *)midQ->resp_buf; buf = (char *)midQ->resp_buf;
iov[0].iov_len = be32_to_cpu(midQ->resp_buf->smb_buf_length) + 4; iov[0].iov_base = buf;
if (midQ->largeBuf) iov[0].iov_len = get_rfc1002_length(buf) + 4;
if (midQ->large_buf)
*pRespBufType = CIFS_LARGE_BUFFER; *pRespBufType = CIFS_LARGE_BUFFER;
else else
*pRespBufType = CIFS_SMALL_BUFFER; *pRespBufType = CIFS_SMALL_BUFFER;
...@@ -710,7 +737,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, ...@@ -710,7 +737,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
goto out; goto out;
} }
midQ->midState = MID_REQUEST_SUBMITTED; midQ->mid_state = MID_REQUEST_SUBMITTED;
cifs_in_send_inc(ses->server); cifs_in_send_inc(ses->server);
rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
...@@ -728,7 +755,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, ...@@ -728,7 +755,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
if (rc != 0) { if (rc != 0) {
send_nt_cancel(ses->server, in_buf, midQ); send_nt_cancel(ses->server, in_buf, midQ);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (midQ->midState == MID_REQUEST_SUBMITTED) { if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
/* no longer considered to be "in-flight" */ /* no longer considered to be "in-flight" */
midQ->callback = DeleteMidQEntry; midQ->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -745,13 +772,13 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, ...@@ -745,13 +772,13 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
} }
if (!midQ->resp_buf || !out_buf || if (!midQ->resp_buf || !out_buf ||
midQ->midState != MID_RESPONSE_RECEIVED) { midQ->mid_state != MID_RESPONSE_RECEIVED) {
rc = -EIO; rc = -EIO;
cERROR(1, "Bad MID state?"); cERROR(1, "Bad MID state?");
goto out; goto out;
} }
*pbytes_returned = be32_to_cpu(midQ->resp_buf->smb_buf_length); *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, ses->server, 0); rc = cifs_check_receive(midQ, ses->server, 0);
out: out:
...@@ -844,7 +871,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -844,7 +871,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
midQ->midState = MID_REQUEST_SUBMITTED; midQ->mid_state = MID_REQUEST_SUBMITTED;
cifs_in_send_inc(ses->server); cifs_in_send_inc(ses->server);
rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
cifs_in_send_dec(ses->server); cifs_in_send_dec(ses->server);
...@@ -858,13 +885,13 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -858,13 +885,13 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
/* Wait for a reply - allow signals to interrupt. */ /* Wait for a reply - allow signals to interrupt. */
rc = wait_event_interruptible(ses->server->response_q, rc = wait_event_interruptible(ses->server->response_q,
(!(midQ->midState == MID_REQUEST_SUBMITTED)) || (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) ||
((ses->server->tcpStatus != CifsGood) && ((ses->server->tcpStatus != CifsGood) &&
(ses->server->tcpStatus != CifsNew))); (ses->server->tcpStatus != CifsNew)));
/* Were we interrupted by a signal ? */ /* Were we interrupted by a signal ? */
if ((rc == -ERESTARTSYS) && if ((rc == -ERESTARTSYS) &&
(midQ->midState == MID_REQUEST_SUBMITTED) && (midQ->mid_state == MID_REQUEST_SUBMITTED) &&
((ses->server->tcpStatus == CifsGood) || ((ses->server->tcpStatus == CifsGood) ||
(ses->server->tcpStatus == CifsNew))) { (ses->server->tcpStatus == CifsNew))) {
...@@ -894,7 +921,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -894,7 +921,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
if (rc) { if (rc) {
send_nt_cancel(ses->server, in_buf, midQ); send_nt_cancel(ses->server, in_buf, midQ);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (midQ->midState == MID_REQUEST_SUBMITTED) { if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
/* no longer considered to be "in-flight" */ /* no longer considered to be "in-flight" */
midQ->callback = DeleteMidQEntry; midQ->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -912,13 +939,13 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -912,13 +939,13 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
/* rcvd frame is ok */ /* rcvd frame is ok */
if (out_buf == NULL || midQ->midState != MID_RESPONSE_RECEIVED) { if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) {
rc = -EIO; rc = -EIO;
cERROR(1, "Bad MID state?"); cERROR(1, "Bad MID state?");
goto out; goto out;
} }
*pbytes_returned = be32_to_cpu(midQ->resp_buf->smb_buf_length); *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, ses->server, 0); rc = cifs_check_receive(midQ, ses->server, 0);
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment