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