forked from Minki/linux
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS updates from Steve French. * 'for-next' of git://git.samba.org/sfrench/cifs-2.6: (29 commits) cifs: fix oops while traversing open file list (try #4) cifs: Fix comment as d_alloc_root() is replaced by d_make_root() CIFS: Introduce SMB2 mounts as vers=2.1 CIFS: Introduce SMB2 Kconfig option CIFS: Move add/set_credits and get_credits_field to ops structure CIFS: Move protocol specific demultiplex thread calls to ops struct CIFS: Move protocol specific part from cifs_readv_receive to ops struct CIFS: Move header_size/max_header_size to ops structure CIFS: Move protocol specific part from SendReceive2 to ops struct cifs: Include backup intent search flags during searches {try #2) CIFS: Separate protocol specific part from setlk CIFS: Separate protocol specific part from getlk CIFS: Separate protocol specific lock type handling CIFS: Convert lock type to 32 bit variable CIFS: Move locks to cifsFileInfo structure cifs: convert send_nt_cancel into a version specific op cifs: add a smb_version_operations/values structures and a smb_version enum cifs: remove the vers= and version= synonyms for ver= cifs: add warning about change in default cache semantics in 3.7 cifs: display cache= option in /proc/mounts ...
This commit is contained in:
commit
442a9ffabb
@ -158,3 +158,23 @@ config CIFS_NFSD_EXPORT
|
||||
depends on CIFS && EXPERIMENTAL && BROKEN
|
||||
help
|
||||
Allows NFS server to export a CIFS mounted share (nfsd over cifs)
|
||||
|
||||
config CIFS_SMB2
|
||||
bool "SMB2 network file system support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && INET && BROKEN
|
||||
select NLS
|
||||
select KEYS
|
||||
select FSCACHE
|
||||
select DNS_RESOLVER
|
||||
|
||||
help
|
||||
This enables experimental support for the SMB2 (Server Message Block
|
||||
version 2) protocol. The SMB2 protocol is the successor to the
|
||||
popular CIFS and SMB network file sharing protocols. SMB2 is the
|
||||
native file sharing mechanism for recent versions of Windows
|
||||
operating systems (since Vista). SMB2 enablement will eventually
|
||||
allow users better performance, security and features, than would be
|
||||
possible with cifs. Note that smb2 mount options also are simpler
|
||||
(compared to cifs) due to protocol improvements.
|
||||
|
||||
Unless you are a developer or tester, say N.
|
||||
|
@ -6,7 +6,7 @@ obj-$(CONFIG_CIFS) += cifs.o
|
||||
cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
|
||||
link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \
|
||||
cifs_unicode.o nterr.o xattr.o cifsencrypt.o \
|
||||
readdir.o ioctl.o sess.o export.o
|
||||
readdir.o ioctl.o sess.o export.o smb1ops.o
|
||||
|
||||
cifs-$(CONFIG_CIFS_ACL) += cifsacl.o
|
||||
|
||||
@ -15,3 +15,5 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
|
||||
cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
|
||||
|
||||
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
|
||||
|
||||
cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o
|
||||
|
@ -608,11 +608,6 @@ Stats Lists summary resource usage information as well as per
|
||||
in the kernel configuration.
|
||||
|
||||
Configuration pseudo-files:
|
||||
MultiuserMount If set to one, more than one CIFS session to
|
||||
the same server ip address can be established
|
||||
if more than one uid accesses the same mount
|
||||
point and if the uids user/password mapping
|
||||
information is available. (default is 0)
|
||||
PacketSigningEnabled If set to one, cifs packet signing is enabled
|
||||
and will be used if the server requires
|
||||
it. If set to two, cifs packet signing is
|
||||
|
@ -57,19 +57,21 @@ cifs_dump_mem(char *label, void *data, int length)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
void cifs_dump_detail(void *buf)
|
||||
{
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
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",
|
||||
smb->Command, smb->Status.CifsError,
|
||||
smb->Flags, smb->Flags2, smb->Mid, smb->Pid);
|
||||
cERROR(1, "smb buf %p len %d", smb, smbCalcSize(smb));
|
||||
#endif /* CONFIG_CIFS_DEBUG2 */
|
||||
}
|
||||
|
||||
void cifs_dump_mids(struct TCP_Server_Info *server)
|
||||
{
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
struct list_head *tmp;
|
||||
struct mid_q_entry *mid_entry;
|
||||
|
||||
@ -102,8 +104,8 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
|
||||
}
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
#endif /* CONFIG_CIFS_DEBUG2 */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||
@ -420,7 +422,6 @@ static struct proc_dir_entry *proc_fs_cifs;
|
||||
static const struct file_operations cifsFYI_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 cifs_multiuser_mount_proc_fops;
|
||||
static const struct file_operations cifs_security_flags_proc_fops;
|
||||
static const struct file_operations cifs_linux_ext_proc_fops;
|
||||
|
||||
@ -440,8 +441,6 @@ cifs_proc_init(void)
|
||||
proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops);
|
||||
proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs,
|
||||
&cifs_linux_ext_proc_fops);
|
||||
proc_create("MultiuserMount", 0, proc_fs_cifs,
|
||||
&cifs_multiuser_mount_proc_fops);
|
||||
proc_create("SecurityFlags", 0, proc_fs_cifs,
|
||||
&cifs_security_flags_proc_fops);
|
||||
proc_create("LookupCacheEnabled", 0, proc_fs_cifs,
|
||||
@ -460,7 +459,6 @@ cifs_proc_clean(void)
|
||||
#ifdef CONFIG_CIFS_STATS
|
||||
remove_proc_entry("Stats", proc_fs_cifs);
|
||||
#endif
|
||||
remove_proc_entry("MultiuserMount", proc_fs_cifs);
|
||||
remove_proc_entry("SecurityFlags", proc_fs_cifs);
|
||||
remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
|
||||
remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
|
||||
@ -617,52 +615,6 @@ static const struct file_operations traceSMB_proc_fops = {
|
||||
.write = traceSMB_proc_write,
|
||||
};
|
||||
|
||||
static int cifs_multiuser_mount_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_printf(m, "%d\n", multiuser_mount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cifs_multiuser_mount_proc_open(struct inode *inode, struct file *fh)
|
||||
{
|
||||
return single_open(fh, cifs_multiuser_mount_proc_show, NULL);
|
||||
}
|
||||
|
||||
static ssize_t cifs_multiuser_mount_proc_write(struct file *file,
|
||||
const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
char c;
|
||||
int rc;
|
||||
static bool warned;
|
||||
|
||||
rc = get_user(c, buffer);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (c == '0' || c == 'n' || c == 'N')
|
||||
multiuser_mount = 0;
|
||||
else if (c == '1' || c == 'y' || c == 'Y') {
|
||||
multiuser_mount = 1;
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
printk(KERN_WARNING "CIFS VFS: The legacy multiuser "
|
||||
"mount code is scheduled to be deprecated in "
|
||||
"3.5. Please switch to using the multiuser "
|
||||
"mount option.");
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations cifs_multiuser_mount_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = cifs_multiuser_mount_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.write = cifs_multiuser_mount_proc_write,
|
||||
};
|
||||
|
||||
static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_printf(m, "0x%x\n", global_secflags);
|
||||
|
@ -24,10 +24,10 @@
|
||||
#define _H_CIFS_DEBUG
|
||||
|
||||
void cifs_dump_mem(char *label, void *data, int length);
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
#define DBG2 2
|
||||
void cifs_dump_detail(void *);
|
||||
void cifs_dump_mids(struct TCP_Server_Info *);
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
#define DBG2 2
|
||||
#else
|
||||
#define DBG2 0
|
||||
#endif
|
||||
|
@ -56,7 +56,6 @@ int traceSMB = 0;
|
||||
bool enable_oplocks = true;
|
||||
unsigned int linuxExtEnabled = 1;
|
||||
unsigned int lookupCacheEnabled = 1;
|
||||
unsigned int multiuser_mount = 0;
|
||||
unsigned int global_secflags = CIFSSEC_DEF;
|
||||
/* unsigned int ntlmv2_support = 0; */
|
||||
unsigned int sign_CIFS_PDUs = 1;
|
||||
@ -125,7 +124,7 @@ cifs_read_super(struct super_block *sb)
|
||||
goto out_no_root;
|
||||
}
|
||||
|
||||
/* do that *after* d_alloc_root() - we want NULL ->d_op for root here */
|
||||
/* do that *after* d_make_root() - we want NULL ->d_op for root here */
|
||||
if (cifs_sb_master_tcon(cifs_sb)->nocase)
|
||||
sb->s_d_op = &cifs_ci_dentry_ops;
|
||||
else
|
||||
@ -329,6 +328,19 @@ cifs_show_security(struct seq_file *s, struct TCP_Server_Info *server)
|
||||
seq_printf(s, "i");
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
seq_printf(s, ",cache=");
|
||||
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
|
||||
seq_printf(s, "strict");
|
||||
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
|
||||
seq_printf(s, "none");
|
||||
else
|
||||
seq_printf(s, "loose");
|
||||
}
|
||||
|
||||
/*
|
||||
* cifs_show_options() is for displaying mount options in /proc/mounts.
|
||||
* Not all settable options are displayed but most of the important
|
||||
@ -342,7 +354,9 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
struct sockaddr *srcaddr;
|
||||
srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr;
|
||||
|
||||
seq_printf(s, ",vers=%s", tcon->ses->server->vals->version_string);
|
||||
cifs_show_security(s, tcon->ses->server);
|
||||
cifs_show_cache_flavor(s, cifs_sb);
|
||||
|
||||
seq_printf(s, ",unc=%s", tcon->treeName);
|
||||
|
||||
@ -408,8 +422,6 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
seq_printf(s, ",rwpidforward");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL)
|
||||
seq_printf(s, ",forcemand");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
|
||||
seq_printf(s, ",directio");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
|
||||
seq_printf(s, ",nouser_xattr");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
|
||||
@ -432,8 +444,6 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
seq_printf(s, ",nostrictsync");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
|
||||
seq_printf(s, ",noperm");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
|
||||
seq_printf(s, ",strictcache");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID)
|
||||
seq_printf(s, ",backupuid=%u", cifs_sb->mnt_backupuid);
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID)
|
||||
@ -945,7 +955,6 @@ cifs_init_once(void *inode)
|
||||
struct cifsInodeInfo *cifsi = inode;
|
||||
|
||||
inode_init_once(&cifsi->vfs_inode);
|
||||
INIT_LIST_HEAD(&cifsi->llist);
|
||||
mutex_init(&cifsi->lock_mutex);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
#define CIFS_MIN_RCV_POOL 4
|
||||
|
||||
#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */
|
||||
/*
|
||||
* default attribute cache timeout (jiffies)
|
||||
*/
|
||||
@ -150,6 +151,57 @@ struct cifs_cred {
|
||||
*****************************************************************
|
||||
*/
|
||||
|
||||
enum smb_version {
|
||||
Smb_1 = 1,
|
||||
Smb_21,
|
||||
};
|
||||
|
||||
struct mid_q_entry;
|
||||
struct TCP_Server_Info;
|
||||
struct cifsFileInfo;
|
||||
struct cifs_ses;
|
||||
|
||||
struct smb_version_operations {
|
||||
int (*send_cancel)(struct TCP_Server_Info *, void *,
|
||||
struct mid_q_entry *);
|
||||
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
|
||||
/* setup request: allocate mid, sign message */
|
||||
int (*setup_request)(struct cifs_ses *, struct kvec *, unsigned int,
|
||||
struct mid_q_entry **);
|
||||
/* check response: verify signature, map error */
|
||||
int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
|
||||
bool);
|
||||
void (*add_credits)(struct TCP_Server_Info *, const unsigned int);
|
||||
void (*set_credits)(struct TCP_Server_Info *, const int);
|
||||
int * (*get_credits_field)(struct TCP_Server_Info *);
|
||||
/* data offset from read response message */
|
||||
unsigned int (*read_data_offset)(char *);
|
||||
/* data length from read response message */
|
||||
unsigned int (*read_data_length)(char *);
|
||||
/* map smb to linux error */
|
||||
int (*map_error)(char *, bool);
|
||||
/* find mid corresponding to the response message */
|
||||
struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *);
|
||||
void (*dump_detail)(void *);
|
||||
/* verify the message */
|
||||
int (*check_message)(char *, unsigned int);
|
||||
bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
|
||||
};
|
||||
|
||||
struct smb_version_values {
|
||||
char *version_string;
|
||||
__u32 large_lock_type;
|
||||
__u32 exclusive_lock_type;
|
||||
__u32 shared_lock_type;
|
||||
__u32 unlock_lock_type;
|
||||
size_t header_size;
|
||||
size_t max_header_size;
|
||||
size_t read_rsp_size;
|
||||
};
|
||||
|
||||
#define HEADER_SIZE(server) (server->vals->header_size)
|
||||
#define MAX_HEADER_SIZE(server) (server->vals->max_header_size)
|
||||
|
||||
struct smb_vol {
|
||||
char *username;
|
||||
char *password;
|
||||
@ -205,6 +257,8 @@ struct smb_vol {
|
||||
bool sockopt_tcp_nodelay:1;
|
||||
unsigned short int port;
|
||||
unsigned long actimeo; /* attribute cache timeout (jiffies) */
|
||||
struct smb_version_operations *ops;
|
||||
struct smb_version_values *vals;
|
||||
char *prepath;
|
||||
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
|
||||
struct nls_table *local_nls;
|
||||
@ -242,6 +296,8 @@ struct TCP_Server_Info {
|
||||
int srv_count; /* reference counter */
|
||||
/* 15 character server name + 0x20 16th byte indicating type = srv */
|
||||
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
|
||||
struct smb_version_operations *ops;
|
||||
struct smb_version_values *vals;
|
||||
enum statusEnum tcpStatus; /* what we think the status is */
|
||||
char *hostname; /* hostname portion of UNC string */
|
||||
struct socket *ssocket;
|
||||
@ -321,16 +377,6 @@ in_flight(struct TCP_Server_Info *server)
|
||||
return num;
|
||||
}
|
||||
|
||||
static inline int*
|
||||
get_credits_field(struct TCP_Server_Info *server)
|
||||
{
|
||||
/*
|
||||
* This will change to switch statement when we reserve slots for echos
|
||||
* and oplock breaks.
|
||||
*/
|
||||
return &server->credits;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
has_credits(struct TCP_Server_Info *server, int *credits)
|
||||
{
|
||||
@ -341,16 +387,16 @@ has_credits(struct TCP_Server_Info *server, int *credits)
|
||||
return num > 0;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
header_size(void)
|
||||
static inline void
|
||||
add_credits(struct TCP_Server_Info *server, const unsigned int add)
|
||||
{
|
||||
return sizeof(struct smb_hdr);
|
||||
server->ops->add_credits(server, add);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
max_header_size(void)
|
||||
static inline void
|
||||
set_credits(struct TCP_Server_Info *server, const int val)
|
||||
{
|
||||
return MAX_CIFS_HDR_SIZE;
|
||||
server->ops->set_credits(server, val);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -547,8 +593,7 @@ struct cifsLockInfo {
|
||||
__u64 offset;
|
||||
__u64 length;
|
||||
__u32 pid;
|
||||
__u8 type;
|
||||
__u16 netfid;
|
||||
__u32 type;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -573,6 +618,10 @@ struct cifs_search_info {
|
||||
struct cifsFileInfo {
|
||||
struct list_head tlist; /* pointer to next fid owned by tcon */
|
||||
struct list_head flist; /* next fid (file instance) for this inode */
|
||||
struct list_head llist; /*
|
||||
* brlocks held by this fid, protected by
|
||||
* lock_mutex from cifsInodeInfo structure
|
||||
*/
|
||||
unsigned int uid; /* allows finding which FileInfo structure */
|
||||
__u32 pid; /* process id who opened file */
|
||||
__u16 netfid; /* file id from remote */
|
||||
@ -615,9 +664,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
|
||||
*/
|
||||
|
||||
struct cifsInodeInfo {
|
||||
struct list_head llist; /* brlocks for this inode */
|
||||
bool can_cache_brlcks;
|
||||
struct mutex lock_mutex; /* protect two fields above */
|
||||
struct mutex lock_mutex; /*
|
||||
* protect the field above and llist
|
||||
* from every cifsFileInfo structure
|
||||
* from openFileList
|
||||
*/
|
||||
/* BB add in lists for dirty pages i.e. write caching info for oplock */
|
||||
struct list_head openFileList;
|
||||
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
|
||||
@ -703,7 +755,6 @@ static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon,
|
||||
|
||||
#endif
|
||||
|
||||
struct mid_q_entry;
|
||||
|
||||
/*
|
||||
* This is the prototype for the mid receive function. This function is for
|
||||
@ -1042,12 +1093,7 @@ GLOBAL_EXTERN atomic_t smBufAllocCount;
|
||||
GLOBAL_EXTERN atomic_t midCount;
|
||||
|
||||
/* Misc globals */
|
||||
GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions
|
||||
to be established on existing mount if we
|
||||
have the uid/password or Kerberos credential
|
||||
or equivalent for current user */
|
||||
/* enable or disable oplocks */
|
||||
GLOBAL_EXTERN bool enable_oplocks;
|
||||
GLOBAL_EXTERN bool enable_oplocks; /* enable or disable oplocks */
|
||||
GLOBAL_EXTERN unsigned int lookupCacheEnabled;
|
||||
GLOBAL_EXTERN unsigned int global_secflags; /* if on, session setup sent
|
||||
with more secure ntlmssp2 challenge/resp */
|
||||
@ -1074,4 +1120,11 @@ void cifs_oplock_break(struct work_struct *work);
|
||||
extern const struct slow_work_ops cifs_oplock_break_ops;
|
||||
extern struct workqueue_struct *cifsiod_wq;
|
||||
|
||||
/* Operations for different SMB versions */
|
||||
#define SMB1_VERSION_STRING "1.0"
|
||||
extern struct smb_version_operations smb1_operations;
|
||||
extern struct smb_version_values smb1_values;
|
||||
#define SMB21_VERSION_STRING "2.1"
|
||||
extern struct smb_version_operations smb21_operations;
|
||||
extern struct smb_version_values smb21_values;
|
||||
#endif /* _CIFS_GLOB_H */
|
||||
|
@ -78,6 +78,8 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
|
||||
int * /* bytes returned */ , const int long_op);
|
||||
extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
|
||||
char *in_buf, int flags);
|
||||
extern int cifs_setup_request(struct cifs_ses *, struct kvec *, unsigned int,
|
||||
struct mid_q_entry **);
|
||||
extern int cifs_check_receive(struct mid_q_entry *mid,
|
||||
struct TCP_Server_Info *server, bool log_error);
|
||||
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
|
||||
@ -88,9 +90,6 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
|
||||
struct smb_hdr *in_buf ,
|
||||
struct smb_hdr *out_buf,
|
||||
int *bytes_returned);
|
||||
extern void cifs_add_credits(struct TCP_Server_Info *server,
|
||||
const unsigned int add);
|
||||
extern void cifs_set_credits(struct TCP_Server_Info *server, const int val);
|
||||
extern int checkSMB(char *buf, unsigned int length);
|
||||
extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
|
||||
extern bool backup_cred(struct cifs_sb_info *);
|
||||
@ -192,11 +191,13 @@ extern int CIFSTCon(unsigned int xid, struct cifs_ses *ses,
|
||||
|
||||
extern int CIFSFindFirst(const int xid, struct cifs_tcon *tcon,
|
||||
const char *searchName, const struct nls_table *nls_codepage,
|
||||
__u16 *searchHandle, struct cifs_search_info *psrch_inf,
|
||||
__u16 *searchHandle, __u16 search_flags,
|
||||
struct cifs_search_info *psrch_inf,
|
||||
int map, const char dirsep);
|
||||
|
||||
extern int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
|
||||
__u16 searchHandle, struct cifs_search_info *psrch_inf);
|
||||
__u16 searchHandle, __u16 search_flags,
|
||||
struct cifs_search_info *psrch_inf);
|
||||
|
||||
extern int CIFSFindClose(const int, struct cifs_tcon *tcon,
|
||||
const __u16 search_handle);
|
||||
@ -464,6 +465,9 @@ extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
|
||||
|
||||
/* asynchronous read support */
|
||||
struct cifs_readdata {
|
||||
struct kref refcount;
|
||||
struct list_head list;
|
||||
struct completion done;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct address_space *mapping;
|
||||
__u64 offset;
|
||||
@ -472,12 +476,13 @@ struct cifs_readdata {
|
||||
int result;
|
||||
struct list_head pages;
|
||||
struct work_struct work;
|
||||
int (*marshal_iov) (struct cifs_readdata *rdata,
|
||||
unsigned int remaining);
|
||||
unsigned int nr_iov;
|
||||
struct kvec iov[1];
|
||||
};
|
||||
|
||||
struct cifs_readdata *cifs_readdata_alloc(unsigned int nr_pages);
|
||||
void cifs_readdata_free(struct cifs_readdata *rdata);
|
||||
void cifs_readdata_release(struct kref *refcount);
|
||||
int cifs_async_readv(struct cifs_readdata *rdata);
|
||||
|
||||
/* asynchronous write support */
|
||||
|
@ -87,7 +87,6 @@ static struct {
|
||||
#endif /* CIFS_POSIX */
|
||||
|
||||
/* Forward declarations */
|
||||
static void cifs_readv_complete(struct work_struct *work);
|
||||
|
||||
/* Mark as invalid, all open files on tree connections since they
|
||||
were closed when session to server was lost */
|
||||
@ -461,7 +460,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses)
|
||||
server->maxReq = min_t(unsigned int,
|
||||
le16_to_cpu(rsp->MaxMpxCount),
|
||||
cifs_max_pending);
|
||||
cifs_set_credits(server, server->maxReq);
|
||||
set_credits(server, server->maxReq);
|
||||
server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
|
||||
server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
|
||||
/* even though we do not use raw we might as well set this
|
||||
@ -569,7 +568,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses)
|
||||
little endian */
|
||||
server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount),
|
||||
cifs_max_pending);
|
||||
cifs_set_credits(server, server->maxReq);
|
||||
set_credits(server, server->maxReq);
|
||||
/* probably no need to store and check maxvcs */
|
||||
server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize);
|
||||
server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
|
||||
@ -721,7 +720,7 @@ cifs_echo_callback(struct mid_q_entry *mid)
|
||||
struct TCP_Server_Info *server = mid->callback_data;
|
||||
|
||||
DeleteMidQEntry(mid);
|
||||
cifs_add_credits(server, 1);
|
||||
add_credits(server, 1);
|
||||
}
|
||||
|
||||
int
|
||||
@ -1385,28 +1384,6 @@ openRetry:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct cifs_readdata *
|
||||
cifs_readdata_alloc(unsigned int nr_pages)
|
||||
{
|
||||
struct cifs_readdata *rdata;
|
||||
|
||||
/* readdata + 1 kvec for each page */
|
||||
rdata = kzalloc(sizeof(*rdata) +
|
||||
sizeof(struct kvec) * nr_pages, GFP_KERNEL);
|
||||
if (rdata != NULL) {
|
||||
INIT_WORK(&rdata->work, cifs_readv_complete);
|
||||
INIT_LIST_HEAD(&rdata->pages);
|
||||
}
|
||||
return rdata;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_readdata_free(struct cifs_readdata *rdata)
|
||||
{
|
||||
cifsFileInfo_put(rdata->cfile);
|
||||
kfree(rdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* Discard any remaining data in the current SMB. To do this, we borrow the
|
||||
* current bigbuf.
|
||||
@ -1423,7 +1400,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
|
||||
length = cifs_read_from_socket(server, server->bigbuf,
|
||||
min_t(unsigned int, remaining,
|
||||
CIFSMaxBufSize + max_header_size()));
|
||||
CIFSMaxBufSize + MAX_HEADER_SIZE(server)));
|
||||
if (length < 0)
|
||||
return length;
|
||||
server->total_read += length;
|
||||
@ -1434,38 +1411,14 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
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
|
||||
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
{
|
||||
int length, len;
|
||||
unsigned int data_offset, remaining, data_len;
|
||||
unsigned int data_offset, data_len;
|
||||
struct cifs_readdata *rdata = mid->callback_data;
|
||||
char *buf = server->smallbuf;
|
||||
unsigned int buflen = get_rfc1002_length(buf) + 4;
|
||||
u64 eof;
|
||||
pgoff_t eof_index;
|
||||
struct page *page, *tpage;
|
||||
|
||||
cFYI(1, "%s: mid=%llu offset=%llu bytes=%u", __func__,
|
||||
mid->mid, rdata->offset, rdata->bytes);
|
||||
@ -1475,9 +1428,10 @@ 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
|
||||
* the Mid.
|
||||
*/
|
||||
len = min_t(unsigned int, buflen, read_rsp_size()) - header_size() + 1;
|
||||
len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
|
||||
HEADER_SIZE(server) + 1;
|
||||
|
||||
rdata->iov[0].iov_base = buf + header_size() - 1;
|
||||
rdata->iov[0].iov_base = buf + HEADER_SIZE(server) - 1;
|
||||
rdata->iov[0].iov_len = len;
|
||||
|
||||
length = cifs_readv_from_socket(server, rdata->iov, 1, len);
|
||||
@ -1486,7 +1440,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
server->total_read += length;
|
||||
|
||||
/* Was the SMB read successful? */
|
||||
rdata->result = map_smb_to_linux_error(buf, false);
|
||||
rdata->result = server->ops->map_error(buf, false);
|
||||
if (rdata->result != 0) {
|
||||
cFYI(1, "%s: server returned error %d", __func__,
|
||||
rdata->result);
|
||||
@ -1494,14 +1448,15 @@ 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? */
|
||||
if (server->total_read < read_rsp_size()) {
|
||||
if (server->total_read < server->vals->read_rsp_size) {
|
||||
cFYI(1, "%s: server returned short header. got=%u expected=%zu",
|
||||
__func__, server->total_read, read_rsp_size());
|
||||
__func__, server->total_read,
|
||||
server->vals->read_rsp_size);
|
||||
rdata->result = -EIO;
|
||||
return cifs_readv_discard(server, mid);
|
||||
}
|
||||
|
||||
data_offset = read_data_offset(buf) + 4;
|
||||
data_offset = server->ops->read_data_offset(buf) + 4;
|
||||
if (data_offset < server->total_read) {
|
||||
/*
|
||||
* win2k8 sometimes sends an offset of 0 when the read
|
||||
@ -1540,7 +1495,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
|
||||
|
||||
/* how much data is in the response? */
|
||||
data_len = read_data_length(buf);
|
||||
data_len = server->ops->read_data_length(buf);
|
||||
if (data_offset + data_len > buflen) {
|
||||
/* data_len is corrupt -- discard frame */
|
||||
rdata->result = -EIO;
|
||||
@ -1548,64 +1503,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
}
|
||||
|
||||
/* marshal up the page array */
|
||||
len = 0;
|
||||
remaining = data_len;
|
||||
rdata->nr_iov = 1;
|
||||
|
||||
/* determine the eof that the server (probably) has */
|
||||
eof = CIFS_I(rdata->mapping->host)->server_eof;
|
||||
eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
|
||||
cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index);
|
||||
|
||||
list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
|
||||
if (remaining >= PAGE_CACHE_SIZE) {
|
||||
/* enough data to fill the page */
|
||||
rdata->iov[rdata->nr_iov].iov_base = kmap(page);
|
||||
rdata->iov[rdata->nr_iov].iov_len = PAGE_CACHE_SIZE;
|
||||
cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
|
||||
rdata->nr_iov, page->index,
|
||||
rdata->iov[rdata->nr_iov].iov_base,
|
||||
rdata->iov[rdata->nr_iov].iov_len);
|
||||
++rdata->nr_iov;
|
||||
len += PAGE_CACHE_SIZE;
|
||||
remaining -= PAGE_CACHE_SIZE;
|
||||
} else if (remaining > 0) {
|
||||
/* enough for partial page, fill and zero the rest */
|
||||
rdata->iov[rdata->nr_iov].iov_base = kmap(page);
|
||||
rdata->iov[rdata->nr_iov].iov_len = remaining;
|
||||
cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
|
||||
rdata->nr_iov, page->index,
|
||||
rdata->iov[rdata->nr_iov].iov_base,
|
||||
rdata->iov[rdata->nr_iov].iov_len);
|
||||
memset(rdata->iov[rdata->nr_iov].iov_base + remaining,
|
||||
'\0', PAGE_CACHE_SIZE - remaining);
|
||||
++rdata->nr_iov;
|
||||
len += remaining;
|
||||
remaining = 0;
|
||||
} else if (page->index > eof_index) {
|
||||
/*
|
||||
* The VFS will not try to do readahead past the
|
||||
* i_size, but it's possible that we have outstanding
|
||||
* writes with gaps in the middle and the i_size hasn't
|
||||
* caught up yet. Populate those with zeroed out pages
|
||||
* to prevent the VFS from repeatedly attempting to
|
||||
* fill them until the writes are flushed.
|
||||
*/
|
||||
zero_user(page, 0, PAGE_CACHE_SIZE);
|
||||
list_del(&page->lru);
|
||||
lru_cache_add_file(page);
|
||||
flush_dcache_page(page);
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
} else {
|
||||
/* no need to hold page hostage */
|
||||
list_del(&page->lru);
|
||||
lru_cache_add_file(page);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
}
|
||||
}
|
||||
len = rdata->marshal_iov(rdata, data_len);
|
||||
data_len -= len;
|
||||
|
||||
/* issue the read if we have any iovecs left to fill */
|
||||
if (rdata->nr_iov > 1) {
|
||||
@ -1621,7 +1520,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
rdata->bytes = length;
|
||||
|
||||
cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read,
|
||||
buflen, remaining);
|
||||
buflen, data_len);
|
||||
|
||||
/* discard anything left over */
|
||||
if (server->total_read < buflen)
|
||||
@ -1631,33 +1530,6 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
return length;
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_readv_complete(struct work_struct *work)
|
||||
{
|
||||
struct cifs_readdata *rdata = container_of(work,
|
||||
struct cifs_readdata, work);
|
||||
struct page *page, *tpage;
|
||||
|
||||
list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
|
||||
list_del(&page->lru);
|
||||
lru_cache_add_file(page);
|
||||
|
||||
if (rdata->result == 0) {
|
||||
kunmap(page);
|
||||
flush_dcache_page(page);
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
if (rdata->result == 0)
|
||||
cifs_readpage_to_fscache(rdata->mapping->host, page);
|
||||
|
||||
page_cache_release(page);
|
||||
}
|
||||
cifs_readdata_free(rdata);
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_readv_callback(struct mid_q_entry *mid)
|
||||
{
|
||||
@ -1691,7 +1563,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
|
||||
|
||||
queue_work(cifsiod_wq, &rdata->work);
|
||||
DeleteMidQEntry(mid);
|
||||
cifs_add_credits(server, 1);
|
||||
add_credits(server, 1);
|
||||
}
|
||||
|
||||
/* cifs_async_readv - send an async write, and set up mid to handle result */
|
||||
@ -1744,12 +1616,15 @@ cifs_async_readv(struct cifs_readdata *rdata)
|
||||
rdata->iov[0].iov_base = smb;
|
||||
rdata->iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
|
||||
|
||||
kref_get(&rdata->refcount);
|
||||
rc = cifs_call_async(tcon->ses->server, rdata->iov, 1,
|
||||
cifs_readv_receive, cifs_readv_callback,
|
||||
rdata, false);
|
||||
|
||||
if (rc == 0)
|
||||
cifs_stats_inc(&tcon->num_reads);
|
||||
else
|
||||
kref_put(&rdata->refcount, cifs_readdata_release);
|
||||
|
||||
cifs_small_buf_release(smb);
|
||||
return rc;
|
||||
@ -2135,7 +2010,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
|
||||
|
||||
queue_work(cifsiod_wq, &wdata->work);
|
||||
DeleteMidQEntry(mid);
|
||||
cifs_add_credits(tcon->ses->server, 1);
|
||||
add_credits(tcon->ses->server, 1);
|
||||
}
|
||||
|
||||
/* cifs_async_writev - send an async write, and set up mid to handle result */
|
||||
@ -4344,7 +4219,7 @@ int
|
||||
CIFSFindFirst(const int xid, struct cifs_tcon *tcon,
|
||||
const char *searchName,
|
||||
const struct nls_table *nls_codepage,
|
||||
__u16 *pnetfid,
|
||||
__u16 *pnetfid, __u16 search_flags,
|
||||
struct cifs_search_info *psrch_inf, int remap, const char dirsep)
|
||||
{
|
||||
/* level 257 SMB_ */
|
||||
@ -4416,8 +4291,7 @@ findFirstRetry:
|
||||
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
|
||||
ATTR_DIRECTORY);
|
||||
pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
|
||||
pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
|
||||
CIFS_SEARCH_RETURN_RESUME);
|
||||
pSMB->SearchFlags = cpu_to_le16(search_flags);
|
||||
pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
|
||||
|
||||
/* BB what should we set StorageType to? Does it matter? BB */
|
||||
@ -4487,8 +4361,8 @@ findFirstRetry:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
|
||||
__u16 searchHandle, struct cifs_search_info *psrch_inf)
|
||||
int CIFSFindNext(const int xid, struct cifs_tcon *tcon, __u16 searchHandle,
|
||||
__u16 search_flags, struct cifs_search_info *psrch_inf)
|
||||
{
|
||||
TRANSACTION2_FNEXT_REQ *pSMB = NULL;
|
||||
TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
|
||||
@ -4531,8 +4405,7 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
|
||||
cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
|
||||
pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
|
||||
pSMB->ResumeKey = psrch_inf->resume_key;
|
||||
pSMB->SearchFlags =
|
||||
cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
|
||||
pSMB->SearchFlags = cpu_to_le16(search_flags);
|
||||
|
||||
name_len = psrch_inf->resume_name_len;
|
||||
params += name_len;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* fs/cifs/connect.c
|
||||
*
|
||||
* Copyright (C) International Business Machines Corp., 2002,2009
|
||||
* Copyright (C) International Business Machines Corp., 2002,2011
|
||||
* Author(s): Steve French (sfrench@us.ibm.com)
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
@ -102,7 +102,7 @@ enum {
|
||||
Opt_srcaddr, Opt_prefixpath,
|
||||
Opt_iocharset, Opt_sockopt,
|
||||
Opt_netbiosname, Opt_servern,
|
||||
Opt_ver, Opt_sec,
|
||||
Opt_ver, Opt_vers, Opt_sec, Opt_cache,
|
||||
|
||||
/* Mount options to be ignored */
|
||||
Opt_ignore,
|
||||
@ -210,9 +210,9 @@ static const match_table_t cifs_mount_option_tokens = {
|
||||
{ Opt_netbiosname, "netbiosname=%s" },
|
||||
{ Opt_servern, "servern=%s" },
|
||||
{ Opt_ver, "ver=%s" },
|
||||
{ Opt_ver, "vers=%s" },
|
||||
{ Opt_ver, "version=%s" },
|
||||
{ Opt_vers, "vers=%s" },
|
||||
{ Opt_sec, "sec=%s" },
|
||||
{ Opt_cache, "cache=%s" },
|
||||
|
||||
{ Opt_ignore, "cred" },
|
||||
{ Opt_ignore, "credentials" },
|
||||
@ -261,6 +261,26 @@ static const match_table_t cifs_secflavor_tokens = {
|
||||
{ Opt_sec_err, NULL }
|
||||
};
|
||||
|
||||
/* cache flavors */
|
||||
enum {
|
||||
Opt_cache_loose,
|
||||
Opt_cache_strict,
|
||||
Opt_cache_none,
|
||||
Opt_cache_err
|
||||
};
|
||||
|
||||
static const match_table_t cifs_cacheflavor_tokens = {
|
||||
{ Opt_cache_loose, "loose" },
|
||||
{ Opt_cache_strict, "strict" },
|
||||
{ Opt_cache_none, "none" },
|
||||
{ Opt_cache_err, NULL }
|
||||
};
|
||||
|
||||
static const match_table_t cifs_smb_version_tokens = {
|
||||
{ Smb_1, SMB1_VERSION_STRING },
|
||||
{ Smb_21, SMB21_VERSION_STRING },
|
||||
};
|
||||
|
||||
static int 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);
|
||||
@ -549,7 +569,7 @@ allocate_buffers(struct TCP_Server_Info *server)
|
||||
}
|
||||
} else if (server->large_buf) {
|
||||
/* we are reusing a dirty large buf, clear its start */
|
||||
memset(server->bigbuf, 0, header_size());
|
||||
memset(server->bigbuf, 0, HEADER_SIZE(server));
|
||||
}
|
||||
|
||||
if (!server->smallbuf) {
|
||||
@ -563,7 +583,7 @@ allocate_buffers(struct TCP_Server_Info *server)
|
||||
/* beginning of smb buffer is cleared in our buf_get */
|
||||
} else {
|
||||
/* if existing small buf clear beginning */
|
||||
memset(server->smallbuf, 0, header_size());
|
||||
memset(server->smallbuf, 0, HEADER_SIZE(server));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -764,25 +784,6 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct mid_q_entry *
|
||||
find_mid(struct TCP_Server_Info *server, char *buffer)
|
||||
{
|
||||
struct smb_hdr *buf = (struct smb_hdr *)buffer;
|
||||
struct mid_q_entry *mid;
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
|
||||
if (mid->mid == buf->Mid &&
|
||||
mid->mid_state == MID_REQUEST_SUBMITTED &&
|
||||
le16_to_cpu(mid->command) == buf->Command) {
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
dequeue_mid(struct mid_q_entry *mid, bool malformed)
|
||||
{
|
||||
@ -934,7 +935,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
unsigned int pdu_length = get_rfc1002_length(buf);
|
||||
|
||||
/* make sure this will fit in a large buffer */
|
||||
if (pdu_length > CIFSMaxBufSize + max_header_size() - 4) {
|
||||
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - 4) {
|
||||
cERROR(1, "SMB response too long (%u bytes)",
|
||||
pdu_length);
|
||||
cifs_reconnect(server);
|
||||
@ -950,8 +951,8 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
}
|
||||
|
||||
/* now read the rest */
|
||||
length = cifs_read_from_socket(server, buf + header_size() - 1,
|
||||
pdu_length - header_size() + 1 + 4);
|
||||
length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
|
||||
pdu_length - HEADER_SIZE(server) + 1 + 4);
|
||||
if (length < 0)
|
||||
return length;
|
||||
server->total_read += length;
|
||||
@ -967,7 +968,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
|
||||
* into the payload for debugging purposes.
|
||||
*/
|
||||
length = checkSMB(buf, server->total_read);
|
||||
length = server->ops->check_message(buf, server->total_read);
|
||||
if (length != 0)
|
||||
cifs_dump_mem("Bad SMB: ", buf,
|
||||
min_t(unsigned int, server->total_read, 48));
|
||||
@ -1025,7 +1026,7 @@ cifs_demultiplex_thread(void *p)
|
||||
continue;
|
||||
|
||||
/* make sure we have enough to get to the MID */
|
||||
if (pdu_length < header_size() - 1 - 4) {
|
||||
if (pdu_length < HEADER_SIZE(server) - 1 - 4) {
|
||||
cERROR(1, "SMB response too short (%u bytes)",
|
||||
pdu_length);
|
||||
cifs_reconnect(server);
|
||||
@ -1035,12 +1036,12 @@ cifs_demultiplex_thread(void *p)
|
||||
|
||||
/* read down to the MID */
|
||||
length = cifs_read_from_socket(server, buf + 4,
|
||||
header_size() - 1 - 4);
|
||||
HEADER_SIZE(server) - 1 - 4);
|
||||
if (length < 0)
|
||||
continue;
|
||||
server->total_read += length;
|
||||
|
||||
mid_entry = find_mid(server, buf);
|
||||
mid_entry = server->ops->find_mid(server, buf);
|
||||
|
||||
if (!mid_entry || !mid_entry->receive)
|
||||
length = standard_receive3(server, mid_entry);
|
||||
@ -1057,12 +1058,13 @@ cifs_demultiplex_thread(void *p)
|
||||
if (mid_entry != NULL) {
|
||||
if (!mid_entry->multiRsp || mid_entry->multiEnd)
|
||||
mid_entry->callback(mid_entry);
|
||||
} else if (!is_valid_oplock_break(buf, server)) {
|
||||
} else if (!server->ops->is_oplock_break(buf, server)) {
|
||||
cERROR(1, "No task to wake, unknown frame received! "
|
||||
"NumMids %d", atomic_read(&midCount));
|
||||
cifs_dump_mem("Received Data is: ", buf, header_size());
|
||||
cifs_dump_mem("Received Data is: ", buf,
|
||||
HEADER_SIZE(server));
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
cifs_dump_detail(buf);
|
||||
server->ops->dump_detail(buf);
|
||||
cifs_dump_mids(server);
|
||||
#endif /* CIFS_DEBUG2 */
|
||||
|
||||
@ -1185,6 +1187,54 @@ static int cifs_parse_security_flavors(char *value,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_parse_cache_flavor(char *value, struct smb_vol *vol)
|
||||
{
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
switch (match_token(value, cifs_cacheflavor_tokens, args)) {
|
||||
case Opt_cache_loose:
|
||||
vol->direct_io = false;
|
||||
vol->strict_io = false;
|
||||
break;
|
||||
case Opt_cache_strict:
|
||||
vol->direct_io = false;
|
||||
vol->strict_io = true;
|
||||
break;
|
||||
case Opt_cache_none:
|
||||
vol->direct_io = true;
|
||||
vol->strict_io = false;
|
||||
break;
|
||||
default:
|
||||
cERROR(1, "bad cache= option: %s", value);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_parse_smb_version(char *value, struct smb_vol *vol)
|
||||
{
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
switch (match_token(value, cifs_smb_version_tokens, args)) {
|
||||
case Smb_1:
|
||||
vol->ops = &smb1_operations;
|
||||
vol->vals = &smb1_values;
|
||||
break;
|
||||
#ifdef CONFIG_CIFS_SMB2
|
||||
case Smb_21:
|
||||
vol->ops = &smb21_operations;
|
||||
vol->vals = &smb21_values;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
cERROR(1, "Unknown vers= option specified: %s", value);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
struct smb_vol *vol)
|
||||
@ -1203,6 +1253,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
char *string = NULL;
|
||||
char *tmp_end, *value;
|
||||
char delim;
|
||||
bool cache_specified = false;
|
||||
static bool cache_warned = false;
|
||||
|
||||
separator[0] = ',';
|
||||
separator[1] = 0;
|
||||
@ -1236,6 +1288,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
|
||||
vol->actimeo = CIFS_DEF_ACTIMEO;
|
||||
|
||||
/* FIXME: add autonegotiation -- for now, SMB1 is default */
|
||||
vol->ops = &smb1_operations;
|
||||
vol->vals = &smb1_values;
|
||||
|
||||
if (!mountdata)
|
||||
goto cifs_parse_mount_err;
|
||||
|
||||
@ -1414,10 +1470,20 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
vol->seal = 1;
|
||||
break;
|
||||
case Opt_direct:
|
||||
vol->direct_io = 1;
|
||||
cache_specified = true;
|
||||
vol->direct_io = true;
|
||||
vol->strict_io = false;
|
||||
cERROR(1, "The \"directio\" option will be removed in "
|
||||
"3.7. Please switch to the \"cache=none\" "
|
||||
"option.");
|
||||
break;
|
||||
case Opt_strictcache:
|
||||
vol->strict_io = 1;
|
||||
cache_specified = true;
|
||||
vol->direct_io = false;
|
||||
vol->strict_io = true;
|
||||
cERROR(1, "The \"strictcache\" option will be removed "
|
||||
"in 3.7. Please switch to the \"cache=strict\" "
|
||||
"option.");
|
||||
break;
|
||||
case Opt_noac:
|
||||
printk(KERN_WARNING "CIFS: Mount option noac not "
|
||||
@ -1821,8 +1887,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
if (string == NULL)
|
||||
goto out_nomem;
|
||||
|
||||
if (strnicmp(string, "cifs", 4) == 0 ||
|
||||
strnicmp(string, "1", 1) == 0) {
|
||||
if (strnicmp(string, "1", 1) == 0) {
|
||||
/* This is the default */
|
||||
break;
|
||||
}
|
||||
@ -1830,6 +1895,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
printk(KERN_WARNING "CIFS: Invalid version"
|
||||
" specified\n");
|
||||
goto cifs_parse_mount_err;
|
||||
case Opt_vers:
|
||||
string = match_strdup(args);
|
||||
if (string == NULL)
|
||||
goto out_nomem;
|
||||
|
||||
if (cifs_parse_smb_version(string, vol) != 0)
|
||||
goto cifs_parse_mount_err;
|
||||
break;
|
||||
case Opt_sec:
|
||||
string = match_strdup(args);
|
||||
if (string == NULL)
|
||||
@ -1838,6 +1911,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
if (cifs_parse_security_flavors(string, vol) != 0)
|
||||
goto cifs_parse_mount_err;
|
||||
break;
|
||||
case Opt_cache:
|
||||
cache_specified = true;
|
||||
string = match_strdup(args);
|
||||
if (string == NULL)
|
||||
goto out_nomem;
|
||||
|
||||
if (cifs_parse_cache_flavor(string, vol) != 0)
|
||||
goto cifs_parse_mount_err;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* An option we don't recognize. Save it off for later
|
||||
@ -1881,6 +1963,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
printk(KERN_NOTICE "CIFS: ignoring forcegid mount option "
|
||||
"specified with no gid= option.\n");
|
||||
|
||||
/* FIXME: remove this block in 3.7 */
|
||||
if (!cache_specified && !cache_warned) {
|
||||
cache_warned = true;
|
||||
printk(KERN_NOTICE "CIFS: no cache= option specified, using "
|
||||
"\"cache=loose\". This default will change "
|
||||
"to \"cache=strict\" in 3.7.\n");
|
||||
}
|
||||
|
||||
kfree(mountdata_copy);
|
||||
return 0;
|
||||
|
||||
@ -2041,6 +2131,9 @@ match_security(struct TCP_Server_Info *server, struct smb_vol *vol)
|
||||
static int match_server(struct TCP_Server_Info *server, struct sockaddr *addr,
|
||||
struct smb_vol *vol)
|
||||
{
|
||||
if ((server->vals != vol->vals) || (server->ops != vol->ops))
|
||||
return 0;
|
||||
|
||||
if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
|
||||
return 0;
|
||||
|
||||
@ -2163,6 +2256,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
tcp_ses->ops = volume_info->ops;
|
||||
tcp_ses->vals = volume_info->vals;
|
||||
cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
|
||||
tcp_ses->hostname = extract_hostname(volume_info->UNC);
|
||||
if (IS_ERR(tcp_ses->hostname)) {
|
||||
@ -3569,6 +3664,7 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
|
||||
if (cifs_parse_mount_options(mount_data, devname, volume_info))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
if (volume_info->nullauth) {
|
||||
cFYI(1, "Anonymous login");
|
||||
kfree(volume_info->username);
|
||||
@ -4010,11 +4106,11 @@ int cifs_negotiate_protocol(unsigned int xid, struct cifs_ses *ses)
|
||||
if (server->maxBuf != 0)
|
||||
return 0;
|
||||
|
||||
cifs_set_credits(server, 1);
|
||||
set_credits(server, 1);
|
||||
rc = CIFSSMBNegotiate(xid, ses);
|
||||
if (rc == -EAGAIN) {
|
||||
/* retry only once on 1st time connection */
|
||||
cifs_set_credits(server, 1);
|
||||
set_credits(server, 1);
|
||||
rc = CIFSSMBNegotiate(xid, ses);
|
||||
if (rc == -EAGAIN)
|
||||
rc = -EHOSTDOWN;
|
||||
|
685
fs/cifs/file.c
685
fs/cifs/file.c
File diff suppressed because it is too large
Load Diff
@ -51,7 +51,15 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
|
||||
switch (command) {
|
||||
static bool warned = false;
|
||||
case CIFS_IOC_CHECKUMOUNT:
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
cERROR(1, "the CIFS_IOC_CHECKMOUNT ioctl will "
|
||||
"be deprecated in 3.7. Please "
|
||||
"migrate away from the use of "
|
||||
"umount.cifs");
|
||||
}
|
||||
cFYI(1, "User unmount attempted");
|
||||
if (cifs_sb->mnt_uid == current_uid())
|
||||
rc = 0;
|
||||
|
@ -306,8 +306,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
||||
const struct cifs_tcon *treeCon, int word_count
|
||||
/* length of fixed section (word count) in two byte units */)
|
||||
{
|
||||
struct list_head *temp_item;
|
||||
struct cifs_ses *ses;
|
||||
char *temp = (char *) buffer;
|
||||
|
||||
memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */
|
||||
@ -337,51 +335,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
||||
/* Uid is not converted */
|
||||
buffer->Uid = treeCon->ses->Suid;
|
||||
buffer->Mid = GetNextMid(treeCon->ses->server);
|
||||
if (multiuser_mount != 0) {
|
||||
/* For the multiuser case, there are few obvious technically */
|
||||
/* possible mechanisms to match the local linux user (uid) */
|
||||
/* to a valid remote smb user (smb_uid): */
|
||||
/* 1) Query Winbind (or other local pam/nss daemon */
|
||||
/* for userid/password/logon_domain or credential */
|
||||
/* 2) Query Winbind for uid to sid to username mapping */
|
||||
/* and see if we have a matching password for existing*/
|
||||
/* session for that user perhas getting password by */
|
||||
/* adding a new pam_cifs module that stores passwords */
|
||||
/* so that the cifs vfs can get at that for all logged*/
|
||||
/* on users */
|
||||
/* 3) (Which is the mechanism we have chosen) */
|
||||
/* Search through sessions to the same server for a */
|
||||
/* a match on the uid that was passed in on mount */
|
||||
/* with the current processes uid (or euid?) and use */
|
||||
/* that smb uid. If no existing smb session for */
|
||||
/* that uid found, use the default smb session ie */
|
||||
/* the smb session for the volume mounted which is */
|
||||
/* the same as would be used if the multiuser mount */
|
||||
/* flag were disabled. */
|
||||
|
||||
/* BB Add support for establishing new tCon and SMB Session */
|
||||
/* with userid/password pairs found on the smb session */
|
||||
/* for other target tcp/ip addresses BB */
|
||||
if (current_fsuid() != treeCon->ses->linux_uid) {
|
||||
cFYI(1, "Multiuser mode and UID "
|
||||
"did not match tcon uid");
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
|
||||
ses = list_entry(temp_item, struct cifs_ses, smb_ses_list);
|
||||
if (ses->linux_uid == current_fsuid()) {
|
||||
if (ses->server == treeCon->ses->server) {
|
||||
cFYI(1, "found matching uid substitute right smb_uid");
|
||||
buffer->Uid = ses->Suid;
|
||||
break;
|
||||
} else {
|
||||
/* BB eventually call cifs_setup_session here */
|
||||
cFYI(1, "local UID found but no smb sess with this server exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
|
||||
buffer->Flags2 |= SMBFLG2_DFS;
|
||||
@ -700,22 +653,3 @@ backup_cred(struct cifs_sb_info *cifs_sb)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add)
|
||||
{
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits += add;
|
||||
server->in_flight--;
|
||||
spin_unlock(&server->req_lock);
|
||||
wake_up(&server->request_q);
|
||||
}
|
||||
|
||||
void
|
||||
cifs_set_credits(struct TCP_Server_Info *server, const int val)
|
||||
{
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits = val;
|
||||
server->oplocks = val > 1 ? enable_oplocks : false;
|
||||
spin_unlock(&server->req_lock);
|
||||
}
|
||||
|
@ -219,6 +219,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
|
||||
|
||||
static int initiate_cifs_search(const int xid, struct file *file)
|
||||
{
|
||||
__u16 search_flags;
|
||||
int rc = 0;
|
||||
char *full_path = NULL;
|
||||
struct cifsFileInfo *cifsFile;
|
||||
@ -270,8 +271,12 @@ ffirst_retry:
|
||||
cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
|
||||
}
|
||||
|
||||
search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
|
||||
if (backup_cred(cifs_sb))
|
||||
search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
|
||||
|
||||
rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls,
|
||||
&cifsFile->netfid, &cifsFile->srch_inf,
|
||||
&cifsFile->netfid, search_flags, &cifsFile->srch_inf,
|
||||
cifs_sb->mnt_cifs_flags &
|
||||
CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
|
||||
if (rc == 0)
|
||||
@ -502,11 +507,13 @@ static int cifs_save_resume_key(const char *current_entry,
|
||||
static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon,
|
||||
struct file *file, char **ppCurrentEntry, int *num_to_ret)
|
||||
{
|
||||
__u16 search_flags;
|
||||
int rc = 0;
|
||||
int pos_in_buf = 0;
|
||||
loff_t first_entry_in_buffer;
|
||||
loff_t index_to_find = file->f_pos;
|
||||
struct cifsFileInfo *cifsFile = file->private_data;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||
/* check if index in the buffer */
|
||||
|
||||
if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
|
||||
@ -560,10 +567,14 @@ static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon,
|
||||
cifsFile);
|
||||
}
|
||||
|
||||
search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
|
||||
if (backup_cred(cifs_sb))
|
||||
search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
|
||||
|
||||
while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
|
||||
(rc == 0) && !cifsFile->srch_inf.endOfSearch) {
|
||||
cFYI(1, "calling findnext2");
|
||||
rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
|
||||
rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, search_flags,
|
||||
&cifsFile->srch_inf);
|
||||
/* FindFirst/Next set last_entry to NULL on malformed reply */
|
||||
if (cifsFile->srch_inf.last_entry)
|
||||
|
154
fs/cifs/smb1ops.c
Normal file
154
fs/cifs/smb1ops.c
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* SMB1 (CIFS) version specific operations
|
||||
*
|
||||
* Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License v2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
* the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cifsglob.h"
|
||||
#include "cifsproto.h"
|
||||
#include "cifs_debug.h"
|
||||
#include "cifspdu.h"
|
||||
|
||||
/*
|
||||
* An NT cancel request header looks just like the original request except:
|
||||
*
|
||||
* The Command is SMB_COM_NT_CANCEL
|
||||
* The WordCount is zeroed out
|
||||
* The ByteCount is zeroed out
|
||||
*
|
||||
* This function mangles an existing request buffer into a
|
||||
* SMB_COM_NT_CANCEL request and then sends it.
|
||||
*/
|
||||
static int
|
||||
send_nt_cancel(struct TCP_Server_Info *server, void *buf,
|
||||
struct mid_q_entry *mid)
|
||||
{
|
||||
int rc = 0;
|
||||
struct smb_hdr *in_buf = (struct smb_hdr *)buf;
|
||||
|
||||
/* -4 for RFC1001 length and +2 for BCC field */
|
||||
in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2);
|
||||
in_buf->Command = SMB_COM_NT_CANCEL;
|
||||
in_buf->WordCount = 0;
|
||||
put_bcc(0, in_buf);
|
||||
|
||||
mutex_lock(&server->srv_mutex);
|
||||
rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
|
||||
if (rc) {
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
return rc;
|
||||
}
|
||||
rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
|
||||
cFYI(1, "issued NT_CANCEL for mid %u, rc = %d",
|
||||
in_buf->Mid, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool
|
||||
cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
|
||||
{
|
||||
return ob1->netfid == ob2->netfid;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
cifs_read_data_offset(char *buf)
|
||||
{
|
||||
READ_RSP *rsp = (READ_RSP *)buf;
|
||||
return le16_to_cpu(rsp->DataOffset);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
cifs_read_data_length(char *buf)
|
||||
{
|
||||
READ_RSP *rsp = (READ_RSP *)buf;
|
||||
return (le16_to_cpu(rsp->DataLengthHigh) << 16) +
|
||||
le16_to_cpu(rsp->DataLength);
|
||||
}
|
||||
|
||||
static struct mid_q_entry *
|
||||
cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
|
||||
{
|
||||
struct smb_hdr *buf = (struct smb_hdr *)buffer;
|
||||
struct mid_q_entry *mid;
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
|
||||
if (mid->mid == buf->Mid &&
|
||||
mid->mid_state == MID_REQUEST_SUBMITTED &&
|
||||
le16_to_cpu(mid->command) == buf->Command) {
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add)
|
||||
{
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits += add;
|
||||
server->in_flight--;
|
||||
spin_unlock(&server->req_lock);
|
||||
wake_up(&server->request_q);
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_set_credits(struct TCP_Server_Info *server, const int val)
|
||||
{
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits = val;
|
||||
server->oplocks = val > 1 ? enable_oplocks : false;
|
||||
spin_unlock(&server->req_lock);
|
||||
}
|
||||
|
||||
static int *
|
||||
cifs_get_credits_field(struct TCP_Server_Info *server)
|
||||
{
|
||||
return &server->credits;
|
||||
}
|
||||
|
||||
struct smb_version_operations smb1_operations = {
|
||||
.send_cancel = send_nt_cancel,
|
||||
.compare_fids = cifs_compare_fids,
|
||||
.setup_request = cifs_setup_request,
|
||||
.check_receive = cifs_check_receive,
|
||||
.add_credits = cifs_add_credits,
|
||||
.set_credits = cifs_set_credits,
|
||||
.get_credits_field = cifs_get_credits_field,
|
||||
.read_data_offset = cifs_read_data_offset,
|
||||
.read_data_length = cifs_read_data_length,
|
||||
.map_error = map_smb_to_linux_error,
|
||||
.find_mid = cifs_find_mid,
|
||||
.check_message = checkSMB,
|
||||
.dump_detail = cifs_dump_detail,
|
||||
.is_oplock_break = is_valid_oplock_break,
|
||||
};
|
||||
|
||||
struct smb_version_values smb1_values = {
|
||||
.version_string = SMB1_VERSION_STRING,
|
||||
.large_lock_type = LOCKING_ANDX_LARGE_FILES,
|
||||
.exclusive_lock_type = 0,
|
||||
.shared_lock_type = LOCKING_ANDX_SHARED_LOCK,
|
||||
.unlock_lock_type = 0,
|
||||
.header_size = sizeof(struct smb_hdr),
|
||||
.max_header_size = MAX_CIFS_HDR_SIZE,
|
||||
.read_rsp_size = sizeof(READ_RSP),
|
||||
};
|
27
fs/cifs/smb2ops.c
Normal file
27
fs/cifs/smb2ops.c
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SMB2 version specific operations
|
||||
*
|
||||
* Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License v2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
* the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cifsglob.h"
|
||||
|
||||
struct smb_version_operations smb21_operations = {
|
||||
};
|
||||
|
||||
struct smb_version_values smb21_values = {
|
||||
.version_string = SMB21_VERSION_STRING,
|
||||
};
|
@ -304,7 +304,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int optype,
|
||||
static int
|
||||
wait_for_free_request(struct TCP_Server_Info *server, const int optype)
|
||||
{
|
||||
return wait_for_free_credits(server, optype, get_credits_field(server));
|
||||
return wait_for_free_credits(server, optype,
|
||||
server->ops->get_credits_field(server));
|
||||
}
|
||||
|
||||
static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
|
||||
@ -396,7 +397,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
|
||||
rc = cifs_setup_async_request(server, iov, nvec, &mid);
|
||||
if (rc) {
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
cifs_add_credits(server, 1);
|
||||
add_credits(server, 1);
|
||||
wake_up(&server->request_q);
|
||||
return rc;
|
||||
}
|
||||
@ -418,7 +419,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
|
||||
return rc;
|
||||
out_err:
|
||||
delete_mid(mid);
|
||||
cifs_add_credits(server, 1);
|
||||
add_credits(server, 1);
|
||||
wake_up(&server->request_q);
|
||||
return rc;
|
||||
}
|
||||
@ -483,41 +484,11 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* An NT cancel request header looks just like the original request except:
|
||||
*
|
||||
* The Command is SMB_COM_NT_CANCEL
|
||||
* The WordCount is zeroed out
|
||||
* The ByteCount is zeroed out
|
||||
*
|
||||
* This function mangles an existing request buffer into a
|
||||
* SMB_COM_NT_CANCEL request and then sends it.
|
||||
*/
|
||||
static int
|
||||
send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf,
|
||||
struct mid_q_entry *mid)
|
||||
static inline int
|
||||
send_cancel(struct TCP_Server_Info *server, void *buf, struct mid_q_entry *mid)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* -4 for RFC1001 length and +2 for BCC field */
|
||||
in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2);
|
||||
in_buf->Command = SMB_COM_NT_CANCEL;
|
||||
in_buf->WordCount = 0;
|
||||
put_bcc(0, in_buf);
|
||||
|
||||
mutex_lock(&server->srv_mutex);
|
||||
rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
|
||||
if (rc) {
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
return rc;
|
||||
}
|
||||
rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
|
||||
cFYI(1, "issued NT_CANCEL for mid %u, rc = %d",
|
||||
in_buf->Mid, rc);
|
||||
|
||||
return rc;
|
||||
return server->ops->send_cancel ?
|
||||
server->ops->send_cancel(server, buf, mid) : 0;
|
||||
}
|
||||
|
||||
int
|
||||
@ -544,7 +515,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||
return map_smb_to_linux_error(mid->resp_buf, log_error);
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
cifs_setup_request(struct cifs_ses *ses, struct kvec *iov,
|
||||
unsigned int nvec, struct mid_q_entry **ret_mid)
|
||||
{
|
||||
@ -607,12 +578,12 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
||||
|
||||
mutex_lock(&ses->server->srv_mutex);
|
||||
|
||||
rc = cifs_setup_request(ses, iov, n_vec, &midQ);
|
||||
rc = ses->server->ops->setup_request(ses, iov, n_vec, &midQ);
|
||||
if (rc) {
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
cifs_small_buf_release(buf);
|
||||
/* Update # of requests on wire to server */
|
||||
cifs_add_credits(ses->server, 1);
|
||||
add_credits(ses->server, 1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -636,13 +607,13 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
||||
|
||||
rc = wait_for_response(ses->server, midQ);
|
||||
if (rc != 0) {
|
||||
send_nt_cancel(ses->server, (struct smb_hdr *)buf, midQ);
|
||||
send_cancel(ses->server, buf, midQ);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
|
||||
midQ->callback = DeleteMidQEntry;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
cifs_small_buf_release(buf);
|
||||
cifs_add_credits(ses->server, 1);
|
||||
add_credits(ses->server, 1);
|
||||
return rc;
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
@ -652,7 +623,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
||||
|
||||
rc = cifs_sync_mid_result(midQ, ses->server);
|
||||
if (rc != 0) {
|
||||
cifs_add_credits(ses->server, 1);
|
||||
add_credits(ses->server, 1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -670,14 +641,15 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
||||
else
|
||||
*pRespBufType = CIFS_SMALL_BUFFER;
|
||||
|
||||
rc = cifs_check_receive(midQ, ses->server, flags & CIFS_LOG_ERROR);
|
||||
rc = ses->server->ops->check_receive(midQ, ses->server,
|
||||
flags & CIFS_LOG_ERROR);
|
||||
|
||||
/* mark it so buf will not be freed by delete_mid */
|
||||
if ((flags & CIFS_NO_RESP) == 0)
|
||||
midQ->resp_buf = NULL;
|
||||
out:
|
||||
delete_mid(midQ);
|
||||
cifs_add_credits(ses->server, 1);
|
||||
add_credits(ses->server, 1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -727,7 +699,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||
if (rc) {
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
/* Update # of requests on wire to server */
|
||||
cifs_add_credits(ses->server, 1);
|
||||
add_credits(ses->server, 1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -753,13 +725,13 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||
|
||||
rc = wait_for_response(ses->server, midQ);
|
||||
if (rc != 0) {
|
||||
send_nt_cancel(ses->server, in_buf, midQ);
|
||||
send_cancel(ses->server, in_buf, midQ);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
|
||||
/* no longer considered to be "in-flight" */
|
||||
midQ->callback = DeleteMidQEntry;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
cifs_add_credits(ses->server, 1);
|
||||
add_credits(ses->server, 1);
|
||||
return rc;
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
@ -767,7 +739,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||
|
||||
rc = cifs_sync_mid_result(midQ, ses->server);
|
||||
if (rc != 0) {
|
||||
cifs_add_credits(ses->server, 1);
|
||||
add_credits(ses->server, 1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -783,7 +755,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||
rc = cifs_check_receive(midQ, ses->server, 0);
|
||||
out:
|
||||
delete_mid(midQ);
|
||||
cifs_add_credits(ses->server, 1);
|
||||
add_credits(ses->server, 1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -898,7 +870,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
if (in_buf->Command == SMB_COM_TRANSACTION2) {
|
||||
/* POSIX lock. We send a NT_CANCEL SMB to cause the
|
||||
blocking lock to return. */
|
||||
rc = send_nt_cancel(ses->server, in_buf, midQ);
|
||||
rc = send_cancel(ses->server, in_buf, midQ);
|
||||
if (rc) {
|
||||
delete_mid(midQ);
|
||||
return rc;
|
||||
@ -919,7 +891,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = wait_for_response(ses->server, midQ);
|
||||
if (rc) {
|
||||
send_nt_cancel(ses->server, in_buf, midQ);
|
||||
send_cancel(ses->server, in_buf, midQ);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
|
||||
/* no longer considered to be "in-flight" */
|
||||
|
Loading…
Reference in New Issue
Block a user