Commit ac34bb40 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull smb client fixes from Steve French:
 "Most are from the recent SMB3.1.1 test event, and also an important
  netfs fix for a cifs mtime write regression

   - fix mode reported by stat of readonly directories and files

   - DFS (global namespace) related fixes

   - fixes for special file support via reparse points

   - mount improvement and reconnect fix

   - fix for noisy log message on umount

   - two netfs related fixes, one fixing a recent regression, and add
     new write tracepoint"

* tag 'v6.12-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6:
  netfs, cifs: Fix mtime/ctime update for mmapped writes
  cifs: update internal version number
  smb: client: print failed session logoffs with FYI
  cifs: Fix reversion of the iter in cifs_readv_receive().
  smb3: fix incorrect mode displayed for read-only files
  smb: client: fix parsing of device numbers
  smb: client: set correct device number on nfs reparse points
  smb: client: propagate error from cifs_construct_tcon()
  smb: client: fix DFS failover in multiuser mounts
  cifs: Make the write_{enter,done,err} tracepoints display netfs info
  smb: client: fix DFS interlink failover
  smb: client: improve purging of cached referrals
  smb: client: avoid unnecessary reconnects when refreshing referrals
parents 5159938e 665db14d
...@@ -552,6 +552,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr ...@@ -552,6 +552,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr
trace_netfs_folio(folio, netfs_folio_trace_mkwrite); trace_netfs_folio(folio, netfs_folio_trace_mkwrite);
netfs_set_group(folio, netfs_group); netfs_set_group(folio, netfs_group);
file_update_time(file); file_update_time(file);
set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags);
if (ictx->ops->post_modify) if (ictx->ops->post_modify)
ictx->ops->post_modify(inode); ictx->ops->post_modify(inode);
ret = VM_FAULT_LOCKED; ret = VM_FAULT_LOCKED;
......
...@@ -146,6 +146,6 @@ extern const struct export_operations cifs_export_ops; ...@@ -146,6 +146,6 @@ extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */ #endif /* CONFIG_CIFS_NFSD_EXPORT */
/* when changing internal version - update following two lines at same time */ /* when changing internal version - update following two lines at same time */
#define SMB3_PRODUCT_BUILD 50 #define SMB3_PRODUCT_BUILD 51
#define CIFS_VERSION "2.50" #define CIFS_VERSION "2.51"
#endif /* _CIFSFS_H */ #endif /* _CIFSFS_H */
...@@ -821,6 +821,7 @@ struct TCP_Server_Info { ...@@ -821,6 +821,7 @@ struct TCP_Server_Info {
* format: \\HOST\SHARE[\OPTIONAL PATH] * format: \\HOST\SHARE[\OPTIONAL PATH]
*/ */
char *leaf_fullpath; char *leaf_fullpath;
bool dfs_conn:1;
}; };
static inline bool is_smb1(struct TCP_Server_Info *server) static inline bool is_smb1(struct TCP_Server_Info *server)
...@@ -1059,6 +1060,7 @@ struct cifs_ses { ...@@ -1059,6 +1060,7 @@ struct cifs_ses {
struct list_head smb_ses_list; struct list_head smb_ses_list;
struct list_head rlist; /* reconnect list */ struct list_head rlist; /* reconnect list */
struct list_head tcon_list; struct list_head tcon_list;
struct list_head dlist; /* dfs list */
struct cifs_tcon *tcon_ipc; struct cifs_tcon *tcon_ipc;
spinlock_t ses_lock; /* protect anything here that is not protected */ spinlock_t ses_lock; /* protect anything here that is not protected */
struct mutex session_mutex; struct mutex session_mutex;
...@@ -1287,6 +1289,7 @@ struct cifs_tcon { ...@@ -1287,6 +1289,7 @@ struct cifs_tcon {
/* BB add field for back pointer to sb struct(s)? */ /* BB add field for back pointer to sb struct(s)? */
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
struct delayed_work dfs_cache_work; struct delayed_work dfs_cache_work;
struct list_head dfs_ses_list;
#endif #endif
struct delayed_work query_interfaces; /* query interfaces workqueue job */ struct delayed_work query_interfaces; /* query interfaces workqueue job */
char *origin_fullpath; /* canonical copy of smb3_fs_context::source */ char *origin_fullpath; /* canonical copy of smb3_fs_context::source */
......
...@@ -724,15 +724,9 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) ...@@ -724,15 +724,9 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
/* Put references of @ses and its children */
static inline void cifs_put_smb_ses(struct cifs_ses *ses) static inline void cifs_put_smb_ses(struct cifs_ses *ses)
{ {
struct cifs_ses *next; __cifs_put_smb_ses(ses);
do {
next = ses->dfs_root_ses;
__cifs_put_smb_ses(ses);
} while ((ses = next));
} }
/* Get an active reference of @ses and its children. /* Get an active reference of @ses and its children.
...@@ -746,9 +740,7 @@ static inline void cifs_put_smb_ses(struct cifs_ses *ses) ...@@ -746,9 +740,7 @@ static inline void cifs_put_smb_ses(struct cifs_ses *ses)
static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses) static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses)
{ {
lockdep_assert_held(&cifs_tcp_ses_lock); lockdep_assert_held(&cifs_tcp_ses_lock);
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) static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)
......
...@@ -811,13 +811,9 @@ cifs_read_iter_from_socket(struct TCP_Server_Info *server, struct iov_iter *iter ...@@ -811,13 +811,9 @@ cifs_read_iter_from_socket(struct TCP_Server_Info *server, struct iov_iter *iter
unsigned int to_read) unsigned int to_read)
{ {
struct msghdr smb_msg = { .msg_iter = *iter }; struct msghdr smb_msg = { .msg_iter = *iter };
int ret;
iov_iter_truncate(&smb_msg.msg_iter, to_read); iov_iter_truncate(&smb_msg.msg_iter, to_read);
ret = cifs_readv_from_socket(server, &smb_msg); return cifs_readv_from_socket(server, &smb_msg);
if (ret > 0)
iov_iter_advance(iter, ret);
return ret;
} }
static bool static bool
...@@ -1530,6 +1526,9 @@ static int match_server(struct TCP_Server_Info *server, ...@@ -1530,6 +1526,9 @@ static int match_server(struct TCP_Server_Info *server,
if (server->nosharesock) if (server->nosharesock)
return 0; return 0;
if (!match_super && (ctx->dfs_conn || server->dfs_conn))
return 0;
/* If multidialect negotiation see if existing sessions match one */ /* If multidialect negotiation see if existing sessions match one */
if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) {
if (server->vals->protocol_id < SMB30_PROT_ID) if (server->vals->protocol_id < SMB30_PROT_ID)
...@@ -1723,6 +1722,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, ...@@ -1723,6 +1722,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
if (ctx->nosharesock) if (ctx->nosharesock)
tcp_ses->nosharesock = true; tcp_ses->nosharesock = true;
tcp_ses->dfs_conn = ctx->dfs_conn;
tcp_ses->ops = ctx->ops; tcp_ses->ops = ctx->ops;
tcp_ses->vals = ctx->vals; tcp_ses->vals = ctx->vals;
...@@ -1873,13 +1873,15 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, ...@@ -1873,13 +1873,15 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
} }
/* this function must be called with ses_lock and chan_lock held */ /* this function must be called with ses_lock and chan_lock held */
static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) static int match_session(struct cifs_ses *ses,
struct smb3_fs_context *ctx,
bool match_super)
{ {
if (ctx->sectype != Unspecified && if (ctx->sectype != Unspecified &&
ctx->sectype != ses->sectype) ctx->sectype != ses->sectype)
return 0; return 0;
if (ctx->dfs_root_ses != ses->dfs_root_ses) if (!match_super && ctx->dfs_root_ses != ses->dfs_root_ses)
return 0; return 0;
/* /*
...@@ -1998,7 +2000,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ...@@ -1998,7 +2000,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
continue; continue;
} }
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
if (match_session(ses, ctx)) { if (match_session(ses, ctx, false)) {
spin_unlock(&ses->chan_lock); spin_unlock(&ses->chan_lock);
spin_unlock(&ses->ses_lock); spin_unlock(&ses->ses_lock);
ret = ses; ret = ses;
...@@ -2058,8 +2060,7 @@ void __cifs_put_smb_ses(struct cifs_ses *ses) ...@@ -2058,8 +2060,7 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
if (do_logoff) { if (do_logoff) {
xid = get_xid(); xid = get_xid();
rc = server->ops->logoff(xid, ses); rc = server->ops->logoff(xid, ses);
if (rc) cifs_server_dbg(FYI, "%s: Session Logoff: rc=%d\n",
cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
__func__, rc); __func__, rc);
_free_xid(xid); _free_xid(xid);
} }
...@@ -2382,8 +2383,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ...@@ -2382,8 +2383,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
* need to lock before changing something in the session. * need to lock before changing something in the session.
*/ */
spin_lock(&cifs_tcp_ses_lock); 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; ses->dfs_root_ses = ctx->dfs_root_ses;
list_add(&ses->smb_ses_list, &server->smb_ses_list); list_add(&ses->smb_ses_list, &server->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
...@@ -2458,6 +2457,7 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) ...@@ -2458,6 +2457,7 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
{ {
unsigned int xid; unsigned int xid;
struct cifs_ses *ses; struct cifs_ses *ses;
LIST_HEAD(ses_list);
/* /*
* IPC tcon share the lifetime of their session and are * IPC tcon share the lifetime of their session and are
...@@ -2482,6 +2482,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) ...@@ -2482,6 +2482,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
list_del_init(&tcon->tcon_list); list_del_init(&tcon->tcon_list);
tcon->status = TID_EXITING; tcon->status = TID_EXITING;
#ifdef CONFIG_CIFS_DFS_UPCALL
list_replace_init(&tcon->dfs_ses_list, &ses_list);
#endif
spin_unlock(&tcon->tc_lock); spin_unlock(&tcon->tc_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
...@@ -2509,6 +2512,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) ...@@ -2509,6 +2512,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
cifs_fscache_release_super_cookie(tcon); cifs_fscache_release_super_cookie(tcon);
tconInfoFree(tcon, netfs_trace_tcon_ref_free); tconInfoFree(tcon, netfs_trace_tcon_ref_free);
cifs_put_smb_ses(ses); cifs_put_smb_ses(ses);
#ifdef CONFIG_CIFS_DFS_UPCALL
dfs_put_root_smb_sessions(&ses_list);
#endif
} }
/** /**
...@@ -2892,7 +2898,7 @@ cifs_match_super(struct super_block *sb, void *data) ...@@ -2892,7 +2898,7 @@ cifs_match_super(struct super_block *sb, void *data)
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
spin_lock(&tcon->tc_lock); spin_lock(&tcon->tc_lock);
if (!match_server(tcp_srv, ctx, true) || if (!match_server(tcp_srv, ctx, true) ||
!match_session(ses, ctx) || !match_session(ses, ctx, true) ||
!match_tcon(tcon, ctx) || !match_tcon(tcon, ctx) ||
!match_prepath(sb, tcon, mnt_data)) { !match_prepath(sb, tcon, mnt_data)) {
rc = 0; rc = 0;
...@@ -3623,13 +3629,12 @@ int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx) ...@@ -3623,13 +3629,12 @@ int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx)
int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
{ {
struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
bool isdfs;
int rc; int rc;
rc = dfs_mount_share(&mnt_ctx, &isdfs); rc = dfs_mount_share(&mnt_ctx);
if (rc) if (rc)
goto error; goto error;
if (!isdfs) if (!ctx->dfs_conn)
goto out; goto out;
/* /*
...@@ -4034,7 +4039,7 @@ cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) ...@@ -4034,7 +4039,7 @@ cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
} }
static struct cifs_tcon * 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; int rc;
struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb);
...@@ -4132,17 +4137,6 @@ __cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) ...@@ -4132,17 +4137,6 @@ __cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
return tcon; 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 * struct cifs_tcon *
cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
{ {
...@@ -4212,9 +4206,9 @@ tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink) ...@@ -4212,9 +4206,9 @@ tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink)
struct tcon_link * struct tcon_link *
cifs_sb_tlink(struct cifs_sb_info *cifs_sb) cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
{ {
int ret;
kuid_t fsuid = current_fsuid();
struct tcon_link *tlink, *newtlink; struct tcon_link *tlink, *newtlink;
kuid_t fsuid = current_fsuid();
int err;
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
...@@ -4249,9 +4243,9 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb) ...@@ -4249,9 +4243,9 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
spin_unlock(&cifs_sb->tlink_tree_lock); spin_unlock(&cifs_sb->tlink_tree_lock);
} else { } else {
wait_for_construction: wait_for_construction:
ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, err = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING,
TASK_INTERRUPTIBLE); TASK_INTERRUPTIBLE);
if (ret) { if (err) {
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return ERR_PTR(-ERESTARTSYS); return ERR_PTR(-ERESTARTSYS);
} }
...@@ -4262,8 +4256,9 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb) ...@@ -4262,8 +4256,9 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
/* return error if we tried this already recently */ /* return error if we tried this already recently */
if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) {
err = PTR_ERR(tlink->tl_tcon);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return ERR_PTR(-EACCES); return ERR_PTR(err);
} }
if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags))
...@@ -4275,8 +4270,11 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb) ...@@ -4275,8 +4270,11 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING);
if (IS_ERR(tlink->tl_tcon)) { if (IS_ERR(tlink->tl_tcon)) {
err = PTR_ERR(tlink->tl_tcon);
if (err == -ENOKEY)
err = -EACCES;
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return ERR_PTR(-EACCES); return ERR_PTR(err);
} }
return tlink; return tlink;
......
...@@ -69,7 +69,7 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) ...@@ -69,7 +69,7 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
* Get an active reference of @ses so that next call to cifs_put_tcon() won't * 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. * release it as any new DFS referrals must go through its IPC tcon.
*/ */
static void add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) static void set_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
{ {
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct cifs_ses *ses = mnt_ctx->ses; struct cifs_ses *ses = mnt_ctx->ses;
...@@ -95,7 +95,7 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx, ...@@ -95,7 +95,7 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx,
return rc; return rc;
} }
static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx, static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx,
struct dfs_info3_param *tgt, struct dfs_info3_param *tgt,
struct dfs_ref_walk *rw) struct dfs_ref_walk *rw)
{ {
...@@ -120,6 +120,7 @@ static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx, ...@@ -120,6 +120,7 @@ static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx,
} }
ref_walk_path(rw) = ref_path; ref_walk_path(rw) = ref_path;
ref_walk_fpath(rw) = full_path; ref_walk_fpath(rw) = full_path;
ref_walk_ses(rw) = ctx->dfs_root_ses;
return 0; return 0;
} }
...@@ -128,11 +129,11 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, ...@@ -128,11 +129,11 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
{ {
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct dfs_info3_param tgt = {}; struct dfs_info3_param tgt = {};
bool is_refsrv;
int rc = -ENOENT; int rc = -ENOENT;
again: again:
do { do {
ctx->dfs_root_ses = ref_walk_ses(rw);
if (ref_walk_empty(rw)) { if (ref_walk_empty(rw)) {
rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1, rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1,
NULL, ref_walk_tl(rw)); NULL, ref_walk_tl(rw));
...@@ -158,10 +159,7 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, ...@@ -158,10 +159,7 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
if (rc) if (rc)
continue; continue;
is_refsrv = tgt.server_type == DFS_TYPE_ROOT ||
DFS_INTERLINK(tgt.flags);
ref_walk_set_tgt_hint(rw); ref_walk_set_tgt_hint(rw);
if (tgt.flags & DFSREF_STORAGE_SERVER) { if (tgt.flags & DFSREF_STORAGE_SERVER) {
rc = cifs_mount_get_tcon(mnt_ctx); rc = cifs_mount_get_tcon(mnt_ctx);
if (!rc) if (!rc)
...@@ -172,12 +170,10 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, ...@@ -172,12 +170,10 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
continue; continue;
} }
if (is_refsrv) set_root_smb_session(mnt_ctx);
add_root_smb_session(mnt_ctx);
rc = ref_walk_advance(rw); rc = ref_walk_advance(rw);
if (!rc) { if (!rc) {
rc = set_ref_paths(mnt_ctx, &tgt, rw); rc = setup_dfs_ref(mnt_ctx, &tgt, rw);
if (!rc) { if (!rc) {
rc = -EREMOTE; rc = -EREMOTE;
goto again; goto again;
...@@ -193,20 +189,22 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, ...@@ -193,20 +189,22 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
return rc; return rc;
} }
static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx) static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
struct dfs_ref_walk **rw)
{ {
struct dfs_ref_walk *rw;
int rc; int rc;
rw = ref_walk_alloc(); *rw = ref_walk_alloc();
if (IS_ERR(rw)) if (IS_ERR(*rw)) {
return PTR_ERR(rw); rc = PTR_ERR(*rw);
*rw = NULL;
return rc;
}
ref_walk_init(rw); ref_walk_init(*rw);
rc = set_ref_paths(mnt_ctx, NULL, rw); rc = setup_dfs_ref(mnt_ctx, NULL, *rw);
if (!rc) if (!rc)
rc = __dfs_referral_walk(mnt_ctx, rw); rc = __dfs_referral_walk(mnt_ctx, *rw);
ref_walk_free(rw);
return rc; return rc;
} }
...@@ -214,16 +212,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) ...@@ -214,16 +212,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
{ {
struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct dfs_ref_walk *rw = NULL;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
char *origin_fullpath; char *origin_fullpath;
bool new_tcon = true;
int rc; int rc;
origin_fullpath = dfs_get_path(cifs_sb, ctx->source); origin_fullpath = dfs_get_path(cifs_sb, ctx->source);
if (IS_ERR(origin_fullpath)) if (IS_ERR(origin_fullpath))
return PTR_ERR(origin_fullpath); return PTR_ERR(origin_fullpath);
rc = dfs_referral_walk(mnt_ctx); rc = dfs_referral_walk(mnt_ctx, &rw);
if (!rc) { if (!rc) {
/* /*
* Prevent superblock from being created with any missing * Prevent superblock from being created with any missing
...@@ -241,21 +239,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) ...@@ -241,21 +239,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
tcon = mnt_ctx->tcon; tcon = mnt_ctx->tcon;
spin_lock(&tcon->tc_lock); spin_lock(&tcon->tc_lock);
if (!tcon->origin_fullpath) { tcon->origin_fullpath = origin_fullpath;
tcon->origin_fullpath = origin_fullpath; origin_fullpath = NULL;
origin_fullpath = NULL; ref_walk_set_tcon(rw, tcon);
} else {
new_tcon = false;
}
spin_unlock(&tcon->tc_lock); spin_unlock(&tcon->tc_lock);
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
if (new_tcon) { dfs_cache_get_ttl() * HZ);
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
dfs_cache_get_ttl() * HZ);
}
out: out:
kfree(origin_fullpath); kfree(origin_fullpath);
ref_walk_free(rw);
return rc; return rc;
} }
...@@ -279,7 +272,7 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx) ...@@ -279,7 +272,7 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
return rc; return rc;
} }
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
{ {
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
bool nodfs = ctx->nodfs; bool nodfs = ctx->nodfs;
...@@ -289,7 +282,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) ...@@ -289,7 +282,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
if (rc) if (rc)
return rc; return rc;
*isdfs = false;
rc = get_session(mnt_ctx, NULL); rc = get_session(mnt_ctx, NULL);
if (rc) if (rc)
return rc; return rc;
...@@ -317,10 +309,15 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) ...@@ -317,10 +309,15 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
return rc; return rc;
} }
*isdfs = true; if (!ctx->dfs_conn) {
add_root_smb_session(mnt_ctx); ctx->dfs_conn = true;
rc = __dfs_mount_share(mnt_ctx); cifs_mount_put_conns(mnt_ctx);
dfs_put_root_smb_sessions(mnt_ctx); rc = get_session(mnt_ctx, NULL);
}
if (!rc) {
set_root_smb_session(mnt_ctx);
rc = __dfs_mount_share(mnt_ctx);
}
return rc; return rc;
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
struct dfs_ref { struct dfs_ref {
char *path; char *path;
char *full_path; char *full_path;
struct cifs_ses *ses;
struct dfs_cache_tgt_list tl; struct dfs_cache_tgt_list tl;
struct dfs_cache_tgt_iterator *tit; struct dfs_cache_tgt_iterator *tit;
}; };
...@@ -38,6 +39,7 @@ struct dfs_ref_walk { ...@@ -38,6 +39,7 @@ struct dfs_ref_walk {
#define ref_walk_path(w) (ref_walk_cur(w)->path) #define ref_walk_path(w) (ref_walk_cur(w)->path)
#define ref_walk_fpath(w) (ref_walk_cur(w)->full_path) #define ref_walk_fpath(w) (ref_walk_cur(w)->full_path)
#define ref_walk_tl(w) (&ref_walk_cur(w)->tl) #define ref_walk_tl(w) (&ref_walk_cur(w)->tl)
#define ref_walk_ses(w) (ref_walk_cur(w)->ses)
static inline struct dfs_ref_walk *ref_walk_alloc(void) static inline struct dfs_ref_walk *ref_walk_alloc(void)
{ {
...@@ -60,14 +62,19 @@ static inline void __ref_walk_free(struct dfs_ref *ref) ...@@ -60,14 +62,19 @@ static inline void __ref_walk_free(struct dfs_ref *ref)
kfree(ref->path); kfree(ref->path);
kfree(ref->full_path); kfree(ref->full_path);
dfs_cache_free_tgts(&ref->tl); dfs_cache_free_tgts(&ref->tl);
if (ref->ses)
cifs_put_smb_ses(ref->ses);
memset(ref, 0, sizeof(*ref)); memset(ref, 0, sizeof(*ref));
} }
static inline void ref_walk_free(struct dfs_ref_walk *rw) static inline void ref_walk_free(struct dfs_ref_walk *rw)
{ {
struct dfs_ref *ref = ref_walk_start(rw); struct dfs_ref *ref;
for (; ref <= ref_walk_end(rw); ref++) if (!rw)
return;
for (ref = ref_walk_start(rw); ref <= ref_walk_end(rw); ref++)
__ref_walk_free(ref); __ref_walk_free(ref);
kfree(rw); kfree(rw);
} }
...@@ -116,9 +123,22 @@ static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw) ...@@ -116,9 +123,22 @@ static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw)
ref_walk_tit(rw)); ref_walk_tit(rw));
} }
static inline void ref_walk_set_tcon(struct dfs_ref_walk *rw,
struct cifs_tcon *tcon)
{
struct dfs_ref *ref = ref_walk_start(rw);
for (; ref <= ref_walk_cur(rw); ref++) {
if (WARN_ON_ONCE(!ref->ses))
continue;
list_add(&ref->ses->dlist, &tcon->dfs_ses_list);
ref->ses = NULL;
}
}
int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
struct smb3_fs_context *ctx); struct smb3_fs_context *ctx);
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs); int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx);
static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path) static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path)
{ {
...@@ -142,20 +162,14 @@ static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *p ...@@ -142,20 +162,14 @@ static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *p
* references of all DFS root sessions that were used across the mount process * references of all DFS root sessions that were used across the mount process
* in dfs_mount_share(). * in dfs_mount_share().
*/ */
static inline void dfs_put_root_smb_sessions(struct cifs_mount_ctx *mnt_ctx) static inline void dfs_put_root_smb_sessions(struct list_head *head)
{ {
const struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct cifs_ses *ses, *n;
struct cifs_ses *ses = ctx->dfs_root_ses;
struct cifs_ses *cur;
if (!ses)
return;
for (cur = ses; cur; cur = cur->dfs_root_ses) { list_for_each_entry_safe(ses, n, head, dlist) {
if (cur->dfs_root_ses) list_del_init(&ses->dlist);
cifs_put_smb_ses(cur->dfs_root_ses); cifs_put_smb_ses(ses);
} }
cifs_put_smb_ses(ses);
} }
#endif /* _CIFS_DFS_H */ #endif /* _CIFS_DFS_H */
...@@ -126,6 +126,7 @@ static inline void free_tgts(struct cache_entry *ce) ...@@ -126,6 +126,7 @@ static inline void free_tgts(struct cache_entry *ce)
static inline void flush_cache_ent(struct cache_entry *ce) static inline void flush_cache_ent(struct cache_entry *ce)
{ {
cifs_dbg(FYI, "%s: %s\n", __func__, ce->path);
hlist_del_init(&ce->hlist); hlist_del_init(&ce->hlist);
kfree(ce->path); kfree(ce->path);
free_tgts(ce); free_tgts(ce);
...@@ -441,34 +442,31 @@ static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int n ...@@ -441,34 +442,31 @@ static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int n
return ce; return ce;
} }
static void remove_oldest_entry_locked(void) /* Remove all referrals that have a single target or oldest entry */
static void purge_cache(void)
{ {
int i; int i;
struct cache_entry *ce; struct cache_entry *ce;
struct cache_entry *to_del = NULL; struct cache_entry *oldest = NULL;
WARN_ON(!rwsem_is_locked(&htable_rw_lock));
for (i = 0; i < CACHE_HTABLE_SIZE; i++) { for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
struct hlist_head *l = &cache_htable[i]; struct hlist_head *l = &cache_htable[i];
struct hlist_node *n;
hlist_for_each_entry(ce, l, hlist) { hlist_for_each_entry_safe(ce, n, l, hlist) {
if (hlist_unhashed(&ce->hlist)) if (hlist_unhashed(&ce->hlist))
continue; continue;
if (!to_del || timespec64_compare(&ce->etime, if (ce->numtgts == 1)
&to_del->etime) < 0) flush_cache_ent(ce);
to_del = ce; else if (!oldest ||
timespec64_compare(&ce->etime,
&oldest->etime) < 0)
oldest = ce;
} }
} }
if (!to_del) { if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES && oldest)
cifs_dbg(FYI, "%s: no entry to remove\n", __func__); flush_cache_ent(oldest);
return;
}
cifs_dbg(FYI, "%s: removing entry\n", __func__);
dump_ce(to_del);
flush_cache_ent(to_del);
} }
/* Add a new DFS cache entry */ /* Add a new DFS cache entry */
...@@ -484,7 +482,7 @@ static struct cache_entry *add_cache_entry_locked(struct dfs_info3_param *refs, ...@@ -484,7 +482,7 @@ static struct cache_entry *add_cache_entry_locked(struct dfs_info3_param *refs,
if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) { if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) {
cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES); cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES);
remove_oldest_entry_locked(); purge_cache();
} }
rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash); rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash);
...@@ -1095,16 +1093,18 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, ...@@ -1095,16 +1093,18 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
return 0; return 0;
} }
static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2) static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
{ {
char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; struct TCP_Server_Info *server = tcon->ses->server;
struct sockaddr_storage ss;
const char *host; const char *host;
const char *s2 = &tcon->tree_name[1];
size_t hostlen; size_t hostlen;
struct sockaddr_storage ss; char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0};
bool match; bool match;
int rc; int rc;
if (strcasecmp(s1, s2)) if (strcasecmp(s2, s1))
return false; return false;
/* /*
...@@ -1128,34 +1128,6 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c ...@@ -1128,34 +1128,6 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c
return match; return match;
} }
/*
* Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new
* target shares in @refs.
*/
static void mark_for_reconnect_if_needed(struct TCP_Server_Info *server,
const char *path,
struct dfs_cache_tgt_list *old_tl,
struct dfs_cache_tgt_list *new_tl)
{
struct dfs_cache_tgt_iterator *oit, *nit;
for (oit = dfs_cache_get_tgt_iterator(old_tl); oit;
oit = dfs_cache_get_next_tgt(old_tl, oit)) {
for (nit = dfs_cache_get_tgt_iterator(new_tl); nit;
nit = dfs_cache_get_next_tgt(new_tl, nit)) {
if (target_share_equal(server,
dfs_cache_get_tgt_name(oit),
dfs_cache_get_tgt_name(nit))) {
dfs_cache_noreq_update_tgthint(path, nit);
return;
}
}
}
cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__);
cifs_signal_cifsd_for_reconnect(server, true);
}
static bool is_ses_good(struct cifs_ses *ses) static bool is_ses_good(struct cifs_ses *ses)
{ {
struct TCP_Server_Info *server = ses->server; struct TCP_Server_Info *server = ses->server;
...@@ -1172,41 +1144,35 @@ static bool is_ses_good(struct cifs_ses *ses) ...@@ -1172,41 +1144,35 @@ static bool is_ses_good(struct cifs_ses *ses)
return ret; return ret;
} }
/* Refresh dfs referral of @ses and mark it for reconnect if needed */ static char *get_ses_refpath(struct cifs_ses *ses)
static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh)
{ {
struct TCP_Server_Info *server = ses->server; struct TCP_Server_Info *server = ses->server;
DFS_CACHE_TGT_LIST(old_tl); char *path = ERR_PTR(-ENOENT);
DFS_CACHE_TGT_LIST(new_tl);
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); mutex_lock(&server->refpath_lock);
if (server->leaf_fullpath) { if (server->leaf_fullpath) {
path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC); path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC);
if (!path) if (!path)
rc = -ENOMEM; path = ERR_PTR(-ENOMEM);
} }
mutex_unlock(&server->refpath_lock); mutex_unlock(&server->refpath_lock);
if (!path) return path;
goto out; }
down_read(&htable_rw_lock); /* Refresh dfs referral of @ses */
ce = lookup_cache_entry(path); static void refresh_ses_referral(struct cifs_ses *ses)
needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); {
if (!IS_ERR(ce)) { struct cache_entry *ce;
rc = get_targets(ce, &old_tl); unsigned int xid;
cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc); char *path;
} int rc = 0;
up_read(&htable_rw_lock);
if (!needs_refresh) { xid = get_xid();
rc = 0;
path = get_ses_refpath(ses);
if (IS_ERR(path)) {
rc = PTR_ERR(path);
path = NULL;
goto out; goto out;
} }
...@@ -1217,29 +1183,106 @@ static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh) ...@@ -1217,29 +1183,106 @@ static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh)
goto out; goto out;
} }
ce = cache_refresh_path(xid, ses, path, true); ce = cache_refresh_path(xid, ses, path, false);
if (!IS_ERR(ce)) { if (!IS_ERR(ce))
rc = get_targets(ce, &new_tl);
up_read(&htable_rw_lock); up_read(&htable_rw_lock);
cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc); else
mark_for_reconnect_if_needed(server, path, &old_tl, &new_tl); rc = PTR_ERR(ce);
}
out: out:
free_xid(xid); free_xid(xid);
dfs_cache_free_tgts(&old_tl);
dfs_cache_free_tgts(&new_tl);
kfree(path); kfree(path);
} }
static inline void refresh_ses_referral(struct cifs_ses *ses) static int __refresh_tcon_referral(struct cifs_tcon *tcon,
const char *path,
struct dfs_info3_param *refs,
int numrefs, bool force_refresh)
{ {
__refresh_ses_referral(ses, false); struct cache_entry *ce;
bool reconnect = force_refresh;
int rc = 0;
int i;
if (unlikely(!numrefs))
return 0;
if (force_refresh) {
for (i = 0; i < numrefs; i++) {
/* TODO: include prefix paths in the matching */
if (target_share_equal(tcon, refs[i].node_name)) {
reconnect = false;
break;
}
}
}
down_write(&htable_rw_lock);
ce = lookup_cache_entry(path);
if (!IS_ERR(ce)) {
if (force_refresh || cache_entry_expired(ce))
rc = update_cache_entry_locked(ce, refs, numrefs);
} else if (PTR_ERR(ce) == -ENOENT) {
ce = add_cache_entry_locked(refs, numrefs);
}
up_write(&htable_rw_lock);
if (IS_ERR(ce))
rc = PTR_ERR(ce);
if (reconnect) {
cifs_tcon_dbg(FYI, "%s: mark for reconnect\n", __func__);
cifs_signal_cifsd_for_reconnect(tcon->ses->server, true);
}
return rc;
} }
static inline void force_refresh_ses_referral(struct cifs_ses *ses) static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh)
{ {
__refresh_ses_referral(ses, true); struct dfs_info3_param *refs = NULL;
struct cache_entry *ce;
struct cifs_ses *ses;
unsigned int xid;
bool needs_refresh;
char *path;
int numrefs = 0;
int rc = 0;
xid = get_xid();
ses = tcon->ses;
path = get_ses_refpath(ses);
if (IS_ERR(path)) {
rc = PTR_ERR(path);
path = NULL;
goto out;
}
down_read(&htable_rw_lock);
ce = lookup_cache_entry(path);
needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce);
if (!needs_refresh) {
up_read(&htable_rw_lock);
goto out;
}
up_read(&htable_rw_lock);
ses = CIFS_DFS_ROOT_SES(ses);
if (!is_ses_good(ses)) {
cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
__func__);
goto out;
}
rc = get_dfs_referral(xid, ses, path, &refs, &numrefs);
if (!rc) {
rc = __refresh_tcon_referral(tcon, path, refs,
numrefs, force_refresh);
}
out:
free_xid(xid);
kfree(path);
free_dfs_info_array(refs, numrefs);
} }
/** /**
...@@ -1280,7 +1323,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) ...@@ -1280,7 +1323,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
*/ */
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
force_refresh_ses_referral(tcon->ses); refresh_tcon_referral(tcon, true);
return 0; return 0;
} }
...@@ -1292,8 +1335,9 @@ void dfs_cache_refresh(struct work_struct *work) ...@@ -1292,8 +1335,9 @@ void dfs_cache_refresh(struct work_struct *work)
tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work); tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work);
for (ses = tcon->ses; ses; ses = ses->dfs_root_ses) list_for_each_entry(ses, &tcon->dfs_ses_list, dlist)
refresh_ses_referral(ses); refresh_ses_referral(ses);
refresh_tcon_referral(tcon, false);
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
atomic_read(&dfs_cache_ttl) * HZ); atomic_read(&dfs_cache_ttl) * HZ);
......
...@@ -284,6 +284,7 @@ struct smb3_fs_context { ...@@ -284,6 +284,7 @@ struct smb3_fs_context {
struct cifs_ses *dfs_root_ses; struct cifs_ses *dfs_root_ses;
bool dfs_automount:1; /* set for dfs automount only */ bool dfs_automount:1; /* set for dfs automount only */
enum cifs_reparse_type reparse_type; enum cifs_reparse_type reparse_type;
bool dfs_conn:1; /* set for dfs mounts */
}; };
extern const struct fs_parameter_spec smb3_fs_parameters[]; extern const struct fs_parameter_spec smb3_fs_parameters[];
......
...@@ -834,10 +834,6 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, ...@@ -834,10 +834,6 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_REG; fattr->cf_dtype = DT_REG;
/* clear write bits if ATTR_READONLY is set */
if (fattr->cf_cifsattrs & ATTR_READONLY)
fattr->cf_mode &= ~(S_IWUGO);
/* /*
* Don't accept zero nlink from non-unix servers unless * Don't accept zero nlink from non-unix servers unless
* delete is pending. Instead mark it as unknown. * delete is pending. Instead mark it as unknown.
...@@ -850,6 +846,10 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, ...@@ -850,6 +846,10 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
} }
} }
/* clear write bits if ATTR_READONLY is set */
if (fattr->cf_cifsattrs & ATTR_READONLY)
fattr->cf_mode &= ~(S_IWUGO);
out_reparse: out_reparse:
if (S_ISLNK(fattr->cf_mode)) { if (S_ISLNK(fattr->cf_mode)) {
if (likely(data->symlink_target)) if (likely(data->symlink_target))
...@@ -1267,11 +1267,14 @@ static int cifs_get_fattr(struct cifs_open_info_data *data, ...@@ -1267,11 +1267,14 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
__func__, rc); __func__, rc);
goto out; goto out;
} }
} } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
/* fill in remaining high mode bits e.g. SUID, VTX */
/* fill in remaining high mode bits e.g. SUID, VTX */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
cifs_sfu_mode(fattr, full_path, cifs_sb, xid); cifs_sfu_mode(fattr, full_path, cifs_sb, xid);
else if (!(tcon->posix_extensions))
/* clear write bits if ATTR_READONLY is set */
if (fattr->cf_cifsattrs & ATTR_READONLY)
fattr->cf_mode &= ~(S_IWUGO);
/* check for Minshall+French symlinks */ /* check for Minshall+French symlinks */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
......
...@@ -145,6 +145,9 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace) ...@@ -145,6 +145,9 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace)
mutex_init(&ret_buf->fscache_lock); mutex_init(&ret_buf->fscache_lock);
#endif #endif
trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace); trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace);
#ifdef CONFIG_CIFS_DFS_UPCALL
INIT_LIST_HEAD(&ret_buf->dfs_ses_list);
#endif
return ret_buf; return ret_buf;
} }
...@@ -1108,7 +1111,8 @@ static void tcon_super_cb(struct super_block *sb, void *arg) ...@@ -1108,7 +1111,8 @@ static void tcon_super_cb(struct super_block *sb, void *arg)
t2 = cifs_sb_master_tcon(cifs_sb); t2 = cifs_sb_master_tcon(cifs_sb);
spin_lock(&t2->tc_lock); spin_lock(&t2->tc_lock);
if (t1->ses == t2->ses && if ((t1->ses == t2->ses ||
t1->ses->dfs_root_ses == t2->ses->dfs_root_ses) &&
t1->ses->server == t2->ses->server && t1->ses->server == t2->ses->server &&
t2->origin_fullpath && t2->origin_fullpath &&
dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath)) dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath))
......
...@@ -240,7 +240,7 @@ static struct vfsmount *cifs_do_automount(struct path *path) ...@@ -240,7 +240,7 @@ static struct vfsmount *cifs_do_automount(struct path *path)
ctx->source = NULL; ctx->source = NULL;
goto out; goto out;
} }
ctx->dfs_automount = is_dfs_mount(mntpt); ctx->dfs_automount = ctx->dfs_conn = is_dfs_mount(mntpt);
cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dfs_automount=%d\n", cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dfs_automount=%d\n",
__func__, ctx->source, ctx->UNC, ctx->prepath, ctx->dfs_automount); __func__, ctx->source, ctx->UNC, ctx->prepath, ctx->dfs_automount);
......
...@@ -108,8 +108,8 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf, ...@@ -108,8 +108,8 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
buf->InodeType = cpu_to_le64(type); buf->InodeType = cpu_to_le64(type);
buf->ReparseDataLength = cpu_to_le16(len + dlen - buf->ReparseDataLength = cpu_to_le16(len + dlen -
sizeof(struct reparse_data_buffer)); sizeof(struct reparse_data_buffer));
*(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) | *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MINOR(dev) << 32) |
MINOR(dev)); MAJOR(dev));
iov->iov_base = buf; iov->iov_base = buf;
iov->iov_len = len + dlen; iov->iov_len = len + dlen;
return 0; return 0;
...@@ -468,7 +468,7 @@ static void wsl_to_fattr(struct cifs_open_info_data *data, ...@@ -468,7 +468,7 @@ static void wsl_to_fattr(struct cifs_open_info_data *data,
else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen))
fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v); fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen))
fattr->cf_rdev = wsl_mkdev(v); fattr->cf_rdev = reparse_mkdev(v);
} while (next); } while (next);
out: out:
fattr->cf_dtype = S_DT(fattr->cf_mode); fattr->cf_dtype = S_DT(fattr->cf_mode);
...@@ -485,11 +485,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, ...@@ -485,11 +485,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
switch (le64_to_cpu(buf->InodeType)) { switch (le64_to_cpu(buf->InodeType)) {
case NFS_SPECFILE_CHR: case NFS_SPECFILE_CHR:
fattr->cf_mode |= S_IFCHR; fattr->cf_mode |= S_IFCHR;
fattr->cf_rdev = reparse_nfs_mkdev(buf); fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break; break;
case NFS_SPECFILE_BLK: case NFS_SPECFILE_BLK:
fattr->cf_mode |= S_IFBLK; fattr->cf_mode |= S_IFBLK;
fattr->cf_rdev = reparse_nfs_mkdev(buf); fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break; break;
case NFS_SPECFILE_FIFO: case NFS_SPECFILE_FIFO:
fattr->cf_mode |= S_IFIFO; fattr->cf_mode |= S_IFIFO;
......
...@@ -18,14 +18,7 @@ ...@@ -18,14 +18,7 @@
*/ */
#define IO_REPARSE_TAG_INTERNAL ((__u32)~0U) #define IO_REPARSE_TAG_INTERNAL ((__u32)~0U)
static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) static inline dev_t reparse_mkdev(void *ptr)
{
u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
return MKDEV(v >> 32, v & 0xffffffff);
}
static inline dev_t wsl_mkdev(void *ptr)
{ {
u64 v = le64_to_cpu(*(__le64 *)ptr); u64 v = le64_to_cpu(*(__le64 *)ptr);
......
...@@ -4869,9 +4869,12 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, ...@@ -4869,9 +4869,12 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
goto discard_data; goto discard_data;
server->total_read += rc; server->total_read += rc;
if (rc < len) if (rc < len) {
iov_iter_zero(len - rc, &iter); struct iov_iter tmp = iter;
iov_iter_revert(&iter, len);
iov_iter_advance(&tmp, rc);
iov_iter_zero(len - rc, &tmp);
}
iov_iter_truncate(&iter, dw->len); iov_iter_truncate(&iter, dw->len);
rc = cifs_discard_remaining_data(server); rc = cifs_discard_remaining_data(server);
......
...@@ -4866,7 +4866,9 @@ smb2_writev_callback(struct mid_q_entry *mid) ...@@ -4866,7 +4866,9 @@ smb2_writev_callback(struct mid_q_entry *mid)
#endif #endif
if (result) { if (result) {
cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
trace_smb3_write_err(wdata->xid, trace_smb3_write_err(wdata->rreq->debug_id,
wdata->subreq.debug_index,
wdata->xid,
wdata->req->cfile->fid.persistent_fid, wdata->req->cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, wdata->subreq.start, tcon->tid, tcon->ses->Suid, wdata->subreq.start,
wdata->subreq.len, wdata->result); wdata->subreq.len, wdata->result);
...@@ -4874,7 +4876,9 @@ smb2_writev_callback(struct mid_q_entry *mid) ...@@ -4874,7 +4876,9 @@ smb2_writev_callback(struct mid_q_entry *mid)
pr_warn_once("Out of space writing to %s\n", pr_warn_once("Out of space writing to %s\n",
tcon->tree_name); tcon->tree_name);
} else } else
trace_smb3_write_done(0 /* no xid */, trace_smb3_write_done(wdata->rreq->debug_id,
wdata->subreq.debug_index,
wdata->xid,
wdata->req->cfile->fid.persistent_fid, wdata->req->cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, tcon->tid, tcon->ses->Suid,
wdata->subreq.start, wdata->subreq.len); wdata->subreq.start, wdata->subreq.len);
...@@ -4952,7 +4956,9 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) ...@@ -4952,7 +4956,9 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
offsetof(struct smb2_write_req, Buffer)); offsetof(struct smb2_write_req, Buffer));
req->RemainingBytes = 0; req->RemainingBytes = 0;
trace_smb3_write_enter(wdata->xid, trace_smb3_write_enter(wdata->rreq->debug_id,
wdata->subreq.debug_index,
wdata->xid,
io_parms->persistent_fid, io_parms->persistent_fid,
io_parms->tcon->tid, io_parms->tcon->tid,
io_parms->tcon->ses->Suid, io_parms->tcon->ses->Suid,
...@@ -5032,7 +5038,9 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) ...@@ -5032,7 +5038,9 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
wdata, flags, &wdata->credits); wdata, flags, &wdata->credits);
/* Can't touch wdata if rc == 0 */ /* Can't touch wdata if rc == 0 */
if (rc) { if (rc) {
trace_smb3_write_err(xid, trace_smb3_write_err(wdata->rreq->debug_id,
wdata->subreq.debug_index,
xid,
io_parms->persistent_fid, io_parms->persistent_fid,
io_parms->tcon->tid, io_parms->tcon->tid,
io_parms->tcon->ses->Suid, io_parms->tcon->ses->Suid,
...@@ -5112,7 +5120,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, ...@@ -5112,7 +5120,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
offsetof(struct smb2_write_req, Buffer)); offsetof(struct smb2_write_req, Buffer));
req->RemainingBytes = 0; req->RemainingBytes = 0;
trace_smb3_write_enter(xid, io_parms->persistent_fid, trace_smb3_write_enter(0, 0, xid, io_parms->persistent_fid,
io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->tcon->tid, io_parms->tcon->ses->Suid,
io_parms->offset, io_parms->length); io_parms->offset, io_parms->length);
...@@ -5133,7 +5141,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, ...@@ -5133,7 +5141,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; rsp = (struct smb2_write_rsp *)rsp_iov.iov_base;
if (rc) { if (rc) {
trace_smb3_write_err(xid, trace_smb3_write_err(0, 0, xid,
req->PersistentFileId, req->PersistentFileId,
io_parms->tcon->tid, io_parms->tcon->tid,
io_parms->tcon->ses->Suid, io_parms->tcon->ses->Suid,
...@@ -5142,7 +5150,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, ...@@ -5142,7 +5150,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
cifs_dbg(VFS, "Send error in write = %d\n", rc); cifs_dbg(VFS, "Send error in write = %d\n", rc);
} else { } else {
*nbytes = le32_to_cpu(rsp->DataLength); *nbytes = le32_to_cpu(rsp->DataLength);
trace_smb3_write_done(xid, trace_smb3_write_done(0, 0, xid,
req->PersistentFileId, req->PersistentFileId,
io_parms->tcon->tid, io_parms->tcon->tid,
io_parms->tcon->ses->Suid, io_parms->tcon->ses->Suid,
......
...@@ -157,6 +157,7 @@ DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \ ...@@ -157,6 +157,7 @@ DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \
TP_ARGS(rreq_debug_id, rreq_debug_index, xid, fid, tid, sesid, offset, len, rc)) TP_ARGS(rreq_debug_id, rreq_debug_index, xid, fid, tid, sesid, offset, len, rc))
DEFINE_SMB3_RW_ERR_EVENT(read_err); DEFINE_SMB3_RW_ERR_EVENT(read_err);
DEFINE_SMB3_RW_ERR_EVENT(write_err);
/* For logging errors in other file I/O ops */ /* For logging errors in other file I/O ops */
DECLARE_EVENT_CLASS(smb3_other_err_class, DECLARE_EVENT_CLASS(smb3_other_err_class,
...@@ -202,7 +203,6 @@ DEFINE_EVENT(smb3_other_err_class, smb3_##name, \ ...@@ -202,7 +203,6 @@ DEFINE_EVENT(smb3_other_err_class, smb3_##name, \
int rc), \ int rc), \
TP_ARGS(xid, fid, tid, sesid, offset, len, rc)) TP_ARGS(xid, fid, tid, sesid, offset, len, rc))
DEFINE_SMB3_OTHER_ERR_EVENT(write_err);
DEFINE_SMB3_OTHER_ERR_EVENT(query_dir_err); DEFINE_SMB3_OTHER_ERR_EVENT(query_dir_err);
DEFINE_SMB3_OTHER_ERR_EVENT(zero_err); DEFINE_SMB3_OTHER_ERR_EVENT(zero_err);
DEFINE_SMB3_OTHER_ERR_EVENT(falloc_err); DEFINE_SMB3_OTHER_ERR_EVENT(falloc_err);
...@@ -370,6 +370,8 @@ DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \ ...@@ -370,6 +370,8 @@ DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \
DEFINE_SMB3_RW_DONE_EVENT(read_enter); DEFINE_SMB3_RW_DONE_EVENT(read_enter);
DEFINE_SMB3_RW_DONE_EVENT(read_done); DEFINE_SMB3_RW_DONE_EVENT(read_done);
DEFINE_SMB3_RW_DONE_EVENT(write_enter);
DEFINE_SMB3_RW_DONE_EVENT(write_done);
/* For logging successful other op */ /* For logging successful other op */
DECLARE_EVENT_CLASS(smb3_other_done_class, DECLARE_EVENT_CLASS(smb3_other_done_class,
...@@ -411,11 +413,9 @@ DEFINE_EVENT(smb3_other_done_class, smb3_##name, \ ...@@ -411,11 +413,9 @@ DEFINE_EVENT(smb3_other_done_class, smb3_##name, \
__u32 len), \ __u32 len), \
TP_ARGS(xid, fid, tid, sesid, offset, len)) TP_ARGS(xid, fid, tid, sesid, offset, len))
DEFINE_SMB3_OTHER_DONE_EVENT(write_enter);
DEFINE_SMB3_OTHER_DONE_EVENT(query_dir_enter); DEFINE_SMB3_OTHER_DONE_EVENT(query_dir_enter);
DEFINE_SMB3_OTHER_DONE_EVENT(zero_enter); DEFINE_SMB3_OTHER_DONE_EVENT(zero_enter);
DEFINE_SMB3_OTHER_DONE_EVENT(falloc_enter); DEFINE_SMB3_OTHER_DONE_EVENT(falloc_enter);
DEFINE_SMB3_OTHER_DONE_EVENT(write_done);
DEFINE_SMB3_OTHER_DONE_EVENT(query_dir_done); DEFINE_SMB3_OTHER_DONE_EVENT(query_dir_done);
DEFINE_SMB3_OTHER_DONE_EVENT(zero_done); DEFINE_SMB3_OTHER_DONE_EVENT(zero_done);
DEFINE_SMB3_OTHER_DONE_EVENT(falloc_done); DEFINE_SMB3_OTHER_DONE_EVENT(falloc_done);
......
...@@ -1817,11 +1817,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1817,11 +1817,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
length = data_len; /* An RDMA read is already done. */ length = data_len; /* An RDMA read is already done. */
else else
#endif #endif
{
length = cifs_read_iter_from_socket(server, &rdata->subreq.io_iter, length = cifs_read_iter_from_socket(server, &rdata->subreq.io_iter,
data_len); data_len);
iov_iter_revert(&rdata->subreq.io_iter, data_len);
}
if (length > 0) if (length > 0)
rdata->got_bytes += length; rdata->got_bytes += length;
server->total_read += length; server->total_read += length;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment