Commit 0235da0f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
  ...
parents 109daa23 8d7690b3
Loading
Loading
Loading
Loading
+134 −36
Original line number Diff line number Diff line
@@ -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);
	}
	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.
	 * Queue dropping of the dentries once locks have been dropped
	 */
		kref_put(&cfid->refcount, smb2_close_cached_fid);
	}
	if (!list_empty(&cfids->dying))
		queue_work(cfid_put_wq, &cfids->invalidation_work);
	spin_unlock(&cfids->cfid_list_lock);
}

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);

		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) {
+5 −1
Original line number Diff line number Diff line
@@ -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;
};

+11 −1
Original line number Diff line number Diff line
@@ -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:
+2 −2
Original line number Diff line number Diff line
@@ -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 */
+3 −1
Original line number Diff line number Diff line
@@ -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;
Loading