]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/fs/rom/v2_0/support/mk_romfs.c
Initial revision
[karo-tx-redboot.git] / packages / fs / rom / v2_0 / support / mk_romfs.c
1 //==========================================================================
2 //
3 //      mk_romfs.c
4 //
5 //      Create ROM file system image
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):           richard.panton@3glab.com
44 // Contributors:        richard.panton@3glab.com
45 // Date:                2000-07-25
46 // Purpose:             ROM file system
47 // Description:         This program creates a ROM file system image, suitable
48 //                      for use with the sample ROM file system implemented by
49 //                      this package.
50 //                      * CAUTION! * This is host code and can only be built
51 //                      in a host, e.g. Linux, environment.
52 //
53 //####DESCRIPTIONEND####
54 //==========================================================================
55
56 #include <stdlib.h>
57 #include <string.h>
58 #include <stdio.h>
59 #include <stdarg.h>
60 #include <sys/stat.h>
61 #include <sys/types.h>
62 #include <dirent.h>
63 #include <fcntl.h>
64 #include <unistd.h>
65 #include <errno.h>
66
67 //==========================================================================
68 //
69 // CONFIGURABLE ITEMS HERE
70 //
71 //==========================================================================
72
73 // define LONG to be a four byte unsigned integer on the host
74 #define LONG    unsigned long
75
76 // define SHORT to be a two byte unsigned integer on the host
77 #define SHORT   unsigned short
78
79 // All data files should be aligned to this sized boundary (minimum probably 32)
80 #define DATA_ALIGN      32
81
82 // The data stored in a directory should be aligned to this size boundary
83 #define DIRECTORY_ALIGN 32
84
85 // All executable files should be aligned to this sized boundary (minimum probably 32)
86 #define EXEC_ALIGN      32
87
88 // Undefine this if the host filesystem does not support lstat()
89 #define HAS_LSTAT
90
91 //==========================================================================
92
93 // Return (n) aligned to the next (b) byte boundary
94 #define ALIGN_TO( n, b ) (( (n) + (b)-1 ) & ~((b)-1))
95
96 // Set the stat call to use
97 #ifdef HAS_LSTAT
98 #define get_status( p, b )      lstat( (p), (b) )
99 #else
100 #define get_status( p, b )      stat( (p), (b) )
101 #endif
102
103 // This is how we identify a directory from its mode
104 #define IS_DIRECTORY( m )       (S_ISDIR(m))
105
106 // This is how we identify a data file from its mode
107 #define IS_DATAFILE( m )        (S_ISREG(m) && ((m)&S_IXUSR) == 0 )
108
109 // This is how we identify an executable from its mode
110 #define IS_EXECUTABLE( m )      (S_ISREG(m) && ((m)&S_IXUSR) != 0 )
111
112 // This is how we identify a symbolic link from its mode
113 #define IS_SYMLINK( m )         (S_ISLNK(m))
114
115 #define ROMFS_MAGIC     0x526f6d2e
116
117 //=========================================================================
118 // EXIT CODES
119 #define EXIT_SUCCESS    0
120 #define EXIT_ARGS       1
121 #define EXIT_MALLOC     2
122 #define EXIT_FILESYS    3
123 #define EXIT_WRITE      4
124 #define EXIT_SEEK       5
125 #define EXIT_COMPILE    6
126 #define EXIT_BUG        7
127
128
129
130 // These are the structures we will build into the ROMFS image.
131 // The sizes of these structures should be fixed for all architectures
132 typedef struct romfs_dirent {
133     LONG        node;           // 4
134     LONG        next;           // 8
135     char        name[0];        // 8 + strlen(name) + 1
136 } romfs_dirent;                 // Aligns to next 32 byte boundary
137
138 typedef struct romfs_node {
139     LONG        mode;           // 4
140     LONG        nlink;          // 8
141     SHORT       uid;            // 10
142     SHORT       gid;            // 12
143     LONG        size;           // 16
144     LONG        ctime;          // 20
145     LONG        data_offset;    // 24
146     char        pad[8];         // 32
147 } romfs_node;                   // Next node begins here
148
149 typedef struct romfs_disk {
150     LONG        magic;          // 4
151     LONG        nodecount;      // 8
152     LONG        disksize;       // 12
153     LONG        dev_id;         // 16
154     char        name[16];       // 32
155 } romfs_disk;                   // Nodes start here
156
157 // This is the holding structure for a node
158 typedef struct node {
159     const char *path;           // Filename (inc. path) of a link to this node
160     size_t size;                // Size of file/directory/link
161     mode_t st_mode;             // Type and permissions
162     uid_t uid;                  // Owner id
163     gid_t gid;                  // Group id
164     time_t ctime;               // File creation time
165     int nodenum;                // Nodenumber of this node in the ROMFS image
166     dev_t device;               // Device (for hardlink check)
167     ino_t inode;                // Inode (for hardlink check)
168     int nlink;                  // [DIRECTORIES] Number of sub-directories [FILES] hard links
169     romfs_dirent *entry;        // [DIRECTORIES] Points to an array of directory entries
170     int entry_size;             // Size to be allocated to file (includes alignment bytes)
171     unsigned long offset;       // Offset within ROMFS image of data
172     struct node *sibling;       // Points to the next entry in this directory
173     struct node *child;         // [DIRECTORIES] Points to any subdirectories
174     struct node *next_in_rom;   // Next in ROMFS write order
175     struct node *next_multilink;// Next node that is multilinked
176 } node;
177
178 static int nodes = 0;
179 static char *prog;
180 static int verbose = 1;
181 static int dowrite = 1;
182 static int bigendian = 0;
183 static int hardlinks = 0;
184 static unsigned long coffset = 0;
185 static node * first = NULL;
186 static node ** last_p = &first;
187 static node * first_multilink = NULL;
188 static node ** last_multilink_p = &first_multilink;
189 static int fd = -1;
190
191 #define VERB_NONE       0
192 #define VERB_MINIMUM    1
193 #define VERB_SUB        2
194 #define VERB_MAX        3
195 #define VERB_EXCESSIVE  4
196
197 // Use gcc format argument checking on this function, which cannot return
198 static void fatal_error( int exitcode, const char *fmt, ... ) \
199         __attribute__ (( noreturn,format (printf, 2, 3) ));
200
201 // Use gcc format argument checking on this function
202 static void verb_printf( int level, const char *fmt, ... ) \
203         __attribute__ ((format (printf, 2, 3) ));
204
205 static void fatal_error( int exitcode, const char *fmt, ... ) {
206     va_list v;
207
208     va_start( v, fmt );
209     vfprintf( stderr, fmt, v );
210
211     exit(exitcode);
212 }
213
214 static void verb_printf( int level, const char *fmt, ... ){
215     if ( level <= verbose ) {
216         va_list v;
217         va_start( v,fmt );
218         vprintf(fmt, v);
219     }
220 }
221
222 static void *mymalloc( size_t size ) {
223     void *p = malloc(size);
224     if ( !p ) {
225         fatal_error( EXIT_MALLOC, "Out of memory allocating %d bytes\n", size );
226     }
227     return p;
228 }
229
230 static void myrealloc( void **o, size_t newsize ) {
231     if ( *o == NULL )
232         *o = mymalloc( newsize );
233     else if ( !(*o = realloc( *o, newsize )) ) {
234         fatal_error( EXIT_MALLOC, "Out of memory re-allocating %d bytes\n", newsize );
235     }
236 }
237
238 static void outputlong( unsigned char *b, unsigned long w ) {
239     if ( bigendian ) {
240         b[0] = (w>>24) & 0xff;
241         b[1] = (w>>16) & 0xff;
242         b[2] = (w>> 8) & 0xff;
243         b[3] = (w    ) & 0xff;
244     } else {
245         b[3] = (w>>24) & 0xff;
246         b[2] = (w>>16) & 0xff;
247         b[1] = (w>> 8) & 0xff;
248         b[0] = (w    ) & 0xff;
249     }
250 }
251
252 static void outputshort( unsigned char *b, unsigned short w ) {
253     if ( bigendian ) {
254         b[0] = (w>> 8) & 0xff;
255         b[1] = (w    ) & 0xff;
256     } else {
257         b[1] = (w>> 8) & 0xff;
258         b[0] = (w    ) & 0xff;
259     }
260 }
261
262 static unsigned long ConvertMode( unsigned long posix_mode ) {
263     unsigned long result = 0;
264     if ( S_ISDIR( posix_mode ) ) result |= 1<<0;
265     if ( S_ISCHR( posix_mode ) ) result |= 1<<1;
266     if ( S_ISBLK( posix_mode ) ) result |= 1<<2;
267     if ( S_ISREG( posix_mode ) ) result |= 1<<3;
268     if ( S_ISFIFO(posix_mode ) ) result |= 1<<4;
269     // We cannot create MQ, SEM, or SHM entries here
270     if ( posix_mode & S_IRUSR )  result |= 1<<16;
271     if ( posix_mode & S_IWUSR )  result |= 1<<17;
272     if ( posix_mode & S_IXUSR )  result |= 1<<18;
273     if ( posix_mode & S_IRGRP )  result |= 1<<19;
274     if ( posix_mode & S_IWGRP )  result |= 1<<20;
275     if ( posix_mode & S_IXGRP )  result |= 1<<21;
276     if ( posix_mode & S_IROTH )  result |= 1<<22;
277     if ( posix_mode & S_IWOTH )  result |= 1<<23;
278     if ( posix_mode & S_IXOTH )  result |= 1<<24;
279     if ( posix_mode & S_ISUID )  result |= 1<<25;
280     if ( posix_mode & S_ISGID )  result |= 1<<26;
281     return result;
282 }
283
284 static const char *AddDirEntry( const char *name, node *parent_node, int node_num ) {
285     int this_size = ((strlen(name) + 4 + 4 + 1) + 31) & ~31;
286     int start = parent_node->size;
287     romfs_dirent *g;
288     myrealloc( (void**)&parent_node->entry, (parent_node->size += this_size) );
289     g = (romfs_dirent *)((unsigned char *)parent_node->entry + start);
290     memset( (void*)g, '\0', this_size );
291     outputlong( (char*)&g->node, node_num);
292     outputlong( (char*)&g->next, parent_node->size);
293     strcpy(g->name,name);
294     verb_printf( VERB_MAX, "\t%s --> node %d\n", name, node_num );
295     return (const char *)g->name;
296 }
297
298 extern int errno;
299
300 static node * FindLink( dev_t d, ino_t i ) {
301     // See if the node has been previously included by checking the device/inode
302     // combinations of all known multi-linked nodes
303     node *np = first_multilink;
304
305     for ( ; np ; np = np->next_multilink ) {
306         if ( np->device == d && np->inode == i )
307             return np;
308     }
309     return NULL;
310 }
311
312 static node * GetNodeInfo( const char *path, const char *name, int *hlink ) {
313     char newpath[1024];
314     node *node, *lnode;
315     struct stat stbuff;
316
317     sprintf(newpath,"%s/%s",path,name);
318     if ( (get_status(newpath,&stbuff)) < 0 ) {
319         fatal_error(EXIT_FILESYS, "stat(%s) failed: %s\n", newpath, strerror(errno));
320     }
321     if ( !(stbuff.st_mode & S_IRUSR) ) {
322         fatal_error(EXIT_FILESYS, "\"%s\" is not readable\n", newpath );
323     }
324     if ( hardlinks && S_ISREG( stbuff.st_mode ) && stbuff.st_nlink > 1 ) {
325
326         // See if this node has already been loaded
327         lnode = FindLink( stbuff.st_dev, stbuff.st_ino );
328
329         if ( lnode ) {
330             lnode->nlink++;
331             *hlink = 1;
332             return lnode; // Return the found link instead
333         }
334
335         // Create a new node
336         node = mymalloc( sizeof(struct node) );
337
338         // Incorporate the new link into the 'multi-linked' node list
339         *last_multilink_p = node;
340         last_multilink_p = &node->next_multilink;
341     } else {
342         // Create a new node
343         node = mymalloc( sizeof(struct node) );
344     }
345     node->path = strdup( newpath );
346     // We re-calculate the size for directories
347     node->size = IS_DIRECTORY( stbuff.st_mode ) ? 0 : stbuff.st_size;
348     node->st_mode = stbuff.st_mode;
349     node->uid = stbuff.st_uid;
350     node->gid = stbuff.st_gid;
351     node->ctime = stbuff.st_ctime;
352     node->nodenum = nodes++;
353     node->device = stbuff.st_dev;
354     node->inode = stbuff.st_ino;
355     // We always re-calculate the number of links
356     node->nlink = IS_DIRECTORY( stbuff.st_mode ) ? 2 : 1;
357     node->entry = NULL;
358     node->entry_size = 0;
359     node->offset = 0;
360     node->sibling = NULL;
361     node->child = NULL;
362     node->next_in_rom = NULL;
363     node->next_multilink = NULL;
364     *hlink = 0;
365     return node;
366 }
367
368 static void ScanDirectory(node *mynode, int p_node) {
369
370     DIR *dh;
371     struct dirent *e;
372     node **last_p = &mynode->child;
373     node *th;
374     int was_hardlinked;
375
376     if ( (dh  = opendir( mynode->path )) == NULL ) {
377         perror(mynode->path);
378         return;
379     }
380
381     verb_printf(VERB_EXCESSIVE, "Construct directory '%s'(%d):\n", 
382             mynode->path, mynode->nodenum );
383
384     // Add . & .. here because they MUST be present in the image
385     AddDirEntry( ".", mynode, mynode->nodenum );
386     AddDirEntry( "..", mynode, p_node );
387
388     while ( (e = readdir( dh )) ) {
389         // Ignore . & .. here because they MAY NOT be in the host filesystem
390         if ( strcmp(e->d_name,".") && strcmp(e->d_name,"..") ) {
391             
392
393             th = GetNodeInfo( mynode->path, e->d_name, &was_hardlinked );
394             AddDirEntry( e->d_name, mynode, th->nodenum );
395
396             if ( !was_hardlinked ) {
397                 verb_printf( VERB_EXCESSIVE, "\t\tNew node %d for entry '%s'\n", th->nodenum, e->d_name);
398                 *last_p = th;
399                 last_p = &th->sibling;
400             } else {
401                 verb_printf( VERB_EXCESSIVE, "\t\tRe-used node %d for entry '%s'\n", th->nodenum, e->d_name);
402             }
403         }
404     }
405     closedir( dh );
406     verb_printf(VERB_EXCESSIVE,"Completed '%s'. Checking for child directories...\n", mynode->path);
407
408     for ( th = mynode->child ; th ; th = th->sibling ) {
409         if ( IS_DIRECTORY( th->st_mode ) ) {
410             mynode->nlink++;
411             ScanDirectory( th, mynode->nodenum );
412         }
413     }
414 }
415
416 static void AllocateSpaceToDirectories( node *first ) {
417     node *np;
418
419     for ( np = first ; np ; np = np->sibling ) {
420         if ( IS_DIRECTORY( np->st_mode ) ) {
421             // The first node is a directory. Add its data
422             np->offset = coffset;
423             np->entry_size = ALIGN_TO( np->size, DIRECTORY_ALIGN );
424             coffset += np->entry_size;
425
426             verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", 
427                         np->nodenum, np->offset, np->entry_size );
428
429             // Link this node into the write order chain.
430             // For node 0 (the root), this will overwrite the first pointer with itself
431             *last_p = np;
432             last_p = &np->next_in_rom;
433         }
434     }
435     
436     // Now add any child directories
437     for ( np = first ; np ; np = np->sibling ) {
438         if ( IS_DIRECTORY( np->st_mode ) && np->child )
439             AllocateSpaceToDirectories( np->child );
440     }
441 }
442
443 static void AllocateSpaceToDataFiles( node *first ) {
444     node *np;
445
446     // There are two loops below. It CAN be done in just one, but this re-orders
447     // the file positions in relation to their inode numbers. To keep it simple
448     // to check, allocation takes place in the first loop, recursion in the second
449
450     // Search for child data files
451     for ( np = first->child ; np ; np = np->sibling ) {
452         if ( IS_DATAFILE( np->st_mode ) || IS_SYMLINK( np->st_mode ) ) {
453             np->offset = coffset;
454             np->entry_size = ALIGN_TO( np->size, DATA_ALIGN );
455             coffset += np->entry_size;
456
457             // Link in to the rom write order list
458             *last_p = np;
459             last_p = &np->next_in_rom;
460
461             verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", 
462                         np->nodenum, np->offset, np->entry_size );
463         }
464     }
465
466     // Recurse into sub-directories
467     for ( np = first->child ; np ; np = np->sibling ) {
468         if ( IS_DIRECTORY( np->st_mode ) ) {
469             AllocateSpaceToDataFiles( np );
470         }
471     }
472 }
473
474 static void AllocateSpaceToExecutables( node *first ) {
475     node *np;
476
477     // The first node is a directory. Don't bother with that...
478
479     // Search for child executables
480     for ( np = first->child ; np ; np = np->sibling ) {
481         if ( IS_EXECUTABLE( np->st_mode ) ) {
482             np->offset = coffset;
483             np->entry_size = ALIGN_TO( np->size, EXEC_ALIGN );
484             coffset += np->entry_size;
485
486             // Link in to the rom write order list
487             *last_p = np;
488             last_p = &np->next_in_rom;
489
490             verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n", 
491                         np->nodenum, np->offset, np->entry_size );
492         }
493     }
494
495     // Recurse into sub-directories
496     for ( np = first->child ; np ; np = np->sibling ) {
497         if ( IS_DIRECTORY( np->st_mode ) ) {
498             AllocateSpaceToExecutables( np );
499         }
500     }
501 }
502
503 static void WriteNode( int fd, node *np ) {
504     romfs_node anode;
505     char padhere[9];
506     outputlong( (char*) &anode.mode, ConvertMode( np->st_mode ) );
507     outputlong( (char*) &anode.nlink, np->nlink );
508     outputshort((char*) &anode.uid, np->uid );
509     outputshort((char*) &anode.gid, np->gid );
510     outputlong( (char*) &anode.size, np->size );
511     outputlong( (char*) &anode.ctime, np->ctime );
512     outputlong( (char*) &anode.data_offset, np->offset );
513     sprintf( padhere, "<%6d>", np->nodenum );
514     memcpy( anode.pad, padhere, 8 );
515     if ( dowrite && write( fd, (void*)&anode, sizeof(anode) ) != sizeof(anode) )
516         fatal_error(EXIT_WRITE, "Error writing node %d (%s): %s\n", np->nodenum, np->path, strerror(errno) );
517 }
518
519 static int WriteNodeAndSiblings( int fd, int nodenum, node *first ) {
520     node *np;
521
522     for ( np = first ; np ; np = np->sibling ) {
523         if ( np->nodenum != nodenum++ ) {
524             fatal_error(EXIT_BUG, "BUG: Out of sequence node number; got %d, expected %d\n", np->nodenum, nodenum-1);
525         }
526         WriteNode( fd, np );
527     }
528
529     for ( np = first ; np ; np = np->sibling ) {
530         if ( IS_DIRECTORY( np->st_mode ) && np->child ) {
531             nodenum = WriteNodeAndSiblings( fd, nodenum, np->child );
532         }
533     }
534     return nodenum;
535 }
536
537 static void WriteNodeTable( int fd ) {
538     romfs_disk header;
539     int wnodes;
540
541     outputlong( (char*) &header.magic, ROMFS_MAGIC );
542     outputlong( (char*) &header.nodecount, nodes );
543     outputlong( (char*) &header.disksize, coffset );
544     outputlong( (char*) &header.dev_id, 0x01020304 );
545     strcpy( header.name, "ROMFS v1.0" );
546     if ( dowrite && write( fd, (void*)&header, sizeof(header) ) != sizeof(header) )
547         fatal_error(EXIT_WRITE, "Error writing ROMFS header: %s\n", strerror(errno) );
548
549     if ( (wnodes = WriteNodeAndSiblings( fd, 0, first )) != nodes ) {
550         fatal_error(EXIT_BUG, "BUG: Lost/gained some nodes; wrote %d, expected %d\n", wnodes, nodes );
551     }
552 }
553
554 #ifndef O_BINARY
555 #define O_BINARY 0
556 #endif
557
558 static void WriteData( int fd, node *np ) {
559     char newpath[1024];
560     int ffd;
561     unsigned long todo;
562
563     if ( IS_SYMLINK( np->st_mode ) ) {
564         if ( (ffd = readlink( np->path, newpath, sizeof(newpath) )) < 0 )
565             fatal_error(EXIT_FILESYS, "Error reading symlink \"%s\": %s\n", np->path, strerror(errno) );
566
567         if ( !dowrite ) return;
568
569         if ( lseek( fd, np->offset, SEEK_SET ) != np->offset )
570             fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", np->offset, strerror(errno) );
571
572         if ( write( fd, newpath, ffd ) != ffd )
573             fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) );
574
575         return;
576     }
577     
578     if ( (ffd=open(np->path, O_RDONLY | O_BINARY )) < 0 )
579         fatal_error(EXIT_FILESYS, "Error opening \"%s\": %s\n", np->path, strerror(errno) );
580
581     if ( dowrite && lseek( fd, np->offset, SEEK_SET ) != np->offset )
582         fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", np->offset, strerror(errno) );
583
584     todo = np->size;
585     while ( todo >= 1024 ) {
586         if ( read( ffd, newpath, 1024 ) != 1024 )
587             fatal_error(EXIT_FILESYS, "Error reading file \"%s\" at offset 0x%lX: %s\n", np->path, np->size - todo, strerror(errno) );
588         if ( dowrite && write( fd, newpath, 1024 ) != 1024 )
589             fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) );
590         todo -= 1024;
591     }
592
593     if ( todo ) {
594         if ( read( ffd, newpath, todo ) != todo )
595             fatal_error(EXIT_FILESYS, "Error reading file \"%s\" at offset 0x%lX: %s\n", np->path, np->size - todo, strerror(errno) );
596         if ( dowrite && write( fd, newpath, todo ) != todo )
597             fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) );
598     }
599
600     close(ffd);
601
602 }
603
604 static void WriteDataBlocks( int fd, node *first ) {
605     for ( ; first ; first = first->next_in_rom ) {
606         if ( dowrite && lseek( fd, first->offset, SEEK_SET ) != first->offset )
607             fatal_error(EXIT_SEEK, "Error seeking to offset 0x%lX: %s\n", first->offset, strerror(errno) );
608         if ( IS_DIRECTORY( first->st_mode ) ) {
609             if ( dowrite && write( fd, first->entry, first->size ) != first->size )
610                 fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) );
611         } else {
612             WriteData( fd, first );
613         }
614     }
615 }
616
617 static void usage(void) {
618     fprintf(stderr,"\n%s - Create an eCos ROMFS disk image from the files\n",prog);
619     fprintf(stderr,"%*s   contained under a specified directory\n\n", strlen(prog), "");
620     fprintf(stderr,"Usage: %s [options] <fs_root> <fs_file>\n", prog);
621     fprintf(stderr," fs_root    is the directory containing the files to package into the ROMFS image\n");
622     fprintf(stderr," fs_file    is the name of the ROMFS image file to create\n");
623     fprintf(stderr,"          Options include:\n");
624     fprintf(stderr," -v / -q    increase / decrease verbosity\n");
625     fprintf(stderr," -n         do everything EXCEPT creating the output file\n");
626     fprintf(stderr," -b         write a big-endian image (default is little endian)\n");
627     fprintf(stderr," -l         collapse hard links to a single node\n");
628     fprintf(stderr,"\n");
629     exit(EXIT_ARGS);
630 }
631
632 int main(int ac, char *av[]) {
633     int dummy;
634
635     prog = av[0];
636
637     // Check structure sizes
638     if (sizeof(romfs_node) != 32) {
639         fatal_error(EXIT_COMPILE , "Size of romfs_node is %d, NOT 32\n", sizeof(romfs_node) );
640     } else if (sizeof(romfs_dirent) != 8) {
641         fatal_error(EXIT_COMPILE , "Size of romfs_dirent is %d, NOT 8\n", sizeof(romfs_dirent) );
642     } else if (sizeof(romfs_disk) != 32) {
643         fatal_error(EXIT_COMPILE , "Size of romfs_disk is %d, NOT 32\n", sizeof(romfs_disk) );
644     }
645
646     // Parse option arguments
647     while ( ac > 1 && av[1][0] == '-' ) {
648         char *o = &av[1][1];
649         for ( ; *o ; o++ ) {
650             switch ( *o ) {
651                 case 'q' :
652                     verbose--;
653                     break;
654                 case 'v' :
655                     verbose++;
656                     break;
657                 case 'n' :
658                     dowrite = 0;
659                     break;
660                 case 'b' :
661                     bigendian = 1;
662                     break;
663                 case 'l' :
664                     hardlinks = 1;
665                     break;
666                 default :
667                     fprintf(stderr,"%s: Invalid flag -%c\n", prog, *o );
668                     usage();
669             }
670         }
671         av++; ac--;
672     }
673
674     // Check remaining arguments
675     if ( ac != 3 ) usage();
676
677
678     verb_printf( VERB_MINIMUM, "%s: Verbosity %d %s%s endian\n",
679                 prog, verbose,
680                 dowrite ? "" : "no write, ",
681                 bigendian ? "big" : "little" );
682
683     // Phase 1. Recursively scan the root directory for files and directories.
684     verb_printf(VERB_MINIMUM, "Phase 1  - Build file list\n");
685
686     first = GetNodeInfo( av[1], ".", &dummy );  // Initialize the root node entry.
687     ScanDirectory( first, 0 );
688
689     // Phase 2. Work out space allocations for filesystem
690     verb_printf(VERB_MINIMUM, "Phase 2  - Calculate space allocation\n");
691     coffset = sizeof(romfs_disk) + nodes * sizeof(romfs_node);
692     verb_printf(VERB_MAX,"\t\tnode table : 0x000000 (+0x%05lX) %d nodes\n", coffset, nodes );
693     
694     // Phase 2a. Work out space allocations for the directories of the filesystem
695     verb_printf(VERB_SUB,"Phase 2a -     * Directories\n");
696     coffset = ALIGN_TO( coffset, DIRECTORY_ALIGN );
697     AllocateSpaceToDirectories( first );
698
699     // Phase 2b. Work out space allocations for the data files of the filesystem
700     verb_printf(VERB_SUB,"Phase 2b -     * Regular files\n");
701     coffset = ALIGN_TO( coffset, DATA_ALIGN );
702     AllocateSpaceToDataFiles( first );
703
704     // Phase 2c. Work out space allocations for the executable files of the filesystem
705     verb_printf(VERB_SUB,"Phase 2c -     * Executable files\n");
706     coffset = ALIGN_TO( coffset, EXEC_ALIGN );
707     AllocateSpaceToExecutables( first );
708
709     // Round off the image size...
710     coffset = ALIGN_TO( coffset, EXEC_ALIGN );
711
712     // Phase 3. Write out the image file
713     verb_printf(VERB_MINIMUM, "Phase 3  - Construct ROMFS image file (%ld kb)\n", ALIGN_TO( coffset, 1024 )/1024);
714
715     if ( dowrite ) {
716         if ( (fd = open( av[2], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666 )) < 0 ) {
717             fatal_error(EXIT_WRITE,"Failed to open output file '%s', errno=%d\n", av[2], errno );
718         }
719     } else {
720         verb_printf(VERB_NONE,"           (No image is being written)\n");
721     }
722
723     verb_printf(VERB_SUB,"Phase 3a -     * Node table\n");
724     WriteNodeTable( fd );
725
726     verb_printf(VERB_SUB,"Phase 3b -     * Data blocks\n");
727     WriteDataBlocks( fd, first );
728
729     if ( fd >= 0 ) close(fd);
730
731     verb_printf(VERB_MINIMUM,  "%s completed\n", av[2] );
732
733     return 0;
734 }