efivarfs: guid part of filenames are case-insensitive
It makes no sense to treat the following filenames as unique,
	VarName-abcdefab-abcd-abcd-abcd-abcdefabcdef
	VarName-ABCDEFAB-ABCD-ABCD-ABCD-ABCDEFABCDEF
	VarName-ABcDEfAB-ABcD-ABcD-ABcD-ABcDEfABcDEf
	VarName-aBcDEfAB-aBcD-aBcD-aBcD-aBcDEfaBcDEf
	... etc ...
since the guid will be converted into a binary representation, which
has no case.
Roll our own dentry operations so that we can treat the variable name
part of filenames ("VarName" in the above example) as case-sensitive,
but the guid portion as case-insensitive. That way, efivarfs will
refuse to create the above files if any one already exists.
Reported-by: Lingzhu Xiang <lxiang@redhat.com>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Jeremy Kerr <jeremy.kerr@canonical.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
	
	
This commit is contained in:
		
					parent
					
						
							
								47f531e8ba
							
						
					
				
			
			
				commit
				
					
						da27a24383
					
				
			
		
					 1 changed files with 93 additions and 2 deletions
				
			
		| 
						 | 
					@ -1043,6 +1043,84 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
 | 
				
			||||||
	return -EINVAL;
 | 
						return -EINVAL;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Compare two efivarfs file names.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * An efivarfs filename is composed of two parts,
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	1. A case-sensitive variable name
 | 
				
			||||||
 | 
					 *	2. A case-insensitive GUID
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * So we need to perform a case-sensitive match on part 1 and a
 | 
				
			||||||
 | 
					 * case-insensitive match on part 2.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode,
 | 
				
			||||||
 | 
								      const struct dentry *dentry, const struct inode *inode,
 | 
				
			||||||
 | 
								      unsigned int len, const char *str,
 | 
				
			||||||
 | 
								      const struct qstr *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int guid = len - GUID_LEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (name->len != len)
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Case-sensitive compare for the variable name */
 | 
				
			||||||
 | 
						if (memcmp(str, name->name, guid))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Case-insensitive compare for the GUID */
 | 
				
			||||||
 | 
						return strncasecmp(name->name + guid, str + guid, GUID_LEN);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int efivarfs_d_hash(const struct dentry *dentry,
 | 
				
			||||||
 | 
								   const struct inode *inode, struct qstr *qstr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long hash = init_name_hash();
 | 
				
			||||||
 | 
						const unsigned char *s = qstr->name;
 | 
				
			||||||
 | 
						unsigned int len = qstr->len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!efivarfs_valid_name(s, len))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (len-- > GUID_LEN)
 | 
				
			||||||
 | 
							hash = partial_name_hash(*s++, hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* GUID is case-insensitive. */
 | 
				
			||||||
 | 
						while (len--)
 | 
				
			||||||
 | 
							hash = partial_name_hash(tolower(*s++), hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qstr->hash = end_name_hash(hash);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Retaining negative dentries for an in-memory filesystem just wastes
 | 
				
			||||||
 | 
					 * memory and lookup time: arrange for them to be deleted immediately.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int efivarfs_delete_dentry(const struct dentry *dentry)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct dentry_operations efivarfs_d_ops = {
 | 
				
			||||||
 | 
						.d_compare = efivarfs_d_compare,
 | 
				
			||||||
 | 
						.d_hash = efivarfs_d_hash,
 | 
				
			||||||
 | 
						.d_delete = efivarfs_delete_dentry,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct qstr q;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q.name = name;
 | 
				
			||||||
 | 
						q.len = strlen(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (efivarfs_d_hash(NULL, NULL, &q))
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return d_alloc(parent, &q);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 | 
					static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *inode = NULL;
 | 
						struct inode *inode = NULL;
 | 
				
			||||||
| 
						 | 
					@ -1058,6 +1136,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
	sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
 | 
						sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
 | 
				
			||||||
	sb->s_magic             = EFIVARFS_MAGIC;
 | 
						sb->s_magic             = EFIVARFS_MAGIC;
 | 
				
			||||||
	sb->s_op                = &efivarfs_ops;
 | 
						sb->s_op                = &efivarfs_ops;
 | 
				
			||||||
 | 
						sb->s_d_op		= &efivarfs_d_ops;
 | 
				
			||||||
	sb->s_time_gran         = 1;
 | 
						sb->s_time_gran         = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
 | 
						inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
 | 
				
			||||||
| 
						 | 
					@ -1098,7 +1177,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
		if (!inode)
 | 
							if (!inode)
 | 
				
			||||||
			goto fail_name;
 | 
								goto fail_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dentry = d_alloc_name(root, name);
 | 
							dentry = efivarfs_alloc_dentry(root, name);
 | 
				
			||||||
		if (!dentry)
 | 
							if (!dentry)
 | 
				
			||||||
			goto fail_inode;
 | 
								goto fail_inode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1148,8 +1227,20 @@ static struct file_system_type efivarfs_type = {
 | 
				
			||||||
	.kill_sb = efivarfs_kill_sb,
 | 
						.kill_sb = efivarfs_kill_sb,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Handle negative dentry.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
 | 
									      unsigned int flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (dentry->d_name.len > NAME_MAX)
 | 
				
			||||||
 | 
							return ERR_PTR(-ENAMETOOLONG);
 | 
				
			||||||
 | 
						d_add(dentry, NULL);
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct inode_operations efivarfs_dir_inode_operations = {
 | 
					static const struct inode_operations efivarfs_dir_inode_operations = {
 | 
				
			||||||
	.lookup = simple_lookup,
 | 
						.lookup = efivarfs_lookup,
 | 
				
			||||||
	.unlink = efivarfs_unlink,
 | 
						.unlink = efivarfs_unlink,
 | 
				
			||||||
	.create = efivarfs_create,
 | 
						.create = efivarfs_create,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue