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