/* * Build a JFFS image in a file, from a given directory tree. * * By default, builds an image that is of the same endianness as the * host. * The -a option can be used when building for a target system which * has a different endianness than the host. */ /* $Id: mkfs.jffs.c,v 1.15 2005/11/07 11:15:13 gleixner Exp $ */ #include #include #include #include #include #include #include #include #include #include #define swab16(x) \ ((__u16)( \ (((__u16)(x) & (__u16)0x00ffU) << 8) | \ (((__u16)(x) & (__u16)0xff00U) >> 8) )) #define swab32(x) \ ((__u32)( \ (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ (((__u32)(x) & (__u32)0xff000000UL) >> 24) )) #if __BYTE_ORDER == __BIG_ENDIAN #define cpu_to_le16(x) ({ __u16 _x = x; swab16(_x); }) #define cpu_to_le32(x) ({ __u32 _x = x; swab32(_x); }) #define cpu_to_be16(x) (x) #define cpu_to_be32(x) (x) #else #define cpu_to_le16(x) (x) #define cpu_to_le32(x) (x) #define cpu_to_be16(x) ({ __u16 _x = x; swab16(_x); }) #define cpu_to_be32(x) ({ __u32 _x = x; swab32(_x); }) #endif #define le32_to_cpu(x) cpu_to_le32(x) #define be32_to_cpu(x) cpu_to_be32(x) #define BLOCK_SIZE 1024 #define JFFS_MAGIC 0x34383931 /* "1984" */ #define JFFS_MAX_NAME_LEN 256 #define JFFS_MIN_INO 1 #define JFFS_TRACE_INDENT 4 #define JFFS_ALIGN_SIZE 4 static int MAX_CHUNK_SIZE = 32768; /* How many padding bytes should be inserted between two chunks of data on the flash? */ #define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \ - ((__u32)(size) % JFFS_ALIGN_SIZE)) \ % JFFS_ALIGN_SIZE) struct jffs_raw_inode { __u32 magic; /* A constant magic number. */ __u32 ino; /* Inode number. */ __u32 pino; /* Parent's inode number. */ __u32 version; /* Version number. */ __u32 mode; /* file_type, mode */ __u16 uid; __u16 gid; __u32 atime; __u32 mtime; __u32 ctime; __u32 offset; /* Where to begin to write. */ __u32 dsize; /* Size of the file data. */ __u32 rsize; /* How much are going to be replaced? */ __u8 nsize; /* Name length. */ __u8 nlink; /* Number of links. */ __u8 spare : 6; /* For future use. */ __u8 rename : 1; /* Is this a special rename? */ __u8 deleted : 1; /* Has this file been deleted? */ __u8 accurate; /* The inode is obsolete if accurate == 0. */ __u32 dchksum; /* Checksum for the data. */ __u16 nchksum; /* Checksum for the name. */ __u16 chksum; /* Checksum for the raw_inode. */ }; struct jffs_file { struct jffs_raw_inode inode; char *name; unsigned char *data; }; char *root_directory_name = NULL; int fs_pos = 0; int verbose = 0; #define ENDIAN_HOST 0 #define ENDIAN_BIG 1 #define ENDIAN_LITTLE 2 int endian = ENDIAN_HOST; static __u32 jffs_checksum(void *data, int size); void jffs_print_trace(const char *path, int depth); int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path, int depth); void write_file(struct jffs_file *f, FILE *fs, struct stat st); void read_data(struct jffs_file *f, const char *path, int offset); int mkfs(FILE *fs, const char *path, int ino, int parent, int depth); static __u32 jffs_checksum(void *data, int size) { __u32 sum = 0; __u8 *ptr = (__u8 *)data; while (size-- > 0) { sum += *ptr++; } return sum; } void jffs_print_trace(const char *path, int depth) { int path_len = strlen(path); int out_pos = depth * JFFS_TRACE_INDENT; int pos = path_len - 1; char *out = (char *)alloca(depth * JFFS_TRACE_INDENT + path_len + 1); if (verbose >= 2) { fprintf(stderr, "jffs_print_trace(): path: \"%s\"\n", path); } if (!out) { fprintf(stderr, "jffs_print_trace(): Allocation failed.\n"); fprintf(stderr, " path: \"%s\"\n", path); fprintf(stderr, "depth: %d\n", depth); exit(1); } memset(out, ' ', depth * JFFS_TRACE_INDENT); if (path[pos] == '/') { pos--; } while (path[pos] && (path[pos] != '/')) { pos--; } for (pos++; path[pos] && (path[pos] != '/'); pos++) { out[out_pos++] = path[pos]; } out[out_pos] = '\0'; fprintf(stderr, "%s\n", out); } /* Print the contents of a raw inode. */ void jffs_print_raw_inode(struct jffs_raw_inode *raw_inode) { fprintf(stderr, "jffs_raw_inode: inode number: %u\n", raw_inode->ino); fprintf(stderr, "{\n"); fprintf(stderr, " 0x%08x, /* magic */\n", raw_inode->magic); fprintf(stderr, " 0x%08x, /* ino */\n", raw_inode->ino); fprintf(stderr, " 0x%08x, /* pino */\n", raw_inode->pino); fprintf(stderr, " 0x%08x, /* version */\n", raw_inode->version); fprintf(stderr, " 0x%08x, /* mode */\n", raw_inode->mode); fprintf(stderr, " 0x%04x, /* uid */\n", raw_inode->uid); fprintf(stderr, " 0x%04x, /* gid */\n", raw_inode->gid); fprintf(stderr, " 0x%08x, /* atime */\n", raw_inode->atime); fprintf(stderr, " 0x%08x, /* mtime */\n", raw_inode->mtime); fprintf(stderr, " 0x%08x, /* ctime */\n", raw_inode->ctime); fprintf(stderr, " 0x%08x, /* offset */\n", raw_inode->offset); fprintf(stderr, " 0x%08x, /* dsize */\n", raw_inode->dsize); fprintf(stderr, " 0x%08x, /* rsize */\n", raw_inode->rsize); fprintf(stderr, " 0x%02x, /* nsize */\n", raw_inode->nsize); fprintf(stderr, " 0x%02x, /* nlink */\n", raw_inode->nlink); fprintf(stderr, " 0x%02x, /* spare */\n", raw_inode->spare); fprintf(stderr, " %u, /* rename */\n", raw_inode->rename); fprintf(stderr, " %u, /* deleted */\n", raw_inode->deleted); fprintf(stderr, " 0x%02x, /* accurate */\n", raw_inode->accurate); fprintf(stderr, " 0x%08x, /* dchksum */\n", raw_inode->dchksum); fprintf(stderr, " 0x%04x, /* nchksum */\n", raw_inode->nchksum); fprintf(stderr, " 0x%04x, /* chksum */\n", raw_inode->chksum); fprintf(stderr, "}\n"); } static void write_val32(__u32 *adr, __u32 val) { switch(endian) { case ENDIAN_HOST: *adr = val; break; case ENDIAN_LITTLE: *adr = cpu_to_le32(val); break; case ENDIAN_BIG: *adr = cpu_to_be32(val); break; } } static void write_val16(__u16 *adr, __u16 val) { switch(endian) { case ENDIAN_HOST: *adr = val; break; case ENDIAN_LITTLE: *adr = cpu_to_le16(val); break; case ENDIAN_BIG: *adr = cpu_to_be16(val); break; } } static __u32 read_val32(__u32 *adr) { __u32 val = 0; switch(endian) { case ENDIAN_HOST: val = *adr; break; case ENDIAN_LITTLE: val = le32_to_cpu(*adr); break; case ENDIAN_BIG: val = be32_to_cpu(*adr); break; } return val; } /* This function constructs a root inode with no name and no data. The inode is then written to the filesystem image. */ int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path, int depth) { struct jffs_file f; struct stat st; if (stat(root_dir_path, &st) < 0) { perror("stat"); exit(1); } write_val32(&f.inode.magic, JFFS_MAGIC); write_val32(&f.inode.ino, first_ino); write_val32(&f.inode.pino, 0); write_val32(&f.inode.version, 1); write_val32(&f.inode.mode, st.st_mode); write_val16(&f.inode.uid, 0); /* root */ write_val16(&f.inode.gid, 0); /* root */ write_val32(&f.inode.atime, st.st_atime); write_val32(&f.inode.mtime, st.st_mtime); write_val32(&f.inode.ctime, st.st_ctime); write_val32(&f.inode.offset, 0); write_val32(&f.inode.dsize, 0); write_val32(&f.inode.rsize,0); f.inode.nsize = 0; /*f.inode.nlink = st.st_nlink;*/ f.inode.nlink = 1; f.inode.spare = 0; f.inode.rename = 0; f.inode.deleted = 0; f.inode.accurate = 0; write_val32(&f.inode.dchksum, 0); write_val16(&f.inode.nchksum, 0); write_val16(&f.inode.chksum, 0); f.name = 0; f.data = 0; write_val16(&f.inode.chksum, jffs_checksum(&f.inode, sizeof(struct jffs_raw_inode))); f.inode.accurate = 0xff; write_file(&f, fs, st); if (verbose >= 1) { jffs_print_trace(root_dir_path, depth); } if (verbose >= 2) { jffs_print_raw_inode(&f.inode); } return first_ino; } /* This function writes a chunks of data. A data chunk consists of a raw inode, perhaps a name and perhaps some data. */ void write_file(struct jffs_file *f, FILE *fs, struct stat st) { int npad = JFFS_GET_PAD_BYTES(f->inode.nsize); int dpad = JFFS_GET_PAD_BYTES(read_val32(&f->inode.dsize)); int size = sizeof(struct jffs_raw_inode) + f->inode.nsize + npad + read_val32(&f->inode.dsize) + dpad; unsigned char ff_data[] = { 0xff, 0xff, 0xff, 0xff }; if (verbose >= 2) { fprintf(stderr, "***write_file()\n"); } /* Write the raw inode. */ fwrite((void *)&f->inode, sizeof(struct jffs_raw_inode), 1, fs); /* Write the name. */ if (f->inode.nsize) { fwrite(f->name, 1, f->inode.nsize, fs); if (npad) { fwrite(ff_data, 1, npad, fs); } } /* Write the data. */ if (read_val32(&f->inode.dsize)) { if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) { __u16 tmp; switch(endian) { case ENDIAN_HOST: tmp = st.st_rdev; break; case ENDIAN_LITTLE: tmp = cpu_to_le16(st.st_rdev); break; case ENDIAN_BIG: tmp = cpu_to_be16(st.st_rdev); break; } fwrite((char *)&tmp, sizeof(st.st_rdev) / 4, 1, fs); } else { fwrite(f->data, 1, read_val32(&f->inode.dsize), fs); } if (dpad) { fwrite(ff_data, 1, dpad, fs); } } fs_pos += size; /* If the space left on the block is smaller than the size of an inode, then skip it. */ } void read_data(struct jffs_file *f, const char *path, int offset) { FILE *file; char *tot_path; int pos = 0; int r; if (verbose >= 2) { fprintf(stderr, "***read_data(): f: 0x%08x, path: \"%s\", offset: %u\r\n", (unsigned int)f, path, offset); fprintf(stderr, " file's size: %u\n", read_val32(&f->inode.dsize)); } if (!(f->data = (unsigned char *)malloc(read_val32(&f->inode.dsize)))) { fprintf(stderr, "read_data(): malloc() failed! (*data)\n"); exit(1); } if (!(tot_path = (char *)alloca(strlen(path) + f->inode.nsize + 1))) { fprintf(stderr, "read_data(): alloca() failed! (tot_path)\n"); exit(1); } strcpy(tot_path, path); strncat(tot_path, f->name, f->inode.nsize); if (!(file = fopen(tot_path, "r"))) { fprintf(stderr, "read_data(): Couldn't open \"%s\".\n", tot_path); exit(1); } if (fseek(file, offset, SEEK_SET) < 0) { fprintf(stderr, "read_data(): fseek failure: path = %s, offset = %u.\n", path, offset); exit(1); } while (pos < read_val32(&f->inode.dsize)) { if ((r = fread(&f->data[pos], 1, read_val32(&f->inode.dsize) - pos, file)) < 0) { fprintf(stderr, "read_data(): fread failure (%s).\n", path); exit(1); } pos += r; } fclose(file); } /* This is the routine that constructs the filesystem image. */ int mkfs(FILE *fs, const char *path, int ino, int parent, int depth) { struct dirent *dir_entry; DIR *dir; struct stat st; struct jffs_file f; int name_len; int pos = 0; int new_ino = ino; char *filename; int path_len = strlen(path); if (verbose >= 2) { fprintf(stderr, "***mkfs(): path: \"%s\"\r\n", path); } if (!(dir = opendir(path))) { perror("opendir"); fprintf(stderr, "mkfs(): opendir() failed! (%s)\n", path); exit(1); } while ((dir_entry = readdir(dir))) { if (verbose >= 2) { fprintf(stderr, "mkfs(): name: %s\n", dir_entry->d_name); } name_len = strlen(dir_entry->d_name); if (((name_len == 1) && (dir_entry->d_name[0] == '.')) || ((name_len == 2) && (dir_entry->d_name[0] == '.') && (dir_entry->d_name[1] == '.'))) { continue; } if (!(filename = (char *)alloca(path_len + name_len + 1))) { fprintf(stderr, "mkfs(): Allocation failed!\n"); exit(0); } strcpy(filename, path); strcat(filename, dir_entry->d_name); if (verbose >= 2) { fprintf(stderr, "mkfs(): filename: %s\n", filename); } if (lstat(filename, &st) < 0) { perror("lstat"); exit(1); } if (verbose >= 2) { fprintf(stderr, "mkfs(): filename: \"%s\", ino: %d, parent: %d\n", filename, new_ino, parent); } write_val32(&f.inode.magic, JFFS_MAGIC); write_val32(&f.inode.ino, new_ino); write_val32(&f.inode.pino, parent); write_val32(&f.inode.version, 1); write_val32(&f.inode.mode, st.st_mode); write_val16(&f.inode.uid, st.st_uid); write_val16(&f.inode.gid, st.st_gid); write_val32(&f.inode.atime, st.st_atime); write_val32(&f.inode.mtime, st.st_mtime); write_val32(&f.inode.ctime, st.st_ctime); write_val32(&f.inode.dsize, 0); write_val32(&f.inode.rsize, 0); f.inode.nsize = name_len; /*f.inode.nlink = st.st_nlink;*/ f.inode.nlink = 1; f.inode.spare = 0; f.inode.rename = 0; f.inode.deleted = 0; f.inode.accurate = 0; write_val32(&f.inode.dchksum, 0); write_val16(&f.inode.nchksum, 0); write_val16(&f.inode.chksum, 0); if (dir_entry->d_name) { f.name = strdup(dir_entry->d_name); } else { f.name = 0; } repeat: write_val32(&f.inode.offset, pos); f.data = 0; f.inode.accurate = 0; if (S_ISREG(st.st_mode) && st.st_size) { if (st.st_size - pos < MAX_CHUNK_SIZE) { write_val32(&f.inode.dsize, st.st_size - pos); } else { write_val32(&f.inode.dsize, MAX_CHUNK_SIZE); } read_data(&f, path, pos); pos += read_val32(&f.inode.dsize); } else if (S_ISLNK(st.st_mode)) { int linklen; char *linkdata = malloc(1000); if (!linkdata) { fprintf(stderr, "mkfs(): malloc() failed! (linkdata)\n"); exit(1); } if ((linklen = readlink(filename, linkdata, 1000)) < 0) { free(linkdata); fprintf(stderr, "mkfs(): readlink() failed! f.name = \"%s\"\n", f.name); exit(1); } write_val32(&f.inode.dsize, linklen); f.data = (unsigned char *)linkdata; f.data[linklen] = '\0'; } else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) { write_val32(&f.inode.dsize, sizeof(st.st_rdev) / 4); } write_val16(&f.inode.chksum, 0); if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) { write_val32(&f.inode.dchksum, jffs_checksum((void *)f.data, read_val32(&f.inode.dsize))); } else { write_val32(&f.inode.dchksum, jffs_checksum((void *)&st.st_rdev, sizeof(st.st_rdev) / 4)); } write_val16(&f.inode.nchksum, jffs_checksum((void *)f.name, f.inode.nsize)); write_val16(&f.inode.chksum, jffs_checksum((void *)&f.inode, sizeof(struct jffs_raw_inode))); f.inode.accurate = 0xff; write_file(&f, fs, st); if (S_ISREG(st.st_mode) && st.st_size) { if (pos < st.st_size) { write_val32(&f.inode.version, read_val32(&f.inode.version) + 1); goto repeat; } } new_ino++; pos = 0; if (verbose >= 1) { jffs_print_trace(f.name, depth); } if (verbose >= 2) { jffs_print_raw_inode(&f.inode); } if (S_ISDIR(st.st_mode)) { char *new_path; if (!(new_path = (char *)alloca(strlen(path) + name_len + 1 + 1))) { fprintf(stderr, "mkfs(): alloca() failed! (new_path)\n"); exit(1); } strcpy(new_path, path); strncat(new_path, f.name, f.inode.nsize); strcat(new_path, "/"); if (verbose >= 2) { fprintf(stderr, "mkfs(): new_path: \"%s\"\n", new_path); } new_ino = mkfs(fs, new_path, new_ino, new_ino - 1, depth + 1); } if (f.name) { free(f.name); } if (f.data) { free(f.data); } } closedir(dir); return new_ino; } void usage(void) { fprintf(stderr, "Usage: mkfs.jffs -d root_directory [-a little|big] [-e erase_size] [-o output_file] [-v[0-9]]\n"); fprintf(stderr, " By default, the file system is built using the same endianness as the\n"); fprintf(stderr, " host. If building for a different target, use the -a option.\n"); } int main(int argc, char **argv) { FILE *fs; int root_ino; int len; int ch; extern int optind; extern char *optarg; fs = stdout; /* Send constructed file system to stdout by default */ while ((ch = getopt(argc, argv, "a:d:e:v::o:h?")) != -1) { switch((char)ch) { case 'd': len = strlen(optarg); root_directory_name = (char *)malloc(len + 2); memcpy(root_directory_name, optarg, len); if (root_directory_name[len - 1] != '/') { root_directory_name[len++] = '/'; } root_directory_name[len] = '\0'; break; case 'v': if (!optarg || strlen(optarg) == 0) { verbose = 1; } else if (strlen(optarg) > 1 || !isdigit(optarg[0])) { fprintf(stderr, "verbose level must be between 0 and 9!\n"); usage(); exit(1); } else { verbose = strtol(optarg, NULL, 0); } break; case 'o': fs = fopen(optarg, "w"); if (!fs) { fprintf(stderr, "unable to open file %s for output.\n", optarg); exit(1); } break; case 'a': if (strcmp(optarg, "little") == 0) { endian = ENDIAN_LITTLE; } else if (strcmp(optarg, "big") == 0) { endian = ENDIAN_BIG; } else { usage(); exit(1); } break; case 'e': MAX_CHUNK_SIZE = strtol(optarg, NULL, 0) / 2; break; case 'h': case '?': default: usage(); exit(0); } } if ((argc -= optind)) { usage(); exit(1); } if (root_directory_name == NULL) { fprintf(stderr, "Error: must specify a root directory\n"); usage(); exit(1); } if (verbose >= 1) { fprintf(stderr, "Constructing JFFS filesystem...\n"); } root_ino = make_root_dir(fs, JFFS_MIN_INO, root_directory_name, 0); mkfs(fs, root_directory_name, root_ino + 1, root_ino, 1); fclose(fs); free(root_directory_name); exit(0); }