Home | History | Annotate | Download | only in utils
      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 (at) 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 
     40 static struct selabel_handle *sehnd;
     41 static unsigned int seprefixlen;
     42 static 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 
     53 unsigned source_path_len = 0;
     54 unsigned yaffs_traceMask=0;
     55 
     56 #define MAX_OBJECTS 50000
     57 
     58 unsigned chunkSize = 2048;
     59 unsigned spareSize = 64;
     60 
     61 const char * mkyaffsimage_c_version = "$Id: mkyaffs2image.c,v 1.2 2005/12/13 00:34:58 tpoynor Exp $";
     62 
     63 
     64 typedef struct
     65 {
     66 	dev_t dev;
     67 	ino_t ino;
     68 	int   obj;
     69 } objItem;
     70 
     71 
     72 static objItem obj_list[MAX_OBJECTS];
     73 static int n_obj = 0;
     74 static int obj_id = YAFFS_NOBJECT_BUCKETS + 1;
     75 
     76 static int nObjects, nDirectories, nPages;
     77 
     78 static int outFile;
     79 
     80 static int error;
     81 
     82 #ifdef HAVE_BIG_ENDIAN
     83 static int convert_endian = 1;
     84 #elif defined(HAVE_LITTLE_ENDIAN)
     85 static int convert_endian = 0;
     86 #endif
     87 
     88 static 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 
    104 static 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 
    124 static 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.
    153 static 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  */
    214 static 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 
    222 static 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 
    260 static 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 
    332 static 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 
    338 static 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 
    386 					char *sepath = NULL;
    387 					if (dest_name[0] == '/')
    388 					        sepath = strdup(dest_name);
    389 					else if (asprintf(&sepath, "/%s", dest_name) < 0)
    390                                                 sepath = NULL;
    391 
    392 					if (!sepath) {
    393 					        perror("malloc");
    394 					        exit(1);
    395 					}
    396 
    397 					if (selabel_lookup(sehnd, &secontext,
    398 							   sepath,
    399 							   stats.st_mode) < 0) {
    400 					        perror("selabel_lookup");
    401 					        free(sepath);
    402 					        exit(1);
    403 					}
    404 					free(sepath);
    405 				}
    406 #endif
    407 
    408 				if(S_ISLNK(stats.st_mode) ||
    409 				    S_ISREG(stats.st_mode) ||
    410 				    S_ISDIR(stats.st_mode) ||
    411 				    S_ISFIFO(stats.st_mode) ||
    412 				    S_ISBLK(stats.st_mode) ||
    413 				    S_ISCHR(stats.st_mode) ||
    414 				    S_ISSOCK(stats.st_mode))
    415 				{
    416 
    417 					newObj = obj_id++;
    418 					nObjects++;
    419 
    420                     if (fixstats) {
    421                         fix_stat(full_name, &stats);
    422                     }
    423 
    424 					//printf("Object %d, %s is a ",newObj,full_name);
    425 
    426 					/* We're going to create an object for it */
    427 					if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0)
    428 					{
    429 					 	/* we need to make a hard link */
    430 					 	//printf("hard link to object %d\n",equivalentObj);
    431 						error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL, secontext);
    432 					}
    433 					else
    434 					{
    435 
    436 						add_obj_to_list(stats.st_dev,stats.st_ino,newObj);
    437 
    438 						if(S_ISLNK(stats.st_mode))
    439 						{
    440 
    441 							char symname[500];
    442 
    443 							memset(symname,0, sizeof(symname));
    444 
    445 							readlink(full_name,symname,sizeof(symname) -1);
    446 
    447 							//printf("symlink to \"%s\"\n",symname);
    448 							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname, secontext);
    449 
    450 						}
    451 						else if(S_ISREG(stats.st_mode))
    452 						{
    453 							//printf("file, ");
    454 							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL, secontext);
    455 
    456 							if(error >= 0)
    457 							{
    458 								int h;
    459 								__u8 bytes[chunkSize];
    460 								int nBytes;
    461 								int chunk = 0;
    462 
    463 								h = open(full_name,O_RDONLY);
    464 								if(h >= 0)
    465 								{
    466 									memset(bytes,0xff,sizeof(bytes));
    467 									while((nBytes = read(h,bytes,sizeof(bytes))) > 0)
    468 									{
    469 										chunk++;
    470 										write_chunk(bytes,newObj,chunk,nBytes);
    471 										memset(bytes,0xff,sizeof(bytes));
    472 									}
    473 									if(nBytes < 0)
    474 									   error = nBytes;
    475 
    476 									//printf("%d data chunks written\n",chunk);
    477 								}
    478 								else
    479 								{
    480 									perror("Error opening file");
    481 								}
    482 								close(h);
    483 
    484 							}
    485 
    486 						}
    487 						else if(S_ISSOCK(stats.st_mode))
    488 						{
    489 							//printf("socket\n");
    490 							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
    491 						}
    492 						else if(S_ISFIFO(stats.st_mode))
    493 						{
    494 							//printf("fifo\n");
    495 							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
    496 						}
    497 						else if(S_ISCHR(stats.st_mode))
    498 						{
    499 							//printf("character device\n");
    500 							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
    501 						}
    502 						else if(S_ISBLK(stats.st_mode))
    503 						{
    504 							//printf("block device\n");
    505 							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
    506 						}
    507 						else if(S_ISDIR(stats.st_mode))
    508 						{
    509 							//printf("directory\n");
    510 							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL, secontext);
    511 // NCB modified 10/9/2001				process_directory(1,full_name);
    512 							process_directory(newObj,full_name,fixstats);
    513 						}
    514 					}
    515 				}
    516 				else
    517 				{
    518 					//printf(" we don't handle this type\n");
    519 				}
    520 			}
    521 		}
    522 		closedir(dir);
    523 	}
    524 
    525 	return 0;
    526 
    527 }
    528 
    529 static void usage(void)
    530 {
    531 	fprintf(stderr,"mkyaffs2image: image building tool for YAFFS2 built "__DATE__"\n");
    532 	fprintf(stderr,"usage: mkyaffs2image [-f] [-c <size>] [-s <size>] dir image_file [file_contexts mountpoint] [convert]\n");
    533 	fprintf(stderr,"           -f         fix file stat (mods, user, group) for device\n");
    534 	fprintf(stderr,"           -c <size>  set the chunk (NAND page) size. default: 2048\n");
    535 	fprintf(stderr,"           -s <size>  set the spare (NAND OOB) size. default: 64\n");
    536 	fprintf(stderr,"           dir        the directory tree to be converted\n");
    537 	fprintf(stderr,"           image_file the output file to hold the image\n");
    538 	fprintf(stderr,"           file_contexts the file contexts configuration used to assign SELinux file context attributes\n");
    539 	fprintf(stderr,"           mountpoint the directory where this image be mounted on the device\n");
    540 	fprintf(stderr,"           'convert'  produce a big-endian image from a little-endian machine\n");
    541 }
    542 
    543 int main(int argc, char *argv[])
    544 {
    545 	int fixstats = 0;
    546 	struct stat stats;
    547 	int opt;
    548 	char *image;
    549 	char *dir;
    550 	char *secontext = NULL;
    551 
    552 	while ((opt = getopt(argc, argv, "fc:s:")) != -1) {
    553 		switch (opt) {
    554 		case 'f':
    555 			fixstats = 1;
    556 			break;
    557 		case 'c':
    558 			chunkSize = (unsigned)strtoul(optarg, NULL, 0);
    559 			break;
    560 		case 's':
    561 			spareSize = (unsigned)strtoul(optarg, NULL, 0);
    562 			break;
    563 		default:
    564 			usage();
    565 			exit(1);
    566 		}
    567 	}
    568 
    569 	if (!chunkSize || !spareSize) {
    570 		usage();
    571 		exit(1);
    572 	}
    573 
    574 	if ((argc - optind < 2) || (argc - optind > 4)) {
    575 		usage();
    576 		exit(1);
    577 	}
    578 
    579 	dir = argv[optind];
    580 #ifdef HAVE_SELINUX
    581 	seprefixlen = strlen(dir);
    582 #endif
    583 	image = argv[optind + 1];
    584 
    585 	if (optind + 2 < argc) {
    586 		if (!strncmp(argv[optind + 2], "convert", strlen("convert")))
    587 			convert_endian = 1;
    588 		else {
    589 #ifdef HAVE_SELINUX
    590 			struct selinux_opt seopts[] = {
    591 				{ SELABEL_OPT_PATH, argv[optind + 2] }
    592 			};
    593 			sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
    594 			if (!sehnd) {
    595 				perror(argv[optind + 2]);
    596 				usage();
    597 				exit(1);
    598 			}
    599 			if (optind + 3 >= argc) {
    600 				usage();
    601 				exit(1);
    602 			}
    603 			mntpoint = argv[optind + 3];
    604 			if (optind + 4 < argc) {
    605 				if (!strncmp(argv[optind + 4], "convert", strlen("convert")))
    606 					convert_endian = 1;
    607 			}
    608 #else
    609 			usage();
    610 			exit(1);
    611 #endif
    612 		}
    613 	}
    614 
    615 	if(stat(dir,&stats) < 0)
    616 	{
    617 		fprintf(stderr,"Could not stat %s\n",dir);
    618 		exit(1);
    619 	}
    620 
    621 	if(!S_ISDIR(stats.st_mode))
    622 	{
    623 		fprintf(stderr," %s is not a directory\n",dir);
    624 		exit(1);
    625 	}
    626 
    627 	outFile = open(image,O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE);
    628 
    629 
    630 	if(outFile < 0)
    631 	{
    632 		fprintf(stderr,"Could not open output file %s\n",image);
    633 		exit(1);
    634 	}
    635 
    636     if (fixstats) {
    637         int len = strlen(dir);
    638 
    639         if((len >= 4) && (!strcmp(dir + len - 4, "data"))) {
    640             source_path_len = len - 4;
    641         } else if((len >= 6) && (!strcmp(dir + len - 6, "system"))) {
    642             source_path_len = len - 6;
    643         } else {
    644             fprintf(stderr,"Fixstats (-f) option requested but filesystem is not data or android!\n");
    645             exit(1);
    646         }
    647         fix_stat(dir, &stats);
    648     }
    649 
    650 	//printf("Processing directory %s into image file %s\n",dir,image);
    651 #ifdef HAVE_SELINUX
    652     if (sehnd) {
    653 
    654         char *sepath = NULL;
    655         if (mntpoint[0] == '/')
    656 	    sepath = strdup(mntpoint);
    657         else if (asprintf(&sepath, "/%s", mntpoint) < 0)
    658             sepath = NULL;
    659 
    660         if (!sepath) {
    661 	    perror("malloc");
    662 	    exit(1);
    663 	}
    664 
    665 	if (selabel_lookup(sehnd, &secontext, sepath, stats.st_mode) < 0) {
    666 	    perror("selabel_lookup");
    667 	    free(sepath);
    668 	    exit(1);
    669 	}
    670 
    671 	free(sepath);
    672     }
    673 #endif
    674 
    675     error =  write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL, secontext);
    676 	if(error)
    677 	error = process_directory(YAFFS_OBJECTID_ROOT,dir,fixstats);
    678 
    679 	close(outFile);
    680 
    681 	if(error < 0)
    682 	{
    683 		perror("operation incomplete");
    684 		exit(1);
    685 	}
    686 	else
    687 	{
    688         /*
    689 		printf("Operation complete.\n"
    690 		       "%d objects in %d directories\n"
    691 		       "%d NAND pages\n",nObjects, nDirectories, nPages);
    692         */
    693 	}
    694 
    695 	close(outFile);
    696 
    697 	exit(0);
    698 }
    699 
    700