Skip to content

Commit

Permalink
Merge branch 'efivarfs' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
ardbiesheuvel committed Jan 19, 2025
2 parents 8a32d46 + 908af31 commit e7b4b1f
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 232 deletions.
59 changes: 50 additions & 9 deletions fs/efivarfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,41 @@ static ssize_t efivarfs_file_write(struct file *file,
if (IS_ERR(data))
return PTR_ERR(data);

inode_lock(inode);
if (var->removed) {
/*
* file got removed; don't allow a set. Caused by an
* unsuccessful create or successful delete write
* racing with us.
*/
bytes = -EIO;
goto out;
}

bytes = efivar_entry_set_get_size(var, attributes, &datasize,
data, &set);
if (!set && bytes) {
if (!set) {
if (bytes == -ENOENT)
bytes = -EIO;
goto out;
}

if (bytes == -ENOENT) {
drop_nlink(inode);
d_delete(file->f_path.dentry);
dput(file->f_path.dentry);
/*
* zero size signals to release that the write deleted
* the variable
*/
i_size_write(inode, 0);
} else {
inode_lock(inode);
i_size_write(inode, datasize + sizeof(attributes));
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
inode_unlock(inode);
}

bytes = count;

out:
inode_unlock(inode);

kfree(data);

return bytes;
Expand Down Expand Up @@ -106,8 +119,36 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}

static int efivarfs_file_release(struct inode *inode, struct file *file)
{
struct efivar_entry *var = inode->i_private;

inode_lock(inode);
var->removed = (--var->open_count == 0 && i_size_read(inode) == 0);
inode_unlock(inode);

if (var->removed)
simple_recursive_removal(file->f_path.dentry, NULL);

return 0;
}

static int efivarfs_file_open(struct inode *inode, struct file *file)
{
struct efivar_entry *entry = inode->i_private;

file->private_data = entry;

inode_lock(inode);
entry->open_count++;
inode_unlock(inode);

return 0;
}

const struct file_operations efivarfs_file_operations = {
.open = simple_open,
.read = efivarfs_file_read,
.write = efivarfs_file_write,
.open = efivarfs_file_open,
.read = efivarfs_file_read,
.write = efivarfs_file_write,
.release = efivarfs_file_release,
};
41 changes: 13 additions & 28 deletions fs/efivarfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,61 +77,46 @@ static bool efivarfs_valid_name(const char *str, int len)
static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl)
{
struct efivarfs_fs_info *info = dir->i_sb->s_fs_info;
struct inode *inode = NULL;
struct efivar_entry *var;
int namelen, i = 0, err = 0;
bool is_removable = false;
efi_guid_t vendor;

if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
return -EINVAL;

var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
if (!var)
return -ENOMEM;

/* length of the variable name itself: remove GUID and separator */
namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1;

err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid);
err = guid_parse(dentry->d_name.name + namelen + 1, &vendor);
if (err)
goto out;
if (guid_equal(&var->var.VendorGuid, &LINUX_EFI_RANDOM_SEED_TABLE_GUID)) {
err = -EPERM;
goto out;
}
return err;
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
return -EPERM;

if (efivar_variable_is_removable(var->var.VendorGuid,
if (efivar_variable_is_removable(vendor,
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;
}
if (!inode)
return -ENOMEM;
var = efivar_entry(inode);

var->var.VendorGuid = vendor;

for (i = 0; i < namelen; i++)
var->var.VariableName[i] = dentry->d_name.name[i];

var->var.VariableName[i] = '\0';

inode->i_private = var;
kmemleak_ignore(var);

err = efivar_entry_add(var, &info->efivarfs_list);
if (err)
goto out;

d_instantiate(dentry, inode);
dget(dentry);
out:
if (err) {
kfree(var);
if (inode)
iput(inode);
}
return err;

return 0;
}

static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
Expand Down
26 changes: 13 additions & 13 deletions fs/efivarfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#ifndef EFIVAR_FS_INTERNAL_H
#define EFIVAR_FS_INTERNAL_H

#include <linux/list.h>
#include <linux/efi.h>

struct efivarfs_mount_opts {
Expand All @@ -16,30 +15,30 @@ struct efivarfs_mount_opts {

struct efivarfs_fs_info {
struct efivarfs_mount_opts mount_opts;
struct list_head efivarfs_list;
struct super_block *sb;
struct notifier_block nb;
};

struct efi_variable {
efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
efi_guid_t VendorGuid;
__u32 Attributes;
};

struct efivar_entry {
struct efi_variable var;
struct list_head list;
struct kobject kobj;
struct inode vfs_inode;
unsigned long open_count;
bool removed;
};

int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
struct list_head *),
void *data, struct list_head *head);
static inline struct efivar_entry *efivar_entry(struct inode *inode)
{
return container_of(inode, struct efivar_entry, vfs_inode);
}

int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data);

int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
void efivar_entry_remove(struct efivar_entry *entry);
int efivar_entry_delete(struct efivar_entry *entry);

int efivar_entry_size(struct efivar_entry *entry, unsigned long *size);
Expand All @@ -50,13 +49,14 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
unsigned long *size, void *data, bool *set);

int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data);

bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
unsigned long data_size);
bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
size_t len);
char *efivar_get_utf8name(const efi_char16_t *name16, efi_guid_t *vendor);
bool efivarfs_variable_is_present(efi_char16_t *variable_name,
efi_guid_t *vendor, void *data);

extern const struct file_operations efivarfs_file_operations;
extern const struct inode_operations efivarfs_dir_inode_operations;
Expand Down
Loading

0 comments on commit e7b4b1f

Please sign in to comment.