1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2012 Red Hat, Inc. 4 * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com> 5 */ 6 7 #include <linux/efi.h> 8 #include <linux/fs.h> 9 #include <linux/ctype.h> 10 #include <linux/kmemleak.h> 11 #include <linux/slab.h> 12 #include <linux/uuid.h> 13 14 #include "internal.h" 15 16 struct inode *efivarfs_get_inode(struct super_block *sb, 17 const struct inode *dir, int mode, 18 dev_t dev, bool is_removable) 19 { 20 struct inode *inode = new_inode(sb); 21 22 if (inode) { 23 inode->i_ino = get_next_ino(); 24 inode->i_mode = mode; 25 inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); 26 inode->i_flags = is_removable ? 0 : S_IMMUTABLE; 27 switch (mode & S_IFMT) { 28 case S_IFREG: 29 inode->i_fop = &efivarfs_file_operations; 30 break; 31 case S_IFDIR: 32 inode->i_op = &efivarfs_dir_inode_operations; 33 inode->i_fop = &simple_dir_operations; 34 inc_nlink(inode); 35 break; 36 } 37 } 38 return inode; 39 } 40 41 /* 42 * Return true if 'str' is a valid efivarfs filename of the form, 43 * 44 * VariableName-12345678-1234-1234-1234-1234567891bc 45 */ 46 bool efivarfs_valid_name(const char *str, int len) 47 { 48 const char *s = str + len - EFI_VARIABLE_GUID_LEN; 49 50 /* 51 * We need a GUID, plus at least one letter for the variable name, 52 * plus the '-' separator 53 */ 54 if (len < EFI_VARIABLE_GUID_LEN + 2) 55 return false; 56 57 /* GUID must be preceded by a '-' */ 58 if (*(s - 1) != '-') 59 return false; 60 61 /* 62 * Validate that 's' is of the correct format, e.g. 63 * 64 * 12345678-1234-1234-1234-123456789abc 65 */ 66 return uuid_is_valid(s); 67 } 68 69 static int efivarfs_create(struct user_namespace *mnt_userns, struct inode *dir, 70 struct dentry *dentry, umode_t mode, bool excl) 71 { 72 struct inode *inode = NULL; 73 struct efivar_entry *var; 74 int namelen, i = 0, err = 0; 75 bool is_removable = false; 76 77 if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) 78 return -EINVAL; 79 80 var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL); 81 if (!var) 82 return -ENOMEM; 83 84 /* length of the variable name itself: remove GUID and separator */ 85 namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1; 86 87 err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid); 88 if (err) 89 goto out; 90 91 if (efivar_variable_is_removable(var->var.VendorGuid, 92 dentry->d_name.name, namelen)) 93 is_removable = true; 94 95 inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable); 96 if (!inode) { 97 err = -ENOMEM; 98 goto out; 99 } 100 101 for (i = 0; i < namelen; i++) 102 var->var.VariableName[i] = dentry->d_name.name[i]; 103 104 var->var.VariableName[i] = '\0'; 105 106 inode->i_private = var; 107 kmemleak_ignore(var); 108 109 err = efivar_entry_add(var, &efivarfs_list); 110 if (err) 111 goto out; 112 113 d_instantiate(dentry, inode); 114 dget(dentry); 115 out: 116 if (err) { 117 kfree(var); 118 if (inode) 119 iput(inode); 120 } 121 return err; 122 } 123 124 static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) 125 { 126 struct efivar_entry *var = d_inode(dentry)->i_private; 127 128 if (efivar_entry_delete(var)) 129 return -EINVAL; 130 131 drop_nlink(d_inode(dentry)); 132 dput(dentry); 133 return 0; 134 }; 135 136 const struct inode_operations efivarfs_dir_inode_operations = { 137 .lookup = simple_lookup, 138 .unlink = efivarfs_unlink, 139 .create = efivarfs_create, 140 }; 141