@ -376,9 +376,11 @@ EXPORT_SYMBOL(generic_permission);
* flag in inode - > i_opflags , that says " this has not special
* permission function , use the fast case " .
*/
static inline int do_inode_permission ( struct inode * inode , int mask )
static inline int do_inode_permission ( struct vfsmount * mnt , struct inode * inode , int mask )
{
if ( unlikely ( ! ( inode - > i_opflags & IOP_FASTPERM ) ) ) {
if ( likely ( mnt & & inode - > i_op - > permission2 ) )
return inode - > i_op - > permission2 ( mnt , inode , mask ) ;
if ( likely ( inode - > i_op - > permission ) )
return inode - > i_op - > permission ( inode , mask ) ;
@ -402,7 +404,7 @@ static inline int do_inode_permission(struct inode *inode, int mask)
* This does not check for a read - only file system . You probably want
* inode_permission ( ) .
*/
int __inode_permission ( struct inode * inode , int mask )
int __inode_permission2 ( struct vfsmount * mnt , struct inode * inode , int mask )
{
int retval ;
@ -422,7 +424,7 @@ int __inode_permission(struct inode *inode, int mask)
return - EACCES ;
}
retval = do_inode_permission ( inode , mask ) ;
retval = do_inode_permission ( mnt , inode , mask ) ;
if ( retval )
return retval ;
@ -430,7 +432,14 @@ int __inode_permission(struct inode *inode, int mask)
if ( retval )
return retval ;
return security_inode_permission ( inode , mask ) ;
retval = security_inode_permission ( inode , mask ) ;
return retval ;
}
EXPORT_SYMBOL ( __inode_permission2 ) ;
int __inode_permission ( struct inode * inode , int mask )
{
return __inode_permission2 ( NULL , inode , mask ) ;
}
EXPORT_SYMBOL ( __inode_permission ) ;
@ -465,14 +474,20 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
*
* When checking for MAY_APPEND , MAY_WRITE must also be set in @ mask .
*/
int inode_permission ( struct inode * inode , int mask )
int inode_permission2 ( struct vfsmount * mnt , struct inode * inode , int mask )
{
int retval ;
retval = sb_permission ( inode - > i_sb , inode , mask ) ;
if ( retval )
return retval ;
return __inode_permission ( inode , mask ) ;
return __inode_permission2 ( mnt , inode , mask ) ;
}
EXPORT_SYMBOL ( inode_permission2 ) ;
int inode_permission ( struct inode * inode , int mask )
{
return inode_permission2 ( NULL , inode , mask ) ;
}
EXPORT_SYMBOL ( inode_permission ) ;
@ -1667,13 +1682,13 @@ out:
static inline int may_lookup ( struct nameidata * nd )
{
if ( nd - > flags & LOOKUP_RCU ) {
int err = inode_permission ( nd - > inode , MAY_EXEC | MAY_NOT_BLOCK ) ;
int err = inode_permission2 ( nd - > path . mnt , nd - > inode , MAY_EXEC | MAY_NOT_BLOCK ) ;
if ( err ! = - ECHILD )
return err ;
if ( unlazy_walk ( nd ) )
return - ECHILD ;
}
return inode_permission ( nd - > inode , MAY_EXEC ) ;
return inode_permission2 ( nd - > path . mnt , nd - > inode , MAY_EXEC ) ;
}
static inline int handle_dots ( struct nameidata * nd , int type )
@ -2449,6 +2464,7 @@ EXPORT_SYMBOL(vfs_path_lookup);
/**
* lookup_one_len - filesystem helper to lookup single pathname component
* @ name : pathname component to lookup
* @ mnt : mount we are looking up on
* @ base : base directory to lookup from
* @ len : maximum length @ len should be interpreted to
*
@ -2457,7 +2473,7 @@ EXPORT_SYMBOL(vfs_path_lookup);
*
* The caller must hold base - > i_mutex .
*/
struct dentry * lookup_one_len ( const char * name , struct dentry * base , int len )
struct dentry * lookup_one_len2 ( const char * name , struct vfsmount * mnt , struct dentry * base , int len )
{
struct qstr this ;
unsigned int c ;
@ -2491,12 +2507,18 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
return ERR_PTR ( err ) ;
}
err = inode_permission ( base - > d_inode , MAY_EXEC ) ;
err = inode_permission2 ( mnt , base - > d_inode , MAY_EXEC ) ;
if ( err )
return ERR_PTR ( err ) ;
return __lookup_hash ( & this , base , 0 ) ;
}
EXPORT_SYMBOL ( lookup_one_len2 ) ;
struct dentry * lookup_one_len ( const char * name , struct dentry * base , int len )
{
return lookup_one_len2 ( name , NULL , base , len ) ;
}
EXPORT_SYMBOL ( lookup_one_len ) ;
/**
@ -2774,7 +2796,7 @@ EXPORT_SYMBOL(__check_sticky);
* 11. We don ' t allow removal of NFS sillyrenamed files ; it ' s handled by
* nfs_async_unlink ( ) .
*/
static int may_delete ( struct inode * dir , struct dentry * victim , bool isdir )
static int may_delete ( struct vfsmount * mnt , struct inode * dir , struct dentry * victim , bool isdir )
{
struct inode * inode = d_backing_inode ( victim ) ;
int error ;
@ -2786,7 +2808,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
BUG_ON ( victim - > d_parent - > d_inode ! = dir ) ;
audit_inode_child ( dir , victim , AUDIT_TYPE_CHILD_DELETE ) ;
error = inode_permission ( dir , MAY_WRITE | MAY_EXEC ) ;
error = inode_permission2 ( mnt , dir , MAY_WRITE | MAY_EXEC ) ;
if ( error )
return error ;
if ( IS_APPEND ( dir ) )
@ -2818,7 +2840,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
* 4. We should have write and exec permissions on dir
* 5. We can ' t do it if dir is immutable ( done in permission ( ) )
*/
static inline int may_create ( struct inode * dir , struct dentry * child )
static inline int may_create ( struct vfsmount * mnt , struct inode * dir , struct dentry * child )
{
struct user_namespace * s_user_ns ;
audit_inode_child ( dir , child , AUDIT_TYPE_CHILD_CREATE ) ;
@ -2830,7 +2852,7 @@ static inline int may_create(struct inode *dir, struct dentry *child)
if ( ! kuid_has_mapping ( s_user_ns , current_fsuid ( ) ) | |
! kgid_has_mapping ( s_user_ns , current_fsgid ( ) ) )
return - EOVERFLOW ;
return inode_permission ( dir , MAY_WRITE | MAY_EXEC ) ;
return inode_permission2 ( mnt , dir , MAY_WRITE | MAY_EXEC ) ;
}
/*
@ -2877,10 +2899,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
}
EXPORT_SYMBOL ( unlock_rename ) ;
int vfs_create ( struct inode * dir , struct dentry * dentry , umode_t mode ,
bool want_excl )
int vfs_create2 ( struct vfsmount * mnt , struct inode * dir , struct dentry * dentry ,
umode_t mode , bool want_excl )
{
int error = may_create ( dir , dentry ) ;
int error = may_create ( mnt , dir , dentry ) ;
if ( error )
return error ;
@ -2896,6 +2918,13 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
fsnotify_create ( dir , dentry ) ;
return error ;
}
EXPORT_SYMBOL ( vfs_create2 ) ;
int vfs_create ( struct inode * dir , struct dentry * dentry , umode_t mode ,
bool want_excl )
{
return vfs_create2 ( NULL , dir , dentry , mode , want_excl ) ;
}
EXPORT_SYMBOL ( vfs_create ) ;
bool may_open_dev ( const struct path * path )
@ -2907,6 +2936,7 @@ bool may_open_dev(const struct path *path)
static int may_open ( const struct path * path , int acc_mode , int flag )
{
struct dentry * dentry = path - > dentry ;
struct vfsmount * mnt = path - > mnt ;
struct inode * inode = dentry - > d_inode ;
int error ;
@ -2931,7 +2961,7 @@ static int may_open(const struct path *path, int acc_mode, int flag)
break ;
}
error = inode_permission ( inode , MAY_OPEN | acc_mode ) ;
error = inode_permission2 ( mnt , inode , MAY_OPEN | acc_mode ) ;
if ( error )
return error ;
@ -2993,7 +3023,7 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m
! kgid_has_mapping ( s_user_ns , current_fsgid ( ) ) )
return - EOVERFLOW ;
error = inode_permission ( dir - > dentry - > d_inode , MAY_WRITE | MAY_EXEC ) ;
error = inode_permission2 ( dir - > mnt , dir - > dentry - > d_inode , MAY_WRITE | MAY_EXEC ) ;
if ( error )
return error ;
@ -3406,7 +3436,8 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag)
int error ;
/* we want directory to be writable */
error = inode_permission ( dir , MAY_WRITE | MAY_EXEC ) ;
error = inode_permission2 ( ERR_PTR ( - EOPNOTSUPP ) , dir ,
MAY_WRITE | MAY_EXEC ) ;
if ( error )
goto out_err ;
error = - EOPNOTSUPP ;
@ -3684,9 +3715,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
}
EXPORT_SYMBOL ( user_path_create ) ;
int vfs_mknod ( struct inode * dir , struct dentry * dentry , umode_t mode , dev_t dev )
int vfs_mknod2 ( struct vfsmount * mnt , struct inode * dir , struct dentry * dentry , umode_t mode , dev_t dev )
{
int error = may_create ( dir , dentry ) ;
int error = may_create ( mnt , dir , dentry ) ;
if ( error )
return error ;
@ -3710,6 +3741,12 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
fsnotify_create ( dir , dentry ) ;
return error ;
}
EXPORT_SYMBOL ( vfs_mknod2 ) ;
int vfs_mknod ( struct inode * dir , struct dentry * dentry , umode_t mode , dev_t dev )
{
return vfs_mknod2 ( NULL , dir , dentry , mode , dev ) ;
}
EXPORT_SYMBOL ( vfs_mknod ) ;
static int may_mknod ( umode_t mode )
@ -3752,12 +3789,12 @@ retry:
goto out ;
switch ( mode & S_IFMT ) {
case 0 : case S_IFREG :
error = vfs_create ( path . dentry - > d_inode , dentry , mode , true ) ;
error = vfs_create2 ( path . mnt , path . dentry - > d_inode , dentry , mode , true ) ;
if ( ! error )
ima_post_path_mknod ( dentry ) ;
break ;
case S_IFCHR : case S_IFBLK :
error = vfs_mknod ( path . dentry - > d_inode , dentry , mode ,
error = vfs_mknod2 ( path . mnt , path . dentry - > d_inode , dentry , mode ,
new_decode_dev ( dev ) ) ;
break ;
case S_IFIFO : case S_IFSOCK :
@ -3778,9 +3815,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
return sys_mknodat ( AT_FDCWD , filename , mode , dev ) ;
}
int vfs_mkdir ( struct inode * dir , struct dentry * dentry , umode_t mode )
int vfs_mkdir2 ( struct vfsmount * mnt , struct inode * dir , struct dentry * dentry , umode_t mode )
{
int error = may_create ( dir , dentry ) ;
int error = may_create ( mnt , dir , dentry ) ;
unsigned max_links = dir - > i_sb - > s_max_links ;
if ( error )
@ -3802,6 +3839,12 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
fsnotify_mkdir ( dir , dentry ) ;
return error ;
}
EXPORT_SYMBOL ( vfs_mkdir2 ) ;
int vfs_mkdir ( struct inode * dir , struct dentry * dentry , umode_t mode )
{
return vfs_mkdir2 ( NULL , dir , dentry , mode ) ;
}
EXPORT_SYMBOL ( vfs_mkdir ) ;
SYSCALL_DEFINE3 ( mkdirat , int , dfd , const char __user * , pathname , umode_t , mode )
@ -3820,7 +3863,7 @@ retry:
mode & = ~ current_umask ( ) ;
error = security_path_mkdir ( & path , dentry , mode ) ;
if ( ! error )
error = vfs_mkdir ( path . dentry - > d_inode , dentry , mode ) ;
error = vfs_mkdir2 ( path . mnt , path . dentry - > d_inode , dentry , mode ) ;
done_path_create ( & path , dentry ) ;
if ( retry_estale ( error , lookup_flags ) ) {
lookup_flags | = LOOKUP_REVAL ;
@ -3834,9 +3877,9 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
return sys_mkdirat ( AT_FDCWD , pathname , mode ) ;
}
int vfs_rmdir ( struct inode * dir , struct dentry * dentry )
int vfs_rmdir2 ( struct vfsmount * mnt , struct inode * dir , struct dentry * dentry )
{
int error = may_delete ( dir , dentry , 1 ) ;
int error = may_delete ( mnt , dir , dentry , 1 ) ;
if ( error )
return error ;
@ -3871,6 +3914,10 @@ out:
d_delete ( dentry ) ;
return error ;
}
int vfs_rmdir ( struct inode * dir , struct dentry * dentry )
{
return vfs_rmdir2 ( NULL , dir , dentry ) ;
}
EXPORT_SYMBOL ( vfs_rmdir ) ;
static long do_rmdir ( int dfd , const char __user * pathname )
@ -3916,7 +3963,7 @@ retry:
error = security_path_rmdir ( & path , dentry ) ;
if ( error )
goto exit3 ;
error = vfs_rmdir ( path . dentry - > d_inode , dentry ) ;
error = vfs_rmdir2 ( path . mnt , path . dentry - > d_inode , dentry ) ;
exit3 :
dput ( dentry ) ;
exit2 :
@ -3955,10 +4002,10 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported .
*/
int vfs_unlink ( struct inode * dir , struct dentry * dentry , struct inode * * delegated_inode )
int vfs_unlink2 ( struct vfsmount * mnt , struct inode * dir , struct dentry * dentry , struct inode * * delegated_inode )
{
struct inode * target = dentry - > d_inode ;
int error = may_delete ( dir , dentry , 0 ) ;
int error = may_delete ( mnt , dir , dentry , 0 ) ;
if ( error )
return error ;
@ -3993,6 +4040,12 @@ out:
return error ;
}
EXPORT_SYMBOL ( vfs_unlink2 ) ;
int vfs_unlink ( struct inode * dir , struct dentry * dentry , struct inode * * delegated_inode )
{
return vfs_unlink2 ( NULL , dir , dentry , delegated_inode ) ;
}
EXPORT_SYMBOL ( vfs_unlink ) ;
/*
@ -4040,7 +4093,7 @@ retry_deleg:
error = security_path_unlink ( & path , dentry ) ;
if ( error )
goto exit2 ;
error = vfs_unlink ( path . dentry - > d_inode , dentry , & delegated_inode ) ;
error = vfs_unlink2 ( path . mnt , path . dentry - > d_inode , dentry , & delegated_inode ) ;
exit2 :
dput ( dentry ) ;
}
@ -4090,9 +4143,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
return do_unlinkat ( AT_FDCWD , pathname ) ;
}
int vfs_symlink ( struct inode * dir , struct dentry * dentry , const char * oldname )
int vfs_symlink2 ( struct vfsmount * mnt , struct inode * dir , struct dentry * dentry , const char * oldname )
{
int error = may_create ( dir , dentry ) ;
int error = may_create ( mnt , dir , dentry ) ;
if ( error )
return error ;
@ -4109,6 +4162,12 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
fsnotify_create ( dir , dentry ) ;
return error ;
}
EXPORT_SYMBOL ( vfs_symlink2 ) ;
int vfs_symlink ( struct inode * dir , struct dentry * dentry , const char * oldname )
{
return vfs_symlink2 ( NULL , dir , dentry , oldname ) ;
}
EXPORT_SYMBOL ( vfs_symlink ) ;
SYSCALL_DEFINE3 ( symlinkat , const char __user * , oldname ,
@ -4131,7 +4190,7 @@ retry:
error = security_path_symlink ( & path , dentry , from - > name ) ;
if ( ! error )
error = vfs_symlink ( path . dentry - > d_inode , dentry , from - > name ) ;
error = vfs_symlink2 ( path . mnt , path . dentry - > d_inode , dentry , from - > name ) ;
done_path_create ( & path , dentry ) ;
if ( retry_estale ( error , lookup_flags ) ) {
lookup_flags | = LOOKUP_REVAL ;
@ -4166,7 +4225,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported .
*/
int vfs_link ( struct dentry * old_dentry , struct inode * dir , struct dentry * new_dentry , struct inode * * delegated_inode )
int vfs_link2 ( struct vfsmount * mnt , struct dentry * old_dentry , struct inode * dir , struct dentry * new_dentry , struct inode * * delegated_inode )
{
struct inode * inode = old_dentry - > d_inode ;
unsigned max_links = dir - > i_sb - > s_max_links ;
@ -4175,7 +4234,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if ( ! inode )
return - ENOENT ;
error = may_create ( dir , new_dentry ) ;
error = may_create ( mnt , dir , new_dentry ) ;
if ( error )
return error ;
@ -4225,6 +4284,12 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
fsnotify_link ( dir , inode , new_dentry ) ;
return error ;
}
EXPORT_SYMBOL ( vfs_link2 ) ;
int vfs_link ( struct dentry * old_dentry , struct inode * dir , struct dentry * new_dentry , struct inode * * delegated_inode )
{
return vfs_link2 ( NULL , old_dentry , dir , new_dentry , delegated_inode ) ;
}
EXPORT_SYMBOL ( vfs_link ) ;
/*
@ -4280,7 +4345,7 @@ retry:
error = security_path_link ( old_path . dentry , & new_path , new_dentry ) ;
if ( error )
goto out_dput ;
error = vfs_link ( old_path . dentry , new_path . dentry - > d_inode , new_dentry , & delegated_inode ) ;
error = vfs_link2 ( old_path . mnt , old_path . dentry , new_path . dentry - > d_inode , new_dentry , & delegated_inode ) ;
out_dput :
done_path_create ( & new_path , new_dentry ) ;
if ( delegated_inode ) {
@ -4356,7 +4421,8 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
* - > i_mutex on parents , which works but leads to some truly excessive
* locking ] .
*/
int vfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
int vfs_rename2 ( struct vfsmount * mnt ,
struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry ,
struct inode * * delegated_inode , unsigned int flags )
{
@ -4371,19 +4437,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if ( source = = target )
return 0 ;
error = may_delete ( old_dir , old_dentry , is_dir ) ;
error = may_delete ( mnt , old_dir , old_dentry , is_dir ) ;
if ( error )
return error ;
if ( ! target ) {
error = may_create ( new_dir , new_dentry ) ;
error = may_create ( mnt , new_dir , new_dentry ) ;
} else {
new_is_dir = d_is_dir ( new_dentry ) ;
if ( ! ( flags & RENAME_EXCHANGE ) )
error = may_delete ( new_dir , new_dentry , is_dir ) ;
error = may_delete ( mnt , new_dir , new_dentry , is_dir ) ;
else
error = may_delete ( new_dir , new_dentry , new_is_dir ) ;
error = may_delete ( mnt , new_dir , new_dentry , new_is_dir ) ;
}
if ( error )
return error ;
@ -4397,12 +4463,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
*/
if ( new_dir ! = old_dir ) {
if ( is_dir ) {
error = inode_permission ( source , MAY_WRITE ) ;
error = inode_permission2 ( mnt , source , MAY_WRITE ) ;
if ( error )
return error ;
}
if ( ( flags & RENAME_EXCHANGE ) & & new_is_dir ) {
error = inode_permission ( target , MAY_WRITE ) ;
error = inode_permission2 ( mnt , target , MAY_WRITE ) ;
if ( error )
return error ;
}
@ -4479,6 +4545,14 @@ out:
return error ;
}
EXPORT_SYMBOL ( vfs_rename2 ) ;
int vfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry ,
struct inode * * delegated_inode , unsigned int flags )
{
return vfs_rename2 ( NULL , old_dir , old_dentry , new_dir , new_dentry , delegated_inode , flags ) ;
}
EXPORT_SYMBOL ( vfs_rename ) ;
SYSCALL_DEFINE5 ( renameat2 , int , olddfd , const char __user * , oldname ,
@ -4592,7 +4666,7 @@ retry_deleg:
& new_path , new_dentry , flags ) ;
if ( error )
goto exit5 ;
error = vfs_rename ( old_path . dentry - > d_inode , old_dentry ,
error = vfs_rename2 ( old_path . mnt , old_path . dentry - > d_inode , old_dentry ,
new_path . dentry - > d_inode , new_dentry ,
& delegated_inode , flags ) ;
exit5 :
@ -4637,7 +4711,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
int vfs_whiteout ( struct inode * dir , struct dentry * dentry )
{
int error = may_create ( dir , dentry ) ;
int error = may_create ( NULL , dir , dentry ) ;
if ( error )
return error ;