Line data Source code
1 : /*
2 : * Copyright (C) 2008 Nokia Corporation.
3 : *
4 : * This program is free software; you can redistribute it and/or modify it
5 : * under the terms of the GNU General Public License version 2 as published by
6 : * the Free Software Foundation.
7 : *
8 : * This program is distributed in the hope that it will be useful, but WITHOUT
9 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 : * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 : * more details.
12 : *
13 : * You should have received a copy of the GNU General Public License along with
14 : * this program; if not, write to the Free Software Foundation, Inc., 51
15 : * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 : *
17 : * Author: Artem Bityutskiy
18 : *
19 : * Part of the device table parsing code was taken from the mkfs.jffs2 utility.
20 : * The original author of that code is Erik Andersen, hence:
21 : * Copyright (C) 2001, 2002 Erik Andersen <andersen@codepoet.org>
22 : */
23 :
24 : /*
25 : * This file implemented device table support. Device table entries take the
26 : * form of:
27 : * <path> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
28 : * /dev/mem c 640 0 0 1 1 0 0 -
29 : *
30 : * Type can be one of:
31 : * f A regular file
32 : * d Directory
33 : * c Character special device file
34 : * b Block special device file
35 : * p Fifo (named pipe)
36 : * l Link
37 : *
38 : * Don't bother with hard links (special cases of regular files), or sockets
39 : * (why bother).
40 : *
41 : * Regular files must exist in the target root directory. If a char, block,
42 : * fifo, or directory does not exist, it will be created.
43 : *
44 : * Please, refer the device_table.txt file which can be found at MTD utilities
45 : * for more information about what the device table is.
46 : */
47 :
48 : #include <string.h>
49 : #include <ctype.h>
50 : #include <sys/stat.h>
51 : #include <sys/types.h>
52 : #include <sys/sysmacros.h>
53 :
54 : #include "devtable.h"
55 : #include "ubifs.h"
56 : #include "defs.h"
57 : #include "hashtable/hashtable.h"
58 : #include "hashtable/hashtable_itr.h"
59 :
60 : /*
61 : * The hash table which contains paths to files/directories/device nodes
62 : * referred to in the device table. For example, if the device table refers
63 : * "/dev/loop0", the @path_htbl will contain "/dev" element.
64 : */
65 : static struct hashtable *path_htbl;
66 :
67 : /* Hash function used for hash tables */
68 0 : static unsigned int r5_hash(void *s)
69 : {
70 0 : unsigned int a = 0;
71 0 : const signed char *str = s;
72 :
73 0 : while (*str) {
74 0 : a += *str << 4;
75 0 : a += *str >> 4;
76 0 : a *= 11;
77 0 : str++;
78 : }
79 :
80 0 : return a;
81 : }
82 :
83 : /*
84 : * Check whether 2 keys of a hash table are equivalent. The keys are path/file
85 : * names, so we simply use 'strcmp()'.
86 : */
87 0 : static int is_equivalent(void *k1, void *k2)
88 : {
89 0 : return !strcmp(k1, k2);
90 : }
91 :
92 : /**
93 : * separate_last - separate out the last path component
94 : * @buf: the path to split
95 : * @len: length of the @buf string
96 : * @path: the beginning of path is returned here
97 : * @name: the last path component is returned here
98 : *
99 : * This helper function separates out the the last component of the full path
100 : * string. For example, "/dev/loop" would be split on "/dev" and "loop". This
101 : * function allocates memory for @path and @name and return the result there.
102 : * Returns zero in case of success and a negative error code in case of
103 : * failure.
104 : */
105 0 : static int separate_last(const char *buf, int len, char **path, char **name)
106 : {
107 0 : int path_len = len, name_len;
108 0 : const char *p = buf + len, *n;
109 :
110 0 : while (*--p != '/')
111 0 : path_len -= 1;
112 :
113 : /* Drop the final '/' unless this is the root directory */
114 0 : name_len = len - path_len;
115 0 : n = buf + path_len;
116 0 : if (path_len > 1)
117 0 : path_len -= 1;
118 :
119 0 : *path = malloc(path_len + 1);
120 0 : if (!*path)
121 0 : return errmsg("cannot allocate %d bytes of memory",
122 : path_len + 1);
123 0 : memcpy(*path, buf, path_len);
124 0 : (*path)[path_len] = '\0';
125 :
126 0 : *name = malloc(name_len + 1);
127 0 : if (!*name) {
128 0 : free(*path);
129 0 : return errmsg("cannot allocate %d bytes of memory",
130 : name_len + 1);
131 : }
132 0 : memcpy(*name, n, name_len + 1);
133 :
134 0 : return 0;
135 : }
136 :
137 0 : static int interpret_table_entry(const char *line)
138 : {
139 0 : char buf[1024], type, *path = NULL, *name = NULL;
140 : int len;
141 0 : struct path_htbl_element *ph_elt = NULL;
142 0 : struct name_htbl_element *nh_elt = NULL;
143 0 : unsigned int mode = 0755, uid = 0, gid = 0, major = 0, minor = 0;
144 0 : unsigned int start = 0, increment = 0, count = 0;
145 :
146 0 : if (sscanf(line, "%1023s %c %o %u %u %u %u %u %u %u",
147 : buf, &type, &mode, &uid, &gid, &major, &minor,
148 : &start, &increment, &count) < 0)
149 0 : return sys_errmsg("sscanf failed");
150 :
151 0 : pr_debug("name %s, type %c, mode %o, uid %u, gid %u, major %u, "
152 : "minor %u, start %u, inc %u, cnt %u\n",
153 : buf, type, mode, uid, gid, major, minor, start,
154 : increment, count);
155 :
156 0 : len = strnlen(buf, 1024);
157 0 : if (len == 0)
158 0 : return errmsg("empty path");
159 0 : if (len == 1024)
160 0 : return errmsg("too long path");
161 :
162 0 : if (buf[0] != '/')
163 0 : return errmsg("device table entries require absolute paths");
164 0 : if (strstr(buf, "//"))
165 0 : return errmsg("'//' cannot be used in the path");
166 0 : if (len > 1 && buf[len - 1] == '/')
167 0 : return errmsg("do not put '/' at the end");
168 :
169 0 : if (strstr(buf, "/./") || strstr(buf, "/../") ||
170 0 : !strcmp(buf + len - 2, "/.") || !strcmp(buf + len - 3, "/.."))
171 0 : return errmsg("'.' and '..' cannot be used in the path");
172 :
173 0 : switch (type) {
174 0 : case 'd':
175 0 : mode |= S_IFDIR;
176 0 : break;
177 0 : case 'f':
178 0 : mode |= S_IFREG;
179 0 : break;
180 0 : case 'p':
181 0 : mode |= S_IFIFO;
182 0 : break;
183 0 : case 'c':
184 0 : mode |= S_IFCHR;
185 0 : break;
186 0 : case 'b':
187 0 : mode |= S_IFBLK;
188 0 : break;
189 0 : case 'l':
190 0 : mode |= S_IFLNK;
191 0 : if ((mode & 0777) != 0777)
192 0 : return errmsg("link permission must be 0777");
193 : break;
194 0 : default:
195 0 : return errmsg("unsupported file type '%c'", type);
196 : }
197 :
198 0 : if (separate_last(buf, len, &path, &name))
199 : return -1;
200 :
201 : /*
202 : * Check if this path already exist in the path hash table and add it
203 : * if it is not.
204 : */
205 0 : ph_elt = hashtable_search(path_htbl, path);
206 0 : if (!ph_elt) {
207 0 : pr_debug("inserting '%s' into path hash table\n", path);
208 0 : ph_elt = malloc(sizeof(struct path_htbl_element));
209 0 : if (!ph_elt) {
210 0 : errmsg("cannot allocate %zd bytes of memory",
211 : sizeof(struct path_htbl_element));
212 0 : goto out_free;
213 : }
214 :
215 0 : if (!hashtable_insert(path_htbl, path, ph_elt)) {
216 0 : errmsg("cannot insert into path hash table");
217 0 : goto out_free;
218 : }
219 :
220 0 : ph_elt->path = path;
221 0 : path = NULL;
222 0 : ph_elt->name_htbl = create_hashtable(128, &r5_hash,
223 : &is_equivalent);
224 0 : if (!ph_elt->name_htbl) {
225 0 : errmsg("cannot create name hash table");
226 0 : goto out_free;
227 : }
228 : }
229 :
230 0 : if (increment != 0 && count == 0) {
231 0 : errmsg("count cannot be zero if increment is non-zero");
232 0 : goto out_free;
233 : }
234 :
235 : /*
236 : * Add the file/directory/device node (last component of the path) to
237 : * the name hashtable. The name hashtable resides in the corresponding
238 : * path hashtable element.
239 : */
240 :
241 0 : if (count == 0) {
242 : /* This entry does not require any iterating */
243 0 : nh_elt = malloc(sizeof(struct name_htbl_element));
244 0 : if (!nh_elt) {
245 0 : errmsg("cannot allocate %zd bytes of memory",
246 : sizeof(struct name_htbl_element));
247 0 : goto out_free;
248 : }
249 :
250 0 : nh_elt->mode = mode;
251 0 : nh_elt->uid = uid;
252 0 : nh_elt->gid = gid;
253 0 : nh_elt->dev = makedev(major, minor);
254 :
255 0 : pr_debug("inserting '%s' into name hash table (major %d, minor %d)\n",
256 : name, major(nh_elt->dev), minor(nh_elt->dev));
257 :
258 0 : if (hashtable_search(ph_elt->name_htbl, name)) {
259 0 : errmsg("'%s' is referred twice", buf);
260 0 : goto out_free;
261 : }
262 :
263 0 : nh_elt->name = name;
264 0 : if (!hashtable_insert(ph_elt->name_htbl, name, nh_elt)) {
265 0 : errmsg("cannot insert into name hash table");
266 0 : goto out_free;
267 : }
268 : } else {
269 0 : int i, num = start + count, len = strlen(name) + 20;
270 : char *nm;
271 :
272 0 : for (i = start; i < num; i++) {
273 0 : nh_elt = malloc(sizeof(struct name_htbl_element));
274 0 : if (!nh_elt) {
275 0 : errmsg("cannot allocate %zd bytes of memory",
276 : sizeof(struct name_htbl_element));
277 0 : goto out_free;
278 : }
279 :
280 0 : nh_elt->mode = mode;
281 0 : nh_elt->uid = uid;
282 0 : nh_elt->gid = gid;
283 0 : nh_elt->dev = makedev(major, minor + (i - start) * increment);
284 :
285 0 : nm = malloc(len);
286 0 : if (!nm) {
287 0 : errmsg("cannot allocate %d bytes of memory", len);
288 0 : goto out_free;
289 : }
290 :
291 0 : sprintf(nm, "%s%d", name, i);
292 0 : nh_elt->name = nm;
293 :
294 0 : pr_debug("inserting '%s' into name hash table (major %d, minor %d)\n",
295 : nm, major(nh_elt->dev), minor(nh_elt->dev));
296 :
297 0 : if (hashtable_search(ph_elt->name_htbl, nm)) {
298 0 : errmsg("'%s' is referred twice", buf);
299 0 : free (nm);
300 0 : goto out_free;
301 : }
302 :
303 0 : if (!hashtable_insert(ph_elt->name_htbl, nm, nh_elt)) {
304 0 : errmsg("cannot insert into name hash table");
305 0 : free (nm);
306 0 : goto out_free;
307 : }
308 : }
309 0 : free(name);
310 : name = NULL;
311 : }
312 :
313 : return 0;
314 :
315 0 : out_free:
316 0 : free(ph_elt);
317 0 : free(nh_elt);
318 0 : free(path);
319 0 : free(name);
320 0 : return -1;
321 : }
322 :
323 : /**
324 : * parse_devtable - parse the device table.
325 : * @tbl_file: device table file name
326 : *
327 : * This function parses the device table and prepare the hash table which will
328 : * later be used by mkfs.ubifs to create the specified files/device nodes.
329 : * Returns zero in case of success and a negative error code in case of
330 : * failure.
331 : */
332 0 : int parse_devtable(const char *tbl_file)
333 : {
334 : FILE *f;
335 0 : char *line = NULL;
336 : struct stat st;
337 : size_t len;
338 :
339 0 : pr_debug("parsing device table file '%s'\n", tbl_file);
340 :
341 0 : path_htbl = create_hashtable(128, &r5_hash, &is_equivalent);
342 0 : if (!path_htbl)
343 0 : return errmsg("cannot create path hash table");
344 :
345 0 : f = fopen(tbl_file, "r");
346 0 : if (!f)
347 0 : return sys_errmsg("cannot open '%s'", tbl_file);
348 :
349 0 : if (fstat(fileno(f), &st) < 0) {
350 0 : sys_errmsg("cannot stat '%s'", tbl_file);
351 0 : goto out_close;
352 : }
353 :
354 0 : if (st.st_size < 10) {
355 0 : sys_errmsg("'%s' is too short", tbl_file);
356 0 : goto out_close;
357 : }
358 :
359 : /*
360 : * The general plan now is to read in one line at a time, check for
361 : * leading comment delimiters ('#'), then try and parse the line as a
362 : * device table
363 : */
364 0 : while (getline(&line, &len, f) != -1) {
365 : /* First trim off any white-space */
366 0 : len = strlen(line);
367 :
368 : /* Trim trailing white-space */
369 0 : while (len > 0 && isspace(line[len - 1]))
370 0 : line[--len] = '\0';
371 : /* Trim leading white-space */
372 0 : memmove(line, &line[strspn(line, " \n\r\t\v")], len);
373 :
374 : /* How long are we after trimming? */
375 0 : len = strlen(line);
376 :
377 : /* If this is not a comment line, try to interpret it */
378 0 : if (len && *line != '#') {
379 0 : if (interpret_table_entry(line)) {
380 0 : errmsg("cannot parse '%s'", line);
381 0 : goto out_close;
382 : }
383 : }
384 :
385 0 : free(line);
386 0 : line = NULL;
387 : }
388 :
389 0 : pr_debug("finished parsing\n");
390 0 : fclose(f);
391 0 : return 0;
392 :
393 0 : out_close:
394 0 : fclose(f);
395 0 : free_devtable_info();
396 0 : return -1;
397 : }
398 :
399 : /**
400 : * devtbl_find_path - find a path in the path hash table.
401 : * @path: UBIFS path to find.
402 : *
403 : * This looks up the path hash table. Returns the path hash table element
404 : * reference if @path was found and %NULL if not.
405 : */
406 17280 : struct path_htbl_element *devtbl_find_path(const char *path)
407 : {
408 17280 : if (!path_htbl)
409 : return NULL;
410 :
411 0 : return hashtable_search(path_htbl, (void *)path);
412 : }
413 :
414 : /**
415 : * devtbl_find_name - find a name in the name hash table.
416 : * @ph_etl: path hash table element to find at
417 : * @name: name to find
418 : *
419 : * This looks up the name hash table. Returns the name hash table element
420 : * reference if @name found and %NULL if not.
421 : */
422 0 : struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt,
423 : const char *name)
424 : {
425 0 : if (!path_htbl)
426 : return NULL;
427 :
428 0 : return hashtable_search(ph_elt->name_htbl, (void *)name);
429 : }
430 :
431 : /**
432 : * override_attributes - override inode attributes.
433 : * @st: struct stat object to containing the attributes to override
434 : * @ph_elt: path hash table element object
435 : * @nh_elt: name hash table element object containing the new values
436 : *
437 : * The device table file may override attributes like UID of files. For
438 : * example, the device table may contain a "/dev" entry, and the UBIFS FS on
439 : * the host may contain "/dev" directory. In this case the attributes of the
440 : * "/dev" directory inode has to be as the device table specifies.
441 : *
442 : * Note, the hash element is removed by this function as well.
443 : */
444 0 : int override_attributes(struct stat *st, struct path_htbl_element *ph_elt,
445 : struct name_htbl_element *nh_elt)
446 : {
447 0 : if (!path_htbl)
448 : return 0;
449 :
450 0 : if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode) ||
451 : S_ISFIFO(st->st_mode))
452 0 : return errmsg("%s/%s both exists at UBIFS root at host, "
453 : "and is referred from the device table",
454 : strcmp(ph_elt->path, "/") ? ph_elt->path : "",
455 : nh_elt->name);
456 :
457 0 : if ((st->st_mode & S_IFMT) != (nh_elt->mode & S_IFMT))
458 0 : return errmsg("%s/%s is referred from the device table also exists in "
459 : "the UBIFS root directory at host, but the file type is "
460 : "different", strcmp(ph_elt->path, "/") ? ph_elt->path : "",
461 : nh_elt->name);
462 :
463 0 : pr_debug("set UID %d, GID %d, mode %o for %s/%s as device table says\n",
464 : nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name);
465 :
466 0 : st->st_uid = nh_elt->uid;
467 0 : st->st_gid = nh_elt->gid;
468 0 : st->st_mode = nh_elt->mode;
469 :
470 0 : hashtable_remove(ph_elt->name_htbl, (void *)nh_elt->name);
471 0 : return 0;
472 : }
473 :
474 : /**
475 : * first_name_htbl_element - return first element of the name hash table.
476 : * @ph_elt: the path hash table the name hash table belongs to
477 : * @itr: double pointer to a 'struct hashtable_itr' object where the
478 : * information about further iterations is stored
479 : *
480 : * This function implements name hash table iteration together with
481 : * 'next_name_htbl_element()'. Returns the first name hash table element or
482 : * %NULL if the hash table is empty.
483 : */
484 : struct name_htbl_element *
485 16640 : first_name_htbl_element(struct path_htbl_element *ph_elt,
486 : struct hashtable_itr **itr)
487 : {
488 16640 : if (!path_htbl || !ph_elt || hashtable_count(ph_elt->name_htbl) == 0)
489 : return NULL;
490 :
491 0 : *itr = hashtable_iterator(ph_elt->name_htbl);
492 0 : return hashtable_iterator_value(*itr);
493 : }
494 :
495 : /**
496 : * first_name_htbl_element - return next element of the name hash table.
497 : * @ph_elt: the path hash table the name hash table belongs to
498 : * @itr: double pointer to a 'struct hashtable_itr' object where the
499 : * information about further iterations is stored
500 : *
501 : * This function implements name hash table iteration together with
502 : * 'first_name_htbl_element()'. Returns the next name hash table element or
503 : * %NULL if there are no more elements.
504 : */
505 : struct name_htbl_element *
506 0 : next_name_htbl_element(struct path_htbl_element *ph_elt,
507 : struct hashtable_itr **itr)
508 : {
509 0 : if (!path_htbl || !ph_elt || !hashtable_iterator_advance(*itr))
510 : return NULL;
511 :
512 0 : return hashtable_iterator_value(*itr);
513 : }
514 :
515 : /**
516 : * free_devtable_info - free device table information.
517 : *
518 : * This function frees the path hash table and the name hash tables.
519 : */
520 640 : void free_devtable_info(void)
521 : {
522 : struct hashtable_itr *ph_itr;
523 : struct path_htbl_element *ph_elt;
524 :
525 640 : if (!path_htbl)
526 : return;
527 :
528 0 : if (hashtable_count(path_htbl) > 0) {
529 0 : ph_itr = hashtable_iterator(path_htbl);
530 : do {
531 0 : ph_elt = hashtable_iterator_value(ph_itr);
532 : /*
533 : * Note, since we use the same string for the key and
534 : * @name in the name hash table elements, we do not
535 : * have to iterate name hash table because @name memory
536 : * will be freed when freeing the key.
537 : */
538 0 : hashtable_destroy(ph_elt->name_htbl, 1);
539 0 : } while (hashtable_iterator_advance(ph_itr));
540 0 : free(ph_itr);
541 : }
542 0 : hashtable_destroy(path_htbl, 1);
543 : }
|