Upload Tizen:Base source
[framework/base/util-linux-ng.git] / disk-utils / mkfs.cramfs.c
1 /*
2  * mkcramfs - make a cramfs file system
3  *
4  * Copyright (C) 1999-2002 Transmeta Corporation
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /*
22  * Old version would die on largish filesystems. Change to mmap the
23  * files one by one instaed of all simultaneously. - aeb, 2002-11-01
24  */
25
26 #include <sys/types.h>
27 #include <stdio.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <sys/mman.h>
31 #include <fcntl.h>
32 #include <dirent.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <getopt.h>
37 #include <stdarg.h>
38 #include <zlib.h>
39
40 #include "cramfs.h"
41 #include "cramfs_common.h"
42 #include "md5.h"
43 #include "nls.h"
44
45 /* Exit codes used by mkfs-type programs */
46 #define MKFS_OK          0     /* No errors */
47 #define MKFS_ERROR       8     /* Operational error */
48 #define MKFS_USAGE       16    /* Usage or syntax error */
49
50 /* The kernel only supports PAD_SIZE of 0 and 512. */
51 #define PAD_SIZE 512
52
53 static const char *progname = "mkcramfs";
54 static int verbose = 0;
55
56 static unsigned int blksize; /* settable via -b option */
57 static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */
58 static int image_length = 0;
59 static int cramfs_is_big_endian = 0; /* target is big endian */
60
61 /*
62  * If opt_holes is set, then mkcramfs can create explicit holes in the
63  * data, which saves 26 bytes per hole (which is a lot smaller a
64  * saving than for most filesystems).
65  *
66  * Note that kernels up to at least 2.3.39 don't support cramfs holes,
67  * which is why this is turned off by default.
68  */
69 static int opt_edition = 0;
70 static int opt_errors = 0;
71 static int opt_holes = 0;
72 static int opt_pad = 0;
73 static char *opt_image = NULL;
74 static char *opt_name = NULL;
75
76 static int warn_dev = 0;
77 static int warn_gid = 0;
78 static int warn_namelen = 0;
79 static int warn_skip = 0;
80 static int warn_size = 0;
81 static int warn_uid = 0;
82
83 #ifndef MIN
84 # define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
85 #endif
86
87 /* entry.flags */
88 #define CRAMFS_EFLAG_MD5        1
89 #define CRAMFS_EFLAG_INVALID    2
90
91 /* In-core version of inode / directory entry. */
92 struct entry {
93         /* stats */
94         unsigned char *name;
95         unsigned int mode, size, uid, gid;
96         unsigned char md5sum[16];
97         unsigned char flags;       /* CRAMFS_EFLAG_* */
98
99         /* FS data */
100         char *path;
101         int fd;                     /* temporarily open files while mmapped */
102         struct entry *same;         /* points to other identical file */
103         unsigned int offset;        /* pointer to compressed data in archive */
104         unsigned int dir_offset;    /* offset of directory entry in archive */
105
106         /* organization */
107         struct entry *child;        /* NULL for non-directory and empty dir */
108         struct entry *next;
109 };
110
111 /*
112  * Width of various bitfields in struct cramfs_inode.
113  * Used only to generate warnings.
114  */
115 #define CRAMFS_SIZE_WIDTH 24
116 #define CRAMFS_UID_WIDTH 16
117 #define CRAMFS_GID_WIDTH 8
118 #define CRAMFS_OFFSET_WIDTH 26
119
120 /* Input status of 0 to print help and exit without an error. */
121 static void
122 usage(int status) {
123         FILE *stream = status ? stderr : stdout;
124
125         fprintf(stream,
126                 _("usage: %s [-h] [-v] [-b blksize] [-e edition] [-N endian] [-i file] "
127                   "[-n name] dirname outfile\n"
128                   " -h         print this help\n"
129                   " -v         be verbose\n"
130                   " -E         make all warnings errors "
131                     "(non-zero exit status)\n"
132                   " -b blksize use this blocksize, must equal page size\n"
133                   " -e edition set edition number (part of fsid)\n"
134                   " -N endian  set cramfs endianness (big|little|host), default host\n"
135                   " -i file    insert a file image into the filesystem "
136                     "(requires >= 2.4.0)\n"
137                   " -n name    set name of cramfs filesystem\n"
138                   " -p         pad by %d bytes for boot code\n"
139                   " -s         sort directory entries (old option, ignored)\n"
140                   " -z         make explicit holes (requires >= 2.3.39)\n"
141                   " dirname    root of the filesystem to be compressed\n"
142                   " outfile    output file\n"),
143                 progname, PAD_SIZE);
144
145         exit(status);
146 }
147
148 /* malloc or die */
149 static void *
150 xmalloc (size_t size) {
151         void *t = malloc(size);
152         if (t == NULL) {
153                 perror(NULL);
154                 exit(8);        /* out of memory */
155         }
156         return t;
157 }
158
159 static char *
160 do_mmap(char *path, unsigned int size, unsigned int mode){
161         int fd;
162         char *start;
163
164         if (!size)
165                 return NULL;
166
167         if (S_ISLNK(mode)) {
168                 start = xmalloc(size);
169                 if (readlink(path, start, size) < 0) {
170                         perror(path);
171                         warn_skip = 1;
172                         start = NULL;
173                 }
174                 return start;
175         }
176
177         fd = open(path, O_RDONLY);
178         if (fd < 0) {
179                 perror(path);
180                 warn_skip = 1;
181                 return NULL;
182         }
183
184         start = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
185         if (-1 == (int) (long) start) {
186                 perror("mmap");
187                 exit(8);
188         }
189         close(fd);
190
191         return start;
192 }
193
194 static void
195 do_munmap(char *start, unsigned int size, unsigned int mode){
196         if (S_ISLNK(mode))
197                 free(start);
198         else
199                 munmap(start, size);
200 }
201
202 /* compute md5sums, so that we do not have to compare every pair of files */
203 static void
204 mdfile(struct entry *e) {
205         MD5_CTX ctx;
206         char *start;
207
208         start = do_mmap(e->path, e->size, e->mode);
209         if (start == NULL) {
210                 e->flags |= CRAMFS_EFLAG_INVALID;
211         } else {
212                 MD5Init(&ctx);
213                 MD5Update(&ctx, (unsigned char *) start, e->size);
214                 MD5Final(e->md5sum, &ctx);
215
216                 do_munmap(start, e->size, e->mode);
217
218                 e->flags |= CRAMFS_EFLAG_MD5;
219         }
220 }
221
222 /* md5 digests are equal; files are almost certainly the same,
223    but just to be sure, do the comparison */
224 static int
225 identical_file(struct entry *e1, struct entry *e2){
226         char *start1, *start2;
227         int equal;
228
229         start1 = do_mmap(e1->path, e1->size, e1->mode);
230         if (!start1)
231                 return 0;
232         start2 = do_mmap(e2->path, e2->size, e2->mode);
233         if (!start2)
234                 return 0;
235         equal = !memcmp(start1, start2, e1->size);
236         do_munmap(start1, e1->size, e1->mode);
237         do_munmap(start2, e2->size, e2->mode);
238         return equal;
239 }
240
241 /*
242  * The longest file name component to allow for in the input directory tree.
243  * Ext2fs (and many others) allow up to 255 bytes.  A couple of filesystems
244  * allow longer (e.g. smbfs 1024), but there isn't much use in supporting
245  * >255-byte names in the input directory tree given that such names get
246  * truncated to 255 bytes when written to cramfs.
247  */
248 #define MAX_INPUT_NAMELEN 255
249
250 static int find_identical_file(struct entry *orig, struct entry *new, loff_t *fslen_ub)
251 {
252         if (orig == new)
253                 return 1;
254         if (!orig)
255                 return 0;
256         if (orig->size == new->size && orig->path) {
257                 if (!orig->flags)
258                         mdfile(orig);
259                 if (!new->flags)
260                         mdfile(new);
261
262                 if ((orig->flags & CRAMFS_EFLAG_MD5) &&
263                     (new->flags & CRAMFS_EFLAG_MD5) &&
264                     !memcmp(orig->md5sum, new->md5sum, 16) &&
265                     identical_file(orig, new)) {
266                         new->same = orig;
267                         *fslen_ub -= new->size;
268                         return 1;
269                 }
270         }
271         return find_identical_file(orig->child, new, fslen_ub) ||
272                    find_identical_file(orig->next, new, fslen_ub);
273 }
274
275 static void eliminate_doubles(struct entry *root, struct entry *orig, loff_t *fslen_ub) {
276         if (orig) {
277                 if (orig->size && orig->path)
278                         find_identical_file(root,orig, fslen_ub);
279                 eliminate_doubles(root,orig->child, fslen_ub);
280                 eliminate_doubles(root,orig->next, fslen_ub);
281         }
282 }
283
284 /*
285  * We define our own sorting function instead of using alphasort which
286  * uses strcoll and changes ordering based on locale information.
287  */
288 static int cramsort (const void *a, const void *b)
289 {
290         return strcmp ((*(const struct dirent **) a)->d_name,
291                        (*(const struct dirent **) b)->d_name);
292 }
293
294 static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub)
295 {
296         struct dirent **dirlist;
297         int totalsize = 0, dircount, dirindex;
298         char *path, *endpath;
299         size_t len = strlen(name);
300
301         /* Set up the path. */
302         /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */
303         path = xmalloc(len + 1 + MAX_INPUT_NAMELEN + 1);
304         memcpy(path, name, len);
305         endpath = path + len;
306         *endpath = '/';
307         endpath++;
308
309         /* read in the directory and sort */
310         dircount = scandir(name, &dirlist, 0, cramsort);
311
312         if (dircount < 0) {
313                 perror(name);
314                 exit(8);
315         }
316
317         /* process directory */
318         for (dirindex = 0; dirindex < dircount; dirindex++) {
319                 struct dirent *dirent;
320                 struct entry *entry;
321                 struct stat st;
322                 int size;
323                 size_t namelen;
324
325                 dirent = dirlist[dirindex];
326
327                 /* Ignore "." and ".." - we won't be adding them
328                    to the archive */
329                 if (dirent->d_name[0] == '.') {
330                         if (dirent->d_name[1] == '\0')
331                                 continue;
332                         if (dirent->d_name[1] == '.') {
333                                 if (dirent->d_name[2] == '\0')
334                                         continue;
335                         }
336                 }
337                 namelen = strlen(dirent->d_name);
338                 if (namelen > MAX_INPUT_NAMELEN) {
339                         fprintf(stderr,
340                                 _("Very long (%zu bytes) filename `%s' found.\n"
341                                   " Please increase MAX_INPUT_NAMELEN in "
342                                   "mkcramfs.c and recompile.  Exiting.\n"),
343                                 namelen, dirent->d_name);
344                         exit(8);
345                 }
346                 memcpy(endpath, dirent->d_name, namelen + 1);
347
348                 if (lstat(path, &st) < 0) {
349                         perror(endpath);
350                         warn_skip = 1;
351                         continue;
352                 }
353                 entry = calloc(1, sizeof(struct entry));
354                 if (!entry) {
355                         perror(NULL);
356                         exit(8);
357                 }
358                 entry->name = (unsigned char *)strdup(dirent->d_name);
359                 if (!entry->name) {
360                         perror(NULL);
361                         exit(8);
362                 }
363                 if (namelen > 255) {
364                         /* Can't happen when reading from ext2fs. */
365
366                         /* TODO: we ought to avoid chopping in half
367                            multi-byte UTF8 characters. */
368                         entry->name[namelen = 255] = '\0';
369                         warn_namelen = 1;
370                 }
371                 entry->mode = st.st_mode;
372                 entry->size = st.st_size;
373                 entry->uid = st.st_uid;
374                 if (entry->uid >= 1 << CRAMFS_UID_WIDTH)
375                         warn_uid = 1;
376                 entry->gid = st.st_gid;
377                 if (entry->gid >= 1 << CRAMFS_GID_WIDTH)
378                         /* TODO: We ought to replace with a default
379                            gid instead of truncating; otherwise there
380                            are security problems.  Maybe mode should
381                            be &= ~070.  Same goes for uid once Linux
382                            supports >16-bit uids. */
383                         warn_gid = 1;
384                 size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
385                 *fslen_ub += size;
386                 if (S_ISDIR(st.st_mode)) {
387                         entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub);
388                 } else if (S_ISREG(st.st_mode)) {
389                         entry->path = strdup(path);
390                         if (entry->size) {
391                                 if (entry->size >= (1 << CRAMFS_SIZE_WIDTH)) {
392                                         warn_size = 1;
393                                         entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1;
394                                 }
395                         }
396                 } else if (S_ISLNK(st.st_mode)) {
397                         entry->path = strdup(path);
398                 } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
399                         /* maybe we should skip sockets */
400                         entry->size = 0;
401                 } else {
402                         entry->size = st.st_rdev;
403                         if (entry->size & -(1<<CRAMFS_SIZE_WIDTH))
404                                 warn_dev = 1;
405                 }
406
407                 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
408                         int blocks = ((entry->size - 1) / blksize + 1);
409
410                         /* block pointers & data expansion allowance + data */
411                         if (entry->size)
412                                 *fslen_ub += (4+26)*blocks + entry->size + 3;
413                 }
414
415                 /* Link it into the list */
416                 *prev = entry;
417                 prev = &entry->next;
418                 totalsize += size;
419         }
420         free(path);
421         free(dirlist);          /* allocated by scandir() with malloc() */
422         return totalsize;
423 }
424
425 /* Returns sizeof(struct cramfs_super), which includes the root inode. */
426 static unsigned int write_superblock(struct entry *root, char *base, int size)
427 {
428         struct cramfs_super *super = (struct cramfs_super *) base;
429         unsigned int offset = sizeof(struct cramfs_super) + image_length;
430
431         if (opt_pad) {
432                 offset += opt_pad;
433         }
434
435         super->magic = CRAMFS_MAGIC;
436         super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS;
437         if (opt_holes)
438                 super->flags |= CRAMFS_FLAG_HOLES;
439         if (image_length > 0)
440                 super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET;
441         super->size = size;
442         memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature));
443
444         super->fsid.crc = crc32(0L, Z_NULL, 0);
445         super->fsid.edition = opt_edition;
446         super->fsid.blocks = total_blocks;
447         super->fsid.files = total_nodes;
448
449         memset(super->name, 0x00, sizeof(super->name));
450         if (opt_name)
451                 strncpy((char *)super->name, opt_name, sizeof(super->name));
452         else
453                 strncpy((char *)super->name, "Compressed", sizeof(super->name));
454
455         super->root.mode = root->mode;
456         super->root.uid = root->uid;
457         super->root.gid = root->gid;
458         super->root.size = root->size;
459         super->root.offset = offset >> 2;
460
461         super_toggle_endianness(cramfs_is_big_endian, super);
462         inode_from_host(cramfs_is_big_endian, &super->root, &super->root);
463
464         return offset;
465 }
466
467 static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
468 {
469         struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
470         inode_to_host(cramfs_is_big_endian, inode, inode);
471         if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) {
472                 fprintf(stderr, _("filesystem too big.  Exiting.\n"));
473                 exit(8);
474         }
475         inode->offset = (offset >> 2);
476         inode_from_host(cramfs_is_big_endian, inode, inode);
477 }
478
479
480 /*
481  * We do a width-first printout of the directory
482  * entries, using a stack to remember the directories
483  * we've seen.
484  */
485 static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset)
486 {
487         int stack_entries = 0;
488         int stack_size = 64;
489         struct entry **entry_stack;
490
491         entry_stack = xmalloc(stack_size * sizeof(struct entry *));
492
493         for (;;) {
494                 int dir_start = stack_entries;
495                 while (entry) {
496                         struct cramfs_inode *inode =
497                                 (struct cramfs_inode *) (base + offset);
498                         size_t len = strlen((const char *)entry->name);
499
500                         entry->dir_offset = offset;
501
502                         inode->mode = entry->mode;
503                         inode->uid = entry->uid;
504                         inode->gid = entry->gid;
505                         inode->size = entry->size;
506                         inode->offset = 0;
507                         /* Non-empty directories, regfiles and symlinks will
508                            write over inode->offset later. */
509
510                         offset += sizeof(struct cramfs_inode);
511                         total_nodes++;  /* another node */
512                         memcpy(base + offset, entry->name, len);
513                         /* Pad up the name to a 4-byte boundary */
514                         while (len & 3) {
515                                 *(base + offset + len) = '\0';
516                                 len++;
517                         }
518                         inode->namelen = len >> 2;
519                         offset += len;
520
521                         if (verbose)
522                                 printf("  %s\n", entry->name);
523                         if (entry->child) {
524                                 if (stack_entries >= stack_size) {
525                                         stack_size *= 2;
526                                         entry_stack = realloc(entry_stack, stack_size * sizeof(struct entry *));
527                                         if (!entry_stack) {
528                                                 perror(NULL);
529                                                 exit(8);        /* out of memory */
530                                         }
531                                 }
532                                 entry_stack[stack_entries] = entry;
533                                 stack_entries++;
534                         }
535                         inode_from_host(cramfs_is_big_endian, inode, inode);
536                         entry = entry->next;
537                 }
538
539                 /*
540                  * Reverse the order the stack entries pushed during
541                  * this directory, for a small optimization of disk
542                  * access in the created fs.  This change makes things
543                  * `ls -UR' order.
544                  */
545                 {
546                         struct entry **lo = entry_stack + dir_start;
547                         struct entry **hi = entry_stack + stack_entries;
548                         struct entry *tmp;
549
550                         while (lo < --hi) {
551                                 tmp = *lo;
552                                 *lo++ = *hi;
553                                 *hi = tmp;
554                         }
555                 }
556
557                 /* Pop a subdirectory entry from the stack, and recurse. */
558                 if (!stack_entries)
559                         break;
560                 stack_entries--;
561                 entry = entry_stack[stack_entries];
562
563                 set_data_offset(entry, base, offset);
564                 if (verbose)
565                         printf("'%s':\n", entry->name);
566                 entry = entry->child;
567         }
568         free(entry_stack);
569         return offset;
570 }
571
572 static int is_zero(unsigned char const *begin, unsigned len)
573 {
574         if (opt_holes)
575                 /* Returns non-zero iff the first LEN bytes from BEGIN are
576                    all NULs. */
577                 return (len-- == 0 ||
578                         (begin[0] == '\0' &&
579                          (len-- == 0 ||
580                           (begin[1] == '\0' &&
581                            (len-- == 0 ||
582                             (begin[2] == '\0' &&
583                              (len-- == 0 ||
584                               (begin[3] == '\0' &&
585                                memcmp(begin, begin + 4, len) == 0))))))));
586         else
587                 /* Never create holes. */
588                 return 0;
589 }
590
591 /*
592  * One 4-byte pointer per block and then the actual blocked
593  * output. The first block does not need an offset pointer,
594  * as it will start immediately after the pointer block;
595  * so the i'th pointer points to the end of the i'th block
596  * (i.e. the start of the (i+1)'th block or past EOF).
597  *
598  * Note that size > 0, as a zero-sized file wouldn't ever
599  * have gotten here in the first place.
600  */
601 static unsigned int
602 do_compress(char *base, unsigned int offset, unsigned char const *name,
603             char *path, unsigned int size, unsigned int mode)
604 {
605         unsigned long original_size, original_offset, new_size, blocks, curr;
606         long change;
607         char *start;
608         Bytef *p;
609
610         /* get uncompressed data */
611         start = do_mmap(path, size, mode);
612         if (start == NULL)
613                 return offset;
614         p = (Bytef *) start;
615
616         original_size = size;
617         original_offset = offset;
618         blocks = (size - 1) / blksize + 1;
619         curr = offset + 4 * blocks;
620
621         total_blocks += blocks;
622
623         do {
624                 uLongf len = 2 * blksize;
625                 uLongf input = size;
626                 if (input > blksize)
627                         input = blksize;
628                 size -= input;
629                 if (!is_zero (p, input)) {
630                         compress((Bytef *)(base + curr), &len, p, input);
631                         curr += len;
632                 }
633                 p += input;
634
635                 if (len > blksize*2) {
636                         /* (I don't think this can happen with zlib.) */
637                         printf(_("AIEEE: block \"compressed\" to > "
638                                  "2*blocklength (%ld)\n"),
639                                len);
640                         exit(8);
641                 }
642
643                 *(u32 *) (base + offset) = u32_toggle_endianness(cramfs_is_big_endian, curr);
644                 offset += 4;
645         } while (size);
646
647         do_munmap(start, original_size, mode);
648
649         curr = (curr + 3) & ~3;
650         new_size = curr - original_offset;
651         /* TODO: Arguably, original_size in these 2 lines should be
652            st_blocks * 512.  But if you say that, then perhaps
653            administrative data should also be included in both. */
654         change = new_size - original_size;
655         if (verbose)
656                 printf(_("%6.2f%% (%+ld bytes)\t%s\n"),
657                        (change * 100) / (double) original_size, change, name);
658
659         return curr;
660 }
661
662
663 /*
664  * Traverse the entry tree, writing data for every item that has
665  * non-null entry->path (i.e. every symlink and non-empty
666  * regfile).
667  */
668 static unsigned int
669 write_data(struct entry *entry, char *base, unsigned int offset) {
670         struct entry *e;
671
672         for (e = entry; e; e = e->next) {
673                 if (e->path) {
674                         if (e->same) {
675                                 set_data_offset(e, base, e->same->offset);
676                                 e->offset = e->same->offset;
677                         } else if (e->size) {
678                                 set_data_offset(e, base, offset);
679                                 e->offset = offset;
680                                 offset = do_compress(base, offset, e->name,
681                                                      e->path, e->size,e->mode);
682                         }
683                 } else if (e->child)
684                         offset = write_data(e->child, base, offset);
685         }
686         return offset;
687 }
688
689 static unsigned int write_file(char *file, char *base, unsigned int offset)
690 {
691         int fd;
692         char *buf;
693
694         fd = open(file, O_RDONLY);
695         if (fd < 0) {
696                 perror(file);
697                 exit(8);
698         }
699         buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0);
700         memcpy(base + offset, buf, image_length);
701         munmap(buf, image_length);
702         close (fd);
703         /* Pad up the image_length to a 4-byte boundary */
704         while (image_length & 3) {
705                 *(base + offset + image_length) = '\0';
706                 image_length++;
707         }
708         return (offset + image_length);
709 }
710
711 /*
712  * Maximum size fs you can create is roughly 256MB.  (The last file's
713  * data must begin within 256MB boundary but can extend beyond that.)
714  *
715  * Note that if you want it to fit in a ROM then you're limited to what the
716  * hardware and kernel can support (64MB?).
717  */
718 static unsigned int
719 maxfslen(void) {
720         return (((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2)    /* offset */
721                 + (1 << CRAMFS_SIZE_WIDTH) - 1            /* filesize */
722                 + (1 << CRAMFS_SIZE_WIDTH) * 4 / blksize; /* block pointers */
723 }
724
725 /*
726  * Usage:
727  *
728  *      mkcramfs directory-name outfile
729  *
730  * where "directory-name" is simply the root of the directory
731  * tree that we want to generate a compressed filesystem out
732  * of.
733  */
734 int main(int argc, char **argv)
735 {
736         struct stat st;         /* used twice... */
737         struct entry *root_entry;
738         char *rom_image;
739         ssize_t offset, written;
740         int fd;
741         /* initial guess (upper-bound) of required filesystem size */
742         loff_t fslen_ub = sizeof(struct cramfs_super);
743         unsigned int fslen_max;
744         char const *dirname, *outfile;
745         u32 crc = crc32(0L, Z_NULL, 0);
746         int c;
747         cramfs_is_big_endian = HOST_IS_BIG_ENDIAN; /* default is to use host order */
748
749         blksize = getpagesize();
750         total_blocks = 0;
751
752         if (argc) {
753                 char *p;
754                 progname = argv[0];
755                 if ((p = strrchr(progname, '/')) != NULL)
756                         progname = p+1;
757         }
758
759         setlocale(LC_ALL, "");
760         bindtextdomain(PACKAGE, LOCALEDIR);
761         textdomain(PACKAGE);
762
763         /* command line options */
764         while ((c = getopt(argc, argv, "hb:Ee:i:n:N:psVvz")) != EOF) {
765                 switch (c) {
766                 case 'h':
767                         usage(0);
768                 case 'b':
769                         blksize = atoi(optarg);
770                         if (blksize <= 0)
771                                 usage(1);
772                         break;
773                 case 'E':
774                         opt_errors = 1;
775                         break;
776                 case 'e':
777                         opt_edition = atoi(optarg);
778                         break;
779                 case 'N':
780                         if (strcmp(optarg, "big") == 0)  {
781                                 cramfs_is_big_endian = 1;
782                         }
783                         else if (strcmp(optarg, "little") == 0) {
784                                 cramfs_is_big_endian = 0;
785                         }
786                         else if (strcmp(optarg, "host") == 0);  /* default */
787                         else    {
788                                 perror("invalid endianness given. Must be 'big', 'little', or 'host'");
789                                 exit(16);
790                         }
791
792                         break;
793                 case 'i':
794                         opt_image = optarg;
795                         if (lstat(opt_image, &st) < 0) {
796                                 perror(opt_image);
797                                 exit(16);
798                         }
799                         image_length = st.st_size; /* may be padded later */
800                         fslen_ub += (image_length + 3); /* 3 is for padding */
801                         break;
802                 case 'n':
803                         opt_name = optarg;
804                         break;
805                 case 'p':
806                         opt_pad = PAD_SIZE;
807                         fslen_ub += PAD_SIZE;
808                         break;
809                 case 's':
810                         /* old option, ignored */
811                         break;
812                 case 'V':
813                         printf(_("%s (%s)\n"),
814                                progname, PACKAGE_STRING);
815                         exit(0);
816                 case 'v':
817                         verbose = 1;
818                         break;
819                 case 'z':
820                         opt_holes = 1;
821                         break;
822                 }
823         }
824
825         if ((argc - optind) != 2)
826                 usage(16);
827         dirname = argv[optind];
828         outfile = argv[optind + 1];
829
830         if (stat(dirname, &st) < 0) {
831                 perror(dirname);
832                 exit(16);
833         }
834         fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
835
836         root_entry = calloc(1, sizeof(struct entry));
837         if (!root_entry) {
838                 perror(NULL);
839                 exit(8);
840         }
841         root_entry->mode = st.st_mode;
842         root_entry->uid = st.st_uid;
843         root_entry->gid = st.st_gid;
844
845         root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub);
846
847         /* always allocate a multiple of blksize bytes because that's
848            what we're going to write later on */
849         fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1;
850         fslen_max = maxfslen();
851
852         if (fslen_ub > fslen_max) {
853                 fprintf(stderr,
854                         _("warning: guestimate of required size (upper bound) "
855                           "is %lldMB, but maximum image size is %uMB.  "
856                           "We might die prematurely.\n"),
857                         (long long)fslen_ub >> 20,
858                         fslen_max >> 20);
859                 fslen_ub = fslen_max;
860         }
861
862         /* find duplicate files */
863         eliminate_doubles(root_entry,root_entry, &fslen_ub);
864
865         /* TODO: Why do we use a private/anonymous mapping here
866            followed by a write below, instead of just a shared mapping
867            and a couple of ftruncate calls?  Is it just to save us
868            having to deal with removing the file afterwards?  If we
869            really need this huge anonymous mapping, we ought to mmap
870            in smaller chunks, so that the user doesn't need nn MB of
871            RAM free.  If the reason is to be able to write to
872            un-mmappable block devices, then we could try shared mmap
873            and revert to anonymous mmap if the shared mmap fails. */
874         rom_image = mmap(NULL,
875                          fslen_ub?fslen_ub:1,
876                          PROT_READ | PROT_WRITE,
877                          MAP_PRIVATE | MAP_ANONYMOUS,
878                          -1, 0);
879
880         if (-1 == (int) (long) rom_image) {
881                 perror(_("ROM image map"));
882                 exit(8);
883         }
884
885         /* Skip the first opt_pad bytes for boot loader code */
886         offset = opt_pad;
887         memset(rom_image, 0x00, opt_pad);
888
889         /* Skip the superblock and come back to write it later. */
890         offset += sizeof(struct cramfs_super);
891
892         /* Insert a file image. */
893         if (opt_image) {
894                 if (verbose)
895                         printf(_("Including: %s\n"), opt_image);
896                 offset = write_file(opt_image, rom_image, offset);
897         }
898
899         offset = write_directory_structure(root_entry->child, rom_image, offset);
900         if (verbose)
901                 printf(_("Directory data: %zd bytes\n"), offset);
902
903         offset = write_data(root_entry, rom_image, offset);
904
905         /* We always write a multiple of blksize bytes, so that
906            losetup works. */
907         offset = ((offset - 1) | (blksize - 1)) + 1;
908         if (verbose)
909                 printf(_("Everything: %zd kilobytes\n"), offset >> 10);
910
911         /* Write the superblock now that we can fill in all of the fields. */
912         write_superblock(root_entry, rom_image+opt_pad, offset);
913         if (verbose)
914                 printf(_("Super block: %zd bytes\n"),
915                        sizeof(struct cramfs_super));
916
917         /* Put the checksum in. */
918         crc = crc32(crc, (unsigned char *) (rom_image+opt_pad), (offset-opt_pad));
919         ((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = u32_toggle_endianness(cramfs_is_big_endian, crc);
920         if (verbose)
921                 printf(_("CRC: %x\n"), crc);
922
923         /* Check to make sure we allocated enough space. */
924         if (fslen_ub < offset) {
925                 fprintf(stderr,
926                         _("not enough space allocated for ROM image "
927                           "(%lld allocated, %zu used)\n"),
928                         (long long) fslen_ub, offset);
929                 exit(8);
930         }
931
932         written = write(fd, rom_image, offset);
933         if (written < 0) {
934                 perror(_("ROM image"));
935                 exit(8);
936         }
937         if (offset != written) {
938                 fprintf(stderr, _("ROM image write failed (%zd %zd)\n"),
939                         written, offset);
940                 exit(8);
941         }
942
943         /* (These warnings used to come at the start, but they scroll off the
944            screen too quickly.) */
945         if (warn_namelen) /* (can't happen when reading from ext2fs) */
946                 fprintf(stderr, /* bytes, not chars: think UTF8. */
947                         _("warning: filenames truncated to 255 bytes.\n"));
948         if (warn_skip)
949                 fprintf(stderr,
950                         _("warning: files were skipped due to errors.\n"));
951         if (warn_size)
952                 fprintf(stderr,
953                         _("warning: file sizes truncated to %luMB "
954                           "(minus 1 byte).\n"),
955                         1L << (CRAMFS_SIZE_WIDTH - 20));
956         if (warn_uid) /* (not possible with current Linux versions) */
957                 fprintf(stderr,
958                         _("warning: uids truncated to %u bits.  "
959                           "(This may be a security concern.)\n"),
960                         CRAMFS_UID_WIDTH);
961         if (warn_gid)
962                 fprintf(stderr,
963                         _("warning: gids truncated to %u bits.  "
964                           "(This may be a security concern.)\n"),
965                         CRAMFS_GID_WIDTH);
966         if (warn_dev)
967                 fprintf(stderr,
968                         _("WARNING: device numbers truncated to %u bits.  "
969                           "This almost certainly means\n"
970                           "that some device files will be wrong.\n"),
971                         CRAMFS_OFFSET_WIDTH);
972         if (opt_errors &&
973             (warn_namelen|warn_skip|warn_size|warn_uid|warn_gid|warn_dev))
974                 exit(8);
975         return 0;
976 }