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