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