Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c542edd authored by Jeremy Kerr's avatar Jeremy Kerr Committed by Matt Fleming
Browse files

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: default avatarJeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: default avatarMatt Fleming <matt.fleming@intel.com>
parent 5d9db883
Loading
Loading
Loading
Loading
+39 −10
Original line number Diff line number Diff line
@@ -658,6 +658,7 @@ static ssize_t efivarfs_file_write(struct file *file,
	u32 attributes;
	struct inode *inode = file->f_mapping->host;
	int datasize = count - sizeof(attributes);
	unsigned long newdatasize;

	if (count < sizeof(attributes))
		return -EINVAL;
@@ -696,32 +697,60 @@ static ssize_t efivarfs_file_write(struct file *file,

	switch (status) {
	case EFI_SUCCESS:
		mutex_lock(&inode->i_mutex);
		i_size_write(inode, count);
		mutex_unlock(&inode->i_mutex);
		break;
	case EFI_INVALID_PARAMETER:
		count = -EINVAL;
		break;
		goto out;
	case EFI_OUT_OF_RESOURCES:
		count = -ENOSPC;
		break;
		goto out;
	case EFI_DEVICE_ERROR:
		count = -EIO;
		break;
		goto out;
	case EFI_WRITE_PROTECTED:
		count = -EROFS;
		break;
		goto out;
	case EFI_SECURITY_VIOLATION:
		count = -EACCES;
		break;
		goto out;
	case EFI_NOT_FOUND:
		count = -ENOENT;
		break;
		goto out;
	default:
		count = -EINVAL;
		break;
		goto out;
	}

	/*
	 * Writing to the variable may have caused a change in size (which
	 * could either be an append or an overwrite), or the variable to be
	 * deleted. Perform a GetVariable() so we can tell what actually
	 * happened.
	 */
	newdatasize = 0;
	status = efivars->ops->get_variable(var->var.VariableName,
					    &var->var.VendorGuid,
					    NULL, &newdatasize,
					    NULL);

	if (status == EFI_BUFFER_TOO_SMALL) {
		mutex_lock(&inode->i_mutex);
		i_size_write(inode, newdatasize + sizeof(attributes));
		mutex_unlock(&inode->i_mutex);

	} else if (status == EFI_NOT_FOUND) {
		spin_lock(&efivars->lock);
		list_del(&var->list);
		spin_unlock(&efivars->lock);
		efivar_unregister(var);
		drop_nlink(inode);
		dput(file->f_dentry);

	} else {
		pr_warn("efivarfs: inconsistent EFI variable implementation? "
				"status = %lx\n", status);
	}

out:
	kfree(data);