aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/filesystems/efivarfs.txt7
-rw-r--r--drivers/firmware/efi/efivars.c35
-rw-r--r--drivers/firmware/efi/vars.c143
-rw-r--r--fs/efivarfs/file.c70
-rw-r--r--fs/efivarfs/inode.c30
-rw-r--r--fs/efivarfs/internal.h3
-rw-r--r--fs/efivarfs/super.c16
-rw-r--r--include/linux/efi.h5
-rw-r--r--include/linux/ucs2_string.h4
-rw-r--r--lib/ucs2_string.c62
-rwxr-xr-xtools/testing/selftests/efivarfs/efivarfs.sh19
-rw-r--r--tools/testing/selftests/efivarfs/open-unlink.c72
12 files changed, 383 insertions, 83 deletions
diff --git a/Documentation/filesystems/efivarfs.txt b/Documentation/filesystems/efivarfs.txt
index c477af086e65..686a64bba775 100644
--- a/Documentation/filesystems/efivarfs.txt
+++ b/Documentation/filesystems/efivarfs.txt
@@ -14,3 +14,10 @@ filesystem.
14efivarfs is typically mounted like this, 14efivarfs is typically mounted like this,
15 15
16 mount -t efivarfs none /sys/firmware/efi/efivars 16 mount -t efivarfs none /sys/firmware/efi/efivars
17
18Due to the presence of numerous firmware bugs where removing non-standard
19UEFI variables causes the system firmware to fail to POST, efivarfs
20files that are not well-known standardized variables are created
21as immutable files. This doesn't prevent removal - "chattr -i" will work -
22but it does prevent this kind of failure from being accomplished
23accidentally.
diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
index 756eca8c4cf8..10e6774ab2a2 100644
--- a/drivers/firmware/efi/efivars.c
+++ b/drivers/firmware/efi/efivars.c
@@ -221,7 +221,7 @@ sanity_check(struct efi_variable *var, efi_char16_t *name, efi_guid_t vendor,
221 } 221 }
222 222
223 if ((attributes & ~EFI_VARIABLE_MASK) != 0 || 223 if ((attributes & ~EFI_VARIABLE_MASK) != 0 ||
224 efivar_validate(name, data, size) == false) { 224 efivar_validate(vendor, name, data, size) == false) {
225 printk(KERN_ERR "efivars: Malformed variable content\n"); 225 printk(KERN_ERR "efivars: Malformed variable content\n");
226 return -EINVAL; 226 return -EINVAL;
227 } 227 }
@@ -447,7 +447,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
447 } 447 }
448 448
449 if ((attributes & ~EFI_VARIABLE_MASK) != 0 || 449 if ((attributes & ~EFI_VARIABLE_MASK) != 0 ||
450 efivar_validate(name, data, size) == false) { 450 efivar_validate(new_var->VendorGuid, name, data,
451 size) == false) {
451 printk(KERN_ERR "efivars: Malformed variable content\n"); 452 printk(KERN_ERR "efivars: Malformed variable content\n");
452 return -EINVAL; 453 return -EINVAL;
453 } 454 }
@@ -540,38 +541,30 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
540static int 541static int
541efivar_create_sysfs_entry(struct efivar_entry *new_var) 542efivar_create_sysfs_entry(struct efivar_entry *new_var)
542{ 543{
543 int i, short_name_size; 544 int short_name_size;
544 char *short_name; 545 char *short_name;
545 unsigned long variable_name_size; 546 unsigned long utf8_name_size;
546 efi_char16_t *variable_name; 547 efi_char16_t *variable_name = new_var->var.VariableName;
547 int ret; 548 int ret;
548 549
549 variable_name = new_var->var.VariableName;
550 variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t);
551
552 /* 550 /*
553 * Length of the variable bytes in ASCII, plus the '-' separator, 551 * Length of the variable bytes in UTF8, plus the '-' separator,
554 * plus the GUID, plus trailing NUL 552 * plus the GUID, plus trailing NUL
555 */ 553 */
556 short_name_size = variable_name_size / sizeof(efi_char16_t) 554 utf8_name_size = ucs2_utf8size(variable_name);
557 + 1 + EFI_VARIABLE_GUID_LEN + 1; 555 short_name_size = utf8_name_size + 1 + EFI_VARIABLE_GUID_LEN + 1;
558
559 short_name = kzalloc(short_name_size, GFP_KERNEL);
560 556
557 short_name = kmalloc(short_name_size, GFP_KERNEL);
561 if (!short_name) 558 if (!short_name)
562 return -ENOMEM; 559 return -ENOMEM;
563 560
564 /* Convert Unicode to normal chars (assume top bits are 0), 561 ucs2_as_utf8(short_name, variable_name, short_name_size);
565 ala UTF-8 */ 562
566 for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) {
567 short_name[i] = variable_name[i] & 0xFF;
568 }
569 /* This is ugly, but necessary to separate one vendor's 563 /* This is ugly, but necessary to separate one vendor's
570 private variables from another's. */ 564 private variables from another's. */
571 565 short_name[utf8_name_size] = '-';
572 *(short_name + strlen(short_name)) = '-';
573 efi_guid_to_str(&new_var->var.VendorGuid, 566 efi_guid_to_str(&new_var->var.VendorGuid,
574 short_name + strlen(short_name)); 567 short_name + utf8_name_size + 1);
575 568
576 new_var->kobj.kset = efivars_kset; 569 new_var->kobj.kset = efivars_kset;
577 570
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index 70a0fb10517f..50f10bad2604 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -165,67 +165,132 @@ validate_ascii_string(efi_char16_t *var_name, int match, u8 *buffer,
165} 165}
166 166
167struct variable_validate { 167struct variable_validate {
168 efi_guid_t vendor;
168 char *name; 169 char *name;
169 bool (*validate)(efi_char16_t *var_name, int match, u8 *data, 170 bool (*validate)(efi_char16_t *var_name, int match, u8 *data,
170 unsigned long len); 171 unsigned long len);
171}; 172};
172 173
174/*
175 * This is the list of variables we need to validate, as well as the
176 * whitelist for what we think is safe not to default to immutable.
177 *
178 * If it has a validate() method that's not NULL, it'll go into the
179 * validation routine. If not, it is assumed valid, but still used for
180 * whitelisting.
181 *
182 * Note that it's sorted by {vendor,name}, but globbed names must come after
183 * any other name with the same prefix.
184 */
173static const struct variable_validate variable_validate[] = { 185static const struct variable_validate variable_validate[] = {
174 { "BootNext", validate_uint16 }, 186 { EFI_GLOBAL_VARIABLE_GUID, "BootNext", validate_uint16 },
175 { "BootOrder", validate_boot_order }, 187 { EFI_GLOBAL_VARIABLE_GUID, "BootOrder", validate_boot_order },
176 { "DriverOrder", validate_boot_order }, 188 { EFI_GLOBAL_VARIABLE_GUID, "Boot*", validate_load_option },
177 { "Boot*", validate_load_option }, 189 { EFI_GLOBAL_VARIABLE_GUID, "DriverOrder", validate_boot_order },
178 { "Driver*", validate_load_option }, 190 { EFI_GLOBAL_VARIABLE_GUID, "Driver*", validate_load_option },
179 { "ConIn", validate_device_path }, 191 { EFI_GLOBAL_VARIABLE_GUID, "ConIn", validate_device_path },
180 { "ConInDev", validate_device_path }, 192 { EFI_GLOBAL_VARIABLE_GUID, "ConInDev", validate_device_path },
181 { "ConOut", validate_device_path }, 193 { EFI_GLOBAL_VARIABLE_GUID, "ConOut", validate_device_path },
182 { "ConOutDev", validate_device_path }, 194 { EFI_GLOBAL_VARIABLE_GUID, "ConOutDev", validate_device_path },
183 { "ErrOut", validate_device_path }, 195 { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path },
184 { "ErrOutDev", validate_device_path }, 196 { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path },
185 { "Timeout", validate_uint16 }, 197 { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string },
186 { "Lang", validate_ascii_string }, 198 { EFI_GLOBAL_VARIABLE_GUID, "OsIndications", NULL },
187 { "PlatformLang", validate_ascii_string }, 199 { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string },
188 { "", NULL }, 200 { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 },
201 { NULL_GUID, "", NULL },
189}; 202};
190 203
204static bool
205variable_matches(const char *var_name, size_t len, const char *match_name,
206 int *match)
207{
208 for (*match = 0; ; (*match)++) {
209 char c = match_name[*match];
210 char u = var_name[*match];
211
212 /* Wildcard in the matching name means we've matched */
213 if (c == '*')
214 return true;
215
216 /* Case sensitive match */
217 if (!c && *match == len)
218 return true;
219
220 if (c != u)
221 return false;
222
223 if (!c)
224 return true;
225 }
226 return true;
227}
228
191bool 229bool
192efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long len) 230efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
231 unsigned long data_size)
193{ 232{
194 int i; 233 int i;
195 u16 *unicode_name = var_name; 234 unsigned long utf8_size;
235 u8 *utf8_name;
196 236
197 for (i = 0; variable_validate[i].validate != NULL; i++) { 237 utf8_size = ucs2_utf8size(var_name);
198 const char *name = variable_validate[i].name; 238 utf8_name = kmalloc(utf8_size + 1, GFP_KERNEL);
199 int match; 239 if (!utf8_name)
240 return false;
200 241
201 for (match = 0; ; match++) { 242 ucs2_as_utf8(utf8_name, var_name, utf8_size);
202 char c = name[match]; 243 utf8_name[utf8_size] = '\0';
203 u16 u = unicode_name[match];
204 244
205 /* All special variables are plain ascii */ 245 for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
206 if (u > 127) 246 const char *name = variable_validate[i].name;
207 return true; 247 int match = 0;
208 248
209 /* Wildcard in the matching name means we've matched */ 249 if (efi_guidcmp(vendor, variable_validate[i].vendor))
210 if (c == '*') 250 continue;
211 return variable_validate[i].validate(var_name,
212 match, data, len);
213 251
214 /* Case sensitive match */ 252 if (variable_matches(utf8_name, utf8_size+1, name, &match)) {
215 if (c != u) 253 if (variable_validate[i].validate == NULL)
216 break; 254 break;
217 255 kfree(utf8_name);
218 /* Reached the end of the string while matching */ 256 return variable_validate[i].validate(var_name, match,
219 if (!c) 257 data, data_size);
220 return variable_validate[i].validate(var_name,
221 match, data, len);
222 } 258 }
223 } 259 }
224 260 kfree(utf8_name);
225 return true; 261 return true;
226} 262}
227EXPORT_SYMBOL_GPL(efivar_validate); 263EXPORT_SYMBOL_GPL(efivar_validate);
228 264
265bool
266efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
267 size_t len)
268{
269 int i;
270 bool found = false;
271 int match = 0;
272
273 /*
274 * Check if our variable is in the validated variables list
275 */
276 for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
277 if (efi_guidcmp(variable_validate[i].vendor, vendor))
278 continue;
279
280 if (variable_matches(var_name, len,
281 variable_validate[i].name, &match)) {
282 found = true;
283 break;
284 }
285 }
286
287 /*
288 * If it's in our list, it is removable.
289 */
290 return found;
291}
292EXPORT_SYMBOL_GPL(efivar_variable_is_removable);
293
229static efi_status_t 294static efi_status_t
230check_var_size(u32 attributes, unsigned long size) 295check_var_size(u32 attributes, unsigned long size)
231{ 296{
@@ -852,7 +917,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
852 917
853 *set = false; 918 *set = false;
854 919
855 if (efivar_validate(name, data, *size) == false) 920 if (efivar_validate(*vendor, name, data, *size) == false)
856 return -EINVAL; 921 return -EINVAL;
857 922
858 /* 923 /*
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index c424e4813ec8..d48e0d261d78 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -10,6 +10,7 @@
10#include <linux/efi.h> 10#include <linux/efi.h>
11#include <linux/fs.h> 11#include <linux/fs.h>
12#include <linux/slab.h> 12#include <linux/slab.h>
13#include <linux/mount.h>
13 14
14#include "internal.h" 15#include "internal.h"
15 16
@@ -103,9 +104,78 @@ out_free:
103 return size; 104 return size;
104} 105}
105 106
107static int
108efivarfs_ioc_getxflags(struct file *file, void __user *arg)
109{
110 struct inode *inode = file->f_mapping->host;
111 unsigned int i_flags;
112 unsigned int flags = 0;
113
114 i_flags = inode->i_flags;
115 if (i_flags & S_IMMUTABLE)
116 flags |= FS_IMMUTABLE_FL;
117
118 if (copy_to_user(arg, &flags, sizeof(flags)))
119 return -EFAULT;
120 return 0;
121}
122
123static int
124efivarfs_ioc_setxflags(struct file *file, void __user *arg)
125{
126 struct inode *inode = file->f_mapping->host;
127 unsigned int flags;
128 unsigned int i_flags = 0;
129 int error;
130
131 if (!inode_owner_or_capable(inode))
132 return -EACCES;
133
134 if (copy_from_user(&flags, arg, sizeof(flags)))
135 return -EFAULT;
136
137 if (flags & ~FS_IMMUTABLE_FL)
138 return -EOPNOTSUPP;
139
140 if (!capable(CAP_LINUX_IMMUTABLE))
141 return -EPERM;
142
143 if (flags & FS_IMMUTABLE_FL)
144 i_flags |= S_IMMUTABLE;
145
146
147 error = mnt_want_write_file(file);
148 if (error)
149 return error;
150
151 inode_lock(inode);
152 inode_set_flags(inode, i_flags, S_IMMUTABLE);
153 inode_unlock(inode);
154
155 mnt_drop_write_file(file);
156
157 return 0;
158}
159
160long
161efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p)
162{
163 void __user *arg = (void __user *)p;
164
165 switch (cmd) {
166 case FS_IOC_GETFLAGS:
167 return efivarfs_ioc_getxflags(file, arg);
168 case FS_IOC_SETFLAGS:
169 return efivarfs_ioc_setxflags(file, arg);
170 }
171
172 return -ENOTTY;
173}
174
106const struct file_operations efivarfs_file_operations = { 175const struct file_operations efivarfs_file_operations = {
107 .open = simple_open, 176 .open = simple_open,
108 .read = efivarfs_file_read, 177 .read = efivarfs_file_read,
109 .write = efivarfs_file_write, 178 .write = efivarfs_file_write,
110 .llseek = no_llseek, 179 .llseek = no_llseek,
180 .unlocked_ioctl = efivarfs_file_ioctl,
111}; 181};
diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c
index 3381b9da9ee6..e2ab6d0497f2 100644
--- a/fs/efivarfs/inode.c
+++ b/fs/efivarfs/inode.c
@@ -15,7 +15,8 @@
15#include "internal.h" 15#include "internal.h"
16 16
17struct inode *efivarfs_get_inode(struct super_block *sb, 17struct inode *efivarfs_get_inode(struct super_block *sb,
18 const struct inode *dir, int mode, dev_t dev) 18 const struct inode *dir, int mode,
19 dev_t dev, bool is_removable)
19{ 20{
20 struct inode *inode = new_inode(sb); 21 struct inode *inode = new_inode(sb);
21 22
@@ -23,6 +24,7 @@ struct inode *efivarfs_get_inode(struct super_block *sb,
23 inode->i_ino = get_next_ino(); 24 inode->i_ino = get_next_ino();
24 inode->i_mode = mode; 25 inode->i_mode = mode;
25 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; 26 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
27 inode->i_flags = is_removable ? 0 : S_IMMUTABLE;
26 switch (mode & S_IFMT) { 28 switch (mode & S_IFMT) {
27 case S_IFREG: 29 case S_IFREG:
28 inode->i_fop = &efivarfs_file_operations; 30 inode->i_fop = &efivarfs_file_operations;
@@ -102,22 +104,17 @@ static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid)
102static int efivarfs_create(struct inode *dir, struct dentry *dentry, 104static int efivarfs_create(struct inode *dir, struct dentry *dentry,
103 umode_t mode, bool excl) 105 umode_t mode, bool excl)
104{ 106{
105 struct inode *inode; 107 struct inode *inode = NULL;
106 struct efivar_entry *var; 108 struct efivar_entry *var;
107 int namelen, i = 0, err = 0; 109 int namelen, i = 0, err = 0;
110 bool is_removable = false;
108 111
109 if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) 112 if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
110 return -EINVAL; 113 return -EINVAL;
111 114
112 inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
113 if (!inode)
114 return -ENOMEM;
115
116 var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL); 115 var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
117 if (!var) { 116 if (!var)
118 err = -ENOMEM; 117 return -ENOMEM;
119 goto out;
120 }
121 118
122 /* length of the variable name itself: remove GUID and separator */ 119 /* length of the variable name itself: remove GUID and separator */
123 namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1; 120 namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1;
@@ -125,6 +122,16 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
125 efivarfs_hex_to_guid(dentry->d_name.name + namelen + 1, 122 efivarfs_hex_to_guid(dentry->d_name.name + namelen + 1,
126 &var->var.VendorGuid); 123 &var->var.VendorGuid);
127 124
125 if (efivar_variable_is_removable(var->var.VendorGuid,
126 dentry->d_name.name, namelen))
127 is_removable = true;
128
129 inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable);
130 if (!inode) {
131 err = -ENOMEM;
132 goto out;
133 }
134
128 for (i = 0; i < namelen; i++) 135 for (i = 0; i < namelen; i++)
129 var->var.VariableName[i] = dentry->d_name.name[i]; 136 var->var.VariableName[i] = dentry->d_name.name[i];
130 137
@@ -138,7 +145,8 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
138out: 145out:
139 if (err) { 146 if (err) {
140 kfree(var); 147 kfree(var);
141 iput(inode); 148 if (inode)
149 iput(inode);
142 } 150 }
143 return err; 151 return err;
144} 152}
diff --git a/fs/efivarfs/internal.h b/fs/efivarfs/internal.h
index b5ff16addb7c..b4505188e799 100644
--- a/fs/efivarfs/internal.h
+++ b/fs/efivarfs/internal.h
@@ -15,7 +15,8 @@ extern const struct file_operations efivarfs_file_operations;
15extern const struct inode_operations efivarfs_dir_inode_operations; 15extern const struct inode_operations efivarfs_dir_inode_operations;
16extern bool efivarfs_valid_name(const char *str, int len); 16extern bool efivarfs_valid_name(const char *str, int len);
17extern struct inode *efivarfs_get_inode(struct super_block *sb, 17extern struct inode *efivarfs_get_inode(struct super_block *sb,
18 const struct inode *dir, int mode, dev_t dev); 18 const struct inode *dir, int mode, dev_t dev,
19 bool is_removable);
19 20
20extern struct list_head efivarfs_list; 21extern struct list_head efivarfs_list;
21 22
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index b8a564f29107..dd029d13ea61 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -118,8 +118,9 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
118 struct dentry *dentry, *root = sb->s_root; 118 struct dentry *dentry, *root = sb->s_root;
119 unsigned long size = 0; 119 unsigned long size = 0;
120 char *name; 120 char *name;
121 int len, i; 121 int len;
122 int err = -ENOMEM; 122 int err = -ENOMEM;
123 bool is_removable = false;
123 124
124 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 125 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
125 if (!entry) 126 if (!entry)
@@ -128,15 +129,17 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
128 memcpy(entry->var.VariableName, name16, name_size); 129 memcpy(entry->var.VariableName, name16, name_size);
129 memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); 130 memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
130 131
131 len = ucs2_strlen(entry->var.VariableName); 132 len = ucs2_utf8size(entry->var.VariableName);
132 133
133 /* name, plus '-', plus GUID, plus NUL*/ 134 /* name, plus '-', plus GUID, plus NUL*/
134 name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL); 135 name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
135 if (!name) 136 if (!name)
136 goto fail; 137 goto fail;
137 138
138 for (i = 0; i < len; i++) 139 ucs2_as_utf8(name, entry->var.VariableName, len);
139 name[i] = entry->var.VariableName[i] & 0xFF; 140
141 if (efivar_variable_is_removable(entry->var.VendorGuid, name, len))
142 is_removable = true;
140 143
141 name[len] = '-'; 144 name[len] = '-';
142 145
@@ -144,7 +147,8 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
144 147
145 name[len + EFI_VARIABLE_GUID_LEN+1] = '\0'; 148 name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
146 149
147 inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0); 150 inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0,
151 is_removable);
148 if (!inode) 152 if (!inode)
149 goto fail_name; 153 goto fail_name;
150 154
@@ -200,7 +204,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
200 sb->s_d_op = &efivarfs_d_ops; 204 sb->s_d_op = &efivarfs_d_ops;
201 sb->s_time_gran = 1; 205 sb->s_time_gran = 1;
202 206
203 inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); 207 inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0, true);
204 if (!inode) 208 if (!inode)
205 return -ENOMEM; 209 return -ENOMEM;
206 inode->i_op = &efivarfs_dir_inode_operations; 210 inode->i_op = &efivarfs_dir_inode_operations;
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 569b5a866bb1..47be3ad7d3e5 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1199,7 +1199,10 @@ int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
1199struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid, 1199struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
1200 struct list_head *head, bool remove); 1200 struct list_head *head, bool remove);
1201 1201
1202bool efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long len); 1202bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
1203 unsigned long data_size);
1204bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
1205 size_t len);
1203 1206
1204extern struct work_struct efivar_work; 1207extern struct work_struct efivar_work;
1205void efivar_run_worker(void); 1208void efivar_run_worker(void);
diff --git a/include/linux/ucs2_string.h b/include/linux/ucs2_string.h
index cbb20afdbc01..bb679b48f408 100644
--- a/include/linux/ucs2_string.h
+++ b/include/linux/ucs2_string.h
@@ -11,4 +11,8 @@ unsigned long ucs2_strlen(const ucs2_char_t *s);
11unsigned long ucs2_strsize(const ucs2_char_t *data, unsigned long maxlength); 11unsigned long ucs2_strsize(const ucs2_char_t *data, unsigned long maxlength);
12int ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len); 12int ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len);
13 13
14unsigned long ucs2_utf8size(const ucs2_char_t *src);
15unsigned long ucs2_as_utf8(u8 *dest, const ucs2_char_t *src,
16 unsigned long maxlength);
17
14#endif /* _LINUX_UCS2_STRING_H_ */ 18#endif /* _LINUX_UCS2_STRING_H_ */
diff --git a/lib/ucs2_string.c b/lib/ucs2_string.c
index 6f500ef2301d..17dd74e21ef9 100644
--- a/lib/ucs2_string.c
+++ b/lib/ucs2_string.c
@@ -49,3 +49,65 @@ ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len)
49 } 49 }
50} 50}
51EXPORT_SYMBOL(ucs2_strncmp); 51EXPORT_SYMBOL(ucs2_strncmp);
52
53unsigned long
54ucs2_utf8size(const ucs2_char_t *src)
55{
56 unsigned long i;
57 unsigned long j = 0;
58
59 for (i = 0; i < ucs2_strlen(src); i++) {
60 u16 c = src[i];
61
62 if (c > 0x800)
63 j += 3;
64 else if (c > 0x80)
65 j += 2;
66 else
67 j += 1;
68 }
69
70 return j;
71}
72EXPORT_SYMBOL(ucs2_utf8size);
73
74/*
75 * copy at most maxlength bytes of whole utf8 characters to dest from the
76 * ucs2 string src.
77 *
78 * The return value is the number of characters copied, not including the
79 * final NUL character.
80 */
81unsigned long
82ucs2_as_utf8(u8 *dest, const ucs2_char_t *src, unsigned long maxlength)
83{
84 unsigned int i;
85 unsigned long j = 0;
86 unsigned long limit = ucs2_strnlen(src, maxlength);
87
88 for (i = 0; maxlength && i < limit; i++) {
89 u16 c = src[i];
90
91 if (c > 0x800) {
92 if (maxlength < 3)
93 break;
94 maxlength -= 3;
95 dest[j++] = 0xe0 | (c & 0xf000) >> 12;
96 dest[j++] = 0x80 | (c & 0x0fc0) >> 8;
97 dest[j++] = 0x80 | (c & 0x003f);
98 } else if (c > 0x80) {
99 if (maxlength < 2)
100 break;
101 maxlength -= 2;
102 dest[j++] = 0xc0 | (c & 0xfe0) >> 5;
103 dest[j++] = 0x80 | (c & 0x01f);
104 } else {
105 maxlength -= 1;
106 dest[j++] = c & 0x7f;
107 }
108 }
109 if (maxlength)
110 dest[j] = '\0';
111 return j;
112}
113EXPORT_SYMBOL(ucs2_as_utf8);
diff --git a/tools/testing/selftests/efivarfs/efivarfs.sh b/tools/testing/selftests/efivarfs/efivarfs.sh
index 77edcdcc016b..057278448515 100755
--- a/tools/testing/selftests/efivarfs/efivarfs.sh
+++ b/tools/testing/selftests/efivarfs/efivarfs.sh
@@ -88,7 +88,11 @@ test_delete()
88 exit 1 88 exit 1
89 fi 89 fi
90 90
91 rm $file 91 rm $file 2>/dev/null
92 if [ $? -ne 0 ]; then
93 chattr -i $file
94 rm $file
95 fi
92 96
93 if [ -e $file ]; then 97 if [ -e $file ]; then
94 echo "$file couldn't be deleted" >&2 98 echo "$file couldn't be deleted" >&2
@@ -111,6 +115,7 @@ test_zero_size_delete()
111 exit 1 115 exit 1
112 fi 116 fi
113 117
118 chattr -i $file
114 printf "$attrs" > $file 119 printf "$attrs" > $file
115 120
116 if [ -e $file ]; then 121 if [ -e $file ]; then
@@ -141,7 +146,11 @@ test_valid_filenames()
141 echo "$file could not be created" >&2 146 echo "$file could not be created" >&2
142 ret=1 147 ret=1
143 else 148 else
144 rm $file 149 rm $file 2>/dev/null
150 if [ $? -ne 0 ]; then
151 chattr -i $file
152 rm $file
153 fi
145 fi 154 fi
146 done 155 done
147 156
@@ -174,7 +183,11 @@ test_invalid_filenames()
174 183
175 if [ -e $file ]; then 184 if [ -e $file ]; then
176 echo "Creating $file should have failed" >&2 185 echo "Creating $file should have failed" >&2
177 rm $file 186 rm $file 2>/dev/null
187 if [ $? -ne 0 ]; then
188 chattr -i $file
189 rm $file
190 fi
178 ret=1 191 ret=1
179 fi 192 fi
180 done 193 done
diff --git a/tools/testing/selftests/efivarfs/open-unlink.c b/tools/testing/selftests/efivarfs/open-unlink.c
index 8c0764407b3c..4af74f733036 100644
--- a/tools/testing/selftests/efivarfs/open-unlink.c
+++ b/tools/testing/selftests/efivarfs/open-unlink.c
@@ -1,10 +1,68 @@
1#include <errno.h>
1#include <stdio.h> 2#include <stdio.h>
2#include <stdint.h> 3#include <stdint.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <unistd.h> 5#include <unistd.h>
6#include <sys/ioctl.h>
5#include <sys/types.h> 7#include <sys/types.h>
6#include <sys/stat.h> 8#include <sys/stat.h>
7#include <fcntl.h> 9#include <fcntl.h>
10#include <linux/fs.h>
11
12static int set_immutable(const char *path, int immutable)
13{
14 unsigned int flags;
15 int fd;
16 int rc;
17 int error;
18
19 fd = open(path, O_RDONLY);
20 if (fd < 0)
21 return fd;
22
23 rc = ioctl(fd, FS_IOC_GETFLAGS, &flags);
24 if (rc < 0) {
25 error = errno;
26 close(fd);
27 errno = error;
28 return rc;
29 }
30
31 if (immutable)
32 flags |= FS_IMMUTABLE_FL;
33 else
34 flags &= ~FS_IMMUTABLE_FL;
35
36 rc = ioctl(fd, FS_IOC_SETFLAGS, &flags);
37 error = errno;
38 close(fd);
39 errno = error;
40 return rc;
41}
42
43static int get_immutable(const char *path)
44{
45 unsigned int flags;
46 int fd;
47 int rc;
48 int error;
49
50 fd = open(path, O_RDONLY);
51 if (fd < 0)
52 return fd;
53
54 rc = ioctl(fd, FS_IOC_GETFLAGS, &flags);
55 if (rc < 0) {
56 error = errno;
57 close(fd);
58 errno = error;
59 return rc;
60 }
61 close(fd);
62 if (flags & FS_IMMUTABLE_FL)
63 return 1;
64 return 0;
65}
8 66
9int main(int argc, char **argv) 67int main(int argc, char **argv)
10{ 68{
@@ -27,7 +85,7 @@ int main(int argc, char **argv)
27 buf[4] = 0; 85 buf[4] = 0;
28 86
29 /* create a test variable */ 87 /* create a test variable */
30 fd = open(path, O_WRONLY | O_CREAT); 88 fd = open(path, O_WRONLY | O_CREAT, 0600);
31 if (fd < 0) { 89 if (fd < 0) {
32 perror("open(O_WRONLY)"); 90 perror("open(O_WRONLY)");
33 return EXIT_FAILURE; 91 return EXIT_FAILURE;
@@ -41,6 +99,18 @@ int main(int argc, char **argv)
41 99
42 close(fd); 100 close(fd);
43 101
102 rc = get_immutable(path);
103 if (rc < 0) {
104 perror("ioctl(FS_IOC_GETFLAGS)");
105 return EXIT_FAILURE;
106 } else if (rc) {
107 rc = set_immutable(path, 0);
108 if (rc < 0) {
109 perror("ioctl(FS_IOC_SETFLAGS)");
110 return EXIT_FAILURE;
111 }
112 }
113
44 fd = open(path, O_RDONLY); 114 fd = open(path, O_RDONLY);
45 if (fd < 0) { 115 if (fd < 0) {
46 perror("open"); 116 perror("open");