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