1 //==========================================================================
5 // Create ROM file system image
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.
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.
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
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.
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.
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.
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####
43 // Author(s): richard.panton@3glab.com
44 // Contributors: richard.panton@3glab.com
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
50 // * CAUTION! * This is host code and can only be built
51 // in a host, e.g. Linux, environment.
53 //####DESCRIPTIONEND####
54 //==========================================================================
61 #include <sys/types.h>
67 //==========================================================================
69 // CONFIGURABLE ITEMS HERE
71 //==========================================================================
73 // define LONG to be a four byte unsigned integer on the host
74 #define LONG unsigned long
76 // define SHORT to be a two byte unsigned integer on the host
77 #define SHORT unsigned short
79 // All data files should be aligned to this sized boundary (minimum probably 32)
82 // The data stored in a directory should be aligned to this size boundary
83 #define DIRECTORY_ALIGN 32
85 // All executable files should be aligned to this sized boundary (minimum probably 32)
88 // Undefine this if the host filesystem does not support lstat()
91 //==========================================================================
93 // Return (n) aligned to the next (b) byte boundary
94 #define ALIGN_TO( n, b ) (( (n) + (b)-1 ) & ~((b)-1))
96 // Set the stat call to use
98 #define get_status( p, b ) lstat( (p), (b) )
100 #define get_status( p, b ) stat( (p), (b) )
103 // This is how we identify a directory from its mode
104 #define IS_DIRECTORY( m ) (S_ISDIR(m))
106 // This is how we identify a data file from its mode
107 #define IS_DATAFILE( m ) (S_ISREG(m) && ((m)&S_IXUSR) == 0 )
109 // This is how we identify an executable from its mode
110 #define IS_EXECUTABLE( m ) (S_ISREG(m) && ((m)&S_IXUSR) != 0 )
112 // This is how we identify a symbolic link from its mode
113 #define IS_SYMLINK( m ) (S_ISLNK(m))
115 #define ROMFS_MAGIC 0x526f6d2e
117 //=========================================================================
119 #define EXIT_SUCCESS 0
121 #define EXIT_MALLOC 2
122 #define EXIT_FILESYS 3
125 #define EXIT_COMPILE 6
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 {
135 char name[0]; // 8 + strlen(name) + 1
136 } romfs_dirent; // Aligns to next 32 byte boundary
138 typedef struct romfs_node {
145 LONG data_offset; // 24
147 } romfs_node; // Next node begins here
149 typedef struct romfs_disk {
155 } romfs_disk; // Nodes start here
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
178 static int nodes = 0;
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;
192 #define VERB_MINIMUM 1
195 #define VERB_EXCESSIVE 4
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) ));
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) ));
205 static void fatal_error( int exitcode, const char *fmt, ... ) {
209 vfprintf( stderr, fmt, v );
214 static void verb_printf( int level, const char *fmt, ... ){
215 if ( level <= verbose ) {
222 static void *mymalloc( size_t size ) {
223 void *p = malloc(size);
225 fatal_error( EXIT_MALLOC, "Out of memory allocating %d bytes\n", size );
230 static void myrealloc( void **o, size_t newsize ) {
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 );
238 static void outputlong( unsigned char *b, unsigned long w ) {
240 b[0] = (w>>24) & 0xff;
241 b[1] = (w>>16) & 0xff;
242 b[2] = (w>> 8) & 0xff;
245 b[3] = (w>>24) & 0xff;
246 b[2] = (w>>16) & 0xff;
247 b[1] = (w>> 8) & 0xff;
252 static void outputshort( unsigned char *b, unsigned short w ) {
254 b[0] = (w>> 8) & 0xff;
257 b[1] = (w>> 8) & 0xff;
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;
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;
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;
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;
305 for ( ; np ; np = np->next_multilink ) {
306 if ( np->device == d && np->inode == i )
312 static node * GetNodeInfo( const char *path, const char *name, int *hlink ) {
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));
321 if ( !(stbuff.st_mode & S_IRUSR) ) {
322 fatal_error(EXIT_FILESYS, "\"%s\" is not readable\n", newpath );
324 if ( hardlinks && S_ISREG( stbuff.st_mode ) && stbuff.st_nlink > 1 ) {
326 // See if this node has already been loaded
327 lnode = FindLink( stbuff.st_dev, stbuff.st_ino );
332 return lnode; // Return the found link instead
336 node = mymalloc( sizeof(struct node) );
338 // Incorporate the new link into the 'multi-linked' node list
339 *last_multilink_p = node;
340 last_multilink_p = &node->next_multilink;
343 node = mymalloc( sizeof(struct node) );
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;
358 node->entry_size = 0;
360 node->sibling = NULL;
362 node->next_in_rom = NULL;
363 node->next_multilink = NULL;
368 static void ScanDirectory(node *mynode, int p_node) {
372 node **last_p = &mynode->child;
376 if ( (dh = opendir( mynode->path )) == NULL ) {
377 perror(mynode->path);
381 verb_printf(VERB_EXCESSIVE, "Construct directory '%s'(%d):\n",
382 mynode->path, mynode->nodenum );
384 // Add . & .. here because they MUST be present in the image
385 AddDirEntry( ".", mynode, mynode->nodenum );
386 AddDirEntry( "..", mynode, p_node );
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,"..") ) {
393 th = GetNodeInfo( mynode->path, e->d_name, &was_hardlinked );
394 AddDirEntry( e->d_name, mynode, th->nodenum );
396 if ( !was_hardlinked ) {
397 verb_printf( VERB_EXCESSIVE, "\t\tNew node %d for entry '%s'\n", th->nodenum, e->d_name);
399 last_p = &th->sibling;
401 verb_printf( VERB_EXCESSIVE, "\t\tRe-used node %d for entry '%s'\n", th->nodenum, e->d_name);
406 verb_printf(VERB_EXCESSIVE,"Completed '%s'. Checking for child directories...\n", mynode->path);
408 for ( th = mynode->child ; th ; th = th->sibling ) {
409 if ( IS_DIRECTORY( th->st_mode ) ) {
411 ScanDirectory( th, mynode->nodenum );
416 static void AllocateSpaceToDirectories( node *first ) {
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;
426 verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n",
427 np->nodenum, np->offset, np->entry_size );
429 // Link this node into the write order chain.
430 // For node 0 (the root), this will overwrite the first pointer with itself
432 last_p = &np->next_in_rom;
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 );
443 static void AllocateSpaceToDataFiles( node *first ) {
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
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;
457 // Link in to the rom write order list
459 last_p = &np->next_in_rom;
461 verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n",
462 np->nodenum, np->offset, np->entry_size );
466 // Recurse into sub-directories
467 for ( np = first->child ; np ; np = np->sibling ) {
468 if ( IS_DIRECTORY( np->st_mode ) ) {
469 AllocateSpaceToDataFiles( np );
474 static void AllocateSpaceToExecutables( node *first ) {
477 // The first node is a directory. Don't bother with that...
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;
486 // Link in to the rom write order list
488 last_p = &np->next_in_rom;
490 verb_printf( VERB_MAX, "\t\tnode %5d : 0x%06lX (+0x%05X)\n",
491 np->nodenum, np->offset, np->entry_size );
495 // Recurse into sub-directories
496 for ( np = first->child ; np ; np = np->sibling ) {
497 if ( IS_DIRECTORY( np->st_mode ) ) {
498 AllocateSpaceToExecutables( np );
503 static void WriteNode( int fd, node *np ) {
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) );
519 static int WriteNodeAndSiblings( int fd, int nodenum, node *first ) {
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);
529 for ( np = first ; np ; np = np->sibling ) {
530 if ( IS_DIRECTORY( np->st_mode ) && np->child ) {
531 nodenum = WriteNodeAndSiblings( fd, nodenum, np->child );
537 static void WriteNodeTable( int fd ) {
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) );
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 );
558 static void WriteData( int fd, node *np ) {
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) );
567 if ( !dowrite ) return;
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) );
572 if ( write( fd, newpath, ffd ) != ffd )
573 fatal_error(EXIT_WRITE, "Write error: %s\n", strerror(errno) );
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) );
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) );
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) );
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) );
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) );
612 WriteData( fd, first );
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");
632 int main(int ac, char *av[]) {
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) );
646 // Parse option arguments
647 while ( ac > 1 && av[1][0] == '-' ) {
667 fprintf(stderr,"%s: Invalid flag -%c\n", prog, *o );
674 // Check remaining arguments
675 if ( ac != 3 ) usage();
678 verb_printf( VERB_MINIMUM, "%s: Verbosity %d %s%s endian\n",
680 dowrite ? "" : "no write, ",
681 bigendian ? "big" : "little" );
683 // Phase 1. Recursively scan the root directory for files and directories.
684 verb_printf(VERB_MINIMUM, "Phase 1 - Build file list\n");
686 first = GetNodeInfo( av[1], ".", &dummy ); // Initialize the root node entry.
687 ScanDirectory( first, 0 );
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 );
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 );
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 );
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 );
709 // Round off the image size...
710 coffset = ALIGN_TO( coffset, EXEC_ALIGN );
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);
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 );
720 verb_printf(VERB_NONE," (No image is being written)\n");
723 verb_printf(VERB_SUB,"Phase 3a - * Node table\n");
724 WriteNodeTable( fd );
726 verb_printf(VERB_SUB,"Phase 3b - * Data blocks\n");
727 WriteDataBlocks( fd, first );
729 if ( fd >= 0 ) close(fd);
731 verb_printf(VERB_MINIMUM, "%s completed\n", av[2] );