diff options
| -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 | }; |
