Commit 3fab70c165795431f00ddf9be8b84ddd07bd1f8f
Committed by
Matt Fleming
1 parent
f722406faa
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
efivarfs: Never return ENOENT from firmware again
Previously in 1fa7e69 efi_status_to_err() translated firmware status EFI_NOT_FOUND to -EIO instead of -ENOENT for efivarfs operations to avoid confusion. After refactoring in e14ab23, it is also used in other places where the translation may be unnecessary. So move the translation to efivarfs specific code. Also return EOF for reading zero-length files, which is what users would expect. Cc: Josh Boyer <jwboyer@redhat.com> Cc: Jeremy Kerr <jk@ozlabs.org> Cc: Lee, Chun-Yi <jlee@suse.com> Cc: Andy Whitcroft <apw@canonical.com> Signed-off-by: Lingzhu Xiang <lxiang@redhat.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Showing 1 changed file with 12 additions and 2 deletions Inline Diff
fs/efivarfs/file.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2012 Red Hat, Inc. | 2 | * Copyright (C) 2012 Red Hat, Inc. |
3 | * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com> | 3 | * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com> |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 7 | * published by the Free Software Foundation. |
8 | */ | 8 | */ |
9 | 9 | ||
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 | 13 | ||
14 | #include "internal.h" | 14 | #include "internal.h" |
15 | 15 | ||
16 | static ssize_t efivarfs_file_write(struct file *file, | 16 | static ssize_t efivarfs_file_write(struct file *file, |
17 | const char __user *userbuf, size_t count, loff_t *ppos) | 17 | const char __user *userbuf, size_t count, loff_t *ppos) |
18 | { | 18 | { |
19 | struct efivar_entry *var = file->private_data; | 19 | struct efivar_entry *var = file->private_data; |
20 | void *data; | 20 | void *data; |
21 | u32 attributes; | 21 | u32 attributes; |
22 | struct inode *inode = file->f_mapping->host; | 22 | struct inode *inode = file->f_mapping->host; |
23 | unsigned long datasize = count - sizeof(attributes); | 23 | unsigned long datasize = count - sizeof(attributes); |
24 | ssize_t bytes = 0; | 24 | ssize_t bytes = 0; |
25 | bool set = false; | 25 | bool set = false; |
26 | 26 | ||
27 | if (count < sizeof(attributes)) | 27 | if (count < sizeof(attributes)) |
28 | return -EINVAL; | 28 | return -EINVAL; |
29 | 29 | ||
30 | if (copy_from_user(&attributes, userbuf, sizeof(attributes))) | 30 | if (copy_from_user(&attributes, userbuf, sizeof(attributes))) |
31 | return -EFAULT; | 31 | return -EFAULT; |
32 | 32 | ||
33 | if (attributes & ~(EFI_VARIABLE_MASK)) | 33 | if (attributes & ~(EFI_VARIABLE_MASK)) |
34 | return -EINVAL; | 34 | return -EINVAL; |
35 | 35 | ||
36 | data = kmalloc(datasize, GFP_KERNEL); | 36 | data = kmalloc(datasize, GFP_KERNEL); |
37 | if (!data) | 37 | if (!data) |
38 | return -ENOMEM; | 38 | return -ENOMEM; |
39 | 39 | ||
40 | if (copy_from_user(data, userbuf + sizeof(attributes), datasize)) { | 40 | if (copy_from_user(data, userbuf + sizeof(attributes), datasize)) { |
41 | bytes = -EFAULT; | 41 | bytes = -EFAULT; |
42 | goto out; | 42 | goto out; |
43 | } | 43 | } |
44 | 44 | ||
45 | bytes = efivar_entry_set_get_size(var, attributes, &datasize, | 45 | bytes = efivar_entry_set_get_size(var, attributes, &datasize, |
46 | data, &set); | 46 | data, &set); |
47 | if (!set && bytes) | 47 | if (!set && bytes) { |
48 | if (bytes == -ENOENT) | ||
49 | bytes = -EIO; | ||
48 | goto out; | 50 | goto out; |
51 | } | ||
49 | 52 | ||
50 | if (bytes == -ENOENT) { | 53 | if (bytes == -ENOENT) { |
51 | drop_nlink(inode); | 54 | drop_nlink(inode); |
52 | d_delete(file->f_dentry); | 55 | d_delete(file->f_dentry); |
53 | dput(file->f_dentry); | 56 | dput(file->f_dentry); |
54 | } else { | 57 | } else { |
55 | mutex_lock(&inode->i_mutex); | 58 | mutex_lock(&inode->i_mutex); |
56 | i_size_write(inode, datasize + sizeof(attributes)); | 59 | i_size_write(inode, datasize + sizeof(attributes)); |
57 | mutex_unlock(&inode->i_mutex); | 60 | mutex_unlock(&inode->i_mutex); |
58 | } | 61 | } |
59 | 62 | ||
60 | bytes = count; | 63 | bytes = count; |
61 | 64 | ||
62 | out: | 65 | out: |
63 | kfree(data); | 66 | kfree(data); |
64 | 67 | ||
65 | return bytes; | 68 | return bytes; |
66 | } | 69 | } |
67 | 70 | ||
68 | static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, | 71 | static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, |
69 | size_t count, loff_t *ppos) | 72 | size_t count, loff_t *ppos) |
70 | { | 73 | { |
71 | struct efivar_entry *var = file->private_data; | 74 | struct efivar_entry *var = file->private_data; |
72 | unsigned long datasize = 0; | 75 | unsigned long datasize = 0; |
73 | u32 attributes; | 76 | u32 attributes; |
74 | void *data; | 77 | void *data; |
75 | ssize_t size = 0; | 78 | ssize_t size = 0; |
76 | int err; | 79 | int err; |
77 | 80 | ||
78 | err = efivar_entry_size(var, &datasize); | 81 | err = efivar_entry_size(var, &datasize); |
79 | if (err) | 82 | |
83 | /* | ||
84 | * efivarfs represents uncommitted variables with | ||
85 | * zero-length files. Reading them should return EOF. | ||
86 | */ | ||
87 | if (err == -ENOENT) | ||
88 | return 0; | ||
89 | else if (err) | ||
80 | return err; | 90 | return err; |
81 | 91 | ||
82 | data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL); | 92 | data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL); |
83 | 93 | ||
84 | if (!data) | 94 | if (!data) |
85 | return -ENOMEM; | 95 | return -ENOMEM; |
86 | 96 | ||
87 | size = efivar_entry_get(var, &attributes, &datasize, | 97 | size = efivar_entry_get(var, &attributes, &datasize, |
88 | data + sizeof(attributes)); | 98 | data + sizeof(attributes)); |
89 | if (size) | 99 | if (size) |
90 | goto out_free; | 100 | goto out_free; |
91 | 101 | ||
92 | memcpy(data, &attributes, sizeof(attributes)); | 102 | memcpy(data, &attributes, sizeof(attributes)); |
93 | size = simple_read_from_buffer(userbuf, count, ppos, | 103 | size = simple_read_from_buffer(userbuf, count, ppos, |
94 | data, datasize + sizeof(attributes)); | 104 | data, datasize + sizeof(attributes)); |
95 | out_free: | 105 | out_free: |
96 | kfree(data); | 106 | kfree(data); |
97 | 107 | ||
98 | return size; | 108 | return size; |
99 | } | 109 | } |
100 | 110 | ||
101 | const struct file_operations efivarfs_file_operations = { | 111 | const struct file_operations efivarfs_file_operations = { |
102 | .open = simple_open, | 112 | .open = simple_open, |
103 | .read = efivarfs_file_read, | 113 | .read = efivarfs_file_read, |
104 | .write = efivarfs_file_write, | 114 | .write = efivarfs_file_write, |
105 | .llseek = no_llseek, | 115 | .llseek = no_llseek, |
106 | }; | 116 | }; |
107 | 117 |