forked from Minki/linux
Various smb3 fixes (including 12 for stable) and also features (addition of multichannel support)
-----BEGIN PGP SIGNATURE----- iQGzBAABCAAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAl3fB2gACgkQiiy9cAdy T1G4rgwAwTVvUYOmUVxJt1OxJDAnVxLXOH08FmrSL08nacrUCb6fPRw0z0Eb65SB 4/Yc3xIH+bJInNpACKZVY0ghPlDYC4u7AtLUZqm4+ruDVSjoWmF4s38WsNCrZywm uAWq4p+j5A62uGdgdSzVxdUJkSLGRTc+7ib+y/TZ2U4BLjb6AY2etUtOpnA6lQ4R UHvjf7leHw55pvPbNnpZ/2zT18KpPH8twCj0f3lH4yt8Akw0p/va6tRcfsmE2plh V3dxiPmS86otXFPyHpu9B9plrxJCcGuYUL+R8pPpbLD3MZMsdK7J+sGNdayOoLXl bpuoTvl3LThEwcg7bFeB/jHXGx95KPm3vh99lrZvbM9Snya+ueSfudrhmoqCEiSo 5ri1tWH7GlPr9nEaxp/aYB2LZENpbYvwn0yxoUdgK+R9EN5F3utUwK54CgYPVz9Y 1zp0LksafWZu7NrIPOuoq+/ATJ1WgbDYrhotk9fwaZoc2EsUfJd0I31XGECisjdh fcHNxbA5 =EIrP -----END PGP SIGNATURE----- Merge tag '5.5-rc-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6 Pull cifs updates from Steve French: "Various smb3 fixes (including 12 for stable) and also features (addition of multichannel support)" * tag '5.5-rc-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: (41 commits) CIFS: fix a white space issue in cifs_get_inode_info() cifs: update internal module version number cifs: Always update signing key of first channel cifs: Fix retrieval of DFS referrals in cifs_mount() cifs: Fix potential softlockups while refreshing DFS cache cifs: Fix lookup of root ses in DFS referral cache cifs: Fix use-after-free bug in cifs_reconnect() cifs: dump channel info in DebugData smb3: dump in_send and num_waiters stats counters by default cifs: try harder to open new channels CIFS: Properly process SMB3 lease breaks cifs: move cifsFileInfo_put logic into a work-queue cifs: try opening channels after mounting CIFS: refactor cifs_get_inode_info() cifs: switch servers depending on binding state cifs: add server param cifs: add multichannel mount options and data structs cifs: sort interface list by speed CIFS: Fix SMB2 oplock break processing cifs: don't use 'pre:' for MODULE_SOFTDEP ...
This commit is contained in:
commit
21b26d2679
@ -121,6 +121,27 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
|
||||
seq_putc(m, '\n');
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
|
||||
{
|
||||
struct TCP_Server_Info *server = chan->server;
|
||||
|
||||
seq_printf(m, "\t\tChannel %d Number of credits: %d Dialect 0x%x "
|
||||
"TCP status: %d Instance: %d Local Users To Server: %d "
|
||||
"SecMode: 0x%x Req On Wire: %d In Send: %d "
|
||||
"In MaxReq Wait: %d\n",
|
||||
i+1,
|
||||
server->credits,
|
||||
server->dialect,
|
||||
server->tcpStatus,
|
||||
server->reconnect_instance,
|
||||
server->srv_count,
|
||||
server->sec_mode,
|
||||
in_flight(server),
|
||||
atomic_read(&server->in_send),
|
||||
atomic_read(&server->num_waiters));
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
|
||||
{
|
||||
@ -256,6 +277,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||
if (!server->rdma)
|
||||
goto skip_rdma;
|
||||
|
||||
if (!server->smbd_conn) {
|
||||
seq_printf(m, "\nSMBDirect transport not available");
|
||||
goto skip_rdma;
|
||||
}
|
||||
|
||||
seq_printf(m, "\nSMBDirect (in hex) protocol version: %x "
|
||||
"transport status: %x",
|
||||
server->smbd_conn->protocol,
|
||||
@ -360,11 +386,10 @@ skip_rdma:
|
||||
server->srv_count,
|
||||
server->sec_mode, in_flight(server));
|
||||
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
seq_printf(m, " In Send: %d In MaxReq Wait: %d",
|
||||
atomic_read(&server->in_send),
|
||||
atomic_read(&server->num_waiters));
|
||||
#endif
|
||||
|
||||
/* dump session id helpful for use with network trace */
|
||||
seq_printf(m, " SessionId: 0x%llx", ses->Suid);
|
||||
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
||||
@ -372,6 +397,13 @@ skip_rdma:
|
||||
if (ses->sign)
|
||||
seq_puts(m, " signed");
|
||||
|
||||
if (ses->chan_count > 1) {
|
||||
seq_printf(m, "\n\n\tExtra Channels: %zu\n",
|
||||
ses->chan_count-1);
|
||||
for (j = 1; j < ses->chan_count; j++)
|
||||
cifs_dump_channel(m, j, &ses->chans[j]);
|
||||
}
|
||||
|
||||
seq_puts(m, "\n\tShares:");
|
||||
j = 0;
|
||||
|
||||
@ -410,8 +442,13 @@ skip_rdma:
|
||||
seq_printf(m, "\n\tServer interfaces: %zu\n",
|
||||
ses->iface_count);
|
||||
for (j = 0; j < ses->iface_count; j++) {
|
||||
struct cifs_server_iface *iface;
|
||||
|
||||
iface = &ses->iface_list[j];
|
||||
seq_printf(m, "\t%d)", j);
|
||||
cifs_dump_iface(m, &ses->iface_list[j]);
|
||||
cifs_dump_iface(m, iface);
|
||||
if (is_ses_using_iface(ses, iface))
|
||||
seq_puts(m, "\t\t[CONNECTED]\n");
|
||||
}
|
||||
spin_unlock(&ses->iface_lock);
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ struct key_type cifs_spnego_key_type = {
|
||||
struct key *
|
||||
cifs_get_spnego_key(struct cifs_ses *sesInfo)
|
||||
{
|
||||
struct TCP_Server_Info *server = sesInfo->server;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(sesInfo);
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
|
||||
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
|
||||
char *description, *dp;
|
||||
|
@ -39,8 +39,6 @@ static const struct cifs_sid sid_everyone = {
|
||||
/* security id for Authenticated Users system group */
|
||||
static const struct cifs_sid sid_authusers = {
|
||||
1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} };
|
||||
/* group users */
|
||||
static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
|
||||
|
||||
/* S-1-22-1 Unmapped Unix users */
|
||||
static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22},
|
||||
|
@ -119,6 +119,7 @@ extern mempool_t *cifs_mid_poolp;
|
||||
|
||||
struct workqueue_struct *cifsiod_wq;
|
||||
struct workqueue_struct *decrypt_wq;
|
||||
struct workqueue_struct *fileinfo_put_wq;
|
||||
struct workqueue_struct *cifsoplockd_wq;
|
||||
__u32 cifs_lock_secret;
|
||||
|
||||
@ -613,6 +614,10 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
/* convert actimeo and display it in seconds */
|
||||
seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
|
||||
|
||||
if (tcon->ses->chan_max > 1)
|
||||
seq_printf(s, ",multichannel,max_channel=%zu",
|
||||
tcon->ses->chan_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1219,6 +1224,7 @@ const struct file_operations cifs_file_ops = {
|
||||
.open = cifs_open,
|
||||
.release = cifs_close,
|
||||
.lock = cifs_lock,
|
||||
.flock = cifs_flock,
|
||||
.fsync = cifs_fsync,
|
||||
.flush = cifs_flush,
|
||||
.mmap = cifs_file_mmap,
|
||||
@ -1238,6 +1244,7 @@ const struct file_operations cifs_file_strict_ops = {
|
||||
.open = cifs_open,
|
||||
.release = cifs_close,
|
||||
.lock = cifs_lock,
|
||||
.flock = cifs_flock,
|
||||
.fsync = cifs_strict_fsync,
|
||||
.flush = cifs_flush,
|
||||
.mmap = cifs_file_strict_mmap,
|
||||
@ -1257,6 +1264,7 @@ const struct file_operations cifs_file_direct_ops = {
|
||||
.open = cifs_open,
|
||||
.release = cifs_close,
|
||||
.lock = cifs_lock,
|
||||
.flock = cifs_flock,
|
||||
.fsync = cifs_fsync,
|
||||
.flush = cifs_flush,
|
||||
.mmap = cifs_file_mmap,
|
||||
@ -1554,11 +1562,18 @@ init_cifs(void)
|
||||
goto out_destroy_cifsiod_wq;
|
||||
}
|
||||
|
||||
fileinfo_put_wq = alloc_workqueue("cifsfileinfoput",
|
||||
WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
|
||||
if (!fileinfo_put_wq) {
|
||||
rc = -ENOMEM;
|
||||
goto out_destroy_decrypt_wq;
|
||||
}
|
||||
|
||||
cifsoplockd_wq = alloc_workqueue("cifsoplockd",
|
||||
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
|
||||
if (!cifsoplockd_wq) {
|
||||
rc = -ENOMEM;
|
||||
goto out_destroy_decrypt_wq;
|
||||
goto out_destroy_fileinfo_put_wq;
|
||||
}
|
||||
|
||||
rc = cifs_fscache_register();
|
||||
@ -1624,6 +1639,8 @@ out_unreg_fscache:
|
||||
cifs_fscache_unregister();
|
||||
out_destroy_cifsoplockd_wq:
|
||||
destroy_workqueue(cifsoplockd_wq);
|
||||
out_destroy_fileinfo_put_wq:
|
||||
destroy_workqueue(fileinfo_put_wq);
|
||||
out_destroy_decrypt_wq:
|
||||
destroy_workqueue(decrypt_wq);
|
||||
out_destroy_cifsiod_wq:
|
||||
@ -1653,6 +1670,7 @@ exit_cifs(void)
|
||||
cifs_fscache_unregister();
|
||||
destroy_workqueue(cifsoplockd_wq);
|
||||
destroy_workqueue(decrypt_wq);
|
||||
destroy_workqueue(fileinfo_put_wq);
|
||||
destroy_workqueue(cifsiod_wq);
|
||||
cifs_proc_clean();
|
||||
}
|
||||
@ -1663,17 +1681,17 @@ MODULE_DESCRIPTION
|
||||
("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and "
|
||||
"also older servers complying with the SNIA CIFS Specification)");
|
||||
MODULE_VERSION(CIFS_VERSION);
|
||||
MODULE_SOFTDEP("pre: ecb");
|
||||
MODULE_SOFTDEP("pre: hmac");
|
||||
MODULE_SOFTDEP("pre: md4");
|
||||
MODULE_SOFTDEP("pre: md5");
|
||||
MODULE_SOFTDEP("pre: nls");
|
||||
MODULE_SOFTDEP("pre: aes");
|
||||
MODULE_SOFTDEP("pre: cmac");
|
||||
MODULE_SOFTDEP("pre: sha256");
|
||||
MODULE_SOFTDEP("pre: sha512");
|
||||
MODULE_SOFTDEP("pre: aead2");
|
||||
MODULE_SOFTDEP("pre: ccm");
|
||||
MODULE_SOFTDEP("pre: gcm");
|
||||
MODULE_SOFTDEP("ecb");
|
||||
MODULE_SOFTDEP("hmac");
|
||||
MODULE_SOFTDEP("md4");
|
||||
MODULE_SOFTDEP("md5");
|
||||
MODULE_SOFTDEP("nls");
|
||||
MODULE_SOFTDEP("aes");
|
||||
MODULE_SOFTDEP("cmac");
|
||||
MODULE_SOFTDEP("sha256");
|
||||
MODULE_SOFTDEP("sha512");
|
||||
MODULE_SOFTDEP("aead2");
|
||||
MODULE_SOFTDEP("ccm");
|
||||
MODULE_SOFTDEP("gcm");
|
||||
module_init(init_cifs)
|
||||
module_exit(exit_cifs)
|
||||
|
@ -108,6 +108,7 @@ extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to);
|
||||
extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from);
|
||||
extern ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from);
|
||||
extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from);
|
||||
extern int cifs_flock(struct file *pfile, int cmd, struct file_lock *plock);
|
||||
extern int cifs_lock(struct file *, int, struct file_lock *);
|
||||
extern int cifs_fsync(struct file *, loff_t, loff_t, int);
|
||||
extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int);
|
||||
@ -152,5 +153,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
|
||||
extern const struct export_operations cifs_export_ops;
|
||||
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
||||
|
||||
#define CIFS_VERSION "2.23"
|
||||
#define CIFS_VERSION "2.24"
|
||||
#endif /* _CIFSFS_H */
|
||||
|
@ -230,7 +230,8 @@ struct smb_version_operations {
|
||||
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
|
||||
/* setup request: allocate mid, sign message */
|
||||
struct mid_q_entry *(*setup_request)(struct cifs_ses *,
|
||||
struct smb_rqst *);
|
||||
struct TCP_Server_Info *,
|
||||
struct smb_rqst *);
|
||||
/* setup async request: allocate mid, sign message */
|
||||
struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *,
|
||||
struct smb_rqst *);
|
||||
@ -268,8 +269,9 @@ struct smb_version_operations {
|
||||
int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
|
||||
bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
|
||||
int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *);
|
||||
void (*downgrade_oplock)(struct TCP_Server_Info *,
|
||||
struct cifsInodeInfo *, bool);
|
||||
void (*downgrade_oplock)(struct TCP_Server_Info *server,
|
||||
struct cifsInodeInfo *cinode, __u32 oplock,
|
||||
unsigned int epoch, bool *purge_cache);
|
||||
/* process transaction2 response */
|
||||
bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *,
|
||||
char *, int);
|
||||
@ -591,6 +593,10 @@ struct smb_vol {
|
||||
bool resilient:1; /* noresilient not required since not fored for CA */
|
||||
bool domainauto:1;
|
||||
bool rdma:1;
|
||||
bool multichannel:1;
|
||||
bool use_client_guid:1;
|
||||
/* reuse existing guid for multichannel */
|
||||
u8 client_guid[SMB2_CLIENT_GUID_SIZE];
|
||||
unsigned int bsize;
|
||||
unsigned int rsize;
|
||||
unsigned int wsize;
|
||||
@ -607,6 +613,7 @@ struct smb_vol {
|
||||
__u64 snapshot_time; /* needed for timewarp tokens */
|
||||
__u32 handle_timeout; /* persistent and durable handle timeout in ms */
|
||||
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
|
||||
unsigned int max_channels;
|
||||
__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
|
||||
bool rootfs:1; /* if it's a SMB root file system */
|
||||
};
|
||||
@ -736,12 +743,12 @@ struct TCP_Server_Info {
|
||||
/* Total size of this PDU. Only valid from cifs_demultiplex_thread */
|
||||
unsigned int pdu_size;
|
||||
unsigned int total_read; /* total amount of data read in this pass */
|
||||
atomic_t in_send; /* requests trying to send */
|
||||
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
|
||||
#ifdef CONFIG_CIFS_FSCACHE
|
||||
struct fscache_cookie *fscache; /* client index cache cookie */
|
||||
#endif
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
atomic_t in_send; /* requests trying to send */
|
||||
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
|
||||
atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */
|
||||
atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */
|
||||
__u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */
|
||||
@ -953,6 +960,11 @@ struct cifs_server_iface {
|
||||
struct sockaddr_storage sockaddr;
|
||||
};
|
||||
|
||||
struct cifs_chan {
|
||||
struct TCP_Server_Info *server;
|
||||
__u8 signkey[SMB3_SIGN_KEY_SIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* Session structure. One of these for each uid session with a particular host
|
||||
*/
|
||||
@ -983,12 +995,15 @@ struct cifs_ses {
|
||||
bool sign; /* is signing required? */
|
||||
bool need_reconnect:1; /* connection reset, uid now invalid */
|
||||
bool domainAuto:1;
|
||||
bool binding:1; /* are we binding the session? */
|
||||
__u16 session_flags;
|
||||
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
|
||||
|
||||
__u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
|
||||
|
||||
/*
|
||||
* Network interfaces available on the server this session is
|
||||
* connected to.
|
||||
@ -1002,8 +1017,37 @@ struct cifs_ses {
|
||||
struct cifs_server_iface *iface_list;
|
||||
size_t iface_count;
|
||||
unsigned long iface_last_update; /* jiffies */
|
||||
|
||||
#define CIFS_MAX_CHANNELS 16
|
||||
struct cifs_chan chans[CIFS_MAX_CHANNELS];
|
||||
size_t chan_count;
|
||||
size_t chan_max;
|
||||
atomic_t chan_seq; /* round robin state */
|
||||
};
|
||||
|
||||
/*
|
||||
* When binding a new channel, we need to access the channel which isn't fully
|
||||
* established yet (one past the established count)
|
||||
*/
|
||||
|
||||
static inline
|
||||
struct cifs_chan *cifs_ses_binding_channel(struct cifs_ses *ses)
|
||||
{
|
||||
if (ses->binding)
|
||||
return &ses->chans[ses->chan_count];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct TCP_Server_Info *cifs_ses_server(struct cifs_ses *ses)
|
||||
{
|
||||
if (ses->binding)
|
||||
return ses->chans[ses->chan_count].server;
|
||||
else
|
||||
return ses->server;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
cap_unix(struct cifs_ses *ses)
|
||||
{
|
||||
@ -1260,11 +1304,14 @@ struct cifsFileInfo {
|
||||
unsigned int f_flags;
|
||||
bool invalidHandle:1; /* file closed via session abend */
|
||||
bool oplock_break_cancelled:1;
|
||||
unsigned int oplock_epoch; /* epoch from the lease break */
|
||||
__u32 oplock_level; /* oplock/lease level from the lease break */
|
||||
int count;
|
||||
spinlock_t file_info_lock; /* protects four flag/count fields above */
|
||||
struct mutex fh_mutex; /* prevents reopen race after dead ses*/
|
||||
struct cifs_search_info srch_inf;
|
||||
struct work_struct oplock_break; /* work for oplock breaks */
|
||||
struct work_struct put; /* work for the final part of _put */
|
||||
};
|
||||
|
||||
struct cifs_io_parms {
|
||||
@ -1370,7 +1417,8 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
|
||||
}
|
||||
|
||||
struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
|
||||
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr);
|
||||
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr,
|
||||
bool offload);
|
||||
void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
|
||||
|
||||
#define CIFS_CACHE_READ_FLG 1
|
||||
@ -1405,7 +1453,7 @@ struct cifsInodeInfo {
|
||||
unsigned int epoch; /* used to track lease state changes */
|
||||
#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */
|
||||
#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */
|
||||
#define CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2 (2) /* Downgrade oplock to L2 */
|
||||
#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */
|
||||
#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
|
||||
#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
|
||||
#define CIFS_INO_LOCK (5) /* lock bit for synchronization */
|
||||
@ -1524,6 +1572,7 @@ struct mid_q_entry {
|
||||
struct TCP_Server_Info *server; /* server corresponding to this mid */
|
||||
__u64 mid; /* multiplex id */
|
||||
__u16 credits; /* number of credits consumed by this mid */
|
||||
__u16 credits_received; /* number of credits from the response */
|
||||
__u32 pid; /* process id */
|
||||
__u32 sequence_number; /* for CIFS signing */
|
||||
unsigned long when_alloc; /* when mid was created */
|
||||
@ -1551,12 +1600,12 @@ struct close_cancelled_open {
|
||||
struct cifs_fid fid;
|
||||
struct cifs_tcon *tcon;
|
||||
struct work_struct work;
|
||||
__u64 mid;
|
||||
__u16 cmd;
|
||||
};
|
||||
|
||||
/* Make code in transport.c a little cleaner by moving
|
||||
update of optional stats into function below */
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
|
||||
static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
|
||||
{
|
||||
atomic_inc(&server->in_send);
|
||||
@ -1577,26 +1626,12 @@ static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
|
||||
atomic_dec(&server->num_waiters);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
static inline void cifs_save_when_sent(struct mid_q_entry *mid)
|
||||
{
|
||||
mid->when_sent = jiffies;
|
||||
}
|
||||
#else
|
||||
static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
|
||||
{
|
||||
}
|
||||
static inline void cifs_in_send_dec(struct TCP_Server_Info *server)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cifs_save_when_sent(struct mid_q_entry *mid)
|
||||
{
|
||||
}
|
||||
@ -1907,6 +1942,7 @@ void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
|
||||
extern const struct slow_work_ops cifs_oplock_break_ops;
|
||||
extern struct workqueue_struct *cifsiod_wq;
|
||||
extern struct workqueue_struct *decrypt_wq;
|
||||
extern struct workqueue_struct *fileinfo_put_wq;
|
||||
extern struct workqueue_struct *cifsoplockd_wq;
|
||||
extern __u32 cifs_lock_secret;
|
||||
|
||||
@ -1937,4 +1973,10 @@ extern struct smb_version_values smb302_values;
|
||||
#define ALT_SMB311_VERSION_STRING "3.11"
|
||||
extern struct smb_version_operations smb311_operations;
|
||||
extern struct smb_version_values smb311_values;
|
||||
|
||||
static inline bool is_smb1_server(struct TCP_Server_Info *server)
|
||||
{
|
||||
return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0;
|
||||
}
|
||||
|
||||
#endif /* _CIFS_GLOB_H */
|
||||
|
@ -109,6 +109,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
|
||||
extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
|
||||
char *in_buf, int flags);
|
||||
extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
|
||||
struct TCP_Server_Info *,
|
||||
struct smb_rqst *);
|
||||
extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
|
||||
struct smb_rqst *);
|
||||
@ -242,6 +243,7 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
|
||||
struct tcon_link *tlink,
|
||||
struct cifs_pending_open *open);
|
||||
extern void cifs_del_pending_open(struct cifs_pending_open *open);
|
||||
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb_vol *vol);
|
||||
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
|
||||
int from_reconnect);
|
||||
extern void cifs_put_tcon(struct cifs_tcon *tcon);
|
||||
@ -584,6 +586,12 @@ void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
|
||||
|
||||
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
|
||||
unsigned int *len, unsigned int *offset);
|
||||
int cifs_try_adding_channels(struct cifs_ses *ses);
|
||||
int cifs_ses_add_channel(struct cifs_ses *ses,
|
||||
struct cifs_server_iface *iface);
|
||||
bool is_server_using_iface(struct TCP_Server_Info *server,
|
||||
struct cifs_server_iface *iface);
|
||||
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
|
||||
|
||||
void extract_unc_hostname(const char *unc, const char **h, size_t *len);
|
||||
int copy_path_name(char *dst, const char *src);
|
||||
|
@ -97,6 +97,7 @@ enum {
|
||||
Opt_persistent, Opt_nopersistent,
|
||||
Opt_resilient, Opt_noresilient,
|
||||
Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs,
|
||||
Opt_multichannel, Opt_nomultichannel,
|
||||
Opt_compress,
|
||||
|
||||
/* Mount options which take numeric value */
|
||||
@ -106,7 +107,7 @@ enum {
|
||||
Opt_min_enc_offload,
|
||||
Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo,
|
||||
Opt_echo_interval, Opt_max_credits, Opt_handletimeout,
|
||||
Opt_snapshot,
|
||||
Opt_snapshot, Opt_max_channels,
|
||||
|
||||
/* Mount options which take string value */
|
||||
Opt_user, Opt_pass, Opt_ip,
|
||||
@ -199,6 +200,8 @@ static const match_table_t cifs_mount_option_tokens = {
|
||||
{ Opt_noresilient, "noresilienthandles"},
|
||||
{ Opt_domainauto, "domainauto"},
|
||||
{ Opt_rdma, "rdma"},
|
||||
{ Opt_multichannel, "multichannel" },
|
||||
{ Opt_nomultichannel, "nomultichannel" },
|
||||
|
||||
{ Opt_backupuid, "backupuid=%s" },
|
||||
{ Opt_backupgid, "backupgid=%s" },
|
||||
@ -218,6 +221,7 @@ static const match_table_t cifs_mount_option_tokens = {
|
||||
{ Opt_echo_interval, "echo_interval=%s" },
|
||||
{ Opt_max_credits, "max_credits=%s" },
|
||||
{ Opt_snapshot, "snapshot=%s" },
|
||||
{ Opt_max_channels, "max_channels=%s" },
|
||||
{ Opt_compress, "compress=%s" },
|
||||
|
||||
{ Opt_blank_user, "user=" },
|
||||
@ -387,7 +391,7 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
struct super_cb_data {
|
||||
struct TCP_Server_Info *server;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct super_block *sb;
|
||||
};
|
||||
|
||||
/* These functions must be called with server->srv_mutex held */
|
||||
@ -398,25 +402,39 @@ static void super_cb(struct super_block *sb, void *arg)
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_tcon *tcon;
|
||||
|
||||
if (d->cifs_sb)
|
||||
if (d->sb)
|
||||
return;
|
||||
|
||||
cifs_sb = CIFS_SB(sb);
|
||||
tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
if (tcon->ses->server == d->server)
|
||||
d->cifs_sb = cifs_sb;
|
||||
d->sb = sb;
|
||||
}
|
||||
|
||||
static inline struct cifs_sb_info *
|
||||
find_super_by_tcp(struct TCP_Server_Info *server)
|
||||
static struct super_block *get_tcp_super(struct TCP_Server_Info *server)
|
||||
{
|
||||
struct super_cb_data d = {
|
||||
.server = server,
|
||||
.cifs_sb = NULL,
|
||||
.sb = NULL,
|
||||
};
|
||||
|
||||
iterate_supers_type(&cifs_fs_type, super_cb, &d);
|
||||
return d.cifs_sb ? d.cifs_sb : ERR_PTR(-ENOENT);
|
||||
|
||||
if (unlikely(!d.sb))
|
||||
return ERR_PTR(-ENOENT);
|
||||
/*
|
||||
* Grab an active reference in order to prevent automounts (DFS links)
|
||||
* of expiring and then freeing up our cifs superblock pointer while
|
||||
* we're doing failover.
|
||||
*/
|
||||
cifs_sb_active(d.sb);
|
||||
return d.sb;
|
||||
}
|
||||
|
||||
static inline void put_tcp_super(struct super_block *sb)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(sb))
|
||||
cifs_sb_deactive(sb);
|
||||
}
|
||||
|
||||
static void reconn_inval_dfs_target(struct TCP_Server_Info *server,
|
||||
@ -480,6 +498,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
struct mid_q_entry *mid_entry;
|
||||
struct list_head retry_list;
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
struct super_block *sb = NULL;
|
||||
struct cifs_sb_info *cifs_sb = NULL;
|
||||
struct dfs_cache_tgt_list tgt_list = {0};
|
||||
struct dfs_cache_tgt_iterator *tgt_it = NULL;
|
||||
@ -489,13 +508,15 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
server->nr_targets = 1;
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
cifs_sb = find_super_by_tcp(server);
|
||||
if (IS_ERR(cifs_sb)) {
|
||||
rc = PTR_ERR(cifs_sb);
|
||||
sb = get_tcp_super(server);
|
||||
if (IS_ERR(sb)) {
|
||||
rc = PTR_ERR(sb);
|
||||
cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
|
||||
__func__, rc);
|
||||
cifs_sb = NULL;
|
||||
sb = NULL;
|
||||
} else {
|
||||
cifs_sb = CIFS_SB(sb);
|
||||
|
||||
rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list, &tgt_it);
|
||||
if (rc && (rc != -EOPNOTSUPP)) {
|
||||
cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n",
|
||||
@ -512,6 +533,10 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
/* the demux thread will exit normally
|
||||
next time through the loop */
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
dfs_cache_free_tgts(&tgt_list);
|
||||
put_tcp_super(sb);
|
||||
#endif
|
||||
return rc;
|
||||
} else
|
||||
server->tcpStatus = CifsNeedReconnect;
|
||||
@ -638,7 +663,10 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
__func__, rc);
|
||||
}
|
||||
dfs_cache_free_tgts(&tgt_list);
|
||||
|
||||
}
|
||||
|
||||
put_tcp_super(sb);
|
||||
#endif
|
||||
if (server->tcpStatus == CifsNeedNegotiate)
|
||||
mod_delayed_work(cifsiod_wq, &server->echo, 0);
|
||||
@ -905,6 +933,20 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
|
||||
{
|
||||
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
|
||||
|
||||
/*
|
||||
* SMB1 does not use credits.
|
||||
*/
|
||||
if (server->vals->header_preamble_size)
|
||||
return 0;
|
||||
|
||||
return le16_to_cpu(shdr->CreditRequest);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||
char *buf, int malformed)
|
||||
@ -912,6 +954,7 @@ handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||
if (server->ops->check_trans2 &&
|
||||
server->ops->check_trans2(mid, server, buf, malformed))
|
||||
return;
|
||||
mid->credits_received = smb2_get_credits_from_hdr(buf, server);
|
||||
mid->resp_buf = buf;
|
||||
mid->large_buf = server->large_buf;
|
||||
/* Was previous buf put in mpx struct for multi-rsp? */
|
||||
@ -1222,12 +1265,6 @@ next_pdu:
|
||||
for (i = 0; i < num_mids; i++) {
|
||||
if (mids[i] != NULL) {
|
||||
mids[i]->resp_buf_size = server->pdu_size;
|
||||
if ((mids[i]->mid_flags & MID_WAIT_CANCELLED) &&
|
||||
mids[i]->mid_state == MID_RESPONSE_RECEIVED &&
|
||||
server->ops->handle_cancelled_mid)
|
||||
server->ops->handle_cancelled_mid(
|
||||
mids[i]->resp_buf,
|
||||
server);
|
||||
|
||||
if (!mids[i]->multiRsp || mids[i]->multiEnd)
|
||||
mids[i]->callback(mids[i]);
|
||||
@ -1672,6 +1709,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
|
||||
vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT;
|
||||
|
||||
/* default to no multichannel (single server connection) */
|
||||
vol->multichannel = false;
|
||||
vol->max_channels = 1;
|
||||
|
||||
if (!mountdata)
|
||||
goto cifs_parse_mount_err;
|
||||
|
||||
@ -1965,6 +2006,12 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
case Opt_rdma:
|
||||
vol->rdma = true;
|
||||
break;
|
||||
case Opt_multichannel:
|
||||
vol->multichannel = true;
|
||||
break;
|
||||
case Opt_nomultichannel:
|
||||
vol->multichannel = false;
|
||||
break;
|
||||
case Opt_compress:
|
||||
vol->compression = UNKNOWN_TYPE;
|
||||
cifs_dbg(VFS,
|
||||
@ -2128,6 +2175,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||
}
|
||||
vol->max_credits = option;
|
||||
break;
|
||||
case Opt_max_channels:
|
||||
if (get_option_ul(args, &option) || option < 1 ||
|
||||
option > CIFS_MAX_CHANNELS) {
|
||||
cifs_dbg(VFS, "%s: Invalid max_channels value, needs to be 1-%d\n",
|
||||
__func__, CIFS_MAX_CHANNELS);
|
||||
goto cifs_parse_mount_err;
|
||||
}
|
||||
vol->max_channels = option;
|
||||
break;
|
||||
|
||||
/* String Arguments */
|
||||
|
||||
@ -2713,7 +2769,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
|
||||
send_sig(SIGKILL, task, 1);
|
||||
}
|
||||
|
||||
static struct TCP_Server_Info *
|
||||
struct TCP_Server_Info *
|
||||
cifs_get_tcp_session(struct smb_vol *volume_info)
|
||||
{
|
||||
struct TCP_Server_Info *tcp_ses = NULL;
|
||||
@ -2772,7 +2828,11 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
|
||||
sizeof(tcp_ses->srcaddr));
|
||||
memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
|
||||
sizeof(tcp_ses->dstaddr));
|
||||
generate_random_uuid(tcp_ses->client_guid);
|
||||
if (volume_info->use_client_guid)
|
||||
memcpy(tcp_ses->client_guid, volume_info->client_guid,
|
||||
SMB2_CLIENT_GUID_SIZE);
|
||||
else
|
||||
generate_random_uuid(tcp_ses->client_guid);
|
||||
/*
|
||||
* at this point we are the only ones with the pointer
|
||||
* to the struct since the kernel thread not created yet
|
||||
@ -2861,6 +2921,13 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol)
|
||||
vol->sectype != ses->sectype)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If an existing session is limited to less channels than
|
||||
* requested, it should not be reused
|
||||
*/
|
||||
if (ses->chan_max < vol->max_channels)
|
||||
return 0;
|
||||
|
||||
switch (ses->sectype) {
|
||||
case Kerberos:
|
||||
if (!uid_eq(vol->cred_uid, ses->cred_uid))
|
||||
@ -3031,6 +3098,14 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
|
||||
list_del_init(&ses->smb_ses_list);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/* close any extra channels */
|
||||
if (ses->chan_count > 1) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i < ses->chan_count; i++)
|
||||
cifs_put_tcp_session(ses->chans[i].server, 0);
|
||||
}
|
||||
|
||||
sesInfoFree(ses);
|
||||
cifs_put_tcp_session(server, 0);
|
||||
}
|
||||
@ -3277,14 +3352,25 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
|
||||
ses->sectype = volume_info->sectype;
|
||||
ses->sign = volume_info->sign;
|
||||
mutex_lock(&ses->session_mutex);
|
||||
|
||||
/* add server as first channel */
|
||||
ses->chans[0].server = server;
|
||||
ses->chan_count = 1;
|
||||
ses->chan_max = volume_info->multichannel ? volume_info->max_channels:1;
|
||||
|
||||
rc = cifs_negotiate_protocol(xid, ses);
|
||||
if (!rc)
|
||||
rc = cifs_setup_session(xid, ses, volume_info->local_nls);
|
||||
|
||||
/* each channel uses a different signing key */
|
||||
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
|
||||
sizeof(ses->smb3signingkey));
|
||||
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
if (rc)
|
||||
goto get_ses_fail;
|
||||
|
||||
/* success, put it on the list */
|
||||
/* success, put it on the list and add it as first channel */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_add(&ses->smb_ses_list, &server->smb_ses_list);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
@ -4700,6 +4786,17 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
static inline void set_root_tcon(struct cifs_sb_info *cifs_sb,
|
||||
struct cifs_tcon *tcon,
|
||||
struct cifs_tcon **root)
|
||||
{
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
tcon->tc_count++;
|
||||
tcon->remap = cifs_remap(cifs_sb);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
*root = tcon;
|
||||
}
|
||||
|
||||
int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
|
||||
{
|
||||
int rc = 0;
|
||||
@ -4801,18 +4898,10 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
|
||||
/* Cache out resolved root server */
|
||||
(void)dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
|
||||
root_path + 1, NULL, NULL);
|
||||
/*
|
||||
* Save root tcon for additional DFS requests to update or create a new
|
||||
* DFS cache entry, or even perform DFS failover.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
tcon->tc_count++;
|
||||
tcon->dfs_path = root_path;
|
||||
kfree(root_path);
|
||||
root_path = NULL;
|
||||
tcon->remap = cifs_remap(cifs_sb);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
root_tcon = tcon;
|
||||
set_root_tcon(cifs_sb, tcon, &root_tcon);
|
||||
|
||||
for (count = 1; ;) {
|
||||
if (!rc && tcon) {
|
||||
@ -4849,6 +4938,15 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
|
||||
mount_put_conns(cifs_sb, xid, server, ses, tcon);
|
||||
rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses,
|
||||
&tcon);
|
||||
/*
|
||||
* Ensure that DFS referrals go through new root server.
|
||||
*/
|
||||
if (!rc && tcon &&
|
||||
(tcon->share_flags & (SHI1005_FLAGS_DFS |
|
||||
SHI1005_FLAGS_DFS_ROOT))) {
|
||||
cifs_put_tcon(root_tcon);
|
||||
set_root_tcon(cifs_sb, tcon, &root_tcon);
|
||||
}
|
||||
}
|
||||
if (rc) {
|
||||
if (rc == -EACCES || rc == -EOPNOTSUPP)
|
||||
@ -4897,6 +4995,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
|
||||
cifs_autodisable_serverino(cifs_sb);
|
||||
out:
|
||||
free_xid(xid);
|
||||
cifs_try_adding_channels(ses);
|
||||
return mount_setup_tlink(cifs_sb, ses, tcon);
|
||||
|
||||
error:
|
||||
@ -5142,7 +5241,7 @@ int
|
||||
cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
|
||||
{
|
||||
int rc = 0;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
|
||||
if (!server->ops->need_neg || !server->ops->negotiate)
|
||||
return -ENOSYS;
|
||||
@ -5169,23 +5268,25 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct nls_table *nls_info)
|
||||
{
|
||||
int rc = -ENOSYS;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
|
||||
ses->capabilities = server->capabilities;
|
||||
if (linuxExtEnabled == 0)
|
||||
ses->capabilities &= (~server->vals->cap_unix);
|
||||
if (!ses->binding) {
|
||||
ses->capabilities = server->capabilities;
|
||||
if (linuxExtEnabled == 0)
|
||||
ses->capabilities &= (~server->vals->cap_unix);
|
||||
|
||||
if (ses->auth_key.response) {
|
||||
cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
|
||||
ses->auth_key.response);
|
||||
kfree(ses->auth_key.response);
|
||||
ses->auth_key.response = NULL;
|
||||
ses->auth_key.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
|
||||
server->sec_mode, server->capabilities, server->timeAdj);
|
||||
|
||||
if (ses->auth_key.response) {
|
||||
cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
|
||||
ses->auth_key.response);
|
||||
kfree(ses->auth_key.response);
|
||||
ses->auth_key.response = NULL;
|
||||
ses->auth_key.len = 0;
|
||||
}
|
||||
|
||||
if (server->ops->sess_setup)
|
||||
rc = server->ops->sess_setup(xid, ses, nls_info);
|
||||
|
||||
|
@ -1317,7 +1317,6 @@ static struct cifs_ses *find_root_ses(struct dfs_cache_vol_info *vi,
|
||||
int rc;
|
||||
struct dfs_info3_param ref = {0};
|
||||
char *mdata = NULL, *devname = NULL;
|
||||
bool is_smb3 = tcon->ses->server->vals->header_preamble_size == 0;
|
||||
struct TCP_Server_Info *server;
|
||||
struct cifs_ses *ses;
|
||||
struct smb_vol vol;
|
||||
@ -1344,7 +1343,7 @@ static struct cifs_ses *find_root_ses(struct dfs_cache_vol_info *vi,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = cifs_setup_volume_info(&vol, mdata, devname, is_smb3);
|
||||
rc = cifs_setup_volume_info(&vol, mdata, devname, false);
|
||||
kfree(devname);
|
||||
|
||||
if (rc) {
|
||||
|
@ -244,10 +244,8 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||
*oplock = REQ_OPLOCK;
|
||||
|
||||
full_path = build_path_from_dentry(direntry);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!full_path)
|
||||
return -ENOMEM;
|
||||
|
||||
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
|
||||
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
||||
|
159
fs/cifs/file.c
159
fs/cifs/file.c
@ -288,6 +288,8 @@ cifs_down_write(struct rw_semaphore *sem)
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
static void cifsFileInfo_put_work(struct work_struct *work);
|
||||
|
||||
struct cifsFileInfo *
|
||||
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
struct tcon_link *tlink, __u32 oplock)
|
||||
@ -325,6 +327,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
cfile->invalidHandle = false;
|
||||
cfile->tlink = cifs_get_tlink(tlink);
|
||||
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
|
||||
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
|
||||
mutex_init(&cfile->fh_mutex);
|
||||
spin_lock_init(&cfile->file_info_lock);
|
||||
|
||||
@ -375,6 +378,41 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)
|
||||
return cifs_file;
|
||||
}
|
||||
|
||||
static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
|
||||
{
|
||||
struct inode *inode = d_inode(cifs_file->dentry);
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||
struct cifsLockInfo *li, *tmp;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
||||
/*
|
||||
* Delete any outstanding lock records. We'll lose them when the file
|
||||
* is closed anyway.
|
||||
*/
|
||||
cifs_down_write(&cifsi->lock_sem);
|
||||
list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
|
||||
list_del(&li->llist);
|
||||
cifs_del_lock_waiters(li);
|
||||
kfree(li);
|
||||
}
|
||||
list_del(&cifs_file->llist->llist);
|
||||
kfree(cifs_file->llist);
|
||||
up_write(&cifsi->lock_sem);
|
||||
|
||||
cifs_put_tlink(cifs_file->tlink);
|
||||
dput(cifs_file->dentry);
|
||||
cifs_sb_deactive(sb);
|
||||
kfree(cifs_file);
|
||||
}
|
||||
|
||||
static void cifsFileInfo_put_work(struct work_struct *work)
|
||||
{
|
||||
struct cifsFileInfo *cifs_file = container_of(work,
|
||||
struct cifsFileInfo, put);
|
||||
|
||||
cifsFileInfo_put_final(cifs_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* cifsFileInfo_put - release a reference of file priv data
|
||||
*
|
||||
@ -382,15 +420,15 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)
|
||||
*/
|
||||
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
||||
{
|
||||
_cifsFileInfo_put(cifs_file, true);
|
||||
_cifsFileInfo_put(cifs_file, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* _cifsFileInfo_put - release a reference of file priv data
|
||||
*
|
||||
* This may involve closing the filehandle @cifs_file out on the
|
||||
* server. Must be called without holding tcon->open_file_lock and
|
||||
* cifs_file->file_info_lock.
|
||||
* server. Must be called without holding tcon->open_file_lock,
|
||||
* cinode->open_file_lock and cifs_file->file_info_lock.
|
||||
*
|
||||
* If @wait_for_oplock_handler is true and we are releasing the last
|
||||
* reference, wait for any running oplock break handler of the file
|
||||
@ -398,7 +436,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
||||
* oplock break handler, you need to pass false.
|
||||
*
|
||||
*/
|
||||
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
|
||||
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
|
||||
bool wait_oplock_handler, bool offload)
|
||||
{
|
||||
struct inode *inode = d_inode(cifs_file->dentry);
|
||||
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
|
||||
@ -406,7 +445,6 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct cifsLockInfo *li, *tmp;
|
||||
struct cifs_fid fid;
|
||||
struct cifs_pending_open open;
|
||||
bool oplock_break_cancelled;
|
||||
@ -467,24 +505,10 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
|
||||
|
||||
cifs_del_pending_open(&open);
|
||||
|
||||
/*
|
||||
* Delete any outstanding lock records. We'll lose them when the file
|
||||
* is closed anyway.
|
||||
*/
|
||||
cifs_down_write(&cifsi->lock_sem);
|
||||
list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
|
||||
list_del(&li->llist);
|
||||
cifs_del_lock_waiters(li);
|
||||
kfree(li);
|
||||
}
|
||||
list_del(&cifs_file->llist->llist);
|
||||
kfree(cifs_file->llist);
|
||||
up_write(&cifsi->lock_sem);
|
||||
|
||||
cifs_put_tlink(cifs_file->tlink);
|
||||
dput(cifs_file->dentry);
|
||||
cifs_sb_deactive(sb);
|
||||
kfree(cifs_file);
|
||||
if (offload)
|
||||
queue_work(fileinfo_put_wq, &cifs_file->put);
|
||||
else
|
||||
cifsFileInfo_put_final(cifs_file);
|
||||
}
|
||||
|
||||
int cifs_open(struct inode *inode, struct file *file)
|
||||
@ -728,6 +752,13 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
||||
if (backup_cred(cifs_sb))
|
||||
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
||||
|
||||
/* O_SYNC also has bit for O_DSYNC so following check picks up either */
|
||||
if (cfile->f_flags & O_SYNC)
|
||||
create_options |= CREATE_WRITE_THROUGH;
|
||||
|
||||
if (cfile->f_flags & O_DIRECT)
|
||||
create_options |= CREATE_NO_BUFFER;
|
||||
|
||||
if (server->ops->get_lease_key)
|
||||
server->ops->get_lease_key(inode, &cfile->fid);
|
||||
|
||||
@ -808,7 +839,7 @@ reopen_error_exit:
|
||||
int cifs_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (file->private_data != NULL) {
|
||||
cifsFileInfo_put(file->private_data);
|
||||
_cifsFileInfo_put(file->private_data, true, false);
|
||||
file->private_data = NULL;
|
||||
}
|
||||
|
||||
@ -1681,7 +1712,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
|
||||
rc = server->ops->mand_unlock_range(cfile, flock, xid);
|
||||
|
||||
out:
|
||||
if (flock->fl_flags & FL_POSIX) {
|
||||
if ((flock->fl_flags & FL_POSIX) || (flock->fl_flags & FL_FLOCK)) {
|
||||
/*
|
||||
* If this is a request to remove all locks because we
|
||||
* are closing the file, it doesn't matter if the
|
||||
@ -1698,6 +1729,52 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cifs_flock(struct file *file, int cmd, struct file_lock *fl)
|
||||
{
|
||||
int rc, xid;
|
||||
int lock = 0, unlock = 0;
|
||||
bool wait_flag = false;
|
||||
bool posix_lck = false;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifsFileInfo *cfile;
|
||||
__u32 type;
|
||||
|
||||
rc = -EACCES;
|
||||
xid = get_xid();
|
||||
|
||||
if (!(fl->fl_flags & FL_FLOCK))
|
||||
return -ENOLCK;
|
||||
|
||||
cfile = (struct cifsFileInfo *)file->private_data;
|
||||
tcon = tlink_tcon(cfile->tlink);
|
||||
|
||||
cifs_read_flock(fl, &type, &lock, &unlock, &wait_flag,
|
||||
tcon->ses->server);
|
||||
cifs_sb = CIFS_FILE_SB(file);
|
||||
|
||||
if (cap_unix(tcon->ses) &&
|
||||
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
|
||||
((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
|
||||
posix_lck = true;
|
||||
|
||||
if (!lock && !unlock) {
|
||||
/*
|
||||
* if no lock or unlock then nothing to do since we do not
|
||||
* know what it is
|
||||
*/
|
||||
free_xid(xid);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
rc = cifs_setlk(file, fl, type, wait_flag, posix_lck, lock, unlock,
|
||||
xid);
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
|
||||
|
||||
}
|
||||
|
||||
int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
|
||||
{
|
||||
int rc, xid;
|
||||
@ -2757,9 +2834,17 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
|
||||
if (!rc) {
|
||||
if (wdata->cfile->invalidHandle)
|
||||
rc = -EAGAIN;
|
||||
else
|
||||
else {
|
||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||
if (wdata->mr) {
|
||||
wdata->mr->need_invalidate = true;
|
||||
smbd_deregister_mr(wdata->mr);
|
||||
wdata->mr = NULL;
|
||||
}
|
||||
#endif
|
||||
rc = server->ops->async_writev(wdata,
|
||||
cifs_uncached_writedata_release);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the write was successfully sent, we are done */
|
||||
@ -3482,8 +3567,16 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
|
||||
if (!rc) {
|
||||
if (rdata->cfile->invalidHandle)
|
||||
rc = -EAGAIN;
|
||||
else
|
||||
else {
|
||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||
if (rdata->mr) {
|
||||
rdata->mr->need_invalidate = true;
|
||||
smbd_deregister_mr(rdata->mr);
|
||||
rdata->mr = NULL;
|
||||
}
|
||||
#endif
|
||||
rc = server->ops->async_readv(rdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the read was successfully sent, we are done */
|
||||
@ -4637,12 +4730,13 @@ void cifs_oplock_break(struct work_struct *work)
|
||||
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
int rc = 0;
|
||||
bool purge_cache = false;
|
||||
|
||||
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
|
||||
server->ops->downgrade_oplock(server, cinode,
|
||||
test_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags));
|
||||
server->ops->downgrade_oplock(server, cinode, cfile->oplock_level,
|
||||
cfile->oplock_epoch, &purge_cache);
|
||||
|
||||
if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) &&
|
||||
cifs_has_mand_locks(cinode)) {
|
||||
@ -4657,18 +4751,21 @@ void cifs_oplock_break(struct work_struct *work)
|
||||
else
|
||||
break_lease(inode, O_WRONLY);
|
||||
rc = filemap_fdatawrite(inode->i_mapping);
|
||||
if (!CIFS_CACHE_READ(cinode)) {
|
||||
if (!CIFS_CACHE_READ(cinode) || purge_cache) {
|
||||
rc = filemap_fdatawait(inode->i_mapping);
|
||||
mapping_set_error(inode->i_mapping, rc);
|
||||
cifs_zap_mapping(inode);
|
||||
}
|
||||
cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc);
|
||||
if (CIFS_CACHE_WRITE(cinode))
|
||||
goto oplock_break_ack;
|
||||
}
|
||||
|
||||
rc = cifs_push_locks(cfile);
|
||||
if (rc)
|
||||
cifs_dbg(VFS, "Push locks rc = %d\n", rc);
|
||||
|
||||
oplock_break_ack:
|
||||
/*
|
||||
* releasing stale oplock after recent reconnect of smb session using
|
||||
* a now incorrect file handle is not a data integrity issue but do
|
||||
@ -4680,7 +4777,7 @@ void cifs_oplock_break(struct work_struct *work)
|
||||
cinode);
|
||||
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
|
||||
}
|
||||
_cifsFileInfo_put(cfile, false /* do not wait for ourself */);
|
||||
_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
|
||||
cifs_done_oplock_break(cinode);
|
||||
}
|
||||
|
||||
|
349
fs/cifs/inode.c
349
fs/cifs/inode.c
@ -727,22 +727,138 @@ static __u64 simple_hashstr(const char *str)
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* cifs_backup_query_path_info - SMB1 fallback code to get ino
|
||||
*
|
||||
* Fallback code to get file metadata when we don't have access to
|
||||
* @full_path (EACCESS) and have backup creds.
|
||||
*
|
||||
* @data will be set to search info result buffer
|
||||
* @resp_buf will be set to cifs resp buf and needs to be freed with
|
||||
* cifs_buf_release() when done with @data.
|
||||
*/
|
||||
static int
|
||||
cifs_backup_query_path_info(int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct super_block *sb,
|
||||
const char *full_path,
|
||||
void **resp_buf,
|
||||
FILE_ALL_INFO **data)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct cifs_search_info info = {0};
|
||||
u16 flags;
|
||||
int rc;
|
||||
|
||||
*resp_buf = NULL;
|
||||
info.endOfSearch = false;
|
||||
if (tcon->unix_ext)
|
||||
info.info_level = SMB_FIND_FILE_UNIX;
|
||||
else if ((tcon->ses->capabilities &
|
||||
tcon->ses->server->vals->cap_nt_find) == 0)
|
||||
info.info_level = SMB_FIND_FILE_INFO_STANDARD;
|
||||
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
|
||||
info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
|
||||
else /* no srvino useful for fallback to some netapp */
|
||||
info.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
|
||||
|
||||
flags = CIFS_SEARCH_CLOSE_ALWAYS |
|
||||
CIFS_SEARCH_CLOSE_AT_END |
|
||||
CIFS_SEARCH_BACKUP_SEARCH;
|
||||
|
||||
rc = CIFSFindFirst(xid, tcon, full_path,
|
||||
cifs_sb, NULL, flags, &info, false);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*resp_buf = (void *)info.ntwrk_buf_start;
|
||||
*data = (FILE_ALL_INFO *)info.srch_entries_start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_set_fattr_ino(int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct super_block *sb,
|
||||
struct inode **inode,
|
||||
const char *full_path,
|
||||
FILE_ALL_INFO *data,
|
||||
struct cifs_fattr *fattr)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
int rc;
|
||||
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
|
||||
if (*inode)
|
||||
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
else
|
||||
fattr->cf_uniqueid = iunique(sb, ROOT_I);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have an inode pass a NULL tcon to ensure we don't
|
||||
* make a round trip to the server. This only works for SMB2+.
|
||||
*/
|
||||
rc = server->ops->get_srv_inum(xid,
|
||||
*inode ? NULL : tcon,
|
||||
cifs_sb, full_path,
|
||||
&fattr->cf_uniqueid,
|
||||
data);
|
||||
if (rc) {
|
||||
/*
|
||||
* If that fails reuse existing ino or generate one
|
||||
* and disable server ones
|
||||
*/
|
||||
if (*inode)
|
||||
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
else {
|
||||
fattr->cf_uniqueid = iunique(sb, ROOT_I);
|
||||
cifs_autodisable_serverino(cifs_sb);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If no errors, check for zero root inode (invalid) */
|
||||
if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) {
|
||||
cifs_dbg(FYI, "Invalid (0) inodenum\n");
|
||||
if (*inode) {
|
||||
/* reuse */
|
||||
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
} else {
|
||||
/* make an ino by hashing the UNC */
|
||||
fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO;
|
||||
fattr->cf_uniqueid = simple_hashstr(tcon->treeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_inode_cache_good(struct inode *ino)
|
||||
{
|
||||
return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0;
|
||||
}
|
||||
|
||||
int
|
||||
cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
FILE_ALL_INFO *data, struct super_block *sb, int xid,
|
||||
cifs_get_inode_info(struct inode **inode,
|
||||
const char *full_path,
|
||||
FILE_ALL_INFO *in_data,
|
||||
struct super_block *sb, int xid,
|
||||
const struct cifs_fid *fid)
|
||||
{
|
||||
__u16 srchflgs;
|
||||
int rc = 0, tmprc = ENOSYS;
|
||||
|
||||
struct cifs_tcon *tcon;
|
||||
struct TCP_Server_Info *server;
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
char *buf = NULL;
|
||||
bool adjust_tz = false;
|
||||
struct cifs_fattr fattr;
|
||||
struct cifs_search_info *srchinf = NULL;
|
||||
struct cifs_fattr fattr = {0};
|
||||
bool symlink = false;
|
||||
FILE_ALL_INFO *data = in_data;
|
||||
FILE_ALL_INFO *tmp_data = NULL;
|
||||
void *smb1_backup_rsp_buf = NULL;
|
||||
int rc = 0;
|
||||
int tmprc = 0;
|
||||
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink))
|
||||
@ -750,142 +866,88 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
tcon = tlink_tcon(tlink);
|
||||
server = tcon->ses->server;
|
||||
|
||||
cifs_dbg(FYI, "Getting info on %s\n", full_path);
|
||||
/*
|
||||
* 1. Fetch file metadata if not provided (data)
|
||||
*/
|
||||
|
||||
if ((data == NULL) && (*inode != NULL)) {
|
||||
if (CIFS_CACHE_READ(CIFS_I(*inode)) &&
|
||||
CIFS_I(*inode)->time != 0) {
|
||||
if (!data) {
|
||||
if (is_inode_cache_good(*inode)) {
|
||||
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* if inode info is not passed, get it from server */
|
||||
if (data == NULL) {
|
||||
if (!server->ops->query_path_info) {
|
||||
rc = -ENOSYS;
|
||||
goto cgii_exit;
|
||||
}
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
tmp_data = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (!tmp_data) {
|
||||
rc = -ENOMEM;
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
data = (FILE_ALL_INFO *)buf;
|
||||
rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path,
|
||||
data, &adjust_tz, &symlink);
|
||||
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
|
||||
full_path, tmp_data,
|
||||
&adjust_tz, &symlink);
|
||||
data = tmp_data;
|
||||
}
|
||||
|
||||
if (!rc) {
|
||||
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz,
|
||||
symlink);
|
||||
} else if (rc == -EREMOTE) {
|
||||
cifs_create_dfs_fattr(&fattr, sb);
|
||||
rc = 0;
|
||||
} else if ((rc == -EACCES) && backup_cred(cifs_sb) &&
|
||||
(strcmp(server->vals->version_string, SMB1_VERSION_STRING)
|
||||
== 0)) {
|
||||
/*
|
||||
* For SMB2 and later the backup intent flag is already
|
||||
* sent if needed on open and there is no path based
|
||||
* FindFirst operation to use to retry with
|
||||
*/
|
||||
|
||||
srchinf = kzalloc(sizeof(struct cifs_search_info),
|
||||
GFP_KERNEL);
|
||||
if (srchinf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto cgii_exit;
|
||||
}
|
||||
|
||||
srchinf->endOfSearch = false;
|
||||
if (tcon->unix_ext)
|
||||
srchinf->info_level = SMB_FIND_FILE_UNIX;
|
||||
else if ((tcon->ses->capabilities &
|
||||
tcon->ses->server->vals->cap_nt_find) == 0)
|
||||
srchinf->info_level = SMB_FIND_FILE_INFO_STANDARD;
|
||||
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
|
||||
srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
|
||||
else /* no srvino useful for fallback to some netapp */
|
||||
srchinf->info_level = SMB_FIND_FILE_DIRECTORY_INFO;
|
||||
|
||||
srchflgs = CIFS_SEARCH_CLOSE_ALWAYS |
|
||||
CIFS_SEARCH_CLOSE_AT_END |
|
||||
CIFS_SEARCH_BACKUP_SEARCH;
|
||||
|
||||
rc = CIFSFindFirst(xid, tcon, full_path,
|
||||
cifs_sb, NULL, srchflgs, srchinf, false);
|
||||
if (!rc) {
|
||||
data = (FILE_ALL_INFO *)srchinf->srch_entries_start;
|
||||
|
||||
cifs_dir_info_to_fattr(&fattr,
|
||||
(FILE_DIRECTORY_INFO *)data, cifs_sb);
|
||||
fattr.cf_uniqueid = le64_to_cpu(
|
||||
((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId);
|
||||
|
||||
cifs_buf_release(srchinf->ntwrk_buf_start);
|
||||
}
|
||||
kfree(srchinf);
|
||||
if (rc)
|
||||
goto cgii_exit;
|
||||
} else
|
||||
goto cgii_exit;
|
||||
|
||||
/*
|
||||
* If an inode wasn't passed in, then get the inode number
|
||||
*
|
||||
* Is an i_ino of zero legal? Can we use that to check if the server
|
||||
* supports returning inode numbers? Are there other sanity checks we
|
||||
* can use to ensure that the server is really filling in that field?
|
||||
* 2. Convert it to internal cifs metadata (fattr)
|
||||
*/
|
||||
if (*inode == NULL) {
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
|
||||
if (server->ops->get_srv_inum)
|
||||
tmprc = server->ops->get_srv_inum(xid,
|
||||
tcon, cifs_sb, full_path,
|
||||
&fattr.cf_uniqueid, data);
|
||||
if (tmprc) {
|
||||
cifs_dbg(FYI, "GetSrvInodeNum rc %d\n",
|
||||
tmprc);
|
||||
fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
||||
cifs_autodisable_serverino(cifs_sb);
|
||||
} else if ((fattr.cf_uniqueid == 0) &&
|
||||
strlen(full_path) == 0) {
|
||||
/* some servers ret bad root ino ie 0 */
|
||||
cifs_dbg(FYI, "Invalid (0) inodenum\n");
|
||||
fattr.cf_flags |=
|
||||
CIFS_FATTR_FAKE_ROOT_INO;
|
||||
fattr.cf_uniqueid =
|
||||
simple_hashstr(tcon->treeName);
|
||||
}
|
||||
} else
|
||||
fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
||||
} else {
|
||||
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
|
||||
&& server->ops->get_srv_inum) {
|
||||
/*
|
||||
* Pass a NULL tcon to ensure we don't make a round
|
||||
* trip to the server. This only works for SMB2+.
|
||||
*/
|
||||
tmprc = server->ops->get_srv_inum(xid,
|
||||
NULL, cifs_sb, full_path,
|
||||
&fattr.cf_uniqueid, data);
|
||||
if (tmprc)
|
||||
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
else if ((fattr.cf_uniqueid == 0) &&
|
||||
strlen(full_path) == 0) {
|
||||
/*
|
||||
* Reuse existing root inode num since
|
||||
* inum zero for root causes ls of . and .. to
|
||||
* not be returned
|
||||
*/
|
||||
cifs_dbg(FYI, "Srv ret 0 inode num for root\n");
|
||||
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
}
|
||||
} else
|
||||
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
|
||||
switch (rc) {
|
||||
case 0:
|
||||
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz, symlink);
|
||||
break;
|
||||
case -EREMOTE:
|
||||
/* DFS link, no metadata available on this server */
|
||||
cifs_create_dfs_fattr(&fattr, sb);
|
||||
rc = 0;
|
||||
break;
|
||||
case -EACCES:
|
||||
/*
|
||||
* perm errors, try again with backup flags if possible
|
||||
*
|
||||
* For SMB2 and later the backup intent flag
|
||||
* is already sent if needed on open and there
|
||||
* is no path based FindFirst operation to use
|
||||
* to retry with
|
||||
*/
|
||||
if (backup_cred(cifs_sb) && is_smb1_server(server)) {
|
||||
/* for easier reading */
|
||||
FILE_DIRECTORY_INFO *fdi;
|
||||
SEARCH_ID_FULL_DIR_INFO *si;
|
||||
|
||||
rc = cifs_backup_query_path_info(xid, tcon, sb,
|
||||
full_path,
|
||||
&smb1_backup_rsp_buf,
|
||||
&data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
fdi = (FILE_DIRECTORY_INFO *)data;
|
||||
si = (SEARCH_ID_FULL_DIR_INFO *)data;
|
||||
|
||||
cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb);
|
||||
fattr.cf_uniqueid = le64_to_cpu(si->UniqueId);
|
||||
/* uniqueid set, skip get inum step */
|
||||
goto handle_mnt_opt;
|
||||
} else {
|
||||
/* nothing we can do, bail out */
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3. Get or update inode number (fattr.cf_uniqueid)
|
||||
*/
|
||||
|
||||
cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, &fattr);
|
||||
|
||||
/*
|
||||
* 4. Tweak fattr based on mount options
|
||||
*/
|
||||
|
||||
handle_mnt_opt:
|
||||
/* query for SFU type info if supported and needed */
|
||||
if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
|
||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
|
||||
@ -900,8 +962,8 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
full_path, fid);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n",
|
||||
__func__, rc);
|
||||
goto cgii_exit;
|
||||
__func__, rc);
|
||||
goto out;
|
||||
}
|
||||
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
|
||||
rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false,
|
||||
@ -909,7 +971,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n",
|
||||
__func__, rc);
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -925,6 +987,10 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
|
||||
}
|
||||
|
||||
/*
|
||||
* 5. Update inode with final fattr data
|
||||
*/
|
||||
|
||||
if (!*inode) {
|
||||
*inode = cifs_iget(sb, &fattr);
|
||||
if (!*inode)
|
||||
@ -937,7 +1003,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) {
|
||||
CIFS_I(*inode)->time = 0; /* force reval */
|
||||
rc = -ESTALE;
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if filetype is different, return error */
|
||||
@ -945,18 +1011,15 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
(fattr.cf_mode & S_IFMT))) {
|
||||
CIFS_I(*inode)->time = 0; /* force reval */
|
||||
rc = -ESTALE;
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cifs_fattr_to_inode(*inode, &fattr);
|
||||
}
|
||||
|
||||
cgii_exit:
|
||||
if ((*inode) && ((*inode)->i_ino == 0))
|
||||
cifs_dbg(FYI, "inode number of zero returned\n");
|
||||
|
||||
kfree(buf);
|
||||
out:
|
||||
cifs_buf_release(smb1_backup_rsp_buf);
|
||||
cifs_put_tlink(tlink);
|
||||
kfree(tmp_data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -488,21 +488,10 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
||||
set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
|
||||
&pCifsInode->flags);
|
||||
|
||||
/*
|
||||
* Set flag if the server downgrades the oplock
|
||||
* to L2 else clear.
|
||||
*/
|
||||
if (pSMB->OplockLevel)
|
||||
set_bit(
|
||||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&pCifsInode->flags);
|
||||
else
|
||||
clear_bit(
|
||||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&pCifsInode->flags);
|
||||
|
||||
cifs_queue_oplock_break(netfile);
|
||||
netfile->oplock_epoch = 0;
|
||||
netfile->oplock_level = pSMB->OplockLevel;
|
||||
netfile->oplock_break_cancelled = false;
|
||||
cifs_queue_oplock_break(netfile);
|
||||
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
230
fs/cifs/sess.c
230
fs/cifs/sess.c
@ -31,6 +31,231 @@
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/slab.h>
|
||||
#include "cifs_spnego.h"
|
||||
#include "smb2proto.h"
|
||||
|
||||
bool
|
||||
is_server_using_iface(struct TCP_Server_Info *server,
|
||||
struct cifs_server_iface *iface)
|
||||
{
|
||||
struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
|
||||
struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
|
||||
struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
|
||||
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
|
||||
|
||||
if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
|
||||
return false;
|
||||
if (server->dstaddr.ss_family == AF_INET) {
|
||||
if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
|
||||
return false;
|
||||
} else if (server->dstaddr.ss_family == AF_INET6) {
|
||||
if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
|
||||
sizeof(i6->sin6_addr)) != 0)
|
||||
return false;
|
||||
} else {
|
||||
/* unknown family.. */
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
if (is_server_using_iface(ses->chans[i].server, iface))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* returns number of channels added */
|
||||
int cifs_try_adding_channels(struct cifs_ses *ses)
|
||||
{
|
||||
int old_chan_count = ses->chan_count;
|
||||
int left = ses->chan_max - ses->chan_count;
|
||||
int i = 0;
|
||||
int rc = 0;
|
||||
int tries = 0;
|
||||
|
||||
if (left <= 0) {
|
||||
cifs_dbg(FYI,
|
||||
"ses already at max_channels (%zu), nothing to open\n",
|
||||
ses->chan_max);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ses->server->dialect < SMB30_PROT_ID) {
|
||||
cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep connecting to same, fastest, iface for all channels as
|
||||
* long as its RSS. Try next fastest one if not RSS or channel
|
||||
* creation fails.
|
||||
*/
|
||||
while (left > 0) {
|
||||
struct cifs_server_iface *iface;
|
||||
|
||||
tries++;
|
||||
if (tries > 3*ses->chan_max) {
|
||||
cifs_dbg(FYI, "too many attempt at opening channels (%d channels left to open)\n",
|
||||
left);
|
||||
break;
|
||||
}
|
||||
|
||||
iface = &ses->iface_list[i];
|
||||
if (is_ses_using_iface(ses, iface) && !iface->rss_capable) {
|
||||
i = (i+1) % ses->iface_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = cifs_ses_add_channel(ses, iface);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "failed to open extra channel on iface#%d rc=%d\n",
|
||||
i, rc);
|
||||
i = (i+1) % ses->iface_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "successfully opened new channel on iface#%d\n",
|
||||
i);
|
||||
left--;
|
||||
}
|
||||
|
||||
return ses->chan_count - old_chan_count;
|
||||
}
|
||||
|
||||
int
|
||||
cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
{
|
||||
struct cifs_chan *chan;
|
||||
struct smb_vol vol = {NULL};
|
||||
static const char unc_fmt[] = "\\%s\\foo";
|
||||
char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
|
||||
int rc;
|
||||
unsigned int xid = get_xid();
|
||||
|
||||
cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ",
|
||||
ses, iface->speed, iface->rdma_capable ? "yes" : "no");
|
||||
if (iface->sockaddr.ss_family == AF_INET)
|
||||
cifs_dbg(FYI, "ip:%pI4)\n", &ipv4->sin_addr);
|
||||
else
|
||||
cifs_dbg(FYI, "ip:%pI6)\n", &ipv6->sin6_addr);
|
||||
|
||||
/*
|
||||
* Setup a smb_vol with mostly the same info as the existing
|
||||
* session and overwrite it with the requested iface data.
|
||||
*
|
||||
* We need to setup at least the fields used for negprot and
|
||||
* sesssetup.
|
||||
*
|
||||
* We only need the volume here, so we can reuse memory from
|
||||
* the session and server without caring about memory
|
||||
* management.
|
||||
*/
|
||||
|
||||
/* Always make new connection for now (TODO?) */
|
||||
vol.nosharesock = true;
|
||||
|
||||
/* Auth */
|
||||
vol.domainauto = ses->domainAuto;
|
||||
vol.domainname = ses->domainName;
|
||||
vol.username = ses->user_name;
|
||||
vol.password = ses->password;
|
||||
vol.sectype = ses->sectype;
|
||||
vol.sign = ses->sign;
|
||||
|
||||
/* UNC and paths */
|
||||
/* XXX: Use ses->server->hostname? */
|
||||
sprintf(unc, unc_fmt, ses->serverName);
|
||||
vol.UNC = unc;
|
||||
vol.prepath = "";
|
||||
|
||||
/* Re-use same version as master connection */
|
||||
vol.vals = ses->server->vals;
|
||||
vol.ops = ses->server->ops;
|
||||
|
||||
vol.noblocksnd = ses->server->noblocksnd;
|
||||
vol.noautotune = ses->server->noautotune;
|
||||
vol.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
|
||||
vol.echo_interval = ses->server->echo_interval / HZ;
|
||||
|
||||
/*
|
||||
* This will be used for encoding/decoding user/domain/pw
|
||||
* during sess setup auth.
|
||||
*
|
||||
* XXX: We use the default for simplicity but the proper way
|
||||
* would be to use the one that ses used, which is not
|
||||
* stored. This might break when dealing with non-ascii
|
||||
* strings.
|
||||
*/
|
||||
vol.local_nls = load_nls_default();
|
||||
|
||||
/* Use RDMA if possible */
|
||||
vol.rdma = iface->rdma_capable;
|
||||
memcpy(&vol.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
|
||||
|
||||
/* reuse master con client guid */
|
||||
memcpy(&vol.client_guid, ses->server->client_guid,
|
||||
SMB2_CLIENT_GUID_SIZE);
|
||||
vol.use_client_guid = true;
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
|
||||
chan = &ses->chans[ses->chan_count];
|
||||
chan->server = cifs_get_tcp_session(&vol);
|
||||
if (IS_ERR(chan->server)) {
|
||||
rc = PTR_ERR(chan->server);
|
||||
chan->server = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to allocate the server crypto now as we will need
|
||||
* to sign packets before we generate the channel signing key
|
||||
* (we sign with the session key)
|
||||
*/
|
||||
rc = smb311_crypto_shash_allocate(chan->server);
|
||||
if (rc) {
|
||||
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ses->binding = true;
|
||||
rc = cifs_negotiate_protocol(xid, ses);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = cifs_setup_session(xid, ses, vol.local_nls);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* success, put it on the list
|
||||
* XXX: sharing ses between 2 tcp server is not possible, the
|
||||
* way "internal" linked lists works in linux makes element
|
||||
* only able to belong to one list
|
||||
*
|
||||
* the binding session is already established so the rest of
|
||||
* the code should be able to look it up, no need to add the
|
||||
* ses to the new server.
|
||||
*/
|
||||
|
||||
ses->chan_count++;
|
||||
atomic_set(&ses->chan_seq, 0);
|
||||
out:
|
||||
ses->binding = false;
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
|
||||
if (rc && chan->server)
|
||||
cifs_put_tcp_session(chan->server, 0);
|
||||
unload_nls(vol.local_nls);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
|
||||
{
|
||||
@ -342,6 +567,7 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
|
||||
void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
||||
struct cifs_ses *ses)
|
||||
{
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
|
||||
__u32 flags;
|
||||
|
||||
@ -354,9 +580,9 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
||||
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
|
||||
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
|
||||
NTLMSSP_NEGOTIATE_SEAL;
|
||||
if (ses->server->sign)
|
||||
if (server->sign)
|
||||
flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
|
||||
if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
|
||||
flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
|
||||
|
||||
sec_blob->NegotiateFlags = cpu_to_le32(flags);
|
||||
|
@ -369,12 +369,10 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
|
||||
|
||||
static void
|
||||
cifs_downgrade_oplock(struct TCP_Server_Info *server,
|
||||
struct cifsInodeInfo *cinode, bool set_level2)
|
||||
struct cifsInodeInfo *cinode, __u32 oplock,
|
||||
unsigned int epoch, bool *purge_cache)
|
||||
{
|
||||
if (set_level2)
|
||||
cifs_set_oplock_level(cinode, OPLOCK_READ);
|
||||
else
|
||||
cifs_set_oplock_level(cinode, 0);
|
||||
cifs_set_oplock_level(cinode, oplock);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "cifs_unicode.h"
|
||||
#include "smb2status.h"
|
||||
#include "smb2glob.h"
|
||||
#include "nterr.h"
|
||||
|
||||
static int
|
||||
check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
|
||||
@ -249,16 +250,10 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
|
||||
* of junk. Other servers match RFC1001 len to actual
|
||||
* SMB2/SMB3 frame length (header + smb2 response specific data)
|
||||
* Some windows servers also pad up to 8 bytes when compounding.
|
||||
* If pad is longer than eight bytes, log the server behavior
|
||||
* (once), since may indicate a problem but allow it and continue
|
||||
* since the frame is parseable.
|
||||
*/
|
||||
if (clc_len < len) {
|
||||
pr_warn_once(
|
||||
"srv rsp padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
|
||||
len, clc_len, command, mid);
|
||||
if (clc_len < len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_warn_once(
|
||||
"srv rsp too short, len %d not %d. cmd:%d mid:%llu\n",
|
||||
len, clc_len, command, mid);
|
||||
@ -534,7 +529,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
|
||||
|
||||
cifs_dbg(FYI, "found in the open list\n");
|
||||
cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
|
||||
le32_to_cpu(rsp->NewLeaseState));
|
||||
lease_state);
|
||||
|
||||
if (ack_req)
|
||||
cfile->oplock_break_cancelled = false;
|
||||
@ -543,17 +538,8 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
|
||||
|
||||
set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
|
||||
|
||||
/*
|
||||
* Set or clear flags depending on the lease state being READ.
|
||||
* HANDLE caching flag should be added when the client starts
|
||||
* to defer closing remote file handles with HANDLE leases.
|
||||
*/
|
||||
if (lease_state & SMB2_LEASE_READ_CACHING_HE)
|
||||
set_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&cinode->flags);
|
||||
else
|
||||
clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&cinode->flags);
|
||||
cfile->oplock_epoch = le16_to_cpu(rsp->Epoch);
|
||||
cfile->oplock_level = lease_state;
|
||||
|
||||
cifs_queue_oplock_break(cfile);
|
||||
kfree(lw);
|
||||
@ -576,7 +562,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
|
||||
|
||||
cifs_dbg(FYI, "found in the pending open list\n");
|
||||
cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
|
||||
le32_to_cpu(rsp->NewLeaseState));
|
||||
lease_state);
|
||||
|
||||
open->oplock = lease_state;
|
||||
}
|
||||
@ -673,10 +659,10 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(tmp, &server->smb_ses_list) {
|
||||
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
|
||||
|
||||
list_for_each(tmp1, &ses->tcon_list) {
|
||||
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
|
||||
|
||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each(tmp2, &tcon->openFileList) {
|
||||
cfile = list_entry(tmp2, struct cifsFileInfo,
|
||||
@ -688,6 +674,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
||||
continue;
|
||||
|
||||
cifs_dbg(FYI, "file id match, oplock break\n");
|
||||
cifs_stats_inc(
|
||||
&tcon->stats.cifs_stats.num_oplock_brks);
|
||||
cinode = CIFS_I(d_inode(cfile->dentry));
|
||||
spin_lock(&cfile->file_info_lock);
|
||||
if (!CIFS_CACHE_WRITE(cinode) &&
|
||||
@ -699,18 +687,9 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
||||
set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
|
||||
&cinode->flags);
|
||||
|
||||
/*
|
||||
* Set flag if the server downgrades the oplock
|
||||
* to L2 else clear.
|
||||
*/
|
||||
if (rsp->OplockLevel)
|
||||
set_bit(
|
||||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&cinode->flags);
|
||||
else
|
||||
clear_bit(
|
||||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&cinode->flags);
|
||||
cfile->oplock_epoch = 0;
|
||||
cfile->oplock_level = rsp->OplockLevel;
|
||||
|
||||
spin_unlock(&cfile->file_info_lock);
|
||||
|
||||
cifs_queue_oplock_break(cfile);
|
||||
@ -720,9 +699,6 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
||||
return true;
|
||||
}
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_dbg(FYI, "No matching file for oplock break\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
@ -735,45 +711,98 @@ smb2_cancelled_close_fid(struct work_struct *work)
|
||||
{
|
||||
struct close_cancelled_open *cancelled = container_of(work,
|
||||
struct close_cancelled_open, work);
|
||||
struct cifs_tcon *tcon = cancelled->tcon;
|
||||
int rc;
|
||||
|
||||
cifs_dbg(VFS, "Close unmatched open\n");
|
||||
if (cancelled->mid)
|
||||
cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llx\n",
|
||||
cancelled->mid);
|
||||
else
|
||||
cifs_tcon_dbg(VFS, "Close interrupted close\n");
|
||||
|
||||
SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid,
|
||||
cancelled->fid.volatile_fid);
|
||||
cifs_put_tcon(cancelled->tcon);
|
||||
rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid,
|
||||
cancelled->fid.volatile_fid);
|
||||
if (rc)
|
||||
cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc);
|
||||
|
||||
cifs_put_tcon(tcon);
|
||||
kfree(cancelled);
|
||||
}
|
||||
|
||||
/*
|
||||
* Caller should already has an extra reference to @tcon
|
||||
* This function is used to queue work to close a handle to prevent leaks
|
||||
* on the server.
|
||||
* We handle two cases. If an open was interrupted after we sent the
|
||||
* SMB2_CREATE to the server but before we processed the reply, and second
|
||||
* if a close was interrupted before we sent the SMB2_CLOSE to the server.
|
||||
*/
|
||||
static int
|
||||
__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid,
|
||||
__u64 persistent_fid, __u64 volatile_fid)
|
||||
{
|
||||
struct close_cancelled_open *cancelled;
|
||||
|
||||
cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
|
||||
if (!cancelled)
|
||||
return -ENOMEM;
|
||||
|
||||
cancelled->fid.persistent_fid = persistent_fid;
|
||||
cancelled->fid.volatile_fid = volatile_fid;
|
||||
cancelled->tcon = tcon;
|
||||
cancelled->cmd = cmd;
|
||||
cancelled->mid = mid;
|
||||
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
|
||||
WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
|
||||
__u64 volatile_fid)
|
||||
{
|
||||
int rc;
|
||||
|
||||
cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
tcon->tc_count++;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0,
|
||||
persistent_fid, volatile_fid);
|
||||
if (rc)
|
||||
cifs_put_tcon(tcon);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
|
||||
{
|
||||
struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer;
|
||||
struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
|
||||
struct cifs_tcon *tcon;
|
||||
struct close_cancelled_open *cancelled;
|
||||
int rc;
|
||||
|
||||
if (sync_hdr->Command != SMB2_CREATE ||
|
||||
sync_hdr->Status != STATUS_SUCCESS)
|
||||
return 0;
|
||||
|
||||
cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
|
||||
if (!cancelled)
|
||||
return -ENOMEM;
|
||||
|
||||
tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
|
||||
sync_hdr->TreeId);
|
||||
if (!tcon) {
|
||||
kfree(cancelled);
|
||||
if (!tcon)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
cancelled->fid.persistent_fid = rsp->PersistentFileId;
|
||||
cancelled->fid.volatile_fid = rsp->VolatileFileId;
|
||||
cancelled->tcon = tcon;
|
||||
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
|
||||
queue_work(cifsiod_wq, &cancelled->work);
|
||||
rc = __smb2_handle_cancelled_cmd(tcon,
|
||||
le16_to_cpu(sync_hdr->Command),
|
||||
le64_to_cpu(sync_hdr->MessageId),
|
||||
rsp->PersistentFileId,
|
||||
rsp->VolatileFileId);
|
||||
if (rc)
|
||||
cifs_put_tcon(tcon);
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -788,23 +817,37 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
|
||||
int i, rc;
|
||||
struct sdesc *d;
|
||||
struct smb2_sync_hdr *hdr;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
|
||||
if (ses->server->tcpStatus == CifsGood) {
|
||||
/* skip non smb311 connections */
|
||||
if (ses->server->dialect != SMB311_PROT_ID)
|
||||
return 0;
|
||||
hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
|
||||
/* neg prot are always taken */
|
||||
if (hdr->Command == SMB2_NEGOTIATE)
|
||||
goto ok;
|
||||
|
||||
/* skip last sess setup response */
|
||||
hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
|
||||
if (hdr->Flags & SMB2_FLAGS_SIGNED)
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* If we process a command which wasn't a negprot it means the
|
||||
* neg prot was already done, so the server dialect was set
|
||||
* and we can test it. Preauth requires 3.1.1 for now.
|
||||
*/
|
||||
if (server->dialect != SMB311_PROT_ID)
|
||||
return 0;
|
||||
|
||||
rc = smb311_crypto_shash_allocate(ses->server);
|
||||
if (hdr->Command != SMB2_SESSION_SETUP)
|
||||
return 0;
|
||||
|
||||
/* skip last sess setup response */
|
||||
if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
|
||||
&& (hdr->Status == NT_STATUS_OK
|
||||
|| (hdr->Status !=
|
||||
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))))
|
||||
return 0;
|
||||
|
||||
ok:
|
||||
rc = smb311_crypto_shash_allocate(server);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
d = ses->server->secmech.sdescsha512;
|
||||
d = server->secmech.sdescsha512;
|
||||
rc = crypto_shash_init(&d->shash);
|
||||
if (rc) {
|
||||
cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/sort.h>
|
||||
#include <crypto/aead.h>
|
||||
#include "cifsglob.h"
|
||||
#include "smb2pdu.h"
|
||||
@ -151,13 +152,7 @@ smb2_get_credits_field(struct TCP_Server_Info *server, const int optype)
|
||||
static unsigned int
|
||||
smb2_get_credits(struct mid_q_entry *mid)
|
||||
{
|
||||
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)mid->resp_buf;
|
||||
|
||||
if (mid->mid_state == MID_RESPONSE_RECEIVED
|
||||
|| mid->mid_state == MID_RESPONSE_MALFORMED)
|
||||
return le16_to_cpu(shdr->CreditRequest);
|
||||
|
||||
return 0;
|
||||
return mid->credits_received;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -315,7 +310,7 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ses->server->CurrentMid = 0;
|
||||
cifs_ses_server(ses)->CurrentMid = 0;
|
||||
rc = SMB2_negotiate(xid, ses);
|
||||
/* BB we probably don't need to retry with modern servers */
|
||||
if (rc == -EAGAIN)
|
||||
@ -558,6 +553,13 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int compare_iface(const void *ia, const void *ib)
|
||||
{
|
||||
const struct cifs_server_iface *a = (struct cifs_server_iface *)ia;
|
||||
const struct cifs_server_iface *b = (struct cifs_server_iface *)ib;
|
||||
|
||||
return a->speed == b->speed ? 0 : (a->speed > b->speed ? -1 : 1);
|
||||
}
|
||||
|
||||
static int
|
||||
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
@ -587,6 +589,9 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* sort interfaces from fastest to slowest */
|
||||
sort(iface_list, iface_count, sizeof(*iface_list), compare_iface, NULL);
|
||||
|
||||
spin_lock(&ses->iface_lock);
|
||||
kfree(ses->iface_list);
|
||||
ses->iface_list = iface_list;
|
||||
@ -1402,15 +1407,10 @@ smb2_ioctl_query_info(const unsigned int xid,
|
||||
if (smb3_encryption_required(tcon))
|
||||
flags |= CIFS_TRANSFORM_REQ;
|
||||
|
||||
buffer = kmalloc(qi.output_buffer_length, GFP_KERNEL);
|
||||
if (buffer == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(buffer, arg + sizeof(struct smb_query_info),
|
||||
qi.output_buffer_length)) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
buffer = memdup_user(arg + sizeof(struct smb_query_info),
|
||||
qi.output_buffer_length);
|
||||
if (IS_ERR(buffer))
|
||||
return PTR_ERR(buffer);
|
||||
|
||||
/* Open */
|
||||
memset(&open_iov, 0, sizeof(open_iov));
|
||||
@ -1529,35 +1529,32 @@ smb2_ioctl_query_info(const unsigned int xid,
|
||||
if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
|
||||
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
|
||||
if (qi.input_buffer_length > 0 &&
|
||||
le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length > rsp_iov[1].iov_len) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
|
||||
sizeof(qi.input_buffer_length))) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length
|
||||
> rsp_iov[1].iov_len)
|
||||
goto e_fault;
|
||||
|
||||
if (copy_to_user(&pqi->input_buffer_length,
|
||||
&qi.input_buffer_length,
|
||||
sizeof(qi.input_buffer_length)))
|
||||
goto e_fault;
|
||||
|
||||
if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info),
|
||||
(const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset),
|
||||
qi.input_buffer_length)) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
qi.input_buffer_length))
|
||||
goto e_fault;
|
||||
} else {
|
||||
pqi = (struct smb_query_info __user *)arg;
|
||||
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
|
||||
if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length)
|
||||
qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength);
|
||||
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
|
||||
sizeof(qi.input_buffer_length))) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
if (copy_to_user(pqi + 1, qi_rsp->Buffer, qi.input_buffer_length)) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
if (copy_to_user(&pqi->input_buffer_length,
|
||||
&qi.input_buffer_length,
|
||||
sizeof(qi.input_buffer_length)))
|
||||
goto e_fault;
|
||||
|
||||
if (copy_to_user(pqi + 1, qi_rsp->Buffer,
|
||||
qi.input_buffer_length))
|
||||
goto e_fault;
|
||||
}
|
||||
|
||||
iqinf_exit:
|
||||
@ -1573,6 +1570,10 @@ smb2_ioctl_query_info(const unsigned int xid,
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
|
||||
return rc;
|
||||
|
||||
e_fault:
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -3281,22 +3282,38 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
|
||||
|
||||
static void
|
||||
smb2_downgrade_oplock(struct TCP_Server_Info *server,
|
||||
struct cifsInodeInfo *cinode, bool set_level2)
|
||||
struct cifsInodeInfo *cinode, __u32 oplock,
|
||||
unsigned int epoch, bool *purge_cache)
|
||||
{
|
||||
if (set_level2)
|
||||
server->ops->set_oplock_level(cinode, SMB2_OPLOCK_LEVEL_II,
|
||||
0, NULL);
|
||||
else
|
||||
server->ops->set_oplock_level(cinode, 0, 0, NULL);
|
||||
server->ops->set_oplock_level(cinode, oplock, 0, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
smb21_downgrade_oplock(struct TCP_Server_Info *server,
|
||||
struct cifsInodeInfo *cinode, bool set_level2)
|
||||
smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
|
||||
unsigned int epoch, bool *purge_cache);
|
||||
|
||||
static void
|
||||
smb3_downgrade_oplock(struct TCP_Server_Info *server,
|
||||
struct cifsInodeInfo *cinode, __u32 oplock,
|
||||
unsigned int epoch, bool *purge_cache)
|
||||
{
|
||||
server->ops->set_oplock_level(cinode,
|
||||
set_level2 ? SMB2_LEASE_READ_CACHING_HE :
|
||||
0, 0, NULL);
|
||||
unsigned int old_state = cinode->oplock;
|
||||
unsigned int old_epoch = cinode->epoch;
|
||||
unsigned int new_state;
|
||||
|
||||
if (epoch > old_epoch) {
|
||||
smb21_set_oplock_level(cinode, oplock, 0, NULL);
|
||||
cinode->epoch = epoch;
|
||||
}
|
||||
|
||||
new_state = cinode->oplock;
|
||||
*purge_cache = false;
|
||||
|
||||
if ((old_state & CIFS_CACHE_READ_FLG) != 0 &&
|
||||
(new_state & CIFS_CACHE_READ_FLG) == 0)
|
||||
*purge_cache = true;
|
||||
else if (old_state == new_state && (epoch - old_epoch > 1))
|
||||
*purge_cache = true;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -3598,14 +3615,16 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
|
||||
u8 *ses_enc_key;
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
|
||||
if (ses->Suid != ses_id)
|
||||
continue;
|
||||
ses_enc_key = enc ? ses->smb3encryptionkey :
|
||||
ses->smb3decryptionkey;
|
||||
memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
|
||||
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
|
||||
if (ses->Suid == ses_id) {
|
||||
ses_enc_key = enc ? ses->smb3encryptionkey :
|
||||
ses->smb3decryptionkey;
|
||||
memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
@ -4556,7 +4575,7 @@ struct smb_version_operations smb21_operations = {
|
||||
.print_stats = smb2_print_stats,
|
||||
.is_oplock_break = smb2_is_valid_oplock_break,
|
||||
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
||||
.downgrade_oplock = smb21_downgrade_oplock,
|
||||
.downgrade_oplock = smb2_downgrade_oplock,
|
||||
.need_neg = smb2_need_neg,
|
||||
.negotiate = smb2_negotiate,
|
||||
.negotiate_wsize = smb2_negotiate_wsize,
|
||||
@ -4656,7 +4675,7 @@ struct smb_version_operations smb30_operations = {
|
||||
.dump_share_caps = smb2_dump_share_caps,
|
||||
.is_oplock_break = smb2_is_valid_oplock_break,
|
||||
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
||||
.downgrade_oplock = smb21_downgrade_oplock,
|
||||
.downgrade_oplock = smb3_downgrade_oplock,
|
||||
.need_neg = smb2_need_neg,
|
||||
.negotiate = smb2_negotiate,
|
||||
.negotiate_wsize = smb3_negotiate_wsize,
|
||||
@ -4764,7 +4783,7 @@ struct smb_version_operations smb311_operations = {
|
||||
.dump_share_caps = smb2_dump_share_caps,
|
||||
.is_oplock_break = smb2_is_valid_oplock_break,
|
||||
.handle_cancelled_mid = smb2_handle_cancelled_mid,
|
||||
.downgrade_oplock = smb21_downgrade_oplock,
|
||||
.downgrade_oplock = smb3_downgrade_oplock,
|
||||
.need_neg = smb2_need_neg,
|
||||
.negotiate = smb2_negotiate,
|
||||
.negotiate_wsize = smb3_negotiate_wsize,
|
||||
|
@ -252,7 +252,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
||||
if (tcon == NULL)
|
||||
return 0;
|
||||
|
||||
if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
|
||||
if (smb2_command == SMB2_TREE_CONNECT)
|
||||
return 0;
|
||||
|
||||
if (tcon->tidStatus == CifsExiting) {
|
||||
@ -426,16 +426,9 @@ fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, void *buf,
|
||||
* SMB information in the SMB header. If the return code is zero, this
|
||||
* function must have filled in request_buf pointer.
|
||||
*/
|
||||
static int
|
||||
smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
void **request_buf, unsigned int *total_len)
|
||||
static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
void **request_buf, unsigned int *total_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = smb2_reconnect(smb2_command, tcon);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* BB eventually switch this to SMB2 specific small buf size */
|
||||
if (smb2_command == SMB2_SET_INFO)
|
||||
*request_buf = cifs_buf_get();
|
||||
@ -456,7 +449,31 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
cifs_stats_inc(&tcon->num_smbs_sent);
|
||||
}
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
void **request_buf, unsigned int *total_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = smb2_reconnect(smb2_command, tcon);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return __smb2_plain_req_init(smb2_command, tcon, request_buf,
|
||||
total_len);
|
||||
}
|
||||
|
||||
static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon,
|
||||
void **request_buf, unsigned int *total_len)
|
||||
{
|
||||
/* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */
|
||||
if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) {
|
||||
return __smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf,
|
||||
total_len);
|
||||
}
|
||||
return smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf, total_len);
|
||||
}
|
||||
|
||||
/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */
|
||||
@ -791,7 +808,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
struct kvec rsp_iov;
|
||||
int rc = 0;
|
||||
int resp_buftype;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
int blob_offset, blob_length;
|
||||
char *security_blob;
|
||||
int flags = CIFS_NEG_OP;
|
||||
@ -813,7 +830,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
|
||||
memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
|
||||
|
||||
if (strcmp(ses->server->vals->version_string,
|
||||
if (strcmp(server->vals->version_string,
|
||||
SMB3ANY_VERSION_STRING) == 0) {
|
||||
req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
|
||||
req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
|
||||
@ -829,7 +846,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
total_len += 8;
|
||||
} else {
|
||||
/* otherwise send specific dialect */
|
||||
req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
|
||||
req->Dialects[0] = cpu_to_le16(server->vals->protocol_id);
|
||||
req->DialectCount = cpu_to_le16(1);
|
||||
total_len += 2;
|
||||
}
|
||||
@ -1171,7 +1188,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct smb2_sess_setup_req *req;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
unsigned int total_len;
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, (void **) &req,
|
||||
@ -1179,13 +1196,21 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* First session, not a reauthenticate */
|
||||
req->sync_hdr.SessionId = 0;
|
||||
|
||||
/* if reconnect, we need to send previous sess id, otherwise it is 0 */
|
||||
req->PreviousSessionId = sess_data->previous_session;
|
||||
|
||||
req->Flags = 0; /* MBZ */
|
||||
if (sess_data->ses->binding) {
|
||||
req->sync_hdr.SessionId = sess_data->ses->Suid;
|
||||
req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
|
||||
req->PreviousSessionId = 0;
|
||||
req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
|
||||
} else {
|
||||
/* First session, not a reauthenticate */
|
||||
req->sync_hdr.SessionId = 0;
|
||||
/*
|
||||
* if reconnect, we need to send previous sess id
|
||||
* otherwise it is 0
|
||||
*/
|
||||
req->PreviousSessionId = sess_data->previous_session;
|
||||
req->Flags = 0; /* MBZ */
|
||||
}
|
||||
|
||||
/* enough to enable echos and oplocks and one max size write */
|
||||
req->sync_hdr.CreditRequest = cpu_to_le16(130);
|
||||
@ -1258,28 +1283,33 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc = 0;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
|
||||
mutex_lock(&ses->server->srv_mutex);
|
||||
if (ses->server->ops->generate_signingkey) {
|
||||
rc = ses->server->ops->generate_signingkey(ses);
|
||||
mutex_lock(&server->srv_mutex);
|
||||
if (server->ops->generate_signingkey) {
|
||||
rc = server->ops->generate_signingkey(ses);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI,
|
||||
"SMB3 session key generation failed\n");
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (!ses->server->session_estab) {
|
||||
ses->server->sequence_number = 0x2;
|
||||
ses->server->session_estab = true;
|
||||
if (!server->session_estab) {
|
||||
server->sequence_number = 0x2;
|
||||
server->session_estab = true;
|
||||
}
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
|
||||
cifs_dbg(FYI, "SMB2/3 session established successfully\n");
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
ses->status = CifsGood;
|
||||
ses->need_reconnect = false;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
/* keep existing ses state if binding */
|
||||
if (!ses->binding) {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
ses->status = CifsGood;
|
||||
ses->need_reconnect = false;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1317,16 +1347,19 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
||||
goto out_put_spnego_key;
|
||||
}
|
||||
|
||||
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
|
||||
GFP_KERNEL);
|
||||
if (!ses->auth_key.response) {
|
||||
cifs_dbg(VFS,
|
||||
"Kerberos can't allocate (%u bytes) memory",
|
||||
msg->sesskey_len);
|
||||
rc = -ENOMEM;
|
||||
goto out_put_spnego_key;
|
||||
/* keep session key if binding */
|
||||
if (!ses->binding) {
|
||||
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
|
||||
GFP_KERNEL);
|
||||
if (!ses->auth_key.response) {
|
||||
cifs_dbg(VFS,
|
||||
"Kerberos can't allocate (%u bytes) memory",
|
||||
msg->sesskey_len);
|
||||
rc = -ENOMEM;
|
||||
goto out_put_spnego_key;
|
||||
}
|
||||
ses->auth_key.len = msg->sesskey_len;
|
||||
}
|
||||
ses->auth_key.len = msg->sesskey_len;
|
||||
|
||||
sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
|
||||
sess_data->iov[1].iov_len = msg->secblob_len;
|
||||
@ -1336,9 +1369,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
||||
goto out_put_spnego_key;
|
||||
|
||||
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
||||
ses->Suid = rsp->sync_hdr.SessionId;
|
||||
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
/* keep session id and flags if binding */
|
||||
if (!ses->binding) {
|
||||
ses->Suid = rsp->sync_hdr.SessionId;
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
}
|
||||
|
||||
rc = SMB2_sess_establish_session(sess_data);
|
||||
out_put_spnego_key:
|
||||
@ -1432,9 +1467,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
||||
|
||||
cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
|
||||
|
||||
|
||||
ses->Suid = rsp->sync_hdr.SessionId;
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
/* keep existing ses id and flags if binding */
|
||||
if (!ses->binding) {
|
||||
ses->Suid = rsp->sync_hdr.SessionId;
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(ntlmssp_blob);
|
||||
@ -1491,8 +1528,11 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
|
||||
|
||||
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
||||
|
||||
ses->Suid = rsp->sync_hdr.SessionId;
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
/* keep existing ses id and flags if binding */
|
||||
if (!ses->binding) {
|
||||
ses->Suid = rsp->sync_hdr.SessionId;
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
}
|
||||
|
||||
rc = SMB2_sess_establish_session(sess_data);
|
||||
out:
|
||||
@ -1509,7 +1549,7 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int type;
|
||||
|
||||
type = smb2_select_sectype(ses->server, ses->sectype);
|
||||
type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype);
|
||||
cifs_dbg(FYI, "sess setup type %d\n", type);
|
||||
if (type == Unspecified) {
|
||||
cifs_dbg(VFS,
|
||||
@ -1537,7 +1577,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
int rc = 0;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
struct SMB2_sess_data *sess_data;
|
||||
|
||||
cifs_dbg(FYI, "Session Setup\n");
|
||||
@ -1563,7 +1603,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||
/*
|
||||
* Initialize the session hash with the server one.
|
||||
*/
|
||||
memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash,
|
||||
memcpy(ses->preauth_sha_hash, server->preauth_sha_hash,
|
||||
SMB2_PREAUTH_HASH_SIZE);
|
||||
|
||||
while (sess_data->func)
|
||||
@ -1807,6 +1847,8 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
|
||||
return 0;
|
||||
|
||||
close_shroot(&tcon->crfid);
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req,
|
||||
&total_len);
|
||||
if (rc)
|
||||
@ -2661,7 +2703,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
|
||||
int rc;
|
||||
char *in_data_buf;
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_IOCTL, tcon, (void **) &req, &total_len);
|
||||
rc = smb2_ioctl_req_init(opcode, tcon, (void **) &req, &total_len);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@ -2972,7 +3014,21 @@ int
|
||||
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid)
|
||||
{
|
||||
return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
|
||||
int rc;
|
||||
int tmp_rc;
|
||||
|
||||
rc = SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
|
||||
|
||||
/* retry close in a worker thread if this one is interrupted */
|
||||
if (rc == -EINTR) {
|
||||
tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid,
|
||||
volatile_fid);
|
||||
if (tmp_rc)
|
||||
cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
|
||||
persistent_fid, tmp_rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1386,7 +1386,7 @@ struct smb2_oplock_break {
|
||||
struct smb2_lease_break {
|
||||
struct smb2_sync_hdr sync_hdr;
|
||||
__le16 StructureSize; /* Must be 44 */
|
||||
__le16 Reserved;
|
||||
__le16 Epoch;
|
||||
__le32 Flags;
|
||||
__u8 LeaseKey[16];
|
||||
__le32 CurrentLeaseState;
|
||||
|
@ -46,7 +46,8 @@ extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *);
|
||||
extern int smb2_check_receive(struct mid_q_entry *mid,
|
||||
struct TCP_Server_Info *server, bool log_error);
|
||||
extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
|
||||
struct smb_rqst *rqst);
|
||||
struct TCP_Server_Info *,
|
||||
struct smb_rqst *rqst);
|
||||
extern struct mid_q_entry *smb2_setup_async_request(
|
||||
struct TCP_Server_Info *server, struct smb_rqst *rqst);
|
||||
extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
|
||||
@ -212,6 +213,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const u64 persistent_fid, const u64 volatile_fid,
|
||||
const __u8 oplock_level);
|
||||
extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon,
|
||||
__u64 persistent_fid,
|
||||
__u64 volatile_fid);
|
||||
extern int smb2_handle_cancelled_mid(char *buffer,
|
||||
struct TCP_Server_Info *server);
|
||||
void smb2_cancelled_close_fid(struct work_struct *work);
|
||||
|
@ -98,6 +98,61 @@ err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
|
||||
{
|
||||
struct cifs_chan *chan;
|
||||
struct cifs_ses *ses = NULL;
|
||||
int i;
|
||||
int rc = 0;
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
|
||||
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
|
||||
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
|
||||
if (ses->Suid == ses_id)
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n",
|
||||
__func__, ses_id);
|
||||
rc = -ENOENT;
|
||||
goto out;
|
||||
|
||||
found:
|
||||
if (ses->binding) {
|
||||
/*
|
||||
* If we are in the process of binding a new channel
|
||||
* to an existing session, use the master connection
|
||||
* session key
|
||||
*/
|
||||
memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, use the channel key.
|
||||
*/
|
||||
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
chan = ses->chans + i;
|
||||
if (chan->server == server) {
|
||||
memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cifs_dbg(VFS,
|
||||
"%s: Could not find channel signing key for session 0x%llx\n",
|
||||
__func__, ses_id);
|
||||
rc = -ENOENT;
|
||||
|
||||
out:
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct cifs_ses *
|
||||
smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
|
||||
{
|
||||
@ -328,21 +383,45 @@ generate_smb3signingkey(struct cifs_ses *ses,
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = generate_key(ses, ptriplet->signing.label,
|
||||
ptriplet->signing.context, ses->smb3signingkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
/*
|
||||
* All channels use the same encryption/decryption keys but
|
||||
* they have their own signing key.
|
||||
*
|
||||
* When we generate the keys, check if it is for a new channel
|
||||
* (binding) in which case we only need to generate a signing
|
||||
* key and store it in the channel as to not overwrite the
|
||||
* master connection signing key stored in the session
|
||||
*/
|
||||
|
||||
rc = generate_key(ses, ptriplet->encryption.label,
|
||||
ptriplet->encryption.context, ses->smb3encryptionkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (ses->binding) {
|
||||
rc = generate_key(ses, ptriplet->signing.label,
|
||||
ptriplet->signing.context,
|
||||
cifs_ses_binding_channel(ses)->signkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
} else {
|
||||
rc = generate_key(ses, ptriplet->signing.label,
|
||||
ptriplet->signing.context,
|
||||
ses->smb3signingkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = generate_key(ses, ptriplet->decryption.label,
|
||||
ptriplet->decryption.context,
|
||||
ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
|
||||
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
|
||||
rc = generate_key(ses, ptriplet->encryption.label,
|
||||
ptriplet->encryption.context,
|
||||
ses->smb3encryptionkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
rc = generate_key(ses, ptriplet->decryption.label,
|
||||
ptriplet->decryption.context,
|
||||
ses->smb3decryptionkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -431,21 +510,19 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
unsigned char *sigptr = smb3_signature;
|
||||
struct kvec *iov = rqst->rq_iov;
|
||||
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
|
||||
struct cifs_ses *ses;
|
||||
struct shash_desc *shash = &server->secmech.sdesccmacaes->shash;
|
||||
struct smb_rqst drqst;
|
||||
u8 key[SMB3_SIGN_KEY_SIZE];
|
||||
|
||||
ses = smb2_find_smb_ses(server, shdr->SessionId);
|
||||
if (!ses) {
|
||||
cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
|
||||
rc = smb2_get_sign_key(shdr->SessionId, server, key);
|
||||
if (rc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
|
||||
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
|
||||
|
||||
rc = crypto_shash_setkey(server->secmech.cmacaes,
|
||||
ses->smb3signingkey, SMB2_CMACAES_SIZE);
|
||||
key, SMB2_CMACAES_SIZE);
|
||||
if (rc) {
|
||||
cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
|
||||
return rc;
|
||||
@ -494,16 +571,25 @@ static int
|
||||
smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
{
|
||||
int rc = 0;
|
||||
struct smb2_sync_hdr *shdr =
|
||||
(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
|
||||
struct smb2_sync_hdr *shdr;
|
||||
struct smb2_sess_setup_req *ssr;
|
||||
bool is_binding;
|
||||
bool is_signed;
|
||||
|
||||
if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
|
||||
server->tcpStatus == CifsNeedNegotiate)
|
||||
return rc;
|
||||
shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
|
||||
ssr = (struct smb2_sess_setup_req *)shdr;
|
||||
|
||||
if (!server->session_estab) {
|
||||
is_binding = shdr->Command == SMB2_SESSION_SETUP &&
|
||||
(ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING);
|
||||
is_signed = shdr->Flags & SMB2_FLAGS_SIGNED;
|
||||
|
||||
if (!is_signed)
|
||||
return 0;
|
||||
if (server->tcpStatus == CifsNeedNegotiate)
|
||||
return 0;
|
||||
if (!is_binding && !server->session_estab) {
|
||||
strncpy(shdr->Signature, "BSRSPYL", 8);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = server->ops->calc_signature(rqst, server);
|
||||
@ -610,18 +696,18 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
|
||||
struct mid_q_entry **mid)
|
||||
smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
|
||||
struct smb2_sync_hdr *shdr, struct mid_q_entry **mid)
|
||||
{
|
||||
if (ses->server->tcpStatus == CifsExiting)
|
||||
if (server->tcpStatus == CifsExiting)
|
||||
return -ENOENT;
|
||||
|
||||
if (ses->server->tcpStatus == CifsNeedReconnect) {
|
||||
if (server->tcpStatus == CifsNeedReconnect) {
|
||||
cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ses->server->tcpStatus == CifsNeedNegotiate &&
|
||||
if (server->tcpStatus == CifsNeedNegotiate &&
|
||||
shdr->Command != SMB2_NEGOTIATE)
|
||||
return -EAGAIN;
|
||||
|
||||
@ -638,11 +724,11 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
|
||||
/* else ok - we are shutting down the session */
|
||||
}
|
||||
|
||||
*mid = smb2_mid_entry_alloc(shdr, ses->server);
|
||||
*mid = smb2_mid_entry_alloc(shdr, server);
|
||||
if (*mid == NULL)
|
||||
return -ENOMEM;
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
|
||||
list_add_tail(&(*mid)->qhead, &server->pending_mid_q);
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
|
||||
return 0;
|
||||
@ -675,24 +761,25 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||
}
|
||||
|
||||
struct mid_q_entry *
|
||||
smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
|
||||
smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server,
|
||||
struct smb_rqst *rqst)
|
||||
{
|
||||
int rc;
|
||||
struct smb2_sync_hdr *shdr =
|
||||
(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
|
||||
struct mid_q_entry *mid;
|
||||
|
||||
smb2_seq_num_into_buf(ses->server, shdr);
|
||||
smb2_seq_num_into_buf(server, shdr);
|
||||
|
||||
rc = smb2_get_mid_entry(ses, shdr, &mid);
|
||||
rc = smb2_get_mid_entry(ses, server, shdr, &mid);
|
||||
if (rc) {
|
||||
revert_current_mid_from_hdr(ses->server, shdr);
|
||||
revert_current_mid_from_hdr(server, shdr);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
rc = smb2_sign_rqst(rqst, ses->server);
|
||||
rc = smb2_sign_rqst(rqst, server);
|
||||
if (rc) {
|
||||
revert_current_mid_from_hdr(ses->server, shdr);
|
||||
revert_current_mid_from_hdr(server, shdr);
|
||||
cifs_delete_mid(mid);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
@ -1069,7 +1069,7 @@ static int smbd_post_send_data(
|
||||
|
||||
if (n_vec > SMBDIRECT_MAX_SGE) {
|
||||
cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec);
|
||||
return -ENOMEM;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sg_init_table(sgl, n_vec);
|
||||
@ -1476,6 +1476,7 @@ void smbd_destroy(struct TCP_Server_Info *server)
|
||||
info->transport_status = SMBD_DESTROYED;
|
||||
|
||||
destroy_workqueue(info->workqueue);
|
||||
log_rdma_event(INFO, "rdma session destroyed\n");
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
@ -1505,8 +1506,9 @@ create_conn:
|
||||
log_rdma_event(INFO, "creating rdma session\n");
|
||||
server->smbd_conn = smbd_get_connection(
|
||||
server, (struct sockaddr *) &server->dstaddr);
|
||||
log_rdma_event(INFO, "created rdma session info=%p\n",
|
||||
server->smbd_conn);
|
||||
|
||||
if (server->smbd_conn)
|
||||
cifs_dbg(VFS, "RDMA transport re-established\n");
|
||||
|
||||
return server->smbd_conn ? 0 : -ENOENT;
|
||||
}
|
||||
@ -1970,7 +1972,7 @@ read_rfc1002_done:
|
||||
|
||||
if (info->transport_status != SMBD_CONNECTED) {
|
||||
log_read(ERR, "disconnected\n");
|
||||
return 0;
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
goto again;
|
||||
@ -2269,12 +2271,7 @@ static void smbd_mr_recovery_work(struct work_struct *work)
|
||||
int rc;
|
||||
|
||||
list_for_each_entry(smbdirect_mr, &info->mr_list, list) {
|
||||
if (smbdirect_mr->state == MR_INVALIDATED)
|
||||
ib_dma_unmap_sg(
|
||||
info->id->device, smbdirect_mr->sgl,
|
||||
smbdirect_mr->sgl_count,
|
||||
smbdirect_mr->dir);
|
||||
else if (smbdirect_mr->state == MR_ERROR) {
|
||||
if (smbdirect_mr->state == MR_ERROR) {
|
||||
|
||||
/* recover this MR entry */
|
||||
rc = ib_dereg_mr(smbdirect_mr->mr);
|
||||
@ -2602,11 +2599,20 @@ int smbd_deregister_mr(struct smbd_mr *smbdirect_mr)
|
||||
*/
|
||||
smbdirect_mr->state = MR_INVALIDATED;
|
||||
|
||||
/*
|
||||
* Schedule the work to do MR recovery for future I/Os
|
||||
* MR recovery is slow and we don't want it to block the current I/O
|
||||
*/
|
||||
queue_work(info->workqueue, &info->mr_recovery_work);
|
||||
if (smbdirect_mr->state == MR_INVALIDATED) {
|
||||
ib_dma_unmap_sg(
|
||||
info->id->device, smbdirect_mr->sgl,
|
||||
smbdirect_mr->sgl_count,
|
||||
smbdirect_mr->dir);
|
||||
smbdirect_mr->state = MR_READY;
|
||||
if (atomic_inc_return(&info->mr_ready_count) == 1)
|
||||
wake_up_interruptible(&info->wait_mr);
|
||||
} else
|
||||
/*
|
||||
* Schedule the work to do MR recovery for future I/Os MR
|
||||
* recovery is slow and don't want it to block current I/O
|
||||
*/
|
||||
queue_work(info->workqueue, &info->mr_recovery_work);
|
||||
|
||||
done:
|
||||
if (atomic_dec_and_test(&info->mr_used_count))
|
||||
|
@ -93,8 +93,14 @@ static void _cifs_mid_q_entry_release(struct kref *refcount)
|
||||
__u16 smb_cmd = le16_to_cpu(midEntry->command);
|
||||
unsigned long now;
|
||||
unsigned long roundtrip_time;
|
||||
struct TCP_Server_Info *server = midEntry->server;
|
||||
#endif
|
||||
struct TCP_Server_Info *server = midEntry->server;
|
||||
|
||||
if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) &&
|
||||
midEntry->mid_state == MID_RESPONSE_RECEIVED &&
|
||||
server->ops->handle_cancelled_mid)
|
||||
server->ops->handle_cancelled_mid(midEntry->resp_buf, server);
|
||||
|
||||
midEntry->mid_state = MID_FREE;
|
||||
atomic_dec(&midCount);
|
||||
if (midEntry->large_buf)
|
||||
@ -319,8 +325,11 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
|
||||
int val = 1;
|
||||
__be32 rfc1002_marker;
|
||||
|
||||
if (cifs_rdma_enabled(server) && server->smbd_conn) {
|
||||
rc = smbd_send(server, num_rqst, rqst);
|
||||
if (cifs_rdma_enabled(server)) {
|
||||
/* return -EAGAIN when connecting or reconnecting */
|
||||
rc = -EAGAIN;
|
||||
if (server->smbd_conn)
|
||||
rc = smbd_send(server, num_rqst, rqst);
|
||||
goto smbd_done;
|
||||
}
|
||||
|
||||
@ -927,7 +936,8 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||
}
|
||||
|
||||
struct mid_q_entry *
|
||||
cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
|
||||
cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
|
||||
struct smb_rqst *rqst)
|
||||
{
|
||||
int rc;
|
||||
struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
|
||||
@ -999,7 +1009,18 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
server = ses->server;
|
||||
if (!ses->binding) {
|
||||
uint index = 0;
|
||||
|
||||
if (ses->chan_count > 1) {
|
||||
index = (uint)atomic_inc_return(&ses->chan_seq);
|
||||
index %= ses->chan_count;
|
||||
}
|
||||
server = ses->chans[index].server;
|
||||
} else {
|
||||
server = cifs_ses_server(ses);
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsExiting)
|
||||
return -ENOENT;
|
||||
|
||||
@ -1044,7 +1065,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||
}
|
||||
|
||||
for (i = 0; i < num_rqst; i++) {
|
||||
midQ[i] = server->ops->setup_request(ses, &rqst[i]);
|
||||
midQ[i] = server->ops->setup_request(ses, server, &rqst[i]);
|
||||
if (IS_ERR(midQ[i])) {
|
||||
revert_current_mid(server, i);
|
||||
for (j = 0; j < i; j++)
|
||||
@ -1119,8 +1140,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||
midQ[i]->mid, le16_to_cpu(midQ[i]->command));
|
||||
send_cancel(server, &rqst[i], midQ[i]);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
|
||||
if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) {
|
||||
midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
|
||||
midQ[i]->callback = cifs_cancelled_callback;
|
||||
cancelled_mid[i] = true;
|
||||
credits[i].value = 0;
|
||||
@ -1287,7 +1308,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||
|
||||
rc = allocate_mid(ses, in_buf, &midQ);
|
||||
if (rc) {
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
/* Update # of requests on wire to server */
|
||||
add_credits(server, &credits, 0);
|
||||
return rc;
|
||||
|
Loading…
Reference in New Issue
Block a user