22 SMB3 client fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmdKXd4ACgkQiiy9cAdy
 T1FjCwv/f3lb79FaVhv18LAvayqoj+iWaZg6yYepZxp2hyMTm59i5I1t7Ar92VES
 2+A3XmdeK5U6y9JjMFYHY4B720JumBDTJ/FPgUPZtZqCHSnYZCm5MUQtH85VSisf
 /4Ua5kv5tzCuu9UvoCp3UFcsbCCrXH8JYTBGR2EKfIHqN7ae1F80pLgBxEnBuuw9
 rRx0RKxKb9CkYB8GDOHi60hF03DRlXdMa7I/aJ6dtyhZkzoDgc0PqndFQuwLMY07
 f09rnXnDvHqqC+SFHprqE6V8uWl77IX6lzZ4PCz52dsm6Y/BKD22fBQA3w4zz/0s
 nMvxbEaxcealSq08pTn7zWVi0tw7Ku35c0plXCtjzS4UmcouMdwI6SP986IqEH+C
 0la/mDnPk36EQYdd1yYVYAcbW9VrmiYs23PvKfH4Hj6JvQeZ872RnMYZPPl+jJh6
 Gazdn3yWRiKRuTcO42eC+JKaoJKG/JBqkd74WBe85Q0yVNf2m0CJEICfAcWDlK/Z
 fIl3WxjN
 =dfo/
 -----END PGP SIGNATURE-----

Merge tag '6.13-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client updates from Steve French:

 - directory lease fixes

 - password rotation fixes

 - reconnect fix

 - fix for SMB3.02 mounts

 - DFS (global namespace) fixes

 - fixes for special file handling (most relating to better handling
   various types of symlinks)

 - two minor cleanups

* tag '6.13-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: (22 commits)
  cifs: update internal version number
  cifs: unlock on error in smb3_reconfigure()
  cifs: during remount, make sure passwords are in sync
  cifs: support mounting with alternate password to allow password rotation
  smb: Initialize cfid->tcon before performing network ops
  smb: During unmount, ensure all cached dir instances drop their dentry
  smb: client: fix noisy message when mounting shares
  smb: client: don't try following DFS links in cifs_tree_connect()
  smb: client: allow reconnect when sending ioctl
  smb: client: get rid of @nlsc param in cifs_tree_connect()
  smb: client: allow more DFS referrals to be cached
  cifs: Fix parsing reparse point with native symlink in SMB1 non-UNICODE session
  cifs: Validate content of WSL reparse point buffers
  cifs: Improve guard for excluding $LXDEV xattr
  cifs: Add support for parsing WSL-style symlinks
  cifs: Validate content of native symlink
  cifs: Fix parsing native symlinks relative to the export
  smb: client: fix NULL ptr deref in crypto_aead_setkey()
  Update misleading comment in cifs_chan_update_iface
  smb: client: change return value in open_cached_dir_by_dentry() if !cfids
  ...
This commit is contained in:
Linus Torvalds 2024-11-30 10:14:42 -08:00
commit 0235da0fae
24 changed files with 523 additions and 297 deletions

View File

