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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * If you change the disk format of cramfs, please update fs/cramfs/README.
23 */
24
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <sys/mman.h>
30 #include <fcntl.h>
31 #include <dirent.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <linux/cramfs_fs.h>
37 #include <zlib.h>
38
39 #define MAP_ANONYMOUS MAP_ANON
40 typedef long long loff_t;
41
42 /* Exit codes used by mkfs-type programs */
43 #define MKFS_OK 0 /* No errors */
44 #define MKFS_ERROR 8 /* Operational error */
45 #define MKFS_USAGE 16 /* Usage or syntax error */
46
47 /* The kernel only supports PAD_SIZE of 0 and 512. */
48 #define PAD_SIZE 512
49
50 /* The kernel assumes PAGE_CACHE_SIZE as block size. */
51 #define PAGE_CACHE_SIZE (4096)
52
53 /*
54 * The longest filename component to allow for in the input directory tree.
55 * ext2fs (and many others) allow up to 255 bytes. A couple of filesystems
56 * allow longer (e.g. smbfs 1024), but there isn't much use in supporting
57 * >255-byte names in the input directory tree given that such names get
58 * truncated to CRAMFS_MAXPATHLEN (252 bytes) when written to cramfs.
59 *
60 * Old versions of mkcramfs generated corrupted filesystems if any input
61 * filenames exceeded CRAMFS_MAXPATHLEN (252 bytes), however old
62 * versions of cramfsck seem to have been able to detect the corruption.
63 */
64 #define MAX_INPUT_NAMELEN 255
65
66 /*
67 * Maximum size fs you can create is roughly 256MB. (The last file's
68 * data must begin within 256MB boundary but can extend beyond that.)
69 *
70 * Note that if you want it to fit in a ROM then you're limited to what the
71 * hardware and kernel can support.
72 */
73 #define MAXFSLEN ((((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2) /* offset */ \
74 + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */ \
75 + (1 << CRAMFS_SIZE_WIDTH) * 4 / PAGE_CACHE_SIZE /* block pointers */ )
76
77 static const char *progname = "mkcramfs";
78 static unsigned int blksize = PAGE_CACHE_SIZE;
79 static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */
80 static int image_length = 0;
81
82 /*
83 * If opt_holes is set, then mkcramfs can create explicit holes in the
84 * data, which saves 26 bytes per hole (which is a lot smaller a
85 * saving than most most filesystems).
86 *
87 * Note that kernels up to at least 2.3.39 don't support cramfs holes,
88 * which is why this is turned off by default.
89 *
90 * If opt_verbose is 1, be verbose. If it is higher, be even more verbose.
91 */
92 static u32 opt_edition = 0;
93 static int opt_errors = 0;
94 static int opt_holes = 0;
95 static int opt_pad = 0;
96 static int opt_verbose = 0;
97 static char *opt_image = NULL;
98 static char *opt_name = NULL;
99
100 static int warn_dev, warn_gid, warn_namelen, warn_skip, warn_size, warn_uid;
101
102 /* In-core version of inode / directory entry. */
103 struct entry {
104 /* stats */
105 unsigned char *name;
106 unsigned int mode, size, uid, gid;
107
108 /* these are only used for non-empty files */
109 char *path; /* always null except non-empty files */
110 int fd; /* temporarily open files while mmapped */
111
112 /* FS data */
113 void *uncompressed;
114 /* points to other identical file */
115 struct entry *same;
116 unsigned int offset; /* pointer to compressed data in archive */
117 unsigned int dir_offset; /* Where in the archive is the directory entry? */
118
119 /* organization */
120 struct entry *child; /* null for non-directories and empty directories */
121 struct entry *next;
122 };
123
124 /* Input status of 0 to print help and exit without an error. */
usage(int status)125 static void usage(int status)
126 {
127 FILE *stream = status ? stderr : stdout;
128
129 fprintf(stream, "usage: %s [-h] [-e edition] [-i file] [-n name] dirname outfile\n"
130 " -h print this help\n"
131 " -E make all warnings errors (non-zero exit status)\n"
132 " -e edition set edition number (part of fsid)\n"
133 " -i file insert a file image into the filesystem (requires >= 2.4.0)\n"
134 " -n name set name of cramfs filesystem\n"
135 " -p pad by %d bytes for boot code\n"
136 " -s sort directory entries (old option, ignored)\n"
137 " -v be more verbose\n"
138 " -z make explicit holes (requires >= 2.3.39)\n"
139 " dirname root of the directory tree to be compressed\n"
140 " outfile output file\n", progname, PAD_SIZE);
141
142 exit(status);
143 }
144
die(int status,int syserr,const char * fmt,...)145 static void die(int status, int syserr, const char *fmt, ...)
146 {
147 va_list arg_ptr;
148 int save = errno;
149
150 fflush(0);
151 va_start(arg_ptr, fmt);
152 fprintf(stderr, "%s: ", progname);
153 vfprintf(stderr, fmt, arg_ptr);
154 if (syserr) {
155 fprintf(stderr, ": %s", strerror(save));
156 }
157 fprintf(stderr, "\n");
158 va_end(arg_ptr);
159 exit(status);
160 }
161
map_entry(struct entry * entry)162 static void map_entry(struct entry *entry)
163 {
164 if (entry->path) {
165 entry->fd = open(entry->path, O_RDONLY);
166 if (entry->fd < 0) {
167 die(MKFS_ERROR, 1, "open failed: %s", entry->path);
168 }
169 entry->uncompressed = mmap(NULL, entry->size, PROT_READ, MAP_PRIVATE, entry->fd, 0);
170 if (entry->uncompressed == MAP_FAILED) {
171 die(MKFS_ERROR, 1, "mmap failed: %s", entry->path);
172 }
173 }
174 }
175
unmap_entry(struct entry * entry)176 static void unmap_entry(struct entry *entry)
177 {
178 if (entry->path) {
179 if (munmap(entry->uncompressed, entry->size) < 0) {
180 die(MKFS_ERROR, 1, "munmap failed: %s", entry->path);
181 }
182 close(entry->fd);
183 }
184 }
185
find_identical_file(struct entry * orig,struct entry * newfile)186 static int find_identical_file(struct entry *orig, struct entry *newfile)
187 {
188 if (orig == newfile)
189 return 1;
190 if (!orig)
191 return 0;
192 if (orig->size == newfile->size && (orig->path || orig->uncompressed))
193 {
194 map_entry(orig);
195 map_entry(newfile);
196 if (!memcmp(orig->uncompressed, newfile->uncompressed, orig->size))
197 {
198 newfile->same = orig;
199 unmap_entry(newfile);
200 unmap_entry(orig);
201 return 1;
202 }
203 unmap_entry(newfile);
204 unmap_entry(orig);
205 }
206 return (find_identical_file(orig->child, newfile) ||
207 find_identical_file(orig->next, newfile));
208 }
209
eliminate_doubles(struct entry * root,struct entry * orig)210 static void eliminate_doubles(struct entry *root, struct entry *orig) {
211 if (orig) {
212 if (orig->size && (orig->path || orig->uncompressed))
213 find_identical_file(root, orig);
214 eliminate_doubles(root, orig->child);
215 eliminate_doubles(root, orig->next);
216 }
217 }
218
219 /*
220 * We define our own sorting function instead of using alphasort which
221 * uses strcoll and changes ordering based on locale information.
222 */
cramsort(const void * a,const void * b)223 static int cramsort (const void *a, const void *b)
224 {
225 return strcmp ((*(const struct dirent **) a)->d_name,
226 (*(const struct dirent **) b)->d_name);
227 }
228
parse_directory(struct entry * root_entry,const char * name,struct entry ** prev,loff_t * fslen_ub)229 static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub)
230 {
231 struct dirent **dirlist;
232 int totalsize = 0, dircount, dirindex;
233 char *path, *endpath;
234 size_t len = strlen(name);
235
236 /* Set up the path. */
237 /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */
238 path = malloc(len + 1 + MAX_INPUT_NAMELEN + 1);
239 if (!path) {
240 die(MKFS_ERROR, 1, "malloc failed");
241 }
242 memcpy(path, name, len);
243 endpath = path + len;
244 *endpath = '/';
245 endpath++;
246
247 /* read in the directory and sort */
248 dircount = scandir(name, &dirlist, 0, cramsort);
249
250 if (dircount < 0) {
251 die(MKFS_ERROR, 1, "scandir failed: %s", name);
252 }
253
254 /* process directory */
255 for (dirindex = 0; dirindex < dircount; dirindex++) {
256 struct dirent *dirent;
257 struct entry *entry;
258 struct stat st;
259 int size;
260 size_t namelen;
261
262 dirent = dirlist[dirindex];
263
264 /* Ignore "." and ".." - we won't be adding them to the archive */
265 if (dirent->d_name[0] == '.') {
266 if (dirent->d_name[1] == '\0')
267 continue;
268 if (dirent->d_name[1] == '.') {
269 if (dirent->d_name[2] == '\0')
270 continue;
271 }
272 }
273 namelen = strlen(dirent->d_name);
274 if (namelen > MAX_INPUT_NAMELEN) {
275 die(MKFS_ERROR, 0,
276 "very long (%u bytes) filename found: %s\n"
277 "please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile",
278 namelen, dirent->d_name);
279 }
280 memcpy(endpath, dirent->d_name, namelen + 1);
281
282 if (lstat(path, &st) < 0) {
283 warn_skip = 1;
284 continue;
285 }
286 entry = calloc(1, sizeof(struct entry));
287 if (!entry) {
288 die(MKFS_ERROR, 1, "calloc failed");
289 }
290 entry->name = strdup(dirent->d_name);
291 if (!entry->name) {
292 die(MKFS_ERROR, 1, "strdup failed");
293 }
294 /* truncate multi-byte UTF-8 filenames on character boundary */
295 if (namelen > CRAMFS_MAXPATHLEN) {
296 namelen = CRAMFS_MAXPATHLEN;
297 warn_namelen = 1;
298 /* the first lost byte must not be a trail byte */
299 while ((entry->name[namelen] & 0xc0) == 0x80) {
300 namelen--;
301 /* are we reasonably certain it was UTF-8 ? */
302 if (entry->name[namelen] < 0x80 || !namelen) {
303 die(MKFS_ERROR, 0, "cannot truncate filenames not encoded in UTF-8");
304 }
305 }
306 entry->name[namelen] = '\0';
307 }
308 entry->mode = st.st_mode;
309 entry->size = st.st_size;
310 entry->uid = st.st_uid;
311 if (entry->uid >= 1 << CRAMFS_UID_WIDTH)
312 warn_uid = 1;
313 entry->gid = st.st_gid;
314 if (entry->gid >= 1 << CRAMFS_GID_WIDTH)
315 /* TODO: We ought to replace with a default
316 gid instead of truncating; otherwise there
317 are security problems. Maybe mode should
318 be &= ~070. Same goes for uid once Linux
319 supports >16-bit uids. */
320 warn_gid = 1;
321 size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
322 *fslen_ub += size;
323 if (S_ISDIR(st.st_mode)) {
324 entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub);
325 } else if (S_ISREG(st.st_mode)) {
326 if (entry->size) {
327 if (access(path, R_OK) < 0) {
328 warn_skip = 1;
329 continue;
330 }
331 entry->path = strdup(path);
332 if (!entry->path) {
333 die(MKFS_ERROR, 1, "strdup failed");
334 }
335 if ((entry->size >= 1 << CRAMFS_SIZE_WIDTH)) {
336 warn_size = 1;
337 entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1;
338 }
339 }
340 } else if (S_ISLNK(st.st_mode)) {
341 entry->uncompressed = malloc(entry->size);
342 if (!entry->uncompressed) {
343 die(MKFS_ERROR, 1, "malloc failed");
344 }
345 if (readlink(path, entry->uncompressed, entry->size) < 0) {
346 warn_skip = 1;
347 continue;
348 }
349 } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
350 /* maybe we should skip sockets */
351 entry->size = 0;
352 } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
353 entry->size = st.st_rdev;
354 if (entry->size & -(1<<CRAMFS_SIZE_WIDTH))
355 warn_dev = 1;
356 } else {
357 die(MKFS_ERROR, 0, "bogus file type: %s", entry->name);
358 }
359
360 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
361 int blocks = ((entry->size - 1) / blksize + 1);
362
363 /* block pointers & data expansion allowance + data */
364 if (entry->size)
365 *fslen_ub += (4+26)*blocks + entry->size + 3;
366 }
367
368 /* Link it into the list */
369 *prev = entry;
370 prev = &entry->next;
371 totalsize += size;
372 }
373 free(path);
374 free(dirlist); /* allocated by scandir() with malloc() */
375 return totalsize;
376 }
377
378 /* Returns sizeof(struct cramfs_super), which includes the root inode. */
write_superblock(struct entry * root,char * base,int size)379 static unsigned int write_superblock(struct entry *root, char *base, int size)
380 {
381 struct cramfs_super *super = (struct cramfs_super *) base;
382 unsigned int offset = sizeof(struct cramfs_super) + image_length;
383
384 offset += opt_pad; /* 0 if no padding */
385
386 super->magic = CRAMFS_MAGIC;
387 super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS;
388 if (opt_holes)
389 super->flags |= CRAMFS_FLAG_HOLES;
390 if (image_length > 0)
391 super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET;
392 super->size = size;
393 memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature));
394
395 super->fsid.crc = crc32(0L, Z_NULL, 0);
396 super->fsid.edition = opt_edition;
397 super->fsid.blocks = total_blocks;
398 super->fsid.files = total_nodes;
399
400 memset(super->name, 0x00, sizeof(super->name));
401 if (opt_name)
402 strncpy(super->name, opt_name, sizeof(super->name));
403 else
404 strncpy(super->name, "Compressed", sizeof(super->name));
405
406 super->root.mode = root->mode;
407 super->root.uid = root->uid;
408 super->root.gid = root->gid;
409 super->root.size = root->size;
410 super->root.offset = offset >> 2;
411
412 return offset;
413 }
414
set_data_offset(struct entry * entry,char * base,unsigned long offset)415 static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
416 {
417 struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
418
419 if ((offset & 3) != 0) {
420 die(MKFS_ERROR, 0, "illegal offset of %lu bytes", offset);
421 }
422 if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) {
423 die(MKFS_ERROR, 0, "filesystem too big");
424 }
425 inode->offset = (offset >> 2);
426 }
427
428 /*
429 * TODO: Does this work for chars >= 0x80? Most filesystems use UTF-8
430 * encoding for filenames, whereas the console is a single-byte
431 * character set like iso-latin-1.
432 */
print_node(struct entry * e)433 static void print_node(struct entry *e)
434 {
435 char info[10];
436 char type = '?';
437
438 if (S_ISREG(e->mode)) type = 'f';
439 else if (S_ISDIR(e->mode)) type = 'd';
440 else if (S_ISLNK(e->mode)) type = 'l';
441 else if (S_ISCHR(e->mode)) type = 'c';
442 else if (S_ISBLK(e->mode)) type = 'b';
443 else if (S_ISFIFO(e->mode)) type = 'p';
444 else if (S_ISSOCK(e->mode)) type = 's';
445
446 if (S_ISCHR(e->mode) || (S_ISBLK(e->mode))) {
447 /* major/minor numbers can be as high as 2^12 or 4096 */
448 snprintf(info, 10, "%4d,%4d", major(e->size), minor(e->size));
449 }
450 else {
451 /* size be as high as 2^24 or 16777216 */
452 snprintf(info, 10, "%9d", e->size);
453 }
454
455 printf("%c %04o %s %5d:%-3d %s\n",
456 type, e->mode & ~S_IFMT, info, e->uid, e->gid, e->name);
457 }
458
459 /*
460 * We do a width-first printout of the directory
461 * entries, using a stack to remember the directories
462 * we've seen.
463 */
write_directory_structure(struct entry * entry,char * base,unsigned int offset)464 static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset)
465 {
466 int stack_entries = 0;
467 int stack_size = 64;
468 struct entry **entry_stack;
469
470 entry_stack = malloc(stack_size * sizeof(struct entry *));
471 if (!entry_stack) {
472 die(MKFS_ERROR, 1, "malloc failed");
473 }
474
475 if (opt_verbose) {
476 printf("root:\n");
477 }
478
479 for (;;) {
480 int dir_start = stack_entries;
481 while (entry) {
482 struct cramfs_inode *inode = (struct cramfs_inode *) (base + offset);
483 size_t len = strlen(entry->name);
484
485 entry->dir_offset = offset;
486
487 inode->mode = entry->mode;
488 inode->uid = entry->uid;
489 inode->gid = entry->gid;
490 inode->size = entry->size;
491 inode->offset = 0;
492 /* Non-empty directories, regfiles and symlinks will
493 write over inode->offset later. */
494
495 offset += sizeof(struct cramfs_inode);
496 total_nodes++; /* another node */
497 memcpy(base + offset, entry->name, len);
498 /* Pad up the name to a 4-byte boundary */
499 while (len & 3) {
500 *(base + offset + len) = '\0';
501 len++;
502 }
503 inode->namelen = len >> 2;
504 offset += len;
505
506 if (opt_verbose)
507 print_node(entry);
508
509 if (entry->child) {
510 if (stack_entries >= stack_size) {
511 stack_size *= 2;
512 entry_stack = realloc(entry_stack, stack_size * sizeof(struct entry *));
513 if (!entry_stack) {
514 die(MKFS_ERROR, 1, "realloc failed");
515 }
516 }
517 entry_stack[stack_entries] = entry;
518 stack_entries++;
519 }
520 entry = entry->next;
521 }
522
523 /*
524 * Reverse the order the stack entries pushed during
525 * this directory, for a small optimization of disk
526 * access in the created fs. This change makes things
527 * `ls -UR' order.
528 */
529 {
530 struct entry **lo = entry_stack + dir_start;
531 struct entry **hi = entry_stack + stack_entries;
532 struct entry *tmp;
533
534 while (lo < --hi) {
535 tmp = *lo;
536 *lo++ = *hi;
537 *hi = tmp;
538 }
539 }
540
541 /* Pop a subdirectory entry from the stack, and recurse. */
542 if (!stack_entries)
543 break;
544 stack_entries--;
545 entry = entry_stack[stack_entries];
546
547 set_data_offset(entry, base, offset);
548 if (opt_verbose) {
549 printf("%s:\n", entry->name);
550 }
551 entry = entry->child;
552 }
553 free(entry_stack);
554 return offset;
555 }
556
is_zero(char const * begin,unsigned len)557 static int is_zero(char const *begin, unsigned len)
558 {
559 /* Returns non-zero iff the first LEN bytes from BEGIN are all NULs. */
560 return (len-- == 0 ||
561 (begin[0] == '\0' &&
562 (len-- == 0 ||
563 (begin[1] == '\0' &&
564 (len-- == 0 ||
565 (begin[2] == '\0' &&
566 (len-- == 0 ||
567 (begin[3] == '\0' &&
568 memcmp(begin, begin + 4, len) == 0))))))));
569 }
570
571 /*
572 * One 4-byte pointer per block and then the actual blocked
573 * output. The first block does not need an offset pointer,
574 * as it will start immediately after the pointer block;
575 * so the i'th pointer points to the end of the i'th block
576 * (i.e. the start of the (i+1)'th block or past EOF).
577 *
578 * Note that size > 0, as a zero-sized file wouldn't ever
579 * have gotten here in the first place.
580 */
do_compress(char * base,unsigned int offset,char const * name,char * uncompressed,unsigned int size)581 static unsigned int do_compress(char *base, unsigned int offset, char const *name, char *uncompressed, unsigned int size)
582 {
583 unsigned long original_size = size;
584 unsigned long original_offset = offset;
585 unsigned long new_size;
586 unsigned long blocks = (size - 1) / blksize + 1;
587 unsigned long curr = offset + 4 * blocks;
588 int change;
589
590 total_blocks += blocks;
591
592 do {
593 unsigned long len = 2 * blksize;
594 unsigned int input = size;
595 int err;
596
597 if (input > blksize)
598 input = blksize;
599 size -= input;
600 if (!(opt_holes && is_zero (uncompressed, input))) {
601 err = compress2(base + curr, &len, uncompressed, input, Z_BEST_COMPRESSION);
602 if (err != Z_OK) {
603 die(MKFS_ERROR, 0, "compression error: %s", zError(err));
604 }
605 curr += len;
606 }
607 uncompressed += input;
608
609 if (len > blksize*2) {
610 /* (I don't think this can happen with zlib.) */
611 die(MKFS_ERROR, 0, "AIEEE: block \"compressed\" to > 2*blocklength (%ld)", len);
612 }
613
614 *(u32 *) (base + offset) = curr;
615 offset += 4;
616 } while (size);
617
618 curr = (curr + 3) & ~3;
619 new_size = curr - original_offset;
620 /* TODO: Arguably, original_size in these 2 lines should be
621 st_blocks * 512. But if you say that then perhaps
622 administrative data should also be included in both. */
623 change = new_size - original_size;
624 if (opt_verbose > 1) {
625 printf("%6.2f%% (%+d bytes)\t%s\n",
626 (change * 100) / (double) original_size, change, name);
627 }
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 non-empty regfile) and non-null
636 * entry->uncompressed (i.e. every symlink).
637 */
write_data(struct entry * entry,char * base,unsigned int offset)638 static unsigned int write_data(struct entry *entry, char *base, unsigned int offset)
639 {
640 do {
641 if (entry->path || entry->uncompressed) {
642 if (entry->same) {
643 set_data_offset(entry, base, entry->same->offset);
644 entry->offset = entry->same->offset;
645 }
646 else {
647 set_data_offset(entry, base, offset);
648 entry->offset = offset;
649 map_entry(entry);
650 offset = do_compress(base, offset, entry->name, entry->uncompressed, entry->size);
651 unmap_entry(entry);
652 }
653 }
654 else if (entry->child)
655 offset = write_data(entry->child, base, offset);
656 entry=entry->next;
657 } while (entry);
658 return offset;
659 }
660
write_file(char * file,char * base,unsigned int offset)661 static unsigned int write_file(char *file, char *base, unsigned int offset)
662 {
663 int fd;
664 char *buf;
665
666 fd = open(file, O_RDONLY);
667 if (fd < 0) {
668 die(MKFS_ERROR, 1, "open failed: %s", file);
669 }
670 buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0);
671 if (buf == MAP_FAILED) {
672 die(MKFS_ERROR, 1, "mmap failed");
673 }
674 memcpy(base + offset, buf, image_length);
675 munmap(buf, image_length);
676 close (fd);
677 /* Pad up the image_length to a 4-byte boundary */
678 while (image_length & 3) {
679 *(base + offset + image_length) = '\0';
680 image_length++;
681 }
682 return (offset + image_length);
683 }
684
main(int argc,char ** argv)685 int main(int argc, char **argv)
686 {
687 struct stat st; /* used twice... */
688 struct entry *root_entry;
689 char *rom_image;
690 ssize_t offset, written;
691 int fd;
692 /* initial guess (upper-bound) of required filesystem size */
693 loff_t fslen_ub = sizeof(struct cramfs_super);
694 char const *dirname, *outfile;
695 u32 crc;
696 int c; /* for getopt */
697 char *ep; /* for strtoul */
698
699 total_blocks = 0;
700
701 if (argc)
702 progname = argv[0];
703
704 /* command line options */
705 while ((c = getopt(argc, argv, "hEe:i:n:psvz")) != EOF) {
706 switch (c) {
707 case 'h':
708 usage(MKFS_OK);
709 case 'E':
710 opt_errors = 1;
711 break;
712 case 'e':
713 errno = 0;
714 opt_edition = strtoul(optarg, &ep, 10);
715 if (errno || optarg[0] == '\0' || *ep != '\0')
716 usage(MKFS_USAGE);
717 break;
718 case 'i':
719 opt_image = optarg;
720 if (lstat(opt_image, &st) < 0) {
721 die(MKFS_ERROR, 1, "lstat failed: %s", opt_image);
722 }
723 image_length = st.st_size; /* may be padded later */
724 fslen_ub += (image_length + 3); /* 3 is for padding */
725 break;
726 case 'n':
727 opt_name = optarg;
728 break;
729 case 'p':
730 opt_pad = PAD_SIZE;
731 fslen_ub += PAD_SIZE;
732 break;
733 case 's':
734 /* old option, ignored */
735 break;
736 case 'v':
737 opt_verbose++;
738 break;
739 case 'z':
740 opt_holes = 1;
741 break;
742 }
743 }
744
745 if ((argc - optind) != 2)
746 usage(MKFS_USAGE);
747 dirname = argv[optind];
748 outfile = argv[optind + 1];
749
750 if (stat(dirname, &st) < 0) {
751 die(MKFS_USAGE, 1, "stat failed: %s", dirname);
752 }
753 fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
754 if (fd < 0) {
755 die(MKFS_USAGE, 1, "open failed: %s", outfile);
756 }
757
758 root_entry = calloc(1, sizeof(struct entry));
759 if (!root_entry) {
760 die(MKFS_ERROR, 1, "calloc failed");
761 }
762 root_entry->mode = st.st_mode;
763 root_entry->uid = st.st_uid;
764 root_entry->gid = st.st_gid;
765
766 root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub);
767
768 /* always allocate a multiple of blksize bytes because that's
769 what we're going to write later on */
770 fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1;
771
772 if (fslen_ub > MAXFSLEN) {
773 fprintf(stderr,
774 "warning: estimate of required size (upper bound) is %LdMB, but maximum image size is %uMB, we might die prematurely\n",
775 fslen_ub >> 20,
776 MAXFSLEN >> 20);
777 fslen_ub = MAXFSLEN;
778 }
779
780 /* find duplicate files. TODO: uses the most inefficient algorithm
781 possible. */
782 eliminate_doubles(root_entry, root_entry);
783
784 /* TODO: Why do we use a private/anonymous mapping here
785 followed by a write below, instead of just a shared mapping
786 and a couple of ftruncate calls? Is it just to save us
787 having to deal with removing the file afterwards? If we
788 really need this huge anonymous mapping, we ought to mmap
789 in smaller chunks, so that the user doesn't need nn MB of
790 RAM free. If the reason is to be able to write to
791 un-mmappable block devices, then we could try shared mmap
792 and revert to anonymous mmap if the shared mmap fails. */
793 rom_image = mmap(NULL, fslen_ub?fslen_ub:1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
794
795 if (rom_image == MAP_FAILED) {
796 die(MKFS_ERROR, 1, "mmap failed");
797 }
798
799 /* Skip the first opt_pad bytes for boot loader code */
800 offset = opt_pad;
801 memset(rom_image, 0x00, opt_pad);
802
803 /* Skip the superblock and come back to write it later. */
804 offset += sizeof(struct cramfs_super);
805
806 /* Insert a file image. */
807 if (opt_image) {
808 printf("Including: %s\n", opt_image);
809 offset = write_file(opt_image, rom_image, offset);
810 }
811
812 offset = write_directory_structure(root_entry->child, rom_image, offset);
813 printf("Directory data: %d bytes\n", offset);
814
815 offset = write_data(root_entry, rom_image, offset);
816
817 /* We always write a multiple of blksize bytes, so that
818 losetup works. */
819 offset = ((offset - 1) | (blksize - 1)) + 1;
820 printf("Everything: %d kilobytes\n", offset >> 10);
821
822 /* Write the superblock now that we can fill in all of the fields. */
823 write_superblock(root_entry, rom_image+opt_pad, offset);
824 printf("Super block: %d bytes\n", sizeof(struct cramfs_super));
825
826 /* Put the checksum in. */
827 crc = crc32(0L, Z_NULL, 0);
828 crc = crc32(crc, (rom_image+opt_pad), (offset-opt_pad));
829 ((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = crc;
830 printf("CRC: %x\n", crc);
831
832 /* Check to make sure we allocated enough space. */
833 if (fslen_ub < offset) {
834 die(MKFS_ERROR, 0, "not enough space allocated for ROM image (%Ld allocated, %d used)", fslen_ub, offset);
835 }
836
837 written = write(fd, rom_image, offset);
838 if (written < 0) {
839 die(MKFS_ERROR, 1, "write failed");
840 }
841 if (offset != written) {
842 die(MKFS_ERROR, 0, "ROM image write failed (wrote %d of %d bytes)", written, offset);
843 }
844
845 /* (These warnings used to come at the start, but they scroll off the
846 screen too quickly.) */
847 if (warn_namelen)
848 fprintf(stderr, /* bytes, not chars: think UTF-8. */
849 "warning: filenames truncated to %d bytes (possibly less if multi-byte UTF-8)\n",
850 CRAMFS_MAXPATHLEN);
851 if (warn_skip)
852 fprintf(stderr, "warning: files were skipped due to errors\n");
853 if (warn_size)
854 fprintf(stderr,
855 "warning: file sizes truncated to %luMB (minus 1 byte)\n",
856 1L << (CRAMFS_SIZE_WIDTH - 20));
857 if (warn_uid) /* (not possible with current Linux versions) */
858 fprintf(stderr,
859 "warning: uids truncated to %u bits (this may be a security concern)\n",
860 CRAMFS_UID_WIDTH);
861 if (warn_gid)
862 fprintf(stderr,
863 "warning: gids truncated to %u bits (this may be a security concern)\n",
864 CRAMFS_GID_WIDTH);
865 if (warn_dev)
866 fprintf(stderr,
867 "WARNING: device numbers truncated to %u bits (this almost certainly means\n"
868 "that some device files will be wrong)\n",
869 CRAMFS_OFFSET_WIDTH);
870 if (opt_errors &&
871 (warn_namelen||warn_skip||warn_size||warn_uid||warn_gid||warn_dev))
872 exit(MKFS_ERROR);
873
874 exit(MKFS_OK);
875 }
876
877 /*
878 * Local variables:
879 * c-file-style: "linux"
880 * End:
881 */
882