|
@@ -33,6 +33,7 @@
|
|
#include <linux/cleancache.h>
|
|
#include <linux/cleancache.h>
|
|
#include <linux/fsnotify.h>
|
|
#include <linux/fsnotify.h>
|
|
#include <linux/lockdep.h>
|
|
#include <linux/lockdep.h>
|
|
|
|
+#include <linux/user_namespace.h>
|
|
#include "internal.h"
|
|
#include "internal.h"
|
|
|
|
|
|
|
|
|
|
@@ -165,6 +166,7 @@ static void destroy_super(struct super_block *s)
|
|
list_lru_destroy(&s->s_inode_lru);
|
|
list_lru_destroy(&s->s_inode_lru);
|
|
security_sb_free(s);
|
|
security_sb_free(s);
|
|
WARN_ON(!list_empty(&s->s_mounts));
|
|
WARN_ON(!list_empty(&s->s_mounts));
|
|
|
|
+ put_user_ns(s->s_user_ns);
|
|
kfree(s->s_subtype);
|
|
kfree(s->s_subtype);
|
|
kfree(s->s_options);
|
|
kfree(s->s_options);
|
|
call_rcu(&s->rcu, destroy_super_rcu);
|
|
call_rcu(&s->rcu, destroy_super_rcu);
|
|
@@ -174,11 +176,13 @@ static void destroy_super(struct super_block *s)
|
|
* alloc_super - create new superblock
|
|
* alloc_super - create new superblock
|
|
* @type: filesystem type superblock should belong to
|
|
* @type: filesystem type superblock should belong to
|
|
* @flags: the mount flags
|
|
* @flags: the mount flags
|
|
|
|
+ * @user_ns: User namespace for the super_block
|
|
*
|
|
*
|
|
* Allocates and initializes a new &struct super_block. alloc_super()
|
|
* Allocates and initializes a new &struct super_block. alloc_super()
|
|
* returns a pointer new superblock or %NULL if allocation had failed.
|
|
* returns a pointer new superblock or %NULL if allocation had failed.
|
|
*/
|
|
*/
|
|
-static struct super_block *alloc_super(struct file_system_type *type, int flags)
|
|
|
|
|
|
+static struct super_block *alloc_super(struct file_system_type *type, int flags,
|
|
|
|
+ struct user_namespace *user_ns)
|
|
{
|
|
{
|
|
struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);
|
|
struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);
|
|
static const struct super_operations default_op;
|
|
static const struct super_operations default_op;
|
|
@@ -188,6 +192,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
INIT_LIST_HEAD(&s->s_mounts);
|
|
INIT_LIST_HEAD(&s->s_mounts);
|
|
|
|
+ s->s_user_ns = get_user_ns(user_ns);
|
|
|
|
|
|
if (security_sb_alloc(s))
|
|
if (security_sb_alloc(s))
|
|
goto fail;
|
|
goto fail;
|
|
@@ -443,17 +448,18 @@ void generic_shutdown_super(struct super_block *sb)
|
|
EXPORT_SYMBOL(generic_shutdown_super);
|
|
EXPORT_SYMBOL(generic_shutdown_super);
|
|
|
|
|
|
/**
|
|
/**
|
|
- * sget - find or create a superblock
|
|
|
|
|
|
+ * sget_userns - find or create a superblock
|
|
* @type: filesystem type superblock should belong to
|
|
* @type: filesystem type superblock should belong to
|
|
* @test: comparison callback
|
|
* @test: comparison callback
|
|
* @set: setup callback
|
|
* @set: setup callback
|
|
* @flags: mount flags
|
|
* @flags: mount flags
|
|
|
|
+ * @user_ns: User namespace for the super_block
|
|
* @data: argument to each of them
|
|
* @data: argument to each of them
|
|
*/
|
|
*/
|
|
-struct super_block *sget(struct file_system_type *type,
|
|
|
|
|
|
+struct super_block *sget_userns(struct file_system_type *type,
|
|
int (*test)(struct super_block *,void *),
|
|
int (*test)(struct super_block *,void *),
|
|
int (*set)(struct super_block *,void *),
|
|
int (*set)(struct super_block *,void *),
|
|
- int flags,
|
|
|
|
|
|
+ int flags, struct user_namespace *user_ns,
|
|
void *data)
|
|
void *data)
|
|
{
|
|
{
|
|
struct super_block *s = NULL;
|
|
struct super_block *s = NULL;
|
|
@@ -466,6 +472,14 @@ retry:
|
|
hlist_for_each_entry(old, &type->fs_supers, s_instances) {
|
|
hlist_for_each_entry(old, &type->fs_supers, s_instances) {
|
|
if (!test(old, data))
|
|
if (!test(old, data))
|
|
continue;
|
|
continue;
|
|
|
|
+ if (user_ns != old->s_user_ns) {
|
|
|
|
+ spin_unlock(&sb_lock);
|
|
|
|
+ if (s) {
|
|
|
|
+ up_write(&s->s_umount);
|
|
|
|
+ destroy_super(s);
|
|
|
|
+ }
|
|
|
|
+ return ERR_PTR(-EBUSY);
|
|
|
|
+ }
|
|
if (!grab_super(old))
|
|
if (!grab_super(old))
|
|
goto retry;
|
|
goto retry;
|
|
if (s) {
|
|
if (s) {
|
|
@@ -478,7 +492,7 @@ retry:
|
|
}
|
|
}
|
|
if (!s) {
|
|
if (!s) {
|
|
spin_unlock(&sb_lock);
|
|
spin_unlock(&sb_lock);
|
|
- s = alloc_super(type, flags);
|
|
|
|
|
|
+ s = alloc_super(type, flags, user_ns);
|
|
if (!s)
|
|
if (!s)
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
goto retry;
|
|
goto retry;
|
|
@@ -501,6 +515,31 @@ retry:
|
|
return s;
|
|
return s;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+EXPORT_SYMBOL(sget_userns);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * sget - find or create a superblock
|
|
|
|
+ * @type: filesystem type superblock should belong to
|
|
|
|
+ * @test: comparison callback
|
|
|
|
+ * @set: setup callback
|
|
|
|
+ * @flags: mount flags
|
|
|
|
+ * @data: argument to each of them
|
|
|
|
+ */
|
|
|
|
+struct super_block *sget(struct file_system_type *type,
|
|
|
|
+ int (*test)(struct super_block *,void *),
|
|
|
|
+ int (*set)(struct super_block *,void *),
|
|
|
|
+ int flags,
|
|
|
|
+ void *data)
|
|
|
|
+{
|
|
|
|
+ struct user_namespace *user_ns = current_user_ns();
|
|
|
|
+
|
|
|
|
+ /* Ensure the requestor has permissions over the target filesystem */
|
|
|
|
+ if (!(flags & MS_KERNMOUNT) && !ns_capable(user_ns, CAP_SYS_ADMIN))
|
|
|
|
+ return ERR_PTR(-EPERM);
|
|
|
|
+
|
|
|
|
+ return sget_userns(type, test, set, flags, user_ns, data);
|
|
|
|
+}
|
|
|
|
+
|
|
EXPORT_SYMBOL(sget);
|
|
EXPORT_SYMBOL(sget);
|
|
|
|
|
|
void drop_super(struct super_block *sb)
|
|
void drop_super(struct super_block *sb)
|
|
@@ -930,7 +969,8 @@ struct dentry *mount_ns(struct file_system_type *fs_type,
|
|
if (!(flags & MS_KERNMOUNT) && !ns_capable(user_ns, CAP_SYS_ADMIN))
|
|
if (!(flags & MS_KERNMOUNT) && !ns_capable(user_ns, CAP_SYS_ADMIN))
|
|
return ERR_PTR(-EPERM);
|
|
return ERR_PTR(-EPERM);
|
|
|
|
|
|
- sb = sget(fs_type, ns_test_super, ns_set_super, flags, ns);
|
|
|
|
|
|
+ sb = sget_userns(fs_type, ns_test_super, ns_set_super, flags,
|
|
|
|
+ user_ns, ns);
|
|
if (IS_ERR(sb))
|
|
if (IS_ERR(sb))
|
|
return ERR_CAST(sb);
|
|
return ERR_CAST(sb);
|
|
|
|
|