/* * Build a JFFS2 image in a file, from a given directory tree. * * Copyright 2001, 2002 Red Hat, Inc. * 2001 David A. Schleef * 2002 Axis Communications AB * 2001, 2002 Erik Andersen * 2004 University of Szeged, Hungary * 2006 KaiGai Kohei * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Cross-endian support added by David Schleef . * * Major architectural rewrite by Erik Andersen * to allow support for making hard links (though hard links support is * not yet implemented), and for munging file permissions and ownership * on the fly using --faketime, --squash, --devtable. And I plugged a * few memory leaks, adjusted the error handling and fixed some little * nits here and there. * * I also added a sample device table file. See device_table.txt * -Erik, September 2001 * * Cleanmarkers support added by Axis Communications AB * * Rewritten again. Cleanly separated host and target filsystem * activities (mainly so I can reuse all the host handling stuff as I * rewrite other mkfs utils). Added a verbose option to list types * and attributes as files are added to the file system. Major cleanup * and scrubbing of the code so it can be read, understood, and * modified by mere mortals. * * -Erik, November 2002 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __cplusplus >= 201103 #include #endif #include #ifdef HAVE_CONFIG_H #include #endif #include "rbtree.h" #include #include extern CLocaleManager *g_Locale; #define PROGRAM_NAME "mkfs.jffs2" int page_size = -1; int target_endian = __BYTE_ORDER; struct rb_root hardlinks; struct filesystem_entry { char *name; /* Name of this directory (think basename) */ char *path; /* Path of this directory (think dirname) */ char *fullname; /* Full name of this directory (i.e. path+name) */ char *hostname; /* Full path to this file on the host filesystem */ uint32_t ino; /* Inode number of this file in JFFS2 */ struct stat sb; /* Stores directory permissions and whatnot */ char *link; /* Target a symlink points to. */ struct filesystem_entry *parent; /* Parent directory */ struct filesystem_entry *prev; /* Only relevant to non-directories */ struct filesystem_entry *next; /* Only relevant to non-directories */ struct filesystem_entry *files; /* Only relevant to directories */ struct rb_node hardlink_rb; }; struct filesystem_entry *fse_root = NULL; #include "common.h" #include "mtd/jffs2-user.h" struct jffs2_unknown_node cleanmarker; #include "compr.h" #include "mkfs.jffs2.h" #include "sumtool.h" /* Do not use the weird XPG version of basename */ #undef basename //#define DMALLOC //#define mkfs_debug_msg errmsg #define mkfs_debug_msg(a...) { } #define PAD(x) (((x)+3)&~3) #define JFFS2_MAX_FILE_SIZE 0xFFFFFFFF #ifndef JFFS2_MAX_SYMLINK_LEN #define JFFS2_MAX_SYMLINK_LEN 254 #endif CMkfsJFFS2::CMkfsJFFS2() { Init(); } CMkfsJFFS2::~CMkfsJFFS2() { classClear(); } bool CMkfsJFFS2::classInit() { if (useSumtool_) { sumName_ = imageName_; imageName_ += ".tmp"; } out_fd = open(imageName_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0644); if (out_fd == -1) { sys_errmsg("open output file: %s", imageName_.c_str()); return false; } jffs2_compressors_init(); compressorIsInit = true; return true; } void CMkfsJFFS2::classClear() { if (fse_root != NULL) { cleanup(fse_root); fse_root = NULL; } if (out_fd > 0) { close(out_fd); out_fd = -1; } if (compressorIsInit) { jffs2_compressors_exit(); compressorIsInit = false; } } void CMkfsJFFS2::Init() { out_fd = -1; squash_uids = 0; squash_perms = 0; fake_times = 0; target_endian = __BYTE_ORDER; all_read = 0; printProgress = 0; kbUsed = 0; progressBar = NULL; ino = 0; out_ofs = 0; erase_block_size = 65536; pad_fs_size = 0; add_cleanmarkers = 0; cleanmarker_size = sizeof(cleanmarker); compressorIsInit = false; fse_root = NULL; imageName_ = ""; sumName_ = ""; useSumtool_ = true; page_size = sysconf(_SC_PAGESIZE); if (page_size < 0) /* System doesn't know so ... */ page_size = 4096; /* ... we make an educated guess */ memset(ffbuf, 0xFF, sizeof(ffbuf)); } char *CMkfsJFFS2::xreadlink(const char *path) { static const int GROWBY = 80; /* how large we will grow strings by */ char *buf = NULL; int bufsize = 0, readsize = 0; do { buf = (char*)xrealloc(buf, bufsize += GROWBY); readsize = readlink(path, buf, bufsize); /* 1st try */ if (readsize == -1) { sys_errmsg("%s:%s", PROGRAM_NAME, path); return NULL; } } while (bufsize < readsize + 1); buf[readsize] = '\0'; return buf; } void CMkfsJFFS2::cleanup(struct filesystem_entry *dir) { struct filesystem_entry *e; e = dir->files; while (e) { if (e->name) free(e->name); if (e->path) free(e->path); if (e->fullname) free(e->fullname); e->name = NULL; e->path = NULL; e->fullname = NULL; e->prev = NULL; filesystem_entry *prev = e; if (S_ISDIR(e->sb.st_mode)) { cleanup(e); } e = e->next; free(prev); } } struct filesystem_entry *CMkfsJFFS2::add_host_filesystem_entry(const char *name, const char *path, unsigned long uid, unsigned long gid, unsigned long mode, dev_t rdev, struct filesystem_entry *parent) { int status; char *tmp; struct stat sb; time_t timestamp = time(NULL); struct filesystem_entry *entry; memset(&sb, 0, sizeof(struct stat)); status = lstat(path, &sb); if (status >= 0) { /* It is ok for some types of files to not exit on disk (such as * device nodes), but if they _do_ exist the specified mode had * better match the actual file or strange things will happen.... */ if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) { sys_errmsg ("%s: file type does not match specified type!", path); } timestamp = sb.st_mtime; } else { /* If this is a regular file, it _must_ exist on disk */ if ((mode & S_IFMT) == S_IFREG) { sys_errmsg("%s: does not exist!", path); } } /* Squash all permissions so files are owned by root, all * timestamps are _right now_, and file permissions * have group and other write removed */ if (squash_uids) { uid = gid = 0; } if (squash_perms) { if (!S_ISLNK(mode)) { mode &= ~(S_IWGRP | S_IWOTH); mode &= ~(S_ISUID | S_ISGID); } } if (fake_times) { timestamp = 0; } entry = reinterpret_cast(xcalloc(1, sizeof(struct filesystem_entry))); entry->hostname = xstrdup(path); entry->fullname = xstrdup(name); tmp = xstrdup(name); entry->name = xstrdup(basename(tmp)); free(tmp); tmp = xstrdup(name); entry->path = xstrdup(dirname(tmp)); free(tmp); entry->sb.st_ino = sb.st_ino; entry->sb.st_dev = sb.st_dev; entry->sb.st_nlink = sb.st_nlink; entry->sb.st_uid = uid; entry->sb.st_gid = gid; entry->sb.st_mode = mode; entry->sb.st_rdev = rdev; entry->sb.st_atime = entry->sb.st_ctime = entry->sb.st_mtime = timestamp; if (S_ISREG(mode)) { entry->sb.st_size = sb.st_size; } if (S_ISLNK(mode)) { entry->link = xreadlink(path); entry->sb.st_size = strlen(entry->link); } /* This happens only for root */ if (!parent) return (entry); /* Hook the file into the parent directory */ entry->parent = parent; if (!parent->files) { parent->files = entry; } else { struct filesystem_entry *prev; for (prev = parent->files; prev->next; prev = prev->next); prev->next = entry; entry->prev = prev; } return (entry); } struct filesystem_entry *CMkfsJFFS2::find_filesystem_entry( struct filesystem_entry *dir, char *fullname, uint32_t type) { struct filesystem_entry *e = dir; if (S_ISDIR(dir->sb.st_mode)) { /* If this is the first call, and we actually want this * directory, then return it now */ if (strcmp(fullname, e->fullname) == 0) return e; e = dir->files; } while (e) { if (S_ISDIR(e->sb.st_mode)) { int len = strlen(e->fullname); /* Check if we are a parent of the correct path */ if (strncmp(e->fullname, fullname, len) == 0) { /* Is this an _exact_ match? */ if (strcmp(fullname, e->fullname) == 0) { return (e); } /* Looks like we found a parent of the correct path */ if (fullname[len] == '/') { if (e->files) { return (find_filesystem_entry (e, fullname, type)); } else { return NULL; } } } } else { if (strcmp(fullname, e->fullname) == 0) { return (e); } } e = e->next; } return (NULL); } void CMkfsJFFS2::printProgressData(bool finish) { static int p_old = -1; if (finish) p_old = -1; int p = (finish) ? 100 : ((all_read/1024)*100 )/kbUsed; if (p != p_old) { p_old = p; printf("mkfs.jffs2: %ld KB from %ld KB read (%d%%)\n", all_read / 1024, kbUsed, p); fflush(stdout); } } void CMkfsJFFS2::paintProgressBar(bool finish) { static int p_old = -1; if (finish) p_old = -1; int p = (finish) ? 100 : ((all_read/1024)*100 )/kbUsed; if (p != p_old) { p_old = p; progressBar->showLocalStatus(p); } } unsigned int CMkfsJFFS2::write_regular_file(struct filesystem_entry *e) { int fd, len; uint32_t ver; unsigned int offset; unsigned char *buf, *cbuf, *wbuf; struct jffs2_raw_inode ri; struct stat *statbuf; unsigned int totcomp = 0; statbuf = &(e->sb); if ((uint32_t)statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { errmsg("Skipping file \"%s\" too large.", e->path); return -1; } fd = open(e->hostname, O_RDONLY); if (fd == -1) { sys_errmsg("%s: open file", e->hostname); } e->ino = ++ino; mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu", e->name, (unsigned long) e->ino, (unsigned long) e->parent->ino); write_dirent(e); buf = (unsigned char*)xmalloc(page_size); cbuf = NULL; ver = 0; offset = 0; memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.ino = cpu_to_je32(e->ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(statbuf->st_size); while ((len = read(fd, buf, page_size))) { unsigned char *tbuf = buf; if (len < 0) { sys_errmsg("read"); } if ((printProgress) || (progressBar)) { all_read += len; if (printProgress) printProgressData(false); if (progressBar) paintProgressBar(); } while (len) { uint32_t dsize, space; uint16_t compression; pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN); dsize = len; space = erase_block_size - (out_ofs % erase_block_size) - sizeof(ri); if (space > dsize) space = dsize; compression = jffs2_compress(tbuf, &cbuf, &dsize, &space); ri.compr = compression & 0xff; ri.usercompr = (compression >> 8) & 0xff; if (ri.compr) { wbuf = cbuf; } else { wbuf = tbuf; dsize = space; } ri.totlen = cpu_to_je32(sizeof(ri) + space); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.version = cpu_to_je32(++ver); ri.offset = cpu_to_je32(offset); ri.csize = cpu_to_je32(space); ri.dsize = cpu_to_je32(dsize); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(mtd_crc32(0, wbuf, space)); full_write(out_fd, &ri, sizeof(ri)); totcomp += sizeof(ri); full_write(out_fd, wbuf, space); totcomp += space; padword(); if (tbuf != cbuf) { free(cbuf); cbuf = NULL; } tbuf += dsize; len -= dsize; offset += dsize; } } if (!je32_to_cpu(ri.version)) { /* Was empty file */ pad_block_if_less_than(sizeof(ri)); ri.version = cpu_to_je32(++ver); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.csize = cpu_to_je32(0); ri.dsize = cpu_to_je32(0); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); full_write(out_fd, &ri, sizeof(ri)); padword(); } free(buf); close(fd); return totcomp; } void CMkfsJFFS2::write_symlink(struct filesystem_entry *e) { int len; struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); e->ino = ++ino; mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", e->name, (unsigned long) e->ino, (unsigned long) e->parent->ino); write_dirent(e); len = strlen(e->link); if (len > JFFS2_MAX_SYMLINK_LEN) { errmsg("symlink too large. Truncated to %d chars.", JFFS2_MAX_SYMLINK_LEN); len = JFFS2_MAX_SYMLINK_LEN; } memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + len); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(e->ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(statbuf->st_size); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(len); ri.dsize = cpu_to_je32(len); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(mtd_crc32(0, e->link, len)); pad_block_if_less_than(sizeof(ri) + len); full_write(out_fd, &ri, sizeof(ri)); full_write(out_fd, e->link, len); padword(); } void CMkfsJFFS2::write_pipe(struct filesystem_entry *e) { struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); e->ino = ++ino; if (S_ISDIR(statbuf->st_mode)) { mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", e->name, (unsigned long) e->ino, (unsigned long) (e->parent) ? e->parent->ino : 1); } write_dirent(e); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(e->ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(0); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(0); ri.dsize = cpu_to_je32(0); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(0); pad_block_if_less_than(sizeof(ri)); full_write(out_fd, &ri, sizeof(ri)); padword(); } void CMkfsJFFS2::write_special_file(struct filesystem_entry *e) { jint16_t kdev; struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); e->ino = ++ino; write_dirent(e); kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) + minor(statbuf->st_rdev)); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev)); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(e->ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(statbuf->st_size); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(sizeof(kdev)); ri.dsize = cpu_to_je32(sizeof(kdev)); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(mtd_crc32(0, &kdev, sizeof(kdev))); pad_block_if_less_than(sizeof(ri) + sizeof(kdev)); full_write(out_fd, &ri, sizeof(ri)); full_write(out_fd, &kdev, sizeof(kdev)); padword(); } void CMkfsJFFS2::write_dirent(struct filesystem_entry *e) { char *name = e->name; struct jffs2_raw_dirent rd; struct stat *statbuf = &(e->sb); static uint32_t version = 0; memset(&rd, 0, sizeof(rd)); rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name)); rd.hdr_crc = cpu_to_je32(mtd_crc32(0, &rd, sizeof(struct jffs2_unknown_node) - 4)); rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); rd.version = cpu_to_je32(version++); rd.ino = cpu_to_je32(e->ino); rd.mctime = cpu_to_je32(statbuf->st_mtime); rd.nsize = strlen(name); rd.type = IFTODT(statbuf->st_mode); //rd.unused[0] = 0; //rd.unused[1] = 0; rd.node_crc = cpu_to_je32(mtd_crc32(0, &rd, sizeof(rd) - 8)); rd.name_crc = cpu_to_je32(mtd_crc32(0, name, strlen(name))); pad_block_if_less_than(sizeof(rd) + rd.nsize); full_write(out_fd, &rd, sizeof(rd)); full_write(out_fd, name, rd.nsize); padword(); } uint32_t CMkfsJFFS2::find_hardlink(struct filesystem_entry *e) { struct rb_node **n = &hardlinks.rb_node; struct rb_node *parent = NULL; while (*n) { struct filesystem_entry *f; parent = *n; f = rb_entry(parent, struct filesystem_entry, hardlink_rb); if ((f->sb.st_dev < e->sb.st_dev) || (f->sb.st_dev == e->sb.st_dev && f->sb.st_ino < e->sb.st_ino)) n = &parent->rb_left; else if ((f->sb.st_dev > e->sb.st_dev) || (f->sb.st_dev == e->sb.st_dev && f->sb.st_ino > e->sb.st_ino)) { n = &parent->rb_right; } else return f->ino; } rb_link_node(&e->hardlink_rb, parent, n); rb_insert_color(&e->hardlink_rb, &hardlinks); return 0; } void CMkfsJFFS2::full_write(int fd, const void *buf, int len) { char* buf1 = (char*)buf; while (len > 0) { int ret = write(fd, buf1, len); if (ret < 0) sys_errmsg("write"); if (ret == 0) sys_errmsg("write returned zero"); len -= ret; buf1 += ret; out_ofs += ret; } } void CMkfsJFFS2::pad_block_if_less_than(int req) { if (add_cleanmarkers) { if ((out_ofs % erase_block_size) == 0) { full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); pad(cleanmarker_size - sizeof(cleanmarker)); padword(); } } if ((out_ofs % erase_block_size) + req > erase_block_size) { padblock(); } if (add_cleanmarkers) { if ((out_ofs % erase_block_size) == 0) { full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); pad(cleanmarker_size - sizeof(cleanmarker)); padword(); } } } void CMkfsJFFS2::padblock(void) { while (out_ofs % erase_block_size) { #if __cplusplus < 201103 full_write(out_fd, ffbuf, min(sizeof(ffbuf), #else full_write(out_fd, ffbuf, std::min(sizeof(ffbuf), #endif (size_t)(erase_block_size - (out_ofs % erase_block_size)))); } } void CMkfsJFFS2::pad(int req) { while (req) { if (req > (int)sizeof(ffbuf)) { full_write(out_fd, ffbuf, sizeof(ffbuf)); req -= sizeof(ffbuf); } else { full_write(out_fd, ffbuf, req); req = 0; } } } void CMkfsJFFS2::recursive_populate_directory(struct filesystem_entry *dir) { struct filesystem_entry *e; e = dir->files; while (e) { if (e->sb.st_nlink >= 1 && (e->ino = find_hardlink(e))) write_dirent(e); else { switch (e->sb.st_mode & S_IFMT) { case S_IFDIR: write_pipe(e); break; case S_IFSOCK: write_pipe(e); break; case S_IFIFO: write_pipe(e); break; case S_IFCHR: write_special_file(e); break; case S_IFBLK: write_special_file(e); break; case S_IFLNK: write_symlink(e); break; case S_IFREG: write_regular_file(e); break; default: errmsg("Unknown mode %o for %s", e->sb.st_mode, e->fullname); break; } if ((printProgress) || (progressBar)) { switch (e->sb.st_mode & S_IFMT) { case S_IFDIR: case S_IFSOCK: case S_IFIFO: case S_IFCHR: case S_IFBLK: case S_IFLNK: all_read += e->sb.st_size; break; default: break; } } } if (printProgress) printProgressData(false); if (progressBar) paintProgressBar(); e = e->next; } e = dir->files; while (e) { if (S_ISDIR(e->sb.st_mode)) { if (e->files) { recursive_populate_directory(e); } } e = e->next; } } void CMkfsJFFS2::create_target_filesystem(struct filesystem_entry *root) { cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); cleanmarker.totlen = cpu_to_je32(cleanmarker_size); cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); if (ino == 0) ino = 1; root->ino = 1; recursive_populate_directory(root); if (pad_fs_size == -1) { padblock(); } else { if (pad_fs_size && add_cleanmarkers) { padblock(); while (out_ofs < pad_fs_size) { full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); pad(cleanmarker_size - sizeof(cleanmarker)); padblock(); } } else { while (out_ofs < pad_fs_size) { #if __cplusplus < 201103 full_write(out_fd, ffbuf, min(sizeof(ffbuf), (size_t)(pad_fs_size - out_ofs))); #else full_write(out_fd, ffbuf, std::min(sizeof(ffbuf), (size_t)(pad_fs_size - out_ofs))); #endif } } } } /* device table entries take the form of: /dev/mem c 640 0 0 1 1 0 0 - type can be one of: f A regular file d Directory c Character special device file b Block special device file p Fifo (named pipe) I don't bother with symlinks (permissions are irrelevant), hard links (special cases of regular files), or sockets (why bother). Regular files must exist in the target root directory. If a char, block, fifo, or directory does not exist, it will be created. */ bool CMkfsJFFS2::interpret_table_entry(struct filesystem_entry *root, const char *line) { char *hostpath; char type; char name[512]; unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; unsigned long start = 0, increment = 1, count = 0; struct filesystem_entry *entry; memset(name, '\0', 512); if (sscanf(line, "%511s %c %lo %lu %lu %lu %lu %lu %lu %lu", name, &type, &mode, &uid, &gid, &major, &minor, &start, &increment, &count) < 0) return false; if (!strcmp(name, "/")) { sys_errmsg("Device table entries require absolute paths"); } xasprintf(&hostpath, "%s%s", rootdir.c_str(), name); /* Check if this file already exists... */ switch (type) { case 'd': mode |= S_IFDIR; break; case 'f': mode |= S_IFREG; break; case 'p': mode |= S_IFIFO; break; case 'c': mode |= S_IFCHR; break; case 'b': mode |= S_IFBLK; break; case 'l': mode |= S_IFLNK; break; default: sys_errmsg("Unsupported file type '%c'", type); } entry = find_filesystem_entry(root, name, mode); if (entry && !(count > 0 && (type == 'c' || type == 'b'))) { /* Ok, we just need to fixup the existing entry * and we will be all done... */ entry->sb.st_uid = uid; entry->sb.st_gid = gid; entry->sb.st_mode = mode; if (major && minor) { entry->sb.st_rdev = makedev(major, minor); } } else { /* If parent is NULL (happens with device table entries), * try and find our parent now) */ char *tmp, *dir; struct filesystem_entry *parent; tmp = strdup(name); dir = dirname(tmp); parent = find_filesystem_entry(root, dir, S_IFDIR); free(tmp); if (parent == NULL) { errmsg ("skipping device_table entry '%s': no parent directory!", name); free(hostpath); return false; } switch (type) { case 'd': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'f': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'p': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'c': case 'b': if (count > 0) { dev_t rdev; unsigned long i; char *dname, *hpath; for (i = start; i < (start + count); i++) { xasprintf(&dname, "%s%lu", name, i); xasprintf(&hpath, "%s/%s%lu", rootdir.c_str(), name, i); rdev = makedev(major, minor + (i - start) * increment); add_host_filesystem_entry(dname, hpath, uid, gid, mode, rdev, parent); free(dname); free(hpath); } } else { dev_t rdev = makedev(major, minor); add_host_filesystem_entry(name, hostpath, uid, gid, mode, rdev, parent); } break; default: sys_errmsg("Unsupported file type '%c'", type); } } free(hostpath); return true; } bool CMkfsJFFS2::parse_device_table(struct filesystem_entry *root, v_devtable_t *v_devtable) { /* Turn off squash, since we must ensure that values * entered via the device table are not squashed */ squash_uids = 0; squash_perms = 0; bool status = false; std::vector::iterator it = v_devtable->begin(); while (it < v_devtable->end()) { std::string line = *it; line = trim(line); if (interpret_table_entry(root, line.c_str())) status = true; ++it; } return status; } struct filesystem_entry *CMkfsJFFS2::recursive_add_host_directory( struct filesystem_entry *parent, const char *targetpath, const char *hostpath, bool skipSpezialFolders, CProgressWindow *progress) { int i, n; struct stat sb; char *hpath, *tpath; struct dirent **namelist; struct filesystem_entry *entry; bool skipCheck = false; if (lstat(hostpath, &sb)) { sys_errmsg("%s", hostpath); } if (skipSpezialFolders) { // check for spezial folders and mounted folders struct statfs s; ::statfs(hostpath, &s); switch (static_cast(s.f_type)) { case 0xEF53L: /*EXT2 & EXT3*/ case 0x6969L: /*NFS*/ case 0xFF534D42L: /*CIFS*/ case 0x517BL: /*SMB*/ case 0x52654973L: /*REISERFS*/ case 0x65735546L: /*fuse for ntfs*/ case 0x58465342L: /*xfs*/ case 0x4d44L: /*msdos*/ skipCheck = true; break; } if (((sb.st_dev == dev_x[dev_dev]) && (strstr(targetpath, "/dev") == targetpath)) || ((sb.st_dev == dev_x[dev_proc]) && (strstr(targetpath, "/proc") == targetpath)) || ((sb.st_dev == dev_x[dev_sys]) && (strstr(targetpath, "/sys") == targetpath)) || ((sb.st_dev == dev_x[dev_tmp]) && (strstr(targetpath, "/tmp") == targetpath))) { skipCheck = true; } if (strstr(targetpath, "/var_init") == targetpath) return NULL; if ((!skipCheck) && (sb.st_dev != dev_x[dev_jffs2]) && !(strstr(targetpath, "/var") == targetpath)) /* jffs2 */ return NULL; } entry = add_host_filesystem_entry(targetpath, hostpath, sb.st_uid, sb.st_gid, sb.st_mode, 0, parent); if ((skipSpezialFolders) && (skipCheck)) return NULL; #if 0 if (strstr(targetpath, "/var/epg") == targetpath) return NULL; #endif n = scandir(hostpath, &namelist, 0, alphasort); if (n < 0) { sys_errmsg("opening directory %s", hostpath); } int i_pr = 0, n_pr = 0; if (progress != NULL) { n_pr = (n <= 2) ? 1 : n-2; } for (i=0; id_name[0] == '.' && (dp->d_name[1] == 0 || (dp->d_name[1] == '.' && dp->d_name[2] == 0))) { free(dp); continue; } xasprintf(&hpath, "%s/%s", hostpath, dp->d_name); if (lstat(hpath, &sb)) { sys_errmsg("%s", hpath); } if (strcmp(targetpath, "/") == 0) { xasprintf(&tpath, "%s%s", targetpath, dp->d_name); } else { xasprintf(&tpath, "%s/%s", targetpath, dp->d_name); } switch (sb.st_mode & S_IFMT) { case S_IFDIR: recursive_add_host_directory(entry, tpath, hpath, skipSpezialFolders); break; case S_IFREG: case S_IFSOCK: case S_IFIFO: case S_IFLNK: case S_IFCHR: case S_IFBLK: add_host_filesystem_entry(tpath, hpath, sb.st_uid, sb.st_gid, sb.st_mode, sb.st_rdev, entry); break; default: errmsg("Unknown file type %o for %s", sb.st_mode, hpath); break; } free(dp); free(hpath); free(tpath); if (progress != NULL) { progress->showLocalStatus((i_pr * 100) / n_pr); i_pr++; } } free(namelist); return (entry); } #ifdef __GNUC__ #define GETCWD_SIZE 0 #else #define GETCWD_SIZE -1 #endif bool CMkfsJFFS2::makeJffs2Image(std::string& path, std::string& imageName, int eraseBlockSize, int padFsSize, int addCleanmarkers, int targetEndian, bool skipSpezialFolders, bool useSumtool, CProgressWindow *progress/*=NULL*/, v_devtable_t *v_devtable/*=NULL*/) { Init(); imageName_ = imageName; useSumtool_ = useSumtool; erase_block_size = eraseBlockSize; // -e pad_fs_size = padFsSize; // -p add_cleanmarkers = addCleanmarkers; // -n target_endian = targetEndian; // -l squash_uids = 1; // -U rootdir = path; // -r printProgress = 1; progressBar = progress; hardlinks.rb_node = NULL; printf("[%s] erase_block_size: 0x%X\n", __FUNCTION__, eraseBlockSize); classInit(); char *cwd; struct stat sb; if (lstat(rootdir.c_str(), &sb)) { sys_errmsg("%s", rootdir.c_str()); } if (chdir(rootdir.c_str())) sys_errmsg("%s", rootdir.c_str()); if (!(cwd = getcwd(0, GETCWD_SIZE))) sys_errmsg("getcwd failed"); if (skipSpezialFolders) { std::string tmpPath = (path == "/") ? "" : path; std::string path_x[dev_max] = {tmpPath + "/sys", tmpPath + "/proc", tmpPath + "/tmp", tmpPath + "/dev", tmpPath + "/"}; for (int ix = dev_sys; ix < dev_max; ix++) { if (lstat(path_x[ix].c_str(), &sb) == 0) dev_x[ix] = sb.st_dev; else dev_x[ix] = 0; } } if (progressBar != NULL) { progressBar->showLocalStatus(0); progressBar->showGlobalStatus(0); progressBar->showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_MKFS_PREPARING_FILES)); } fse_root = recursive_add_host_directory(NULL, "/", cwd, skipSpezialFolders, progressBar); if (progressBar != NULL) { progressBar->showLocalStatus(100); progressBar->showGlobalStatus(50); } if ((v_devtable != NULL) && (!v_devtable->empty())) parse_device_table(fse_root, v_devtable); pid_t pid; std::string pcmd = "du -sx " + rootdir; FILE* fp = my_popen(pid, pcmd.c_str(), "r"); if (fp != NULL) { char buff[256]; fgets(buff, sizeof(buff), fp); fclose(fp); kbUsed = atol(buff); } if (kbUsed == 0) kbUsed = 60000; if (progressBar != NULL) { progressBar->showLocalStatus(0); progressBar->showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_MKFS_CREATE_IMAGE)); } create_target_filesystem(fse_root); if (progressBar != NULL) { progressBar->showGlobalStatus(90); progressBar->showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_MKFS_USING_SUMTOOL)); } if (printProgress) printProgressData(true); classClear(); sync(); if (useSumtool_) { CSumtoolJFFS2 st; st.sumtool(imageName_, sumName_, eraseBlockSize, ((padFsSize==0)?0:1), addCleanmarkers, targetEndian); unlink(imageName_.c_str()); } if (progressBar != NULL) { paintProgressBar(true); progressBar->showGlobalStatus(100); sync(); sleep(2); } progressBar = NULL; return true; }