aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-02-20 12:32:40 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-02-20 12:32:40 -0500
commit0389075ecfb6231818de9b0225d3a5a21a661171 (patch)
tree991154188efee4117974d9aa5bcd7497257c7656
parent06b74c658c595e4ef9fd5746f927a1cb6c3a59d5 (diff)
parentf4eafd8bcd5229e998aa252627703b8462c3b90f (diff)
Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 fixes from Ingo Molnar: "This is unusually large, partly due to the EFI fixes that prevent accidental deletion of EFI variables through efivarfs that may brick machines. These fixes are somewhat involved to maintain compatibility with existing install methods and other usage modes, while trying to turn off the 'rm -rf' bricking vector. Other fixes are for large page ioremap()s and for non-temporal user-memcpy()s" * 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/mm: Fix vmalloc_fault() to handle large pages properly hpet: Drop stale URLs x86/uaccess/64: Handle the caching of 4-byte nocache copies properly in __copy_user_nocache() x86/uaccess/64: Make the __copy_user_nocache() assembly code more readable lib/ucs2_string: Correct ucs2 -> utf8 conversion efi: Add pstore variables to the deletion whitelist efi: Make efivarfs entries immutable by default efi: Make our variable validation list include the guid efi: Do variable name validation tests in utf8 efi: Use ucs2_as_utf8 in efivarfs instead of open coding a bad version lib/ucs2_string: Add ucs2 -> utf8 helper functions
-rw-r--r--Documentation/filesystems/efivarfs.txt7
-rw-r--r--Documentation/timers/hpet.txt4
-rw-r--r--arch/x86/Kconfig4
-rw-r--r--arch/x86/lib/copy_user_64.S142
-rw-r--r--arch/x86/mm/fault.c15
-rw-r--r--drivers/char/hpet.c2
-rw-r--r--drivers/firmware/efi/efivars.c35
-rw-r--r--drivers/firmware/efi/vars.c144
-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
17 files changed, 500 insertions, 134 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/Documentation/timers/hpet.txt b/Documentation/timers/hpet.txt
index 767392ffd31e..a484d2c109d7 100644
--- a/Documentation/timers/hpet.txt
+++ b/Documentation/timers/hpet.txt
@@ -1,9 +1,7 @@
1 High Precision Event Timer Driver for Linux 1 High Precision Event Timer Driver for Linux
2 2
3The High Precision Event Timer (HPET) hardware follows a specification 3The High Precision Event Timer (HPET) hardware follows a specification
4by Intel and Microsoft which can be found at 4by Intel and Microsoft, revision 1.
5
6 http://www.intel.com/hardwaredesign/hpetspec_1.pdf
7 5
8Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision") 6Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision")
9and up to 32 comparators. Normally three or more comparators are provided, 7and up to 32 comparators. Normally three or more comparators are provided,
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index ab2ed5328f0a..c46662f64c39 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -778,8 +778,8 @@ config HPET_TIMER
778 HPET is the next generation timer replacing legacy 8254s. 778 HPET is the next generation timer replacing legacy 8254s.
779 The HPET provides a stable time base on SMP 779 The HPET provides a stable time base on SMP
780 systems, unlike the TSC, but it is more expensive to access, 780 systems, unlike the TSC, but it is more expensive to access,
781 as it is off-chip. You can find the HPET spec at 781 as it is off-chip. The interface used is documented
782 <http://www.intel.com/hardwaredesign/hpetspec_1.pdf>. 782 in the HPET spec, revision 1.
783 783
784 You can safely choose Y here. However, HPET will only be 784 You can safely choose Y here. However, HPET will only be
785 activated if the platform and the BIOS support this feature. 785 activated if the platform and the BIOS support this feature.
diff --git a/arch/x86/lib/copy_user_64.S b/arch/x86/lib/copy_user_64.S
index 982ce34f4a9b..27f89c79a44b 100644
--- a/arch/x86/lib/copy_user_64.S
+++ b/arch/x86/lib/copy_user_64.S
@@ -232,17 +232,31 @@ ENDPROC(copy_user_enhanced_fast_string)
232 232
233/* 233/*
234 * copy_user_nocache - Uncached memory copy with exception handling 234 * copy_user_nocache - Uncached memory copy with exception handling
235 * This will force destination/source out of cache for more performance. 235 * This will force destination out of cache for more performance.
236 *
237 * Note: Cached memory copy is used when destination or size is not
238 * naturally aligned. That is:
239 * - Require 8-byte alignment when size is 8 bytes or larger.
240 * - Require 4-byte alignment when size is 4 bytes.
236 */ 241 */
237ENTRY(__copy_user_nocache) 242ENTRY(__copy_user_nocache)
238 ASM_STAC 243 ASM_STAC
244
245 /* If size is less than 8 bytes, go to 4-byte copy */
239 cmpl $8,%edx 246 cmpl $8,%edx
240 jb 20f /* less then 8 bytes, go to byte copy loop */ 247 jb .L_4b_nocache_copy_entry
248
249 /* If destination is not 8-byte aligned, "cache" copy to align it */
241 ALIGN_DESTINATION 250 ALIGN_DESTINATION
251
252 /* Set 4x8-byte copy count and remainder */
242 movl %edx,%ecx 253 movl %edx,%ecx
243 andl $63,%edx 254 andl $63,%edx
244 shrl $6,%ecx 255 shrl $6,%ecx
245 jz 17f 256 jz .L_8b_nocache_copy_entry /* jump if count is 0 */
257
258 /* Perform 4x8-byte nocache loop-copy */
259.L_4x8b_nocache_copy_loop:
2461: movq (%rsi),%r8 2601: movq (%rsi),%r8
2472: movq 1*8(%rsi),%r9 2612: movq 1*8(%rsi),%r9
2483: movq 2*8(%rsi),%r10 2623: movq 2*8(%rsi),%r10
@@ -262,60 +276,106 @@ ENTRY(__copy_user_nocache)
262 leaq 64(%rsi),%rsi 276 leaq 64(%rsi),%rsi
263 leaq 64(%rdi),%rdi 277 leaq 64(%rdi),%rdi
264 decl %ecx 278 decl %ecx
265 jnz 1b 279 jnz .L_4x8b_nocache_copy_loop
26617: movl %edx,%ecx 280
281 /* Set 8-byte copy count and remainder */
282.L_8b_nocache_copy_entry:
283 movl %edx,%ecx
267 andl $7,%edx 284 andl $7,%edx
268 shrl $3,%ecx 285 shrl $3,%ecx
269 jz 20f 286 jz .L_4b_nocache_copy_entry /* jump if count is 0 */
27018: movq (%rsi),%r8 287
27119: movnti %r8,(%rdi) 288 /* Perform 8-byte nocache loop-copy */
289.L_8b_nocache_copy_loop:
29020: movq (%rsi),%r8
29121: movnti %r8,(%rdi)
272 leaq 8(%rsi),%rsi 292 leaq 8(%rsi),%rsi
273 leaq 8(%rdi),%rdi 293 leaq 8(%rdi),%rdi
274 decl %ecx 294 decl %ecx
275 jnz 18b 295 jnz .L_8b_nocache_copy_loop
27620: andl %edx,%edx 296
277 jz 23f 297 /* If no byte left, we're done */
298.L_4b_nocache_copy_entry:
299 andl %edx,%edx
300 jz .L_finish_copy
301
302 /* If destination is not 4-byte aligned, go to byte copy: */
303 movl %edi,%ecx
304 andl $3,%ecx
305 jnz .L_1b_cache_copy_entry
306
307 /* Set 4-byte copy count (1 or 0) and remainder */
278 movl %edx,%ecx 308 movl %edx,%ecx
27921: movb (%rsi),%al 309 andl $3,%edx
28022: movb %al,(%rdi) 310 shrl $2,%ecx
311 jz .L_1b_cache_copy_entry /* jump if count is 0 */
312
313 /* Perform 4-byte nocache copy: */
31430: movl (%rsi),%r8d
31531: movnti %r8d,(%rdi)
316 leaq 4(%rsi),%rsi
317 leaq 4(%rdi),%rdi
318
319 /* If no bytes left, we're done: */
320 andl %edx,%edx
321 jz .L_finish_copy
322
323 /* Perform byte "cache" loop-copy for the remainder */
324.L_1b_cache_copy_entry:
325 movl %edx,%ecx
326.L_1b_cache_copy_loop:
32740: movb (%rsi),%al
32841: movb %al,(%rdi)
281 incq %rsi 329 incq %rsi
282 incq %rdi 330 incq %rdi
283 decl %ecx 331 decl %ecx
284 jnz 21b 332 jnz .L_1b_cache_copy_loop
28523: xorl %eax,%eax 333
334 /* Finished copying; fence the prior stores */
335.L_finish_copy:
336 xorl %eax,%eax
286 ASM_CLAC 337 ASM_CLAC
287 sfence 338 sfence
288 ret 339 ret
289 340
290 .section .fixup,"ax" 341 .section .fixup,"ax"
29130: shll $6,%ecx 342.L_fixup_4x8b_copy:
343 shll $6,%ecx
292 addl %ecx,%edx 344 addl %ecx,%edx
293 jmp 60f 345 jmp .L_fixup_handle_tail
29440: lea (%rdx,%rcx,8),%rdx 346.L_fixup_8b_copy:
295 jmp 60f 347 lea (%rdx,%rcx,8),%rdx
29650: movl %ecx,%edx 348 jmp .L_fixup_handle_tail
29760: sfence 349.L_fixup_4b_copy:
350 lea (%rdx,%rcx,4),%rdx
351 jmp .L_fixup_handle_tail
352.L_fixup_1b_copy:
353 movl %ecx,%edx
354.L_fixup_handle_tail:
355 sfence
298 jmp copy_user_handle_tail 356 jmp copy_user_handle_tail
299 .previous 357 .previous
300 358
301 _ASM_EXTABLE(1b,30b) 359 _ASM_EXTABLE(1b,.L_fixup_4x8b_copy)
302 _ASM_EXTABLE(2b,30b) 360 _ASM_EXTABLE(2b,.L_fixup_4x8b_copy)
303 _ASM_EXTABLE(3b,30b) 361 _ASM_EXTABLE(3b,.L_fixup_4x8b_copy)
304 _ASM_EXTABLE(4b,30b) 362 _ASM_EXTABLE(4b,.L_fixup_4x8b_copy)
305 _ASM_EXTABLE(5b,30b) 363 _ASM_EXTABLE(5b,.L_fixup_4x8b_copy)
306 _ASM_EXTABLE(6b,30b) 364 _ASM_EXTABLE(6b,.L_fixup_4x8b_copy)
307 _ASM_EXTABLE(7b,30b) 365 _ASM_EXTABLE(7b,.L_fixup_4x8b_copy)
308 _ASM_EXTABLE(8b,30b) 366 _ASM_EXTABLE(8b,.L_fixup_4x8b_copy)
309 _ASM_EXTABLE(9b,30b) 367 _ASM_EXTABLE(9b,.L_fixup_4x8b_copy)
310 _ASM_EXTABLE(10b,30b) 368 _ASM_EXTABLE(10b,.L_fixup_4x8b_copy)
311 _ASM_EXTABLE(11b,30b) 369 _ASM_EXTABLE(11b,.L_fixup_4x8b_copy)
312 _ASM_EXTABLE(12b,30b) 370 _ASM_EXTABLE(12b,.L_fixup_4x8b_copy)
313 _ASM_EXTABLE(13b,30b) 371 _ASM_EXTABLE(13b,.L_fixup_4x8b_copy)
314 _ASM_EXTABLE(14b,30b) 372 _ASM_EXTABLE(14b,.L_fixup_4x8b_copy)
315 _ASM_EXTABLE(15b,30b) 373 _ASM_EXTABLE(15b,.L_fixup_4x8b_copy)
316 _ASM_EXTABLE(16b,30b) 374 _ASM_EXTABLE(16b,.L_fixup_4x8b_copy)
317 _ASM_EXTABLE(18b,40b) 375 _ASM_EXTABLE(20b,.L_fixup_8b_copy)
318 _ASM_EXTABLE(19b,40b) 376 _ASM_EXTABLE(21b,.L_fixup_8b_copy)
319 _ASM_EXTABLE(21b,50b) 377 _ASM_EXTABLE(30b,.L_fixup_4b_copy)
320 _ASM_EXTABLE(22b,50b) 378 _ASM_EXTABLE(31b,.L_fixup_4b_copy)
379 _ASM_EXTABLE(40b,.L_fixup_1b_copy)
380 _ASM_EXTABLE(41b,.L_fixup_1b_copy)
321ENDPROC(__copy_user_nocache) 381ENDPROC(__copy_user_nocache)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index eef44d9a3f77..e830c71a1323 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -287,6 +287,9 @@ static noinline int vmalloc_fault(unsigned long address)
287 if (!pmd_k) 287 if (!pmd_k)
288 return -1; 288 return -1;
289 289
290 if (pmd_huge(*pmd_k))
291 return 0;
292
290 pte_k = pte_offset_kernel(pmd_k, address); 293 pte_k = pte_offset_kernel(pmd_k, address);
291 if (!pte_present(*pte_k)) 294 if (!pte_present(*pte_k))
292 return -1; 295 return -1;
@@ -360,8 +363,6 @@ void vmalloc_sync_all(void)
360 * 64-bit: 363 * 64-bit:
361 * 364 *
362 * Handle a fault on the vmalloc area 365 * Handle a fault on the vmalloc area
363 *
364 * This assumes no large pages in there.
365 */ 366 */
366static noinline int vmalloc_fault(unsigned long address) 367static noinline int vmalloc_fault(unsigned long address)
367{ 368{
@@ -403,17 +404,23 @@ static noinline int vmalloc_fault(unsigned long address)
403 if (pud_none(*pud_ref)) 404 if (pud_none(*pud_ref))
404 return -1; 405 return -1;
405 406
406 if (pud_none(*pud) || pud_page_vaddr(*pud) != pud_page_vaddr(*pud_ref)) 407 if (pud_none(*pud) || pud_pfn(*pud) != pud_pfn(*pud_ref))
407 BUG(); 408 BUG();
408 409
410 if (pud_huge(*pud))
411 return 0;
412
409 pmd = pmd_offset(pud, address); 413 pmd = pmd_offset(pud, address);
410 pmd_ref = pmd_offset(pud_ref, address); 414 pmd_ref = pmd_offset(pud_ref, address);
411 if (pmd_none(*pmd_ref)) 415 if (pmd_none(*pmd_ref))
412 return -1; 416 return -1;
413 417
414 if (pmd_none(*pmd) || pmd_page(*pmd) != pmd_page(*pmd_ref)) 418 if (pmd_none(*pmd) || pmd_pfn(*pmd) != pmd_pfn(*pmd_ref))
415 BUG(); 419 BUG();
416 420
421 if (pmd_huge(*pmd))
422 return 0;
423
417 pte_ref = pte_offset_kernel(pmd_ref, address); 424 pte_ref = pte_offset_kernel(pmd_ref, address);
418 if (!pte_present(*pte_ref)) 425 if (!pte_present(*pte_ref))
419 return -1; 426 return -1;
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 240b6cf1d97c..be54e5331a45 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -42,7 +42,7 @@
42/* 42/*
43 * The High Precision Event Timer driver. 43 * The High Precision Event Timer driver.
44 * This driver is closely modelled after the rtc.c driver. 44 * This driver is closely modelled after the rtc.c driver.
45 * http://www.intel.com/hardwaredesign/hpetspec_1.pdf 45 * See HPET spec revision 1.
46 */ 46 */
47#define HPET_USER_FREQ (64) 47#define HPET_USER_FREQ (64)
48#define HPET_DRIFT (500) 48#define HPET_DRIFT (500)
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..7f2ea21c730d 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -165,67 +165,133 @@ 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 { LINUX_EFI_CRASH_GUID, "*", NULL },
202 { NULL_GUID, "", NULL },
189}; 203};
190 204
205static bool
206variable_matches(const char *var_name, size_t len, const char *match_name,
207 int *match)
208{
209 for (*match = 0; ; (*match)++) {
210 char c = match_name[*match];
211 char u = var_name[*match];
212
213 /* Wildcard in the matching name means we've matched */
214 if (c == '*')
215 return true;
216
217 /* Case sensitive match */
218 if (!c && *match == len)
219 return true;
220
221 if (c != u)
222 return false;
223
224 if (!c)
225 return true;
226 }
227 return true;
228}
229
191bool 230bool
192efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long len) 231efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
232 unsigned long data_size)
193{ 233{
194 int i; 234 int i;
195 u16 *unicode_name = var_name; 235 unsigned long utf8_size;
236 u8 *utf8_name;
196 237
197 for (i = 0; variable_validate[i].validate != NULL; i++) { 238 utf8_size = ucs2_utf8size(var_name);
198 const char *name = variable_validate[i].name; 239 utf8_name = kmalloc(utf8_size + 1, GFP_KERNEL);
199 int match; 240 if (!utf8_name)
241 return false;
200 242
201 for (match = 0; ; match++) { 243 ucs2_as_utf8(utf8_name, var_name, utf8_size);
202 char c = name[match]; 244 utf8_name[utf8_size] = '\0';
203 u16 u = unicode_name[match];
204 245
205 /* All special variables are plain ascii */ 246 for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
206 if (u > 127) 247 const char *name = variable_validate[i].name;
207 return true; 248 int match = 0;
208 249
209 /* Wildcard in the matching name means we've matched */ 250 if (efi_guidcmp(vendor, variable_validate[i].vendor))
210 if (c == '*') 251 continue;
211 return variable_validate[i].validate(var_name,
212 match, data, len);
213 252
214 /* Case sensitive match */ 253 if (variable_matches(utf8_name, utf8_size+1, name, &match)) {
215 if (c != u) 254 if (variable_validate[i].validate == NULL)
216 break; 255 break;
217 256 kfree(utf8_name);
218 /* Reached the end of the string while matching */ 257 return variable_validate[i].validate(var_name, match,
219 if (!c) 258 data, data_size);
220 return variable_validate[i].validate(var_name,
221 match, data, len);
222 } 259 }
223 } 260 }
224 261 kfree(utf8_name);
225 return true; 262 return true;
226} 263}
227EXPORT_SYMBOL_GPL(efivar_validate); 264EXPORT_SYMBOL_GPL(efivar_validate);
228 265
266bool
267efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
268 size_t len)
269{
270 int i;
271 bool found = false;
272 int match = 0;
273
274 /*
275 * Check if our variable is in the validated variables list
276 */
277 for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
278 if (efi_guidcmp(variable_validate[i].vendor, vendor))
279 continue;
280
281 if (variable_matches(var_name, len,
282 variable_validate[i].name, &match)) {
283 found = true;
284 break;
285 }
286 }
287
288 /*
289 * If it's in our list, it is removable.
290 */
291 return found;
292}
293EXPORT_SYMBOL_GPL(efivar_variable_is_removable);
294
229static efi_status_t 295static efi_status_t
230check_var_size(u32 attributes, unsigned long size) 296check_var_size(u32 attributes, unsigned long size)
231{ 297{
@@ -852,7 +918,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
852 918
853 *set = false; 919 *set = false;
854 920
855 if (efivar_validate(name, data, *size) == false) 921 if (efivar_validate(*vendor, name, data, *size) == false)
856 return -EINVAL; 922 return -EINVAL;
857 923
858 /* 924 /*
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..f0b323abb4c6 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) >> 6;
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 & 0x7c0) >> 6;
103 dest[j++] = 0x80 | (c & 0x03f);
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");