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>
2018-02-22 20:15:06 +03:00
# include <linux/delay.h>
2013-02-08 20:27:24 +04:00
# 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 ) ) ;
2020-05-28 22:49:04 +03:00
inode - > i_mtime = current_time ( inode ) ;
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 ;
2020-05-28 22:49:05 +03:00
while ( ! __ratelimit ( & file - > f_cred - > user - > ratelimit ) )
msleep ( 50 ) ;
2018-02-22 20:15:06 +03:00
2013-02-08 20:27:24 +04:00
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 ;
}
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 ,
} ;