diff options
author | Peter Jones <pjones@redhat.com> | 2016-02-08 14:48:15 -0500 |
---|---|---|
committer | Matt Fleming <matt@codeblueprint.co.uk> | 2016-02-10 11:25:52 -0500 |
commit | ed8b0de5a33d2a2557dce7f9429dca8cb5bc5879 (patch) | |
tree | 1dcd2e49cc432ae312677ec47453ec3abc18f1d2 | |
parent | 8282f5d9c17fe15a9e658c06e3f343efae1a2a2f (diff) |
efi: Make efivarfs entries immutable by default
"rm -rf" is bricking some peoples' laptops because of variables being
used to store non-reinitializable firmware driver data that's required
to POST the hardware.
These are 100% bugs, and they need to be fixed, but in the mean time it
shouldn't be easy to *accidentally* brick machines.
We have to have delete working, and picking which variables do and don't
work for deletion is quite intractable, so instead make everything
immutable by default (except for a whitelist), and make tools that
aren't quite so broad-spectrum unset the immutable flag.
Signed-off-by: Peter Jones <pjones@redhat.com>
Tested-by: Lee, Chun-Yi <jlee@suse.com>
Acked-by: Matthew Garrett <mjg59@coreos.com>
Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
-rw-r--r-- | Documentation/filesystems/efivarfs.txt | 7 | ||||
-rw-r--r-- | drivers/firmware/efi/vars.c | 87 | ||||
-rw-r--r-- | fs/efivarfs/file.c | 70 | ||||
-rw-r--r-- | fs/efivarfs/inode.c | 30 | ||||
-rw-r--r-- | fs/efivarfs/internal.h | 3 | ||||
-rw-r--r-- | fs/efivarfs/super.c | 9 | ||||
-rw-r--r-- | include/linux/efi.h | 2 | ||||
-rwxr-xr-x | tools/testing/selftests/efivarfs/efivarfs.sh | 19 | ||||
-rw-r--r-- | tools/testing/selftests/efivarfs/open-unlink.c | 72 |
9 files changed, 258 insertions, 41 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. | |||
14 | efivarfs is typically mounted like this, | 14 | efivarfs 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 | |||
18 | Due to the presence of numerous firmware bugs where removing non-standard | ||
19 | UEFI variables causes the system firmware to fail to POST, efivarfs | ||
20 | files that are not well-known standardized variables are created | ||
21 | as immutable files. This doesn't prevent removal - "chattr -i" will work - | ||
22 | but it does prevent this kind of failure from being accomplished | ||
23 | accidentally. | ||
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 9a53da21e7b6..50f10bad2604 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c | |||
@@ -172,10 +172,12 @@ struct variable_validate { | |||
172 | }; | 172 | }; |
173 | 173 | ||
174 | /* | 174 | /* |
175 | * This is the list of variables we need to validate. | 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. | ||
176 | * | 177 | * |
177 | * If it has a validate() method that's not NULL, it'll go into the | 178 | * If it has a validate() method that's not NULL, it'll go into the |
178 | * validation routine. If not, it is assumed valid. | 179 | * validation routine. If not, it is assumed valid, but still used for |
180 | * whitelisting. | ||
179 | * | 181 | * |
180 | * Note that it's sorted by {vendor,name}, but globbed names must come after | 182 | * Note that it's sorted by {vendor,name}, but globbed names must come after |
181 | * any other name with the same prefix. | 183 | * any other name with the same prefix. |
@@ -193,11 +195,37 @@ static const struct variable_validate variable_validate[] = { | |||
193 | { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path }, | 195 | { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path }, |
194 | { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path }, | 196 | { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path }, |
195 | { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string }, | 197 | { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string }, |
198 | { EFI_GLOBAL_VARIABLE_GUID, "OsIndications", NULL }, | ||
196 | { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string }, | 199 | { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string }, |
197 | { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 }, | 200 | { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 }, |
198 | { NULL_GUID, "", NULL }, | 201 | { NULL_GUID, "", NULL }, |
199 | }; | 202 | }; |
200 | 203 | ||
204 | static bool | ||
205 | variable_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 | |||
201 | bool | 229 | bool |
202 | efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, | 230 | efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, |
203 | unsigned long data_size) | 231 | unsigned long data_size) |
@@ -221,35 +249,48 @@ efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, | |||
221 | if (efi_guidcmp(vendor, variable_validate[i].vendor)) | 249 | if (efi_guidcmp(vendor, variable_validate[i].vendor)) |
222 | continue; | 250 | continue; |
223 | 251 | ||
224 | for (match = 0; ; match++) { | 252 | if (variable_matches(utf8_name, utf8_size+1, name, &match)) { |
225 | char c = name[match]; | 253 | if (variable_validate[i].validate == NULL) |
226 | char u = utf8_name[match]; | ||
227 | |||
228 | /* Wildcard in the matching name means we've matched */ | ||
229 | if (c == '*') { | ||
230 | kfree(utf8_name); | ||
231 | return variable_validate[i].validate(var_name, | ||
232 | match, data, data_size); | ||
233 | } | ||
234 | |||
235 | /* Case sensitive match */ | ||
236 | if (c != u) | ||
237 | break; | 254 | break; |
238 | 255 | kfree(utf8_name); | |
239 | /* Reached the end of the string while matching */ | 256 | return variable_validate[i].validate(var_name, match, |
240 | if (!c) { | 257 | data, data_size); |
241 | kfree(utf8_name); | ||
242 | return variable_validate[i].validate(var_name, | ||
243 | match, data, data_size); | ||
244 | } | ||
245 | } | 258 | } |
246 | } | 259 | } |
247 | |||
248 | kfree(utf8_name); | 260 | kfree(utf8_name); |
249 | return true; | 261 | return true; |
250 | } | 262 | } |
251 | EXPORT_SYMBOL_GPL(efivar_validate); | 263 | EXPORT_SYMBOL_GPL(efivar_validate); |
252 | 264 | ||
265 | bool | ||
266 | efivar_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 | } | ||
292 | EXPORT_SYMBOL_GPL(efivar_variable_is_removable); | ||
293 | |||
253 | static efi_status_t | 294 | static efi_status_t |
254 | check_var_size(u32 attributes, unsigned long size) | 295 | check_var_size(u32 attributes, unsigned long size) |
255 | { | 296 | { |
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 | ||
107 | static int | ||
108 | efivarfs_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 | |||
123 | static int | ||
124 | efivarfs_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 | |||
160 | long | ||
161 | efivarfs_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 | |||
106 | const struct file_operations efivarfs_file_operations = { | 175 | const 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 | ||
17 | struct inode *efivarfs_get_inode(struct super_block *sb, | 17 | 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, |
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) | |||
102 | static int efivarfs_create(struct inode *dir, struct dentry *dentry, | 104 | static 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, | |||
138 | out: | 145 | out: |
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; | |||
15 | extern const struct inode_operations efivarfs_dir_inode_operations; | 15 | extern const struct inode_operations efivarfs_dir_inode_operations; |
16 | extern bool efivarfs_valid_name(const char *str, int len); | 16 | extern bool efivarfs_valid_name(const char *str, int len); |
17 | extern struct inode *efivarfs_get_inode(struct super_block *sb, | 17 | extern 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 | ||
20 | extern struct list_head efivarfs_list; | 21 | extern struct list_head efivarfs_list; |
21 | 22 | ||
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 8651ac28ec0d..dd029d13ea61 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c | |||
@@ -120,6 +120,7 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, | |||
120 | char *name; | 120 | char *name; |
121 | int len; | 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) |
@@ -137,13 +138,17 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, | |||
137 | 138 | ||
138 | ucs2_as_utf8(name, entry->var.VariableName, len); | 139 | ucs2_as_utf8(name, entry->var.VariableName, len); |
139 | 140 | ||
141 | if (efivar_variable_is_removable(entry->var.VendorGuid, name, len)) | ||
142 | is_removable = true; | ||
143 | |||
140 | name[len] = '-'; | 144 | name[len] = '-'; |
141 | 145 | ||
142 | efi_guid_to_str(&entry->var.VendorGuid, name + len + 1); | 146 | efi_guid_to_str(&entry->var.VendorGuid, name + len + 1); |
143 | 147 | ||
144 | name[len + EFI_VARIABLE_GUID_LEN+1] = '\0'; | 148 | name[len + EFI_VARIABLE_GUID_LEN+1] = '\0'; |
145 | 149 | ||
146 | 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); | ||
147 | if (!inode) | 152 | if (!inode) |
148 | goto fail_name; | 153 | goto fail_name; |
149 | 154 | ||
@@ -199,7 +204,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
199 | sb->s_d_op = &efivarfs_d_ops; | 204 | sb->s_d_op = &efivarfs_d_ops; |
200 | sb->s_time_gran = 1; | 205 | sb->s_time_gran = 1; |
201 | 206 | ||
202 | inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); | 207 | inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0, true); |
203 | if (!inode) | 208 | if (!inode) |
204 | return -ENOMEM; | 209 | return -ENOMEM; |
205 | 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 16ca611aabc8..47be3ad7d3e5 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
@@ -1201,6 +1201,8 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid, | |||
1201 | 1201 | ||
1202 | bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, | 1202 | bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, |
1203 | unsigned long data_size); | 1203 | unsigned long data_size); |
1204 | bool efivar_variable_is_removable(efi_guid_t vendor, const char *name, | ||
1205 | size_t len); | ||
1204 | 1206 | ||
1205 | extern struct work_struct efivar_work; | 1207 | extern struct work_struct efivar_work; |
1206 | void efivar_run_worker(void); | 1208 | void efivar_run_worker(void); |
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 | |||
12 | static 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 | |||
43 | static 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 | ||
9 | int main(int argc, char **argv) | 67 | int 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"); |