Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2012 Red Hat, Inc.
0004  * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
0005  */
0006 
0007 #include <linux/efi.h>
0008 #include <linux/fs.h>
0009 #include <linux/ctype.h>
0010 #include <linux/kmemleak.h>
0011 #include <linux/slab.h>
0012 #include <linux/uuid.h>
0013 #include <linux/fileattr.h>
0014 
0015 #include "internal.h"
0016 
0017 static const struct inode_operations efivarfs_file_inode_operations;
0018 
0019 struct inode *efivarfs_get_inode(struct super_block *sb,
0020                 const struct inode *dir, int mode,
0021                 dev_t dev, bool is_removable)
0022 {
0023     struct inode *inode = new_inode(sb);
0024 
0025     if (inode) {
0026         inode->i_ino = get_next_ino();
0027         inode->i_mode = mode;
0028         inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
0029         inode->i_flags = is_removable ? 0 : S_IMMUTABLE;
0030         switch (mode & S_IFMT) {
0031         case S_IFREG:
0032             inode->i_op = &efivarfs_file_inode_operations;
0033             inode->i_fop = &efivarfs_file_operations;
0034             break;
0035         case S_IFDIR:
0036             inode->i_op = &efivarfs_dir_inode_operations;
0037             inode->i_fop = &simple_dir_operations;
0038             inc_nlink(inode);
0039             break;
0040         }
0041     }
0042     return inode;
0043 }
0044 
0045 /*
0046  * Return true if 'str' is a valid efivarfs filename of the form,
0047  *
0048  *  VariableName-12345678-1234-1234-1234-1234567891bc
0049  */
0050 bool efivarfs_valid_name(const char *str, int len)
0051 {
0052     const char *s = str + len - EFI_VARIABLE_GUID_LEN;
0053 
0054     /*
0055      * We need a GUID, plus at least one letter for the variable name,
0056      * plus the '-' separator
0057      */
0058     if (len < EFI_VARIABLE_GUID_LEN + 2)
0059         return false;
0060 
0061     /* GUID must be preceded by a '-' */
0062     if (*(s - 1) != '-')
0063         return false;
0064 
0065     /*
0066      * Validate that 's' is of the correct format, e.g.
0067      *
0068      *  12345678-1234-1234-1234-123456789abc
0069      */
0070     return uuid_is_valid(s);
0071 }
0072 
0073 static int efivarfs_create(struct user_namespace *mnt_userns, struct inode *dir,
0074                struct dentry *dentry, umode_t mode, bool excl)
0075 {
0076     struct inode *inode = NULL;
0077     struct efivar_entry *var;
0078     int namelen, i = 0, err = 0;
0079     bool is_removable = false;
0080 
0081     if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
0082         return -EINVAL;
0083 
0084     var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
0085     if (!var)
0086         return -ENOMEM;
0087 
0088     /* length of the variable name itself: remove GUID and separator */
0089     namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1;
0090 
0091     err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid);
0092     if (err)
0093         goto out;
0094 
0095     if (efivar_variable_is_removable(var->var.VendorGuid,
0096                      dentry->d_name.name, namelen))
0097         is_removable = true;
0098 
0099     inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable);
0100     if (!inode) {
0101         err = -ENOMEM;
0102         goto out;
0103     }
0104 
0105     for (i = 0; i < namelen; i++)
0106         var->var.VariableName[i] = dentry->d_name.name[i];
0107 
0108     var->var.VariableName[i] = '\0';
0109 
0110     inode->i_private = var;
0111     kmemleak_ignore(var);
0112 
0113     err = efivar_entry_add(var, &efivarfs_list);
0114     if (err)
0115         goto out;
0116 
0117     d_instantiate(dentry, inode);
0118     dget(dentry);
0119 out:
0120     if (err) {
0121         kfree(var);
0122         if (inode)
0123             iput(inode);
0124     }
0125     return err;
0126 }
0127 
0128 static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
0129 {
0130     struct efivar_entry *var = d_inode(dentry)->i_private;
0131 
0132     if (efivar_entry_delete(var))
0133         return -EINVAL;
0134 
0135     drop_nlink(d_inode(dentry));
0136     dput(dentry);
0137     return 0;
0138 };
0139 
0140 const struct inode_operations efivarfs_dir_inode_operations = {
0141     .lookup = simple_lookup,
0142     .unlink = efivarfs_unlink,
0143     .create = efivarfs_create,
0144 };
0145 
0146 static int
0147 efivarfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
0148 {
0149     unsigned int i_flags;
0150     unsigned int flags = 0;
0151 
0152     i_flags = d_inode(dentry)->i_flags;
0153     if (i_flags & S_IMMUTABLE)
0154         flags |= FS_IMMUTABLE_FL;
0155 
0156     fileattr_fill_flags(fa, flags);
0157 
0158     return 0;
0159 }
0160 
0161 static int
0162 efivarfs_fileattr_set(struct user_namespace *mnt_userns,
0163               struct dentry *dentry, struct fileattr *fa)
0164 {
0165     unsigned int i_flags = 0;
0166 
0167     if (fileattr_has_fsx(fa))
0168         return -EOPNOTSUPP;
0169 
0170     if (fa->flags & ~FS_IMMUTABLE_FL)
0171         return -EOPNOTSUPP;
0172 
0173     if (fa->flags & FS_IMMUTABLE_FL)
0174         i_flags |= S_IMMUTABLE;
0175 
0176     inode_set_flags(d_inode(dentry), i_flags, S_IMMUTABLE);
0177 
0178     return 0;
0179 }
0180 
0181 static const struct inode_operations efivarfs_file_inode_operations = {
0182     .fileattr_get = efivarfs_fileattr_get,
0183     .fileattr_set = efivarfs_fileattr_set,
0184 };