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
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 /*
22  * If you change the disk format of cramfs, please update fs/cramfs/README.
23  */
24 
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <sys/mman.h>
30 #include <fcntl.h>
31 #include <dirent.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <linux/cramfs_fs.h>
37 #include <zlib.h>
38 
39 #define	MAP_ANONYMOUS	MAP_ANON
40 typedef	long long	loff_t;
41 
42 /* Exit codes used by mkfs-type programs */
43 #define MKFS_OK          0	/* No errors */
44 #define MKFS_ERROR       8	/* Operational error */
45 #define MKFS_USAGE       16	/* Usage or syntax error */
46 
47 /* The kernel only supports PAD_SIZE of 0 and 512. */
48 #define PAD_SIZE 512
49 
50 /* The kernel assumes PAGE_CACHE_SIZE as block size. */
51 #define PAGE_CACHE_SIZE (4096)
52 
53 /*
54  * The longest filename component to allow for in the input directory tree.
55  * ext2fs (and many others) allow up to 255 bytes.  A couple of filesystems
56  * allow longer (e.g. smbfs 1024), but there isn't much use in supporting
57  * >255-byte names in the input directory tree given that such names get
58  * truncated to CRAMFS_MAXPATHLEN (252 bytes) when written to cramfs.
59  *
60  * Old versions of mkcramfs generated corrupted filesystems if any input
61  * filenames exceeded CRAMFS_MAXPATHLEN (252 bytes), however old
62  * versions of cramfsck seem to have been able to detect the corruption.
63  */
64 #define MAX_INPUT_NAMELEN 255
65 
66 /*
67  * Maximum size fs you can create is roughly 256MB.  (The last file's
68  * data must begin within 256MB boundary but can extend beyond that.)
69  *
70  * Note that if you want it to fit in a ROM then you're limited to what the
71  * hardware and kernel can support.
72  */
73 #define MAXFSLEN ((((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2) /* offset */ \
74 		  + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */ \
75 		  + (1 << CRAMFS_SIZE_WIDTH) * 4 / PAGE_CACHE_SIZE /* block pointers */ )
76 
77 static const char *progname = "mkcramfs";
78 static unsigned int blksize = PAGE_CACHE_SIZE;
79 static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */
80 static int image_length = 0;
81 
82 /*
83  * If opt_holes is set, then mkcramfs can create explicit holes in the
84  * data, which saves 26 bytes per hole (which is a lot smaller a
85  * saving than most most filesystems).
86  *
87  * Note that kernels up to at least 2.3.39 don't support cramfs holes,
88  * which is why this is turned off by default.
89  *
90  * If opt_verbose is 1, be verbose.  If it is higher, be even more verbose.
91  */
92 static u32 opt_edition = 0;
93 static int opt_errors = 0;
94 static int opt_holes = 0;
95 static int opt_pad = 0;
96 static int opt_verbose = 0;
97 static char *opt_image = NULL;
98 static char *opt_name = NULL;
99 
100 static int warn_dev, warn_gid, warn_namelen, warn_skip, warn_size, warn_uid;
101 
102 /* In-core version of inode / directory entry. */
103 struct entry {
104 	/* stats */
105 	unsigned char *name;
106 	unsigned int mode, size, uid, gid;
107 
108 	/* these are only used for non-empty files */
109 	char *path;		/* always null except non-empty files */
110 	int fd;			/* temporarily open files while mmapped */
111 
112 	/* FS data */
113 	void *uncompressed;
114 	/* points to other identical file */
115 	struct entry *same;
116 	unsigned int offset;		/* pointer to compressed data in archive */
117 	unsigned int dir_offset;	/* Where in the archive is the directory entry? */
118 
119 	/* organization */
120 	struct entry *child; /* null for non-directories and empty directories */
121 	struct entry *next;
122 };
123 
124 /* Input status of 0 to print help and exit without an error. */
usage(int status)125 static void usage(int status)
126 {
127 	FILE *stream = status ? stderr : stdout;
128 
129 	fprintf(stream, "usage: %s [-h] [-e edition] [-i file] [-n name] dirname outfile\n"
130 		" -h         print this help\n"
131 		" -E         make all warnings errors (non-zero exit status)\n"
132 		" -e edition set edition number (part of fsid)\n"
133 		" -i file    insert a file image into the filesystem (requires >= 2.4.0)\n"
134 		" -n name    set name of cramfs filesystem\n"
135 		" -p         pad by %d bytes for boot code\n"
136 		" -s         sort directory entries (old option, ignored)\n"
137 		" -v         be more verbose\n"
138 		" -z         make explicit holes (requires >= 2.3.39)\n"
139 		" dirname    root of the directory tree to be compressed\n"
140 		" outfile    output file\n", progname, PAD_SIZE);
141 
142 	exit(status);
143 }
144 
die(int status,int syserr,const char * fmt,...)145 static void die(int status, int syserr, const char *fmt, ...)
146 {
147 	va_list arg_ptr;
148 	int save = errno;
149 
150 	fflush(0);
151 	va_start(arg_ptr, fmt);
152 	fprintf(stderr, "%s: ", progname);
153 	vfprintf(stderr, fmt, arg_ptr);
154 	if (syserr) {
155 		fprintf(stderr, ": %s", strerror(save));
156 	}
157 	fprintf(stderr, "\n");
158 	va_end(arg_ptr);
159 	exit(status);
160 }
161 
map_entry(struct entry * entry)162 static void map_entry(struct entry *entry)
163 {
164 	if (entry->path) {
165 		entry->fd = open(entry->path, O_RDONLY);
166 		if (entry->fd < 0) {
167 			die(MKFS_ERROR, 1, "open failed: %s", entry->path);
168 		}
169 		entry->uncompressed = mmap(NULL, entry->size, PROT_READ, MAP_PRIVATE, entry->fd, 0);
170 		if (entry->uncompressed == MAP_FAILED) {
171 			die(MKFS_ERROR, 1, "mmap failed: %s", entry->path);
172 		}
173 	}
174 }
175 
unmap_entry(struct entry * entry)176 static void unmap_entry(struct entry *entry)
177 {
178 	if (entry->path) {
179 		if (munmap(entry->uncompressed, entry->size) < 0) {
180 			die(MKFS_ERROR, 1, "munmap failed: %s", entry->path);
181 		}
182 		close(entry->fd);
183 	}
184 }
185 
find_identical_file(struct entry * orig,struct entry * newfile)186 static int find_identical_file(struct entry *orig, struct entry *newfile)
187 {
188 	if (orig == newfile)
189 		return 1;
190 	if (!orig)
191 		return 0;
192 	if (orig->size == newfile->size && (orig->path || orig->uncompressed))
193 	{
194 		map_entry(orig);
195 		map_entry(newfile);
196 		if (!memcmp(orig->uncompressed, newfile->uncompressed, orig->size))
197 		{
198 			newfile->same = orig;
199 			unmap_entry(newfile);
200 			unmap_entry(orig);
201 			return 1;
202 		}
203 		unmap_entry(newfile);
204 		unmap_entry(orig);
205 	}
206 	return (find_identical_file(orig->child, newfile) ||
207 		find_identical_file(orig->next, newfile));
208 }
209 
eliminate_doubles(struct entry * root,struct entry * orig)210 static void eliminate_doubles(struct entry *root, struct entry *orig) {
211 	if (orig) {
212 		if (orig->size && (orig->path || orig->uncompressed))
213 			find_identical_file(root, orig);
214 		eliminate_doubles(root, orig->child);
215 		eliminate_doubles(root, orig->next);
216 	}
217 }
218 
219 /*
220  * We define our own sorting function instead of using alphasort which
221  * uses strcoll and changes ordering based on locale information.
222  */
cramsort(const void * a,const void * b)223 static int cramsort (const void *a, const void *b)
224 {
225 	return strcmp ((*(const struct dirent **) a)->d_name,
226 		       (*(const struct dirent **) b)->d_name);
227 }
228 
parse_directory(struct entry * root_entry,const char * name,struct entry ** prev,loff_t * fslen_ub)229 static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub)
230 {
231 	struct dirent **dirlist;
232 	int totalsize = 0, dircount, dirindex;
233 	char *path, *endpath;
234 	size_t len = strlen(name);
235 
236 	/* Set up the path. */
237 	/* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */
238 	path = malloc(len + 1 + MAX_INPUT_NAMELEN + 1);
239 	if (!path) {
240 		die(MKFS_ERROR, 1, "malloc failed");
241 	}
242 	memcpy(path, name, len);
243 	endpath = path + len;
244 	*endpath = '/';
245 	endpath++;
246 
247 	/* read in the directory and sort */
248 	dircount = scandir(name, &dirlist, 0, cramsort);
249 
250 	if (dircount < 0) {
251 		die(MKFS_ERROR, 1, "scandir failed: %s", name);
252 	}
253 
254 	/* process directory */
255 	for (dirindex = 0; dirindex < dircount; dirindex++) {
256 		struct dirent *dirent;
257 		struct entry *entry;
258 		struct stat st;
259 		int size;
260 		size_t namelen;
261 
262 		dirent = dirlist[dirindex];
263 
264 		/* Ignore "." and ".." - we won't be adding them to the archive */
265 		if (dirent->d_name[0] == '.') {
266 			if (dirent->d_name[1] == '\0')
267 				continue;
268 			if (dirent->d_name[1] == '.') {
269 				if (dirent->d_name[2] == '\0')
270 					continue;
271 			}
272 		}
273 		namelen = strlen(dirent->d_name);
274 		if (namelen > MAX_INPUT_NAMELEN) {
275 			die(MKFS_ERROR, 0,
276 				"very long (%u bytes) filename found: %s\n"
277 				"please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile",
278 				namelen, dirent->d_name);
279 		}
280 		memcpy(endpath, dirent->d_name, namelen + 1);
281 
282 		if (lstat(path, &st) < 0) {
283 			warn_skip = 1;
284 			continue;
285 		}
286 		entry = calloc(1, sizeof(struct entry));
287 		if (!entry) {
288 			die(MKFS_ERROR, 1, "calloc failed");
289 		}
290 		entry->name = strdup(dirent->d_name);
291 		if (!entry->name) {
292 			die(MKFS_ERROR, 1, "strdup failed");
293 		}
294 		/* truncate multi-byte UTF-8 filenames on character boundary */
295 		if (namelen > CRAMFS_MAXPATHLEN) {
296 			namelen = CRAMFS_MAXPATHLEN;
297 			warn_namelen = 1;
298 			/* the first lost byte must not be a trail byte */
299 			while ((entry->name[namelen] & 0xc0) == 0x80) {
300 				namelen--;
301 				/* are we reasonably certain it was UTF-8 ? */
302 				if (entry->name[namelen] < 0x80 || !namelen) {
303 					die(MKFS_ERROR, 0, "cannot truncate filenames not encoded in UTF-8");
304 				}
305 			}
306 			entry->name[namelen] = '\0';
307 		}
308 		entry->mode = st.st_mode;
309 		entry->size = st.st_size;
310 		entry->uid = st.st_uid;
311 		if (entry->uid >= 1 << CRAMFS_UID_WIDTH)
312 			warn_uid = 1;
313 		entry->gid = st.st_gid;
314 		if (entry->gid >= 1 << CRAMFS_GID_WIDTH)
315 			/* TODO: We ought to replace with a default
316 			   gid instead of truncating; otherwise there
317 			   are security problems.  Maybe mode should
318 			   be &= ~070.  Same goes for uid once Linux
319 			   supports >16-bit uids. */
320 			warn_gid = 1;
321 		size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
322 		*fslen_ub += size;
323 		if (S_ISDIR(st.st_mode)) {
324 			entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub);
325 		} else if (S_ISREG(st.st_mode)) {
326 			if (entry->size) {
327 				if (access(path, R_OK) < 0) {
328 					warn_skip = 1;
329 					continue;
330 				}
331 				entry->path = strdup(path);
332 				if (!entry->path) {
333 					die(MKFS_ERROR, 1, "strdup failed");
334 				}
335 				if ((entry->size >= 1 << CRAMFS_SIZE_WIDTH)) {
336 					warn_size = 1;
337 					entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1;
338 				}
339 			}
340 		} else if (S_ISLNK(st.st_mode)) {
341 			entry->uncompressed = malloc(entry->size);
342 			if (!entry->uncompressed) {
343 				die(MKFS_ERROR, 1, "malloc failed");
344 			}
345 			if (readlink(path, entry->uncompressed, entry->size) < 0) {
346 				warn_skip = 1;
347 				continue;
348 			}
349 		} else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
350 			/* maybe we should skip sockets */
351 			entry->size = 0;
352 		} else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
353 			entry->size = st.st_rdev;
354 			if (entry->size & -(1<<CRAMFS_SIZE_WIDTH))
355 				warn_dev = 1;
356 		} else {
357 			die(MKFS_ERROR, 0, "bogus file type: %s", entry->name);
358 		}
359 
360 		if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
361 			int blocks = ((entry->size - 1) / blksize + 1);
362 
363 			/* block pointers & data expansion allowance + data */
364 			if (entry->size)
365 				*fslen_ub += (4+26)*blocks + entry->size + 3;
366 		}
367 
368 		/* Link it into the list */
369 		*prev = entry;
370 		prev = &entry->next;
371 		totalsize += size;
372 	}
373 	free(path);
374 	free(dirlist);		/* allocated by scandir() with malloc() */
375 	return totalsize;
376 }
377 
378 /* Returns sizeof(struct cramfs_super), which includes the root inode. */
write_superblock(struct entry * root,char * base,int size)379 static unsigned int write_superblock(struct entry *root, char *base, int size)
380 {
381 	struct cramfs_super *super = (struct cramfs_super *) base;
382 	unsigned int offset = sizeof(struct cramfs_super) + image_length;
383 
384 	offset += opt_pad;	/* 0 if no padding */
385 
386 	super->magic = CRAMFS_MAGIC;
387 	super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS;
388 	if (opt_holes)
389 		super->flags |= CRAMFS_FLAG_HOLES;
390 	if (image_length > 0)
391 		super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET;
392 	super->size = size;
393 	memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature));
394 
395 	super->fsid.crc = crc32(0L, Z_NULL, 0);
396 	super->fsid.edition = opt_edition;
397 	super->fsid.blocks = total_blocks;
398 	super->fsid.files = total_nodes;
399 
400 	memset(super->name, 0x00, sizeof(super->name));
401 	if (opt_name)
402 		strncpy(super->name, opt_name, sizeof(super->name));
403 	else
404 		strncpy(super->name, "Compressed", sizeof(super->name));
405 
406 	super->root.mode = root->mode;
407 	super->root.uid = root->uid;
408 	super->root.gid = root->gid;
409 	super->root.size = root->size;
410 	super->root.offset = offset >> 2;
411 
412 	return offset;
413 }
414 
set_data_offset(struct entry * entry,char * base,unsigned long offset)415 static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
416 {
417 	struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
418 
419 	if ((offset & 3) != 0) {
420 		die(MKFS_ERROR, 0, "illegal offset of %lu bytes", offset);
421 	}
422 	if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) {
423 		die(MKFS_ERROR, 0, "filesystem too big");
424 	}
425 	inode->offset = (offset >> 2);
426 }
427 
428 /*
429  * TODO: Does this work for chars >= 0x80?  Most filesystems use UTF-8
430  * encoding for filenames, whereas the console is a single-byte
431  * character set like iso-latin-1.
432  */
print_node(struct entry * e)433 static void print_node(struct entry *e)
434 {
435 	char info[10];
436 	char type = '?';
437 
438 	if (S_ISREG(e->mode)) type = 'f';
439 	else if (S_ISDIR(e->mode)) type = 'd';
440 	else if (S_ISLNK(e->mode)) type = 'l';
441 	else if (S_ISCHR(e->mode)) type = 'c';
442 	else if (S_ISBLK(e->mode)) type = 'b';
443 	else if (S_ISFIFO(e->mode)) type = 'p';
444 	else if (S_ISSOCK(e->mode)) type = 's';
445 
446 	if (S_ISCHR(e->mode) || (S_ISBLK(e->mode))) {
447 		/* major/minor numbers can be as high as 2^12 or 4096 */
448 		snprintf(info, 10, "%4d,%4d", major(e->size), minor(e->size));
449 	}
450 	else {
451 		/* size be as high as 2^24 or 16777216 */
452 		snprintf(info, 10, "%9d", e->size);
453 	}
454 
455 	printf("%c %04o %s %5d:%-3d %s\n",
456 	       type, e->mode & ~S_IFMT, info, e->uid, e->gid, e->name);
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  */
write_directory_structure(struct entry * entry,char * base,unsigned int offset)464 static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset)
465 {
466 	int stack_entries = 0;
467 	int stack_size = 64;
468 	struct entry **entry_stack;
469 
470 	entry_stack = malloc(stack_size * sizeof(struct entry *));
471 	if (!entry_stack) {
472 		die(MKFS_ERROR, 1, "malloc failed");
473 	}
474 
475 	if (opt_verbose) {
476 		printf("root:\n");
477 	}
478 
479 	for (;;) {
480 		int dir_start = stack_entries;
481 		while (entry) {
482 			struct cramfs_inode *inode = (struct cramfs_inode *) (base + offset);
483 			size_t len = strlen(entry->name);
484 
485 			entry->dir_offset = offset;
486 
487 			inode->mode = entry->mode;
488 			inode->uid = entry->uid;
489 			inode->gid = entry->gid;
490 			inode->size = entry->size;
491 			inode->offset = 0;
492 			/* Non-empty directories, regfiles and symlinks will
493 			   write over inode->offset later. */
494 
495 			offset += sizeof(struct cramfs_inode);
496 			total_nodes++;	/* another node */
497 			memcpy(base + offset, entry->name, len);
498 			/* Pad up the name to a 4-byte boundary */
499 			while (len & 3) {
500 				*(base + offset + len) = '\0';
501 				len++;
502 			}
503 			inode->namelen = len >> 2;
504 			offset += len;
505 
506 			if (opt_verbose)
507 				print_node(entry);
508 
509 			if (entry->child) {
510 				if (stack_entries >= stack_size) {
511 					stack_size *= 2;
512 					entry_stack = realloc(entry_stack, stack_size * sizeof(struct entry *));
513 					if (!entry_stack) {
514 						die(MKFS_ERROR, 1, "realloc failed");
515 					}
516 				}
517 				entry_stack[stack_entries] = entry;
518 				stack_entries++;
519 			}
520 			entry = entry->next;
521 		}
522 
523 		/*
524 		 * Reverse the order the stack entries pushed during
525 		 * this directory, for a small optimization of disk
526 		 * access in the created fs.  This change makes things
527 		 * `ls -UR' order.
528 		 */
529 		{
530 			struct entry **lo = entry_stack + dir_start;
531 			struct entry **hi = entry_stack + stack_entries;
532 			struct entry *tmp;
533 
534 			while (lo < --hi) {
535 				tmp = *lo;
536 				*lo++ = *hi;
537 				*hi = tmp;
538 			}
539 		}
540 
541 		/* Pop a subdirectory entry from the stack, and recurse. */
542 		if (!stack_entries)
543 			break;
544 		stack_entries--;
545 		entry = entry_stack[stack_entries];
546 
547 		set_data_offset(entry, base, offset);
548 		if (opt_verbose) {
549 			printf("%s:\n", entry->name);
550 		}
551 		entry = entry->child;
552 	}
553 	free(entry_stack);
554 	return offset;
555 }
556 
is_zero(char const * begin,unsigned len)557 static int is_zero(char const *begin, unsigned len)
558 {
559 	/* Returns non-zero iff the first LEN bytes from BEGIN are all NULs. */
560 	return (len-- == 0 ||
561 		(begin[0] == '\0' &&
562 		 (len-- == 0 ||
563 		  (begin[1] == '\0' &&
564 		   (len-- == 0 ||
565 		    (begin[2] == '\0' &&
566 		     (len-- == 0 ||
567 		      (begin[3] == '\0' &&
568 		       memcmp(begin, begin + 4, len) == 0))))))));
569 }
570 
571 /*
572  * One 4-byte pointer per block and then the actual blocked
573  * output. The first block does not need an offset pointer,
574  * as it will start immediately after the pointer block;
575  * so the i'th pointer points to the end of the i'th block
576  * (i.e. the start of the (i+1)'th block or past EOF).
577  *
578  * Note that size > 0, as a zero-sized file wouldn't ever
579  * have gotten here in the first place.
580  */
do_compress(char * base,unsigned int offset,char const * name,char * uncompressed,unsigned int size)581 static unsigned int do_compress(char *base, unsigned int offset, char const *name, char *uncompressed, unsigned int size)
582 {
583 	unsigned long original_size = size;
584 	unsigned long original_offset = offset;
585 	unsigned long new_size;
586 	unsigned long blocks = (size - 1) / blksize + 1;
587 	unsigned long curr = offset + 4 * blocks;
588 	int change;
589 
590 	total_blocks += blocks;
591 
592 	do {
593 		unsigned long len = 2 * blksize;
594 		unsigned int input = size;
595 		int err;
596 
597 		if (input > blksize)
598 			input = blksize;
599 		size -= input;
600 		if (!(opt_holes && is_zero (uncompressed, input))) {
601 			err = compress2(base + curr, &len, uncompressed, input, Z_BEST_COMPRESSION);
602 			if (err != Z_OK) {
603 				die(MKFS_ERROR, 0, "compression error: %s", zError(err));
604 			}
605 			curr += len;
606 		}
607 		uncompressed += input;
608 
609 		if (len > blksize*2) {
610 			/* (I don't think this can happen with zlib.) */
611 			die(MKFS_ERROR, 0, "AIEEE: block \"compressed\" to > 2*blocklength (%ld)", len);
612 		}
613 
614 		*(u32 *) (base + offset) = curr;
615 		offset += 4;
616 	} while (size);
617 
618 	curr = (curr + 3) & ~3;
619 	new_size = curr - original_offset;
620 	/* TODO: Arguably, original_size in these 2 lines should be
621 	   st_blocks * 512.  But if you say that then perhaps
622 	   administrative data should also be included in both. */
623 	change = new_size - original_size;
624 	if (opt_verbose > 1) {
625 		printf("%6.2f%% (%+d bytes)\t%s\n",
626 		       (change * 100) / (double) original_size, change, name);
627 	}
628 
629 	return curr;
630 }
631 
632 
633 /*
634  * Traverse the entry tree, writing data for every item that has
635  * non-null entry->path (i.e. every non-empty regfile) and non-null
636  * entry->uncompressed (i.e. every symlink).
637  */
write_data(struct entry * entry,char * base,unsigned int offset)638 static unsigned int write_data(struct entry *entry, char *base, unsigned int offset)
639 {
640 	do {
641 		if (entry->path || entry->uncompressed) {
642 			if (entry->same) {
643 				set_data_offset(entry, base, entry->same->offset);
644 				entry->offset = entry->same->offset;
645 			}
646 			else {
647 				set_data_offset(entry, base, offset);
648 				entry->offset = offset;
649 				map_entry(entry);
650 				offset = do_compress(base, offset, entry->name, entry->uncompressed, entry->size);
651 				unmap_entry(entry);
652 			}
653 		}
654 		else if (entry->child)
655 			offset = write_data(entry->child, base, offset);
656 		entry=entry->next;
657 	} while (entry);
658 	return offset;
659 }
660 
write_file(char * file,char * base,unsigned int offset)661 static unsigned int write_file(char *file, char *base, unsigned int offset)
662 {
663 	int fd;
664 	char *buf;
665 
666 	fd = open(file, O_RDONLY);
667 	if (fd < 0) {
668 		die(MKFS_ERROR, 1, "open failed: %s", file);
669 	}
670 	buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0);
671 	if (buf == MAP_FAILED) {
672 		die(MKFS_ERROR, 1, "mmap failed");
673 	}
674 	memcpy(base + offset, buf, image_length);
675 	munmap(buf, image_length);
676 	close (fd);
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 
main(int argc,char ** argv)685 int main(int argc, char **argv)
686 {
687 	struct stat st;		/* used twice... */
688 	struct entry *root_entry;
689 	char *rom_image;
690 	ssize_t offset, written;
691 	int fd;
692 	/* initial guess (upper-bound) of required filesystem size */
693 	loff_t fslen_ub = sizeof(struct cramfs_super);
694 	char const *dirname, *outfile;
695 	u32 crc;
696 	int c;			/* for getopt */
697 	char *ep;		/* for strtoul */
698 
699 	total_blocks = 0;
700 
701 	if (argc)
702 		progname = argv[0];
703 
704 	/* command line options */
705 	while ((c = getopt(argc, argv, "hEe:i:n:psvz")) != EOF) {
706 		switch (c) {
707 		case 'h':
708 			usage(MKFS_OK);
709 		case 'E':
710 			opt_errors = 1;
711 			break;
712 		case 'e':
713 			errno = 0;
714 			opt_edition = strtoul(optarg, &ep, 10);
715 			if (errno || optarg[0] == '\0' || *ep != '\0')
716 				usage(MKFS_USAGE);
717 			break;
718 		case 'i':
719 			opt_image = optarg;
720 			if (lstat(opt_image, &st) < 0) {
721 				die(MKFS_ERROR, 1, "lstat failed: %s", opt_image);
722 			}
723 			image_length = st.st_size; /* may be padded later */
724 			fslen_ub += (image_length + 3); /* 3 is for padding */
725 			break;
726 		case 'n':
727 			opt_name = optarg;
728 			break;
729 		case 'p':
730 			opt_pad = PAD_SIZE;
731 			fslen_ub += PAD_SIZE;
732 			break;
733 		case 's':
734 			/* old option, ignored */
735 			break;
736 		case 'v':
737 			opt_verbose++;
738 			break;
739 		case 'z':
740 			opt_holes = 1;
741 			break;
742 		}
743 	}
744 
745 	if ((argc - optind) != 2)
746 		usage(MKFS_USAGE);
747 	dirname = argv[optind];
748 	outfile = argv[optind + 1];
749 
750 	if (stat(dirname, &st) < 0) {
751 		die(MKFS_USAGE, 1, "stat failed: %s", dirname);
752 	}
753 	fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
754 	if (fd < 0) {
755 		die(MKFS_USAGE, 1, "open failed: %s", outfile);
756 	}
757 
758 	root_entry = calloc(1, sizeof(struct entry));
759 	if (!root_entry) {
760 		die(MKFS_ERROR, 1, "calloc failed");
761 	}
762 	root_entry->mode = st.st_mode;
763 	root_entry->uid = st.st_uid;
764 	root_entry->gid = st.st_gid;
765 
766 	root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub);
767 
768 	/* always allocate a multiple of blksize bytes because that's
769 	   what we're going to write later on */
770 	fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1;
771 
772 	if (fslen_ub > MAXFSLEN) {
773 		fprintf(stderr,
774 			"warning: estimate of required size (upper bound) is %LdMB, but maximum image size is %uMB, we might die prematurely\n",
775 			fslen_ub >> 20,
776 			MAXFSLEN >> 20);
777 		fslen_ub = MAXFSLEN;
778 	}
779 
780 	/* find duplicate files. TODO: uses the most inefficient algorithm
781 	   possible. */
782 	eliminate_doubles(root_entry, root_entry);
783 
784 	/* TODO: Why do we use a private/anonymous mapping here
785 	   followed by a write below, instead of just a shared mapping
786 	   and a couple of ftruncate calls?  Is it just to save us
787 	   having to deal with removing the file afterwards?  If we
788 	   really need this huge anonymous mapping, we ought to mmap
789 	   in smaller chunks, so that the user doesn't need nn MB of
790 	   RAM free.  If the reason is to be able to write to
791 	   un-mmappable block devices, then we could try shared mmap
792 	   and revert to anonymous mmap if the shared mmap fails. */
793 	rom_image = mmap(NULL, fslen_ub?fslen_ub:1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
794 
795 	if (rom_image == MAP_FAILED) {
796 		die(MKFS_ERROR, 1, "mmap failed");
797 	}
798 
799 	/* Skip the first opt_pad bytes for boot loader code */
800 	offset = opt_pad;
801 	memset(rom_image, 0x00, opt_pad);
802 
803 	/* Skip the superblock and come back to write it later. */
804 	offset += sizeof(struct cramfs_super);
805 
806 	/* Insert a file image. */
807 	if (opt_image) {
808 		printf("Including: %s\n", opt_image);
809 		offset = write_file(opt_image, rom_image, offset);
810 	}
811 
812 	offset = write_directory_structure(root_entry->child, rom_image, offset);
813 	printf("Directory data: %d bytes\n", offset);
814 
815 	offset = write_data(root_entry, rom_image, offset);
816 
817 	/* We always write a multiple of blksize bytes, so that
818 	   losetup works. */
819 	offset = ((offset - 1) | (blksize - 1)) + 1;
820 	printf("Everything: %d kilobytes\n", offset >> 10);
821 
822 	/* Write the superblock now that we can fill in all of the fields. */
823 	write_superblock(root_entry, rom_image+opt_pad, offset);
824 	printf("Super block: %d bytes\n", sizeof(struct cramfs_super));
825 
826 	/* Put the checksum in. */
827 	crc = crc32(0L, Z_NULL, 0);
828 	crc = crc32(crc, (rom_image+opt_pad), (offset-opt_pad));
829 	((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = crc;
830 	printf("CRC: %x\n", crc);
831 
832 	/* Check to make sure we allocated enough space. */
833 	if (fslen_ub < offset) {
834 		die(MKFS_ERROR, 0, "not enough space allocated for ROM image (%Ld allocated, %d used)", fslen_ub, offset);
835 	}
836 
837 	written = write(fd, rom_image, offset);
838 	if (written < 0) {
839 		die(MKFS_ERROR, 1, "write failed");
840 	}
841 	if (offset != written) {
842 		die(MKFS_ERROR, 0, "ROM image write failed (wrote %d of %d bytes)", written, offset);
843 	}
844 
845 	/* (These warnings used to come at the start, but they scroll off the
846 	   screen too quickly.) */
847 	if (warn_namelen)
848 		fprintf(stderr, /* bytes, not chars: think UTF-8. */
849 			"warning: filenames truncated to %d bytes (possibly less if multi-byte UTF-8)\n",
850 			CRAMFS_MAXPATHLEN);
851 	if (warn_skip)
852 		fprintf(stderr, "warning: files were skipped due to errors\n");
853 	if (warn_size)
854 		fprintf(stderr,
855 			"warning: file sizes truncated to %luMB (minus 1 byte)\n",
856 			1L << (CRAMFS_SIZE_WIDTH - 20));
857 	if (warn_uid) /* (not possible with current Linux versions) */
858 		fprintf(stderr,
859 			"warning: uids truncated to %u bits (this may be a security concern)\n",
860 			CRAMFS_UID_WIDTH);
861 	if (warn_gid)
862 		fprintf(stderr,
863 			"warning: gids truncated to %u bits (this may be a security concern)\n",
864 			CRAMFS_GID_WIDTH);
865 	if (warn_dev)
866 		fprintf(stderr,
867 			"WARNING: device numbers truncated to %u bits (this almost certainly means\n"
868 			"that some device files will be wrong)\n",
869 			CRAMFS_OFFSET_WIDTH);
870 	if (opt_errors &&
871 	    (warn_namelen||warn_skip||warn_size||warn_uid||warn_gid||warn_dev))
872 		exit(MKFS_ERROR);
873 
874 	exit(MKFS_OK);
875 }
876 
877 /*
878  * Local variables:
879  * c-file-style: "linux"
880  * End:
881  */
882