]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/mkfs.cramfs.c
Merge branch 'usage-part1' of https://github.com/rudimeier/util-linux
[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 fputs(USAGE_HELP, stdout);
148 fputs(USAGE_VERSION, stdout);
149 printf(USAGE_MAN_TAIL("mkfs.cramfs(8)"));
150 exit(MKFS_EX_OK);
151 }
152
153 static char *
154 do_mmap(char *path, unsigned int size, unsigned int mode){
155 int fd;
156 char *start = NULL;
157
158 if (!size)
159 return NULL;
160
161 if (S_ISLNK(mode)) {
162 start = xmalloc(size);
163 if (readlink(path, start, size) < 0) {
164 warn(_("readlink failed: %s"), path);
165 warn_skip = 1;
166 goto err;
167 }
168 return start;
169 }
170
171 fd = open(path, O_RDONLY);
172 if (fd < 0) {
173 warn(_("cannot open %s"), path);
174 warn_skip = 1;
175 goto err;
176 }
177
178 start = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
179 close(fd);
180 if (start == MAP_FAILED)
181 err(MKFS_EX_ERROR, "mmap");
182 return start;
183 err:
184 free(start);
185 return NULL;
186 }
187
188 static void
189 do_munmap(char *start, unsigned int size, unsigned int mode){
190 if (S_ISLNK(mode))
191 free(start);
192 else
193 munmap(start, size);
194 }
195
196 /* compute md5sums, so that we do not have to compare every pair of files */
197 static void
198 mdfile(struct entry *e) {
199 MD5_CTX ctx;
200 char *start;
201
202 start = do_mmap(e->path, e->size, e->mode);
203 if (start == NULL) {
204 e->flags |= CRAMFS_EFLAG_INVALID;
205 } else {
206 MD5Init(&ctx);
207 MD5Update(&ctx, (unsigned char *) start, e->size);
208 MD5Final(e->md5sum, &ctx);
209
210 do_munmap(start, e->size, e->mode);
211
212 e->flags |= CRAMFS_EFLAG_MD5;
213 }
214 }
215
216 /* md5 digests are equal; files are almost certainly the same,
217 but just to be sure, do the comparison */
218 static int
219 identical_file(struct entry *e1, struct entry *e2){
220 char *start1, *start2;
221 int equal;
222
223 start1 = do_mmap(e1->path, e1->size, e1->mode);
224 if (!start1)
225 return 0;
226 start2 = do_mmap(e2->path, e2->size, e2->mode);
227 if (!start2) {
228 do_munmap(start1, e1->size, e1->mode);
229 return 0;
230 }
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
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
246 static int find_identical_file(struct entry *orig, struct entry *new, loff_t *fslen_ub)
247 {
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 & CRAMFS_EFLAG_MD5) &&
259 (new->flags & CRAMFS_EFLAG_MD5) &&
260 !memcmp(orig->md5sum, new->md5sum, MD5LENGTH) &&
261 identical_file(orig, new)) {
262 new->same = orig;
263 *fslen_ub -= new->size;
264 return 1;
265 }
266 }
267 return find_identical_file(orig->child, new, fslen_ub) ||
268 find_identical_file(orig->next, new, fslen_ub);
269 }
270
271 static void eliminate_doubles(struct entry *root, struct entry *orig, loff_t *fslen_ub) {
272 if (orig) {
273 if (orig->size && orig->path)
274 find_identical_file(root,orig, fslen_ub);
275 eliminate_doubles(root,orig->child, fslen_ub);
276 eliminate_doubles(root,orig->next, fslen_ub);
277 }
278 }
279
280 /*
281 * We define our own sorting function instead of using alphasort which
282 * uses strcoll and changes ordering based on locale information.
283 */
284 static int cramsort (const struct dirent **a, const struct dirent **b)
285 {
286 return strcmp((*a)->d_name, (*b)->d_name);
287 }
288
289 static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub)
290 {
291 struct dirent **dirlist;
292 int totalsize = 0, dircount, dirindex;
293 char *path, *endpath;
294 size_t len = strlen(name);
295
296 /* Set up the path. */
297 /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */
298 path = xmalloc(len + 1 + MAX_INPUT_NAMELEN + 1);
299 memcpy(path, name, len);
300 endpath = path + len;
301 *endpath = '/';
302 endpath++;
303
304 /* read in the directory and sort */
305 dircount = scandir(name, &dirlist, NULL, cramsort);
306
307 if (dircount < 0)
308 err(MKFS_EX_ERROR, _("could not read directory %s"), name);
309
310 /* process directory */
311 for (dirindex = 0; dirindex < dircount; dirindex++) {
312 struct dirent *dirent;
313 struct entry *entry;
314 struct stat st;
315 int size;
316 size_t namelen;
317
318 dirent = dirlist[dirindex];
319
320 /* Ignore "." and ".." - we won't be adding them
321 to the archive */
322 if (dirent->d_name[0] == '.') {
323 if (dirent->d_name[1] == '\0')
324 continue;
325 if (dirent->d_name[1] == '.' &&
326 dirent->d_name[2] == '\0')
327 continue;
328 }
329 namelen = strlen(dirent->d_name);
330 if (namelen > MAX_INPUT_NAMELEN) {
331 namelen = MAX_INPUT_NAMELEN;
332 warn_namelen = 1;
333 }
334
335 memcpy(endpath, dirent->d_name, namelen + 1);
336
337 if (lstat(path, &st) < 0) {
338 warn(_("stat of %s failed"), endpath);
339 warn_skip = 1;
340 continue;
341 }
342 entry = xcalloc(1, sizeof(struct entry));
343 entry->name = (unsigned char *)xstrndup(dirent->d_name, namelen);
344 entry->mode = st.st_mode;
345 entry->size = st.st_size;
346 entry->uid = st.st_uid;
347 if (entry->uid >= 1 << CRAMFS_UID_WIDTH)
348 warn_uid = 1;
349 entry->gid = st.st_gid;
350 if (entry->gid >= 1 << CRAMFS_GID_WIDTH)
351 /* TODO: We ought to replace with a default
352 gid instead of truncating; otherwise there
353 are security problems. Maybe mode should
354 be &= ~070. Same goes for uid once Linux
355 supports >16-bit uids. */
356 warn_gid = 1;
357 size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
358 *fslen_ub += size;
359 if (S_ISDIR(st.st_mode)) {
360 entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub);
361 } else if (S_ISREG(st.st_mode)) {
362 entry->path = xstrdup(path);
363 if (entry->size && entry->size >= (1 << CRAMFS_SIZE_WIDTH)) {
364 warn_size = 1;
365 entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1;
366 }
367 } else if (S_ISLNK(st.st_mode)) {
368 entry->path = xstrdup(path);
369 } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
370 /* maybe we should skip sockets */
371 entry->size = 0;
372 } else {
373 entry->size = st.st_rdev;
374 if (entry->size & -(1<<CRAMFS_SIZE_WIDTH))
375 warn_dev = 1;
376 }
377
378 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
379 int blocks = ((entry->size - 1) / blksize + 1);
380
381 /* block pointers & data expansion allowance + data */
382 if (entry->size)
383 *fslen_ub += (4+26)*blocks + entry->size + 3;
384 }
385
386 /* Link it into the list */
387 *prev = entry;
388 prev = &entry->next;
389 totalsize += size;
390 }
391 free(path);
392 free(dirlist); /* allocated by scandir() with malloc() */
393 return totalsize;
394 }
395
396 /* Returns sizeof(struct cramfs_super), which includes the root inode. */
397 static unsigned int write_superblock(struct entry *root, char *base, int size)
398 {
399 struct cramfs_super *super = (struct cramfs_super *) base;
400 unsigned int offset = sizeof(struct cramfs_super) + image_length;
401
402 if (opt_pad) {
403 offset += opt_pad;
404 }
405
406 super->magic = CRAMFS_MAGIC;
407 super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS;
408 if (opt_holes)
409 super->flags |= CRAMFS_FLAG_HOLES;
410 if (image_length > 0)
411 super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET;
412 super->size = size;
413 memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature));
414
415 super->fsid.crc = crc32(0L, NULL, 0);
416 super->fsid.edition = opt_edition;
417 super->fsid.blocks = total_blocks;
418 super->fsid.files = total_nodes;
419
420 memset(super->name, 0x00, sizeof(super->name));
421 if (opt_name)
422 strncpy((char *)super->name, opt_name, sizeof(super->name));
423 else
424 strncpy((char *)super->name, "Compressed", sizeof(super->name));
425
426 super->root.mode = root->mode;
427 super->root.uid = root->uid;
428 super->root.gid = root->gid;
429 super->root.size = root->size;
430 super->root.offset = offset >> 2;
431
432 super_toggle_endianness(cramfs_is_big_endian, super);
433 inode_from_host(cramfs_is_big_endian, &super->root, &super->root);
434
435 return offset;
436 }
437
438 static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
439 {
440 struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
441 inode_to_host(cramfs_is_big_endian, inode, inode);
442 if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH)))
443 errx(MKFS_EX_ERROR, _("filesystem too big. Exiting."));
444 inode->offset = (offset >> 2);
445 inode_from_host(cramfs_is_big_endian, inode, inode);
446 }
447
448
449 /*
450 * We do a width-first printout of the directory
451 * entries, using a stack to remember the directories
452 * we've seen.
453 */
454 static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset)
455 {
456 int stack_entries = 0;
457 int stack_size = 64;
458 struct entry **entry_stack;
459
460 entry_stack = xmalloc(stack_size * sizeof(struct entry *));
461
462 for (;;) {
463 int dir_start = stack_entries;
464 while (entry) {
465 struct cramfs_inode *inode =
466 (struct cramfs_inode *) (base + offset);
467 size_t len = strlen((const char *)entry->name);
468
469 entry->dir_offset = offset;
470
471 inode->mode = entry->mode;
472 inode->uid = entry->uid;
473 inode->gid = entry->gid;
474 inode->size = entry->size;
475 inode->offset = 0;
476 /* Non-empty directories, regfiles and symlinks will
477 write over inode->offset later. */
478
479 offset += sizeof(struct cramfs_inode);
480 total_nodes++; /* another node */
481 memcpy(base + offset, entry->name, len);
482 /* Pad up the name to a 4-byte boundary */
483 while (len & 3) {
484 *(base + offset + len) = '\0';
485 len++;
486 }
487 inode->namelen = len >> 2;
488 offset += len;
489
490 if (verbose)
491 printf(" %s\n", entry->name);
492 if (entry->child) {
493 if (stack_entries >= stack_size) {
494 stack_size *= 2;
495 entry_stack = xrealloc(entry_stack, stack_size * sizeof(struct entry *));
496 }
497 entry_stack[stack_entries] = entry;
498 stack_entries++;
499 }
500 inode_from_host(cramfs_is_big_endian, inode, inode);
501 entry = entry->next;
502 }
503
504 /*
505 * Reverse the order the stack entries pushed during
506 * this directory, for a small optimization of disk
507 * access in the created fs. This change makes things
508 * `ls -UR' order.
509 */
510 {
511 struct entry **lo = entry_stack + dir_start;
512 struct entry **hi = entry_stack + stack_entries;
513 struct entry *tmp;
514
515 while (lo < --hi) {
516 tmp = *lo;
517 *lo++ = *hi;
518 *hi = tmp;
519 }
520 }
521
522 /* Pop a subdirectory entry from the stack, and recurse. */
523 if (!stack_entries)
524 break;
525 stack_entries--;
526 entry = entry_stack[stack_entries];
527
528 set_data_offset(entry, base, offset);
529 if (verbose)
530 printf("'%s':\n", entry->name);
531 entry = entry->child;
532 }
533 free(entry_stack);
534 return offset;
535 }
536
537 static int is_zero(unsigned char const *begin, unsigned len)
538 {
539 if (opt_holes)
540 /* Returns non-zero iff the first LEN bytes from BEGIN are
541 all NULs. */
542 return (len-- == 0 ||
543 (begin[0] == '\0' &&
544 (len-- == 0 ||
545 (begin[1] == '\0' &&
546 (len-- == 0 ||
547 (begin[2] == '\0' &&
548 (len-- == 0 ||
549 (begin[3] == '\0' &&
550 memcmp(begin, begin + 4, len) == 0))))))));
551 else
552 /* Never create holes. */
553 return 0;
554 }
555
556 /*
557 * One 4-byte pointer per block and then the actual blocked
558 * output. The first block does not need an offset pointer,
559 * as it will start immediately after the pointer block;
560 * so the i'th pointer points to the end of the i'th block
561 * (i.e. the start of the (i+1)'th block or past EOF).
562 *
563 * Note that size > 0, as a zero-sized file wouldn't ever
564 * have gotten here in the first place.
565 */
566 static unsigned int
567 do_compress(char *base, unsigned int offset, unsigned char const *name,
568 char *path, unsigned int size, unsigned int mode)
569 {
570 unsigned long original_size, original_offset, new_size, blocks, curr;
571 long change;
572 char *start;
573 Bytef *p;
574
575 /* get uncompressed data */
576 start = do_mmap(path, size, mode);
577 if (start == NULL)
578 return offset;
579 p = (Bytef *) start;
580
581 original_size = size;
582 original_offset = offset;
583 blocks = (size - 1) / blksize + 1;
584 curr = offset + 4 * blocks;
585
586 total_blocks += blocks;
587
588 do {
589 uLongf len = 2 * blksize;
590 uLongf input = size;
591 if (input > blksize)
592 input = blksize;
593 size -= input;
594 if (!is_zero (p, input)) {
595 compress((Bytef *)(base + curr), &len, p, input);
596 curr += len;
597 }
598 p += input;
599
600 if (len > blksize*2) {
601 /* (I don't think this can happen with zlib.) */
602 printf(_("AIEEE: block \"compressed\" to > "
603 "2*blocklength (%ld)\n"),
604 len);
605 exit(MKFS_EX_ERROR);
606 }
607
608 *(uint32_t *) (base + offset) = u32_toggle_endianness(cramfs_is_big_endian, curr);
609 offset += 4;
610 } while (size);
611
612 do_munmap(start, original_size, mode);
613
614 curr = (curr + 3) & ~3;
615 new_size = curr - original_offset;
616 /* TODO: Arguably, original_size in these 2 lines should be
617 st_blocks * 512. But if you say that, then perhaps
618 administrative data should also be included in both. */
619 change = new_size - original_size;
620 if (verbose)
621 printf(_("%6.2f%% (%+ld bytes)\t%s\n"),
622 (change * 100) / (double) original_size, change, name);
623
624 return curr;
625 }
626
627
628 /*
629 * Traverse the entry tree, writing data for every item that has
630 * non-null entry->path (i.e. every symlink and non-empty
631 * regfile).
632 */
633 static unsigned int
634 write_data(struct entry *entry, char *base, unsigned int offset) {
635 struct entry *e;
636
637 for (e = entry; e; e = e->next) {
638 if (e->path) {
639 if (e->same) {
640 set_data_offset(e, base, e->same->offset);
641 e->offset = e->same->offset;
642 } else if (e->size) {
643 set_data_offset(e, base, offset);
644 e->offset = offset;
645 offset = do_compress(base, offset, e->name,
646 e->path, e->size,e->mode);
647 }
648 } else if (e->child)
649 offset = write_data(e->child, base, offset);
650 }
651 return offset;
652 }
653
654 static unsigned int write_file(char *file, char *base, unsigned int offset)
655 {
656 int fd;
657 char *buf;
658
659 fd = open(file, O_RDONLY);
660 if (fd < 0)
661 err(MKFS_EX_ERROR, _("cannot open %s"), file);
662 buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0);
663 memcpy(base + offset, buf, image_length);
664 munmap(buf, image_length);
665 if (close (fd) < 0)
666 err(MKFS_EX_ERROR, _("cannot close file %s"), file);
667 /* Pad up the image_length to a 4-byte boundary */
668 while (image_length & 3) {
669 *(base + offset + image_length) = '\0';
670 image_length++;
671 }
672 return (offset + image_length);
673 }
674
675 /*
676 * Maximum size fs you can create is roughly 256MB. (The last file's
677 * data must begin within 256MB boundary but can extend beyond that.)
678 *
679 * Note that if you want it to fit in a ROM then you're limited to what the
680 * hardware and kernel can support (64MB?).
681 */
682 static unsigned int
683 maxfslen(void) {
684 return (((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2) /* offset */
685 + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */
686 + (1 << CRAMFS_SIZE_WIDTH) * 4 / blksize; /* block pointers */
687 }
688
689 /*
690 * Usage:
691 *
692 * mkcramfs directory-name outfile
693 *
694 * where "directory-name" is simply the root of the directory
695 * tree that we want to generate a compressed filesystem out
696 * of.
697 */
698 int main(int argc, char **argv)
699 {
700 struct stat st; /* used twice... */
701 struct entry *root_entry;
702 char *rom_image;
703 ssize_t offset, written;
704 int fd;
705 /* initial guess (upper-bound) of required filesystem size */
706 loff_t fslen_ub = sizeof(struct cramfs_super);
707 unsigned int fslen_max;
708 char const *dirname, *outfile;
709 uint32_t crc = crc32(0L, NULL, 0);
710 int c;
711 cramfs_is_big_endian = HOST_IS_BIG_ENDIAN; /* default is to use host order */
712
713 total_blocks = 0;
714
715 setlocale(LC_ALL, "");
716 bindtextdomain(PACKAGE, LOCALEDIR);
717 textdomain(PACKAGE);
718 atexit(close_stdout);
719
720 if (argc > 1) {
721 /* first arg may be one of our standard longopts */
722 if (!strcmp(argv[1], "--help"))
723 usage();
724 if (!strcmp(argv[1], "--version")) {
725 printf(UTIL_LINUX_VERSION);
726 exit(MKFS_EX_OK);
727 }
728 }
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 EXIT_SUCCESS;
926 }