2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-02-08 20:27:24 +04:00
/*
* Copyright ( C ) 2012 Red Hat , Inc .
* Copyright ( C ) 2012 Jeremy Kerr < jeremy . kerr @ canonical . com >
*/
# include <linux/efi.h>
# include <linux/fs.h>
# include <linux/ctype.h>
2020-11-25 10:45:55 +03:00
# include <linux/kmemleak.h>
2013-05-02 04:51:54 +04:00
# include <linux/slab.h>
2016-05-21 03:01:21 +03:00
# include <linux/uuid.h>
2021-04-07 15:36:44 +03:00
# include <linux/fileattr.h>
2013-02-08 20:27:24 +04:00
# include "internal.h"
2021-04-07 15:36:44 +03:00
static const struct inode_operations efivarfs_file_inode_operations ;
2013-02-08 20:27:24 +04:00
struct inode * efivarfs_get_inode ( struct super_block * sb ,
2016-02-08 22:48:15 +03:00
const struct inode * dir , int mode ,
dev_t dev , bool is_removable )
2013-02-08 20:27:24 +04:00
{
struct inode * inode = new_inode ( sb ) ;
2023-10-20 01:46:39 +03:00
struct efivarfs_fs_info * fsi = sb - > s_fs_info ;
struct efivarfs_mount_opts * opts = & fsi - > mount_opts ;
2013-02-08 20:27:24 +04:00
if ( inode ) {
2023-10-20 01:46:39 +03:00
inode - > i_uid = opts - > uid ;
inode - > i_gid = opts - > gid ;
2013-02-08 20:27:24 +04:00
inode - > i_ino = get_next_ino ( ) ;
inode - > i_mode = mode ;
2023-10-04 21:52:15 +03:00
simple_inode_init_ts ( inode ) ;
2016-02-08 22:48:15 +03:00
inode - > i_flags = is_removable ? 0 : S_IMMUTABLE ;
2013-02-08 20:27:24 +04:00
switch ( mode & S_IFMT ) {
case S_IFREG :
2021-04-07 15:36:44 +03:00
inode - > i_op = & efivarfs_file_inode_operations ;
2013-02-08 20:27:24 +04:00
inode - > i_fop = & efivarfs_file_operations ;
break ;
case S_IFDIR :
inode - > i_op = & efivarfs_dir_inode_operations ;
inode - > i_fop = & simple_dir_operations ;
inc_nlink ( inode ) ;
break ;
}
}
return inode ;
}
/*
* Return true if ' str ' is a valid efivarfs filename of the form ,
*
* VariableName - 12345678 - 1234 - 1234 - 1234 - 1234567891 bc
*/
bool efivarfs_valid_name ( const char * str , int len )
{
const char * s = str + len - EFI_VARIABLE_GUID_LEN ;
/*
* We need a GUID , plus at least one letter for the variable name ,
* plus the ' - ' separator
*/
if ( len < EFI_VARIABLE_GUID_LEN + 2 )
return false ;
/* GUID must be preceded by a '-' */
if ( * ( s - 1 ) ! = ' - ' )
return false ;
/*
* Validate that ' s ' is of the correct format , e . g .
*
* 12345678 - 1234 - 1234 - 1234 - 123456789 abc
*/
2016-05-21 03:01:21 +03:00
return uuid_is_valid ( s ) ;
2013-02-08 20:27:24 +04:00
}
2023-01-13 14:49:13 +03:00
static int efivarfs_create ( struct mnt_idmap * idmap , struct inode * dir ,
2021-01-21 16:19:43 +03:00
struct dentry * dentry , umode_t mode , bool excl )
2013-02-08 20:27:24 +04:00
{
2023-12-08 19:39:29 +03:00
struct efivarfs_fs_info * info = dir - > i_sb - > s_fs_info ;
2016-02-08 22:48:15 +03:00
struct inode * inode = NULL ;
2013-02-08 20:27:24 +04:00
struct efivar_entry * var ;
int namelen , i = 0 , err = 0 ;
2016-02-08 22:48:15 +03:00
bool is_removable = false ;
2013-02-08 20:27:24 +04:00
if ( ! efivarfs_valid_name ( dentry - > d_name . name , dentry - > d_name . len ) )
return - EINVAL ;
var = kzalloc ( sizeof ( struct efivar_entry ) , GFP_KERNEL ) ;
2016-02-08 22:48:15 +03:00
if ( ! var )
return - ENOMEM ;
2013-02-08 20:27:24 +04:00
/* length of the variable name itself: remove GUID and separator */
namelen = dentry - > d_name . len - EFI_VARIABLE_GUID_LEN - 1 ;
2018-07-20 04:47:26 +03:00
err = guid_parse ( dentry - > d_name . name + namelen + 1 , & var - > var . VendorGuid ) ;
if ( err )
goto out ;
2022-11-22 05:04:00 +03:00
if ( guid_equal ( & var - > var . VendorGuid , & LINUX_EFI_RANDOM_SEED_TABLE_GUID ) ) {
err = - EPERM ;
goto out ;
}
2013-02-08 20:27:24 +04:00
2016-02-08 22:48:15 +03:00
if ( efivar_variable_is_removable ( var - > var . VendorGuid ,
dentry - > d_name . name , namelen ) )
is_removable = true ;
inode = efivarfs_get_inode ( dir - > i_sb , dir , mode , 0 , is_removable ) ;
if ( ! inode ) {
err = - ENOMEM ;
goto out ;
}
2013-02-08 20:27:24 +04:00
for ( i = 0 ; i < namelen ; i + + )
var - > var . VariableName [ i ] = dentry - > d_name . name [ i ] ;
var - > var . VariableName [ i ] = ' \0 ' ;
inode - > i_private = var ;
2020-11-25 10:45:55 +03:00
kmemleak_ignore ( var ) ;
2013-02-08 20:27:24 +04:00
2023-12-08 19:39:29 +03:00
err = efivar_entry_add ( var , & info - > efivarfs_list ) ;
2016-07-15 22:36:30 +03:00
if ( err )
goto out ;
2013-02-08 20:27:24 +04:00
d_instantiate ( dentry , inode ) ;
dget ( dentry ) ;
out :
if ( err ) {
kfree ( var ) ;
2016-02-08 22:48:15 +03:00
if ( inode )
iput ( inode ) ;
2013-02-08 20:27:24 +04:00
}
return err ;
}
static int efivarfs_unlink ( struct inode * dir , struct dentry * dentry )
{
2015-03-18 01:25:59 +03:00
struct efivar_entry * var = d_inode ( dentry ) - > i_private ;
2013-02-08 20:27:24 +04:00
if ( efivar_entry_delete ( var ) )
return - EINVAL ;
2015-03-18 01:25:59 +03:00
drop_nlink ( d_inode ( dentry ) ) ;
2013-02-08 20:27:24 +04:00
dput ( dentry ) ;
return 0 ;
} ;
const struct inode_operations efivarfs_dir_inode_operations = {
2013-07-14 17:48:35 +04:00
. lookup = simple_lookup ,
2013-02-08 20:27:24 +04:00
. unlink = efivarfs_unlink ,
. create = efivarfs_create ,
} ;
2021-04-07 15:36:44 +03:00
static int
efivarfs_fileattr_get ( struct dentry * dentry , struct fileattr * fa )
{
unsigned int i_flags ;
unsigned int flags = 0 ;
i_flags = d_inode ( dentry ) - > i_flags ;
if ( i_flags & S_IMMUTABLE )
flags | = FS_IMMUTABLE_FL ;
fileattr_fill_flags ( fa , flags ) ;
return 0 ;
}
static int
2023-01-13 14:49:21 +03:00
efivarfs_fileattr_set ( struct mnt_idmap * idmap ,
2021-04-07 15:36:44 +03:00
struct dentry * dentry , struct fileattr * fa )
{
unsigned int i_flags = 0 ;
if ( fileattr_has_fsx ( fa ) )
return - EOPNOTSUPP ;
if ( fa - > flags & ~ FS_IMMUTABLE_FL )
return - EOPNOTSUPP ;
if ( fa - > flags & FS_IMMUTABLE_FL )
i_flags | = S_IMMUTABLE ;
inode_set_flags ( d_inode ( dentry ) , i_flags , S_IMMUTABLE ) ;
return 0 ;
}
static const struct inode_operations efivarfs_file_inode_operations = {
. fileattr_get = efivarfs_fileattr_get ,
. fileattr_set = efivarfs_fileattr_set ,
} ;