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