LCOV - code coverage report
Current view: top level - common - devtable.c (source / functions) Hit Total Coverage
Test: a simple test Lines: 6 213 2.8 %
Date: 2024-06-05 20:10:43 Functions: 3 11 27.3 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13