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 #define XATTR_NAME_SELINUX "security.selinux"
     36 #include <selinux/selinux.h>
     37 #include <selinux/label.h>
     38 
     39 static struct selabel_handle *sehnd;
     40 static unsigned int seprefixlen;
     41 static 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 
     51 unsigned source_path_len = 0;
     52 unsigned yaffs_traceMask=0;
     53 
     54 #define MAX_OBJECTS 50000
     55 
     56 unsigned chunkSize = 2048;
     57 unsigned spareSize = 64;
     58 
     59 const char * mkyaffsimage_c_version = "$Id: mkyaffs2image.c,v 1.2 2005/12/13 00:34:58 tpoynor Exp $";
     60 
     61 
     62 typedef struct
     63 {
     64 	dev_t dev;
     65 	ino_t ino;
     66 	int   obj;
     67 } objItem;
     68 
     69 
     70 static objItem obj_list[MAX_OBJECTS];
     71 static int n_obj = 0;
     72 static int obj_id = YAFFS_NOBJECT_BUCKETS + 1;
     73 
     74 static int nObjects, nDirectories, nPages;
     75 
     76 static int outFile;
     77 
     78 static int error;
     79 
     80 #ifdef HAVE_BIG_ENDIAN
     81 static int convert_endian = 1;
     82 #elif defined(HAVE_LITTLE_ENDIAN)
     83 static int convert_endian = 0;
     84 #endif
     85 
     86 static 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 
    102 static 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 
    122 static 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.
    151 static 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  */
    212 static 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 
    220 static 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 
    258 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)
    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 
    326 static 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 
    333 static 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 
    520 static 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 
    534 int 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