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
trace_netfs_folio(folio, netfs_folio_trace_mkwrite);
netfs_set_group(folio, netfs_group);
file_update_time(file);
set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags);
if (ictx->ops->post_modify)
ictx->ops->post_modify(inode);
ret = VM_FAULT_LOCKED;
......
......@@ -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 50
#define CIFS_VERSION "2.50"
#define SMB3_PRODUCT_BUILD 51
#define CIFS_VERSION "2.51"
#endif /* _CIFSFS_H */
......@@ -821,6 +821,7 @@ struct TCP_Server_Info {
* format: \\HOST\SHARE[\OPTIONAL PATH]
*/
char *leaf_fullpath;
bool dfs_conn:1;
};
static inline bool is_smb1(struct TCP_Server_Info *server)
......@@ -1059,6 +1060,7 @@ struct cifs_ses {
struct list_head smb_ses_list;
struct list_head rlist; /* reconnect list */
struct list_head tcon_list;
struct list_head dlist; /* dfs list */
struct cifs_tcon *tcon_ipc;
spinlock_t ses_lock; /* protect anything here that is not protected */
struct mutex session_mutex;
......@@ -1287,6 +1289,7 @@ struct cifs_tcon {
/* BB add field for back pointer to sb struct(s)? */
#ifdef CONFIG_CIFS_DFS_UPCALL
struct delayed_work dfs_cache_work;
struct list_head dfs_ses_list;
#endif
struct delayed_work query_interfaces; /* query interfaces workqueue job */
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)
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)
{
struct cifs_ses *next;
do {
next = ses->dfs_root_ses;
__cifs_put_smb_ses(ses);
} while ((ses = next));
__cifs_put_smb_ses(ses);
}
/* Get an active reference of @ses and its children.
......@@ -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)
{
lockdep_assert_held(&cifs_tcp_ses_lock);
for (; ses; ses = ses->dfs_root_ses)
ses->ses_count++;
ses->ses_count++;
}
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
unsigned int to_read)
{
struct msghdr smb_msg = { .msg_iter = *iter };
int ret;
iov_iter_truncate(&smb_msg.msg_iter, to_read);
ret = cifs_readv_from_socket(server, &smb_msg);
if (ret > 0)
iov_iter_advance(iter, ret);
return ret;
return cifs_readv_from_socket(server, &smb_msg);
}
static bool
......@@ -1530,6 +1526,9 @@ static int match_server(struct TCP_Server_Info *server,
if (server->nosharesock)
return 0;
if (!match_super && (ctx->dfs_conn || server->dfs_conn))
return 0;
/* If multidialect negotiation see if existing sessions match one */
if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) {
if (server->vals->protocol_id < SMB30_PROT_ID)
......@@ -1723,6 +1722,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
if (ctx->nosharesock)
tcp_ses->nosharesock = true;
tcp_ses->dfs_conn = ctx->dfs_conn;
tcp_ses->ops = ctx->ops;
tcp_ses->vals = ctx->vals;
......@@ -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 */
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 &&
ctx->sectype != ses->sectype)
return 0;
if (ctx->dfs_root_ses != ses->dfs_root_ses)
if (!match_super && ctx->dfs_root_ses != ses->dfs_root_ses)
return 0;
/*
......@@ -1998,7 +2000,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
continue;
}
spin_lock(&ses->chan_lock);
if (match_session(ses, ctx)) {
if (match_session(ses, ctx, false)) {
spin_unlock(&ses->chan_lock);
spin_unlock(&ses->ses_lock);
ret = ses;
......@@ -2058,8 +2060,7 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
if (do_logoff) {
xid = get_xid();
rc = server->ops->logoff(xid, ses);
if (rc)
cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
cifs_server_dbg(FYI, "%s: Session Logoff: rc=%d\n",
__func__, rc);
_free_xid(xid);
}
......@@ -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.
*/
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;
list_add(&ses->smb_ses_list, &server->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
......@@ -2458,6 +2457,7 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
{
unsigned int xid;
struct cifs_ses *ses;
LIST_HEAD(ses_list);
/*
* 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)
list_del_init(&tcon->tcon_list);
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(&cifs_tcp_ses_lock);
......@@ -2509,6 +2512,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
cifs_fscache_release_super_cookie(tcon);
tconInfoFree(tcon, netfs_trace_tcon_ref_free);
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)
spin_lock(&ses->chan_lock);
spin_lock(&tcon->tc_lock);
if (!match_server(tcp_srv, ctx, true) ||
!match_session(ses, ctx) ||
!match_session(ses, ctx, true) ||
!match_tcon(tcon, ctx) ||
!match_prepath(sb, tcon, mnt_data)) {
rc = 0;
......@@ -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)
{
struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
bool isdfs;
int rc;
rc = dfs_mount_share(&mnt_ctx, &isdfs);
rc = dfs_mount_share(&mnt_ctx);
if (rc)
goto error;
if (!isdfs)
if (!ctx->dfs_conn)
goto out;
/*
......@@ -4034,7 +4039,7 @@ cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
}
static struct cifs_tcon *
__cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
{
int rc;
struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb);
......@@ -4132,17 +4137,6 @@ __cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
return tcon;
}
static struct cifs_tcon *
cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
{
struct cifs_tcon *ret;
cifs_mount_lock();
ret = __cifs_construct_tcon(cifs_sb, fsuid);
cifs_mount_unlock();
return ret;
}
struct cifs_tcon *
cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
{
......@@ -4212,9 +4206,9 @@ tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink)
struct tcon_link *
cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
{
int ret;
kuid_t fsuid = current_fsuid();
struct tcon_link *tlink, *newtlink;
kuid_t fsuid = current_fsuid();
int err;
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
......@@ -4249,9 +4243,9 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
spin_unlock(&cifs_sb->tlink_tree_lock);
} else {
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);
if (ret) {
if (err) {
cifs_put_tlink(tlink);
return ERR_PTR(-ERESTARTSYS);
}
......@@ -4262,8 +4256,9 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
/* return error if we tried this already recently */
if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) {
err = PTR_ERR(tlink->tl_tcon);
cifs_put_tlink(tlink);
return ERR_PTR(-EACCES);
return ERR_PTR(err);
}
if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags))
......@@ -4275,8 +4270,11 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING);
if (IS_ERR(tlink->tl_tcon)) {
err = PTR_ERR(tlink->tl_tcon);
if (err == -ENOKEY)
err = -EACCES;
cifs_put_tlink(tlink);
return ERR_PTR(-EACCES);
return ERR_PTR(err);
}
return tlink;
......
......@@ -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
* 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 cifs_ses *ses = mnt_ctx->ses;
......@@ -95,7 +95,7 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx,
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_ref_walk *rw)
{
......@@ -120,6 +120,7 @@ static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx,
}
ref_walk_path(rw) = ref_path;
ref_walk_fpath(rw) = full_path;
ref_walk_ses(rw) = ctx->dfs_root_ses;
return 0;
}
......@@ -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 dfs_info3_param tgt = {};
bool is_refsrv;
int rc = -ENOENT;
again:
do {
ctx->dfs_root_ses = ref_walk_ses(rw);
if (ref_walk_empty(rw)) {
rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1,
NULL, ref_walk_tl(rw));
......@@ -158,10 +159,7 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
if (rc)
continue;
is_refsrv = tgt.server_type == DFS_TYPE_ROOT ||
DFS_INTERLINK(tgt.flags);
ref_walk_set_tgt_hint(rw);
if (tgt.flags & DFSREF_STORAGE_SERVER) {
rc = cifs_mount_get_tcon(mnt_ctx);
if (!rc)
......@@ -172,12 +170,10 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
continue;
}
if (is_refsrv)
add_root_smb_session(mnt_ctx);
set_root_smb_session(mnt_ctx);
rc = ref_walk_advance(rw);
if (!rc) {
rc = set_ref_paths(mnt_ctx, &tgt, rw);
rc = setup_dfs_ref(mnt_ctx, &tgt, rw);
if (!rc) {
rc = -EREMOTE;
goto again;
......@@ -193,20 +189,22 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
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;
rw = ref_walk_alloc();
if (IS_ERR(rw))
return PTR_ERR(rw);
*rw = ref_walk_alloc();
if (IS_ERR(*rw)) {
rc = PTR_ERR(*rw);
*rw = NULL;
return rc;
}
ref_walk_init(rw);
rc = set_ref_paths(mnt_ctx, NULL, rw);
ref_walk_init(*rw);
rc = setup_dfs_ref(mnt_ctx, NULL, *rw);
if (!rc)
rc = __dfs_referral_walk(mnt_ctx, rw);
ref_walk_free(rw);
rc = __dfs_referral_walk(mnt_ctx, *rw);
return rc;
}
......@@ -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 smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct dfs_ref_walk *rw = NULL;
struct cifs_tcon *tcon;
char *origin_fullpath;
bool new_tcon = true;
int rc;
origin_fullpath = dfs_get_path(cifs_sb, ctx->source);
if (IS_ERR(origin_fullpath))
return PTR_ERR(origin_fullpath);
rc = dfs_referral_walk(mnt_ctx);
rc = dfs_referral_walk(mnt_ctx, &rw);
if (!rc) {
/*
* Prevent superblock from being created with any missing
......@@ -241,21 +239,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
tcon = mnt_ctx->tcon;
spin_lock(&tcon->tc_lock);
if (!tcon->origin_fullpath) {
tcon->origin_fullpath = origin_fullpath;
origin_fullpath = NULL;
} else {
new_tcon = false;
}
tcon->origin_fullpath = origin_fullpath;
origin_fullpath = NULL;
ref_walk_set_tcon(rw, tcon);
spin_unlock(&tcon->tc_lock);
if (new_tcon) {
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
dfs_cache_get_ttl() * HZ);
}
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
dfs_cache_get_ttl() * HZ);
out:
kfree(origin_fullpath);
ref_walk_free(rw);
return rc;
}
......@@ -279,7 +272,7 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
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;
bool nodfs = ctx->nodfs;
......@@ -289,7 +282,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
if (rc)
return rc;
*isdfs = false;
rc = get_session(mnt_ctx, NULL);
if (rc)
return rc;
......@@ -317,10 +309,15 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
return rc;
}
*isdfs = true;
add_root_smb_session(mnt_ctx);
rc = __dfs_mount_share(mnt_ctx);
dfs_put_root_smb_sessions(mnt_ctx);
if (!ctx->dfs_conn) {
ctx->dfs_conn = true;
cifs_mount_put_conns(mnt_ctx);
rc = get_session(mnt_ctx, NULL);
}
if (!rc) {
set_root_smb_session(mnt_ctx);
rc = __dfs_mount_share(mnt_ctx);
}
return rc;
}
......
......@@ -19,6 +19,7 @@
struct dfs_ref {
char *path;
char *full_path;
struct cifs_ses *ses;
struct dfs_cache_tgt_list tl;
struct dfs_cache_tgt_iterator *tit;
};
......@@ -38,6 +39,7 @@ struct dfs_ref_walk {
#define ref_walk_path(w) (ref_walk_cur(w)->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_ses(w) (ref_walk_cur(w)->ses)
static inline struct dfs_ref_walk *ref_walk_alloc(void)
{
......@@ -60,14 +62,19 @@ static inline void __ref_walk_free(struct dfs_ref *ref)
kfree(ref->path);
kfree(ref->full_path);
dfs_cache_free_tgts(&ref->tl);
if (ref->ses)
cifs_put_smb_ses(ref->ses);
memset(ref, 0, sizeof(*ref));
}
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);
kfree(rw);
}
......@@ -116,9 +123,22 @@ static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *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,
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)
{
......@@ -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
* 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 = ctx->dfs_root_ses;
struct cifs_ses *cur;
if (!ses)
return;
struct cifs_ses *ses, *n;
for (cur = ses; cur; cur = cur->dfs_root_ses) {
if (cur->dfs_root_ses)
cifs_put_smb_ses(cur->dfs_root_ses);
list_for_each_entry_safe(ses, n, head, dlist) {
list_del_init(&ses->dlist);
cifs_put_smb_ses(ses);
}
cifs_put_smb_ses(ses);
}
#endif /* _CIFS_DFS_H */
......@@ -126,6 +126,7 @@ static inline void free_tgts(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);
kfree(ce->path);
free_tgts(ce);
......@@ -441,34 +442,31 @@ static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int n
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;
struct cache_entry *ce;
struct cache_entry *to_del = NULL;
WARN_ON(!rwsem_is_locked(&htable_rw_lock));
struct cache_entry *oldest = NULL;
for (i = 0; i < CACHE_HTABLE_SIZE; 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))
continue;
if (!to_del || timespec64_compare(&ce->etime,
&to_del->etime) < 0)
to_del = ce;
if (ce->numtgts == 1)
flush_cache_ent(ce);
else if (!oldest ||
timespec64_compare(&ce->etime,
&oldest->etime) < 0)
oldest = ce;
}
}
if (!to_del) {
cifs_dbg(FYI, "%s: no entry to remove\n", __func__);
return;
}
cifs_dbg(FYI, "%s: removing entry\n", __func__);
dump_ce(to_del);
flush_cache_ent(to_del);
if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES && oldest)
flush_cache_ent(oldest);
}
/* Add a new DFS cache entry */
......@@ -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) {
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);
......@@ -1095,16 +1093,18 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
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 *s2 = &tcon->tree_name[1];
size_t hostlen;
struct sockaddr_storage ss;
char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0};
bool match;
int rc;
if (strcasecmp(s1, s2))
if (strcasecmp(s2, s1))
return false;
/*
......@@ -1128,34 +1128,6 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c
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)
{
struct TCP_Server_Info *server = ses->server;
......@@ -1172,41 +1144,35 @@ static bool is_ses_good(struct cifs_ses *ses)
return ret;
}
/* Refresh dfs referral of @ses and mark it for reconnect if needed */
static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh)
static char *get_ses_refpath(struct cifs_ses *ses)
{
struct TCP_Server_Info *server = ses->server;
DFS_CACHE_TGT_LIST(old_tl);
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();
char *path = ERR_PTR(-ENOENT);
mutex_lock(&server->refpath_lock);
if (server->leaf_fullpath) {
path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC);
if (!path)
rc = -ENOMEM;
path = ERR_PTR(-ENOMEM);
}
mutex_unlock(&server->refpath_lock);
if (!path)
goto out;
return path;
}
down_read(&htable_rw_lock);
ce = lookup_cache_entry(path);
needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce);
if (!IS_ERR(ce)) {
rc = get_targets(ce, &old_tl);
cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc);
}
up_read(&htable_rw_lock);
/* Refresh dfs referral of @ses */
static void refresh_ses_referral(struct cifs_ses *ses)
{
struct cache_entry *ce;
unsigned int xid;
char *path;
int rc = 0;
if (!needs_refresh) {
rc = 0;
xid = get_xid();
path = get_ses_refpath(ses);
if (IS_ERR(path)) {
rc = PTR_ERR(path);
path = NULL;
goto out;
}
......@@ -1217,29 +1183,106 @@ static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh)
goto out;
}
ce = cache_refresh_path(xid, ses, path, true);
if (!IS_ERR(ce)) {
rc = get_targets(ce, &new_tl);
ce = cache_refresh_path(xid, ses, path, false);
if (!IS_ERR(ce))
up_read(&htable_rw_lock);
cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc);
mark_for_reconnect_if_needed(server, path, &old_tl, &new_tl);
}
else
rc = PTR_ERR(ce);
out:
free_xid(xid);
dfs_cache_free_tgts(&old_tl);
dfs_cache_free_tgts(&new_tl);
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)
*/
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
force_refresh_ses_referral(tcon->ses);
refresh_tcon_referral(tcon, true);
return 0;
}
......@@ -1292,8 +1335,9 @@ void dfs_cache_refresh(struct work_struct *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_tcon_referral(tcon, false);
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
atomic_read(&dfs_cache_ttl) * HZ);
......
......@@ -284,6 +284,7 @@ struct smb3_fs_context {
struct cifs_ses *dfs_root_ses;
bool dfs_automount:1; /* set for dfs automount only */
enum cifs_reparse_type reparse_type;
bool dfs_conn:1; /* set for dfs mounts */
};
extern const struct fs_parameter_spec smb3_fs_parameters[];
......
......@@ -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_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
* delete is pending. Instead mark it as unknown.
......@@ -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:
if (S_ISLNK(fattr->cf_mode)) {
if (likely(data->symlink_target))
......@@ -1267,11 +1267,14 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
__func__, rc);
goto out;
}
}
/* fill in remaining high mode bits e.g. SUID, VTX */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
/* fill in remaining high mode bits e.g. SUID, VTX */
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 */
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)
mutex_init(&ret_buf->fscache_lock);
#endif
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;
}
......@@ -1108,7 +1111,8 @@ static void tcon_super_cb(struct super_block *sb, void *arg)
t2 = cifs_sb_master_tcon(cifs_sb);
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 &&
t2->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)
ctx->source = NULL;
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",
__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,
buf->InodeType = cpu_to_le64(type);
buf->ReparseDataLength = cpu_to_le16(len + dlen -
sizeof(struct reparse_data_buffer));
*(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) |
MINOR(dev));
*(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MINOR(dev) << 32) |
MAJOR(dev));
iov->iov_base = buf;
iov->iov_len = len + dlen;
return 0;
......@@ -468,7 +468,7 @@ static void wsl_to_fattr(struct cifs_open_info_data *data,
else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen))
fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen))
fattr->cf_rdev = wsl_mkdev(v);
fattr->cf_rdev = reparse_mkdev(v);
} while (next);
out:
fattr->cf_dtype = S_DT(fattr->cf_mode);
......@@ -485,11 +485,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
switch (le64_to_cpu(buf->InodeType)) {
case NFS_SPECFILE_CHR:
fattr->cf_mode |= S_IFCHR;
fattr->cf_rdev = reparse_nfs_mkdev(buf);
fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break;
case NFS_SPECFILE_BLK:
fattr->cf_mode |= S_IFBLK;
fattr->cf_rdev = reparse_nfs_mkdev(buf);
fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break;
case NFS_SPECFILE_FIFO:
fattr->cf_mode |= S_IFIFO;
......
......@@ -18,14 +18,7 @@
*/
#define IO_REPARSE_TAG_INTERNAL ((__u32)~0U)
static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf)
{
u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
return MKDEV(v >> 32, v & 0xffffffff);
}
static inline dev_t wsl_mkdev(void *ptr)
static inline dev_t reparse_mkdev(void *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,
goto discard_data;
server->total_read += rc;
if (rc < len)
iov_iter_zero(len - rc, &iter);
iov_iter_revert(&iter, len);
if (rc < len) {
struct iov_iter tmp = iter;
iov_iter_advance(&tmp, rc);
iov_iter_zero(len - rc, &tmp);
}
iov_iter_truncate(&iter, dw->len);
rc = cifs_discard_remaining_data(server);
......
......@@ -4866,7 +4866,9 @@ smb2_writev_callback(struct mid_q_entry *mid)
#endif
if (result) {
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,
tcon->tid, tcon->ses->Suid, wdata->subreq.start,
wdata->subreq.len, wdata->result);
......@@ -4874,7 +4876,9 @@ smb2_writev_callback(struct mid_q_entry *mid)
pr_warn_once("Out of space writing to %s\n",
tcon->tree_name);
} 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,
tcon->tid, tcon->ses->Suid,
wdata->subreq.start, wdata->subreq.len);
......@@ -4952,7 +4956,9 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
offsetof(struct smb2_write_req, Buffer));
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->tcon->tid,
io_parms->tcon->ses->Suid,
......@@ -5032,7 +5038,9 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
wdata, flags, &wdata->credits);
/* Can't touch wdata if rc == 0 */
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->tcon->tid,
io_parms->tcon->ses->Suid,
......@@ -5112,7 +5120,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
offsetof(struct smb2_write_req, Buffer));
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->offset, io_parms->length);
......@@ -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;
if (rc) {
trace_smb3_write_err(xid,
trace_smb3_write_err(0, 0, xid,
req->PersistentFileId,
io_parms->tcon->tid,
io_parms->tcon->ses->Suid,
......@@ -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);
} else {
*nbytes = le32_to_cpu(rsp->DataLength);
trace_smb3_write_done(xid,
trace_smb3_write_done(0, 0, xid,
req->PersistentFileId,
io_parms->tcon->tid,
io_parms->tcon->ses->Suid,
......
......@@ -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))
DEFINE_SMB3_RW_ERR_EVENT(read_err);
DEFINE_SMB3_RW_ERR_EVENT(write_err);
/* For logging errors in other file I/O ops */
DECLARE_EVENT_CLASS(smb3_other_err_class,
......@@ -202,7 +203,6 @@ DEFINE_EVENT(smb3_other_err_class, smb3_##name, \
int 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(zero_err);
DEFINE_SMB3_OTHER_ERR_EVENT(falloc_err);
......@@ -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_done);
DEFINE_SMB3_RW_DONE_EVENT(write_enter);
DEFINE_SMB3_RW_DONE_EVENT(write_done);
/* For logging successful other op */
DECLARE_EVENT_CLASS(smb3_other_done_class,
......@@ -411,11 +413,9 @@ DEFINE_EVENT(smb3_other_done_class, smb3_##name, \
__u32 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(zero_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(zero_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)
length = data_len; /* An RDMA read is already done. */
else
#endif
{
length = cifs_read_iter_from_socket(server, &rdata->subreq.io_iter,
data_len);
iov_iter_revert(&rdata->subreq.io_iter, data_len);
}
if (length > 0)
rdata->got_bytes += 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