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