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