Skip to content

Commit b5d1e6e

Browse files
James Bottomleyardbiesheuvel
authored andcommitted
efivarfs: add variable resync after hibernation
Hibernation allows other OSs to boot and thus the variable state might be altered by the time the hibernation image is resumed. Resync the variable state by looping over all the dentries and update the size (in case of alteration) delete any which no-longer exist. Finally, loop over all efi variables creating any which don't have corresponding dentries. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> [ardb: - apply error pointer fixup from Dan Carpenter - rebase onto latest version of James's efivarfs rework] Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
1 parent 0e2f98d commit b5d1e6e

File tree

3 files changed

+156
-4
lines changed

3 files changed

+156
-4
lines changed

fs/efivarfs/internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ struct efivarfs_fs_info {
1717
struct efivarfs_mount_opts mount_opts;
1818
struct super_block *sb;
1919
struct notifier_block nb;
20+
struct notifier_block pm_nb;
2021
};
2122

2223
struct efi_variable {
@@ -37,7 +38,7 @@ static inline struct efivar_entry *efivar_entry(struct inode *inode)
3738
}
3839

3940
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
40-
void *data);
41+
void *data, bool duplicate_check);
4142

4243
int efivar_entry_delete(struct efivar_entry *entry);
4344

fs/efivarfs/super.c

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/pagemap.h>
1414
#include <linux/ucs2_string.h>
1515
#include <linux/slab.h>
16+
#include <linux/suspend.h>
1617
#include <linux/magic.h>
1718
#include <linux/statfs.h>
1819
#include <linux/notifier.h>
@@ -366,7 +367,7 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
366367
if (err)
367368
return err;
368369

369-
return efivar_init(efivarfs_callback, sb);
370+
return efivar_init(efivarfs_callback, sb, true);
370371
}
371372

372373
static int efivarfs_get_tree(struct fs_context *fc)
@@ -390,6 +391,148 @@ static const struct fs_context_operations efivarfs_context_ops = {
390391
.reconfigure = efivarfs_reconfigure,
391392
};
392393

