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 #include <linux/fileattr.h> 14 15 #include "internal.h" 16 17 static const struct inode_operations efivarfs_file_inode_operations; 18 19 struct inode *efivarfs_get_inode(struct super_block *sb, 20 const struct inode *dir, int mode, 21 dev_t dev, bool is_removable) 22 { 23 struct inode *inode = new_inode(sb); 24 struct efivarfs_fs_info *fsi = sb->s_fs_info; 25 struct efivarfs_mount_opts *opts = &fsi->mount_opts; 26 27 if (inode) { 28 inode->i_uid = opts->uid; 29 inode->i_gid = opts->gid; 30 inode->i_ino = get_next_ino(); 31 inode->i_mode = mode; 32 simple_inode_init_ts(inode); 33 inode->i_flags = is_removable ? 0 : S_IMMUTABLE; 34 switch (mode & S_IFMT) { 35 case S_IFREG: 36 inode->i_op = &efivarfs_file_inode_operations; 37 inode->i_fop = &efivarfs_file_operations; 38 break; 39 case S_IFDIR: 40 inode->i_op = &efivarfs_dir_inode_operations; 41 inode->i_fop = &simple_dir_operations; 42 inc_nlink(inode); 43 break; 44 } 45 } 46 return inode; 47 } 48 49 /* 50 * Return true if 'str' is a valid efivarfs filename of the form, 51 * 52 * VariableName-12345678-1234-1234-1234-1234567891bc 53 */ 54 bool efivarfs_valid_name(const char *str, int len) 55 { 56 const char *s = str + len - EFI_VARIABLE_GUID_LEN; 57 58 /* 59 * We need a GUID, plus at least one letter for the variable name, 60 * plus the '-' separator 61 */ 62 if (len < EFI_VARIABLE_GUID_LEN + 2) 63 return false; 64 65 /* GUID must be preceded by a '-' */ 66 if (*(s - 1) != '-') 67 return false; 68 69 /* 70 * Validate that 's' is of the correct format, e.g. 71 * 72 * 12345678-1234-1234-1234-123456789abc 73 */ 74 return uuid_is_valid(s); 75 } 76 77 static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir, 78 struct dentry *dentry, umode_t mode, bool excl) 79 { 80 struct efivarfs_fs_info *info = dir->i_sb->s_fs_info; 81 struct inode *inode = NULL; 82 struct efivar_entry *var; 83 int namelen, i = 0, err = 0; 84 bool is_removable = false; 85 86 if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) 87 return -EINVAL; 88 89 var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL); 90 if (!var) 91 return -ENOMEM; 92 93 /* length of the variable name itself: remove GUID and separator */ 94 namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1; 95 96 err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid); 97 if (err) 98 goto out; 99 if (guid_equal(&var->var.VendorGuid, &LINUX_EFI_RANDOM_SEED_TABLE_GUID)) { 100 err = -EPERM; 101 goto out; 102 } 103 104 if (efivar_variable_is_removable(var->var.VendorGuid, 105 dentry->d_name.name, namelen)) 106 is_removable = true; 107 108 inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable); 109 if (!inode) { 110 err = -ENOMEM; 111 goto out; 112 } 113 114 for (i = 0; i < namelen; i++) 115 var->var.VariableName[i] = dentry->d_name.name[i]; 116 117 var->var.VariableName[i] = '\0'; 118 119 inode->i_private = var; 120 kmemleak_ignore(var); 121 122 err = efivar_entry_add(var, &info->efivarfs_list); 123 if (err) 124 goto out; 125 126 d_instantiate(dentry, inode); 127 dget(dentry); 128 out: 129 if (err) { 130 kfree(var); 131 if (inode) 132 iput(inode); 133 } 134 return err; 135 } 136 137 static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) 138 { 139 struct efivar_entry *var = d_inode(dentry)->i_private; 140 141 if (efivar_entry_delete(var)) 142 return -EINVAL; 143 144 drop_nlink(d_inode(dentry)); 145 dput(dentry); 146 return 0; 147 }; 148 149 const struct inode_operations efivarfs_dir_inode_operations = { 150 .lookup = simple_lookup, 151 .unlink = efivarfs_unlink, 152 .create = efivarfs_create, 153 }; 154 155 static int 156 efivarfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) 157 { 158 unsigned int i_flags; 159 unsigned int flags = 0; 160 161 i_flags = d_inode(dentry)->i_flags; 162 if (i_flags & S_IMMUTABLE) 163 flags |= FS_IMMUTABLE_FL; 164 165 fileattr_fill_flags(fa, flags); 166 167 return 0; 168 } 169 170 static int 171 efivarfs_fileattr_set(struct mnt_idmap *idmap, 172 struct dentry *dentry, struct fileattr *fa) 173 { 174 unsigned int i_flags = 0; 175 176 if (fileattr_has_fsx(fa)) 177 return -EOPNOTSUPP; 178 179 if (fa->flags & ~FS_IMMUTABLE_FL) 180 return -EOPNOTSUPP; 181 182 if (fa->flags & FS_IMMUTABLE_FL) 183 i_flags |= S_IMMUTABLE; 184 185 inode_set_flags(d_inode(dentry), i_flags, S_IMMUTABLE); 186 187 return 0; 188 } 189 190 static const struct inode_operations efivarfs_file_inode_operations = { 191 .fileattr_get = efivarfs_fileattr_get, 192 .fileattr_set = efivarfs_fileattr_set, 193 }; 194
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.