@ -17,6 +17,11 @@ static void free_cached_dir(struct cached_fid *cfid);
static void smb2_close_cached_fid(struct kref *ref);
static void cfids_laundromat_worker(struct work_struct *work);
struct cached_dir_dentry {
struct list_head entry;
struct dentry *dentry;
};
static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
const char *path,
bool lookup_only,
@ -157,15 +162,17 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
const char *npath;
int retries = 0, cur_sleep = 1;
if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache ||
is_smb1_server(tcon->ses->server) || (dir_cache_timeout == 0))
if (cifs_sb->root == NULL)
return -ENOENT;
if (tcon == NULL)
return -EOPNOTSUPP;
ses = tcon->ses;
cfids = tcon->cfids;
if (cifs_sb->root == NULL)
return -ENOENT;
if (cfids == NULL)
return -EOPNOTSUPP;
replay_again:
/* reinitialize for possible replay */
@ -222,6 +229,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
}
}
cfid->dentry = dentry;
cfid->tcon = tcon;
/*
* We do not hold the lock for the open because in case
@ -293,7 +301,6 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
}
goto oshr_free;
}
cfid->tcon = tcon;
cfid->is_open = true;
spin_lock(&cfids->cfid_list_lock);
@ -389,7 +396,7 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
struct cached_fids *cfids = tcon->cfids;
if (cfids == NULL)
return -ENOENT;
return -EOPNOTSUPP;
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry(cfid, &cfids->entries, entry) {
@ -470,7 +477,10 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
struct cifs_tcon *tcon;
struct tcon_link *tlink;
struct cached_fids *cfids;
struct cached_dir_dentry *tmp_list, *q;
LIST_HEAD(entry);
spin_lock(&cifs_sb->tlink_tree_lock);
for (node = rb_first(root); node; node = rb_next(node)) {
tlink = rb_entry(node, struct tcon_link, tl_rbnode);
tcon = tlink_tcon(tlink);
@ -479,11 +489,30 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
cfids = tcon->cfids;
if (cfids == NULL)
continue;
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry(cfid, &cfids->entries, entry) {
dput(cfid->dentry);
tmp_list = kmalloc(sizeof(*tmp_list), GFP_ATOMIC);
if (tmp_list == NULL)
break;
spin_lock(&cfid->fid_lock);
tmp_list->dentry = cfid->dentry;
cfid->dentry = NULL;
spin_unlock(&cfid->fid_lock);
list_add_tail(&tmp_list->entry, &entry);
}
spin_unlock(&cfids->cfid_list_lock);
}
spin_unlock(&cifs_sb->tlink_tree_lock);
list_for_each_entry_safe(tmp_list, q, &entry, entry) {
list_del(&tmp_list->entry);
dput(tmp_list->dentry);
kfree(tmp_list);
}
/* Flush any pending work that will drop dentries */
flush_workqueue(cfid_put_wq);
}
/*
@ -494,14 +523,18 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
{
struct cached_fids *cfids = tcon->cfids;
struct cached_fid *cfid, *q;
LIST_HEAD(entry);
if (cfids == NULL)
return;
/*
* Mark all the cfids as closed, and move them to the cfids->dying list.
* They'll be cleaned up later by cfids_invalidation_worker. Take
* a reference to each cfid during this process.
*/
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
list_move(&cfid->entry, &entry);
list_move(&cfid->entry, &cfids->dying);
cfids->num_entries--;
cfid->is_open = false;
cfid->on_list = false;
@ -514,26 +547,47 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
} else
kref_get(&cfid->refcount);
}
/*
* Queue dropping of the dentries once locks have been dropped
*/
if (!list_empty(&cfids->dying))
queue_work(cfid_put_wq, &cfids->invalidation_work);
spin_unlock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &entry, entry) {
list_del(&cfid->entry);
cancel_work_sync(&cfid->lease_break);
/*
* Drop the ref-count from above, either the lease-ref (if there
* was one) or the extra one acquired.
*/
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
}
static void
smb2_cached_lease_break(struct work_struct *work)
cached_dir_offload_close(struct work_struct *work)
{
struct cached_fid *cfid = container_of(work,
struct cached_fid, lease_break);
struct cached_fid, close_work);
struct cifs_tcon *tcon = cfid->tcon;
WARN_ON(cfid->on_list);
kref_put(&cfid->refcount, smb2_close_cached_fid);
cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cached_close);
}
/*
* Release the cached directory's dentry, and then queue work to drop cached
* directory itself (closing on server if needed).
*
* Must be called with a reference to the cached_fid and a reference to the
* tcon.
*/
static void cached_dir_put_work(struct work_struct *work)
{
struct cached_fid *cfid = container_of(work, struct cached_fid,
put_work);
struct dentry *dentry;
spin_lock(&cfid->fid_lock);
dentry = cfid->dentry;
cfid->dentry = NULL;
spin_unlock(&cfid->fid_lock);
dput(dentry);
queue_work(serverclose_wq, &cfid->close_work);
}
int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
@ -560,8 +614,10 @@ int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
cfid->on_list = false;
cfids->num_entries--;
queue_work(cifsiod_wq,
&cfid->lease_break);
++tcon->tc_count;
trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
netfs_trace_tcon_ref_get_cached_lease_break);
queue_work(cfid_put_wq, &cfid->put_work);
spin_unlock(&cfids->cfid_list_lock);
return true;
}
@ -583,7 +639,8 @@ static struct cached_fid *init_cached_dir(const char *path)
return NULL;
}
INIT_WORK(&cfid->lease_break, smb2_cached_lease_break);
INIT_WORK(&cfid->close_work, cached_dir_offload_close);
INIT_WORK(&cfid->put_work, cached_dir_put_work);
INIT_LIST_HEAD(&cfid->entry);
INIT_LIST_HEAD(&cfid->dirents.entries);
mutex_init(&cfid->dirents.de_mutex);
@ -596,6 +653,9 @@ static void free_cached_dir(struct cached_fid *cfid)
{
struct cached_dirent *dirent, *q;
WARN_ON(work_pending(&cfid->close_work));
WARN_ON(work_pending(&cfid->put_work));
dput(cfid->dentry);
cfid->dentry = NULL;
@ -613,10 +673,30 @@ static void free_cached_dir(struct cached_fid *cfid)
kfree(cfid);
}
static void cfids_invalidation_worker(struct work_struct *work)
{
struct cached_fids *cfids = container_of(work, struct cached_fids,
invalidation_work);
struct cached_fid *cfid, *q;
LIST_HEAD(entry);
spin_lock(&cfids->cfid_list_lock);
/* move cfids->dying to the local list */
list_cut_before(&entry, &cfids->dying, &cfids->dying);
spin_unlock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &entry, entry) {
list_del(&cfid->entry);
/* Drop the ref-count acquired in invalidate_all_cached_dirs */
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
}
static void cfids_laundromat_worker(struct work_struct *work)
{
struct cached_fids *cfids;
struct cached_fid *cfid, *q;
struct dentry *dentry;
LIST_HEAD(entry);
cfids = container_of(work, struct cached_fids, laundromat_work.work);
@ -642,18 +722,28 @@ static void cfids_laundromat_worker(struct work_struct *work)
list_for_each_entry_safe(cfid, q, &entry, entry) {
list_del(&cfid->entry);
/*
* Cancel and wait for the work to finish in case we are racing
* with it.
*/
cancel_work_sync(&cfid->lease_break);
/*
* Drop the ref-count from above, either the lease-ref (if there
* was one) or the extra one acquired.
*/
kref_put(&cfid->refcount, smb2_close_cached_fid);
spin_lock(&cfid->fid_lock);
dentry = cfid->dentry;
cfid->dentry = NULL;
spin_unlock(&cfid->fid_lock);
dput(dentry);
if (cfid->is_open) {
spin_lock(&cifs_tcp_ses_lock);
++cfid->tcon->tc_count;
trace_smb3_tcon_ref(cfid->tcon->debug_id, cfid->tcon->tc_count,
netfs_trace_tcon_ref_get_cached_laundromat);
spin_unlock(&cifs_tcp_ses_lock);
queue_work(serverclose_wq, &cfid->close_work);
} else
/*
* Drop the ref-count from above, either the lease-ref (if there
* was one) or the extra one acquired.
*/
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
queue_delayed_work(cifsiod_wq, &cfids->laundromat_work,
queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
dir_cache_timeout * HZ);
}
@ -666,9 +756,11 @@ struct cached_fids *init_cached_dirs(void)
return NULL;
spin_lock_init(&cfids->cfid_list_lock);
INIT_LIST_HEAD(&cfids->entries);
INIT_LIST_HEAD(&cfids->dying);
INIT_WORK(&cfids->invalidation_work, cfids_invalidation_worker);
INIT_DELAYED_WORK(&cfids->laundromat_work, cfids_laundromat_worker);
queue_delayed_work(cifsiod_wq, &cfids->laundromat_work,
queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
dir_cache_timeout * HZ);
return cfids;
@ -687,6 +779,7 @@ void free_cached_dirs(struct cached_fids *cfids)
return;
cancel_delayed_work_sync(&cfids->laundromat_work);
cancel_work_sync(&cfids->invalidation_work);
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
@ -694,6 +787,11 @@ void free_cached_dirs(struct cached_fids *cfids)
cfid->is_open = false;
list_move(&cfid->entry, &entry);
}
list_for_each_entry_safe(cfid, q, &cfids->dying, entry) {
cfid->on_list = false;
cfid->is_open = false;
list_move(&cfid->entry, &entry);
}
spin_unlock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &entry, entry) {

View File

@ -44,7 +44,8 @@ struct cached_fid {
spinlock_t fid_lock;
struct cifs_tcon *tcon;
struct dentry *dentry;
struct work_struct lease_break;
struct work_struct put_work;
struct work_struct close_work;
struct smb2_file_all_info file_all_info;
struct cached_dirents dirents;
};
@ -53,10 +54,13 @@ struct cached_fid {
struct cached_fids {
/* Must be held when:
* - accessing the cfids->entries list
* - accessing the cfids->dying list
*/
spinlock_t cfid_list_lock;
int num_entries;
struct list_head entries;
struct list_head dying;
struct work_struct invalidation_work;
struct delayed_work laundromat_work;
};

View File

@ -157,6 +157,7 @@ struct workqueue_struct *fileinfo_put_wq;
struct workqueue_struct *cifsoplockd_wq;
struct workqueue_struct *deferredclose_wq;
struct workqueue_struct *serverclose_wq;
struct workqueue_struct *cfid_put_wq;
__u32 cifs_lock_secret;
/*
@ -1920,9 +1921,16 @@ init_cifs(void)
goto out_destroy_deferredclose_wq;
}
cfid_put_wq = alloc_workqueue("cfid_put_wq",
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
if (!cfid_put_wq) {
rc = -ENOMEM;
goto out_destroy_serverclose_wq;
}
rc = cifs_init_inodecache();
if (rc)
goto out_destroy_serverclose_wq;
goto out_destroy_cfid_put_wq;
rc = cifs_init_netfs();
if (rc)
@ -1990,6 +1998,8 @@ init_cifs(void)
cifs_destroy_netfs();
out_destroy_inodecache:
cifs_destroy_inodecache();
out_destroy_cfid_put_wq:
destroy_workqueue(cfid_put_wq);
out_destroy_serverclose_wq:
destroy_workqueue(serverclose_wq);
out_destroy_deferredclose_wq:

View File

@ -146,6 +146,6 @@ extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
/* when changing internal version - update following two lines at same time */
#define SMB3_PRODUCT_BUILD 51
#define CIFS_VERSION "2.51"
#define SMB3_PRODUCT_BUILD 52
#define CIFS_VERSION "2.52"
#endif /* _CIFSFS_H */

View File

@ -594,6 +594,7 @@ struct smb_version_operations {
/* Check for STATUS_NETWORK_NAME_DELETED */
bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
const char *full_path,
struct kvec *rsp_iov,
struct cifs_open_info_data *data);
int (*create_reparse_symlink)(const unsigned int xid,
@ -1990,7 +1991,7 @@ require use of the stronger protocol */
* cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once
* ->can_cache_brlcks
* cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc
* cached_fid->fid_mutex cifs_tcon->crfid tcon_info_alloc
* cached_fids->cfid_list_lock cifs_tcon->cfids->entries init_cached_dirs
* cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo
* cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo
* ->invalidHandle initiate_cifs_search
@ -2078,6 +2079,7 @@ 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 struct workqueue_struct *cfid_put_wq;
extern __u32 cifs_lock_secret;
extern mempool_t *cifs_sm_req_poolp;

View File

@ -314,8 +314,7 @@ extern void cifs_move_llist(struct list_head *source, struct list_head *dest);
extern void cifs_free_llist(struct list_head *llist);
extern void cifs_del_lock_waiters(struct cifsLockInfo *lock);
extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nlsc);
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon);
extern int cifs_negotiate_protocol(const unsigned int xid,
struct cifs_ses *ses,
@ -661,6 +660,7 @@ char *extract_hostname(const char *unc);
char *extract_sharename(const char *unc);
int parse_reparse_point(struct reparse_data_buffer *buf,
u32 plen, struct cifs_sb_info *cifs_sb,
const char *full_path,
bool unicode, struct cifs_open_info_data *data);
int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,

View File

@ -70,10 +70,9 @@ static struct {
static int
cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
{
int rc;
struct cifs_ses *ses;
struct TCP_Server_Info *server;
struct nls_table *nls_codepage = NULL;
struct cifs_ses *ses;
int rc;
/*
* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
@ -131,8 +130,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
}
spin_unlock(&server->srv_lock);
nls_codepage = ses->local_nls;
/*
* need to prevent multiple threads trying to simultaneously
* reconnect the same SMB session
@ -156,7 +153,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
rc = cifs_negotiate_protocol(0, ses, server);
if (!rc)
rc = cifs_setup_session(0, ses, server, nls_codepage);
rc = cifs_setup_session(0, ses, server, ses->local_nls);
/* do we need to reconnect tcon? */
if (rc || !tcon->need_reconnect) {
@ -166,7 +163,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
skip_sess_setup:
cifs_mark_open_files_invalid(tcon);
rc = cifs_tree_connect(0, tcon, nls_codepage);
rc = cifs_tree_connect(0, tcon);
mutex_unlock(&ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
@ -4320,8 +4317,8 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
* CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus
* causing an infinite recursion.
*/
rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc,
(void **)&pSMB, (void **)&pSMBr);
rc = smb_init(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc,
(void **)&pSMB, (void **)&pSMBr);
if (rc)
return rc;

View File

@ -1897,11 +1897,35 @@ static int match_session(struct cifs_ses *ses,
CIFS_MAX_USERNAME_LEN))
return 0;
if ((ctx->username && strlen(ctx->username) != 0) &&
ses->password != NULL &&
strncmp(ses->password,
ctx->password ? ctx->password : "",
CIFS_MAX_PASSWORD_LEN))
return 0;
ses->password != NULL) {
/* New mount can only share sessions with an existing mount if:
* 1. Both password and password2 match, or
* 2. password2 of the old mount matches password of the new mount
* and password of the old mount matches password2 of the new
* mount
*/
if (ses->password2 != NULL && ctx->password2 != NULL) {
if (!((strncmp(ses->password, ctx->password ?
ctx->password : "", CIFS_MAX_PASSWORD_LEN) == 0 &&
strncmp(ses->password2, ctx->password2,
CIFS_MAX_PASSWORD_LEN) == 0) ||
(strncmp(ses->password, ctx->password2,
CIFS_MAX_PASSWORD_LEN) == 0 &&
strncmp(ses->password2, ctx->password ?
ctx->password : "", CIFS_MAX_PASSWORD_LEN) == 0)))
return 0;
} else if ((ses->password2 == NULL && ctx->password2 != NULL) ||
(ses->password2 != NULL && ctx->password2 == NULL)) {
return 0;
} else {
if (strncmp(ses->password, ctx->password ?
ctx->password : "", CIFS_MAX_PASSWORD_LEN))
return 0;
}
}
}
if (strcmp(ctx->local_nls->charset, ses->local_nls->charset))
@ -2244,6 +2268,7 @@ struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
int rc = 0;
int retries = 0;
unsigned int xid;
struct cifs_ses *ses;
struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
@ -2262,6 +2287,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
cifs_dbg(FYI, "Session needs reconnect\n");
mutex_lock(&ses->session_mutex);
retry_old_session:
rc = cifs_negotiate_protocol(xid, ses, server);
if (rc) {
mutex_unlock(&ses->session_mutex);
@ -2274,6 +2301,13 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
rc = cifs_setup_session(xid, ses, server,
ctx->local_nls);
if (rc) {
if (((rc == -EACCES) || (rc == -EKEYEXPIRED) ||
(rc == -EKEYREVOKED)) && !retries && ses->password2) {
retries++;
cifs_dbg(FYI, "Session reconnect failed, retrying with alternate password\n");
swap(ses->password, ses->password2);
goto retry_old_session;
}
mutex_unlock(&ses->session_mutex);
/* problem -- put our reference */
cifs_put_smb_ses(ses);
@ -2369,6 +2403,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->chans_need_reconnect = 1;
spin_unlock(&ses->chan_lock);
retry_new_session:
mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(xid, ses, server);
if (!rc)
@ -2381,8 +2416,16 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
sizeof(ses->smb3signingkey));
spin_unlock(&ses->chan_lock);
if (rc)
goto get_ses_fail;
if (rc) {
if (((rc == -EACCES) || (rc == -EKEYEXPIRED) ||
(rc == -EKEYREVOKED)) && !retries && ses->password2) {
retries++;
cifs_dbg(FYI, "Session setup failed, retrying with alternate password\n");
swap(ses->password, ses->password2);
goto retry_new_session;
} else
goto get_ses_fail;
}
/*
* success, put it on the list and add it as first channel
@ -2571,7 +2614,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
if (ses->server->dialect >= SMB20_PROT_ID &&
(ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING))
nohandlecache = ctx->nohandlecache;
nohandlecache = ctx->nohandlecache || !dir_cache_timeout;
else
nohandlecache = true;
tcon = tcon_info_alloc(!nohandlecache, netfs_trace_tcon_ref_new);
@ -4344,10 +4387,10 @@ cifs_prune_tlinks(struct work_struct *work)
}
#ifndef CONFIG_CIFS_DFS_UPCALL
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon)
{
int rc;
const struct smb_version_operations *ops = tcon->ses->server->ops;
int rc;
/* only send once per connect */
spin_lock(&tcon->tc_lock);
@ -4370,7 +4413,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
tcon->status = TID_IN_TCON;
spin_unlock(&tcon->tc_lock);
rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc);
rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name,
tcon, tcon->ses->local_nls);
if (rc) {
spin_lock(&tcon->tc_lock);
if (tcon->status == TID_IN_TCON)

View File

@ -321,49 +321,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
return rc;
}
/* Update dfs referral path of superblock */
static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb,
const char *target)
{
int rc = 0;
size_t len = strlen(target);
char *refpath, *npath;
if (unlikely(len < 2 || *target != '\\'))
return -EINVAL;
if (target[1] == '\\') {
len += 1;
refpath = kmalloc(len, GFP_KERNEL);
if (!refpath)
return -ENOMEM;
scnprintf(refpath, len, "%s", target);
} else {
len += sizeof("\\");
refpath = kmalloc(len, GFP_KERNEL);
if (!refpath)
return -ENOMEM;
scnprintf(refpath, len, "\\%s", target);
}
npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb));
kfree(refpath);
if (IS_ERR(npath)) {
rc = PTR_ERR(npath);
} else {
mutex_lock(&server->refpath_lock);
spin_lock(&server->srv_lock);
kfree(server->leaf_fullpath);
server->leaf_fullpath = npath;
spin_unlock(&server->srv_lock);
mutex_unlock(&server->refpath_lock);
}
return rc;
}
static int target_share_matches_server(struct TCP_Server_Info *server, char *share,
bool *target_match)
{
@ -388,77 +345,22 @@ static int target_share_matches_server(struct TCP_Server_Info *server, char *sha
return rc;
}
static void __tree_connect_ipc(const unsigned int xid, char *tree,
struct cifs_sb_info *cifs_sb,
struct cifs_ses *ses)
static int tree_connect_dfs_target(const unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
char *tree, bool islink,
struct dfs_cache_tgt_list *tl)
{
struct TCP_Server_Info *server = ses->server;
struct cifs_tcon *tcon = ses->tcon_ipc;
int rc;
spin_lock(&ses->ses_lock);
spin_lock(&ses->chan_lock);
if (cifs_chan_needs_reconnect(ses, server) ||
ses->ses_status != SES_GOOD) {
spin_unlock(&ses->chan_lock);
spin_unlock(&ses->ses_lock);
cifs_server_dbg(FYI, "%s: skipping ipc reconnect due to disconnected ses\n",
__func__);
return;
}
spin_unlock(&ses->chan_lock);
spin_unlock(&ses->ses_lock);
cifs_server_lock(server);
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
cifs_server_unlock(server);
rc = server->ops->tree_connect(xid, ses, tree, tcon,
cifs_sb->local_nls);
cifs_server_dbg(FYI, "%s: tree_reconnect %s: %d\n", __func__, tree, rc);
spin_lock(&tcon->tc_lock);
if (rc) {
tcon->status = TID_NEED_TCON;
} else {
tcon->status = TID_GOOD;
tcon->need_reconnect = false;
}
spin_unlock(&tcon->tc_lock);
}
static void tree_connect_ipc(const unsigned int xid, char *tree,
struct cifs_sb_info *cifs_sb,
struct cifs_tcon *tcon)
{
struct cifs_ses *ses = tcon->ses;
__tree_connect_ipc(xid, tree, cifs_sb, ses);
__tree_connect_ipc(xid, tree, cifs_sb, CIFS_DFS_ROOT_SES(ses));
}
static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, char *tree, bool islink,
struct dfs_cache_tgt_list *tl)
{
int rc;
const struct smb_version_operations *ops = tcon->ses->server->ops;
struct TCP_Server_Info *server = tcon->ses->server;
const struct smb_version_operations *ops = server->ops;
struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
char *share = NULL, *prefix = NULL;
struct dfs_cache_tgt_iterator *tit;
char *share = NULL, *prefix = NULL;
bool target_match;
tit = dfs_cache_get_tgt_iterator(tl);
if (!tit) {
rc = -ENOENT;
goto out;
}
int rc = -ENOENT;
/* Try to tree connect to all dfs targets */
for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
const char *target = dfs_cache_get_tgt_name(tit);
DFS_CACHE_TGT_LIST(ntl);
for (tit = dfs_cache_get_tgt_iterator(tl);
tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
kfree(share);
kfree(prefix);
share = prefix = NULL;
@ -479,74 +381,21 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
}
dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit);
tree_connect_ipc(xid, tree, cifs_sb, tcon);
scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
if (!islink) {
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
break;
}
/*
* If no dfs referrals were returned from link target, then just do a TREE_CONNECT
* to it. Otherwise, cache the dfs referral and then mark current tcp ses for
* reconnect so either the demultiplex thread or the echo worker will reconnect to
* newly resolved target.
*/
if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
NULL, &ntl)) {
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
if (rc)
continue;
rc = ops->tree_connect(xid, tcon->ses, tree,
tcon, tcon->ses->local_nls);
if (islink && !rc && cifs_sb)
rc = cifs_update_super_prepath(cifs_sb, prefix);
} else {
/* Target is another dfs share */
rc = update_server_fullpath(server, cifs_sb, target);
dfs_cache_free_tgts(tl);
if (!rc) {
rc = -EREMOTE;
list_replace_init(&ntl.tl_list, &tl->tl_list);
} else
dfs_cache_free_tgts(&ntl);
}
break;
}
out:
kfree(share);
kfree(prefix);
return rc;
}
static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, char *tree, bool islink,
struct dfs_cache_tgt_list *tl)
{
int rc;
int num_links = 0;
struct TCP_Server_Info *server = tcon->ses->server;
char *old_fullpath = server->leaf_fullpath;
do {
rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl);
if (!rc || rc != -EREMOTE)
break;
} while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
/*
* If we couldn't tree connect to any targets from last referral path, then
* retry it from newly resolved dfs referral.
*/
if (rc && server->leaf_fullpath != old_fullpath)
cifs_signal_cifsd_for_reconnect(server, true);
dfs_cache_free_tgts(tl);
return rc;
}
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon)
{
int rc;
struct TCP_Server_Info *server = tcon->ses->server;
@ -588,7 +437,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
cifs_server_lock(server);
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
cifs_server_unlock(server);
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
rc = ops->tree_connect(xid, tcon->ses, tree,
tcon, tcon->ses->local_nls);
goto out;
}
@ -596,14 +446,11 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
if (!IS_ERR(sb))
cifs_sb = CIFS_SB(sb);
/*
* Tree connect to last share in @tcon->tree_name whether dfs super or
* cached dfs referral was not found.
*/
if (!cifs_sb || !server->leaf_fullpath ||
/* Tree connect to last share in @tcon->tree_name if no DFS referral */
if (!server->leaf_fullpath ||
dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) {
rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon,
cifs_sb ? cifs_sb->local_nls : nlsc);
rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name,
tcon, tcon->ses->local_nls);
goto out;
}

