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