| /* |
| * Copyright (C) 2008 Nokia Corporation. |
| * Copyright (C) 2008 University of Szeged, Hungary |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 as published by |
| * the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * this program; if not, write to the Free Software Foundation, Inc., 51 |
| * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| * Authors: Adrian Hunter |
| * Artem Bityutskiy |
| * Zoltan Sogor |
| */ |
| |
| #define _XOPEN_SOURCE 500 /* For realpath() */ |
| |
| #include "mkfs.ubifs.h" |
| #include <crc32.h> |
| #include "common.h" |
| |
| /* Size (prime number) of hash table for link counting */ |
| #define HASH_TABLE_SIZE 10099 |
| |
| /* The node buffer must allow for worst case compression */ |
| #define NODE_BUFFER_SIZE (UBIFS_DATA_NODE_SZ + \ |
| UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR) |
| |
| /* Default time granularity in nanoseconds */ |
| #define DEFAULT_TIME_GRAN 1000000000 |
| |
| /** |
| * struct idx_entry - index entry. |
| * @next: next index entry (NULL at end of list) |
| * @prev: previous index entry (NULL at beginning of list) |
| * @key: key |
| * @name: directory entry name used for sorting colliding keys by name |
| * @lnum: LEB number |
| * @offs: offset |
| * @len: length |
| * |
| * The index is recorded as a linked list which is sorted and used to create |
| * the bottom level of the on-flash index tree. The remaining levels of the |
| * index tree are each built from the level below. |
| */ |
| struct idx_entry { |
| struct idx_entry *next; |
| struct idx_entry *prev; |
| union ubifs_key key; |
| char *name; |
| int lnum; |
| int offs; |
| int len; |
| }; |
| |
| /** |
| * struct inum_mapping - inode number mapping for link counting. |
| * @next: next inum_mapping (NULL at end of list) |
| * @prev: previous inum_mapping (NULL at beginning of list) |
| * @dev: source device on which the source inode number resides |
| * @inum: source inode number of the file |
| * @use_inum: target inode number of the file |
| * @use_nlink: number of links |
| * @path_name: a path name of the file |
| * @st: struct stat object containing inode attributes which have to be used |
| * when the inode is being created (actually only UID, GID, access |
| * mode, major and minor device numbers) |
| * |
| * If a file has more than one hard link, then the number of hard links that |
| * exist in the source directory hierarchy must be counted to exclude the |
| * possibility that the file is linked from outside the source directory |
| * hierarchy. |
| * |
| * The inum_mappings are stored in a hash_table of linked lists. |
| */ |
| struct inum_mapping { |
| struct inum_mapping *next; |
| struct inum_mapping *prev; |
| dev_t dev; |
| ino_t inum; |
| ino_t use_inum; |
| unsigned int use_nlink; |
| char *path_name; |
| struct stat st; |
| }; |
| |
| /* |
| * Because we copy functions from the kernel, we use a subset of the UBIFS |
| * file-system description object struct ubifs_info. |
| */ |
| struct ubifs_info info_; |
| static struct ubifs_info *c = &info_; |
| static libubi_t ubi; |
| |
| /* Debug levels are: 0 (none), 1 (statistics), 2 (files) ,3 (more details) */ |
| int debug_level; |
| int verbose; |
| int yes; |
| |
| static char *root; |
| static int root_len; |
| static struct stat root_st; |
| static char *output; |
| static int out_fd; |
| static int out_ubi; |
| static int squash_owner; |
| |
| /* The 'head' (position) which nodes are written */ |
| static int head_lnum; |
| static int head_offs; |
| static int head_flags; |
| |
| /* The index list */ |
| static struct idx_entry *idx_list_first; |
| static struct idx_entry *idx_list_last; |
| static size_t idx_cnt; |
| |
| /* Global buffers */ |
| static void *leb_buf; |
| static void *node_buf; |
| static void *block_buf; |
| |
| /* Hash table for inode link counting */ |
| static struct inum_mapping **hash_table; |
| |
| /* Inode creation sequence number */ |
| static unsigned long long creat_sqnum; |
| |
| static const char *optstring = "d:r:m:o:D:yh?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQq"; |
| |
| static const struct option longopts[] = { |
| {"root", 1, NULL, 'r'}, |
| {"min-io-size", 1, NULL, 'm'}, |
| {"leb-size", 1, NULL, 'e'}, |
| {"max-leb-cnt", 1, NULL, 'c'}, |
| {"output", 1, NULL, 'o'}, |
| {"devtable", 1, NULL, 'D'}, |
| {"yes", 0, NULL, 'y'}, |
| {"help", 0, NULL, 'h'}, |
| {"verbose", 0, NULL, 'v'}, |
| {"version", 0, NULL, 'V'}, |
| {"debug-level", 1, NULL, 'g'}, |
| {"jrn-size", 1, NULL, 'j'}, |
| {"reserved", 1, NULL, 'R'}, |
| {"compr", 1, NULL, 'x'}, |
| {"favor-percent", 1, NULL, 'X'}, |
| {"fanout", 1, NULL, 'f'}, |
| {"space-fixup", 0, NULL, 'F'}, |
| {"keyhash", 1, NULL, 'k'}, |
| {"log-lebs", 1, NULL, 'l'}, |
| {"orph-lebs", 1, NULL, 'p'}, |
| {"squash-uids" , 0, NULL, 'U'}, |
| {NULL, 0, NULL, 0} |
| }; |
| |
| static const char *helptext = |
| "Usage: mkfs.ubifs [OPTIONS] target\n" |
| "Make a UBIFS file system image from an existing directory tree\n\n" |
| "Examples:\n" |
| "Build file system from directory /opt/img, writting the result in the ubifs.img file\n" |
| "\tmkfs.ubifs -m 512 -e 128KiB -c 100 -r /opt/img ubifs.img\n" |
| "The same, but writting directly to an UBI volume\n" |
| "\tmkfs.ubifs -r /opt/img /dev/ubi0_0\n" |
| "Creating an empty UBIFS filesystem on an UBI volume\n" |
| "\tmkfs.ubifs /dev/ubi0_0\n\n" |
| "Options:\n" |
| "-r, -d, --root=DIR build file system from directory DIR\n" |
| "-m, --min-io-size=SIZE minimum I/O unit size\n" |
| "-e, --leb-size=SIZE logical erase block size\n" |
| "-c, --max-leb-cnt=COUNT maximum logical erase block count\n" |
| "-o, --output=FILE output to FILE\n" |
| "-j, --jrn-size=SIZE journal size\n" |
| "-R, --reserved=SIZE how much space should be reserved for the super-user\n" |
| "-x, --compr=TYPE compression type - \"lzo\", \"favor_lzo\", \"zlib\" or\n" |
| " \"none\" (default: \"lzo\")\n" |
| "-X, --favor-percent may only be used with favor LZO compression and defines\n" |
| " how many percent better zlib should compress to make\n" |
| " mkfs.ubifs use zlib instead of LZO (default 20%)\n" |
| "-f, --fanout=NUM fanout NUM (default: 8)\n" |
| "-F, --space-fixup file-system free space has to be fixed up on first mount\n" |
| " (requires kernel version 3.0 or greater)\n" |
| "-k, --keyhash=TYPE key hash type - \"r5\" or \"test\" (default: \"r5\")\n" |
| "-p, --orph-lebs=COUNT count of erase blocks for orphans (default: 1)\n" |
| "-D, --devtable=FILE use device table FILE\n" |
| "-U, --squash-uids squash owners making all files owned by root\n" |
| "-l, --log-lebs=COUNT count of erase blocks for the log (used only for\n" |
| " debugging)\n" |
| "-y, --yes assume the answer is \"yes\" for all questions\n" |
| "-v, --verbose verbose operation\n" |
| "-V, --version display version information\n" |
| "-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n" |
| " 2 - files, 3 - more details)\n" |
| "-h, --help display this help text\n\n" |
| "Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n" |
| "Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n" |
| "If you specify \"lzo\" or \"zlib\" compressors, mkfs.ubifs will use this compressor\n" |
| "for all data. The \"none\" disables any data compression. The \"favor_lzo\" is not\n" |
| "really a separate compressor. It is just a method of combining \"lzo\" and \"zlib\"\n" |
| "compressors. Namely, mkfs.ubifs tries to compress data with both \"lzo\" and \"zlib\"\n" |
| "compressors, then it compares which compressor is better. If \"zlib\" compresses 20\n" |
| "or more percent better than \"lzo\", mkfs.ubifs chooses \"lzo\", otherwise it chooses\n" |
| "\"zlib\". The \"--favor-percent\" may specify arbitrary threshold instead of the\n" |
| "default 20%.\n\n" |
| "The -F parameter is used to set the \"fix up free space\" flag in the superblock,\n" |
| "which forces UBIFS to \"fixup\" all the free space which it is going to use. This\n" |
| "option is useful to work-around the problem of double free space programming: if the\n" |
| "flasher program which flashes the UBI image is unable to skip NAND pages containing\n" |
| "only 0xFF bytes, the effect is that some NAND pages are written to twice - first time\n" |
| "when flashing the image and the second time when UBIFS is mounted and writes useful\n" |
| "data there. A proper UBI-aware flasher should skip such NAND pages, though. Note, this\n" |
| "flag may make the first mount very slow, because the \"free space fixup\" procedure\n" |
| "takes time. This feature is supported by the Linux kernel starting from version 3.0.\n"; |
| |
| /** |
| * make_path - make a path name from a directory and a name. |
| * @dir: directory path name |
| * @name: name |
| */ |
| static char *make_path(const char *dir, const char *name) |
| { |
| char *s; |
| |
| s = malloc(strlen(dir) + strlen(name) + 2); |
| if (!s) |
| return NULL; |
| strcpy(s, dir); |
| if (dir[strlen(dir) - 1] != '/') |
| strcat(s, "/"); |
| strcat(s, name); |
| return s; |
| } |
| |
| /** |
| * is_contained - determine if a file is beneath a directory. |
| * @file: file path name |
| * @dir: directory path name |
| * |
| * This function returns %1 if @file is accessible from the @dir directory and |
| * %0 otherwise. In case of error, returns %-1. |
| */ |
| static int is_contained(const char *file, const char *dir) |
| { |
| char *real_file = NULL; |
| char *real_dir = NULL; |
| char *file_base, *copy; |
| int ret = -1; |
| |
| /* Make a copy of the file path because 'dirname()' can modify it */ |
| copy = strdup(file); |
| if (!copy) |
| return -1; |
| file_base = dirname(copy); |
| |
| /* Turn the paths into the canonical form */ |
| real_file = malloc(PATH_MAX); |
| if (!real_file) |
| goto out_free; |
| |
| real_dir = malloc(PATH_MAX); |
| if (!real_dir) |
| goto out_free; |
| |
| if (!realpath(file_base, real_file)) { |
| perror("Could not canonicalize file path"); |
| goto out_free; |
| } |
| if (!realpath(dir, real_dir)) { |
| perror("Could not canonicalize directory"); |
| goto out_free; |
| } |
| |
| ret = !!strstr(real_file, real_dir); |
| |
| out_free: |
| free(copy); |
| free(real_file); |
| free(real_dir); |
| return ret; |
| } |
| |
| /** |
| * calc_min_log_lebs - calculate the minimum number of log LEBs needed. |
| * @max_bud_bytes: journal size (buds only) |
| */ |
| static int calc_min_log_lebs(unsigned long long max_bud_bytes) |
| { |
| int buds, log_lebs; |
| unsigned long long log_size; |
| |
| buds = (max_bud_bytes + c->leb_size - 1) / c->leb_size; |
| log_size = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); |
| log_size *= buds; |
| log_size += ALIGN(UBIFS_CS_NODE_SZ + |
| UBIFS_REF_NODE_SZ * (c->jhead_cnt + 2), |
| c->min_io_size); |
| log_lebs = (log_size + c->leb_size - 1) / c->leb_size; |
| log_lebs += 1; |
| return log_lebs; |
| } |
| |
| /** |
| * add_space_overhead - add UBIFS overhead. |
| * @size: flash space which should be visible to the user |
| * |
| * UBIFS has overhead, and if we need to reserve @size bytes for the user data, |
| * we have to reserve more flash space, to compensate the overhead. This |
| * function calculates and returns the amount of physical flash space which |
| * should be reserved to provide @size bytes for the user. |
| */ |
| static long long add_space_overhead(long long size) |
| { |
| int divisor, factor, f, max_idx_node_sz; |
| |
| /* |
| * Do the opposite to what the 'ubifs_reported_space()' kernel UBIFS |
| * function does. |
| */ |
| max_idx_node_sz = ubifs_idx_node_sz(c, c->fanout); |
| f = c->fanout > 3 ? c->fanout >> 1 : 2; |
| divisor = UBIFS_BLOCK_SIZE; |
| factor = UBIFS_MAX_DATA_NODE_SZ; |
| factor += (max_idx_node_sz * 3) / (f - 1); |
| size *= factor; |
| return size / divisor; |
| } |
| |
| static int validate_options(void) |
| { |
| int tmp; |
| |
| if (!output) |
| return err_msg("no output file or UBI volume specified"); |
| if (root) { |
| tmp = is_contained(output, root); |
| if (tmp < 0) |
| return err_msg("failed to perform output file root check"); |
| else if (tmp) |
| return err_msg("output file cannot be in the UBIFS root " |
| "directory"); |
| } |
| if (!is_power_of_2(c->min_io_size)) |
| return err_msg("min. I/O unit size should be power of 2"); |
| if (c->leb_size < c->min_io_size) |
| return err_msg("min. I/O unit cannot be larger than LEB size"); |
| if (c->leb_size < UBIFS_MIN_LEB_SZ) |
| return err_msg("too small LEB size %d, minimum is %d", |
| c->leb_size, UBIFS_MIN_LEB_SZ); |
| if (c->leb_size % c->min_io_size) |
| return err_msg("LEB should be multiple of min. I/O units"); |
| if (c->leb_size % 8) |
| return err_msg("LEB size has to be multiple of 8"); |
| if (c->leb_size > UBIFS_MAX_LEB_SZ) |
| return err_msg("too large LEB size %d, maximum is %d", |
| c->leb_size, UBIFS_MAX_LEB_SZ); |
| if (c->max_leb_cnt < UBIFS_MIN_LEB_CNT) |
| return err_msg("too low max. count of LEBs, minimum is %d", |
| UBIFS_MIN_LEB_CNT); |
| if (c->fanout < UBIFS_MIN_FANOUT) |
| return err_msg("too low fanout, minimum is %d", |
| UBIFS_MIN_FANOUT); |
| tmp = c->leb_size - UBIFS_IDX_NODE_SZ; |
| tmp /= UBIFS_BRANCH_SZ + UBIFS_MAX_KEY_LEN; |
| if (c->fanout > tmp) |
| return err_msg("too high fanout, maximum is %d", tmp); |
| if (c->log_lebs < UBIFS_MIN_LOG_LEBS) |
| return err_msg("too few log LEBs, minimum is %d", |
| UBIFS_MIN_LOG_LEBS); |
| if (c->log_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) |
| return err_msg("too many log LEBs, maximum is %d", |
| c->max_leb_cnt - UBIFS_MIN_LEB_CNT); |
| if (c->orph_lebs < UBIFS_MIN_ORPH_LEBS) |
| return err_msg("too few orphan LEBs, minimum is %d", |
| UBIFS_MIN_ORPH_LEBS); |
| if (c->orph_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) |
| return err_msg("too many orphan LEBs, maximum is %d", |
| c->max_leb_cnt - UBIFS_MIN_LEB_CNT); |
| tmp = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs; |
| tmp += c->orph_lebs + 4; |
| if (tmp > c->max_leb_cnt) |
| return err_msg("too low max. count of LEBs, expected at " |
| "least %d", tmp); |
| tmp = calc_min_log_lebs(c->max_bud_bytes); |
| if (c->log_lebs < calc_min_log_lebs(c->max_bud_bytes)) |
| return err_msg("too few log LEBs, expected at least %d", tmp); |
| if (c->rp_size >= ((long long)c->leb_size * c->max_leb_cnt) / 2) |
| return err_msg("too much reserved space %lld", c->rp_size); |
| return 0; |
| } |
| |
| /** |
| * get_multiplier - convert size specifier to an integer multiplier. |
| * @str: the size specifier string |
| * |
| * This function parses the @str size specifier, which may be one of |
| * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive |
| * size multiplier in case of success and %-1 in case of failure. |
| */ |
| static int get_multiplier(const char *str) |
| { |
| if (!str) |
| return 1; |
| |
| /* Remove spaces before the specifier */ |
| while (*str == ' ' || *str == '\t') |
| str += 1; |
| |
| if (!strcmp(str, "KiB")) |
| return 1024; |
| if (!strcmp(str, "MiB")) |
| return 1024 * 1024; |
| if (!strcmp(str, "GiB")) |
| return 1024 * 1024 * 1024; |
| |
| return -1; |
| } |
| |
| /** |
| * get_bytes - convert a string containing amount of bytes into an |
| * integer. |
| * @str: string to convert |
| * |
| * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' size |
| * specifiers. Returns positive amount of bytes in case of success and %-1 in |
| * case of failure. |
| */ |
| static long long get_bytes(const char *str) |
| { |
| char *endp; |
| long long bytes = strtoull(str, &endp, 0); |
| |
| if (endp == str || bytes < 0) |
| return err_msg("incorrect amount of bytes: \"%s\"", str); |
| |
| if (*endp != '\0') { |
| int mult = get_multiplier(endp); |
| |
| if (mult == -1) |
| return err_msg("bad size specifier: \"%s\" - " |
| "should be 'KiB', 'MiB' or 'GiB'", endp); |
| bytes *= mult; |
| } |
| |
| return bytes; |
| } |
| /** |
| * open_ubi - open the UBI volume. |
| * @node: name of the UBI volume character device to fetch information about |
| * |
| * Returns %0 in case of success and %-1 in case of failure |
| */ |
| static int open_ubi(const char *node) |
| { |
| struct stat st; |
| |
| if (stat(node, &st) || !S_ISCHR(st.st_mode)) |
| return -1; |
| |
| ubi = libubi_open(); |
| if (!ubi) |
| return -1; |
| if (ubi_get_vol_info(ubi, node, &c->vi)) |
| return -1; |
| if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di)) |
| return -1; |
| return 0; |
| } |
| |
| static int get_options(int argc, char**argv) |
| { |
| int opt, i; |
| const char *tbl_file = NULL; |
| struct stat st; |
| char *endp; |
| |
| c->fanout = 8; |
| c->orph_lebs = 1; |
| c->key_hash = key_r5_hash; |
| c->key_len = UBIFS_SK_LEN; |
| c->default_compr = UBIFS_COMPR_LZO; |
| c->favor_percent = 20; |
| c->lsave_cnt = 256; |
| c->leb_size = -1; |
| c->min_io_size = -1; |
| c->max_leb_cnt = -1; |
| c->max_bud_bytes = -1; |
| c->log_lebs = -1; |
| |
| while (1) { |
| opt = getopt_long(argc, argv, optstring, longopts, &i); |
| if (opt == -1) |
| break; |
| switch (opt) { |
| case 'r': |
| case 'd': |
| root_len = strlen(optarg); |
| root = malloc(root_len + 2); |
| if (!root) |
| return err_msg("cannot allocate memory"); |
| |
| /* |
| * The further code expects '/' at the end of the root |
| * UBIFS directory on the host. |
| */ |
| memcpy(root, optarg, root_len); |
| if (root[root_len - 1] != '/') |
| root[root_len++] = '/'; |
| root[root_len] = 0; |
| |
| /* Make sure the root directory exists */ |
| if (stat(root, &st)) |
| return sys_err_msg("bad root directory '%s'", |
| root); |
| break; |
| case 'm': |
| c->min_io_size = get_bytes(optarg); |
| if (c->min_io_size <= 0) |
| return err_msg("bad min. I/O size"); |
| break; |
| case 'e': |
| c->leb_size = get_bytes(optarg); |
| if (c->leb_size <= 0) |
| return err_msg("bad LEB size"); |
| break; |
| case 'c': |
| c->max_leb_cnt = get_bytes(optarg); |
| if (c->max_leb_cnt <= 0) |
| return err_msg("bad maximum LEB count"); |
| break; |
| case 'o': |
| output = xstrdup(optarg); |
| break; |
| case 'D': |
| tbl_file = optarg; |
| if (stat(tbl_file, &st) < 0) |
| return sys_err_msg("bad device table file '%s'", |
| tbl_file); |
| break; |
| case 'y': |
| yes = 1; |
| break; |
| case 'h': |
| case '?': |
| printf("%s", helptext); |
| exit(0); |
| case 'v': |
| verbose = 1; |
| break; |
| case 'V': |
| common_print_version(); |
| exit(0); |
| case 'g': |
| debug_level = strtol(optarg, &endp, 0); |
| if (*endp != '\0' || endp == optarg || |
| debug_level < 0 || debug_level > 3) |
| return err_msg("bad debugging level '%s'", |
| optarg); |
| break; |
| case 'f': |
| c->fanout = strtol(optarg, &endp, 0); |
| if (*endp != '\0' || endp == optarg || c->fanout <= 0) |
| return err_msg("bad fanout %s", optarg); |
| break; |
| case 'F': |
| c->space_fixup = 1; |
| break; |
| case 'l': |
| c->log_lebs = strtol(optarg, &endp, 0); |
| if (*endp != '\0' || endp == optarg || c->log_lebs <= 0) |
| return err_msg("bad count of log LEBs '%s'", |
| optarg); |
| break; |
| case 'p': |
| c->orph_lebs = strtol(optarg, &endp, 0); |
| if (*endp != '\0' || endp == optarg || |
| c->orph_lebs <= 0) |
| return err_msg("bad orphan LEB count '%s'", |
| optarg); |
| break; |
| case 'k': |
| if (strcmp(optarg, "r5") == 0) { |
| c->key_hash = key_r5_hash; |
| c->key_hash_type = UBIFS_KEY_HASH_R5; |
| } else if (strcmp(optarg, "test") == 0) { |
| c->key_hash = key_test_hash; |
| c->key_hash_type = UBIFS_KEY_HASH_TEST; |
| } else |
| return err_msg("bad key hash"); |
| break; |
| case 'x': |
| if (strcmp(optarg, "favor_lzo") == 0) |
| c->favor_lzo = 1; |
| else if (strcmp(optarg, "zlib") == 0) |
| c->default_compr = UBIFS_COMPR_ZLIB; |
| else if (strcmp(optarg, "none") == 0) |
| c->default_compr = UBIFS_COMPR_NONE; |
| else if (strcmp(optarg, "lzo") != 0) |
| return err_msg("bad compressor name"); |
| break; |
| case 'X': |
| c->favor_percent = strtol(optarg, &endp, 0); |
| if (*endp != '\0' || endp == optarg || |
| c->favor_percent <= 0 || c->favor_percent >= 100) |
| return err_msg("bad favor LZO percent '%s'", |
| optarg); |
| break; |
| case 'j': |
| c->max_bud_bytes = get_bytes(optarg); |
| if (c->max_bud_bytes <= 0) |
| return err_msg("bad maximum amount of buds"); |
| break; |
| case 'R': |
| c->rp_size = get_bytes(optarg); |
| if (c->rp_size < 0) |
| return err_msg("bad reserved bytes count"); |
| break; |
| case 'U': |
| squash_owner = 1; |
| break; |
| } |
| } |
| |
| if (optind != argc && !output) |
| output = xstrdup(argv[optind]); |
| |
| if (!output) |
| return err_msg("not output device or file specified"); |
| |
| out_ubi = !open_ubi(output); |
| |
| if (out_ubi) { |
| c->min_io_size = c->di.min_io_size; |
| c->leb_size = c->vi.leb_size; |
| if (c->max_leb_cnt == -1) |
| c->max_leb_cnt = c->vi.rsvd_lebs; |
| } |
| |
| if (c->min_io_size == -1) |
| return err_msg("min. I/O unit was not specified " |
| "(use -h for help)"); |
| |
| if (c->leb_size == -1) |
| return err_msg("LEB size was not specified (use -h for help)"); |
| |
| if (c->max_leb_cnt == -1) |
| return err_msg("Maximum count of LEBs was not specified " |
| "(use -h for help)"); |
| |
| if (c->max_bud_bytes == -1) { |
| int lebs; |
| |
| lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; |
| lebs -= c->orph_lebs; |
| if (c->log_lebs != -1) |
| lebs -= c->log_lebs; |
| else |
| lebs -= UBIFS_MIN_LOG_LEBS; |
| /* |
| * We do not know lprops geometry so far, so assume minimum |
| * count of lprops LEBs. |
| */ |
| lebs -= UBIFS_MIN_LPT_LEBS; |
| /* Make the journal about 12.5% of main area lebs */ |
| c->max_bud_bytes = (lebs / 8) * (long long)c->leb_size; |
| /* Make the max journal size 8MiB */ |
| if (c->max_bud_bytes > 8 * 1024 * 1024) |
| c->max_bud_bytes = 8 * 1024 * 1024; |
| if (c->max_bud_bytes < 4 * c->leb_size) |
| c->max_bud_bytes = 4 * c->leb_size; |
| } |
| |
| if (c->log_lebs == -1) { |
| c->log_lebs = calc_min_log_lebs(c->max_bud_bytes); |
| c->log_lebs += 2; |
| } |
| |
| if (c->min_io_size < 8) |
| c->min_io_size = 8; |
| c->rp_size = add_space_overhead(c->rp_size); |
| |
| if (verbose) { |
| printf("mkfs.ubifs\n"); |
| printf("\troot: %s\n", root); |
| printf("\tmin_io_size: %d\n", c->min_io_size); |
| printf("\tleb_size: %d\n", c->leb_size); |
| printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt); |
| printf("\toutput: %s\n", output); |
| printf("\tjrn_size: %llu\n", c->max_bud_bytes); |
| printf("\treserved: %llu\n", c->rp_size); |
| switch (c->default_compr) { |
| case UBIFS_COMPR_LZO: |
| printf("\tcompr: lzo\n"); |
| break; |
| case UBIFS_COMPR_ZLIB: |
| printf("\tcompr: zlib\n"); |
| break; |
| case UBIFS_COMPR_NONE: |
| printf("\tcompr: none\n"); |
| break; |
| } |
| printf("\tkeyhash: %s\n", (c->key_hash == key_r5_hash) ? |
| "r5" : "test"); |
| printf("\tfanout: %d\n", c->fanout); |
| printf("\torph_lebs: %d\n", c->orph_lebs); |
| printf("\tspace_fixup: %d\n", c->space_fixup); |
| } |
| |
| if (validate_options()) |
| return -1; |
| |
| if (tbl_file && parse_devtable(tbl_file)) |
| return err_msg("cannot parse device table file '%s'", tbl_file); |
| |
| return 0; |
| } |
| |
| /** |
| * prepare_node - fill in the common header. |
| * @node: node |
| * @len: node length |
| */ |
| static void prepare_node(void *node, int len) |
| { |
| uint32_t crc; |
| struct ubifs_ch *ch = node; |
| |
| ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); |
| ch->len = cpu_to_le32(len); |
| ch->group_type = UBIFS_NO_NODE_GROUP; |
| ch->sqnum = cpu_to_le64(++c->max_sqnum); |
| ch->padding[0] = ch->padding[1] = 0; |
| crc = mtd_crc32(UBIFS_CRC32_INIT, node + 8, len - 8); |
| ch->crc = cpu_to_le32(crc); |
| } |
| |
| /** |
| * write_leb - copy the image of a LEB to the output target. |
| * @lnum: LEB number |
| * @len: length of data in the buffer |
| * @buf: buffer (must be at least c->leb_size bytes) |
| */ |
| int write_leb(int lnum, int len, void *buf) |
| { |
| off_t pos = (off_t)lnum * c->leb_size; |
| |
| dbg_msg(3, "LEB %d len %d", lnum, len); |
| memset(buf + len, 0xff, c->leb_size - len); |
| if (out_ubi) |
| if (ubi_leb_change_start(ubi, out_fd, lnum, c->leb_size)) |
| return sys_err_msg("ubi_leb_change_start failed"); |
| |
| if (lseek(out_fd, pos, SEEK_SET) != pos) |
| return sys_err_msg("lseek failed seeking %"PRIdoff_t, pos); |
| |
| if (write(out_fd, buf, c->leb_size) != c->leb_size) |
| return sys_err_msg("write failed writing %d bytes at pos %"PRIdoff_t, |
| c->leb_size, pos); |
| |
| return 0; |
| } |
| |
| /** |
| * write_empty_leb - copy the image of an empty LEB to the output target. |
| * @lnum: LEB number |
| */ |
| static int write_empty_leb(int lnum) |
| { |
| return write_leb(lnum, 0, leb_buf); |
| } |
| |
| /** |
| * do_pad - pad a buffer to the minimum I/O size. |
| * @buf: buffer |
| * @len: buffer length |
| */ |
| static int do_pad(void *buf, int len) |
| { |
| int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size); |
| uint32_t crc; |
| |
| memset(buf + len, 0xff, alen - len); |
| pad_len = wlen - alen; |
| dbg_msg(3, "len %d pad_len %d", len, pad_len); |
| buf += alen; |
| if (pad_len >= (int)UBIFS_PAD_NODE_SZ) { |
| struct ubifs_ch *ch = buf; |
| struct ubifs_pad_node *pad_node = buf; |
| |
| ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); |
| ch->node_type = UBIFS_PAD_NODE; |
| ch->group_type = UBIFS_NO_NODE_GROUP; |
| ch->padding[0] = ch->padding[1] = 0; |
| ch->sqnum = cpu_to_le64(0); |
| ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); |
| |
| pad_len -= UBIFS_PAD_NODE_SZ; |
| pad_node->pad_len = cpu_to_le32(pad_len); |
| |
| crc = mtd_crc32(UBIFS_CRC32_INIT, buf + 8, |
| UBIFS_PAD_NODE_SZ - 8); |
| ch->crc = cpu_to_le32(crc); |
| |
| memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len); |
| } else if (pad_len > 0) |
| memset(buf, UBIFS_PADDING_BYTE, pad_len); |
| |
| return wlen; |
| } |
| |
| /** |
| * write_node - write a node to a LEB. |
| * @node: node |
| * @len: node length |
| * @lnum: LEB number |
| */ |
| static int write_node(void *node, int len, int lnum) |
| { |
| prepare_node(node, len); |
| |
| memcpy(leb_buf, node, len); |
| |
| len = do_pad(leb_buf, len); |
| |
| return write_leb(lnum, len, leb_buf); |
| } |
| |
| /** |
| * calc_dark - calculate LEB dark space size. |
| * @c: the UBIFS file-system description object |
| * @spc: amount of free and dirty space in the LEB |
| * |
| * This function calculates amount of dark space in an LEB which has @spc bytes |
| * of free and dirty space. Returns the calculations result. |
| * |
| * Dark space is the space which is not always usable - it depends on which |
| * nodes are written in which order. E.g., if an LEB has only 512 free bytes, |
| * it is dark space, because it cannot fit a large data node. So UBIFS cannot |
| * count on this LEB and treat these 512 bytes as usable because it is not true |
| * if, for example, only big chunks of uncompressible data will be written to |
| * the FS. |
| */ |
| static int calc_dark(struct ubifs_info *c, int spc) |
| { |
| if (spc < c->dark_wm) |
| return spc; |
| |
| /* |
| * If we have slightly more space then the dark space watermark, we can |
| * anyway safely assume it we'll be able to write a node of the |
| * smallest size there. |
| */ |
| if (spc - c->dark_wm < (int)MIN_WRITE_SZ) |
| return spc - MIN_WRITE_SZ; |
| |
| return c->dark_wm; |
| } |
| |
| /** |
| * set_lprops - set the LEB property values for a LEB. |
| * @lnum: LEB number |
| * @offs: end offset of data in the LEB |
| * @flags: LEB property flags |
| */ |
| static void set_lprops(int lnum, int offs, int flags) |
| { |
| int i = lnum - c->main_first, free, dirty; |
| int a = max_t(int, c->min_io_size, 8); |
| |
| free = c->leb_size - ALIGN(offs, a); |
| dirty = c->leb_size - free - ALIGN(offs, 8); |
| dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty, |
| flags); |
| if (i < c->main_lebs) { |
| c->lpt[i].free = free; |
| c->lpt[i].dirty = dirty; |
| c->lpt[i].flags = flags; |
| } |
| c->lst.total_free += free; |
| c->lst.total_dirty += dirty; |
| if (flags & LPROPS_INDEX) |
| c->lst.idx_lebs += 1; |
| else { |
| int spc; |
| |
| spc = free + dirty; |
| if (spc < c->dead_wm) |
| c->lst.total_dead += spc; |
| else |
| c->lst.total_dark += calc_dark(c, spc); |
| c->lst.total_used += c->leb_size - spc; |
| } |
| } |
| |
| /** |
| * add_to_index - add a node key and position to the index. |
| * @key: node key |
| * @lnum: node LEB number |
| * @offs: node offset |
| * @len: node length |
| */ |
| static int add_to_index(union ubifs_key *key, char *name, int lnum, int offs, |
| int len) |
| { |
| struct idx_entry *e; |
| |
| dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len); |
| e = malloc(sizeof(struct idx_entry)); |
| if (!e) |
| return err_msg("out of memory"); |
| e->next = NULL; |
| e->prev = idx_list_last; |
| e->key = *key; |
| e->name = name; |
| e->lnum = lnum; |
| e->offs = offs; |
| e->len = len; |
| if (!idx_list_first) |
| idx_list_first = e; |
| if (idx_list_last) |
| idx_list_last->next = e; |
| idx_list_last = e; |
| idx_cnt += 1; |
| return 0; |
| } |
| |
| /** |
| * flush_nodes - write the current head and move the head to the next LEB. |
| */ |
| static int flush_nodes(void) |
| { |
| int len, err; |
| |
| if (!head_offs) |
| return 0; |
| len = do_pad(leb_buf, head_offs); |
| err = write_leb(head_lnum, len, leb_buf); |
| if (err) |
| return err; |
| set_lprops(head_lnum, head_offs, head_flags); |
| head_lnum += 1; |
| head_offs = 0; |
| return 0; |
| } |
| |
| /** |
| * reserve_space - reserve space for a node on the head. |
| * @len: node length |
| * @lnum: LEB number is returned here |
| * @offs: offset is returned here |
| */ |
| static int reserve_space(int len, int *lnum, int *offs) |
| { |
| int err; |
| |
| if (len > c->leb_size - head_offs) { |
| err = flush_nodes(); |
| if (err) |
| return err; |
| } |
| *lnum = head_lnum; |
| *offs = head_offs; |
| head_offs += ALIGN(len, 8); |
| return 0; |
| } |
| |
| /** |
| * add_node - write a node to the head. |
| * @key: node key |
| * @node: node |
| * @len: node length |
| */ |
| static int add_node(union ubifs_key *key, char *name, void *node, int len) |
| { |
| int err, lnum, offs; |
| |
| prepare_node(node, len); |
| |
| err = reserve_space(len, &lnum, &offs); |
| if (err) |
| return err; |
| |
| memcpy(leb_buf + offs, node, len); |
| memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); |
| |
| add_to_index(key, name, lnum, offs, len); |
| |
| return 0; |
| } |
| |
| /** |
| * add_inode_with_data - write an inode. |
| * @st: stat information of source inode |
| * @inum: target inode number |
| * @data: inode data (for special inodes e.g. symlink path etc) |
| * @data_len: inode data length |
| * @flags: source inode flags |
| */ |
| static int add_inode_with_data(struct stat *st, ino_t inum, void *data, |
| unsigned int data_len, int flags) |
| { |
| struct ubifs_ino_node *ino = node_buf; |
| union ubifs_key key; |
| int len, use_flags = 0; |
| |
| if (c->default_compr != UBIFS_COMPR_NONE) |
| use_flags |= UBIFS_COMPR_FL; |
| if (flags & FS_COMPR_FL) |
| use_flags |= UBIFS_COMPR_FL; |
| if (flags & FS_SYNC_FL) |
| use_flags |= UBIFS_SYNC_FL; |
| if (flags & FS_IMMUTABLE_FL) |
| use_flags |= UBIFS_IMMUTABLE_FL; |
| if (flags & FS_APPEND_FL) |
| use_flags |= UBIFS_APPEND_FL; |
| if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode)) |
| use_flags |= UBIFS_DIRSYNC_FL; |
| |
| memset(ino, 0, UBIFS_INO_NODE_SZ); |
| |
| ino_key_init(&key, inum); |
| ino->ch.node_type = UBIFS_INO_NODE; |
| key_write(&key, &ino->key); |
| ino->creat_sqnum = cpu_to_le64(creat_sqnum); |
| ino->size = cpu_to_le64(st->st_size); |
| ino->nlink = cpu_to_le32(st->st_nlink); |
| /* |
| * The time fields are updated assuming the default time granularity |
| * of 1 second. To support finer granularities, utime() would be needed. |
| */ |
| ino->atime_sec = cpu_to_le64(st->st_atime); |
| ino->ctime_sec = cpu_to_le64(st->st_ctime); |
| ino->mtime_sec = cpu_to_le64(st->st_mtime); |
| ino->atime_nsec = 0; |
| ino->ctime_nsec = 0; |
| ino->mtime_nsec = 0; |
| ino->uid = cpu_to_le32(st->st_uid); |
| ino->gid = cpu_to_le32(st->st_gid); |
| ino->mode = cpu_to_le32(st->st_mode); |
| ino->flags = cpu_to_le32(use_flags); |
| ino->data_len = cpu_to_le32(data_len); |
| ino->compr_type = cpu_to_le16(c->default_compr); |
| if (data_len) |
| memcpy(&ino->data, data, data_len); |
| |
| len = UBIFS_INO_NODE_SZ + data_len; |
| |
| return add_node(&key, NULL, ino, len); |
| } |
| |
| /** |
| * add_inode - write an inode. |
| * @st: stat information of source inode |
| * @inum: target inode number |
| * @flags: source inode flags |
| */ |
| static int add_inode(struct stat *st, ino_t inum, int flags) |
| { |
| return add_inode_with_data(st, inum, NULL, 0, flags); |
| } |
| |
| /** |
| * add_dir_inode - write an inode for a directory. |
| * @dir: source directory |
| * @inum: target inode number |
| * @size: target directory size |
| * @nlink: target directory link count |
| * @st: struct stat object describing attributes (except size and nlink) of the |
| * target inode to create |
| * |
| * Note, this function may be called with %NULL @dir, when the directory which |
| * is being created does not exist at the host file system, but is defined by |
| * the device table. |
| */ |
| static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink, |
| struct stat *st) |
| { |
| int fd, flags = 0; |
| |
| st->st_size = size; |
| st->st_nlink = nlink; |
| |
| if (dir) { |
| fd = dirfd(dir); |
| if (fd == -1) |
| return sys_err_msg("dirfd failed"); |
| if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) |
| flags = 0; |
| } |
| |
| return add_inode(st, inum, flags); |
| } |
| |
| /** |
| * add_dev_inode - write an inode for a character or block device. |
| * @st: stat information of source inode |
| * @inum: target inode number |
| * @flags: source inode flags |
| */ |
| static int add_dev_inode(struct stat *st, ino_t inum, int flags) |
| { |
| union ubifs_dev_desc dev; |
| |
| dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev))); |
| return add_inode_with_data(st, inum, &dev, 8, flags); |
| } |
| |
| /** |
| * add_symlink_inode - write an inode for a symbolic link. |
| * @path_name: path name of symbolic link inode itself (not the link target) |
| * @st: stat information of source inode |
| * @inum: target inode number |
| * @flags: source inode flags |
| */ |
| static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum, |
| int flags) |
| { |
| char buf[UBIFS_MAX_INO_DATA + 2]; |
| ssize_t len; |
| |
| /* Take the symlink as is */ |
| len = readlink(path_name, buf, UBIFS_MAX_INO_DATA + 1); |
| if (len <= 0) |
| return sys_err_msg("readlink failed for %s", path_name); |
| if (len > UBIFS_MAX_INO_DATA) |
| return err_msg("symlink too long for %s", path_name); |
| |
| return add_inode_with_data(st, inum, buf, len, flags); |
| } |
| |
| /** |
| * add_dent_node - write a directory entry node. |
| * @dir_inum: target inode number of directory |
| * @name: directory entry name |
| * @inum: target inode number of the directory entry |
| * @type: type of the target inode |
| */ |
| static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum, |
| unsigned char type) |
| { |
| struct ubifs_dent_node *dent = node_buf; |
| union ubifs_key key; |
| struct qstr dname; |
| char *kname; |
| int len; |
| |
| dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, (unsigned long)inum, |
| (unsigned int)type, (unsigned long)dir_inum); |
| memset(dent, 0, UBIFS_DENT_NODE_SZ); |
| |
| dname.name = (void *)name; |
| dname.len = strlen(name); |
| |
| dent->ch.node_type = UBIFS_DENT_NODE; |
| |
| dent_key_init(c, &key, dir_inum, &dname); |
| key_write(&key, dent->key); |
| dent->inum = cpu_to_le64(inum); |
| dent->padding1 = 0; |
| dent->type = type; |
| dent->nlen = cpu_to_le16(dname.len); |
| memcpy(dent->name, dname.name, dname.len); |
| dent->name[dname.len] = '\0'; |
| |
| len = UBIFS_DENT_NODE_SZ + dname.len + 1; |
| |
| kname = strdup(name); |
| if (!kname) |
| return err_msg("cannot allocate memory"); |
| |
| return add_node(&key, kname, dent, len); |
| } |
| |
| /** |
| * lookup_inum_mapping - add an inode mapping for link counting. |
| * @dev: source device on which source inode number resides |
| * @inum: source inode number |
| */ |
| static struct inum_mapping *lookup_inum_mapping(dev_t dev, ino_t inum) |
| { |
| struct inum_mapping *im; |
| unsigned int k; |
| |
| k = inum % HASH_TABLE_SIZE; |
| im = hash_table[k]; |
| while (im) { |
| if (im->dev == dev && im->inum == inum) |
| return im; |
| im = im->next; |
| } |
| im = malloc(sizeof(struct inum_mapping)); |
| if (!im) |
| return NULL; |
| im->next = hash_table[k]; |
| im->prev = NULL; |
| im->dev = dev; |
| im->inum = inum; |
| im->use_inum = 0; |
| im->use_nlink = 0; |
| if (hash_table[k]) |
| hash_table[k]->prev = im; |
| hash_table[k] = im; |
| return im; |
| } |
| |
| /** |
| * all_zero - does a buffer contain only zero bytes. |
| * @buf: buffer |
| * @len: buffer length |
| */ |
| static int all_zero(void *buf, int len) |
| { |
| unsigned char *p = buf; |
| |
| while (len--) |
| if (*p++ != 0) |
| return 0; |
| return 1; |
| } |
| |
| /** |
| * add_file - write the data of a file and its inode to the output file. |
| * @path_name: source path name |
| * @st: source inode stat information |
| * @inum: target inode number |
| * @flags: source inode flags |
| */ |
| static int add_file(const char *path_name, struct stat *st, ino_t inum, |
| int flags) |
| { |
| struct ubifs_data_node *dn = node_buf; |
| void *buf = block_buf; |
| loff_t file_size = 0; |
| ssize_t ret, bytes_read; |
| union ubifs_key key; |
| int fd, dn_len, err, compr_type, use_compr; |
| unsigned int block_no = 0; |
| size_t out_len; |
| |
| fd = open(path_name, O_RDONLY | O_LARGEFILE); |
| if (fd == -1) |
| return sys_err_msg("failed to open file '%s'", path_name); |
| do { |
| /* Read next block */ |
| bytes_read = 0; |
| do { |
| ret = read(fd, buf + bytes_read, |
| UBIFS_BLOCK_SIZE - bytes_read); |
| if (ret == -1) { |
| sys_err_msg("failed to read file '%s'", |
| path_name); |
| close(fd); |
| return 1; |
| } |
| bytes_read += ret; |
| } while (ret != 0 && bytes_read != UBIFS_BLOCK_SIZE); |
| if (bytes_read == 0) |
| break; |
| file_size += bytes_read; |
| /* Skip holes */ |
| if (all_zero(buf, bytes_read)) { |
| block_no += 1; |
| continue; |
| } |
| /* Make data node */ |
| memset(dn, 0, UBIFS_DATA_NODE_SZ); |
| data_key_init(&key, inum, block_no++); |
| dn->ch.node_type = UBIFS_DATA_NODE; |
| key_write(&key, &dn->key); |
| dn->size = cpu_to_le32(bytes_read); |
| out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ; |
| if (c->default_compr == UBIFS_COMPR_NONE && |
| (flags & FS_COMPR_FL)) |
| use_compr = UBIFS_COMPR_LZO; |
| else |
| use_compr = c->default_compr; |
| compr_type = compress_data(buf, bytes_read, &dn->data, |
| &out_len, use_compr); |
| dn->compr_type = cpu_to_le16(compr_type); |
| dn_len = UBIFS_DATA_NODE_SZ + out_len; |
| /* Add data node to file system */ |
| err = add_node(&key, NULL, dn, dn_len); |
| if (err) { |
| close(fd); |
| return err; |
| } |
| } while (ret != 0); |
| if (close(fd) == -1) |
| return sys_err_msg("failed to close file '%s'", path_name); |
| if (file_size != st->st_size) |
| return err_msg("file size changed during writing file '%s'", |
| path_name); |
| return add_inode(st, inum, flags); |
| } |
| |
| /** |
| * add_non_dir - write a non-directory to the output file. |
| * @path_name: source path name |
| * @inum: target inode number is passed and returned here (due to link counting) |
| * @nlink: number of links if known otherwise zero |
| * @type: UBIFS inode type is returned here |
| * @st: struct stat object containing inode attributes which should be use when |
| * creating the UBIFS inode |
| */ |
| static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink, |
| unsigned char *type, struct stat *st) |
| { |
| int fd, flags = 0; |
| |
| dbg_msg(2, "%s", path_name); |
| |
| if (S_ISREG(st->st_mode)) { |
| fd = open(path_name, O_RDONLY); |
| if (fd == -1) |
| return sys_err_msg("failed to open file '%s'", |
| path_name); |
| if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) |
| flags = 0; |
| if (close(fd) == -1) |
| return sys_err_msg("failed to close file '%s'", |
| path_name); |
| *type = UBIFS_ITYPE_REG; |
| } else if (S_ISCHR(st->st_mode)) |
| *type = UBIFS_ITYPE_CHR; |
| else if (S_ISBLK(st->st_mode)) |
| *type = UBIFS_ITYPE_BLK; |
| else if (S_ISLNK(st->st_mode)) |
| *type = UBIFS_ITYPE_LNK; |
| else if (S_ISSOCK(st->st_mode)) |
| *type = UBIFS_ITYPE_SOCK; |
| else if (S_ISFIFO(st->st_mode)) |
| *type = UBIFS_ITYPE_FIFO; |
| else |
| return err_msg("file '%s' has unknown inode type", path_name); |
| |
| if (nlink) |
| st->st_nlink = nlink; |
| else if (st->st_nlink > 1) { |
| /* |
| * If the number of links is greater than 1, then add this file |
| * later when we know the number of links that we actually have. |
| * For now, we just put the inode mapping in the hash table. |
| */ |
| struct inum_mapping *im; |
| |
| im = lookup_inum_mapping(st->st_dev, st->st_ino); |
| if (!im) |
| return err_msg("out of memory"); |
| if (im->use_nlink == 0) { |
| /* New entry */ |
| im->use_inum = *inum; |
| im->use_nlink = 1; |
| im->path_name = malloc(strlen(path_name) + 1); |
| if (!im->path_name) |
| return err_msg("out of memory"); |
| strcpy(im->path_name, path_name); |
| } else { |
| /* Existing entry */ |
| *inum = im->use_inum; |
| im->use_nlink += 1; |
| /* Return unused inode number */ |
| c->highest_inum -= 1; |
| } |
| |
| memcpy(&im->st, st, sizeof(struct stat)); |
| return 0; |
| } else |
| st->st_nlink = 1; |
| |
| creat_sqnum = ++c->max_sqnum; |
| |
| if (S_ISREG(st->st_mode)) |
| return add_file(path_name, st, *inum, flags); |
| if (S_ISCHR(st->st_mode)) |
| return add_dev_inode(st, *inum, flags); |
| if (S_ISBLK(st->st_mode)) |
| return add_dev_inode(st, *inum, flags); |
| if (S_ISLNK(st->st_mode)) |
| return add_symlink_inode(path_name, st, *inum, flags); |
| if (S_ISSOCK(st->st_mode)) |
| return add_inode(st, *inum, flags); |
| if (S_ISFIFO(st->st_mode)) |
| return add_inode(st, *inum, flags); |
| |
| return err_msg("file '%s' has unknown inode type", path_name); |
| } |
| |
| /** |
| * add_directory - write a directory tree to the output file. |
| * @dir_name: directory path name |
| * @dir_inum: UBIFS inode number of directory |
| * @st: directory inode statistics |
| * @non_existing: non-zero if this function is called for a directory which |
| * does not exist on the host file-system and it is being |
| * created because it is defined in the device table file. |
| */ |
| static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, |
| int non_existing) |
| { |
| struct dirent *entry; |
| DIR *dir = NULL; |
| int err = 0; |
| loff_t size = UBIFS_INO_NODE_SZ; |
| char *name = NULL; |
| unsigned int nlink = 2; |
| struct path_htbl_element *ph_elt; |
| struct name_htbl_element *nh_elt = NULL; |
| struct hashtable_itr *itr; |
| ino_t inum; |
| unsigned char type; |
| unsigned long long dir_creat_sqnum = ++c->max_sqnum; |
| |
| dbg_msg(2, "%s", dir_name); |
| if (!non_existing) { |
| dir = opendir(dir_name); |
| if (dir == NULL) |
| return sys_err_msg("cannot open directory '%s'", |
| dir_name); |
| } |
| |
| /* |
| * Check whether this directory contains files which should be |
| * added/changed because they were specified in the device table. |
| * @ph_elt will be non-zero if yes. |
| */ |
| ph_elt = devtbl_find_path(dir_name + root_len - 1); |
| |
| /* |
| * Before adding the directory itself, we have to iterate over all the |
| * entries the device table adds to this directory and create them. |
| */ |
| for (; !non_existing;) { |
| struct stat dent_st; |
| |
| errno = 0; |
| entry = readdir(dir); |
| if (!entry) { |
| if (errno == 0) |
| break; |
| sys_err_msg("error reading directory '%s'", dir_name); |
| err = -1; |
| break; |
| } |
| |
| if (strcmp(".", entry->d_name) == 0) |
| continue; |
| if (strcmp("..", entry->d_name) == 0) |
| continue; |
| |
| if (ph_elt) |
| /* |
| * This directory was referred to at the device table |
| * file. Check if this directory entry is referred at |
| * too. |
| */ |
| nh_elt = devtbl_find_name(ph_elt, entry->d_name); |
| |
| /* |
| * We are going to create the file corresponding to this |
| * directory entry (@entry->d_name). We use 'struct stat' |
| * object to pass information about file attributes (actually |
| * only about UID, GID, mode, major, and minor). Get attributes |
| * for this file from the UBIFS rootfs on the host. |
| */ |
| free(name); |
| name = make_path(dir_name, entry->d_name); |
| if (lstat(name, &dent_st) == -1) { |
| sys_err_msg("lstat failed for file '%s'", name); |
| goto out_free; |
| } |
| |
| if (squash_owner) |
| /* |
| * Squash UID/GID. But the device table may override |
| * this. |
| */ |
| dent_st.st_uid = dent_st.st_gid = 0; |
| |
| /* |
| * And if the device table describes the same file, override |
| * the attributes. However, this is not allowed for device node |
| * files. |
| */ |
| if (nh_elt && override_attributes(&dent_st, ph_elt, nh_elt)) |
| goto out_free; |
| |
| inum = ++c->highest_inum; |
| |
| if (S_ISDIR(dent_st.st_mode)) { |
| err = add_directory(name, inum, &dent_st, 0); |
| if (err) |
| goto out_free; |
| nlink += 1; |
| type = UBIFS_ITYPE_DIR; |
| } else { |
| err = add_non_dir(name, &inum, 0, &type, &dent_st); |
| if (err) |
| goto out_free; |
| } |
| |
| err = add_dent_node(dir_inum, entry->d_name, inum, type); |
| if (err) |
| goto out_free; |
| size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1, |
| 8); |
| } |
| |
| /* |
| * OK, we have created all files in this directory (recursively), let's |
| * also create all files described in the device table. All t |
| */ |
| nh_elt = first_name_htbl_element(ph_elt, &itr); |
| while (nh_elt) { |
| struct stat fake_st; |
| |
| /* |
| * We prohibit creating regular files using the device table, |
| * the device table may only re-define attributes of regular |
| * files. |
| */ |
| if (S_ISREG(nh_elt->mode)) { |
| err_msg("Bad device table entry %s/%s - it is " |
| "prohibited to create regular files " |
| "via device table", |
| strcmp(ph_elt->path, "/") ? ph_elt->path : "", |
| nh_elt->name); |
| goto out_free; |
| } |
| |
| memcpy(&fake_st, &root_st, sizeof(struct stat)); |
| fake_st.st_uid = nh_elt->uid; |
| fake_st.st_uid = nh_elt->uid; |
| fake_st.st_mode = nh_elt->mode; |
| fake_st.st_rdev = nh_elt->dev; |
| fake_st.st_nlink = 1; |
| |
| free(name); |
| name = make_path(dir_name, nh_elt->name); |
| inum = ++c->highest_inum; |
| |
| if (S_ISDIR(nh_elt->mode)) { |
| err = add_directory(name, inum, &fake_st, 1); |
| if (err) |
| goto out_free; |
| nlink += 1; |
| type = UBIFS_ITYPE_DIR; |
| } else { |
| err = add_non_dir(name, &inum, 0, &type, &fake_st); |
| if (err) |
| goto out_free; |
| } |
| |
| err = add_dent_node(dir_inum, nh_elt->name, inum, type); |
| if (err) |
| goto out_free; |
| size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8); |
| |
| nh_elt = next_name_htbl_element(ph_elt, &itr); |
| } |
| |
| creat_sqnum = dir_creat_sqnum; |
| |
| err = add_dir_inode(dir, dir_inum, size, nlink, st); |
| if (err) |
| goto out_free; |
| |
| free(name); |
| if (!non_existing && closedir(dir) == -1) |
| return sys_err_msg("error closing directory '%s'", dir_name); |
| |
| return 0; |
| |
| out_free: |
| free(name); |
| if (!non_existing) |
| closedir(dir); |
| return -1; |
| } |
| |
| /** |
| * add_multi_linked_files - write all the files for which we counted links. |
| */ |
| static int add_multi_linked_files(void) |
| { |
| int i, err; |
| |
| for (i = 0; i < HASH_TABLE_SIZE; i++) { |
| struct inum_mapping *im; |
| unsigned char type = 0; |
| |
| for (im = hash_table[i]; im; im = im->next) { |
| dbg_msg(2, "%s", im->path_name); |
| err = add_non_dir(im->path_name, &im->use_inum, |
| im->use_nlink, &type, &im->st); |
| if (err) |
| return err; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * write_data - write the files and directories. |
| */ |
| static int write_data(void) |
| { |
| int err; |
| mode_t mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; |
| |
| if (root) { |
| err = stat(root, &root_st); |
| if (err) |
| return sys_err_msg("bad root file-system directory '%s'", |
| root); |
| } else { |
| root_st.st_mtime = time(NULL); |
| root_st.st_atime = root_st.st_ctime = root_st.st_mtime; |
| root_st.st_mode = mode; |
| } |
| |
| head_flags = 0; |
| err = add_directory(root, UBIFS_ROOT_INO, &root_st, !root); |
| if (err) |
| return err; |
| err = add_multi_linked_files(); |
| if (err) |
| return err; |
| return flush_nodes(); |
| } |
| |
| static int namecmp(const char *name1, const char *name2) |
| { |
| size_t len1 = strlen(name1), len2 = strlen(name2); |
| size_t clen = (len1 < len2) ? len1 : len2; |
| int cmp; |
| |
| cmp = memcmp(name1, name2, clen); |
| if (cmp) |
| return cmp; |
| return (len1 < len2) ? -1 : 1; |
| } |
| |
| static int cmp_idx(const void *a, const void *b) |
| { |
| const struct idx_entry *e1 = *(const struct idx_entry **)a; |
| const struct idx_entry *e2 = *(const struct idx_entry **)b; |
| int cmp; |
| |
| cmp = keys_cmp(&e1->key, &e2->key); |
| if (cmp) |
| return cmp; |
| return namecmp(e1->name, e2->name); |
| } |
| |
| /** |
| * add_idx_node - write an index node to the head. |
| * @node: index node |
| * @child_cnt: number of children of this index node |
| */ |
| static int add_idx_node(void *node, int child_cnt) |
| { |
| int err, lnum, offs, len; |
| |
| len = ubifs_idx_node_sz(c, child_cnt); |
| |
| prepare_node(node, len); |
| |
| err = reserve_space(len, &lnum, &offs); |
| if (err) |
| return err; |
| |
| memcpy(leb_buf + offs, node, len); |
| memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); |
| |
| c->old_idx_sz += ALIGN(len, 8); |
| |
| dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len, |
| c->old_idx_sz); |
| |
| /* The last index node written will be the root */ |
| c->zroot.lnum = lnum; |
| c->zroot.offs = offs; |
| c->zroot.len = len; |
| |
| return 0; |
| } |
| |
| /** |
| * write_index - write out the index. |
| */ |
| static int write_index(void) |
| { |
| size_t sz, i, cnt, idx_sz, pstep, bcnt; |
| struct idx_entry **idx_ptr, **p; |
| struct ubifs_idx_node *idx; |
| struct ubifs_branch *br; |
| int child_cnt = 0, j, level, blnum, boffs, blen, blast_len, err; |
| |
| dbg_msg(1, "leaf node count: %zd", idx_cnt); |
| |
| /* Reset the head for the index */ |
| head_flags = LPROPS_INDEX; |
| /* Allocate index node */ |
| idx_sz = ubifs_idx_node_sz(c, c->fanout); |
| idx = malloc(idx_sz); |
| if (!idx) |
| return err_msg("out of memory"); |
| /* Make an array of pointers to sort the index list */ |
| sz = idx_cnt * sizeof(struct idx_entry *); |
| if (sz / sizeof(struct idx_entry *) != idx_cnt) { |
| free(idx); |
| return err_msg("index is too big (%zu entries)", idx_cnt); |
| } |
| idx_ptr = malloc(sz); |
| if (!idx_ptr) { |
| free(idx); |
| return err_msg("out of memory - needed %zu bytes for index", |
| sz); |
| } |
| idx_ptr[0] = idx_list_first; |
| for (i = 1; i < idx_cnt; i++) |
| idx_ptr[i] = idx_ptr[i - 1]->next; |
| qsort(idx_ptr, idx_cnt, sizeof(struct idx_entry *), cmp_idx); |
| /* Write level 0 index nodes */ |
| cnt = idx_cnt / c->fanout; |
| if (idx_cnt % c->fanout) |
| cnt += 1; |
| p = idx_ptr; |
| blnum = head_lnum; |
| boffs = head_offs; |
| for (i = 0; i < cnt; i++) { |
| /* |
| * Calculate the child count. All index nodes are created full |
| * except for the last index node on each row. |
| */ |
| if (i == cnt - 1) { |
| child_cnt = idx_cnt % c->fanout; |
| if (child_cnt == 0) |
| child_cnt = c->fanout; |
| } else |
| child_cnt = c->fanout; |
| memset(idx, 0, idx_sz); |
| idx->ch.node_type = UBIFS_IDX_NODE; |
| idx->child_cnt = cpu_to_le16(child_cnt); |
| idx->level = cpu_to_le16(0); |
| for (j = 0; j < child_cnt; j++, p++) { |
| br = ubifs_idx_branch(c, idx, j); |
| key_write_idx(&(*p)->key, &br->key); |
| br->lnum = cpu_to_le32((*p)->lnum); |
| br->offs = cpu_to_le32((*p)->offs); |
| br->len = cpu_to_le32((*p)->len); |
| } |
| add_idx_node(idx, child_cnt); |
| } |
| /* Write level 1 index nodes and above */ |
| level = 0; |
| pstep = 1; |
| while (cnt > 1) { |
| /* |
| * 'blast_len' is the length of the last index node in the level |
| * below. |
| */ |
| blast_len = ubifs_idx_node_sz(c, child_cnt); |
| /* 'bcnt' is the number of index nodes in the level below */ |
| bcnt = cnt; |
| /* 'cnt' is the number of index nodes in this level */ |
| cnt = (cnt + c->fanout - 1) / c->fanout; |
| if (cnt == 0) |
| cnt = 1; |
| level += 1; |
| /* |
| * The key of an index node is the same as the key of its first |
| * child. Thus we can get the key by stepping along the bottom |
| * level 'p' with an increasing large step 'pstep'. |
| */ |
| p = idx_ptr; |
| pstep *= c->fanout; |
| for (i = 0; i < cnt; i++) { |
| /* |
| * Calculate the child count. All index nodes are |
| * created full except for the last index node on each |
| * row. |
| */ |
| if (i == cnt - 1) { |
| child_cnt = bcnt % c->fanout; |
| if (child_cnt == 0) |
| child_cnt = c->fanout; |
| } else |
| child_cnt = c->fanout; |
| memset(idx, 0, idx_sz); |
| idx->ch.node_type = UBIFS_IDX_NODE; |
| idx->child_cnt = cpu_to_le16(child_cnt); |
| idx->level = cpu_to_le16(level); |
| for (j = 0; j < child_cnt; j++) { |
| size_t bn = i * c->fanout + j; |
| |
| /* |
| * The length of the index node in the level |
| * below is 'idx_sz' except when it is the last |
| * node on the row. i.e. all the others on the |
| * row are full. |
| */ |
| if (bn == bcnt - 1) |
| blen = blast_len; |
| else |
| blen = idx_sz; |
| /* |
| * 'blnum' and 'boffs' hold the position of the |
| * index node on the level below. |
| */ |
| if (boffs + blen > c->leb_size) { |
| blnum += 1; |
| boffs = 0; |
| } |
| /* |
| * Fill in the branch with the key and position |
| * of the index node from the level below. |
| */ |
| br = ubifs_idx_branch(c, idx, j); |
| key_write_idx(&(*p)->key, &br->key); |
| br->lnum = cpu_to_le32(blnum); |
| br->offs = cpu_to_le32(boffs); |
| br->len = cpu_to_le32(blen); |
| /* |
| * Step to the next index node on the level |
| * below. |
| */ |
| boffs += ALIGN(blen, 8); |
| p += pstep; |
| } |
| add_idx_node(idx, child_cnt); |
| } |
| } |
| |
| /* Free stuff */ |
| for (i = 0; i < idx_cnt; i++) |
| free(idx_ptr[i]); |
| free(idx_ptr); |
| free(idx); |
| |
| dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs, |
| c->zroot.len); |
| |
| /* Set the index head */ |
| c->ihead_lnum = head_lnum; |
| c->ihead_offs = ALIGN(head_offs, c->min_io_size); |
| dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs); |
| |
| /* Flush the last index LEB */ |
| err = flush_nodes(); |
| if (err) |
| return err; |
| |
| return 0; |
| } |
| |
| /** |
| * set_gc_lnum - set the LEB number reserved for the garbage collector. |
| */ |
| static int set_gc_lnum(void) |
| { |
| int err; |
| |
| c->gc_lnum = head_lnum++; |
| err = write_empty_leb(c->gc_lnum); |
| if (err) |
| return err; |
| set_lprops(c->gc_lnum, 0, 0); |
| c->lst.empty_lebs += 1; |
| return 0; |
| } |
| |
| /** |
| * finalize_leb_cnt - now that we know how many LEBs we used. |
| */ |
| static int finalize_leb_cnt(void) |
| { |
| c->leb_cnt = head_lnum; |
| if (c->leb_cnt > c->max_leb_cnt) |
| return err_msg("max_leb_cnt too low (%d needed)", c->leb_cnt); |
| c->main_lebs = c->leb_cnt - c->main_first; |
| if (verbose) { |
| printf("\tsuper lebs: %d\n", UBIFS_SB_LEBS); |
| printf("\tmaster lebs: %d\n", UBIFS_MST_LEBS); |
| printf("\tlog_lebs: %d\n", c->log_lebs); |
| printf("\tlpt_lebs: %d\n", c->lpt_lebs); |
| printf("\torph_lebs: %d\n", c->orph_lebs); |
| printf("\tmain_lebs: %d\n", c->main_lebs); |
| printf("\tgc lebs: %d\n", 1); |
| printf("\tindex lebs: %d\n", c->lst.idx_lebs); |
| printf("\tleb_cnt: %d\n", c->leb_cnt); |
| } |
| dbg_msg(1, "total_free: %llu", c->lst.total_free); |
| dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty); |
| dbg_msg(1, "total_used: %llu", c->lst.total_used); |
| dbg_msg(1, "total_dead: %llu", c->lst.total_dead); |
| dbg_msg(1, "total_dark: %llu", c->lst.total_dark); |
| dbg_msg(1, "index size: %llu", c->old_idx_sz); |
| dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs); |
| return 0; |
| } |
| |
| /** |
| * write_super - write the super block. |
| */ |
| static int write_super(void) |
| { |
| struct ubifs_sb_node sup; |
| |
| memset(&sup, 0, UBIFS_SB_NODE_SZ); |
| |
| sup.ch.node_type = UBIFS_SB_NODE; |
| sup.key_hash = c->key_hash_type; |
| sup.min_io_size = cpu_to_le32(c->min_io_size); |
| sup.leb_size = cpu_to_le32(c->leb_size); |
| sup.leb_cnt = cpu_to_le32(c->leb_cnt); |
| sup.max_leb_cnt = cpu_to_le32(c->max_leb_cnt); |
| sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes); |
| sup.log_lebs = cpu_to_le32(c->log_lebs); |
| sup.lpt_lebs = cpu_to_le32(c->lpt_lebs); |
| sup.orph_lebs = cpu_to_le32(c->orph_lebs); |
| sup.jhead_cnt = cpu_to_le32(c->jhead_cnt); |
| sup.fanout = cpu_to_le32(c->fanout); |
| sup.lsave_cnt = cpu_to_le32(c->lsave_cnt); |
| sup.fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); |
| sup.default_compr = cpu_to_le16(c->default_compr); |
| sup.rp_size = cpu_to_le64(c->rp_size); |
| sup.time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); |
| uuid_generate_random(sup.uuid); |
| if (verbose) { |
| char s[40]; |
| |
| uuid_unparse_upper(sup.uuid, s); |
| printf("\tUUID: %s\n", s); |
| } |
| if (c->big_lpt) |
| sup.flags |= cpu_to_le32(UBIFS_FLG_BIGLPT); |
| if (c->space_fixup) |
| sup.flags |= cpu_to_le32(UBIFS_FLG_SPACE_FIXUP); |
| |
| return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM); |
| } |
| |
| /** |
| * write_master - write the master node. |
| */ |
| static int write_master(void) |
| { |
| struct ubifs_mst_node mst; |
| int err; |
| |
| memset(&mst, 0, UBIFS_MST_NODE_SZ); |
| |
| mst.ch.node_type = UBIFS_MST_NODE; |
| mst.log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); |
| mst.highest_inum = cpu_to_le64(c->highest_inum); |
| mst.cmt_no = cpu_to_le64(0); |
| mst.flags = cpu_to_le32(UBIFS_MST_NO_ORPHS); |
| mst.root_lnum = cpu_to_le32(c->zroot.lnum); |
| mst.root_offs = cpu_to_le32(c->zroot.offs); |
| mst.root_len = cpu_to_le32(c->zroot.len); |
| mst.gc_lnum = cpu_to_le32(c->gc_lnum); |
| mst.ihead_lnum = cpu_to_le32(c->ihead_lnum); |
| mst.ihead_offs = cpu_to_le32(c->ihead_offs); |
| mst.index_size = cpu_to_le64(c->old_idx_sz); |
| mst.lpt_lnum = cpu_to_le32(c->lpt_lnum); |
| mst.lpt_offs = cpu_to_le32(c->lpt_offs); |
| mst.nhead_lnum = cpu_to_le32(c->nhead_lnum); |
| mst.nhead_offs = cpu_to_le32(c->nhead_offs); |
| mst.ltab_lnum = cpu_to_le32(c->ltab_lnum); |
| mst.ltab_offs = cpu_to_le32(c->ltab_offs); |
| mst.lsave_lnum = cpu_to_le32(c->lsave_lnum); |
| mst.lsave_offs = cpu_to_le32(c->lsave_offs); |
| mst.lscan_lnum = cpu_to_le32(c->lscan_lnum); |
| mst.empty_lebs = cpu_to_le32(c->lst.empty_lebs); |
| mst.idx_lebs = cpu_to_le32(c->lst.idx_lebs); |
| mst.total_free = cpu_to_le64(c->lst.total_free); |
| mst.total_dirty = cpu_to_le64(c->lst.total_dirty); |
| mst.total_used = cpu_to_le64(c->lst.total_used); |
| mst.total_dead = cpu_to_le64(c->lst.total_dead); |
| mst.total_dark = cpu_to_le64(c->lst.total_dark); |
| mst.leb_cnt = cpu_to_le32(c->leb_cnt); |
| |
| err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM); |
| if (err) |
| return err; |
| |
| err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1); |
| if (err) |
| return err; |
| |
| return 0; |
| } |
| |
| /** |
| * write_log - write an empty log. |
| */ |
| static int write_log(void) |
| { |
| struct ubifs_cs_node cs; |
| int err, i, lnum; |
| |
| lnum = UBIFS_LOG_LNUM; |
| |
| cs.ch.node_type = UBIFS_CS_NODE; |
| cs.cmt_no = cpu_to_le64(0); |
| |
| err = write_node(&cs, UBIFS_CS_NODE_SZ, lnum); |
| if (err) |
| return err; |
| |
| lnum += 1; |
| |
| for (i = 1; i < c->log_lebs; i++, lnum++) { |
| err = write_empty_leb(lnum); |
| if (err) |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * write_lpt - write the LEB properties tree. |
| */ |
| static int write_lpt(void) |
| { |
| int err, lnum; |
| |
| err = create_lpt(c); |
| if (err) |
| return err; |
| |
| lnum = c->nhead_lnum + 1; |
| while (lnum <= c->lpt_last) { |
| err = write_empty_leb(lnum++); |
| if (err) |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * write_orphan_area - write an empty orphan area. |
| */ |
| static int write_orphan_area(void) |
| { |
| int err, i, lnum; |
| |
| lnum = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs; |
| for (i = 0; i < c->orph_lebs; i++, lnum++) { |
| err = write_empty_leb(lnum); |
| if (err) |
| return err; |
| } |
| return 0; |
| } |
| |
| /** |
| * check_volume_empty - check if the UBI volume is empty. |
| * |
| * This function checks if the UBI volume is empty by looking if its LEBs are |
| * mapped or not. |
| * |
| * Returns %0 in case of success, %1 is the volume is not empty, |
| * and a negative error code in case of failure. |
| */ |
| static int check_volume_empty(void) |
| { |
| int lnum, err; |
| |
| for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) { |
| err = ubi_is_mapped(out_fd, lnum); |
| if (err < 0) |
| return err; |
| if (err == 1) |
| return 1; |
| } |
| return 0; |
| } |
| |
| /** |
| * open_target - open the output target. |
| * |
| * Open the output target. The target can be an UBI volume |
| * or a file. |
| * |
| * Returns %0 in case of success and %-1 in case of failure. |
| */ |
| static int open_target(void) |
| { |
| if (out_ubi) { |
| out_fd = open(output, O_RDWR | O_EXCL); |
| |
| if (out_fd == -1) |
| return sys_err_msg("cannot open the UBI volume '%s'", |
| output); |
| if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) |
| return sys_err_msg("ubi_set_property failed"); |
| |
| if (!yes && check_volume_empty()) { |
| if (!prompt("UBI volume is not empty. Format anyways?", false)) |
| return err_msg("UBI volume is not empty"); |
| } |
| } else { |
| out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); |
| if (out_fd == -1) |
| return sys_err_msg("cannot create output file '%s'", |
| output); |
| } |
| return 0; |
| } |
| |
| |
| /** |
| * close_target - close the output target. |
| * |
| * Close the output target. If the target was an UBI |
| * volume, also close libubi. |
| * |
| * Returns %0 in case of success and %-1 in case of failure. |
| */ |
| static int close_target(void) |
| { |
| if (ubi) |
| libubi_close(ubi); |
| if (out_fd >= 0 && close(out_fd) == -1) |
| return sys_err_msg("cannot close the target '%s'", output); |
| if (output) |
| free(output); |
| return 0; |
| } |
| |
| /** |
| * init - initialize things. |
| */ |
| static int init(void) |
| { |
| int err, i, main_lebs, big_lpt = 0, sz; |
| |
| c->highest_inum = UBIFS_FIRST_INO; |
| |
| c->jhead_cnt = 1; |
| |
| main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; |
| main_lebs -= c->log_lebs + c->orph_lebs; |
| |
| err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt); |
| if (err) |
| return err; |
| |
| c->main_first = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs + |
| c->orph_lebs; |
| head_lnum = c->main_first; |
| head_offs = 0; |
| |
| c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; |
| c->lpt_last = c->lpt_first + c->lpt_lebs - 1; |
| |
| c->lpt = malloc(c->main_lebs * sizeof(struct ubifs_lprops)); |
| if (!c->lpt) |
| return err_msg("unable to allocate LPT"); |
| |
| c->ltab = malloc(c->lpt_lebs * sizeof(struct ubifs_lprops)); |
| if (!c->ltab) |
| return err_msg("unable to allocate LPT ltab"); |
| |
| /* Initialize LPT's own lprops */ |
| for (i = 0; i < c->lpt_lebs; i++) { |
| c->ltab[i].free = c->leb_size; |
| c->ltab[i].dirty = 0; |
| } |
| |
| c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); |
| c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); |
| dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm); |
| |
| leb_buf = malloc(c->leb_size); |
| if (!leb_buf) |
| return err_msg("out of memory"); |
| |
| node_buf = malloc(NODE_BUFFER_SIZE); |
| if (!node_buf) |
| return err_msg("out of memory"); |
| |
| block_buf = malloc(UBIFS_BLOCK_SIZE); |
| if (!block_buf) |
| return err_msg("out of memory"); |
| |
| sz = sizeof(struct inum_mapping *) * HASH_TABLE_SIZE; |
| hash_table = malloc(sz); |
| if (!hash_table) |
| return err_msg("out of memory"); |
| memset(hash_table, 0, sz); |
| |
| err = init_compression(); |
| if (err) |
| return err; |
| |
| return 0; |
| } |
| |
| static void destroy_hash_table(void) |
| { |
| int i; |
| |
| for (i = 0; i < HASH_TABLE_SIZE; i++) { |
| struct inum_mapping *im, *q; |
| |
| for (im = hash_table[i]; im; ) { |
| q = im; |
| im = im->next; |
| free(q->path_name); |
| free(q); |
| } |
| } |
| } |
| |
| /** |
| * deinit - deinitialize things. |
| */ |
| static void deinit(void) |
| { |
| free(c->lpt); |
| free(c->ltab); |
| free(leb_buf); |
| free(node_buf); |
| free(block_buf); |
| destroy_hash_table(); |
| free(hash_table); |
| destroy_compression(); |
| free_devtable_info(); |
| } |
| |
| /** |
| * mkfs - make the file system. |
| * |
| * Each on-flash area has a corresponding function to create it. The order of |
| * the functions reflects what information must be known to complete each stage. |
| * As a consequence the output file is not written sequentially. No effort has |
| * been made to make efficient use of memory or to allow for the possibility of |
| * incremental updates to the output file. |
| */ |
| static int mkfs(void) |
| { |
| int err = 0; |
| |
| err = init(); |
| if (err) |
| goto out; |
| |
| err = write_data(); |
| if (err) |
| goto out; |
| |
| err = set_gc_lnum(); |
| if (err) |
| goto out; |
| |
| err = write_index(); |
| if (err) |
| goto out; |
| |
| err = finalize_leb_cnt(); |
| if (err) |
| goto out; |
| |
| err = write_lpt(); |
| if (err) |
| goto out; |
| |
| err = write_super(); |
| if (err) |
| goto out; |
| |
| err = write_master(); |
| if (err) |
| goto out; |
| |
| err = write_log(); |
| if (err) |
| goto out; |
| |
| err = write_orphan_area(); |
| |
| out: |
| deinit(); |
| return err; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int err; |
| |
| err = get_options(argc, argv); |
| if (err) |
| return err; |
| |
| err = open_target(); |
| if (err) |
| return err; |
| |
| err = mkfs(); |
| if (err) { |
| close_target(); |
| return err; |
| } |
| |
| err = close_target(); |
| if (err) |
| return err; |
| |
| if (verbose) |
| printf("Success!\n"); |
| |
| return 0; |
| } |