]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/mkfs.cramfs.c
docs: corrections to FSF license files, and postal address
[thirdparty/util-linux.git] / disk-utils / mkfs.cramfs.c
index 32755715dc4f5717ee1e650f6f9e56dfa8058865..a1e16cea9e461e98ecfd60fb553747dbab22bb5c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * mkcramfs - make a cramfs file system
  *
- * Copyright (C) 1999-2001 Transmeta Corporation
+ * Copyright (C) 1999-2002 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
@@ -13,9 +13,9 @@
  * 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
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 /*
 #include <sys/stat.h>
 #include <unistd.h>
 #include <sys/mman.h>
-#include <sys/fcntl.h>
+#include <fcntl.h>
 #include <dirent.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
-#include <assert.h>
 #include <getopt.h>
+#include <zconf.h>
 #include <zlib.h>
 
+#include "c.h"
 #include "cramfs.h"
 #include "md5.h"
 #include "nls.h"
+#include "exitcodes.h"
+#include "strutils.h"
+#define XALLOC_EXIT_CODE MKFS_ERROR
+#include "xalloc.h"
 
-#define PAD_SIZE 512           /* only 0 and 512 supported by kernel */
+/* The kernel only supports PAD_SIZE of 0 and 512. */
+#define PAD_SIZE 512
 
-static const char *progname = "mkcramfs";
 static int verbose = 0;
 
-#ifdef __ia64__
-#define PAGE_CACHE_SIZE (16384)
-#elif defined __alpha__
-#define PAGE_CACHE_SIZE (8192)
-#else
-#define PAGE_CACHE_SIZE (4096)
-#endif
-
-/* The kernel assumes PAGE_CACHE_SIZE as block size. */
-static unsigned int blksize = PAGE_CACHE_SIZE; /* settable via -b option */
+static unsigned int blksize; /* settable via -b option */
 static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */
 static int image_length = 0;
