diff options
author | Jeremy Kerr <jeremy.kerr@canonical.com> | 2012-10-05 01:54:56 -0400 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2012-10-30 06:39:17 -0400 |
commit | 0c542edde3cecc99b180a440ae33dcb7f28642ce (patch) | |
tree | 585dc08504412ff15610f4e716e93b485a4fb2ed /drivers/firmware | |
parent | 5d9db883761ad1bc2245fd3018715549b974203d (diff) |
efi: Handle deletions and size changes in efivarfs_write_file
A write to an efivarfs file will not always result in a variable of
'count' size after the EFI SetVariable() call. We may have appended to
the existing data (ie, with the EFI_VARIABLE_APPEND_WRITE attribute), or
even have deleted the variable (with an authenticated variable update,
with a zero datasize).
This change re-reads the updated variable from firmware, to check for
size changes and deletions. In the latter case, we need to drop the
dentry.
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efivars.c | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index b605c4849772..d7658b4a5010 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -658,6 +658,7 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
658 | u32 attributes; | 658 | u32 attributes; |
659 | struct inode *inode = file->f_mapping->host; | 659 | struct inode *inode = file->f_mapping->host; |
660 | int datasize = count - sizeof(attributes); | 660 | int datasize = count - sizeof(attributes); |
661 | unsigned long newdatasize; | ||
661 | 662 | ||
662 | if (count < sizeof(attributes)) | 663 | if (count < sizeof(attributes)) |
663 | return -EINVAL; | 664 | return -EINVAL; |
@@ -696,32 +697,60 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
696 | 697 | ||
697 | switch (status) { | 698 | switch (status) { |
698 | case EFI_SUCCESS: | 699 | case EFI_SUCCESS: |
699 | mutex_lock(&inode->i_mutex); | ||
700 | i_size_write(inode, count); | ||
701 | mutex_unlock(&inode->i_mutex); | ||
702 | break; | 700 | break; |
703 | case EFI_INVALID_PARAMETER: | 701 | case EFI_INVALID_PARAMETER: |
704 | count = -EINVAL; | 702 | count = -EINVAL; |
705 | break; | 703 | goto out; |
706 | case EFI_OUT_OF_RESOURCES: | 704 | case EFI_OUT_OF_RESOURCES: |
707 | count = -ENOSPC; | 705 | count = -ENOSPC; |
708 | break; | 706 | goto out; |
709 | case EFI_DEVICE_ERROR: | 707 | case EFI_DEVICE_ERROR: |
710 | count = -EIO; | 708 | count = -EIO; |
711 | break; | 709 | goto out; |
712 | case EFI_WRITE_PROTECTED: | 710 | case EFI_WRITE_PROTECTED: |
713 | count = -EROFS; | 711 | count = -EROFS; |
714 | break; | 712 | goto out; |
715 | case EFI_SECURITY_VIOLATION: | 713 | case EFI_SECURITY_VIOLATION: |
716 | count = -EACCES; | 714 | count = -EACCES; |
717 | break; | 715 | goto out; |
718 | case EFI_NOT_FOUND: | 716 | case EFI_NOT_FOUND: |
719 | count = -ENOENT; | 717 | count = -ENOENT; |
720 | break; | 718 | goto out; |
721 | default: | 719 | default: |
722 | count = -EINVAL; | 720 | count = -EINVAL; |
723 | break; | 721 | goto out; |
724 | } | 722 | } |
723 | |||
724 | /* | ||
725 | * Writing to the variable may have caused a change in size (which | ||
726 | * could either be an append or an overwrite), or the variable to be | ||
727 | * deleted. Perform a GetVariable() so we can tell what actually | ||
728 | * happened. | ||
729 | */ | ||
730 | newdatasize = 0; | ||
731 | status = efivars->ops->get_variable(var->var.VariableName, | ||
732 | &var->var.VendorGuid, | ||
733 | NULL, &newdatasize, | ||
734 | NULL); | ||
735 | |||
736 | if (status == EFI_BUFFER_TOO_SMALL) { | ||
737 | mutex_lock(&inode->i_mutex); | ||
738 | i_size_write(inode, newdatasize + sizeof(attributes)); | ||
739 | mutex_unlock(&inode->i_mutex); | ||
740 | |||
741 | } else if (status == EFI_NOT_FOUND) { | ||
742 | spin_lock(&efivars->lock); | ||
743 | list_del(&var->list); | ||
744 | spin_unlock(&efivars->lock); | ||
745 | efivar_unregister(var); | ||
746 | drop_nlink(inode); | ||
747 | dput(file->f_dentry); | ||
748 | |||
749 | } else { | ||
750 | pr_warn("efivarfs: inconsistent EFI variable implementation? " | ||
751 | "status = %lx\n", status); | ||
752 | } | ||
753 | |||
725 | out: | 754 | out: |
726 | kfree(data); | 755 | kfree(data); |
727 | 756 | ||