Skip to content

Commit

Permalink
fs/cifs: make share unaccessible at root level mountable
Browse files Browse the repository at this point in the history
BugLink: http://bugs.launchpad.net/bugs/1615620

commit a6b5058 upstream.

if, when mounting //HOST/share/sub/dir/foo we can query /sub/dir/foo but
not any of the path components above:

- store the /sub/dir/foo prefix in the cifs super_block info
- in the superblock, set root dentry to the subpath dentry (instead of
  the share root)
- set a flag in the superblock to remember it
- use prefixpath when building path from a dentry

fixes bso#8950

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
Reviewed-by: Pavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: Steve French <smfrench@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
  • Loading branch information
aaptel authored and kamalmostafa committed Aug 22, 2016
1 parent 1ee3cee commit db9f727
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 5 deletions.
4 changes: 4 additions & 0 deletions fs/cifs/cifs_fs_sb.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */
#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
* root mountable
*/

struct cifs_sb_info {
struct rb_root tlink_tree;
Expand All @@ -67,5 +70,6 @@ struct cifs_sb_info {
struct backing_dev_info bdi;
struct delayed_work prune_tlinks;
struct rcu_head rcu;
char *prepath;
};
#endif /* _CIFS_FS_SB_H */
14 changes: 13 additions & 1 deletion fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,14 @@ cifs_do_mount(struct file_system_type *fs_type,
goto out_cifs_sb;
}

if (volume_info->prepath) {
cifs_sb->prepath = kstrdup(volume_info->prepath, GFP_KERNEL);
if (cifs_sb->prepath == NULL) {
root = ERR_PTR(-ENOMEM);
goto out_cifs_sb;
}
}

cifs_setup_cifs_sb(volume_info, cifs_sb);

rc = cifs_mount(cifs_sb, volume_info);
Expand Down Expand Up @@ -724,7 +732,11 @@ cifs_do_mount(struct file_system_type *fs_type,
sb->s_flags |= MS_ACTIVE;
}

root = cifs_get_root(volume_info, sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
root = dget(sb->s_root);
else
root = cifs_get_root(volume_info, sb);

if (IS_ERR(root))
goto out_super;

Expand Down
49 changes: 49 additions & 0 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -3517,6 +3517,44 @@ cifs_get_volume_info(char *mount_data, const char *devname)
return volume_info;
}

static int
cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
char *full_path)
{
int rc;
char *s;
char sep, tmp;

sep = CIFS_DIR_SEP(cifs_sb);
s = full_path;

rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
while (rc == 0) {
/* skip separators */
while (*s == sep)
s++;
if (!*s)
break;
/* next separator */
while (*s && *s != sep)
s++;

/*
* temporarily null-terminate the path at the end of
* the current component
*/
tmp = *s;
*s = 0;
rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
full_path);
*s = tmp;
}
return rc;
}

int
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
{
Expand Down Expand Up @@ -3654,6 +3692,16 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
kfree(full_path);
goto mount_fail_check;
}

rc = cifs_are_all_path_components_accessible(server,
xid, tcon, cifs_sb,
full_path);
if (rc != 0) {
cifs_dbg(VFS, "cannot query dirs between root and final path, "
"enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
rc = 0;
}
kfree(full_path);
}

Expand Down Expand Up @@ -3923,6 +3971,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb)

bdi_destroy(&cifs_sb->bdi);
kfree(cifs_sb->mountdata);
kfree(cifs_sb->prepath);
call_rcu(&cifs_sb->rcu, delayed_free);
}

Expand Down
20 changes: 18 additions & 2 deletions fs/cifs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ build_path_from_dentry(struct dentry *direntry)
struct dentry *temp;
int namelen;
int dfsplen;
int pplen = 0;
char *full_path;
char dirsep;
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
Expand All @@ -95,8 +96,12 @@ build_path_from_dentry(struct dentry *direntry)
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else
dfsplen = 0;

if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0;

cifs_bp_rename_retry:
namelen = dfsplen;
namelen = dfsplen + pplen;
seq = read_seqbegin(&rename_lock);
rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) {
Expand Down Expand Up @@ -137,7 +142,7 @@ build_path_from_dentry(struct dentry *direntry)
}
}
rcu_read_unlock();
if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
if (namelen != dfsplen + pplen || read_seqretry(&rename_lock, seq)) {
cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n",
namelen, dfsplen);
/* presumably this is only possible if racing with a rename
Expand All @@ -153,6 +158,17 @@ build_path_from_dentry(struct dentry *direntry)
those safely to '/' if any are found in the middle of the prepath */
/* BB test paths to Windows with '/' in the midst of prepath */

if (pplen) {
int i;

cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath);
memcpy(full_path+dfsplen+1, cifs_sb->prepath, pplen-1);
full_path[dfsplen] = '\\';
for (i = 0; i < pplen-1; i++)
if (full_path[dfsplen+1+i] == '/')
full_path[dfsplen+1+i] = CIFS_DIR_SEP(cifs_sb);
}

if (dfsplen) {
strncpy(full_path, tcon->treeName, dfsplen);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
Expand Down
22 changes: 20 additions & 2 deletions fs/cifs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -982,18 +982,35 @@ struct inode *cifs_root_iget(struct super_block *sb)
struct inode *inode = NULL;
long rc;
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
char *path = NULL;
int len;

if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
&& cifs_sb->prepath) {
len = strlen(cifs_sb->prepath);
path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL);
if (path == NULL)
return ERR_PTR(-ENOMEM);
path[0] = '/';
memcpy(path+1, cifs_sb->prepath, len);
} else {
path = kstrdup("", GFP_KERNEL);
if (path == NULL)
return ERR_PTR(-ENOMEM);
}

xid = get_xid();
if (tcon->unix_ext) {
rc = cifs_get_inode_info_unix(&inode, "", sb, xid);
rc = cifs_get_inode_info_unix(&inode, path, sb, xid);
/* some servers mistakenly claim POSIX support */
if (rc != -EOPNOTSUPP)
goto iget_no_retry;
cifs_dbg(VFS, "server does not support POSIX extensions");
tcon->unix_ext = false;
}

rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL);
convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL);

iget_no_retry:
if (!inode) {
Expand Down Expand Up @@ -1022,6 +1039,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
}

out:
kfree(path);
/* can not call macro free_xid here since in a void func
* TODO: This is no longer true
*/
Expand Down

0 comments on commit db9f727

Please sign in to comment.