Blame view

fs/efivarfs/inode.c 3.03 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
d68772b7c   Matt Fleming   efivarfs: Move to...
2
3
4
  /*
   * Copyright (C) 2012 Red Hat, Inc.
   * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
d68772b7c   Matt Fleming   efivarfs: Move to...
5
6
7
8
9
   */
  
  #include <linux/efi.h>
  #include <linux/fs.h>
  #include <linux/ctype.h>
20b4fb485   Linus Torvalds   Merge branch 'for...
10
  #include <linux/slab.h>
8236431d8   Andy Shevchenko   fs/efivarfs/inode...
11
  #include <linux/uuid.h>
d68772b7c   Matt Fleming   efivarfs: Move to...
12
13
14
15
  
  #include "internal.h"
  
  struct inode *efivarfs_get_inode(struct super_block *sb,
ed8b0de5a   Peter Jones   efi: Make efivarf...
16
17
  				const struct inode *dir, int mode,
  				dev_t dev, bool is_removable)
d68772b7c   Matt Fleming   efivarfs: Move to...
18
19
20
21
22
23
  {
  	struct inode *inode = new_inode(sb);
  
  	if (inode) {
  		inode->i_ino = get_next_ino();
  		inode->i_mode = mode;
078cd8279   Deepa Dinamani   fs: Replace CURRE...
24
  		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
ed8b0de5a   Peter Jones   efi: Make efivarf...
25
  		inode->i_flags = is_removable ? 0 : S_IMMUTABLE;
d68772b7c   Matt Fleming   efivarfs: Move to...
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  		switch (mode & S_IFMT) {
  		case S_IFREG:
  			inode->i_fop = &efivarfs_file_operations;
  			break;
  		case S_IFDIR:
  			inode->i_op = &efivarfs_dir_inode_operations;
  			inode->i_fop = &simple_dir_operations;
  			inc_nlink(inode);
  			break;
  		}
  	}
  	return inode;
  }
  
  /*
   * Return true if 'str' is a valid efivarfs filename of the form,
   *
   *	VariableName-12345678-1234-1234-1234-1234567891bc
   */
  bool efivarfs_valid_name(const char *str, int len)
  {
d68772b7c   Matt Fleming   efivarfs: Move to...
47
  	const char *s = str + len - EFI_VARIABLE_GUID_LEN;
d68772b7c   Matt Fleming   efivarfs: Move to...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  
  	/*
  	 * We need a GUID, plus at least one letter for the variable name,
  	 * plus the '-' separator
  	 */
  	if (len < EFI_VARIABLE_GUID_LEN + 2)
  		return false;
  
  	/* GUID must be preceded by a '-' */
  	if (*(s - 1) != '-')
  		return false;
  
  	/*
  	 * Validate that 's' is of the correct format, e.g.
  	 *
  	 *	12345678-1234-1234-1234-123456789abc
  	 */
8236431d8   Andy Shevchenko   fs/efivarfs/inode...
65
  	return uuid_is_valid(s);
d68772b7c   Matt Fleming   efivarfs: Move to...
66
67
68
69
70
  }
  
  static int efivarfs_create(struct inode *dir, struct dentry *dentry,
  			  umode_t mode, bool excl)
  {
ed8b0de5a   Peter Jones   efi: Make efivarf...
71
  	struct inode *inode = NULL;
d68772b7c   Matt Fleming   efivarfs: Move to...
72
73
  	struct efivar_entry *var;
  	int namelen, i = 0, err = 0;
ed8b0de5a   Peter Jones   efi: Make efivarf...
74
  	bool is_removable = false;
d68772b7c   Matt Fleming   efivarfs: Move to...
75
76
77
  
  	if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
  		return -EINVAL;
d68772b7c   Matt Fleming   efivarfs: Move to...
78
  	var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
ed8b0de5a   Peter Jones   efi: Make efivarf...
79
80
  	if (!var)
  		return -ENOMEM;
d68772b7c   Matt Fleming   efivarfs: Move to...
81
82
83
  
  	/* length of the variable name itself: remove GUID and separator */
  	namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1;
c4326563d   Andy Shevchenko   efivars: Call gui...
84
85
86
  	err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid);
  	if (err)
  		goto out;
d68772b7c   Matt Fleming   efivarfs: Move to...
87

ed8b0de5a   Peter Jones   efi: Make efivarf...
88
89
90
91
92
93
94
95
96
  	if (efivar_variable_is_removable(var->var.VendorGuid,
  					 dentry->d_name.name, namelen))
  		is_removable = true;
  
  	inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable);
  	if (!inode) {
  		err = -ENOMEM;
  		goto out;
  	}
d68772b7c   Matt Fleming   efivarfs: Move to...
97
98
99
100
101
102
  	for (i = 0; i < namelen; i++)
  		var->var.VariableName[i] = dentry->d_name.name[i];
  
  	var->var.VariableName[i] = '\0';
  
  	inode->i_private = var;
21b3ddd39   Sylvain Chouleur   efi: Don't use sp...
103
104
105
  	err = efivar_entry_add(var, &efivarfs_list);
  	if (err)
  		goto out;
d68772b7c   Matt Fleming   efivarfs: Move to...
106
107
108
109
110
  	d_instantiate(dentry, inode);
  	dget(dentry);
  out:
  	if (err) {
  		kfree(var);
ed8b0de5a   Peter Jones   efi: Make efivarf...
111
112
  		if (inode)
  			iput(inode);
d68772b7c   Matt Fleming   efivarfs: Move to...
113
114
115
116
117
118
  	}
  	return err;
  }
  
  static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
  {
2b0143b5c   David Howells   VFS: normal files...
119
  	struct efivar_entry *var = d_inode(dentry)->i_private;
d68772b7c   Matt Fleming   efivarfs: Move to...
120
121
122
  
  	if (efivar_entry_delete(var))
  		return -EINVAL;
2b0143b5c   David Howells   VFS: normal files...
123
  	drop_nlink(d_inode(dentry));
d68772b7c   Matt Fleming   efivarfs: Move to...
124
125
126
  	dput(dentry);
  	return 0;
  };
d68772b7c   Matt Fleming   efivarfs: Move to...
127
  const struct inode_operations efivarfs_dir_inode_operations = {
6e8cd2cb4   Al Viro   efivarfs: we can ...
128
  	.lookup = simple_lookup,
d68772b7c   Matt Fleming   efivarfs: Move to...
129
130
131
  	.unlink = efivarfs_unlink,
  	.create = efivarfs_create,
  };