From: Andy P. <at...@us...> - 2002-04-09 16:38:56
|
Update of /cvsroot/linux-vax/kernel-2.4/scripts/cramfs In directory usw-pr-cvs1:/tmp/cvs-serv4925/cramfs Modified Files: GNUmakefile mkcramfs.c Added Files: cramfsck.c Log Message: synch 2.4.15 commit 26 --- NEW FILE --- /* * cramfsck - check a cramfs file system * * Copyright (C) 2000-2001 Transmeta Corporation * * 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 * * 1999/12/03: Linus Torvalds (cramfs tester and unarchive program) * 2000/06/03: Daniel Quinlan (CRC and length checking program) * 2000/06/04: Daniel Quinlan (merged programs, added options, support * for special files, preserve permissions and * ownership, cramfs superblock v2, bogus mode * test, pathname length test, etc.) * 2000/06/06: Daniel Quinlan (support for holes, pretty-printing, * symlink size test) * 2000/07/11: Daniel Quinlan (file length tests, start at offset 0 or 512, * fsck-compatible exit codes) * 2000/07/15: Daniel Quinlan (initial support for block devices) */ /* compile-time options */ #define INCLUDE_FS_TESTS /* include cramfs checking and extraction */ #include <sys/types.h> #include <stdio.h> #include <sys/stat.h> #include <unistd.h> #include <sys/mman.h> #include <sys/fcntl.h> #include <dirent.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <assert.h> #include <getopt.h> #include <sys/sysmacros.h> #include <utime.h> #include <sys/ioctl.h> #define _LINUX_STRING_H_ #include <linux/fs.h> #include <linux/cramfs_fs.h> #include <zlib.h> static const char *progname = "cramfsck"; static int fd; /* ROM image file descriptor */ static char *filename; /* ROM image filename */ struct cramfs_super *super; /* just find the cramfs superblock once */ static int opt_verbose = 0; /* 1 = verbose (-v), 2+ = very verbose (-vv) */ #ifdef INCLUDE_FS_TESTS static int opt_extract = 0; /* extract cramfs (-x) */ char *extract_dir = NULL; /* extraction directory (-x) */ unsigned long start_inode = 1 << 28; /* start of first non-root inode */ unsigned long end_inode = 0; /* end of the directory structure */ unsigned long start_data = 1 << 28; /* start of the data (256 MB = max) */ unsigned long end_data = 0; /* end of the data */ /* true? cramfs_super < start_inode < end_inode <= start_data <= end_data */ static uid_t euid; /* effective UID */ #define PAD_SIZE 512 #define PAGE_CACHE_SIZE (4096) /* Guarantee access to at least 8kB at a time */ #define ROMBUFFER_BITS 13 #define ROMBUFFERSIZE (1 << ROMBUFFER_BITS) #define ROMBUFFERMASK (ROMBUFFERSIZE-1) static char read_buffer[ROMBUFFERSIZE * 2]; static unsigned long read_buffer_block = ~0UL; /* Uncompressing data structures... */ static char outbuffer[PAGE_CACHE_SIZE*2]; z_stream stream; #endif /* INCLUDE_FS_TESTS */ /* Input status of 0 to print help and exit without an error. */ static void usage(int status) { FILE *stream = status ? stderr : stdout; fprintf(stream, "usage: %s [-hv] [-x dir] file\n" " -h print this help\n" " -x dir extract into dir\n" " -v be more verbose\n" " file file to test\n", progname); exit(status); } #ifdef INCLUDE_FS_TESTS void print_node(char type, struct cramfs_inode *i, char *name) { char info[10]; if (S_ISCHR(i->mode) || (S_ISBLK(i->mode))) { /* major/minor numbers can be as high as 2^12 or 4096 */ snprintf(info, 10, "%4d,%4d", major(i->size), minor(i->size)); } else { /* size be as high as 2^24 or 16777216 */ snprintf(info, 10, "%9d", i->size); } printf("%c %04o %s %5d:%-3d %s\n", type, i->mode & ~S_IFMT, info, i->uid, i->gid, name); } /* * Create a fake "blocked" access */ static void *romfs_read(unsigned long offset) { unsigned int block = offset >> ROMBUFFER_BITS; if (block != read_buffer_block) { read_buffer_block = block; lseek(fd, block << ROMBUFFER_BITS, SEEK_SET); read(fd, read_buffer, ROMBUFFERSIZE * 2); } return read_buffer + (offset & ROMBUFFERMASK); } static struct cramfs_inode *cramfs_iget(struct cramfs_inode * i) { struct cramfs_inode *inode = malloc(sizeof(struct cramfs_inode)); *inode = *i; return inode; } static struct cramfs_inode *iget(unsigned int ino) { return cramfs_iget(romfs_read(ino)); } void iput(struct cramfs_inode *inode) { free(inode); } /* * Return the offset of the root directory, * or 0 if none. */ static struct cramfs_inode *read_super(void) { unsigned long offset; offset = super->root.offset << 2; if (super->magic != CRAMFS_MAGIC) return NULL; if (memcmp(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature)) != 0) return NULL; if (offset < sizeof(super)) return NULL; return cramfs_iget(&super->root); } static int uncompress_block(void *src, int len) { int err; stream.next_in = src; stream.avail_in = len; stream.next_out = (unsigned char *) outbuffer; stream.avail_out = PAGE_CACHE_SIZE*2; inflateReset(&stream); err = inflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { fprintf(stderr, "%s: error %d while decompressing! %p(%d)\n", filename, err, src, len); exit(4); } return stream.total_out; } static void change_file_status(char *path, struct cramfs_inode *i) { struct utimbuf epoch = { 0, 0 }; if (euid == 0) { if (lchown(path, i->uid, i->gid) < 0) { perror(path); exit(8); } if (S_ISLNK(i->mode)) return; if ((S_ISUID | S_ISGID) & i->mode) { if (chmod(path, i->mode) < 0) { perror(path); exit(8); } } } if (S_ISLNK(i->mode)) return; if (utime(path, &epoch) < 0) { perror(path); exit(8); } } static void do_symlink(char *path, struct cramfs_inode *i) { unsigned long offset = i->offset << 2; unsigned long curr = offset + 4; unsigned long next = *(u32 *) romfs_read(offset); unsigned long size; if (next > end_data) { end_data = next; } size = uncompress_block(romfs_read(curr), next - curr); if (size != i->size) { fprintf(stderr, "%s: size error in symlink `%s'\n", filename, path); exit(4); } outbuffer[size] = 0; if (opt_verbose) { char *str; str = malloc(strlen(outbuffer) + strlen(path) + 5); strcpy(str, path); strncat(str, " -> ", 4); strncat(str, outbuffer, size); print_node('l', i, str); if (opt_verbose > 1) { printf(" uncompressing block at %ld to %ld (%ld)\n", curr, next, next - curr); } } if (opt_extract) { symlink(outbuffer, path); change_file_status(path, i); } } static void do_special_inode(char *path, struct cramfs_inode *i) { dev_t devtype = 0; char type; if (S_ISCHR(i->mode)) { devtype = i->size; type = 'c'; } else if (S_ISBLK(i->mode)) { devtype = i->size; type = 'b'; } else if (S_ISFIFO(i->mode)) type = 'p'; else if (S_ISSOCK(i->mode)) type = 's'; else { fprintf(stderr, "%s: bogus mode on `%s' (%o)\n", filename, path, i->mode); exit(4); } if (opt_verbose) { print_node(type, i, path); } if (opt_extract) { if (mknod(path, i->mode, devtype) < 0) { perror(path); exit(8); } change_file_status(path, i); } } static void do_uncompress(int fd, unsigned long offset, unsigned long size) { unsigned long curr = offset + 4 * ((size + PAGE_CACHE_SIZE - 1) / PAGE_CACHE_SIZE); do { unsigned long out = PAGE_CACHE_SIZE; unsigned long next = *(u32 *) romfs_read(offset); if (next > end_data) { end_data = next; } offset += 4; if (curr == next) { if (opt_verbose > 1) { printf(" hole at %ld (%d)\n", curr, PAGE_CACHE_SIZE); } if (size < PAGE_CACHE_SIZE) out = size; memset(outbuffer, 0x00, out); } else { if (opt_verbose > 1) { printf(" uncompressing block at %ld to %ld (%ld)\n", curr, next, next - curr); } out = uncompress_block(romfs_read(curr), next - curr); } if (size >= PAGE_CACHE_SIZE) { if (out != PAGE_CACHE_SIZE) { fprintf(stderr, "%s: Non-block (%ld) bytes\n", filename, out); exit(4); } } else { if (out != size) { fprintf(stderr, "%s: Non-size (%ld vs %ld) bytes\n", filename, out, size); exit(4); } } size -= out; if (opt_extract) { write(fd, outbuffer, out); } curr = next; } while (size); } static void expand_fs(int pathlen, char *path, struct cramfs_inode *inode) { if (S_ISDIR(inode->mode)) { int count = inode->size; unsigned long offset = inode->offset << 2; char *newpath = malloc(pathlen + 256); if (count > 0 && offset < start_inode) { start_inode = offset; } /* XXX - need to check end_inode for empty case? */ memcpy(newpath, path, pathlen); newpath[pathlen] = '/'; pathlen++; if (opt_verbose) { print_node('d', inode, path); } if (opt_extract) { mkdir(path, inode->mode); change_file_status(path, inode); } while (count > 0) { struct cramfs_inode *child = iget(offset); int size; int newlen = child->namelen << 2; size = sizeof(struct cramfs_inode) + newlen; count -= size; offset += sizeof(struct cramfs_inode); memcpy(newpath + pathlen, romfs_read(offset), newlen); newpath[pathlen + newlen] = 0; if ((pathlen + newlen) - strlen(newpath) > 3) { fprintf(stderr, "%s: invalid cramfs--bad path length\n", filename); exit(4); } expand_fs(strlen(newpath), newpath, child); offset += newlen; if (offset > end_inode) { end_inode = offset; } } return; } if (S_ISREG(inode->mode)) { int fd = 0; unsigned long offset = inode->offset << 2; if (offset > 0 && offset < start_data) { start_data = offset; } if (opt_verbose) { print_node('f', inode, path); } if (opt_extract) { fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, inode->mode); } if (inode->size) { do_uncompress(fd, offset, inode->size); } if (opt_extract) { close(fd); change_file_status(path, inode); } return; } if (S_ISLNK(inode->mode)) { unsigned long offset = inode->offset << 2; if (offset < start_data) { start_data = offset; } do_symlink(path, inode); return; } else { do_special_inode(path, inode); return; } } #endif /* INCLUDE_FS_TESTS */ int main(int argc, char **argv) { void *buf; size_t length; struct stat st; u32 crc_old, crc_new; #ifdef INCLUDE_FS_TESTS struct cramfs_inode *root; #endif /* INCLUDE_FS_TESTS */ int c; /* for getopt */ int start = 0; if (argc) progname = argv[0]; /* command line options */ while ((c = getopt(argc, argv, "hx:v")) != EOF) { switch (c) { case 'h': usage(0); case 'x': #ifdef INCLUDE_FS_TESTS opt_extract = 1; extract_dir = malloc(strlen(optarg) + 1); strcpy(extract_dir, optarg); break; #else /* not INCLUDE_FS_TESTS */ fprintf(stderr, "%s: compiled without -x support\n", progname); exit(16); #endif /* not INCLUDE_FS_TESTS */ case 'v': opt_verbose++; break; } } if ((argc - optind) != 1) usage(16); filename = argv[optind]; /* find the physical size of the file or block device */ if (lstat(filename, &st) < 0) { perror(filename); exit(8); } fd = open(filename, O_RDONLY); if (fd < 0) { perror(filename); exit(8); } if (S_ISBLK(st.st_mode)) { if (ioctl(fd, BLKGETSIZE, &length) < 0) { fprintf(stderr, "%s: warning--unable to determine filesystem size \n", filename); exit(4); } length = length * 512; } else if (S_ISREG(st.st_mode)) { length = st.st_size; } else { fprintf(stderr, "%s is not a block device or file\n", filename); exit(8); } if (length < sizeof(struct cramfs_super)) { fprintf(stderr, "%s: invalid cramfs--file length too short\n", filename); exit(4); } if (S_ISBLK(st.st_mode)) { /* nasty because mmap of block devices fails */ buf = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); read(fd, buf, length); } else { /* nice and easy */ buf = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); } /* XXX - this could be cleaner... */ if (((struct cramfs_super *) buf)->magic == CRAMFS_MAGIC) { start = 0; super = (struct cramfs_super *) buf; } else if (length >= (PAD_SIZE + sizeof(struct cramfs_super)) && ((((struct cramfs_super *) (buf + PAD_SIZE))->magic == CRAMFS_MAGIC))) { start = PAD_SIZE; super = (struct cramfs_super *) (buf + PAD_SIZE); } else { fprintf(stderr, "%s: invalid cramfs--wrong magic\n", filename); exit(4); } if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { /* length test */ if (length < super->size) { fprintf(stderr, "%s: invalid cramfs--file length too short\n", filename); exit(4); } else if (length > super->size) { fprintf(stderr, "%s: warning--file length too long, padded image?\n", filename); } /* CRC test */ crc_old = super->fsid.crc; super->fsid.crc = crc32(0L, Z_NULL, 0); crc_new = crc32(0L, Z_NULL, 0); crc_new = crc32(crc_new, (unsigned char *) buf+start, super->size - start); if (crc_new != crc_old) { fprintf(stderr, "%s: invalid cramfs--crc error\n", filename); exit(4); } } else { fprintf(stderr, "%s: warning--old cramfs image, no CRC\n", filename); } #ifdef INCLUDE_FS_TESTS super = (struct cramfs_super *) malloc(sizeof(struct cramfs_super)); if (((struct cramfs_super *) buf)->magic == CRAMFS_MAGIC) { memcpy(super, buf, sizeof(struct cramfs_super)); } else if (length >= (PAD_SIZE + sizeof(struct cramfs_super)) && ((((struct cramfs_super *) (buf + PAD_SIZE))->magic == CRAMFS_MAGIC))) { memcpy(super, (buf + PAD_SIZE), sizeof(struct cramfs_super)); } munmap(buf, length); /* file format test, uses fake "blocked" accesses */ root = read_super(); umask(0); euid = geteuid(); if (!root) { fprintf(stderr, "%s: invalid cramfs--bad superblock\n", filename); exit(4); } stream.next_in = NULL; stream.avail_in = 0; inflateInit(&stream); if (!extract_dir) { extract_dir = "root"; } expand_fs(strlen(extract_dir), extract_dir, root); inflateEnd(&stream); if (start_data != 1 << 28 && end_inode != start_data) { fprintf(stderr, "%s: invalid cramfs--directory data end (%ld) != file data start (%ld)\n", filename, end_inode, start_data); exit(4); } if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { if (end_data > super->size) { fprintf(stderr, "%s: invalid cramfs--invalid file data offset\n", filename); exit(4); } } #endif /* INCLUDE_FS_TESTS */ exit(0); } Index: GNUmakefile =================================================================== RCS file: /cvsroot/linux-vax/kernel-2.4/scripts/cramfs/GNUmakefile,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- GNUmakefile 14 Jan 2001 19:57:59 -0000 1.1.1.1 +++ GNUmakefile 9 Apr 2002 16:38:51 -0000 1.2 @@ -1,7 +1,8 @@ -CFLAGS = -Wall -O2 -CPPFLAGS = -I../../fs/cramfs +CC = gcc +CFLAGS = -W -Wall -O2 -g +CPPFLAGS = -I../../include LDLIBS = -lz -PROGS = mkcramfs +PROGS = mkcramfs cramfsck all: $(PROGS) Index: mkcramfs.c =================================================================== RCS file: /cvsroot/linux-vax/kernel-2.4/scripts/cramfs/mkcramfs.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- mkcramfs.c 14 Jan 2001 19:57:59 -0000 1.1.1.1 +++ mkcramfs.c 9 Apr 2002 16:38:51 -0000 1.2 @@ -1,3 +1,23 @@ +/* + * mkcramfs - make a cramfs file system + * + * Copyright (C) 1999-2001 Transmeta Corporation + * + * 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 + */ + #include <sys/types.h> #include <stdio.h> #include <sys/stat.h> @@ -9,43 +29,58 @@ #include <errno.h> #include <string.h> #include <assert.h> - -/* zlib required.. */ +#include <getopt.h> +#include <linux/cramfs_fs.h> #include <zlib.h> -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -#include "cramfs.h" +#define PAD_SIZE 512 /* only 0 and 512 supported by kernel */ static const char *progname = "mkcramfs"; /* N.B. If you change the disk format of cramfs, please update fs/cramfs/README. */ -static void usage(void) +/* Input status of 0 to print help and exit without an error. */ +static void usage(int status) { - fprintf(stderr, "Usage: '%s dirname outfile'\n" - " where <dirname> is the root of the\n" - " filesystem to be compressed.\n", progname); - exit(1); -} + FILE *stream = status ? stderr : stdout; -/* - * If DO_HOLES is defined, then mkcramfs can create explicit holes in the - * data, which saves 26 bytes per hole (which is a lot smaller a saving than - * most filesystems). - * - * Note that kernels up to at least 2.3.39 don't support cramfs holes, which - * is why this defaults to undefined at the moment. - */ -/* #define DO_HOLES 1 */ + fprintf(stream, "usage: %s [-h] [-e edition] [-i file] [-n name] dirname outfile\n" + " -h print this help\n" + " -E make all warnings errors (non-zero exit status)\n" + " -e edition set edition number (part of fsid)\n" + " -i file insert a file image into the filesystem (requires >= 2.4.0)\n" + " -n name set name of cramfs filesystem\n" + " -p pad by %d bytes for boot code\n" + " -s sort directory entries (old option, ignored)\n" + " -z make explicit holes (requires >= 2.3.39)\n" + " dirname root of the filesystem to be compressed\n" + " outfile output file\n", progname, PAD_SIZE); + + exit(status); +} #define PAGE_CACHE_SIZE (4096) /* The kernel assumes PAGE_CACHE_SIZE as block size. */ static unsigned int blksize = PAGE_CACHE_SIZE; +static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */ +static int image_length = 0; + +/* + * If opt_holes is set, then mkcramfs can create explicit holes in the + * data, which saves 26 bytes per hole (which is a lot smaller a + * saving than most most filesystems). + * + * Note that kernels up to at least 2.3.39 don't support cramfs holes, + * which is why this is turned off by default. + */ +static int opt_edition = 0; +static int opt_errors = 0; +static int opt_holes = 0; +static int opt_pad = 0; +static char *opt_image = NULL; +static char *opt_name = NULL; -static int warn_dev, warn_gid, warn_namelen, warn_size, warn_uid; +static int warn_dev, warn_gid, warn_namelen, warn_skip, warn_size, warn_uid; #ifndef MIN # define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) @@ -70,15 +105,6 @@ }; /* - * Width of various bitfields in struct cramfs_inode. - * Used only to generate warnings. - */ -#define SIZE_WIDTH 24 -#define UID_WIDTH 16 -#define GID_WIDTH 8 -#define OFFSET_WIDTH 26 - -/* * The longest file name component to allow for in the input directory tree. * Ext2fs (and many others) allow up to 255 bytes. A couple of filesystems * allow longer (e.g. smbfs 1024), but there isn't much use in supporting @@ -101,45 +127,60 @@ static void eliminate_doubles(struct entry *root,struct entry *orig) { if(orig) { - if(orig->size && orig->uncompressed) + if(orig->size && orig->uncompressed) find_identical_file(root,orig); eliminate_doubles(root,orig->child); eliminate_doubles(root,orig->next); } } +/* + * We define our own sorting function instead of using alphasort which + * uses strcoll and changes ordering based on locale information. + */ +static int cramsort (const void *a, const void *b) +{ + return strcmp ((*(const struct dirent **) a)->d_name, + (*(const struct dirent **) b)->d_name); +} + static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub) { - DIR *dir; - int count = 0, totalsize = 0; - struct dirent *dirent; + struct dirent **dirlist; + int totalsize = 0, dircount, dirindex; char *path, *endpath; size_t len = strlen(name); - dir = opendir(name); - if (!dir) { - perror(name); - exit(2); - } - /* Set up the path. */ /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */ path = malloc(len + 1 + MAX_INPUT_NAMELEN + 1); if (!path) { perror(NULL); - exit(1); + exit(8); } memcpy(path, name, len); endpath = path + len; *endpath = '/'; endpath++; - while ((dirent = readdir(dir)) != NULL) { + /* read in the directory and sort */ + dircount = scandir(name, &dirlist, 0, cramsort); + + if (dircount < 0) { + perror(name); + exit(8); + } + + /* process directory */ + for (dirindex = 0; dirindex < dircount; dirindex++) { + struct dirent *dirent; struct entry *entry; struct stat st; int size; size_t namelen; + dirent = dirlist[dirindex]; + /* Ignore "." and ".." - we won't be adding them to the archive */ if (dirent->d_name[0] == '.') { if (dirent->d_name[1] == '\0') @@ -155,23 +196,24 @@ "Very long (%u bytes) filename `%s' found.\n" " Please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile. Exiting.\n", namelen, dirent->d_name); - exit(1); + exit(8); } memcpy(endpath, dirent->d_name, namelen + 1); if (lstat(path, &st) < 0) { perror(endpath); + warn_skip = 1; continue; } entry = calloc(1, sizeof(struct entry)); if (!entry) { perror(NULL); - exit(5); + exit(8); } entry->name = strdup(dirent->d_name); if (!entry->name) { perror(NULL); - exit(1); + exit(8); } if (namelen > 255) { /* Can't happen when reading from ext2fs. */ @@ -184,10 +226,10 @@ entry->mode = st.st_mode; entry->size = st.st_size; entry->uid = st.st_uid; - if (entry->uid >= 1 << UID_WIDTH) + if (entry->uid >= 1 << CRAMFS_UID_WIDTH) warn_uid = 1; entry->gid = st.st_gid; - if (entry->gid >= 1 << GID_WIDTH) + if (entry->gid >= 1 << CRAMFS_GID_WIDTH) /* TODO: We ought to replace with a default gid instead of truncating; otherwise there are security problems. Maybe mode should @@ -211,18 +253,19 @@ int fd = open(path, O_RDONLY); if (fd < 0) { perror(path); + warn_skip = 1; continue; } if (entry->size) { - if ((entry->size >= 1 << SIZE_WIDTH)) { + if ((entry->size >= 1 << CRAMFS_SIZE_WIDTH)) { warn_size = 1; - entry->size = (1 << SIZE_WIDTH) - 1; + entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1; } entry->uncompressed = mmap(NULL, entry->size, PROT_READ, MAP_PRIVATE, fd, 0); if (-1 == (int) (long) entry->uncompressed) { perror("mmap"); - exit(5); + exit(8); } } close(fd); @@ -230,63 +273,69 @@ entry->uncompressed = malloc(entry->size); if (!entry->uncompressed) { perror(NULL); - exit(5); + exit(8); } if (readlink(path, entry->uncompressed, entry->size) < 0) { perror(path); + warn_skip = 1; continue; } + } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) { + /* maybe we should skip sockets */ + entry->size = 0; } else { entry->size = st.st_rdev; - if (entry->size & -(1<<SIZE_WIDTH)) + if (entry->size & -(1<<CRAMFS_SIZE_WIDTH)) warn_dev = 1; } if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + int blocks = ((entry->size - 1) / blksize + 1); + /* block pointers & data expansion allowance + data */ - if(entry->size) - *fslen_ub += ((4+26)*((entry->size - 1) / blksize + 1) - + MIN(entry->size + 3, st.st_blocks << 9)); - else - *fslen_ub += MIN(entry->size + 3, st.st_blocks << 9); + if(entry->size) + *fslen_ub += (4+26)*blocks + entry->size + 3; } /* Link it into the list */ *prev = entry; prev = &entry->next; - count++; totalsize += size; } - closedir(dir); free(path); + free(dirlist); /* allocated by scandir() with malloc() */ return totalsize; } -static void set_random(void *area, size_t size) -{ - int fd = open("/dev/random", O_RDONLY); - - if (fd >= 0) { - if (read(fd, area, size) == size) - return; - } - memset(area, 0x00, size); -} - /* Returns sizeof(struct cramfs_super), which includes the root inode. */ -static unsigned int write_superblock(struct entry *root, char *base) +static unsigned int write_superblock(struct entry *root, char *base, int size) { struct cramfs_super *super = (struct cramfs_super *) base; - unsigned int offset = sizeof(struct cramfs_super); + unsigned int offset = sizeof(struct cramfs_super) + image_length; + + if (opt_pad) { + offset += opt_pad; + } super->magic = CRAMFS_MAGIC; - super->flags = 0; - /* Note: 0x10000 is meaningless, which is a bug; but - super->size is never used anyway. */ - super->size = 0x10000; + super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS; + if (opt_holes) + super->flags |= CRAMFS_FLAG_HOLES; + if (image_length > 0) + super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET; + super->size = size; memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature)); - set_random(super->fsid, sizeof(super->fsid)); - strncpy(super->name, "Compressed", sizeof(super->name)); + + super->fsid.crc = crc32(0L, Z_NULL, 0); + super->fsid.edition = opt_edition; + super->fsid.blocks = total_blocks; + super->fsid.files = total_nodes; + + memset(super->name, 0x00, sizeof(super->name)); + if (opt_name) + strncpy(super->name, opt_name, sizeof(super->name)); + else + strncpy(super->name, "Compressed", sizeof(super->name)); super->root.mode = root->mode; super->root.uid = root->uid; @@ -300,10 +349,12 @@ static void set_data_offset(struct entry *entry, char *base, unsigned long offset) { struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset); +#ifdef DEBUG assert ((offset & 3) == 0); - if (offset >= (1 << (2 + OFFSET_WIDTH))) { +#endif /* DEBUG */ + if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) { fprintf(stderr, "filesystem too big. Exiting.\n"); - exit(1); + exit(8); } inode->offset = (offset >> 2); } @@ -337,6 +388,7 @@ write over inode->offset later. */ offset += sizeof(struct cramfs_inode); + total_nodes++; /* another node */ memcpy(base + offset, entry->name, len); /* Pad up the name to a 4-byte boundary */ while (len & 3) { @@ -354,7 +406,7 @@ if (entry->child) { if (stack_entries >= MAXENTRIES) { fprintf(stderr, "Exceeded MAXENTRIES. Raise this value in mkcramfs.c and recompile. Exiting.\n"); - exit(1); + exit(8); } entry_stack[stack_entries] = entry; stack_entries++; @@ -393,26 +445,24 @@ return offset; } -#ifdef DO_HOLES -/* - * Returns non-zero iff the first LEN bytes from BEGIN are all NULs. - */ -static int -is_zero(char const *begin, unsigned len) +static int is_zero(char const *begin, unsigned len) { - return (len-- == 0 || - (begin[0] == '\0' && - (len-- == 0 || - (begin[1] == '\0' && - (len-- == 0 || - (begin[2] == '\0' && - (len-- == 0 || - (begin[3] == '\0' && - memcmp(begin, begin + 4, len) == 0)))))))); -} -#else /* !DO_HOLES */ -# define is_zero(_begin,_len) (0) /* Never create holes. */ -#endif /* !DO_HOLES */ + if (opt_holes) + /* Returns non-zero iff the first LEN bytes from BEGIN are + all NULs. */ + return (len-- == 0 || + (begin[0] == '\0' && + (len-- == 0 || + (begin[1] == '\0' && + (len-- == 0 || + (begin[2] == '\0' && + (len-- == 0 || + (begin[3] == '\0' && + memcmp(begin, begin + 4, len) == 0)))))))); + else + /* Never create holes. */ + return 0; +} /* * One 4-byte pointer per block and then the actual blocked @@ -433,6 +483,8 @@ unsigned long curr = offset + 4 * blocks; int change; + total_blocks += blocks; + do { unsigned long len = 2 * blksize; unsigned int input = size; @@ -448,7 +500,7 @@ if (len > blksize*2) { /* (I don't think this can happen with zlib.) */ printf("AIEEE: block \"compressed\" to > 2*blocklength (%ld)\n", len); - exit(1); + exit(8); } *(u32 *) (base + offset) = curr; @@ -493,6 +545,27 @@ return offset; } +static unsigned int write_file(char *file, char *base, unsigned int offset) +{ + int fd; + char *buf; + + fd = open(file, O_RDONLY); + if (fd < 0) { + perror(file); + exit(8); + } + buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0); + memcpy(base + offset, buf, image_length); + munmap(buf, image_length); + close (fd); + /* Pad up the image_length to a 4-byte boundary */ + while (image_length & 3) { + *(base + offset + image_length) = '\0'; + image_length++; + } + return (offset + image_length); +} /* * Maximum size fs you can create is roughly 256MB. (The last file's @@ -501,9 +574,9 @@ * Note that if you want it to fit in a ROM then you're limited to what the * hardware and kernel can support (64MB?). */ -#define MAXFSLEN ((((1 << OFFSET_WIDTH) - 1) << 2) /* offset */ \ - + (1 << SIZE_WIDTH) - 1 /* filesize */ \ - + (1 << SIZE_WIDTH) * 4 / PAGE_CACHE_SIZE /* block pointers */ ) +#define MAXFSLEN ((((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2) /* offset */ \ + + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */ \ + + (1 << CRAMFS_SIZE_WIDTH) * 4 / PAGE_CACHE_SIZE /* block pointers */ ) /* @@ -517,41 +590,88 @@ */ int main(int argc, char **argv) { - struct stat st; + struct stat st; /* used twice... */ struct entry *root_entry; char *rom_image; - unsigned int offset; - ssize_t written; + ssize_t offset, written; int fd; - loff_t fslen_ub = 0; /* initial guess (upper-bound) of - required filesystem size */ - char const *dirname; + /* initial guess (upper-bound) of required filesystem size */ + loff_t fslen_ub = sizeof(struct cramfs_super); + char const *dirname, *outfile; + u32 crc = crc32(0L, Z_NULL, 0); + int c; /* for getopt */ + + total_blocks = 0; if (argc) progname = argv[0]; - if (argc != 3) - usage(); - if (stat(dirname = argv[1], &st) < 0) { - perror(argv[1]); - exit(1); + /* command line options */ + while ((c = getopt(argc, argv, "hEe:i:n:psz")) != EOF) { + switch (c) { + case 'h': + usage(0); + case 'E': + opt_errors = 1; + break; + case 'e': + opt_edition = atoi(optarg); + break; + case 'i': + opt_image = optarg; + if (lstat(opt_image, &st) < 0) { + perror(opt_image); + exit(16); + } + image_length = st.st_size; /* may be padded later */ + fslen_ub += (image_length + 3); /* 3 is for padding */ + break; + case 'n': + opt_name = optarg; + break; + case 'p': + opt_pad = PAD_SIZE; + fslen_ub += PAD_SIZE; + break; + case 's': + /* old option, ignored */ + break; + case 'z': + opt_holes = 1; + break; + } + } + + if ((argc - optind) != 2) + usage(16); + dirname = argv[optind]; + outfile = argv[optind + 1]; + + if (stat(dirname, &st) < 0) { + perror(dirname); + exit(16); } - fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666); + fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); root_entry = calloc(1, sizeof(struct entry)); if (!root_entry) { perror(NULL); - exit(5); + exit(8); } root_entry->mode = st.st_mode; root_entry->uid = st.st_uid; root_entry->gid = st.st_gid; - root_entry->size = parse_directory(root_entry, argv[1], &root_entry->child, &fslen_ub); + root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub); + + /* always allocate a multiple of blksize bytes because that's + what we're going to write later on */ + fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1; + if (fslen_ub > MAXFSLEN) { fprintf(stderr, - "warning: guestimate of required size (upper bound) is %luMB, but maximum image size is %uMB. We might die prematurely.\n", - (unsigned long) (fslen_ub >> 20), + "warning: guestimate of required size (upper bound) is %LdMB, but maximum image size is %uMB. We might die prematurely.\n", + fslen_ub >> 20, MAXFSLEN >> 20); fslen_ub = MAXFSLEN; } @@ -560,7 +680,6 @@ possible. */ eliminate_doubles(root_entry,root_entry); - /* TODO: Why do we use a private/anonymous mapping here followed by a write below, instead of just a shared mapping and a couple of ftruncate calls? Is it just to save us @@ -570,13 +689,25 @@ RAM free. If the reason is to be able to write to un-mmappable block devices, then we could try shared mmap and revert to anonymous mmap if the shared mmap fails. */ - rom_image = mmap(NULL, fslen_ub, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + rom_image = mmap(NULL, fslen_ub?fslen_ub:1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (-1 == (int) (long) rom_image) { perror("ROM image map"); - exit(1); + exit(8); + } + + /* Skip the first opt_pad bytes for boot loader code */ + offset = opt_pad; + memset(rom_image, 0x00, opt_pad); + + /* Skip the superblock and come back to write it later. */ + offset += sizeof(struct cramfs_super); + + /* Insert a file image. */ + if (opt_image) { + printf("Including: %s\n", opt_image); + offset = write_file(opt_image, rom_image, offset); } - offset = write_superblock(root_entry, rom_image); - printf("Super block: %d bytes\n", offset); offset = write_directory_structure(root_entry->child, rom_image, offset); printf("Directory data: %d bytes\n", offset); @@ -588,14 +719,30 @@ offset = ((offset - 1) | (blksize - 1)) + 1; printf("Everything: %d kilobytes\n", offset >> 10); + /* Write the superblock now that we can fill in all of the fields. */ + write_superblock(root_entry, rom_image+opt_pad, offset); + printf("Super block: %d bytes\n", sizeof(struct cramfs_super)); + + /* Put the checksum in. */ + crc = crc32(crc, (rom_image+opt_pad), (offset-opt_pad)); + ((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = crc; + printf("CRC: %x\n", crc); + + /* Check to make sure we allocated enough space. */ + if (fslen_ub < offset) { + fprintf(stderr, "not enough space allocated for ROM image (%Ld allocated, %d used)\n", + fslen_ub, offset); + exit(8); + } + written = write(fd, rom_image, offset); if (written < 0) { - perror("rom image"); - exit(1); + perror("ROM image"); + exit(8); } if (offset != written) { fprintf(stderr, "ROM image write failed (%d %d)\n", written, offset); - exit(1); + exit(8); } /* (These warnings used to come at the start, but they scroll off the @@ -603,22 +750,27 @@ if (warn_namelen) /* (can't happen when reading from ext2fs) */ fprintf(stderr, /* bytes, not chars: think UTF8. */ "warning: filenames truncated to 255 bytes.\n"); + if (warn_skip) + fprintf(stderr, "warning: files were skipped due to errors.\n"); if (warn_size) fprintf(stderr, "warning: file sizes truncated to %luMB (minus 1 byte).\n", - 1L << (SIZE_WIDTH - 20)); + 1L << (CRAMFS_SIZE_WIDTH - 20)); if (warn_uid) /* (not possible with current Linux versions) */ fprintf(stderr, "warning: uids truncated to %u bits. (This may be a security concern.)\n", - UID_WIDTH); + CRAMFS_UID_WIDTH); if (warn_gid) fprintf(stderr, "warning: gids truncated to %u bits. (This may be a security concern.)\n", - GID_WIDTH); + CRAMFS_GID_WIDTH); if (warn_dev) fprintf(stderr, "WARNING: device numbers truncated to %u bits. This almost certainly means\n" "that some device files will be wrong.\n", - OFFSET_WIDTH); + CRAMFS_OFFSET_WIDTH); + if (opt_errors && + (warn_namelen||warn_skip||warn_size||warn_uid||warn_gid||warn_dev)) + exit(8); return 0; } |