diff options
author | Michael Ellerman <michael@ellerman.id.au> | 2006-03-26 22:26:26 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-03-28 00:45:23 -0500 |
commit | 5149fa47ec90eb5e79e28f3a7fbcf29421524817 (patch) | |
tree | c22023d68bbe23d273311185e284ead4c45d5edc | |
parent | d0160bf0b3e87032be8e85f80ddd2f18e107b86f (diff) |
[PATCH] powerpc: Cope with duplicate node & property names in /proc/device-tree
Various dodgy firmware might give us nodes and/or properties in the device
tree with conflicting names. That's generally ok, except for when we export
the device tree via /proc, so check when we're creating the proc device tree
and munge names accordingly.
Tested on a faked device tree with kexec, would be good if someone with
actual bogus firmware could try it, but just for completeness.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | fs/proc/proc_devtree.c | 103 |
1 files changed, 80 insertions, 23 deletions
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 596b4b4f1cc8..abdf068bc27f 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c | |||
@@ -52,7 +52,8 @@ static int property_read_proc(char *page, char **start, off_t off, | |||
52 | * Add a property to a node | 52 | * Add a property to a node |
53 | */ | 53 | */ |
54 | static struct proc_dir_entry * | 54 | static struct proc_dir_entry * |
55 | __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) | 55 | __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp, |
56 | const char *name) | ||
56 | { | 57 | { |
57 | struct proc_dir_entry *ent; | 58 | struct proc_dir_entry *ent; |
58 | 59 | ||
@@ -60,14 +61,14 @@ __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) | |||
60 | * Unfortunately proc_register puts each new entry | 61 | * Unfortunately proc_register puts each new entry |
61 | * at the beginning of the list. So we rearrange them. | 62 | * at the beginning of the list. So we rearrange them. |
62 | */ | 63 | */ |
63 | ent = create_proc_read_entry(pp->name, | 64 | ent = create_proc_read_entry(name, |
64 | strncmp(pp->name, "security-", 9) | 65 | strncmp(name, "security-", 9) |
65 | ? S_IRUGO : S_IRUSR, de, | 66 | ? S_IRUGO : S_IRUSR, de, |
66 | property_read_proc, pp); | 67 | property_read_proc, pp); |
67 | if (ent == NULL) | 68 | if (ent == NULL) |
68 | return NULL; | 69 | return NULL; |
69 | 70 | ||
70 | if (!strncmp(pp->name, "security-", 9)) | 71 | if (!strncmp(name, "security-", 9)) |
71 | ent->size = 0; /* don't leak number of password chars */ | 72 | ent->size = 0; /* don't leak number of password chars */ |
72 | else | 73 | else |
73 | ent->size = pp->length; | 74 | ent->size = pp->length; |
@@ -78,7 +79,7 @@ __proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp) | |||
78 | 79 | ||
79 | void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop) | 80 | void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop) |
80 | { | 81 | { |
81 | __proc_device_tree_add_prop(pde, prop); | 82 | __proc_device_tree_add_prop(pde, prop, prop->name); |
82 | } | 83 | } |
83 | 84 | ||
84 | void proc_device_tree_remove_prop(struct proc_dir_entry *pde, | 85 | void proc_device_tree_remove_prop(struct proc_dir_entry *pde, |
@@ -106,6 +107,69 @@ void proc_device_tree_update_prop(struct proc_dir_entry *pde, | |||
106 | } | 107 | } |
107 | 108 | ||
108 | /* | 109 | /* |
110 | * Various dodgy firmware might give us nodes and/or properties with | ||
111 | * conflicting names. That's generally ok, except for exporting via /proc, | ||
112 | * so munge names here to ensure they're unique. | ||
113 | */ | ||
114 | |||
115 | static int duplicate_name(struct proc_dir_entry *de, const char *name) | ||
116 | { | ||
117 | struct proc_dir_entry *ent; | ||
118 | int found = 0; | ||
119 | |||
120 | spin_lock(&proc_subdir_lock); | ||
121 | |||
122 | for (ent = de->subdir; ent != NULL; ent = ent->next) { | ||
123 | if (strcmp(ent->name, name) == 0) { | ||
124 | found = 1; | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | spin_unlock(&proc_subdir_lock); | ||
130 | |||
131 | return found; | ||
132 | } | ||
133 | |||
134 | static const char *fixup_name(struct device_node *np, struct proc_dir_entry *de, | ||
135 | const char *name) | ||
136 | { | ||
137 | char *fixed_name; | ||
138 | int fixup_len = strlen(name) + 2 + 1; /* name + #x + \0 */ | ||
139 | int i = 1, size; | ||
140 | |||
141 | realloc: | ||
142 | fixed_name = kmalloc(fixup_len, GFP_KERNEL); | ||
143 | if (fixed_name == NULL) { | ||
144 | printk(KERN_ERR "device-tree: Out of memory trying to fixup " | ||
145 | "name \"%s\"\n", name); | ||
146 | return name; | ||
147 | } | ||
148 | |||
149 | retry: | ||
150 | size = snprintf(fixed_name, fixup_len, "%s#%d", name, i); | ||
151 | size++; /* account for NULL */ | ||
152 | |||
153 | if (size > fixup_len) { | ||
154 | /* We ran out of space, free and reallocate. */ | ||
155 | kfree(fixed_name); | ||
156 | fixup_len = size; | ||
157 | goto realloc; | ||
158 | } | ||
159 | |||
160 | if (duplicate_name(de, fixed_name)) { | ||
161 | /* Multiple duplicates. Retry with a different offset. */ | ||
162 | i++; | ||
163 | goto retry; | ||
164 | } | ||
165 | |||
166 | printk(KERN_WARNING "device-tree: Duplicate name in %s, " | ||
167 | "renamed to \"%s\"\n", np->full_name, fixed_name); | ||
168 | |||
169 | return fixed_name; | ||
170 | } | ||
171 | |||
172 | /* | ||
109 | * Process a node, adding entries for its children and its properties. | 173 | * Process a node, adding entries for its children and its properties. |
110 | */ | 174 | */ |
111 | void proc_device_tree_add_node(struct device_node *np, | 175 | void proc_device_tree_add_node(struct device_node *np, |
@@ -118,37 +182,30 @@ void proc_device_tree_add_node(struct device_node *np, | |||
118 | 182 | ||
119 | set_node_proc_entry(np, de); | 183 | set_node_proc_entry(np, de); |
120 | for (child = NULL; (child = of_get_next_child(np, child));) { | 184 | for (child = NULL; (child = of_get_next_child(np, child));) { |
185 | /* Use everything after the last slash, or the full name */ | ||
121 | p = strrchr(child->full_name, '/'); | 186 | p = strrchr(child->full_name, '/'); |
122 | if (!p) | 187 | if (!p) |
123 | p = child->full_name; | 188 | p = child->full_name; |
124 | else | 189 | else |
125 | ++p; | 190 | ++p; |
191 | |||
192 | if (duplicate_name(de, p)) | ||
193 | p = fixup_name(np, de, p); | ||
194 | |||
126 | ent = proc_mkdir(p, de); | 195 | ent = proc_mkdir(p, de); |
127 | if (ent == 0) | 196 | if (ent == 0) |
128 | break; | 197 | break; |
129 | proc_device_tree_add_node(child, ent); | 198 | proc_device_tree_add_node(child, ent); |
130 | } | 199 | } |
131 | of_node_put(child); | 200 | of_node_put(child); |
201 | |||
132 | for (pp = np->properties; pp != 0; pp = pp->next) { | 202 | for (pp = np->properties; pp != 0; pp = pp->next) { |
133 | /* | 203 | p = pp->name; |
134 | * Yet another Apple device-tree bogosity: on some machines, | 204 | |
135 | * they have properties & nodes with the same name. Those | 205 | if (duplicate_name(de, p)) |
136 | * properties are quite unimportant for us though, thus we | 206 | p = fixup_name(np, de, p); |
137 | * simply "skip" them here, but we do have to check. | ||
138 | */ | ||
139 | spin_lock(&proc_subdir_lock); | ||
140 | for (ent = de->subdir; ent != NULL; ent = ent->next) | ||
141 | if (!strcmp(ent->name, pp->name)) | ||
142 | break; | ||
143 | spin_unlock(&proc_subdir_lock); | ||
144 | if (ent != NULL) { | ||
145 | printk(KERN_WARNING "device-tree: property \"%s\" name" | ||
146 | " conflicts with node in %s\n", pp->name, | ||
147 | np->full_name); | ||
148 | continue; | ||
149 | } | ||
150 | 207 | ||
151 | ent = __proc_device_tree_add_prop(de, pp); | 208 | ent = __proc_device_tree_add_prop(de, pp, p); |
152 | if (ent == 0) | 209 | if (ent == 0) |
153 | break; | 210 | break; |
154 | } | 211 | } |