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