2018-11-04 11:19:03 +03:00
/* Provide a way to create a superblock configuration context within the kernel
* that allows a superblock to be set up prior to mounting .
*
* Copyright ( C ) 2017 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation ; either version
* 2 of the Licence , or ( at your option ) any later version .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/fs_context.h>
# include <linux/fs.h>
# include <linux/mount.h>
# include <linux/nsproxy.h>
# include <linux/slab.h>
# include <linux/magic.h>
# include <linux/security.h>
# include <linux/mnt_namespace.h>
# include <linux/pid_namespace.h>
# include <linux/user_namespace.h>
# include <net/net_namespace.h>
# include "mount.h"
# include "internal.h"
struct legacy_fs_context {
char * legacy_data ; /* Data page for legacy filesystems */
size_t data_size ;
} ;
static int legacy_init_fs_context ( struct fs_context * fc ) ;
/**
* alloc_fs_context - Create a filesystem context .
* @ fs_type : The filesystem type .
* @ reference : The dentry from which this one derives ( or NULL )
* @ sb_flags : Filesystem / superblock flags ( SB_ * )
* @ sb_flags_mask : Applicable members of @ sb_flags
* @ purpose : The purpose that this configuration shall be used for .
*
* Open a filesystem and create a mount context . The mount context is
* initialised with the supplied flags and , if a submount / automount from
* another superblock ( referred to by @ reference ) is supplied , may have
* parameters such as namespaces copied across from that superblock .
*/
static struct fs_context * alloc_fs_context ( struct file_system_type * fs_type ,
struct dentry * reference ,
unsigned int sb_flags ,
unsigned int sb_flags_mask ,
enum fs_context_purpose purpose )
{
struct fs_context * fc ;
int ret = - ENOMEM ;
fc = kzalloc ( sizeof ( struct fs_context ) , GFP_KERNEL ) ;
if ( ! fc )
return ERR_PTR ( - ENOMEM ) ;
fc - > purpose = purpose ;
fc - > sb_flags = sb_flags ;
fc - > sb_flags_mask = sb_flags_mask ;
fc - > fs_type = get_filesystem ( fs_type ) ;
fc - > cred = get_current_cred ( ) ;
fc - > net_ns = get_net ( current - > nsproxy - > net_ns ) ;
switch ( purpose ) {
case FS_CONTEXT_FOR_MOUNT :
fc - > user_ns = get_user_ns ( fc - > cred - > user_ns ) ;
break ;
}
ret = legacy_init_fs_context ( fc ) ;
if ( ret < 0 )
goto err_fc ;
fc - > need_free = true ;
return fc ;
err_fc :
put_fs_context ( fc ) ;
return ERR_PTR ( ret ) ;
}
struct fs_context * fs_context_for_mount ( struct file_system_type * fs_type ,
unsigned int sb_flags )
{
return alloc_fs_context ( fs_type , NULL , sb_flags , 0 ,
FS_CONTEXT_FOR_MOUNT ) ;
}
EXPORT_SYMBOL ( fs_context_for_mount ) ;
2018-12-20 23:04:50 +03:00
void fc_drop_locked ( struct fs_context * fc )
{
struct super_block * sb = fc - > root - > d_sb ;
dput ( fc - > root ) ;
fc - > root = NULL ;
deactivate_locked_super ( sb ) ;
}
2018-11-04 11:19:03 +03:00
static void legacy_fs_context_free ( struct fs_context * fc ) ;
/**
* put_fs_context - Dispose of a superblock configuration context .
* @ fc : The context to dispose of .
*/
void put_fs_context ( struct fs_context * fc )
{
struct super_block * sb ;
if ( fc - > root ) {
sb = fc - > root - > d_sb ;
dput ( fc - > root ) ;
fc - > root = NULL ;
deactivate_super ( sb ) ;
}
if ( fc - > need_free )
legacy_fs_context_free ( fc ) ;
security_free_mnt_opts ( & fc - > security ) ;
if ( fc - > net_ns )
put_net ( fc - > net_ns ) ;
put_user_ns ( fc - > user_ns ) ;
put_cred ( fc - > cred ) ;
kfree ( fc - > subtype ) ;
put_filesystem ( fc - > fs_type ) ;
kfree ( fc - > source ) ;
kfree ( fc ) ;
}
EXPORT_SYMBOL ( put_fs_context ) ;
/*
* Free the config for a filesystem that doesn ' t support fs_context .
*/
static void legacy_fs_context_free ( struct fs_context * fc )
{
kfree ( fc - > fs_private ) ;
}
/*
* Add monolithic mount data .
*/
static int legacy_parse_monolithic ( struct fs_context * fc , void * data )
{
struct legacy_fs_context * ctx = fc - > fs_private ;
ctx - > legacy_data = data ;
if ( ! ctx - > legacy_data )
return 0 ;
if ( fc - > fs_type - > fs_flags & FS_BINARY_MOUNTDATA )
return 0 ;
return security_sb_eat_lsm_opts ( ctx - > legacy_data , & fc - > security ) ;
}
/*
* Get a mountable root with the legacy mount command .
*/
int legacy_get_tree ( struct fs_context * fc )
{
struct legacy_fs_context * ctx = fc - > fs_private ;
struct super_block * sb ;
struct dentry * root ;
root = fc - > fs_type - > mount ( fc - > fs_type , fc - > sb_flags ,
fc - > source , ctx - > legacy_data ) ;
if ( IS_ERR ( root ) )
return PTR_ERR ( root ) ;
sb = root - > d_sb ;
BUG_ON ( ! sb ) ;
fc - > root = root ;
return 0 ;
}
/*
* Initialise a legacy context for a filesystem that doesn ' t support
* fs_context .
*/
static int legacy_init_fs_context ( struct fs_context * fc )
{
fc - > fs_private = kzalloc ( sizeof ( struct legacy_fs_context ) , GFP_KERNEL ) ;
if ( ! fc - > fs_private )
return - ENOMEM ;
return 0 ;
}
int parse_monolithic_mount_data ( struct fs_context * fc , void * data )
{
return legacy_parse_monolithic ( fc , data ) ;
}