1/*
2 * YAFFS: Yet another FFS. A NAND-flash specific file system.
3 *
4 * makeyaffsimage.c
5 *
6 * Makes a YAFFS file system image that can be used to load up a file system.
7 *
8 * Copyright (C) 2002 Aleph One Ltd.
9 *   for Toby Churchill Ltd and Brightstar Engineering
10 *
11 * Created by Charles Manning <charles@aleph1.co.uk>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 *
17 *
18 * Nick Bane modifications flagged NCB
19 *
20 * Endian handling patches by James Ng.
21 *
22 * mkyaffs2image hacks by NCB
23 *
24 */
25
26#include <stdlib.h>
27#include <stdio.h>
28#include <fcntl.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <dirent.h>
32#include <string.h>
33#include <unistd.h>
34
35#define XATTR_NAME_SELINUX "security.selinux"
36#include <selinux/selinux.h>
37#include <selinux/label.h>
38
39static struct selabel_handle *sehnd;
40static unsigned int seprefixlen;
41static char *mntpoint;
42
43#include <private/android_filesystem_config.h>
44
45#include "yaffs_ecc.h"
46#include "yaffs_guts.h"
47
48#include "yaffs_tagsvalidity.h"
49#include "yaffs_packedtags2.h"
50
51unsigned source_path_len = 0;
52unsigned yaffs_traceMask=0;
53
54#define MAX_OBJECTS 50000
55
56unsigned chunkSize = 2048;
57unsigned spareSize = 64;
58
59const char * mkyaffsimage_c_version = "$Id: mkyaffs2image.c,v 1.2 2005/12/13 00:34:58 tpoynor Exp $";
60
61
62typedef struct
63{
64	dev_t dev;
65	ino_t ino;
66	int   obj;
67} objItem;
68
69
70static objItem obj_list[MAX_OBJECTS];
71static int n_obj = 0;
72static int obj_id = YAFFS_NOBJECT_BUCKETS + 1;
73
74static int nObjects, nDirectories, nPages;
75
76static int outFile;
77
78static int error;
79
80#ifdef HAVE_BIG_ENDIAN
81static int convert_endian = 1;
82#elif defined(HAVE_LITTLE_ENDIAN)
83static int convert_endian = 0;
84#endif
85
86static int obj_compare(const void *a, const void * b)
87{
88  objItem *oa, *ob;
89
90  oa = (objItem *)a;
91  ob = (objItem *)b;
92
93  if(oa->dev < ob->dev) return -1;
94  if(oa->dev > ob->dev) return 1;
95  if(oa->ino < ob->ino) return -1;
96  if(oa->ino > ob->ino) return 1;
97
98  return 0;
99}
100
101
102static void add_obj_to_list(dev_t dev, ino_t ino, int obj)
103{
104	if(n_obj < MAX_OBJECTS)
105	{
106		obj_list[n_obj].dev = dev;
107		obj_list[n_obj].ino = ino;
108		obj_list[n_obj].obj = obj;
109		n_obj++;
110		qsort(obj_list,n_obj,sizeof(objItem),obj_compare);
111
112	}
113	else
114	{
115		// oops! not enough space in the object array
116		fprintf(stderr,"Not enough space in object array\n");
117		exit(2);
118	}
119}
120
121
122static int find_obj_in_list(dev_t dev, ino_t ino)
123{
124	objItem *i = NULL;
125	objItem test;
126
127	test.dev = dev;
128	test.ino = ino;
129
130	if(n_obj > 0)
131	{
132		i = bsearch(&test,obj_list,n_obj,sizeof(objItem),obj_compare);
133	}
134
135	if(i)
136	{
137		return i->obj;
138	}
139	return -1;
140}
141
142#define SWAP32(x)   ((((x) & 0x000000FF) << 24) | \
143                     (((x) & 0x0000FF00) << 8 ) | \
144                     (((x) & 0x00FF0000) >> 8 ) | \
145                     (((x) & 0xFF000000) >> 24))
146
147#define SWAP16(x)   ((((x) & 0x00FF) << 8) | \
148                     (((x) & 0xFF00) >> 8))
149
150// This one is easier, since the types are more standard. No funky shifts here.
151static void object_header_little_to_big_endian(yaffs_ObjectHeader* oh)
152{
153    oh->type = SWAP32(oh->type); // GCC makes enums 32 bits.
154    oh->parentObjectId = SWAP32(oh->parentObjectId); // int
155    oh->sum__NoLongerUsed = SWAP16(oh->sum__NoLongerUsed); // __u16 - Not used, but done for completeness.
156    // name = skip. Char array. Not swapped.
157    oh->yst_mode = SWAP32(oh->yst_mode);
158#ifdef CONFIG_YAFFS_WINCE // WinCE doesn't implement this, but we need to just in case.
159    // In fact, WinCE would be *THE* place where this would be an issue!
160    oh->notForWinCE[0] = SWAP32(oh->notForWinCE[0]);
161    oh->notForWinCE[1] = SWAP32(oh->notForWinCE[1]);
162    oh->notForWinCE[2] = SWAP32(oh->notForWinCE[2]);
163    oh->notForWinCE[3] = SWAP32(oh->notForWinCE[3]);
164    oh->notForWinCE[4] = SWAP32(oh->notForWinCE[4]);
165#else
166    // Regular POSIX.
167    oh->yst_uid = SWAP32(oh->yst_uid);
168    oh->yst_gid = SWAP32(oh->yst_gid);
169    oh->yst_atime = SWAP32(oh->yst_atime);
170    oh->yst_mtime = SWAP32(oh->yst_mtime);
171    oh->yst_ctime = SWAP32(oh->yst_ctime);
172#endif
173
174    oh->fileSize = SWAP32(oh->fileSize); // Aiee. An int... signed, at that!
175    oh->equivalentObjectId = SWAP32(oh->equivalentObjectId);
176    // alias  - char array.
177    oh->yst_rdev = SWAP32(oh->yst_rdev);
178
179#ifdef CONFIG_YAFFS_WINCE
180    oh->win_ctime[0] = SWAP32(oh->win_ctime[0]);
181    oh->win_ctime[1] = SWAP32(oh->win_ctime[1]);
182    oh->win_atime[0] = SWAP32(oh->win_atime[0]);
183    oh->win_atime[1] = SWAP32(oh->win_atime[1]);
184    oh->win_mtime[0] = SWAP32(oh->win_mtime[0]);
185    oh->win_mtime[1] = SWAP32(oh->win_mtime[1]);
186    oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]);
187    oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]);
188    oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]);
189    oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]);
190    oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]);
191    oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]);
192#else
193    oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]);
194    oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]);
195    oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]);
196    oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]);
197    oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]);
198    oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]);
199    oh->roomToGrow[6] = SWAP32(oh->roomToGrow[6]);
200    oh->roomToGrow[7] = SWAP32(oh->roomToGrow[7]);
201    oh->roomToGrow[8] = SWAP32(oh->roomToGrow[8]);
202    oh->roomToGrow[9] = SWAP32(oh->roomToGrow[9]);
203    oh->roomToGrow[10] = SWAP32(oh->roomToGrow[10]);
204    oh->roomToGrow[11] = SWAP32(oh->roomToGrow[11]);
205#endif
206}
207
208/* This little function converts a little endian tag to a big endian tag.
209 * NOTE: The tag is not usable after this other than calculating the CRC
210 * with.
211 */
212static void little_to_big_endian(yaffs_PackedTags2 *pt)
213{
214	pt->t.sequenceNumber = SWAP32(pt->t.sequenceNumber);
215	pt->t.objectId = SWAP32(pt->t.objectId);
216	pt->t.chunkId = SWAP32(pt->t.chunkId);
217	pt->t.byteCount = SWAP32(pt->t.byteCount);
218}
219
220static int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes)
221{
222	char spare[spareSize];
223	yaffs_ExtendedTags t;
224	yaffs_PackedTags2 *pt = (yaffs_PackedTags2 *)spare;
225
226	memset(spare, 0xff, spareSize);
227
228	error = write(outFile,data,chunkSize);
229	if(error < 0) return error;
230
231	yaffs_InitialiseTags(&t);
232
233	t.chunkId = chunkId;
234//	t.serialNumber = 0;
235	t.serialNumber = 1;	// **CHECK**
236	t.byteCount = nBytes;
237	t.objectId = objId;
238
239	t.sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
240
241// added NCB **CHECK**
242	t.chunkUsed = 1;
243
244	nPages++;
245
246	yaffs_PackTags2(pt,&t);
247
248	if (convert_endian)
249	{
250		little_to_big_endian(pt);
251	}
252
253//	return write(outFile,&pt,sizeof(yaffs_PackedTags2));
254	return write(outFile,spare, spareSize);
255
256}
257
258static int write_object_header(int objId, yaffs_ObjectType t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias, const char *secontext)
259{
260	__u8 bytes[chunkSize];
261
262
263	yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bytes;
264	char *xb = (char *)bytes + sizeof(*oh);
265	int xnamelen = strlen(XATTR_NAME_SELINUX) + 1;
266	int xvalsize = 0;
267	int xreclen = 0;
268
269	if (secontext) {
270		xvalsize = strlen(secontext) + 1;
271		xreclen = sizeof(int) + xnamelen + xvalsize;
272	}
273
274	memset(bytes,0xff,sizeof(bytes));
275
276	oh->type = t;
277
278	oh->parentObjectId = parent;
279
280	strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
281
282	if (xreclen) {
283		memcpy(xb, &xreclen, sizeof(int));
284		xb += sizeof(int);
285		strcpy(xb, XATTR_NAME_SELINUX);
286		xb += xnamelen;
287		memcpy(xb, secontext, xvalsize);
288	}
289
290	if(t != YAFFS_OBJECT_TYPE_HARDLINK)
291	{
292		oh->yst_mode = s->st_mode;
293		oh->yst_uid = s->st_uid;
294// NCB 12/9/02		oh->yst_gid = s->yst_uid;
295		oh->yst_gid = s->st_gid;
296		oh->yst_atime = s->st_atime;
297		oh->yst_mtime = s->st_mtime;
298		oh->yst_ctime = s->st_ctime;
299		oh->yst_rdev  = s->st_rdev;
300	}
301
302	if(t == YAFFS_OBJECT_TYPE_FILE)
303	{
304		oh->fileSize = s->st_size;
305	}
306
307	if(t == YAFFS_OBJECT_TYPE_HARDLINK)
308	{
309		oh->equivalentObjectId = equivalentObj;
310	}
311
312	if(t == YAFFS_OBJECT_TYPE_SYMLINK)
313	{
314		strncpy(oh->alias,alias,YAFFS_MAX_ALIAS_LENGTH);
315	}
316
317	if (convert_endian)
318	{
319    		object_header_little_to_big_endian(oh);
320	}
321
322	return write_chunk(bytes,objId,0,0xffff);
323
324}
325
326static void fix_stat(const char *path, struct stat *s)
327{
328    uint64_t capabilities;
329    path += source_path_len;
330    fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode, &capabilities);
331}
332
333static int process_directory(int parent, const char *path, int fixstats)
334{
335
336	DIR *dir;
337	struct dirent *entry;
338	char *secontext = NULL;
339
340	nDirectories++;
341
342	dir = opendir(path);
343
344	if(dir)
345	{
346		while((entry = readdir(dir)) != NULL)
347		{
348
349			/* Ignore . and .. */
350			if(strcmp(entry->d_name,".") &&
351			   strcmp(entry->d_name,".."))
352 			{
353 				char full_name[500];
354				char *suffix, dest_name[500];
355				int ret;
356				struct stat stats;
357				int equivalentObj;
358				int newObj;
359
360				sprintf(full_name,"%s/%s",path,entry->d_name);
361
362				lstat(full_name,&stats);
363
364				if (sehnd) {
365					suffix = full_name + seprefixlen;
366					ret = snprintf(dest_name,
367						       sizeof dest_name,
368						       "%s%s", mntpoint,
369						       suffix);
370					if (ret < 0 ||
371					    (size_t) ret >= sizeof dest_name) {
372						fprintf(stderr,
373							"snprintf failed on %s%s\n",
374							mntpoint, suffix);
375						exit(1);
376					}
377
378					char *sepath = NULL;
379					if (dest_name[0] == '/')
380					        sepath = strdup(dest_name);
381					else if (asprintf(&sepath, "/%s", dest_name) < 0)
382                                                sepath = NULL;
383
384					if (!sepath) {
385					        perror("malloc");
386					        exit(1);
387					}
388
389					if (selabel_lookup(sehnd, &secontext,
390							   sepath,
391							   stats.st_mode) < 0) {
392					        perror("selabel_lookup");
393					        free(sepath);
394					        exit(1);
395					}
396					free(sepath);
397				}
398
399				if(S_ISLNK(stats.st_mode) ||
400				    S_ISREG(stats.st_mode) ||
401				    S_ISDIR(stats.st_mode) ||
402				    S_ISFIFO(stats.st_mode) ||
403				    S_ISBLK(stats.st_mode) ||
404				    S_ISCHR(stats.st_mode) ||
405				    S_ISSOCK(stats.st_mode))
406				{
407
408					newObj = obj_id++;
409					nObjects++;
410
411                    if (fixstats) {
412                        fix_stat(full_name, &stats);
413                    }
414
415					//printf("Object %d, %s is a ",newObj,full_name);
416
417					/* We're going to create an object for it */
418					if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0)
419					{
420					 	/* we need to make a hard link */
421					 	//printf("hard link to object %d\n",equivalentObj);
422						error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL, secontext);
423					}
424					else
425					{
426
427						add_obj_to_list(stats.st_dev,stats.st_ino,newObj);
428
429						if(S_ISLNK(stats.st_mode))
430						{
431
432							char symname[500];
433
434							memset(symname,0, sizeof(symname));
435
436							readlink(full_name,symname,sizeof(symname) -1);
437
438							//printf("symlink to \"%s\"\n",symname);
439							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname, secontext);
440
441						}
442						else if(S_ISREG(stats.st_mode))
443						{
444							//printf("file, ");
445							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL, secontext);
446
447							if(error >= 0)
448							{
449								int h;
450								__u8 bytes[chunkSize];
451								int nBytes;
452								int chunk = 0;
453
454								h = open(full_name,O_RDONLY);
455								if(h >= 0)
456								{
457									memset(bytes,0xff,sizeof(bytes));
458									while((nBytes = read(h,bytes,sizeof(bytes))) > 0)
459									{
460										chunk++;
461										write_chunk(bytes,newObj,chunk,nBytes);
462										memset(bytes,0xff,sizeof(bytes));
463									}
464									if(nBytes < 0)
465									   error = nBytes;
466
467									//printf("%d data chunks written\n",chunk);
468								}
469								else
470								{
471									perror("Error opening file");
472								}
473								close(h);
474
475							}
476
477						}
478						else if(S_ISSOCK(stats.st_mode))
479						{
480							//printf("socket\n");
481							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
482						}
483						else if(S_ISFIFO(stats.st_mode))
484						{
485							//printf("fifo\n");
486							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
487						}
488						else if(S_ISCHR(stats.st_mode))
489						{
490							//printf("character device\n");
491							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
492						}
493						else if(S_ISBLK(stats.st_mode))
494						{
495							//printf("block device\n");
496							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
497						}
498						else if(S_ISDIR(stats.st_mode))
499						{
500							//printf("directory\n");
501							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL, secontext);
502// NCB modified 10/9/2001				process_directory(1,full_name);
503							process_directory(newObj,full_name,fixstats);
504						}
505					}
506				}
507				else
508				{
509					//printf(" we don't handle this type\n");
510				}
511			}
512		}
513		closedir(dir);
514	}
515
516	return 0;
517
518}
519
520static void usage(void)
521{
522	fprintf(stderr,"mkyaffs2image: image building tool for YAFFS2 built "__DATE__"\n");
523	fprintf(stderr,"usage: mkyaffs2image [-f] [-c <size>] [-s <size>] dir image_file [file_contexts mountpoint] [convert]\n");
524	fprintf(stderr,"           -f         fix file stat (mods, user, group) for device\n");
525	fprintf(stderr,"           -c <size>  set the chunk (NAND page) size. default: 2048\n");
526	fprintf(stderr,"           -s <size>  set the spare (NAND OOB) size. default: 64\n");
527	fprintf(stderr,"           dir        the directory tree to be converted\n");
528	fprintf(stderr,"           image_file the output file to hold the image\n");
529	fprintf(stderr,"           file_contexts the file contexts configuration used to assign SELinux file context attributes\n");
530	fprintf(stderr,"           mountpoint the directory where this image be mounted on the device\n");
531	fprintf(stderr,"           'convert'  produce a big-endian image from a little-endian machine\n");
532}
533
534int main(int argc, char *argv[])
535{
536	int fixstats = 0;
537	struct stat stats;
538	int opt;
539	char *image;
540	char *dir;
541	char *secontext = NULL;
542
543	while ((opt = getopt(argc, argv, "fc:s:")) != -1) {
544		switch (opt) {
545		case 'f':
546			fixstats = 1;
547			break;
548		case 'c':
549			chunkSize = (unsigned)strtoul(optarg, NULL, 0);
550			break;
551		case 's':
552			spareSize = (unsigned)strtoul(optarg, NULL, 0);
553			break;
554		default:
555			usage();
556			exit(1);
557		}
558	}
559
560	if (!chunkSize || !spareSize) {
561		usage();
562		exit(1);
563	}
564
565	if ((argc - optind < 2) || (argc - optind > 4)) {
566		usage();
567		exit(1);
568	}
569
570	dir = argv[optind];
571	seprefixlen = strlen(dir);
572	image = argv[optind + 1];
573
574	if (optind + 2 < argc) {
575		if (!strncmp(argv[optind + 2], "convert", strlen("convert")))
576			convert_endian = 1;
577		else {
578			struct selinux_opt seopts[] = {
579				{ SELABEL_OPT_PATH, argv[optind + 2] }
580			};
581			sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
582			if (!sehnd) {
583				perror(argv[optind + 2]);
584				usage();
585				exit(1);
586			}
587			if (optind + 3 >= argc) {
588				usage();
589				exit(1);
590			}
591			mntpoint = argv[optind + 3];
592			if (optind + 4 < argc) {
593				if (!strncmp(argv[optind + 4], "convert", strlen("convert")))
594					convert_endian = 1;
595			}
596		}
597	}
598
599	if(stat(dir,&stats) < 0)
600	{
601		fprintf(stderr,"Could not stat %s\n",dir);
602		exit(1);
603	}
604
605	if(!S_ISDIR(stats.st_mode))
606	{
607		fprintf(stderr," %s is not a directory\n",dir);
608		exit(1);
609	}
610
611	outFile = open(image,O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE);
612
613
614	if(outFile < 0)
615	{
616		fprintf(stderr,"Could not open output file %s\n",image);
617		exit(1);
618	}
619
620    if (fixstats) {
621        int len = strlen(dir);
622
623        if((len >= 4) && (!strcmp(dir + len - 4, "data"))) {
624            source_path_len = len - 4;
625        } else if((len >= 6) && (!strcmp(dir + len - 6, "system"))) {
626            source_path_len = len - 6;
627        } else {
628            fprintf(stderr,"Fixstats (-f) option requested but filesystem is not data or android!\n");
629            exit(1);
630        }
631        fix_stat(dir, &stats);
632    }
633
634	//printf("Processing directory %s into image file %s\n",dir,image);
635    if (sehnd) {
636
637        char *sepath = NULL;
638        if (mntpoint[0] == '/')
639	    sepath = strdup(mntpoint);
640        else if (asprintf(&sepath, "/%s", mntpoint) < 0)
641            sepath = NULL;
642
643        if (!sepath) {
644	    perror("malloc");
645	    exit(1);
646	}
647
648	if (selabel_lookup(sehnd, &secontext, sepath, stats.st_mode) < 0) {
649	    perror("selabel_lookup");
650	    free(sepath);
651	    exit(1);
652	}
653
654	free(sepath);
655    }
656
657    error =  write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL, secontext);
658	if(error)
659	error = process_directory(YAFFS_OBJECTID_ROOT,dir,fixstats);
660
661	close(outFile);
662
663	if(error < 0)
664	{
665		perror("operation incomplete");
666		exit(1);
667	}
668	else
669	{
670        /*
671		printf("Operation complete.\n"
672		       "%d objects in %d directories\n"
673		       "%d NAND pages\n",nObjects, nDirectories, nPages);
674        */
675	}
676
677	close(outFile);
678
679	exit(0);
680}
681
682