View File

@ -24,8 +24,8 @@
#include "dfs_cache.h"
#define CACHE_HTABLE_SIZE 32
#define CACHE_MAX_ENTRIES 64
#define CACHE_HTABLE_SIZE 512
#define CACHE_MAX_ENTRIES 1024
#define CACHE_MIN_TTL 120 /* 2 minutes */
#define CACHE_DEFAULT_TTL 300 /* 5 minutes */

View File

@ -920,12 +920,37 @@ do { \
cifs_sb->ctx->field = NULL; \
} while (0)
int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
{
if (ses->password &&
cifs_sb->ctx->password &&
strcmp(ses->password, cifs_sb->ctx->password)) {
kfree_sensitive(cifs_sb->ctx->password);
cifs_sb->ctx->password = kstrdup(ses->password, GFP_KERNEL);
if (!cifs_sb->ctx->password)
return -ENOMEM;
}
if (ses->password2 &&
cifs_sb->ctx->password2 &&
strcmp(ses->password2, cifs_sb->ctx->password2)) {
kfree_sensitive(cifs_sb->ctx->password2);
cifs_sb->ctx->password2 = kstrdup(ses->password2, GFP_KERNEL);
if (!cifs_sb->ctx->password2) {
kfree_sensitive(cifs_sb->ctx->password);
cifs_sb->ctx->password = NULL;
return -ENOMEM;
}
}
return 0;
}
static int smb3_reconfigure(struct fs_context *fc)
{
struct smb3_fs_context *ctx = smb3_fc2context(fc);
struct dentry *root = fc->root;
struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
char *new_password = NULL, *new_password2 = NULL;
bool need_recon = false;
int rc;
@ -945,21 +970,63 @@ static int smb3_reconfigure(struct fs_context *fc)
STEAL_STRING(cifs_sb, ctx, UNC);
STEAL_STRING(cifs_sb, ctx, source);
STEAL_STRING(cifs_sb, ctx, username);
if (need_recon == false)
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
else {
kfree_sensitive(ses->password);
ses->password = kstrdup(ctx->password, GFP_KERNEL);
if (!ses->password)
return -ENOMEM;
kfree_sensitive(ses->password2);
ses->password2 = kstrdup(ctx->password2, GFP_KERNEL);
if (!ses->password2) {
kfree_sensitive(ses->password);
ses->password = NULL;
if (ctx->password) {
new_password = kstrdup(ctx->password, GFP_KERNEL);
if (!new_password)
return -ENOMEM;
} else
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
}
/*
* if a new password2 has been specified, then reset it's value
* inside the ses struct
*/
if (ctx->password2) {
new_password2 = kstrdup(ctx->password2, GFP_KERNEL);
if (!new_password2) {
kfree_sensitive(new_password);
return -ENOMEM;
}
} else
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password2);
/*
* we may update the passwords in the ses struct below. Make sure we do
* not race with smb2_reconnect
*/
mutex_lock(&ses->session_mutex);
/*
* smb2_reconnect may swap password and password2 in case session setup
* failed. First get ctx passwords in sync with ses passwords. It should
* be okay to do this even if this function were to return an error at a
* later stage
*/
rc = smb3_sync_session_ctx_passwords(cifs_sb, ses);
if (rc) {
mutex_unlock(&ses->session_mutex);
return rc;
}
/*
* now that allocations for passwords are done, commit them
*/
if (new_password) {
kfree_sensitive(ses->password);
ses->password = new_password;
}
if (new_password2) {
kfree_sensitive(ses->password2);
ses->password2 = new_password2;
}
mutex_unlock(&ses->session_mutex);
STEAL_STRING(cifs_sb, ctx, domainname);
STEAL_STRING(cifs_sb, ctx, nodename);
STEAL_STRING(cifs_sb, ctx, iocharset);

View File

@ -309,6 +309,7 @@ static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *f
}
extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx);
extern int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);
extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb);
/*

View File

@ -1137,6 +1137,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
rc = 0;
} else if (iov && server->ops->parse_reparse_point) {
rc = server->ops->parse_reparse_point(cifs_sb,
full_path,
iov, data);
}
break;
@ -2495,13 +2496,10 @@ cifs_dentry_needs_reval(struct dentry *dentry)
return true;
if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) {
spin_lock(&cfid->fid_lock);
if (cfid->time && cifs_i->time > cfid->time) {
spin_unlock(&cfid->fid_lock);
close_cached_dir(cfid);
return false;
}
spin_unlock(&cfid->fid_lock);
close_cached_dir(cfid);
}
/*

View File

@ -378,8 +378,8 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
memset(iov, 0, sizeof(*iov));
/* Exclude $LXDEV xattr for sockets and fifos */
if (S_ISSOCK(_mode) || S_ISFIFO(_mode))
/* Exclude $LXDEV xattr for non-device files */
if (!S_ISBLK(_mode) && !S_ISCHR(_mode))
num_xattrs = ARRAY_SIZE(xattrs) - 1;
else
num_xattrs = ARRAY_SIZE(xattrs);
@ -535,9 +535,95 @@ static int parse_reparse_posix(struct reparse_posix_data *buf,
return 0;
}
int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
bool unicode, bool relative,
const char *full_path,
struct cifs_sb_info *cifs_sb)
{
char sep = CIFS_DIR_SEP(cifs_sb);
char *linux_target = NULL;
char *smb_target = NULL;
int levels;
int rc;
int i;
/* Check that length it valid for unicode/non-unicode mode */
if (!len || (unicode && (len % 2))) {
cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
rc = -EIO;
goto out;
}
/*
* Check that buffer does not contain UTF-16 null codepoint in unicode
* mode or null byte in non-unicode mode because Linux cannot process
* symlink with null byte.
*/
if ((unicode && UniStrnlen((wchar_t *)buf, len/2) != len/2) ||
(!unicode && strnlen(buf, len) != len)) {
cifs_dbg(VFS, "srv returned null byte in native symlink target location\n");
rc = -EIO;
goto out;
}
smb_target = cifs_strndup_from_utf16(buf, len, unicode, cifs_sb->local_nls);
if (!smb_target) {
rc = -ENOMEM;
goto out;
}
if (smb_target[0] == sep && relative) {
/*
* This is a relative SMB symlink from the top of the share,
* which is the top level directory of the Linux mount point.
* Linux does not support such relative symlinks, so convert
* it to the relative symlink from the current directory.
* full_path is the SMB path to the symlink (from which is
* extracted current directory) and smb_target is the SMB path
* where symlink points, therefore full_path must always be on
* the SMB share.
*/
int smb_target_len = strlen(smb_target)+1;
levels = 0;
for (i = 1; full_path[i]; i++) { /* i=1 to skip leading sep */
if (full_path[i] == sep)
levels++;
}
linux_target = kmalloc(levels*3 + smb_target_len, GFP_KERNEL);
if (!linux_target) {
rc = -ENOMEM;
goto out;
}
for (i = 0; i < levels; i++) {
linux_target[i*3 + 0] = '.';
linux_target[i*3 + 1] = '.';
linux_target[i*3 + 2] = sep;
}
memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */
} else {
linux_target = smb_target;
smb_target = NULL;
}
if (sep == '\\')
convert_delimiter(linux_target, '/');
rc = 0;
*target = linux_target;
cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, *target);
out:
if (rc != 0)
kfree(linux_target);
kfree(smb_target);
return rc;
}
static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
u32 plen, bool unicode,
struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data)
{
unsigned int len;
@ -552,20 +638,64 @@ static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
return -EIO;
}
data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
len, unicode,
return smb2_parse_native_symlink(&data->symlink_target,
sym->PathBuffer + offs,
len,
unicode,
le32_to_cpu(sym->Flags) & SYMLINK_FLAG_RELATIVE,
full_path,
cifs_sb);
}
static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf,
struct cifs_sb_info *cifs_sb,
struct cifs_open_info_data *data)
{
int len = le16_to_cpu(buf->ReparseDataLength);
int symname_utf8_len;
__le16 *symname_utf16;
int symname_utf16_len;
if (len <= sizeof(buf->Flags)) {
cifs_dbg(VFS, "srv returned malformed wsl symlink buffer\n");
return -EIO;
}
/* PathBuffer is in UTF-8 but without trailing null-term byte */
symname_utf8_len = len - sizeof(buf->Flags);
/*
* Check that buffer does not contain null byte
* because Linux cannot process symlink with null byte.
*/
if (strnlen(buf->PathBuffer, symname_utf8_len) != symname_utf8_len) {
cifs_dbg(VFS, "srv returned null byte in wsl symlink target location\n");
return -EIO;
}
symname_utf16 = kzalloc(symname_utf8_len * 2, GFP_KERNEL);
if (!symname_utf16)
return -ENOMEM;
symname_utf16_len = utf8s_to_utf16s(buf->PathBuffer, symname_utf8_len,
UTF16_LITTLE_ENDIAN,
symname_utf16, symname_utf8_len * 2);
if (symname_utf16_len < 0) {
kfree(symname_utf16);
return symname_utf16_len;
}
symname_utf16_len *= 2; /* utf8s_to_utf16s() returns number of u16 items, not byte length */
data->symlink_target = cifs_strndup_from_utf16((u8 *)symname_utf16,
symname_utf16_len, true,
cifs_sb->local_nls);
kfree(symname_utf16);
if (!data->symlink_target)
return -ENOMEM;
convert_delimiter(data->symlink_target, '/');
cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
return 0;
}
int parse_reparse_point(struct reparse_data_buffer *buf,
u32 plen, struct cifs_sb_info *cifs_sb,
const char *full_path,
bool unicode, struct cifs_open_info_data *data)
{
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
@ -580,12 +710,20 @@ int parse_reparse_point(struct reparse_data_buffer *buf,
case IO_REPARSE_TAG_SYMLINK:
return parse_reparse_symlink(
(struct reparse_symlink_data_buffer *)buf,
plen, unicode, cifs_sb, data);
plen, unicode, cifs_sb, full_path, data);
case IO_REPARSE_TAG_LX_SYMLINK:
return parse_reparse_wsl_symlink(
(struct reparse_wsl_symlink_data_buffer *)buf,
cifs_sb, data);
case IO_REPARSE_TAG_AF_UNIX:
case IO_REPARSE_TAG_LX_FIFO:
case IO_REPARSE_TAG_LX_CHR:
case IO_REPARSE_TAG_LX_BLK:
if (le16_to_cpu(buf->ReparseDataLength) != 0) {
cifs_dbg(VFS, "srv returned malformed buffer for reparse point: 0x%08x\n",
le32_to_cpu(buf->ReparseTag));
return -EIO;
}
break;
default:
cifs_tcon_dbg(VFS | ONCE, "unhandled reparse tag: 0x%08x\n",
@ -596,6 +734,7 @@ int parse_reparse_point(struct reparse_data_buffer *buf,
}
int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
const char *full_path,
struct kvec *rsp_iov,
struct cifs_open_info_data *data)
{
@ -605,7 +744,7 @@ int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
buf = (struct reparse_data_buffer *)((u8 *)io +
le32_to_cpu(io->OutputOffset));
return parse_reparse_point(buf, plen, cifs_sb, true, data);
return parse_reparse_point(buf, plen, cifs_sb, full_path, true, data);
}
static void wsl_to_fattr(struct cifs_open_info_data *data,

View File

@ -117,7 +117,9 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev);
int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov,
int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
const char *full_path,
struct kvec *rsp_iov,
struct cifs_open_info_data *data);
#endif /* _CIFS_REPARSE_H */

