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