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