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