+static int cramfs_is_big_endian = 0; /* target is big endian */
 
 /*
  * If opt_holes is set, then mkcramfs can create explicit holes in the
@@ -85,20 +83,23 @@ static int warn_uid = 0;
 # define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
 #endif
 
+/* entry.flags */
+#define CRAMFS_EFLAG_MD5       1
+#define CRAMFS_EFLAG_INVALID   2
+
 /* In-core version of inode / directory entry. */
 struct entry {
        /* stats */
-       char *name;
+       unsigned char *name;
        unsigned int mode, size, uid, gid;
-       unsigned char md5sum[16];
-       unsigned char flags;
-#define HAVE_MD5       1
-#define        INVALID         2
+       unsigned char md5sum[MD5LENGTH];
+       unsigned char flags;       /* CRAMFS_EFLAG_* */
 
        /* FS data */
        char *path;
-        struct entry *same;        /* points to other identical file */
-        unsigned int offset;        /* pointer to compressed data in archive */
+       int fd;                     /* temporarily open files while mmapped */
+       struct entry *same;         /* points to other identical file */
+       unsigned int offset;        /* pointer to compressed data in archive */
        unsigned int dir_offset;    /* offset of directory entry in archive */
 
        /* organization */
@@ -121,14 +122,15 @@ usage(int status) {
        FILE *stream = status ? stderr : stdout;
 
        fprintf(stream,
-               _("usage: %s [-v] [-b blksz] [-e edition] [-i file] [-n name] "
-                   "dirname outfile\n"
+               _("usage: %s [-h] [-v] [-b blksize] [-e edition] [-N endian] [-i file] "
+                 "[-n name] dirname outfile\n"
                  " -h         print this help\n"
                  " -v         be verbose\n"
                  " -E         make all warnings errors "
                    "(non-zero exit status)\n"
-                 " -b blksz   use this blocksize, must equal page size\n"
+                 " -b blksize use this blocksize, must equal page size\n"
                  " -e edition set edition number (part of fsid)\n"
+                 " -N endian  set cramfs endianness (big|little|host), default host\n"
                  " -i file    insert a file image into the filesystem "
                    "(requires >= 2.4.0)\n"
                  " -n name    set name of cramfs filesystem\n"
@@ -137,22 +139,11 @@ usage(int status) {
                  " -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);
+               program_invocation_short_name, PAD_SIZE);
 
        exit(status);
 }
 
-/* malloc or die */
-static void *
-xmalloc (size_t size) {
-       void *t = malloc(size);
-       if (t == NULL) {
-               perror(NULL);
-               exit(8);        /* out of memory */
-       }
-       return t;
-}
-
 static char *
 do_mmap(char *path, unsigned int size, unsigned int mode){
        int fd;
@@ -179,10 +170,8 @@ do_mmap(char *path, unsigned int size, unsigned int mode){
        }
 
        start = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (-1 == (int) (long) start) {
-               perror("mmap");
-               exit(8);
-       }
+       if (-1 == (int) (long) start)
+               err(MKFS_ERROR, "mmap");
        close(fd);
 
        return start;
@@ -204,15 +193,15 @@ mdfile(struct entry *e) {
 
        start = do_mmap(e->path, e->size, e->mode);
        if (start == NULL) {
-               e->flags |= INVALID;
+               e->flags |= CRAMFS_EFLAG_INVALID;
        } else {
                MD5Init(&ctx);
-               MD5Update(&ctx, start, e->size);
+               MD5Update(&ctx, (unsigned char *) start, e->size);
                MD5Final(e->md5sum, &ctx);
 
                do_munmap(start, e->size, e->mode);
 
-               e->flags |= HAVE_MD5;
+               e->flags |= CRAMFS_EFLAG_MD5;
        }
 }
 
@@ -244,46 +233,47 @@ identical_file(struct entry *e1, struct entry *e2){
  */
 #define MAX_INPUT_NAMELEN 255
 
-static int find_identical_file(struct entry *orig, struct entry *new)
+static int find_identical_file(struct entry *orig, struct entry *new, loff_t *fslen_ub)
 {
-        if (orig == new)
+       if (orig == new)
                return 1;
-        if (!orig)
+       if (!orig)
                return 0;
-        if (orig->size == new->size && orig->path) {
+       if (orig->size == new->size && orig->path) {
                if (!orig->flags)
                        mdfile(orig);
                if (!new->flags)
                        mdfile(new);
 
-               if ((orig->flags & HAVE_MD5) && (new->flags & HAVE_MD5) &&
-                   !memcmp(orig->md5sum, new->md5sum, 16) &&
+               if ((orig->flags & CRAMFS_EFLAG_MD5) &&
+                   (new->flags & CRAMFS_EFLAG_MD5) &&
+                   !memcmp(orig->md5sum, new->md5sum, MD5LENGTH) &&
                    identical_file(orig, new)) {
                        new->same = orig;
+                       *fslen_ub -= new->size;
                        return 1;
                }
-        }
-        return find_identical_file(orig->child, new) ||
-                   find_identical_file(orig->next, new);
+       }
+       return find_identical_file(orig->child, new, fslen_ub) ||
+                  find_identical_file(orig->next, new, fslen_ub);
 }
 
-static void eliminate_doubles(struct entry *root, struct entry *orig) {
-        if (orig) {
-                if (orig->size && orig->path)
-                       find_identical_file(root,orig);
-                eliminate_doubles(root,orig->child);
-                eliminate_doubles(root,orig->next);
-        }
+static void eliminate_doubles(struct entry *root, struct entry *orig, loff_t *fslen_ub) {
+       if (orig) {
+               if (orig->size && orig->path)
+                       find_identical_file(root,orig, fslen_ub);
+               eliminate_doubles(root,orig->child, fslen_ub);
+               eliminate_doubles(root,orig->next, fslen_ub);
+       }
 }
 
 /*
  * 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)
+static int cramsort (const struct dirent **a, const struct dirent **b)
 {
-       return strcmp ((*(const struct dirent **) a)->d_name,
-                      (*(const struct dirent **) b)->d_name);
+       return strcmp((*a)->d_name, (*b)->d_name);
 }
 
 static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub)
@@ -301,13 +291,11 @@ static unsigned int parse_directory(struct entry *root_entry, const char *name,
        *endpath = '/';
        endpath++;
 
-        /* read in the directory and sort */
-        dircount = scandir(name, &dirlist, 0, cramsort);
+       /* read in the directory and sort */
+       dircount = scandir(name, &dirlist, 0, cramsort);
 
-       if (dircount < 0) {
-               perror(name);
-               exit(8);
-       }
+       if (dircount < 0)
+               err(MKFS_ERROR, _("could not read directory %s"), name);
 
        /* process directory */
        for (dirindex = 0; dirindex < dircount; dirindex++) {
@@ -330,14 +318,12 @@ static unsigned int parse_directory(struct entry *root_entry, const char *name,
                        }
                }
                namelen = strlen(dirent->d_name);
-               if (namelen > MAX_INPUT_NAMELEN) {
-                       fprintf(stderr,
-                               _("Very long (%u bytes) filename `%s' found.\n"
+               if (namelen > MAX_INPUT_NAMELEN)
+                       errx(MKFS_ERROR,
+                               _("Very long (%zu bytes) filename `%s' found.\n"
                                  " Please increase MAX_INPUT_NAMELEN in "
-                                 "mkcramfs.c and recompile.  Exiting.\n"),
+                                 "mkcramfs.c and recompile.  Exiting."),
                                namelen, dirent->d_name);
-                       exit(8);
-               }
                memcpy(endpath, dirent->d_name, namelen + 1);
 
                if (lstat(path, &st) < 0) {
@@ -345,16 +331,8 @@ static unsigned int parse_directory(struct entry *root_entry, const char *name,
                        warn_skip = 1;
                        continue;
                }
-               entry = calloc(1, sizeof(struct entry));
-               if (!entry) {
-                       perror(NULL);
-                       exit(8);
-               }
-               entry->name = strdup(dirent->d_name);
-               if (!entry->name) {
-                       perror(NULL);
-                       exit(8);
-               }
+               entry = xcalloc(1, sizeof(struct entry));
+               entry->name = (unsigned char *)xstrdup(dirent->d_name);
                if (namelen > 255) {
                        /* Can't happen when reading from ext2fs. */
 
@@ -371,17 +349,17 @@ static unsigned int parse_directory(struct entry *root_entry, const char *name,
                entry->gid = st.st_gid;
                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
-                           be &= ~070.  Same goes for uid once Linux
-                           supports >16-bit uids. */
+                          gid instead of truncating; otherwise there
+                          are security problems.  Maybe mode should
+                          be &= ~070.  Same goes for uid once Linux
+                          supports >16-bit uids. */
                        warn_gid = 1;
                size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
                *fslen_ub += size;
                if (S_ISDIR(st.st_mode)) {
                        entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub);
                } else if (S_ISREG(st.st_mode)) {
-                       entry->path = strdup(path);
+                       entry->path = xstrdup(path);
                        if (entry->size) {
                                if (entry->size >= (1 << CRAMFS_SIZE_WIDTH)) {
                                        warn_size = 1;
@@ -389,7 +367,7 @@ static unsigned int parse_directory(struct entry *root_entry, const char *name,
                                }
                        }
                } else if (S_ISLNK(st.st_mode)) {
-                       entry->path = strdup(path);
+                       entry->path = xstrdup(path);
                } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
                        /* maybe we should skip sockets */
                        entry->size = 0;
@@ -405,7 +383,7 @@ static unsigned int parse_directory(struct entry *root_entry, const char *name,
                        /* block pointers & data expansion allowance + data */
                        if (entry->size)
                                *fslen_ub += (4+26)*blocks + entry->size + 3;
-                }
+               }
 
                /* Link it into the list */
                *prev = entry;
@@ -443,9 +421,9 @@ static unsigned int write_superblock(struct entry *root, char *base, int size)
 
        memset(super->name, 0x00, sizeof(super->name));
        if (opt_name)
-               strncpy(super->name, opt_name, sizeof(super->name));
+               strncpy((char *)super->name, opt_name, sizeof(super->name));
        else
-               strncpy(super->name, "Compressed", sizeof(super->name));
+               strncpy((char *)super->name, "Compressed", sizeof(super->name));
 
        super->root.mode = root->mode;
        super->root.uid = root->uid;
@@ -453,17 +431,20 @@ static unsigned int write_superblock(struct entry *root, char *base, int size)
        super->root.size = root->size;
        super->root.offset = offset >> 2;
 
+       super_toggle_endianness(cramfs_is_big_endian, super);
+       inode_from_host(cramfs_is_big_endian, &super->root, &super->root);
+
        return offset;
 }
 
 static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
 {
        struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
-       if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) {
-               fprintf(stderr, _("filesystem too big.  Exiting.\n"));
-               exit(8);
-       }
+       inode_to_host(cramfs_is_big_endian, inode, inode);
+       if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH)))
+               errx(MKFS_ERROR, _("filesystem too big.  Exiting."));
        inode->offset = (offset >> 2);
+       inode_from_host(cramfs_is_big_endian, inode, inode);
 }
 
 
@@ -472,18 +453,20 @@ static void set_data_offset(struct entry *entry, char *base, unsigned long offse
  * entries, using a stack to remember the directories
  * we've seen.
  */
-#define MAXENTRIES (100)
 static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset)
 {
        int stack_entries = 0;
-       struct entry *entry_stack[MAXENTRIES];
+       int stack_size = 64;
+       struct entry **entry_stack;
+
+       entry_stack = xmalloc(stack_size * sizeof(struct entry *));
 
        for (;;) {
                int dir_start = stack_entries;
                while (entry) {
                        struct cramfs_inode *inode =
                                (struct cramfs_inode *) (base + offset);
-                       size_t len = strlen(entry->name);
+                       size_t len = strlen((const char *)entry->name);
 
                        entry->dir_offset = offset;
 
@@ -509,25 +492,22 @@ static unsigned int write_directory_structure(struct entry *entry, char *base, u
                        if (verbose)
                                printf("  %s\n", entry->name);
                        if (entry->child) {
-                               if (stack_entries >= MAXENTRIES) {
-                                       fprintf(stderr,
-                                               _("Exceeded MAXENTRIES.  Raise"
-                                                 " this value in mkcramfs.c "
-                                                 "and recompile.  Exiting.\n")
-                                               );
-                                       exit(8);
+                               if (stack_entries >= stack_size) {
+                                       stack_size *= 2;
+                                       entry_stack = xrealloc(entry_stack, stack_size * sizeof(struct entry *));
                                }
                                entry_stack[stack_entries] = entry;
                                stack_entries++;
                        }
+                       inode_from_host(cramfs_is_big_endian, inode, inode);
                        entry = entry->next;
                }
 
                /*
                 * Reverse the order the stack entries pushed during
-                 * this directory, for a small optimization of disk
-                 * access in the created fs.  This change makes things
-                 * `ls -UR' order.
+                * this directory, for a small optimization of disk
+                * access in the created fs.  This change makes things
+                * `ls -UR' order.
                 */
                {
                        struct entry **lo = entry_stack + dir_start;
@@ -552,10 +532,11 @@ static unsigned int write_directory_structure(struct entry *entry, char *base, u
                        printf("'%s':\n", entry->name);
                entry = entry->child;
        }
+       free(entry_stack);
        return offset;
 }
 
-static int is_zero(char const *begin, unsigned len)
+static int is_zero(unsigned char const *begin, unsigned len)
 {
        if (opt_holes)
                /* Returns non-zero iff the first LEN bytes from BEGIN are
@@ -585,18 +566,19 @@ static int is_zero(char const *begin, unsigned len)
  * have gotten here in the first place.
  */
 static unsigned int
-do_compress(char *base, unsigned int offset, char const *name,
+do_compress(char *base, unsigned int offset, unsigned char const *name,
            char *path, unsigned int size, unsigned int mode)
 {
        unsigned long original_size, original_offset, new_size, blocks, curr;
-       int change;
-       char *p, *start;
+       long change;
+       char *start;
+       Bytef *p;
 
        /* get uncompressed data */
        start = do_mmap(path, size, mode);
        if (start == NULL)
                return offset;
-       p = start;
+       p = (Bytef *) start;
 
        original_size = size;
        original_offset = offset;
@@ -606,13 +588,13 @@ do_compress(char *base, unsigned int offset, char const *name,
        total_blocks += blocks;
 
        do {
-               unsigned long len = 2 * blksize;
-               unsigned int input = size;
+               uLongf len = 2 * blksize;
+               uLongf input = size;
                if (input > blksize)
                        input = blksize;
                size -= input;
                if (!is_zero (p, input)) {
-                       compress(base + curr, &len, p, input);
+                       compress((Bytef *)(base + curr), &len, p, input);
                        curr += len;
                }
                p += input;
@@ -622,10 +604,10 @@ do_compress(char *base, unsigned int offset, char const *name,
                        printf(_("AIEEE: block \"compressed\" to > "
                                 "2*blocklength (%ld)\n"),
                               len);
-                       exit(8);
+                       exit(MKFS_ERROR);
                }
 
-               *(u32 *) (base + offset) = curr;
+               *(uint32_t *) (base + offset) = u32_toggle_endianness(cramfs_is_big_endian, curr);
                offset += 4;
        } while (size);
 
@@ -638,7 +620,7 @@ do_compress(char *base, unsigned int offset, char const *name,
           administrative data should also be included in both. */
        change = new_size - original_size;
        if (verbose)
-               printf(_("%6.2f%% (%+d bytes)\t%s\n"),
+               printf(_("%6.2f%% (%+ld bytes)\t%s\n"),
                       (change * 100) / (double) original_size, change, name);
 
        return curr;
@@ -656,15 +638,15 @@ write_data(struct entry *entry, char *base, unsigned int offset) {
 
        for (e = entry; e; e = e->next) {
                if (e->path) {
-                        if (e->same) {
-                                set_data_offset(e, base, e->same->offset);
-                                e->offset = e->same->offset;
-                        } else {
-                                set_data_offset(e, base, offset);
-                                e->offset = offset;
-                                offset = do_compress(base, offset, e->name,
+                       if (e->same) {
+                               set_data_offset(e, base, e->same->offset);
+                               e->offset = e->same->offset;
+                       } else if (e->size) {
+                               set_data_offset(e, base, offset);
+                               e->offset = offset;
+                               offset = do_compress(base, offset, e->name,
                                                     e->path, e->size,e->mode);
-                        }
+                       }
                } else if (e->child)
                        offset = write_data(e->child, base, offset);
        }
@@ -677,14 +659,13 @@ static unsigned int write_file(char *file, char *base, unsigned int offset)
        char *buf;
 
        fd = open(file, O_RDONLY);
-       if (fd < 0) {
-               perror(file);
-               exit(8);
-       }
+       if (fd < 0)
+               err(MKFS_ERROR, _("cannot open file %s"), file);
        buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0);
        memcpy(base + offset, buf, image_length);
        munmap(buf, image_length);
-       close (fd);
+       if (close (fd) < 0)
+               err(MKFS_ERROR, _("cannot close file %s"), file);
        /* Pad up the image_length to a 4-byte boundary */
        while (image_length & 3) {
                *(base + offset + image_length) = '\0';
@@ -727,40 +708,53 @@ int main(int argc, char **argv)
        loff_t fslen_ub = sizeof(struct cramfs_super);
        unsigned int fslen_max;
        char const *dirname, *outfile;
-       u32 crc = crc32(0L, Z_NULL, 0);
+       uint32_t crc = crc32(0L, Z_NULL, 0);
        int c;
+       cramfs_is_big_endian = HOST_IS_BIG_ENDIAN; /* default is to use host order */
 
+       blksize = getpagesize();
        total_blocks = 0;
 
-       if (argc) {
-               char *p;
-               progname = argv[0];
-               if ((p = strrchr(progname, '/')) != NULL)
-                       progname = p+1;
-       }
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
 
        /* command line options */
-       while ((c = getopt(argc, argv, "hb:Ee:i:n:psVvz")) != EOF) {
+       while ((c = getopt(argc, argv, "hb:Ee:i:n:N:psVvz")) != EOF) {
                switch (c) {
                case 'h':
-                       usage(0);
+                       usage(MKFS_OK);
                case 'b':
-                       blksize = atoi(optarg);
-                       if (blksize <= 0)
-                               usage(1);
+               {
+                       long long tmp = strtoll_or_err(optarg,
+                                       _("failed to parse blocksize argument"));
+
+                       if (tmp <= 0 || UINT_MAX < tmp)
+                               errx(MKFS_USAGE, _("invalid block size"));
+                       blksize = tmp;
                        break;
+               }
                case 'E':
                        opt_errors = 1;
                        break;
                case 'e':
-                       opt_edition = atoi(optarg);
+                       opt_edition = strtoll_or_err(optarg, _("edition number argument failed"));
+                       break;
+               case 'N':
+                       if (strcmp(optarg, "big") == 0)
+                               cramfs_is_big_endian = 1;
+                       else if (strcmp(optarg, "little") == 0)
+                               cramfs_is_big_endian = 0;
+                       else if (strcmp(optarg, "host") == 0)
+                               /* default */ ;
+                       else
+                               errx(MKFS_USAGE, _("invalid endianness given."
+                                                  " Must be 'big', 'little', or 'host'"));
                        break;
                case 'i':
                        opt_image = optarg;
-                       if (lstat(opt_image, &st) < 0) {
-                               perror(opt_image);
-                               exit(16);
-                       }
+                       if (lstat(opt_image, &st) < 0)
+                               err(MKFS_USAGE, _("cannot stat %s"), opt_image);
                        image_length = st.st_size; /* may be padded later */
                        fslen_ub += (image_length + 3); /* 3 is for padding */
                        break;
@@ -776,8 +770,8 @@ int main(int argc, char **argv)
                        break;
                case 'V':
                        printf(_("%s from %s\n"),
-                              progname, util_linux_version);
-                       exit(0);
+                              program_invocation_short_name, PACKAGE_STRING);
+                       exit(MKFS_OK);
                case 'v':
                        verbose = 1;
                        break;
@@ -788,64 +782,57 @@ int main(int argc, char **argv)
        }
 
        if ((argc - optind) != 2)
-               usage(16);
+               usage(MKFS_USAGE);
        dirname = argv[optind];
        outfile = argv[optind + 1];
 
-       if (stat(dirname, &st) < 0) {
-               perror(dirname);
-               exit(16);
-       }
+       if (stat(dirname, &st) < 0)
+               err(MKFS_USAGE, _("cannot stat %s"), dirname);
        fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+       if (fd < 0)
+               err(MKFS_USAGE, _("cannot open %s"), outfile);
 
-       root_entry = calloc(1, sizeof(struct entry));
-       if (!root_entry) {
-               perror(NULL);
-               exit(8);
-       }
+       root_entry = xcalloc(1, sizeof(struct entry));
        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, dirname, &root_entry->child, &fslen_ub);
 
+       /* find duplicate files */
+       eliminate_doubles(root_entry,root_entry, &fslen_ub);
+
        /* always allocate a multiple of blksize bytes because that's
-           what we're going to write later on */
+          what we're going to write later on */
        fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1;
        fslen_max = maxfslen();
 
        if (fslen_ub > fslen_max) {
-               fprintf(stderr,
-                       _("warning: guestimate of required size (upper bound) "
-                         "is %LdMB, but maximum image size is %uMB.  "
-                         "We might die prematurely.\n"),
-                       fslen_ub >> 20,
+               warnx(  _("warning: guestimate of required size (upper bound) "
+                         "is %lldMB, but maximum image size is %uMB.  "
+                         "We might die prematurely."),
+                       (long long)fslen_ub >> 20,
                        fslen_max >> 20);
                fslen_ub = fslen_max;
        }
 
-        /* find duplicate files */
-        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
-           having to deal with removing the file afterwards?  If we
-           really need this huge anonymous mapping, we ought to mmap
-           in smaller chunks, so that the user doesn't need nn MB of
-           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. */
+          followed by a write below, instead of just a shared mapping
+          and a couple of ftruncate calls?  Is it just to save us
+          having to deal with removing the file afterwards?  If we
+          really need this huge anonymous mapping, we ought to mmap
+          in smaller chunks, so that the user doesn't need nn MB of
+          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?fslen_ub:1,
                         PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS,
                         -1, 0);
 
-       if (-1 == (int) (long) rom_image) {
-               perror("ROM image map");
-               exit(8);
-       }
+       if (-1 == (int) (long) rom_image)
+               err(MKFS_ERROR, _("ROM image map"));
 
        /* Skip the first opt_pad bytes for boot loader code */
        offset = opt_pad;
@@ -863,79 +850,71 @@ int main(int argc, char **argv)
 
        offset = write_directory_structure(root_entry->child, rom_image, offset);
        if (verbose)
-               printf(_("Directory data: %d bytes\n"), offset);
+               printf(_("Directory data: %zd bytes\n"), offset);
 
        offset = write_data(root_entry, rom_image, offset);
 
        /* We always write a multiple of blksize bytes, so that
-           losetup works. */
+          losetup works. */
        offset = ((offset - 1) | (blksize - 1)) + 1;
        if (verbose)
-               printf(_("Everything: %d kilobytes\n"), offset >> 10);
+               printf(_("Everything: %zd 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);
        if (verbose)
-               printf(_("Super block: %d bytes\n"),
+               printf(_("Super block: %zd 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;
+       crc = crc32(crc, (unsigned char *) (rom_image+opt_pad), (offset-opt_pad));
+       ((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = u32_toggle_endianness(cramfs_is_big_endian, crc);
        if (verbose)
                printf(_("CRC: %x\n"), crc);
 
        /* Check to make sure we allocated enough space. */
-       if (fslen_ub < offset) {
-               fprintf(stderr,
+       if (fslen_ub < offset)
+               errx(MKFS_ERROR,
                        _("not enough space allocated for ROM image "
-                         "(%Ld allocated, %d used)\n"),
-                       fslen_ub, offset);
-               exit(8);
-       }
+                         "(%lld allocated, %zu used)"),
+                       (long long) fslen_ub, offset);
 
        written = write(fd, rom_image, offset);
-       if (written < 0) {
-               perror("ROM image");
-               exit(8);
-       }
-       if (offset != written) {
-               fprintf(stderr, _("ROM image write failed (%d %d)\n"),
+       close(fd);
+       if (written < 0)
+               err(MKFS_ERROR, _("ROM image"));
+       if (offset != written)
+               errx(MKFS_ERROR, _("ROM image write failed (%zd %zd)"),
                        written, offset);
-               exit(8);
-       }
 
-       /* (These warnings used to come at the start, but they scroll off the
-           screen too quickly.) */
-       if (warn_namelen) /* (can't happen when reading from ext2fs) */
-               fprintf(stderr, /* bytes, not chars: think UTF8. */
-                       _("warning: filenames truncated to 255 bytes.\n"));
+       /*
+        * (These warnings used to come at the start, but they scroll off
+        * the screen too quickly.)
+        */
+       if (warn_namelen)
+               /* Can't happen when reading from ext2fs. */
+               /* Bytes, not chars: think UTF8. */
+               warnx(_("warning: filenames truncated to 255 bytes."));
        if (warn_skip)
-               fprintf(stderr,
-                       _("warning: files were skipped due to errors.\n"));
+               warnx(_("warning: files were skipped due to errors."));
        if (warn_size)
-               fprintf(stderr,
-                       _("warning: file sizes truncated to %luMB "
-                         "(minus 1 byte).\n"),
-                       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"),
-                       CRAMFS_UID_WIDTH);
+               warnx(_("warning: file sizes truncated to %luMB "
+                       "(minus 1 byte)."), 1L << (CRAMFS_SIZE_WIDTH - 20));
+       if (warn_uid)
+               /* (not possible with current Linux versions) */
+               warnx(_("warning: uids truncated to %u bits.  "
+                       "(This may be a security concern.)"), CRAMFS_UID_WIDTH);
        if (warn_gid)
-               fprintf(stderr,
-                       _("warning: gids truncated to %u bits.  "
-                         "(This may be a security concern.)\n"),
-                       CRAMFS_GID_WIDTH);
+               warnx(_("warning: gids truncated to %u bits.  "
+                       "(This may be a security concern.)"), 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"),
-                       CRAMFS_OFFSET_WIDTH);
+               warnx(_("WARNING: device numbers truncated to %u bits.  "
+                       "This almost certainly means\n"
+                       "that some device files will be wrong."),
+                     CRAMFS_OFFSET_WIDTH);
        if (opt_errors &&
            (warn_namelen|warn_skip|warn_size|warn_uid|warn_gid|warn_dev))
-               exit(8);
-       return 0;
+               exit(MKFS_ERROR);
+
+       return EXIT_SUCCESS;
 }