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>
2013-05-02 04:51:54 +04:00
# include <linux/slab.h>
2016-02-08 22:48:15 +03:00
# include <linux/mount.h>
2013-02-08 20:27:24 +04:00
# include "internal.h"
static ssize_t efivarfs_file_write ( struct file * file ,
const char __user * userbuf , size_t count , loff_t * ppos )
{
struct efivar_entry * var = file - > private_data ;
void * data ;
u32 attributes ;
struct inode * inode = file - > f_mapping - > host ;
unsigned long datasize = count - sizeof ( attributes ) ;
2013-10-30 22:57:41 +04:00
ssize_t bytes ;
2013-02-08 20:27:24 +04:00
bool set = false ;
if ( count < sizeof ( attributes ) )
return - EINVAL ;
if ( copy_from_user ( & attributes , userbuf , sizeof ( attributes ) ) )
return - EFAULT ;
if ( attributes & ~ ( EFI_VARIABLE_MASK ) )
return - EINVAL ;
2013-10-30 22:57:41 +04:00
data = memdup_user ( userbuf + sizeof ( attributes ) , datasize ) ;
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
2013-02-08 20:27:24 +04:00
bytes = efivar_entry_set_get_size ( var , attributes , & datasize ,
data , & set ) ;
2013-05-10 14:29:21 +04:00
if ( ! set & & bytes ) {
if ( bytes = = - ENOENT )
bytes = - EIO ;
2013-02-08 20:27:24 +04:00
goto out ;
2013-05-10 14:29:21 +04:00
}
2013-02-08 20:27:24 +04:00
if ( bytes = = - ENOENT ) {
drop_nlink ( inode ) ;
2014-10-31 08:22:04 +03:00
d_delete ( file - > f_path . dentry ) ;
dput ( file - > f_path . dentry ) ;
2013-02-08 20:27:24 +04:00
} else {
2016-01-22 23:40:57 +03:00
inode_lock ( inode ) ;
2013-02-08 20:27:24 +04:00
i_size_write ( inode , datasize + sizeof ( attributes ) ) ;
2016-01-22 23:40:57 +03:00
inode_unlock ( inode ) ;
2013-02-08 20:27:24 +04:00
}
bytes = count ;
out :
kfree ( data ) ;
return bytes ;
}
static ssize_t efivarfs_file_read ( struct file * file , char __user * userbuf ,
size_t count , loff_t * ppos )
{
struct efivar_entry * var = file - > private_data ;
unsigned long datasize = 0 ;
u32 attributes ;
void * data ;
ssize_t size = 0 ;
int err ;
err = efivar_entry_size ( var , & datasize ) ;
2013-05-10 14:29:21 +04:00
/*
* efivarfs represents uncommitted variables with
* zero - length files . Reading them should return EOF .
*/
if ( err = = - ENOENT )
return 0 ;
else if ( err )
2013-02-08 20:27:24 +04:00
return err ;
data = kmalloc ( datasize + sizeof ( attributes ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
size = efivar_entry_get ( var , & attributes , & datasize ,
data + sizeof ( attributes ) ) ;
if ( size )
goto out_free ;
memcpy ( data , & attributes , sizeof ( attributes ) ) ;
size = simple_read_from_buffer ( userbuf , count , ppos ,
data , datasize + sizeof ( attributes ) ) ;
out_free :
kfree ( data ) ;
return size ;
}
2016-02-08 22:48:15 +03:00
static int
efivarfs_ioc_getxflags ( struct file * file , void __user * arg )
{
struct inode * inode = file - > f_mapping - > host ;
unsigned int i_flags ;
unsigned int flags = 0 ;
i_flags = inode - > i_flags ;
if ( i_flags & S_IMMUTABLE )
flags | = FS_IMMUTABLE_FL ;
if ( copy_to_user ( arg , & flags , sizeof ( flags ) ) )
return - EFAULT ;
return 0 ;
}
static int
efivarfs_ioc_setxflags ( struct file * file , void __user * arg )
{
struct inode * inode = file - > f_mapping - > host ;
unsigned int flags ;
unsigned int i_flags = 0 ;
int error ;
if ( ! inode_owner_or_capable ( inode ) )
return - EACCES ;
if ( copy_from_user ( & flags , arg , sizeof ( flags ) ) )
return - EFAULT ;
if ( flags & ~ FS_IMMUTABLE_FL )
return - EOPNOTSUPP ;
if ( ! capable ( CAP_LINUX_IMMUTABLE ) )
return - EPERM ;
if ( flags & FS_IMMUTABLE_FL )
i_flags | = S_IMMUTABLE ;
error = mnt_want_write_file ( file ) ;
if ( error )
return error ;
inode_lock ( inode ) ;
inode_set_flags ( inode , i_flags , S_IMMUTABLE ) ;
inode_unlock ( inode ) ;
mnt_drop_write_file ( file ) ;
return 0 ;
}
long
efivarfs_file_ioctl ( struct file * file , unsigned int cmd , unsigned long p )
{
void __user * arg = ( void __user * ) p ;
switch ( cmd ) {
case FS_IOC_GETFLAGS :
return efivarfs_ioc_getxflags ( file , arg ) ;
case FS_IOC_SETFLAGS :
return efivarfs_ioc_setxflags ( file , arg ) ;
}
return - ENOTTY ;
}
2013-02-08 20:27:24 +04:00
const struct file_operations efivarfs_file_operations = {
2013-04-20 20:16:44 +04:00
. open = simple_open ,
2013-02-08 20:27:24 +04:00
. read = efivarfs_file_read ,
. write = efivarfs_file_write ,
. llseek = no_llseek ,
2016-02-08 22:48:15 +03:00
. unlocked_ioctl = efivarfs_file_ioctl ,
2013-02-08 20:27:24 +04:00
} ;