View File

@ -347,10 +347,7 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
spin_unlock(&ses->chan_lock);
}
/*
* update the iface for the channel if necessary.
* Must be called with chan_lock held.
*/
/* update the iface for the channel if necessary. */
void
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
{

View File

@ -994,17 +994,17 @@ static int cifs_query_symlink(const unsigned int xid,
}
static int cifs_parse_reparse_point(struct cifs_sb_info *cifs_sb,
const char *full_path,
struct kvec *rsp_iov,
struct cifs_open_info_data *data)
{
struct reparse_data_buffer *buf;
TRANSACT_IOCTL_RSP *io = rsp_iov->iov_base;
bool unicode = !!(io->hdr.Flags2 & SMBFLG2_UNICODE);
u32 plen = le16_to_cpu(io->ByteCount);
buf = (struct reparse_data_buffer *)((__u8 *)&io->hdr.Protocol +
le32_to_cpu(io->DataOffset));
return parse_reparse_point(buf, plen, cifs_sb, unicode, data);
return parse_reparse_point(buf, plen, cifs_sb, full_path, true, data);
}
static bool

View File

@ -63,12 +63,12 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
return sym;
}
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path)
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov,
const char *full_path, char **path)
{
struct smb2_symlink_err_rsp *sym;
unsigned int sub_offs, sub_len;
unsigned int print_offs, print_len;
char *s;
if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path)
return -EINVAL;
@ -86,15 +86,13 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec
iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
return -EINVAL;
s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true,
cifs_sb->local_nls);
if (!s)
return -ENOMEM;
convert_delimiter(s, '/');
cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s);
*path = s;
return 0;
return smb2_parse_native_symlink(path,
(char *)sym->PathBuffer + sub_offs,
sub_len,
true,
le32_to_cpu(sym->Flags) & SYMLINK_FLAG_RELATIVE,
full_path,
cifs_sb);
}
int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf)
@ -126,6 +124,7 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32
goto out;
if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) {
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
oparms->path,
&data->symlink_target);
if (!rc) {
memset(smb2_data, 0, sizeof(*smb2_data));

View File

@ -828,6 +828,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
static int parse_create_response(struct cifs_open_info_data *data,
struct cifs_sb_info *cifs_sb,
const char *full_path,
const struct kvec *iov)
{
struct smb2_create_rsp *rsp = iov->iov_base;
@ -841,6 +842,7 @@ static int parse_create_response(struct cifs_open_info_data *data,
break;
case STATUS_STOPPED_ON_SYMLINK:
rc = smb2_parse_symlink_response(cifs_sb, iov,
full_path,
&data->symlink_target);
if (rc)
return rc;
@ -930,14 +932,14 @@ int smb2_query_path_info(const unsigned int xid,
switch (rc) {
case 0:
rc = parse_create_response(data, cifs_sb, &out_iov[0]);
rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
break;
case -EOPNOTSUPP:
/*
* BB TODO: When support for special files added to Samba
* re-verify this path.
*/
rc = parse_create_response(data, cifs_sb, &out_iov[0]);
rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
if (rc || !data->reparse_point)
goto out;

View File

@ -2932,7 +2932,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
struct fsctl_get_dfs_referral_req *dfs_req = NULL;
struct get_dfs_referral_rsp *dfs_rsp = NULL;
u32 dfs_req_size = 0, dfs_rsp_size = 0;
int retry_count = 0;
int retry_once = 0;
cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name);
@ -2981,21 +2981,25 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
/* Path to resolve in an UTF-16 null-terminated string */
memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len);
do {
for (;;) {
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
FSCTL_DFS_GET_REFERRALS,
(char *)dfs_req, dfs_req_size, CIFSMaxBufSize,
(char **)&dfs_rsp, &dfs_rsp_size);
if (!is_retryable_error(rc))
if (fatal_signal_pending(current)) {
rc = -EINTR;
break;
}
if (!is_retryable_error(rc) || retry_once++)
break;
usleep_range(512, 2048);
} while (++retry_count < 5);
}
if (!rc && !dfs_rsp)
rc = -EIO;
if (rc) {
if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP)
cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc);
cifs_tcon_dbg(FYI, "%s: ioctl error: rc=%d\n", __func__, rc);
goto out;
}

