diff options
author | Matt Fleming <matt.fleming@intel.com> | 2013-02-01 06:02:28 -0500 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2013-02-12 07:41:54 -0500 |
commit | da27a24383b2b10bf6ebd0db29b325548aafecb4 (patch) | |
tree | db48f001f62af527e832fe2c1cd302b30e794b6a /drivers/firmware | |
parent | 47f531e8ba3bc3901a0c493f4252826c41dea1a1 (diff) |
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>
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efivars.c | 95 |
1 files changed, 93 insertions, 2 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 868cea5cd4b8..8bcb5958f21a 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -1043,6 +1043,84 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) | |||
1043 | return -EINVAL; | 1043 | return -EINVAL; |
1044 | }; | 1044 | }; |
1045 | 1045 | ||
1046 | /* | ||
1047 | * Compare two efivarfs file names. | ||
1048 | * | ||
1049 | * An efivarfs filename is composed of two parts, | ||
1050 | * | ||
1051 | * 1. A case-sensitive variable name | ||
1052 | * 2. A case-insensitive GUID | ||
1053 | * | ||
1054 | * So we need to perform a case-sensitive match on part 1 and a | ||
1055 | * case-insensitive match on part 2. | ||
1056 | */ | ||
1057 | static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode, | ||
1058 | const struct dentry *dentry, const struct inode *inode, | ||
1059 | unsigned int len, const char *str, | ||
1060 | const struct qstr *name) | ||
1061 | { | ||
1062 | int guid = len - GUID_LEN; | ||
1063 | |||
1064 | if (name->len != len) | ||
1065 | return 1; | ||
1066 | |||
1067 | /* Case-sensitive compare for the variable name */ | ||
1068 | if (memcmp(str, name->name, guid)) | ||
1069 | return 1; | ||
1070 | |||
1071 | /* Case-insensitive compare for the GUID */ | ||
1072 | return strncasecmp(name->name + guid, str + guid, GUID_LEN); | ||
1073 | } | ||
1074 | |||
1075 | static int efivarfs_d_hash(const struct dentry *dentry, | ||
1076 | const struct inode *inode, struct qstr *qstr) | ||
1077 | { | ||
1078 | unsigned long hash = init_name_hash(); | ||
1079 | const unsigned char *s = qstr->name; | ||
1080 | unsigned int len = qstr->len; | ||
1081 | |||
1082 | if (!efivarfs_valid_name(s, len)) | ||
1083 | return -EINVAL; | ||
1084 | |||
1085 | while (len-- > GUID_LEN) | ||
1086 | hash = partial_name_hash(*s++, hash); | ||
1087 | |||
1088 | /* GUID is case-insensitive. */ | ||
1089 | while (len--) | ||
1090 | hash = partial_name_hash(tolower(*s++), hash); | ||
1091 | |||
1092 | qstr->hash = end_name_hash(hash); | ||
1093 | return 0; | ||
1094 | } | ||
1095 | |||
1096 | /* | ||
1097 | * Retaining negative dentries for an in-memory filesystem just wastes | ||
1098 | * memory and lookup time: arrange for them to be deleted immediately. | ||
1099 | */ | ||
1100 | static int efivarfs_delete_dentry(const struct dentry *dentry) | ||
1101 | { | ||
1102 | return 1; | ||
1103 | } | ||
1104 | |||
1105 | static struct dentry_operations efivarfs_d_ops = { | ||
1106 | .d_compare = efivarfs_d_compare, | ||
1107 | .d_hash = efivarfs_d_hash, | ||
1108 | .d_delete = efivarfs_delete_dentry, | ||
1109 | }; | ||
1110 | |||
1111 | static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) | ||
1112 | { | ||
1113 | struct qstr q; | ||
1114 | |||
1115 | q.name = name; | ||
1116 | q.len = strlen(name); | ||
1117 | |||
1118 | if (efivarfs_d_hash(NULL, NULL, &q)) | ||
1119 | return NULL; | ||
1120 | |||
1121 | return d_alloc(parent, &q); | ||
1122 | } | ||
1123 | |||
1046 | static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | 1124 | static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) |
1047 | { | 1125 | { |
1048 | struct inode *inode = NULL; | 1126 | struct inode *inode = NULL; |
@@ -1058,6 +1136,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
1058 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | 1136 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
1059 | sb->s_magic = EFIVARFS_MAGIC; | 1137 | sb->s_magic = EFIVARFS_MAGIC; |
1060 | sb->s_op = &efivarfs_ops; | 1138 | sb->s_op = &efivarfs_ops; |
1139 | sb->s_d_op = &efivarfs_d_ops; | ||
1061 | sb->s_time_gran = 1; | 1140 | sb->s_time_gran = 1; |
1062 | 1141 | ||
1063 | inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); | 1142 | 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) | |||
1098 | if (!inode) | 1177 | if (!inode) |
1099 | goto fail_name; | 1178 | goto fail_name; |
1100 | 1179 | ||
1101 | dentry = d_alloc_name(root, name); | 1180 | dentry = efivarfs_alloc_dentry(root, name); |
1102 | if (!dentry) | 1181 | if (!dentry) |
1103 | goto fail_inode; | 1182 | goto fail_inode; |
1104 | 1183 | ||
@@ -1148,8 +1227,20 @@ static struct file_system_type efivarfs_type = { | |||
1148 | .kill_sb = efivarfs_kill_sb, | 1227 | .kill_sb = efivarfs_kill_sb, |
1149 | }; | 1228 | }; |
1150 | 1229 | ||
1230 | /* | ||
1231 | * Handle negative dentry. | ||
1232 | */ | ||
1233 | static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry, | ||
1234 | unsigned int flags) | ||
1235 | { | ||
1236 | if (dentry->d_name.len > NAME_MAX) | ||
1237 | return ERR_PTR(-ENAMETOOLONG); | ||
1238 | d_add(dentry, NULL); | ||
1239 | return NULL; | ||
1240 | } | ||
1241 | |||
1151 | static const struct inode_operations efivarfs_dir_inode_operations = { | 1242 | static const struct inode_operations efivarfs_dir_inode_operations = { |
1152 | .lookup = simple_lookup, | 1243 | .lookup = efivarfs_lookup, |
1153 | .unlink = efivarfs_unlink, | 1244 | .unlink = efivarfs_unlink, |
1154 | .create = efivarfs_create, | 1245 | .create = efivarfs_create, |
1155 | }; | 1246 | }; |