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>
2013-05-02 04:51:54 +04:00
# include <linux/slab.h>
2016-05-21 03:01:21 +03:00
# include <linux/uuid.h>
2013-02-08 20:27:24 +04:00
# include "internal.h"
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 ) ;
if ( inode ) {
inode - > i_ino = get_next_ino ( ) ;
inode - > i_mode = mode ;
2016-09-14 17:48:04 +03:00
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = current_time ( 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 :
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
}
static int efivarfs_create ( struct inode * dir , struct dentry * dentry ,
umode_t mode , bool excl )
{
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 ;
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 ;
2016-07-15 22:36:30 +03:00
err = efivar_entry_add ( var , & efivarfs_list ) ;
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 ,
} ;