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