2013-02-08 20:27:24 +04:00
/*
* Copyright ( C ) 2012 Red Hat , Inc .
* Copyright ( C ) 2012 Jeremy Kerr < jeremy . kerr @ canonical . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# 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 ;
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = CURRENT_TIME ;
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 ;
2016-05-21 03:01:21 +03:00
uuid_le_to_bin ( dentry - > d_name . name + namelen + 1 , & var - > var . VendorGuid ) ;
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 ;
efivar_entry_add ( var , & efivarfs_list ) ;
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 ,
} ;