• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 
obj_compare(const void * a,const void * b)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 
add_obj_to_list(dev_t dev,ino_t ino,int obj)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 
find_obj_in_list(dev_t dev,ino_t ino)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.
object_header_little_to_big_endian(yaffs_ObjectHeader * oh)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  */
little_to_big_endian(yaffs_PackedTags2 * pt)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 
write_chunk(__u8 * data,__u32 objId,__u32 chunkId,__u32 nBytes)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 
write_object_header(int objId,yaffs_ObjectType t,struct stat * s,int parent,const char * name,int equivalentObj,const char * alias,const char * secontext)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 
fix_stat(const char * path,struct stat * s)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 
process_directory(int parent,const char * path,int fixstats)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 
usage(void)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 
main(int argc,char * argv[])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