Commit ef605e86 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

cifs: skip trailing separators of prefix paths



During DFS failover, prefix paths may change, so make sure to not
leave trailing separators when parsing thew in
dfs_cache_get_tgt_share().  The separators of prefix paths are already
handled by build_path_from_dentry_optional_prefix().

Consider the following DFS link:

  //dom/dfs/link: [\srv1\share\dir1, \srv2\share\dir1]

Before commit:

  mount.cifs //dom/dfs/link
  tree connect to \\srv1\share; prefix_path=dir1
  disconnect srv1; failover to srv2
  tree connect to \\srv2\share; prefix_path=dir1\
  mv foo bar

  ...
  SMB2 430 Create Request File: dir1\\foo;GetInfo Request FILE_INFO/SMB2_FILE_ALL_INFO;Close Request
  SMB2 582 Create Response File: dir1\\foo;GetInfo Response;Close Response
  SMB2 430 Create Request File: dir1\\bar;GetInfo Request FILE_INFO/SMB2_FILE_ALL_INFO;Close Request
  SMB2 286 Create Response, Error: STATUS_OBJECT_NAME_NOT_FOUND;GetInfo Response, Error: STATUS_OBJECT_NAME_NOT_FOUND;Close Response, Error: STATUS_OBJECT_NAME_NOT_FOUND
  SMB2 462 Create Request File: dir1\\foo;SetInfo Request FILE_INFO/SMB2_FILE_RENAME_INFO NewName:dir1\\bar;Close Request
  SMB2 478 Create Response File: dir1\\foo;SetInfo Response, Error: STATUS_OBJECT_NAME_INVALID;Close Response

After commit:

  mount.cifs //dom/dfs/link
  tree connect to \\srv1\share; prefix_path=dir1
  disconnect srv1; failover to srv2
  tree connect to \\srv2\share; prefix_path=dir1
  mv foo bar

  ...
  SMB2 430 Create Request File: dir1\foo;GetInfo Request FILE_INFO/SMB2_FILE_ALL_INFO;Close Request
  SMB2 582 Create Response File: dir1\foo;GetInfo Response;Close Response
  SMB2 430 Create Request File: dir1\bar;GetInfo Request FILE_INFO/SMB2_FILE_ALL_INFO;Close Request
  SMB2 286 Create Response, Error: STATUS_OBJECT_NAME_NOT_FOUND;GetInfo Response, Error: STATUS_OBJECT_NAME_NOT_FOUND;Close Response, Error: STATUS_OBJECT_NAME_NOT_FOUND
  SMB2 462 Create Request File: dir1\foo;SetInfo Request FILE_INFO/SMB2_FILE_RENAME_INFO NewName:dir1\bar;Close Request
  SMB2 478 Create Response File: dir1\foo;SetInfo Response;Close Response

Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 096c956b
Loading
Loading
Loading
Loading
+49 −36
Original line number Diff line number Diff line
@@ -1229,6 +1229,30 @@ void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id)
	kref_put(&mg->refcount, mount_group_release);
}

/* Extract share from DFS target and return a pointer to prefix path or NULL */
static const char *parse_target_share(const char *target, char **share)
{
	const char *s, *seps = "/\\";
	size_t len;

	s = strpbrk(target + 1, seps);
	if (!s)
		return ERR_PTR(-EINVAL);

	len = strcspn(s + 1, seps);
	if (!len)
		return ERR_PTR(-EINVAL);
	s += len;

	len = s - target + 1;
	*share = kstrndup(target, len, GFP_KERNEL);
	if (!*share)
		return ERR_PTR(-ENOMEM);

	s = target + len;
	return s + strspn(s, seps);
}

/**
 * dfs_cache_get_tgt_share - parse a DFS target
 *
@@ -1242,56 +1266,45 @@ void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id)
int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share,
			    char **prefix)
{
	char *s, sep, *p;
	size_t len;
	size_t plen1, plen2;
	char sep;
	char *target_share, *ppath;
	const char *target_ppath, *dfsref_ppath;
	size_t target_pplen, dfsref_pplen;
	size_t len, c;

	if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed)
		return -EINVAL;

	*share = NULL;
	*prefix = NULL;

	sep = it->it_name[0];
	if (sep != '\\' && sep != '/')
		return -EINVAL;

	s = strchr(it->it_name + 1, sep);
	if (!s)
		return -EINVAL;
	target_ppath = parse_target_share(it->it_name, &target_share);
	if (IS_ERR(target_ppath))
		return PTR_ERR(target_ppath);

	/* point to prefix in target node */
	s = strchrnul(s + 1, sep);
	/* point to prefix in DFS referral path */
	dfsref_ppath = path + it->it_path_consumed;
	dfsref_ppath += strspn(dfsref_ppath, "/\\");

	/* extract target share */
	*share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL);
	if (!*share)
		return -ENOMEM;
	target_pplen = strlen(target_ppath);
	dfsref_pplen = strlen(dfsref_ppath);

	/* skip separator */
	if (*s)
		s++;
	/* point to prefix in DFS path */
	p = path + it->it_path_consumed;
	if (*p == sep)
		p++;

	/* merge prefix paths from DFS path and target node */
	plen1 = it->it_name + strlen(it->it_name) - s;
	plen2 = path + strlen(path) - p;
	if (plen1 || plen2) {
		len = plen1 + plen2 + 2;
		*prefix = kmalloc(len, GFP_KERNEL);
		if (!*prefix) {
			kfree(*share);
			*share = NULL;
	/* merge prefix paths from DFS referral path and target node */
	if (target_pplen || dfsref_pplen) {
		len = target_pplen + dfsref_pplen + 2;
		ppath = kzalloc(len, GFP_KERNEL);
		if (!ppath) {
			kfree(target_share);
			return -ENOMEM;
		}
		if (plen1)
			scnprintf(*prefix, len, "%.*s%c%.*s", (int)plen1, s, sep, (int)plen2, p);
		else
			strscpy(*prefix, p, len);
		c = strscpy(ppath, target_ppath, len);
		if (c && dfsref_pplen)
			ppath[c] = sep;
		strlcat(ppath, dfsref_ppath, len);
	}
	*share = target_share;
	*prefix = ppath;
	return 0;
}