View File

@ -216,10 +216,9 @@ static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
struct TCP_Server_Info *server, bool from_reconnect)
{
int rc = 0;
struct nls_table *nls_codepage = NULL;
struct cifs_ses *ses;
int xid;
int rc = 0;
/*
* SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
@ -229,11 +228,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if (tcon == NULL)
return 0;
/*
* Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in
* cifs_tree_connect().
*/
if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
if (smb2_command == SMB2_TREE_CONNECT)
return 0;
spin_lock(&tcon->tc_lock);
@ -334,8 +329,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
}
spin_unlock(&server->srv_lock);
nls_codepage = ses->local_nls;
/*
* need to prevent multiple threads trying to simultaneously
* reconnect the same SMB session
@ -372,7 +365,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
}
}
rc = cifs_setup_session(0, ses, server, nls_codepage);
rc = cifs_setup_session(0, ses, server, ses->local_nls);
if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) {
/*
* Try alternate password for next reconnect (key rotation
@ -406,7 +399,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if (tcon->use_persistent)
tcon->need_reopen_files = true;
rc = cifs_tree_connect(0, tcon, nls_codepage);
rc = cifs_tree_connect(0, tcon);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc) {
@ -494,6 +487,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
case SMB2_CHANGE_NOTIFY:
case SMB2_QUERY_INFO:
case SMB2_SET_INFO:
case SMB2_IOCTL:
rc = -EAGAIN;
}
failed:
@ -1231,7 +1225,9 @@ SMB2_negotiate(const unsigned int xid,
* SMB3.0 supports only 1 cipher and doesn't have a encryption neg context
* Set the cipher type manually.
*/
if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
if ((server->dialect == SMB30_PROT_ID ||
server->dialect == SMB302_PROT_ID) &&
(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
server->cipher_type = SMB2_ENCRYPTION_AES128_CCM;
security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,

View File

@ -111,7 +111,14 @@ extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const unsigned char *path, char *pbuf,
unsigned int *pbytes_read);
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path);
int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
bool unicode, bool relative,
const char *full_path,
struct cifs_sb_info *cifs_sb);
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb,
const struct kvec *iov,
const char *full_path,
char **path);
int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
void *buf);
extern int smb2_unlock_range(struct cifsFileInfo *cfile,

View File

@ -44,6 +44,8 @@
EM(netfs_trace_tcon_ref_free_ipc, "FRE Ipc ") \
EM(netfs_trace_tcon_ref_free_ipc_fail, "FRE Ipc-F ") \
EM(netfs_trace_tcon_ref_free_reconnect_server, "FRE Reconn") \
EM(netfs_trace_tcon_ref_get_cached_laundromat, "GET Ch-Lau") \
EM(netfs_trace_tcon_ref_get_cached_lease_break, "GET Ch-Lea") \
EM(netfs_trace_tcon_ref_get_cancelled_close, "GET Cn-Cls") \
EM(netfs_trace_tcon_ref_get_dfs_refer, "GET DfsRef") \
EM(netfs_trace_tcon_ref_get_find, "GET Find ") \
@ -52,6 +54,7 @@
EM(netfs_trace_tcon_ref_new, "NEW ") \
EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \
EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \
EM(netfs_trace_tcon_ref_put_cached_close, "PUT Ch-Cls") \
EM(netfs_trace_tcon_ref_put_cancelled_close, "PUT Cn-Cls") \
EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \
EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \

View File

@ -1552,6 +1552,15 @@ struct reparse_symlink_data_buffer {
/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */
/* For IO_REPARSE_TAG_LX_SYMLINK */
struct reparse_wsl_symlink_data_buffer {
__le32 ReparseTag;
__le16 ReparseDataLength;
__u16 Reserved;
__le32 Flags;
__u8 PathBuffer[]; /* Variable Length UTF-8 string without nul-term */
} __packed;
struct validate_negotiate_info_req {
__le32 Capabilities;
__u8 Guid[SMB2_CLIENT_GUID_SIZE];