Commit 1bfe73c2 authored by Igor Mammedov's avatar Igor Mammedov Committed by Steve French

Remote DFS root support.

Allows to mount share on a server that returns -EREMOTE
 at the tree connect stage or at the check on a full path
 accessibility.
Signed-off-by: default avatarIgor Mammedov <niallain@gmail.com>
Acked-by: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent 85a6dac5
......@@ -2214,9 +2214,56 @@ is_path_accessible(int xid, struct cifsTconInfo *tcon,
return rc;
}
static void
cleanup_volume_info(struct smb_vol **pvolume_info)
{
struct smb_vol *volume_info;
if (!pvolume_info && !*pvolume_info)
return;
volume_info = *pvolume_info;
kzfree(volume_info->password);
kfree(volume_info->UNC);
kfree(volume_info->prepath);
kfree(volume_info);
*pvolume_info = NULL;
return;
}
/* build_path_to_root returns full path to root when
* we do not have an exiting connection (tcon) */
static char *
build_unc_path_to_root(const struct smb_vol *volume_info,
const struct cifs_sb_info *cifs_sb)
{
char *full_path;
int unc_len = strnlen(volume_info->UNC, MAX_TREE_SIZE + 1);
full_path = kmalloc(unc_len + cifs_sb->prepathlen + 1, GFP_KERNEL);
if (full_path == NULL)
return ERR_PTR(-ENOMEM);
strncpy(full_path, volume_info->UNC, unc_len);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
int i;
for (i = 0; i < unc_len; i++) {
if (full_path[i] == '\\')
full_path[i] = '/';
}
}
if (cifs_sb->prepathlen)
strncpy(full_path + unc_len, cifs_sb->prepath,
cifs_sb->prepathlen);
full_path[unc_len + cifs_sb->prepathlen] = 0; /* add trailing null */
return full_path;
}
int
cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
char *mount_data, const char *devname)
char *mount_data_global, const char *devname)
{
int rc = 0;
int xid;
......@@ -2225,6 +2272,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
struct cifsTconInfo *tcon = NULL;
struct TCP_Server_Info *srvTcp = NULL;
char *full_path;
struct dfs_info3_param *referrals = NULL;
unsigned int num_referrals = 0;
char *mount_data = mount_data_global;
try_mount_again:
full_path = NULL;
xid = GetXid();
......@@ -2371,11 +2425,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
}
}
/* check for null share name ie connect to dfs root */
if ((strchr(volume_info->UNC + 3, '\\') == NULL)
&& (strchr(volume_info->UNC + 3, '/') == NULL)) {
/* rc = connect_to_dfs_path(...) */
cFYI(1, ("DFS root not supported"));
cERROR(1, ("Missing share name"));
rc = -ENODEV;
goto mount_fail_check;
} else {
......@@ -2392,7 +2444,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
}
}
if (rc)
goto mount_fail_check;
goto remote_path_check;
tcon->seal = volume_info->seal;
write_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
......@@ -2417,19 +2469,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* BB FIXME fix time_gran to be larger for LANMAN sessions */
sb->s_time_gran = 100;
mount_fail_check:
/* on error free sesinfo and tcon struct if needed */
if (rc) {
/* If find_unc succeeded then rc == 0 so we can not end */
/* up accidently freeing someone elses tcon struct */
if (tcon)
cifs_put_tcon(tcon);
else if (pSesInfo)
cifs_put_smb_ses(pSesInfo);
else
cifs_put_tcp_session(srvTcp);
goto out;
}
if (rc)
goto remote_path_check;
cifs_sb->tcon = tcon;
/* do not care if following two calls succeed - informational */
......@@ -2461,7 +2503,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
cifs_sb->rsize = min(cifs_sb->rsize,
(tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
if (!rc && cifs_sb->prepathlen) {
remote_path_check:
/* check if a whole path (including prepath) is not remote */
if (!rc && cifs_sb->prepathlen && tcon) {
/* build_path_to_root works only when we have a valid tcon */
full_path = cifs_build_path_to_root(cifs_sb);
if (full_path == NULL) {
......@@ -2469,13 +2513,66 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
goto mount_fail_check;
}
rc = is_path_accessible(xid, tcon, cifs_sb, full_path);
if (rc) {
cERROR(1, ("Path %s in not accessible: %d",
full_path, rc));
if (rc != -EREMOTE) {
kfree(full_path);
goto mount_fail_check;
}
kfree(full_path);
}
/* get referral if needed */
if (rc == -EREMOTE) {
/* convert forward to back slashes in prepath here if needed */
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
convert_delimiter(cifs_sb->prepath,
CIFS_DIR_SEP(cifs_sb));
full_path = build_unc_path_to_root(volume_info, cifs_sb);
if (IS_ERR(full_path)) {
rc = PTR_ERR(full_path);
goto mount_fail_check;
}
cFYI(1, ("Getting referral for: %s", full_path));
rc = get_dfs_path(xid, pSesInfo , full_path + 1,
cifs_sb->local_nls, &num_referrals, &referrals,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (!rc && num_referrals > 0) {
char *fake_devname = NULL;
if (mount_data != mount_data_global)
kfree(mount_data);
mount_data = cifs_compose_mount_options(
cifs_sb->mountdata, full_path + 1,
referrals, &fake_devname);
kfree(fake_devname);
free_dfs_info_array(referrals, num_referrals);
if (tcon)
cifs_put_tcon(tcon);
else if (pSesInfo)
cifs_put_smb_ses(pSesInfo);
cleanup_volume_info(&volume_info);
FreeXid(xid);
kfree(full_path);
goto try_mount_again;
}
}
mount_fail_check:
/* on error free sesinfo and tcon struct if needed */
if (rc) {
if (mount_data != mount_data_global)
kfree(mount_data);
/* If find_unc succeeded then rc == 0 so we can not end */
/* up accidently freeing someone elses tcon struct */
if (tcon)
cifs_put_tcon(tcon);
else if (pSesInfo)
cifs_put_smb_ses(pSesInfo);
else
cifs_put_tcp_session(srvTcp);
goto out;
}
/* volume_info->password is freed above when existing session found
......@@ -2484,16 +2581,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
password will be freed at unmount time) */
out:
/* zero out password before freeing */
if (volume_info) {
if (volume_info->password != NULL) {
memset(volume_info->password, 0,
strlen(volume_info->password));
kfree(volume_info->password);
}
kfree(volume_info->UNC);
kfree(volume_info->prepath);
kfree(volume_info);
}
cleanup_volume_info(&volume_info);
FreeXid(xid);
return rc;
}
......
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