aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorJeremy Kerr <jeremy.kerr@canonical.com>2012-10-05 01:54:56 -0400
committerMatt Fleming <matt.fleming@intel.com>2012-10-30 06:39:17 -0400
commit0c542edde3cecc99b180a440ae33dcb7f28642ce (patch)
tree585dc08504412ff15610f4e716e93b485a4fb2ed /drivers/firmware
parent5d9db883761ad1bc2245fd3018715549b974203d (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.c49
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
725out: 754out:
726 kfree(data); 755 kfree(data);
727 756