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, ...@@ -2214,9 +2214,56 @@ is_path_accessible(int xid, struct cifsTconInfo *tcon,
return rc; 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 int
cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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 rc = 0;
int xid; int xid;
...@@ -2225,6 +2272,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2225,6 +2272,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
struct cifsTconInfo *tcon = NULL; struct cifsTconInfo *tcon = NULL;
struct TCP_Server_Info *srvTcp = NULL; struct TCP_Server_Info *srvTcp = NULL;
char *full_path; 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(); xid = GetXid();
...@@ -2371,11 +2425,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -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) if ((strchr(volume_info->UNC + 3, '\\') == NULL)
&& (strchr(volume_info->UNC + 3, '/') == NULL)) { && (strchr(volume_info->UNC + 3, '/') == NULL)) {
/* rc = connect_to_dfs_path(...) */ cERROR(1, ("Missing share name"));
cFYI(1, ("DFS root not supported"));
rc = -ENODEV; rc = -ENODEV;
goto mount_fail_check; goto mount_fail_check;
} else { } else {
...@@ -2392,7 +2444,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2392,7 +2444,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
} }
} }
if (rc) if (rc)
goto mount_fail_check; goto remote_path_check;
tcon->seal = volume_info->seal; tcon->seal = volume_info->seal;
write_lock(&cifs_tcp_ses_lock); write_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &pSesInfo->tcon_list); list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
...@@ -2417,19 +2469,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -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 */ /* BB FIXME fix time_gran to be larger for LANMAN sessions */
sb->s_time_gran = 100; sb->s_time_gran = 100;
mount_fail_check: if (rc)
/* on error free sesinfo and tcon struct if needed */ goto remote_path_check;
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;
}
cifs_sb->tcon = tcon; cifs_sb->tcon = tcon;
/* do not care if following two calls succeed - informational */ /* do not care if following two calls succeed - informational */
...@@ -2461,7 +2503,9 @@ mount_fail_check: ...@@ -2461,7 +2503,9 @@ mount_fail_check:
cifs_sb->rsize = min(cifs_sb->rsize, cifs_sb->rsize = min(cifs_sb->rsize,
(tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); (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 */ /* build_path_to_root works only when we have a valid tcon */
full_path = cifs_build_path_to_root(cifs_sb); full_path = cifs_build_path_to_root(cifs_sb);
if (full_path == NULL) { if (full_path == NULL) {
...@@ -2469,13 +2513,66 @@ mount_fail_check: ...@@ -2469,13 +2513,66 @@ mount_fail_check:
goto mount_fail_check; goto mount_fail_check;
} }
rc = is_path_accessible(xid, tcon, cifs_sb, full_path); rc = is_path_accessible(xid, tcon, cifs_sb, full_path);
if (rc) { if (rc != -EREMOTE) {
cERROR(1, ("Path %s in not accessible: %d", kfree(full_path);
full_path, rc)); goto mount_fail_check;
}
kfree(full_path); 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; 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); 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 /* volume_info->password is freed above when existing session found
...@@ -2484,16 +2581,7 @@ mount_fail_check: ...@@ -2484,16 +2581,7 @@ mount_fail_check:
password will be freed at unmount time) */ password will be freed at unmount time) */
out: out:
/* zero out password before freeing */ /* zero out password before freeing */
if (volume_info) { cleanup_volume_info(&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);
}
FreeXid(xid); FreeXid(xid);
return rc; 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