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