mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-23 16:53:58 -05:00
17 cifs.ko changesets, most also for stable
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmYQirsACgkQiiy9cAdy T1EkRgv+NsMoL0tLx6Ve8wNtJNN6aEFRIXfpIIJn4dzEl6xK5UEiDNM08m8Y2ryg GzV4t7Ba1+2kYcKgBF0ANNLC1605XvvWScZNLpco5LggFq/06YLPuKSB4ygQAJpr +fvdEWeaDuzKbbJRraB1EAsJCr/4vYRM54q/cfy94uo6l3J1EnWdR467q1fkn5WQ ixM8FXUrkFxxOsrlbYoCSRZsgpQukpzTSqlm8QVQ01B7tG4qLwk/GmhqNmdf+1xs Y9RNPy1mc+tcvL2UL+Iagz5gipPwqvs+6L/jqw04UFwsS4F9w6mT5rCgevRYST0S qhz2WHXYCOHqr+wdrYNegtJ35d6F/XjrUKK54sNBEm/W2stoeukgB4EsIMGLeSE5 NJtTWNch5B342sq1xUqJ4lL9QwI3MGZSsL4mOUctMJ0xH4l42gQeRa5wecOpSU+C Tka6JLJ9+UPVAFAaDvm27xji3K6myPns6JIT2ZLnjlxIsSq4ITUCkOEtghoDQqel LZOQZAq9 =eZXs -----END PGP SIGNATURE----- Merge tag '6.9-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6 Pull smb client fixes from Steve French: - fix to retry close to avoid potential handle leaks when server returns EBUSY - DFS fixes including a fix for potential use after free - fscache fix - minor strncpy cleanup - reconnect race fix - deal with various possible UAF race conditions tearing sessions down * tag '6.9-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: smb: client: fix potential UAF in cifs_signal_cifsd_for_reconnect() smb: client: fix potential UAF in smb2_is_network_name_deleted() smb: client: fix potential UAF in is_valid_oplock_break() smb: client: fix potential UAF in smb2_is_valid_oplock_break() smb: client: fix potential UAF in smb2_is_valid_lease_break() smb: client: fix potential UAF in cifs_stats_proc_show() smb: client: fix potential UAF in cifs_stats_proc_write() smb: client: fix potential UAF in cifs_dump_full_key() smb: client: fix potential UAF in cifs_debug_files_proc_show() smb3: retrying on failed server close smb: client: serialise cifs_construct_tcon() with cifs_mount_mutex smb: client: handle DFS tcons in cifs_construct_tcon() smb: client: refresh referral without acquiring refpath_lock smb: client: guarantee refcounted children from parent session cifs: Fix caching to try to do open O_WRONLY as rdwr on server smb: client: fix UAF in smb2_reconnect_server() smb: client: replace deprecated strncpy with strscpy
This commit is contained in:
commit
119c289409
22 changed files with 371 additions and 180 deletions
|
@ -417,6 +417,7 @@ smb2_close_cached_fid(struct kref *ref)
|
|||
{
|
||||
struct cached_fid *cfid = container_of(ref, struct cached_fid,
|
||||
refcount);
|
||||
int rc;
|
||||
|
||||
spin_lock(&cfid->cfids->cfid_list_lock);
|
||||
if (cfid->on_list) {
|
||||
|
@ -430,9 +431,10 @@ smb2_close_cached_fid(struct kref *ref)
|
|||
cfid->dentry = NULL;
|
||||
|
||||
if (cfid->is_open) {
|
||||
SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
|
||||
rc = SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
|
||||
cfid->fid.volatile_fid);
|
||||
atomic_dec(&cfid->tcon->num_remote_opens);
|
||||
if (rc != -EBUSY && rc != -EAGAIN)
|
||||
atomic_dec(&cfid->tcon->num_remote_opens);
|
||||
}
|
||||
|
||||
free_cached_dir(cfid);
|
||||
|
|
|
@ -250,6 +250,8 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
|
|||
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 (cifs_ses_exiting(ses))
|
||||
continue;
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each_entry(cfile, &tcon->openFileList, tlist) {
|
||||
|
@ -676,6 +678,8 @@ static ssize_t cifs_stats_proc_write(struct file *file,
|
|||
}
|
||||
#endif /* CONFIG_CIFS_STATS2 */
|
||||
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
|
||||
if (cifs_ses_exiting(ses))
|
||||
continue;
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
|
||||
atomic_set(&tcon->num_smbs_sent, 0);
|
||||
spin_lock(&tcon->stat_lock);
|
||||
|
@ -755,6 +759,8 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
|
|||
}
|
||||
#endif /* STATS2 */
|
||||
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
|
||||
if (cifs_ses_exiting(ses))
|
||||
continue;
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
|
||||
i++;
|
||||
seq_printf(m, "\n%d) %s", i, tcon->tree_name);
|
||||
|
|
|
@ -156,6 +156,7 @@ struct workqueue_struct *decrypt_wq;
|
|||
struct workqueue_struct *fileinfo_put_wq;
|
||||
struct workqueue_struct *cifsoplockd_wq;
|
||||
struct workqueue_struct *deferredclose_wq;
|
||||
struct workqueue_struct *serverclose_wq;
|
||||
__u32 cifs_lock_secret;
|
||||
|
||||
/*
|
||||
|
@ -1888,6 +1889,13 @@ init_cifs(void)
|
|||
goto out_destroy_cifsoplockd_wq;
|
||||
}
|
||||
|
||||
serverclose_wq = alloc_workqueue("serverclose",
|
||||
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
|
||||
if (!serverclose_wq) {
|
||||
rc = -ENOMEM;
|
||||
goto out_destroy_serverclose_wq;
|
||||
}
|
||||
|
||||
rc = cifs_init_inodecache();
|
||||
if (rc)
|
||||
goto out_destroy_deferredclose_wq;
|
||||
|
@ -1962,6 +1970,8 @@ out_destroy_decrypt_wq:
|
|||
destroy_workqueue(decrypt_wq);
|
||||
out_destroy_cifsiod_wq:
|
||||
destroy_workqueue(cifsiod_wq);
|
||||
out_destroy_serverclose_wq:
|
||||
destroy_workqueue(serverclose_wq);
|
||||
out_clean_proc:
|
||||
cifs_proc_clean();
|
||||
return rc;
|
||||
|
@ -1991,6 +2001,7 @@ exit_cifs(void)
|
|||
destroy_workqueue(cifsoplockd_wq);
|
||||
destroy_workqueue(decrypt_wq);
|
||||
destroy_workqueue(fileinfo_put_wq);
|
||||
destroy_workqueue(serverclose_wq);
|
||||
destroy_workqueue(cifsiod_wq);
|
||||
cifs_proc_clean();
|
||||
}
|
||||
|
|
|
@ -442,10 +442,10 @@ struct smb_version_operations {
|
|||
/* set fid protocol-specific info */
|
||||
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
|
||||
/* close a file */
|
||||
void (*close)(const unsigned int, struct cifs_tcon *,
|
||||
int (*close)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifs_fid *);
|
||||
/* close a file, returning file attributes and timestamps */
|
||||
void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
int (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *pfile_info);
|
||||
/* send a flush request to the server */
|
||||
int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
|
||||
|
@ -1281,7 +1281,6 @@ struct cifs_tcon {
|
|||
struct cached_fids *cfids;
|
||||
/* BB add field for back pointer to sb struct(s)? */
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
struct list_head dfs_ses_list;
|
||||
struct delayed_work dfs_cache_work;
|
||||
#endif
|
||||
struct delayed_work query_interfaces; /* query interfaces workqueue job */
|
||||
|
@ -1440,6 +1439,7 @@ struct cifsFileInfo {
|
|||
bool swapfile:1;
|
||||
bool oplock_break_cancelled:1;
|
||||
bool status_file_deleted:1; /* file has been deleted */
|
||||
bool offload:1; /* offload final part of _put to a wq */
|
||||
unsigned int oplock_epoch; /* epoch from the lease break */
|
||||
__u32 oplock_level; /* oplock/lease level from the lease break */
|
||||
int count;
|
||||
|
@ -1448,6 +1448,7 @@ struct cifsFileInfo {
|
|||
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 work_struct serverclose; /* work for serverclose */
|
||||
struct delayed_work deferred;
|
||||
bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
|
||||
char *symlink_target;
|
||||
|
@ -1804,7 +1805,6 @@ struct cifs_mount_ctx {
|
|||
struct TCP_Server_Info *server;
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon;
|
||||
struct list_head dfs_ses_list;
|
||||
};
|
||||
|
||||
static inline void __free_dfs_info_param(struct dfs_info3_param *param)
|
||||
|
@ -2105,6 +2105,7 @@ extern struct workqueue_struct *decrypt_wq;
|
|||
extern struct workqueue_struct *fileinfo_put_wq;
|
||||
extern struct workqueue_struct *cifsoplockd_wq;
|
||||
extern struct workqueue_struct *deferredclose_wq;
|
||||
extern struct workqueue_struct *serverclose_wq;
|
||||
extern __u32 cifs_lock_secret;
|
||||
|
||||
extern mempool_t *cifs_sm_req_poolp;
|
||||
|
@ -2324,4 +2325,14 @@ struct smb2_compound_vars {
|
|||
struct kvec ea_iov;
|
||||
};
|
||||
|
||||
static inline bool cifs_ses_exiting(struct cifs_ses *ses)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
spin_lock(&ses->ses_lock);
|
||||
ret = ses->ses_status == SES_EXITING;
|
||||
spin_unlock(&ses->ses_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* _CIFS_GLOB_H */
|
||||
|
|
|
@ -725,31 +725,31 @@ struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
|
|||
void cifs_put_tcon_super(struct super_block *sb);
|
||||
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
|
||||
|
||||
/* Put references of @ses and @ses->dfs_root_ses */
|
||||
/* Put references of @ses and its children */
|
||||
static inline void cifs_put_smb_ses(struct cifs_ses *ses)
|
||||
{
|
||||
struct cifs_ses *rses = ses->dfs_root_ses;
|
||||
struct cifs_ses *next;
|
||||
|
||||
__cifs_put_smb_ses(ses);
|
||||
if (rses)
|
||||
__cifs_put_smb_ses(rses);
|
||||
do {
|
||||
next = ses->dfs_root_ses;
|
||||
__cifs_put_smb_ses(ses);
|
||||
} while ((ses = next));
|
||||
}
|
||||
|
||||
/* Get an active reference of @ses and @ses->dfs_root_ses.
|
||||
/* Get an active reference of @ses and its children.
|
||||
*
|
||||
* NOTE: make sure to call this function when incrementing reference count of
|
||||
* @ses to ensure that any DFS root session attached to it (@ses->dfs_root_ses)
|
||||
* will also get its reference count incremented.
|
||||
*
|
||||
* cifs_put_smb_ses() will put both references, so call it when you're done.
|
||||
* cifs_put_smb_ses() will put all references, so call it when you're done.
|
||||
*/
|
||||
static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses)
|
||||
{
|
||||
lockdep_assert_held(&cifs_tcp_ses_lock);
|
||||
|
||||
ses->ses_count++;
|
||||
if (ses->dfs_root_ses)
|
||||
ses->dfs_root_ses->ses_count++;
|
||||
for (; ses; ses = ses->dfs_root_ses)
|
||||
ses->ses_count++;
|
||||
}
|
||||
|
||||
static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)
|
||||
|
|
|
@ -5854,10 +5854,8 @@ SetEARetry:
|
|||
parm_data->list.EA_flags = 0;
|
||||
/* we checked above that name len is less than 255 */
|
||||
parm_data->list.name_len = (__u8)name_len;
|
||||
/* EA names are always ASCII */
|
||||
if (ea_name)
|
||||
strncpy(parm_data->list.name, ea_name, name_len);
|
||||
parm_data->list.name[name_len] = '\0';
|
||||
/* EA names are always ASCII and NUL-terminated */
|
||||
strscpy(parm_data->list.name, ea_name ?: "", name_len + 1);
|
||||
parm_data->list.value_len = cpu_to_le16(ea_value_len);
|
||||
/* caller ensures that ea_value_len is less than 64K but
|
||||
we need to ensure that it fits within the smb */
|
||||
|
|
|
@ -175,6 +175,8 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
|
|||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
if (cifs_ses_exiting(ses))
|
||||
continue;
|
||||
spin_lock(&ses->chan_lock);
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
if (!ses->chans[i].server)
|
||||
|
@ -232,7 +234,13 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
|
|||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
/* check if iface is still active */
|
||||
spin_lock(&ses->ses_lock);
|
||||
if (ses->ses_status == SES_EXITING) {
|
||||
spin_unlock(&ses->ses_lock);
|
||||
continue;
|
||||
}
|
||||
spin_unlock(&ses->ses_lock);
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (cifs_ses_get_chan_index(ses, server) ==
|
||||
CIFS_INVAL_CHAN_INDEX) {
|
||||
|
@ -1860,6 +1868,9 @@ static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx)
|
|||
ctx->sectype != ses->sectype)
|
||||
return 0;
|
||||
|
||||
if (ctx->dfs_root_ses != ses->dfs_root_ses)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If an existing session is limited to less channels than
|
||||
* requested, it should not be reused
|
||||
|
@ -1963,31 +1974,6 @@ out:
|
|||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* cifs_free_ipc - helper to release the session IPC tcon
|
||||
* @ses: smb session to unmount the IPC from
|
||||
*
|
||||
* Needs to be called everytime a session is destroyed.
|
||||
*
|
||||
* On session close, the IPC is closed and the server must release all tcons of the session.
|
||||
* No need to send a tree disconnect here.
|
||||
*
|
||||
* Besides, it will make the server to not close durable and resilient files on session close, as
|
||||
* specified in MS-SMB2 3.3.5.6 Receiving an SMB2 LOGOFF Request.
|
||||
*/
|
||||
static int
|
||||
cifs_free_ipc(struct cifs_ses *ses)
|
||||
{
|
||||
struct cifs_tcon *tcon = ses->tcon_ipc;
|
||||
|
||||
if (tcon == NULL)
|
||||
return 0;
|
||||
|
||||
tconInfoFree(tcon);
|
||||
ses->tcon_ipc = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cifs_ses *
|
||||
cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
||||
{
|
||||
|
@ -2019,48 +2005,52 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
|||
void __cifs_put_smb_ses(struct cifs_ses *ses)
|
||||
{
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
struct cifs_tcon *tcon;
|
||||
unsigned int xid;
|
||||
size_t i;
|
||||
bool do_logoff;
|
||||
int rc;
|
||||
|
||||
spin_lock(&ses->ses_lock);
|
||||
if (ses->ses_status == SES_EXITING) {
|
||||
spin_unlock(&ses->ses_lock);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&ses->ses_lock);
|
||||
|
||||
cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
|
||||
cifs_dbg(FYI,
|
||||
"%s: ses ipc: %s\n", __func__, ses->tcon_ipc ? ses->tcon_ipc->tree_name : "NONE");
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (--ses->ses_count > 0) {
|
||||
spin_lock(&ses->ses_lock);
|
||||
cifs_dbg(FYI, "%s: id=0x%llx ses_count=%d ses_status=%u ipc=%s\n",
|
||||
__func__, ses->Suid, ses->ses_count, ses->ses_status,
|
||||
ses->tcon_ipc ? ses->tcon_ipc->tree_name : "none");
|
||||
if (ses->ses_status == SES_EXITING || --ses->ses_count > 0) {
|
||||
spin_unlock(&ses->ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
spin_lock(&ses->ses_lock);
|
||||
if (ses->ses_status == SES_GOOD)
|
||||
ses->ses_status = SES_EXITING;
|
||||
spin_unlock(&ses->ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/* ses_count can never go negative */
|
||||
WARN_ON(ses->ses_count < 0);
|
||||
|
||||
spin_lock(&ses->ses_lock);
|
||||
if (ses->ses_status == SES_EXITING && server->ops->logoff) {
|
||||
spin_unlock(&ses->ses_lock);
|
||||
cifs_free_ipc(ses);
|
||||
spin_lock(&ses->chan_lock);
|
||||
cifs_chan_clear_need_reconnect(ses, server);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
do_logoff = ses->ses_status == SES_GOOD && server->ops->logoff;
|
||||
ses->ses_status = SES_EXITING;
|
||||
tcon = ses->tcon_ipc;
|
||||
ses->tcon_ipc = NULL;
|
||||
spin_unlock(&ses->ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/*
|
||||
* On session close, the IPC is closed and the server must release all
|
||||
* tcons of the session. No need to send a tree disconnect here.
|
||||
*
|
||||
* Besides, it will make the server to not close durable and resilient
|
||||
* files on session close, as specified in MS-SMB2 3.3.5.6 Receiving an
|
||||
* SMB2 LOGOFF Request.
|
||||
*/
|
||||
tconInfoFree(tcon);
|
||||
if (do_logoff) {
|
||||
xid = get_xid();
|
||||
rc = server->ops->logoff(xid, ses);
|
||||
if (rc)
|
||||
cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
|
||||
__func__, rc);
|
||||
_free_xid(xid);
|
||||
} else {
|
||||
spin_unlock(&ses->ses_lock);
|
||||
cifs_free_ipc(ses);
|
||||
}
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
|
@ -2373,9 +2363,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
|||
* need to lock before changing something in the session.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (ctx->dfs_root_ses)
|
||||
cifs_smb_ses_inc_refcount(ctx->dfs_root_ses);
|
||||
ses->dfs_root_ses = ctx->dfs_root_ses;
|
||||
if (ses->dfs_root_ses)
|
||||
ses->dfs_root_ses->ses_count++;
|
||||
list_add(&ses->smb_ses_list, &server->smb_ses_list);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
|
@ -3326,6 +3316,9 @@ void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx)
|
|||
cifs_put_smb_ses(mnt_ctx->ses);
|
||||
else if (mnt_ctx->server)
|
||||
cifs_put_tcp_session(mnt_ctx->server, 0);
|
||||
mnt_ctx->ses = NULL;
|
||||
mnt_ctx->tcon = NULL;
|
||||
mnt_ctx->server = NULL;
|
||||
mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
|
||||
free_xid(mnt_ctx->xid);
|
||||
}
|
||||
|
@ -3604,8 +3597,6 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
|||
bool isdfs;
|
||||
int rc;
|
||||
|
||||
INIT_LIST_HEAD(&mnt_ctx.dfs_ses_list);
|
||||
|
||||
rc = dfs_mount_share(&mnt_ctx, &isdfs);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
@ -3636,7 +3627,6 @@ out:
|
|||
return rc;
|
||||
|
||||
error:
|
||||
dfs_put_root_smb_sessions(&mnt_ctx.dfs_ses_list);
|
||||
cifs_mount_put_conns(&mnt_ctx);
|
||||
return rc;
|
||||
}
|
||||
|
@ -3651,6 +3641,18 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
|||
goto error;
|
||||
|
||||
rc = cifs_mount_get_tcon(&mnt_ctx);
|
||||
if (!rc) {
|
||||
/*
|
||||
* Prevent superblock from being created with any missing
|
||||
* connections.
|
||||
*/
|
||||
if (WARN_ON(!mnt_ctx.server))
|
||||
rc = -EHOSTDOWN;
|
||||
else if (WARN_ON(!mnt_ctx.ses))
|
||||
rc = -EACCES;
|
||||
else if (WARN_ON(!mnt_ctx.tcon))
|
||||
rc = -ENOENT;
|
||||
}
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
|
@ -3988,13 +3990,14 @@ cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
|
|||
}
|
||||
|
||||
static struct cifs_tcon *
|
||||
cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
|
||||
__cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon = NULL;
|
||||
struct smb3_fs_context *ctx;
|
||||
char *origin_fullpath = NULL;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (ctx == NULL)
|
||||
|
@ -4018,6 +4021,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
|
|||
ctx->sign = master_tcon->ses->sign;
|
||||
ctx->seal = master_tcon->seal;
|
||||
ctx->witness = master_tcon->use_witness;
|
||||
ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses;
|
||||
|
||||
rc = cifs_set_vol_auth(ctx, master_tcon->ses);
|
||||
if (rc) {
|
||||
|
@ -4037,12 +4041,39 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
|
|||
goto out;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
spin_lock(&master_tcon->tc_lock);
|
||||
if (master_tcon->origin_fullpath) {
|
||||
spin_unlock(&master_tcon->tc_lock);
|
||||
origin_fullpath = dfs_get_path(cifs_sb, cifs_sb->ctx->source);
|
||||
if (IS_ERR(origin_fullpath)) {
|
||||
tcon = ERR_CAST(origin_fullpath);
|
||||
origin_fullpath = NULL;
|
||||
cifs_put_smb_ses(ses);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
spin_unlock(&master_tcon->tc_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
tcon = cifs_get_tcon(ses, ctx);
|
||||
if (IS_ERR(tcon)) {
|
||||
cifs_put_smb_ses(ses);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
if (origin_fullpath) {
|
||||
spin_lock(&tcon->tc_lock);
|
||||
tcon->origin_fullpath = origin_fullpath;
|
||||
spin_unlock(&tcon->tc_lock);
|
||||
origin_fullpath = NULL;
|
||||
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
|
||||
dfs_cache_get_ttl() * HZ);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
if (cap_unix(ses))
|
||||
reset_cifs_unix_caps(0, tcon, NULL, ctx);
|
||||
|
@ -4051,11 +4082,23 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
|
|||
out:
|
||||
kfree(ctx->username);
|
||||
kfree_sensitive(ctx->password);
|
||||
kfree(origin_fullpath);
|
||||
kfree(ctx);
|
||||
|
||||
return tcon;
|
||||
}
|
||||
|
||||
static struct cifs_tcon *
|
||||
cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
|
||||
{
|
||||
struct cifs_tcon *ret;
|
||||
|
||||
cifs_mount_lock();
|
||||
ret = __cifs_construct_tcon(cifs_sb, fsuid);
|
||||
cifs_mount_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct cifs_tcon *
|
||||
cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
|
|
|
@ -66,33 +66,20 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
|
|||
}
|
||||
|
||||
/*
|
||||
* Track individual DFS referral servers used by new DFS mount.
|
||||
*
|
||||
* On success, their lifetime will be shared by final tcon (dfs_ses_list).
|
||||
* Otherwise, they will be put by dfs_put_root_smb_sessions() in cifs_mount().
|
||||
* Get an active reference of @ses so that next call to cifs_put_tcon() won't
|
||||
* release it as any new DFS referrals must go through its IPC tcon.
|
||||
*/
|
||||
static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
|
||||
static void add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
|
||||
{
|
||||
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
|
||||
struct dfs_root_ses *root_ses;
|
||||
struct cifs_ses *ses = mnt_ctx->ses;
|
||||
|
||||
if (ses) {
|
||||
root_ses = kmalloc(sizeof(*root_ses), GFP_KERNEL);
|
||||
if (!root_ses)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&root_ses->list);
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
cifs_smb_ses_inc_refcount(ses);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
root_ses->ses = ses;
|
||||
list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list);
|
||||
}
|
||||
/* Select new DFS referral server so that new referrals go through it */
|
||||
ctx->dfs_root_ses = ses;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parse_dfs_target(struct smb3_fs_context *ctx,
|
||||
|
@ -185,11 +172,8 @@ again:
|
|||
continue;
|
||||
}
|
||||
|
||||
if (is_refsrv) {
|
||||
rc = add_root_smb_session(mnt_ctx);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
if (is_refsrv)
|
||||
add_root_smb_session(mnt_ctx);
|
||||
|
||||
rc = ref_walk_advance(rw);
|
||||
if (!rc) {
|
||||
|
@ -232,6 +216,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
|
|||
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
|
||||
struct cifs_tcon *tcon;
|
||||
char *origin_fullpath;
|
||||
bool new_tcon = true;
|
||||
int rc;
|
||||
|
||||
origin_fullpath = dfs_get_path(cifs_sb, ctx->source);
|
||||
|
@ -239,6 +224,18 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
|
|||
return PTR_ERR(origin_fullpath);
|
||||
|
||||
rc = dfs_referral_walk(mnt_ctx);
|
||||
if (!rc) {
|
||||
/*
|
||||
* Prevent superblock from being created with any missing
|
||||
* connections.
|
||||
*/
|
||||
if (WARN_ON(!mnt_ctx->server))
|
||||
rc = -EHOSTDOWN;
|
||||
else if (WARN_ON(!mnt_ctx->ses))
|
||||
rc = -EACCES;
|
||||
else if (WARN_ON(!mnt_ctx->tcon))
|
||||
rc = -ENOENT;
|
||||
}
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
|
@ -247,15 +244,14 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
|
|||
if (!tcon->origin_fullpath) {
|
||||
tcon->origin_fullpath = origin_fullpath;
|
||||
origin_fullpath = NULL;
|
||||
} else {
|
||||
new_tcon = false;
|
||||
}
|
||||
spin_unlock(&tcon->tc_lock);
|
||||
|
||||
if (list_empty(&tcon->dfs_ses_list)) {
|
||||
list_replace_init(&mnt_ctx->dfs_ses_list, &tcon->dfs_ses_list);
|
||||
if (new_tcon) {
|
||||
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
|
||||
dfs_cache_get_ttl() * HZ);
|
||||
} else {
|
||||
dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list);
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -298,7 +294,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
ctx->dfs_root_ses = mnt_ctx->ses;
|
||||
/*
|
||||
* If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally
|
||||
* try to get an DFS referral (even cached) to determine whether it is an DFS mount.
|
||||
|
@ -324,7 +319,9 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
|
|||
|
||||
*isdfs = true;
|
||||
add_root_smb_session(mnt_ctx);
|
||||
return __dfs_mount_share(mnt_ctx);
|
||||
rc = __dfs_mount_share(mnt_ctx);
|
||||
dfs_put_root_smb_sessions(mnt_ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Update dfs referral path of superblock */
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#define _CIFS_DFS_H
|
||||
|
||||
#include "cifsglob.h"
|
||||
#include "cifsproto.h"
|
||||
#include "fs_context.h"
|
||||
#include "dfs_cache.h"
|
||||
#include "cifs_unicode.h"
|
||||
#include <linux/namei.h>
|
||||
|
||||
|
@ -114,11 +116,6 @@ static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw)
|
|||
ref_walk_tit(rw));
|
||||
}
|
||||
|
||||
struct dfs_root_ses {
|
||||
struct list_head list;
|
||||
struct cifs_ses *ses;
|
||||
};
|
||||
|
||||
int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
|
||||
struct smb3_fs_context *ctx);
|
||||
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs);
|
||||
|
@ -133,20 +130,32 @@ static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *p
|
|||
{
|
||||
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
|
||||
struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
|
||||
struct cifs_ses *rses = ctx->dfs_root_ses ?: mnt_ctx->ses;
|
||||
|
||||
return dfs_cache_find(mnt_ctx->xid, ctx->dfs_root_ses, cifs_sb->local_nls,
|
||||
return dfs_cache_find(mnt_ctx->xid, rses, cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb), path, ref, tl);
|
||||
}
|
||||
|
||||
static inline void dfs_put_root_smb_sessions(struct list_head *head)
|
||||
/*
|
||||
* cifs_get_smb_ses() already guarantees an active reference of
|
||||
* @ses->dfs_root_ses when a new session is created, so we need to put extra
|
||||
* references of all DFS root sessions that were used across the mount process
|
||||
* in dfs_mount_share().
|
||||
*/
|
||||
static inline void dfs_put_root_smb_sessions(struct cifs_mount_ctx *mnt_ctx)
|
||||
{
|
||||
struct dfs_root_ses *root, *tmp;
|
||||
const struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
|
||||
struct cifs_ses *ses = ctx->dfs_root_ses;
|
||||
struct cifs_ses *cur;
|
||||
|
||||
list_for_each_entry_safe(root, tmp, head, list) {
|
||||
list_del_init(&root->list);
|
||||
cifs_put_smb_ses(root->ses);
|
||||
kfree(root);
|
||||
if (!ses)
|
||||
return;
|
||||
|
||||
for (cur = ses; cur; cur = cur->dfs_root_ses) {
|
||||
if (cur->dfs_root_ses)
|
||||
cifs_put_smb_ses(cur->dfs_root_ses);
|
||||
}
|
||||
cifs_put_smb_ses(ses);
|
||||
}
|
||||
|
||||
#endif /* _CIFS_DFS_H */
|
||||
|
|
|
@ -1172,8 +1172,8 @@ static bool is_ses_good(struct cifs_ses *ses)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Refresh dfs referral of tcon and mark it for reconnect if needed */
|
||||
static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_refresh)
|
||||
/* Refresh dfs referral of @ses and mark it for reconnect if needed */
|
||||
static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh)
|
||||
{
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
DFS_CACHE_TGT_LIST(old_tl);
|
||||
|
@ -1181,10 +1181,21 @@ static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_ref
|
|||
bool needs_refresh = false;
|
||||
struct cache_entry *ce;
|
||||
unsigned int xid;
|
||||
char *path = NULL;
|
||||
int rc = 0;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
mutex_lock(&server->refpath_lock);
|
||||
if (server->leaf_fullpath) {
|
||||
path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC);
|
||||
if (!path)
|
||||
rc = -ENOMEM;
|
||||
}
|
||||
mutex_unlock(&server->refpath_lock);
|
||||
if (!path)
|
||||
goto out;
|
||||
|
||||
down_read(&htable_rw_lock);
|
||||
ce = lookup_cache_entry(path);
|
||||
needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce);
|
||||
|
@ -1218,19 +1229,17 @@ out:
|
|||
free_xid(xid);
|
||||
dfs_cache_free_tgts(&old_tl);
|
||||
dfs_cache_free_tgts(&new_tl);
|
||||
return rc;
|
||||
kfree(path);
|
||||
}
|
||||
|
||||
static int refresh_tcon(struct cifs_tcon *tcon, bool force_refresh)
|
||||
static inline void refresh_ses_referral(struct cifs_ses *ses)
|
||||
{
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
struct cifs_ses *ses = tcon->ses;
|
||||
__refresh_ses_referral(ses, false);
|
||||
}
|
||||
|
||||
mutex_lock(&server->refpath_lock);
|
||||
if (server->leaf_fullpath)
|
||||
__refresh_tcon(server->leaf_fullpath + 1, ses, force_refresh);
|
||||
mutex_unlock(&server->refpath_lock);
|
||||
return 0;
|
||||
static inline void force_refresh_ses_referral(struct cifs_ses *ses)
|
||||
{
|
||||
__refresh_ses_referral(ses, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1271,34 +1280,20 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
|
|||
*/
|
||||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
|
||||
|
||||
return refresh_tcon(tcon, true);
|
||||
force_refresh_ses_referral(tcon->ses);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Refresh all DFS referrals related to DFS tcon */
|
||||
void dfs_cache_refresh(struct work_struct *work)
|
||||
{
|
||||
struct TCP_Server_Info *server;
|
||||
struct dfs_root_ses *rses;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifs_ses *ses;
|
||||
|
||||
tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work);
|
||||
ses = tcon->ses;
|
||||
server = ses->server;
|
||||
|
||||
mutex_lock(&server->refpath_lock);
|
||||
if (server->leaf_fullpath)
|
||||
__refresh_tcon(server->leaf_fullpath + 1, ses, false);
|
||||
mutex_unlock(&server->refpath_lock);
|
||||
|
||||
list_for_each_entry(rses, &tcon->dfs_ses_list, list) {
|
||||
ses = rses->ses;
|
||||
server = ses->server;
|
||||
mutex_lock(&server->refpath_lock);
|
||||
if (server->leaf_fullpath)
|
||||
__refresh_tcon(server->leaf_fullpath + 1, ses, false);
|
||||
mutex_unlock(&server->refpath_lock);
|
||||
}
|
||||
for (ses = tcon->ses; ses; ses = ses->dfs_root_ses)
|
||||
refresh_ses_referral(ses);
|
||||
|
||||
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
|
||||
atomic_read(&dfs_cache_ttl) * HZ);
|
||||
|
|
|
@ -189,6 +189,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
|
|||
int disposition;
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
struct cifs_open_parms oparms;
|
||||
int rdwr_for_fscache = 0;
|
||||
|
||||
*oplock = 0;
|
||||
if (tcon->ses->server->oplocks)
|
||||
|
@ -200,6 +201,10 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
|
|||
return PTR_ERR(full_path);
|
||||
}
|
||||
|
||||
/* If we're caching, we need to be able to fill in around partial writes. */
|
||||
if (cifs_fscache_enabled(inode) && (oflags & O_ACCMODE) == O_WRONLY)
|
||||
rdwr_for_fscache = 1;
|
||||
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
|
||||
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
||||
|
@ -276,6 +281,8 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
|
|||
desired_access |= GENERIC_READ; /* is this too little? */
|
||||
if (OPEN_FMODE(oflags) & FMODE_WRITE)
|
||||
desired_access |= GENERIC_WRITE;
|
||||
if (rdwr_for_fscache == 1)
|
||||
desired_access |= GENERIC_READ;
|
||||
|
||||
disposition = FILE_OVERWRITE_IF;
|
||||
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
|
||||
|
@ -304,6 +311,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
|
|||
if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
|
||||
create_options |= CREATE_OPTION_READONLY;
|
||||
|
||||
retry_open:
|
||||
oparms = (struct cifs_open_parms) {
|
||||
.tcon = tcon,
|
||||
.cifs_sb = cifs_sb,
|
||||
|
@ -317,8 +325,15 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
|
|||
rc = server->ops->open(xid, &oparms, oplock, buf);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc);
|
||||
if (rc == -EACCES && rdwr_for_fscache == 1) {
|
||||
desired_access &= ~GENERIC_READ;
|
||||
rdwr_for_fscache = 2;
|
||||
goto retry_open;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
if (rdwr_for_fscache == 2)
|
||||
cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
|
||||
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
/*
|
||||
|
|
|
@ -206,12 +206,12 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
|
|||
*/
|
||||
}
|
||||
|
||||
static inline int cifs_convert_flags(unsigned int flags)
|
||||
static inline int cifs_convert_flags(unsigned int flags, int rdwr_for_fscache)
|
||||
{
|
||||
if ((flags & O_ACCMODE) == O_RDONLY)
|
||||
return GENERIC_READ;
|
||||
else if ((flags & O_ACCMODE) == O_WRONLY)
|
||||
return GENERIC_WRITE;
|
||||
return rdwr_for_fscache == 1 ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE;
|
||||
else if ((flags & O_ACCMODE) == O_RDWR) {
|
||||
/* GENERIC_ALL is too much permission to request
|
||||
can cause unnecessary access denied on create */
|
||||
|
@ -348,11 +348,16 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
|
|||
int create_options = CREATE_NOT_DIR;
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
struct cifs_open_parms oparms;
|
||||
int rdwr_for_fscache = 0;
|
||||
|
||||
if (!server->ops->open)
|
||||
return -ENOSYS;
|
||||
|
||||
desired_access = cifs_convert_flags(f_flags);
|
||||
/* If we're caching, we need to be able to fill in around partial writes. */
|
||||
if (cifs_fscache_enabled(inode) && (f_flags & O_ACCMODE) == O_WRONLY)
|
||||
rdwr_for_fscache = 1;
|
||||
|
||||
desired_access = cifs_convert_flags(f_flags, rdwr_for_fscache);
|
||||
|
||||
/*********************************************************************
|
||||
* open flag mapping table:
|
||||
|
@ -389,6 +394,7 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
|
|||
if (f_flags & O_DIRECT)
|
||||
create_options |= CREATE_NO_BUFFER;
|
||||
|
||||
retry_open:
|
||||
oparms = (struct cifs_open_parms) {
|
||||
.tcon = tcon,
|
||||
.cifs_sb = cifs_sb,
|
||||
|
@ -400,8 +406,16 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
|
|||
};
|
||||
|
||||
rc = server->ops->open(xid, &oparms, oplock, buf);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
if (rc == -EACCES && rdwr_for_fscache == 1) {
|
||||
desired_access = cifs_convert_flags(f_flags, 0);
|
||||
rdwr_for_fscache = 2;
|
||||
goto retry_open;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
if (rdwr_for_fscache == 2)
|
||||
cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
|
||||
|
||||
/* TODO: Add support for calling posix query info but with passing in fid */
|
||||
if (tcon->unix_ext)
|
||||
|
@ -445,6 +459,7 @@ cifs_down_write(struct rw_semaphore *sem)
|
|||
}
|
||||
|
||||
static void cifsFileInfo_put_work(struct work_struct *work);
|
||||
void serverclose_work(struct work_struct *work);
|
||||
|
||||
struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
struct tcon_link *tlink, __u32 oplock,
|
||||
|
@ -491,6 +506,7 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
|||
cfile->tlink = cifs_get_tlink(tlink);
|
||||
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
|
||||
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
|
||||
INIT_WORK(&cfile->serverclose, serverclose_work);
|
||||
INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close);
|
||||
mutex_init(&cfile->fh_mutex);
|
||||
spin_lock_init(&cfile->file_info_lock);
|
||||
|
@ -582,6 +598,40 @@ static void cifsFileInfo_put_work(struct work_struct *work)
|
|||
cifsFileInfo_put_final(cifs_file);
|
||||
}
|
||||
|
||||
void serverclose_work(struct work_struct *work)
|
||||
{
|
||||
struct cifsFileInfo *cifs_file = container_of(work,
|
||||
struct cifsFileInfo, serverclose);
|
||||
|
||||
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
|
||||
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
int rc = 0;
|
||||
int retries = 0;
|
||||
int MAX_RETRIES = 4;
|
||||
|
||||
do {
|
||||
if (server->ops->close_getattr)
|
||||
rc = server->ops->close_getattr(0, tcon, cifs_file);
|
||||
else if (server->ops->close)
|
||||
rc = server->ops->close(0, tcon, &cifs_file->fid);
|
||||
|
||||
if (rc == -EBUSY || rc == -EAGAIN) {
|
||||
retries++;
|
||||
msleep(250);
|
||||
}
|
||||
} while ((rc == -EBUSY || rc == -EAGAIN) && (retries < MAX_RETRIES)
|
||||
);
|
||||
|
||||
if (retries == MAX_RETRIES)
|
||||
pr_warn("Serverclose failed %d times, giving up\n", MAX_RETRIES);
|
||||
|
||||
if (cifs_file->offload)
|
||||
queue_work(fileinfo_put_wq, &cifs_file->put);
|
||||
else
|
||||
cifsFileInfo_put_final(cifs_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* cifsFileInfo_put - release a reference of file priv data
|
||||
*
|
||||
|
@ -622,10 +672,13 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
|
|||
struct cifs_fid fid = {};
|
||||
struct cifs_pending_open open;
|
||||
bool oplock_break_cancelled;
|
||||
bool serverclose_offloaded = false;
|
||||
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
spin_lock(&cifsi->open_file_lock);
|
||||
spin_lock(&cifs_file->file_info_lock);
|
||||
|
||||
cifs_file->offload = offload;
|
||||
if (--cifs_file->count > 0) {
|
||||
spin_unlock(&cifs_file->file_info_lock);
|
||||
spin_unlock(&cifsi->open_file_lock);
|
||||
|
@ -667,13 +720,20 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
|
|||
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
unsigned int xid;
|
||||
int rc = 0;
|
||||
|
||||
xid = get_xid();
|
||||
if (server->ops->close_getattr)
|
||||
server->ops->close_getattr(xid, tcon, cifs_file);
|
||||
rc = server->ops->close_getattr(xid, tcon, cifs_file);
|
||||
else if (server->ops->close)
|
||||
server->ops->close(xid, tcon, &cifs_file->fid);
|
||||
rc = server->ops->close(xid, tcon, &cifs_file->fid);
|
||||
_free_xid(xid);
|
||||
|
||||
if (rc == -EBUSY || rc == -EAGAIN) {
|
||||
// Server close failed, hence offloading it as an async op
|
||||
queue_work(serverclose_wq, &cifs_file->serverclose);
|
||||
serverclose_offloaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (oplock_break_cancelled)
|
||||
|
@ -681,10 +741,15 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
|
|||
|
||||
cifs_del_pending_open(&open);
|
||||
|
||||
if (offload)
|
||||
queue_work(fileinfo_put_wq, &cifs_file->put);
|
||||
else
|
||||
cifsFileInfo_put_final(cifs_file);
|
||||
// if serverclose has been offloaded to wq (on failure), it will
|
||||
// handle offloading put as well. If serverclose not offloaded,
|
||||
// we need to handle offloading put here.
|
||||
if (!serverclose_offloaded) {
|
||||
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)
|
||||
|
@ -834,11 +899,11 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||
use_cache:
|
||||
fscache_use_cookie(cifs_inode_cookie(file_inode(file)),
|
||||
file->f_mode & FMODE_WRITE);
|
||||
if (file->f_flags & O_DIRECT &&
|
||||
(!((file->f_flags & O_ACCMODE) != O_RDONLY) ||
|
||||
file->f_flags & O_APPEND))
|
||||
cifs_invalidate_cache(file_inode(file),
|
||||
FSCACHE_INVAL_DIO_WRITE);
|
||||
if (!(file->f_flags & O_DIRECT))
|
||||
goto out;
|
||||
if ((file->f_flags & (O_ACCMODE | O_APPEND)) == O_RDONLY)
|
||||
goto out;
|
||||
cifs_invalidate_cache(file_inode(file), FSCACHE_INVAL_DIO_WRITE);
|
||||
|
||||
out:
|
||||
free_dentry_path(page);
|
||||
|
@ -903,6 +968,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
|||
int disposition = FILE_OPEN;
|
||||
int create_options = CREATE_NOT_DIR;
|
||||
struct cifs_open_parms oparms;
|
||||
int rdwr_for_fscache = 0;
|
||||
|
||||
xid = get_xid();
|
||||
mutex_lock(&cfile->fh_mutex);
|
||||
|
@ -966,7 +1032,11 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
|||
}
|
||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||
|
||||
desired_access = cifs_convert_flags(cfile->f_flags);
|
||||
/* If we're caching, we need to be able to fill in around partial writes. */
|
||||
if (cifs_fscache_enabled(inode) && (cfile->f_flags & O_ACCMODE) == O_WRONLY)
|
||||
rdwr_for_fscache = 1;
|
||||
|
||||
desired_access = cifs_convert_flags(cfile->f_flags, rdwr_for_fscache);
|
||||
|
||||
/* O_SYNC also has bit for O_DSYNC so following check picks up either */
|
||||
if (cfile->f_flags & O_SYNC)
|
||||
|
@ -978,6 +1048,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
|||
if (server->ops->get_lease_key)
|
||||
server->ops->get_lease_key(inode, &cfile->fid);
|
||||
|
||||
retry_open:
|
||||
oparms = (struct cifs_open_parms) {
|
||||
.tcon = tcon,
|
||||
.cifs_sb = cifs_sb,
|
||||
|
@ -1003,6 +1074,11 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
|||
/* indicate that we need to relock the file */
|
||||
oparms.reconnect = true;
|
||||
}
|
||||
if (rc == -EACCES && rdwr_for_fscache == 1) {
|
||||
desired_access = cifs_convert_flags(cfile->f_flags, 0);
|
||||
rdwr_for_fscache = 2;
|
||||
goto retry_open;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
mutex_unlock(&cfile->fh_mutex);
|
||||
|
@ -1011,6 +1087,9 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
|||
goto reopen_error_exit;
|
||||
}
|
||||
|
||||
if (rdwr_for_fscache == 2)
|
||||
cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
|
||||
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
reopen_success:
|
||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include "rfc1002pdu.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
static DEFINE_MUTEX(cifs_mount_mutex);
|
||||
DEFINE_MUTEX(cifs_mount_mutex);
|
||||
|
||||
static const match_table_t cifs_smb_version_tokens = {
|
||||
{ Smb_1, SMB1_VERSION_STRING },
|
||||
|
@ -783,9 +783,9 @@ static int smb3_get_tree(struct fs_context *fc)
|
|||
|
||||
if (err)
|
||||
return err;
|
||||
mutex_lock(&cifs_mount_mutex);
|
||||
cifs_mount_lock();
|
||||
ret = smb3_get_tree_common(fc);
|
||||
mutex_unlock(&cifs_mount_mutex);
|
||||
cifs_mount_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -304,4 +304,16 @@ extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb);
|
|||
#define MAX_CACHED_FIDS 16
|
||||
extern char *cifs_sanitize_prepath(char *prepath, gfp_t gfp);
|
||||
|
||||
extern struct mutex cifs_mount_mutex;
|
||||
|
||||
static inline void cifs_mount_lock(void)
|
||||
{
|
||||
mutex_lock(&cifs_mount_mutex);
|
||||
}
|
||||
|
||||
static inline void cifs_mount_unlock(void)
|
||||
{
|
||||
mutex_unlock(&cifs_mount_mutex);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -109,6 +109,11 @@ static inline void cifs_readahead_to_fscache(struct inode *inode,
|
|||
__cifs_readahead_to_fscache(inode, pos, len);
|
||||
}
|
||||
|
||||
static inline bool cifs_fscache_enabled(struct inode *inode)
|
||||
{
|
||||
return fscache_cookie_enabled(cifs_inode_cookie(inode));
|
||||
}
|
||||
|
||||
#else /* CONFIG_CIFS_FSCACHE */
|
||||
static inline
|
||||
void cifs_fscache_fill_coherency(struct inode *inode,
|
||||
|
@ -124,6 +129,7 @@ static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {}
|
|||
static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {}
|
||||
static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; }
|
||||
static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) {}
|
||||
static inline bool cifs_fscache_enabled(struct inode *inode) { return false; }
|
||||
|
||||
static inline int cifs_fscache_query_occupancy(struct inode *inode,
|
||||
pgoff_t first, unsigned int nr_pages,
|
||||
|
|
|
@ -247,7 +247,9 @@ static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug
|
|||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) {
|
||||
list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) {
|
||||
if (ses_it->Suid == out.session_id) {
|
||||
spin_lock(&ses_it->ses_lock);
|
||||
if (ses_it->ses_status != SES_EXITING &&
|
||||
ses_it->Suid == out.session_id) {
|
||||
ses = ses_it;
|
||||
/*
|
||||
* since we are using the session outside the crit
|
||||
|
@ -255,9 +257,11 @@ static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug
|
|||
* so increment its refcount
|
||||
*/
|
||||
cifs_smb_ses_inc_refcount(ses);
|
||||
spin_unlock(&ses_it->ses_lock);
|
||||
found = true;
|
||||
goto search_end;
|
||||
}
|
||||
spin_unlock(&ses_it->ses_lock);
|
||||
}
|
||||
}
|
||||
search_end:
|
||||
|
|
|
@ -138,9 +138,6 @@ tcon_info_alloc(bool dir_leases_enabled)
|
|||
atomic_set(&ret_buf->num_local_opens, 0);
|
||||
atomic_set(&ret_buf->num_remote_opens, 0);
|
||||
ret_buf->stats_from_time = ktime_get_real_seconds();
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
INIT_LIST_HEAD(&ret_buf->dfs_ses_list);
|
||||
#endif
|
||||
|
||||
return ret_buf;
|
||||
}
|
||||
|
@ -156,9 +153,6 @@ tconInfoFree(struct cifs_tcon *tcon)
|
|||
atomic_dec(&tconInfoAllocCount);
|
||||
kfree(tcon->nativeFileSystem);
|
||||
kfree_sensitive(tcon->password);
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
dfs_put_root_smb_sessions(&tcon->dfs_ses_list);
|
||||
#endif
|
||||
kfree(tcon->origin_fullpath);
|
||||
kfree(tcon);
|
||||
}
|
||||
|
@ -487,6 +481,8 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
|||
/* look up tcon based on tid & uid */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
if (cifs_ses_exiting(ses))
|
||||
continue;
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
|
||||
if (tcon->tid != buf->Tid)
|
||||
continue;
|
||||
|
|
|
@ -753,11 +753,11 @@ cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
|
|||
cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_fid *fid)
|
||||
{
|
||||
CIFSSMBClose(xid, tcon, fid->netfid);
|
||||
return CIFSSMBClose(xid, tcon, fid->netfid);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -622,6 +622,8 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
|
|||
/* look up tcon based on tid & uid */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
if (cifs_ses_exiting(ses))
|
||||
continue;
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
cifs_stats_inc(
|
||||
|
@ -697,6 +699,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
|||
/* look up tcon based on tid & uid */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
if (cifs_ses_exiting(ses))
|
||||
continue;
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
|
||||
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
|
|
|
@ -1412,14 +1412,14 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
|
|||
memcpy(cfile->fid.create_guid, fid->create_guid, 16);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_fid *fid)
|
||||
{
|
||||
SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
||||
return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile)
|
||||
{
|
||||
|
@ -1430,7 +1430,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, &file_inf);
|
||||
if (rc)
|
||||
return;
|
||||
return rc;
|
||||
|
||||
inode = d_inode(cfile->dentry);
|
||||
|
||||
|
@ -1459,6 +1459,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
|
||||
/* End of file and Attributes should not have to be updated on close */
|
||||
spin_unlock(&inode->i_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -2480,6 +2481,8 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
|
|||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
if (cifs_ses_exiting(ses))
|
||||
continue;
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
|
||||
if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) {
|
||||
spin_lock(&tcon->tc_lock);
|
||||
|
@ -3913,7 +3916,7 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
|
|||
strcat(message, "W");
|
||||
}
|
||||
if (!new_oplock)
|
||||
strncpy(message, "None", sizeof(message));
|
||||
strscpy(message, "None");
|
||||
|
||||
cinode->oplock = new_oplock;
|
||||
cifs_dbg(FYI, "%s Lease granted on inode %p\n", message,
|
||||
|
|
|
@ -3628,9 +3628,9 @@ replay_again:
|
|||
memcpy(&pbuf->network_open_info,
|
||||
&rsp->network_open_info,
|
||||
sizeof(pbuf->network_open_info));
|
||||
atomic_dec(&tcon->num_remote_opens);
|
||||
}
|
||||
|
||||
atomic_dec(&tcon->num_remote_opens);
|
||||
close_exit:
|
||||
SMB2_close_free(&rqst);
|
||||
free_rsp_buf(resp_buftype, rsp);
|
||||
|
|
|
@ -659,7 +659,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
|||
}
|
||||
spin_unlock(&server->srv_lock);
|
||||
if (!is_binding && !server->session_estab) {
|
||||
strncpy(shdr->Signature, "BSRSPYL", 8);
|
||||
strscpy(shdr->Signature, "BSRSPYL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue