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