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 inode *inode = NULL; 81 struct efivar_entry *var; 82 int namelen, i = 0, err = 0; 83 bool is_removable = false; 84 85 if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) 86 return -EINVAL; 87 88 var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL); 89 if (!var) 90 return -ENOMEM; 91 92 /* length of the variable name itself: remove GUID and separator */ 93 namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1; 94 95 err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid); 96 if (err) 97 goto out; 98 if (guid_equal(&var->var.VendorGuid, &LINUX_EFI_RANDOM_SEED_TABLE_GUID)) { 99 err = -EPERM; 100 goto out; 101 } 102 103 if (efivar_variable_is_removable(var->var.VendorGuid, 104 dentry->d_name.name, namelen)) 105 is_removable = true; 106 107 inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable); 108 if (!inode) { 109 err = -ENOMEM; 110 goto out; 111 } 112 113 for (i = 0; i < namelen; i++) 114 var->var.VariableName[i] = dentry->d_name.name[i]; 115 116 var->var.VariableName[i] = '\0'; 117 118 inode->i_private = var; 119 kmemleak_ignore(var); 120 121 err = efivar_entry_add(var, &efivarfs_list); 122 if (err) 123 goto out; 124 125 d_instantiate(dentry, inode); 126 dget(dentry); 127 out: 128 if (err) { 129 kfree(var); 130 if (inode) 131 iput(inode); 132 } 133 return err; 134 } 135 136 static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) 137 { 138 struct efivar_entry *var = d_inode(dentry)->i_private; 139 140 if (efivar_entry_delete(var)) 141 return -EINVAL; 142 143 drop_nlink(d_inode(dentry)); 144 dput(dentry); 145 return 0; 146 }; 147 148 const struct inode_operations efivarfs_dir_inode_operations = { 149 .lookup = simple_lookup, 150 .unlink = efivarfs_unlink, 151 .create = efivarfs_create, 152 }; 153 154 static int 155 efivarfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) 156 { 157 unsigned int i_flags; 158 unsigned int flags = 0; 159 160 i_flags = d_inode(dentry)->i_flags; 161 if (i_flags & S_IMMUTABLE) 162 flags |= FS_IMMUTABLE_FL; 163 164 fileattr_fill_flags(fa, flags); 165 166 return 0; 167 } 168 169 static int 170 efivarfs_fileattr_set(struct mnt_idmap *idmap, 171 struct dentry *dentry, struct fileattr *fa) 172 { 173 unsigned int i_flags = 0; 174 175 if (fileattr_has_fsx(fa)) 176 return -EOPNOTSUPP; 177 178 if (fa->flags & ~FS_IMMUTABLE_FL) 179 return -EOPNOTSUPP; 180 181 if (fa->flags & FS_IMMUTABLE_FL) 182 i_flags |= S_IMMUTABLE; 183 184 inode_set_flags(d_inode(dentry), i_flags, S_IMMUTABLE); 185 186 return 0; 187 } 188 189 static const struct inode_operations efivarfs_file_inode_operations = { 190 .fileattr_get = efivarfs_fileattr_get, 191 .fileattr_set = efivarfs_fileattr_set, 192 }; 193