394+
struct efivarfs_ctx {
395+
struct dir_context ctx;
396+
struct super_block *sb;
397+
struct dentry *dentry;
398+
};
399+
400+
static bool efivarfs_actor(struct dir_context *ctx, const char *name, int len,
401+
loff_t offset, u64 ino, unsigned mode)
402+
{
403+
unsigned long size;
404+
struct efivarfs_ctx *ectx = container_of(ctx, struct efivarfs_ctx, ctx);
405+
struct qstr qstr = { .name = name, .len = len };
406+
struct dentry *dentry = d_hash_and_lookup(ectx->sb->s_root, &qstr);
407+
struct inode *inode;
408+
struct efivar_entry *entry;
409+
int err;
410+
411+
if (IS_ERR_OR_NULL(dentry))
412+
return true;
413+
414+
inode = d_inode(dentry);
415+
entry = efivar_entry(inode);
416+
417+
err = efivar_entry_size(entry, &size);
418+
size += sizeof(__u32); /* attributes */
419+
if (err)
420+
size = 0;
421+
422+
inode_lock(inode);
423+
i_size_write(inode, size);
424+
inode_unlock(inode);
425+
426+
if (!size) {
427+
ectx->dentry = dentry;
428+
return false;
429+
}
430+
431+
dput(dentry);
432+
433+
return true;
434+
}
435+
436+
static int efivarfs_check_missing(efi_char16_t *name16, efi_guid_t vendor,
437+
unsigned long name_size, void *data)
438+
{
439+
char *name;
440+
struct super_block *sb = data;
441+
struct dentry *dentry;
442+
struct qstr qstr;
443+
int err;
444+
445+
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
446+
return 0;
447+
448+
name = efivar_get_utf8name(name16, &vendor);
449+
if (!name)
450+
return -ENOMEM;
451+
452+
qstr.name = name;
453+
qstr.len = strlen(name);
454+
dentry = d_hash_and_lookup(sb->s_root, &qstr);
455+
if (IS_ERR(dentry)) {
456+
err = PTR_ERR(dentry);
457+
goto out;
458+
}
459+
460+
if (!dentry) {
461+
/* found missing entry */
462+
pr_info("efivarfs: creating variable %s\n", name);
463+
return efivarfs_create_dentry(sb, name16, name_size, vendor, name);
464+
}
465+
466+
dput(dentry);
467+
err = 0;
468+
469+
out:
470+
kfree(name);
471+
472+
return err;
473+
}
474+
475+
static int efivarfs_pm_notify(struct notifier_block *nb, unsigned long action,
476+
void *ptr)
477+
{
478+
struct efivarfs_fs_info *sfi = container_of(nb, struct efivarfs_fs_info,
479+
pm_nb);
480+
struct path path = { .mnt = NULL, .dentry = sfi->sb->s_root, };
481+
struct efivarfs_ctx ectx = {
482+
.ctx = {
483+
.actor = efivarfs_actor,
484+
},
485+
.sb = sfi->sb,
486+
};
487+
struct file *file;
488+
static bool rescan_done = true;
489+
490+
if (action == PM_HIBERNATION_PREPARE) {
491+
rescan_done = false;
492+
return NOTIFY_OK;
493+
} else if (action != PM_POST_HIBERNATION) {
494+
return NOTIFY_DONE;
495+
}
496+
497+
if (rescan_done)
498+
return NOTIFY_DONE;
499+
500+
pr_info("efivarfs: resyncing variable state\n");
501+
502+
/* O_NOATIME is required to prevent oops on NULL mnt */
503+
file = kernel_file_open(&path, O_RDONLY | O_DIRECTORY | O_NOATIME,
504+
current_cred());
505+
if (IS_ERR(file))
506+
return NOTIFY_DONE;
507+
508+
rescan_done = true;
509+
510+
/*
511+
* First loop over the directory and verify each entry exists,
512+
* removing it if it doesn't
513+
*/
514+
file->f_pos = 2; /* skip . and .. */
515+
do {
516+
ectx.dentry = NULL;
517+
iterate_dir(file, &ectx.ctx);
518+
if (ectx.dentry) {
519+
pr_info("efivarfs: removing variable %pd\n",
520+
ectx.dentry);
521+
simple_recursive_removal(ectx.dentry, NULL);
522+
dput(ectx.dentry);
523+
}
524+
} while (ectx.dentry);
525+
fput(file);
526+
527+
/*
528+
* then loop over variables, creating them if there's no matching
529+
* dentry
530+
*/
531+
efivar_init(efivarfs_check_missing, sfi->sb, false);
532+
533+
return NOTIFY_OK;
534+
}
535+
393536
static int efivarfs_init_fs_context(struct fs_context *fc)
394537
{
395538
struct efivarfs_fs_info *sfi;
@@ -406,6 +549,11 @@ static int efivarfs_init_fs_context(struct fs_context *fc)
406549

407550
fc->s_fs_info = sfi;
408551
fc->ops = &efivarfs_context_ops;
552+
553+
sfi->pm_nb.notifier_call = efivarfs_pm_notify;
554+
sfi->pm_nb.priority = 0;
555+
register_pm_notifier(&sfi->pm_nb);
556+
409557
return 0;
410558
}
411559

@@ -415,6 +563,7 @@ static void efivarfs_kill_sb(struct super_block *sb)
415563

416564
blocking_notifier_chain_unregister(&efivar_ops_nh, &sfi->nb);
417565
kill_litter_super(sb);
566+
unregister_pm_notifier(&sfi->pm_nb);
418567

419568
kfree(sfi);
420569
}

fs/efivarfs/vars.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,15 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
364364
* efivar_init - build the initial list of EFI variables
365365
* @func: callback function to invoke for every variable
366366
* @data: function-specific data to pass to @func
367+
* @duplicate_check: fail if a duplicate variable is found
367368
*
368369
* Get every EFI variable from the firmware and invoke @func. @func
369370
* should populate the initial dentry and inode tree.
370371
*
371372
* Returns 0 on success, or a kernel error code on failure.
372373
*/
373374
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
374-
void *data)
375+
void *data, bool duplicate_check)
375376
{
376377
unsigned long variable_name_size = 512;
377378
efi_char16_t *variable_name;
@@ -415,7 +416,8 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
415416
* we'll ever see a different variable name,
416417
* and may end up looping here forever.
417418
*/
418-
if (efivarfs_variable_is_present(variable_name,
419+
if (duplicate_check &&
420+
efivarfs_variable_is_present(variable_name,
419421
&vendor_guid, data)) {
420422
dup_variable_bug(variable_name, &vendor_guid,
421423
variable_name_size);

0 commit comments

Comments
 (0)