1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2012 Red Hat, Inc. 4 * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com> 5 */ 6 7 #include <linux/efi.h> 8 #include <linux/delay.h> 9 #include <linux/fs.h> 10 #include <linux/slab.h> 11 #include <linux/mount.h> 12 13 #include "internal.h" 14 15 static ssize_t efivarfs_file_write(struct file *file, 16 const char __user *userbuf, size_t count, loff_t *ppos) 17 { 18 struct efivar_entry *var = file->private_data; 19 void *data; 20 u32 attributes; 21 struct inode *inode = file->f_mapping->host; 22 unsigned long datasize = count - sizeof(attributes); 23 ssize_t bytes; 24 bool set = false; 25 26 if (count < sizeof(attributes)) 27 return -EINVAL; 28 29 if (copy_from_user(&attributes, userbuf, sizeof(attributes))) 30 return -EFAULT; 31 32 if (attributes & ~(EFI_VARIABLE_MASK)) 33 return -EINVAL; 34 35 data = memdup_user(userbuf + sizeof(attributes), datasize); 36 if (IS_ERR(data)) 37 return PTR_ERR(data); 38 39 bytes = efivar_entry_set_get_size(var, attributes, &datasize, 40 data, &set); 41 if (!set && bytes) { 42 if (bytes == -ENOENT) 43 bytes = -EIO; 44 goto out; 45 } 46 47 if (bytes == -ENOENT) { 48 drop_nlink(inode); 49 d_delete(file->f_path.dentry); 50 dput(file->f_path.dentry); 51 } else { 52 inode_lock(inode); 53 i_size_write(inode, datasize + sizeof(attributes)); 54 inode_unlock(inode); 55 } 56 57 bytes = count; 58 59 out: 60 kfree(data); 61 62 return bytes; 63 } 64 65 static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, 66 size_t count, loff_t *ppos) 67 { 68 struct efivar_entry *var = file->private_data; 69 unsigned long datasize = 0; 70 u32 attributes; 71 void *data; 72 ssize_t size = 0; 73 int err; 74 75 while (!__ratelimit(&file->f_cred->user->ratelimit)) { 76 if (!msleep_interruptible(50)) 77 return -EINTR; 78 } 79 80 err = efivar_entry_size(var, &datasize); 81 82 /* 83 * efivarfs represents uncommitted variables with 84 * zero-length files. Reading them should return EOF. 85 */ 86 if (err == -ENOENT) 87 return 0; 88 else if (err) 89 return err; 90 91 data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL); 92 93 if (!data) 94 return -ENOMEM; 95 96 size = efivar_entry_get(var, &attributes, &datasize, 97 data + sizeof(attributes)); 98 if (size) 99 goto out_free; 100 101 memcpy(data, &attributes, sizeof(attributes)); 102 size = simple_read_from_buffer(userbuf, count, ppos, 103 data, datasize + sizeof(attributes)); 104 out_free: 105 kfree(data); 106 107 return size; 108 } 109 110 static int 111 efivarfs_ioc_getxflags(struct file *file, void __user *arg) 112 { 113 struct inode *inode = file->f_mapping->host; 114 unsigned int i_flags; 115 unsigned int flags = 0; 116 117 i_flags = inode->i_flags; 118 if (i_flags & S_IMMUTABLE) 119 flags |= FS_IMMUTABLE_FL; 120 121 if (copy_to_user(arg, &flags, sizeof(flags))) 122 return -EFAULT; 123 return 0; 124 } 125 126 static int 127 efivarfs_ioc_setxflags(struct file *file, void __user *arg) 128 { 129 struct inode *inode = file->f_mapping->host; 130 unsigned int flags; 131 unsigned int i_flags = 0; 132 int error; 133 134 if (!inode_owner_or_capable(inode)) 135 return -EACCES; 136 137 if (copy_from_user(&flags, arg, sizeof(flags))) 138 return -EFAULT; 139 140 if (flags & ~FS_IMMUTABLE_FL) 141 return -EOPNOTSUPP; 142 143 if (!capable(CAP_LINUX_IMMUTABLE)) 144 return -EPERM; 145 146 if (flags & FS_IMMUTABLE_FL) 147 i_flags |= S_IMMUTABLE; 148 149 150 error = mnt_want_write_file(file); 151 if (error) 152 return error; 153 154 inode_lock(inode); 155 inode_set_flags(inode, i_flags, S_IMMUTABLE); 156 inode_unlock(inode); 157 158 mnt_drop_write_file(file); 159 160 return 0; 161 } 162 163 static long 164 efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p) 165 { 166 void __user *arg = (void __user *)p; 167 168 switch (cmd) { 169 case FS_IOC_GETFLAGS: 170 return efivarfs_ioc_getxflags(file, arg); 171 case FS_IOC_SETFLAGS: 172 return efivarfs_ioc_setxflags(file, arg); 173 } 174 175 return -ENOTTY; 176 } 177 178 const struct file_operations efivarfs_file_operations = { 179 .open = simple_open, 180 .read = efivarfs_file_read, 181 .write = efivarfs_file_write, 182 .llseek = no_llseek, 183 .unlocked_ioctl = efivarfs_file_ioctl, 184 }; 185