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