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