Files
recycled-ni-neutrino/src/system/mtdutils/mkfs.jffs2.cpp
GetAway 5a10b744db mkfs.jff2: fix -Wnarrowing warning with newer compiler
Origin commit data
------------------
Commit: 3524bd7c82
Author: GetAway <get-away@t-online.de>
Date: 2022-05-26 (Thu, 26 May 2022)

Origin message was:
------------------
- mkfs.jff2: fix -Wnarrowing warning with newer compiler
2022-05-26 23:03:27 +02:00

1263 lines
32 KiB
C++

/*
* Build a JFFS2 image in a file, from a given directory tree.
*
* Copyright 2001, 2002 Red Hat, Inc.
* 2001 David A. Schleef <ds@lineo.com>
* 2002 Axis Communications AB
* 2001, 2002 Erik Andersen <andersen@codepoet.org>
* 2004 University of Szeged, Hungary
* 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* 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 <ds@schleef.org>.
*
* Major architectural rewrite by Erik Andersen <andersen@codepoet.org>
* 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 <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <libgen.h>
#include <ctype.h>
#include <time.h>
#include <byteswap.h>
#include <crc32.h>
#include <inttypes.h>
#if __cplusplus >= 201103
#include <algorithm>
#endif
#include <string>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "rbtree.h"
#include <system/helpers.h>
#include <system/localize.h>
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<filesystem_entry*>(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:
<path> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
/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<std::string>::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<long unsigned int>(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; i<n; i++) {
struct dirent *dp = namelist[i];
if (dp->d_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;
}