2005-04-17 02:20:36 +04:00
# include <stdio.h>
# include <stdlib.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <string.h>
# include <unistd.h>
# include <time.h>
# include <fcntl.h>
# include <errno.h>
# include <ctype.h>
# include <limits.h>
/*
* Original work by Jeff Garzik
*
* External file lists , symlink , pipe and fifo support by Thayne Harbaugh
2007-02-10 12:44:45 +03:00
* Hard link support by Luciano Rocha
2005-04-17 02:20:36 +04:00
*/
# define xstr(s) #s
# define str(s) xstr(s)
static unsigned int offset ;
static unsigned int ino = 721 ;
2011-04-01 01:16:42 +04:00
static time_t default_mtime ;
2005-04-17 02:20:36 +04:00
struct file_handler {
const char * type ;
int ( * handler ) ( const char * line ) ;
} ;
static void push_string ( const char * name )
{
unsigned int name_len = strlen ( name ) + 1 ;
fputs ( name , stdout ) ;
putchar ( 0 ) ;
offset + = name_len ;
}
static void push_pad ( void )
{
while ( offset & 3 ) {
putchar ( 0 ) ;
offset + + ;
}
}
static void push_rest ( const char * name )
{
unsigned int name_len = strlen ( name ) + 1 ;
unsigned int tmp_ofs ;
fputs ( name , stdout ) ;
putchar ( 0 ) ;
offset + = name_len ;
tmp_ofs = name_len + 110 ;
while ( tmp_ofs & 3 ) {
putchar ( 0 ) ;
offset + + ;
tmp_ofs + + ;
}
}
static void push_hdr ( const char * s )
{
fputs ( s , stdout ) ;
offset + = 110 ;
}
static void cpio_trailer ( void )
{
char s [ 256 ] ;
const char name [ ] = " TRAILER!!! " ;
sprintf ( s , " %s%08X%08X%08lX%08lX%08X%08lX "
" %08X%08X%08X%08X%08X%08X%08X " ,
" 070701 " , /* magic */
0 , /* ino */
0 , /* mode */
( long ) 0 , /* uid */
( long ) 0 , /* gid */
1 , /* nlink */
( long ) 0 , /* mtime */
0 , /* filesize */
0 , /* major */
0 , /* minor */
0 , /* rmajor */
0 , /* rminor */
( unsigned ) strlen ( name ) + 1 , /* namesize */
0 ) ; /* chksum */
push_hdr ( s ) ;
push_rest ( name ) ;
while ( offset % 512 ) {
putchar ( 0 ) ;
offset + + ;
}
}
static int cpio_mkslink ( const char * name , const char * target ,
unsigned int mode , uid_t uid , gid_t gid )
{
char s [ 256 ] ;
2010-10-06 11:13:53 +04:00
if ( name [ 0 ] = = ' / ' )
name + + ;
2005-04-17 02:20:36 +04:00
sprintf ( s , " %s%08X%08X%08lX%08lX%08X%08lX "
" %08X%08X%08X%08X%08X%08X%08X " ,
" 070701 " , /* magic */
ino + + , /* ino */
S_IFLNK | mode , /* mode */
( long ) uid , /* uid */
( long ) gid , /* gid */
1 , /* nlink */
2011-04-01 01:16:42 +04:00
( long ) default_mtime , /* mtime */
2005-04-17 02:20:36 +04:00
( unsigned ) strlen ( target ) + 1 , /* filesize */
3 , /* major */
1 , /* minor */
0 , /* rmajor */
0 , /* rminor */
( unsigned ) strlen ( name ) + 1 , /* namesize */
0 ) ; /* chksum */
push_hdr ( s ) ;
push_string ( name ) ;
push_pad ( ) ;
push_string ( target ) ;
push_pad ( ) ;
return 0 ;
}
static int cpio_mkslink_line ( const char * line )
{
char name [ PATH_MAX + 1 ] ;
char target [ PATH_MAX + 1 ] ;
unsigned int mode ;
int uid ;
int gid ;
int rc = - 1 ;
if ( 5 ! = sscanf ( line , " % " str ( PATH_MAX ) " s % " str ( PATH_MAX ) " s %o %d %d " , name , target , & mode , & uid , & gid ) ) {
fprintf ( stderr , " Unrecognized dir format '%s' " , line ) ;
goto fail ;
}
rc = cpio_mkslink ( name , target , mode , uid , gid ) ;
fail :
return rc ;
}
static int cpio_mkgeneric ( const char * name , unsigned int mode ,
uid_t uid , gid_t gid )
{
char s [ 256 ] ;
2010-10-06 11:13:53 +04:00
if ( name [ 0 ] = = ' / ' )
name + + ;
2005-04-17 02:20:36 +04:00
sprintf ( s , " %s%08X%08X%08lX%08lX%08X%08lX "
" %08X%08X%08X%08X%08X%08X%08X " ,
" 070701 " , /* magic */
ino + + , /* ino */
mode , /* mode */
( long ) uid , /* uid */
( long ) gid , /* gid */
2 , /* nlink */
2011-04-01 01:16:42 +04:00
( long ) default_mtime , /* mtime */
2005-04-17 02:20:36 +04:00
0 , /* filesize */
3 , /* major */
1 , /* minor */
0 , /* rmajor */
0 , /* rminor */
( unsigned ) strlen ( name ) + 1 , /* namesize */
0 ) ; /* chksum */
push_hdr ( s ) ;
push_rest ( name ) ;
return 0 ;
}
enum generic_types {
GT_DIR ,
GT_PIPE ,
GT_SOCK
} ;
struct generic_type {
const char * type ;
mode_t mode ;
} ;
static struct generic_type generic_type_table [ ] = {
[ GT_DIR ] = {
. type = " dir " ,
. mode = S_IFDIR
} ,
[ GT_PIPE ] = {
. type = " pipe " ,
. mode = S_IFIFO
} ,
[ GT_SOCK ] = {
. type = " sock " ,
. mode = S_IFSOCK
}
} ;
static int cpio_mkgeneric_line ( const char * line , enum generic_types gt )
{
char name [ PATH_MAX + 1 ] ;
unsigned int mode ;
int uid ;
int gid ;
int rc = - 1 ;
if ( 4 ! = sscanf ( line , " % " str ( PATH_MAX ) " s %o %d %d " , name , & mode , & uid , & gid ) ) {
fprintf ( stderr , " Unrecognized %s format '%s' " ,
line , generic_type_table [ gt ] . type ) ;
goto fail ;
}
mode | = generic_type_table [ gt ] . mode ;
rc = cpio_mkgeneric ( name , mode , uid , gid ) ;
fail :
return rc ;
}
static int cpio_mkdir_line ( const char * line )
{
return cpio_mkgeneric_line ( line , GT_DIR ) ;
}
static int cpio_mkpipe_line ( const char * line )
{
return cpio_mkgeneric_line ( line , GT_PIPE ) ;
}
static int cpio_mksock_line ( const char * line )
{
return cpio_mkgeneric_line ( line , GT_SOCK ) ;
}
static int cpio_mknod ( const char * name , unsigned int mode ,
uid_t uid , gid_t gid , char dev_type ,
unsigned int maj , unsigned int min )
{
char s [ 256 ] ;
if ( dev_type = = ' b ' )
mode | = S_IFBLK ;
else
mode | = S_IFCHR ;
2010-10-06 11:13:53 +04:00
if ( name [ 0 ] = = ' / ' )
name + + ;
2005-04-17 02:20:36 +04:00
sprintf ( s , " %s%08X%08X%08lX%08lX%08X%08lX "
" %08X%08X%08X%08X%08X%08X%08X " ,
" 070701 " , /* magic */
ino + + , /* ino */
mode , /* mode */
( long ) uid , /* uid */
( long ) gid , /* gid */
1 , /* nlink */
2011-04-01 01:16:42 +04:00
( long ) default_mtime , /* mtime */
2005-04-17 02:20:36 +04:00
0 , /* filesize */
3 , /* major */
1 , /* minor */
maj , /* rmajor */
min , /* rminor */
( unsigned ) strlen ( name ) + 1 , /* namesize */
0 ) ; /* chksum */
push_hdr ( s ) ;
push_rest ( name ) ;
return 0 ;
}
static int cpio_mknod_line ( const char * line )
{
char name [ PATH_MAX + 1 ] ;
unsigned int mode ;
int uid ;
int gid ;
char dev_type ;
unsigned int maj ;
unsigned int min ;
int rc = - 1 ;
if ( 7 ! = sscanf ( line , " % " str ( PATH_MAX ) " s %o %d %d %c %u %u " ,
name , & mode , & uid , & gid , & dev_type , & maj , & min ) ) {
fprintf ( stderr , " Unrecognized nod format '%s' " , line ) ;
goto fail ;
}
rc = cpio_mknod ( name , mode , uid , gid , dev_type , maj , min ) ;
fail :
return rc ;
}
static int cpio_mkfile ( const char * name , const char * location ,
2007-02-10 12:44:45 +03:00
unsigned int mode , uid_t uid , gid_t gid ,
unsigned int nlinks )
2005-04-17 02:20:36 +04:00
{
char s [ 256 ] ;
char * filebuf = NULL ;
struct stat buf ;
2007-02-10 12:44:45 +03:00
long size ;
2005-04-17 02:20:36 +04:00
int file = - 1 ;
int retval ;
int rc = - 1 ;
2007-02-10 12:44:45 +03:00
int namesize ;
2012-10-26 00:38:14 +04:00
unsigned int i ;
2005-04-17 02:20:36 +04:00
mode | = S_IFREG ;
file = open ( location , O_RDONLY ) ;
if ( file < 0 ) {
fprintf ( stderr , " File %s could not be opened for reading \n " , location ) ;
goto error ;
}
2011-01-06 01:49:53 +03:00
retval = fstat ( file , & buf ) ;
2010-12-24 23:28:56 +03:00
if ( retval ) {
2011-01-06 01:49:53 +03:00
fprintf ( stderr , " File %s could not be stat()'ed \n " , location ) ;
2010-12-24 23:28:56 +03:00
goto error ;
}
2005-04-17 02:20:36 +04:00
filebuf = malloc ( buf . st_size ) ;
if ( ! filebuf ) {
fprintf ( stderr , " out of memory \n " ) ;
goto error ;
}
retval = read ( file , filebuf , buf . st_size ) ;
if ( retval < 0 ) {
fprintf ( stderr , " Can not read %s file \n " , location ) ;
goto error ;
}
2007-02-10 12:44:45 +03:00
size = 0 ;
for ( i = 1 ; i < = nlinks ; i + + ) {
/* data goes on last link */
if ( i = = nlinks ) size = buf . st_size ;
2010-10-06 11:13:53 +04:00
if ( name [ 0 ] = = ' / ' )
name + + ;
2007-02-10 12:44:45 +03:00
namesize = strlen ( name ) + 1 ;
sprintf ( s , " %s%08X%08X%08lX%08lX%08X%08lX "
" %08lX%08X%08X%08X%08X%08X%08X " ,
" 070701 " , /* magic */
ino , /* ino */
mode , /* mode */
( long ) uid , /* uid */
( long ) gid , /* gid */
nlinks , /* nlink */
( long ) buf . st_mtime , /* mtime */
size , /* filesize */
3 , /* major */
1 , /* minor */
0 , /* rmajor */
0 , /* rminor */
namesize , /* namesize */
0 ) ; /* chksum */
push_hdr ( s ) ;
push_string ( name ) ;
push_pad ( ) ;
if ( size ) {
2009-12-09 14:55:19 +03:00
if ( fwrite ( filebuf , size , 1 , stdout ) ! = 1 ) {
fprintf ( stderr , " writing filebuf failed \n " ) ;
goto error ;
}
2007-02-10 12:44:45 +03:00
offset + = size ;
push_pad ( ) ;
}
name + = namesize ;
}
ino + + ;
2005-04-17 02:20:36 +04:00
rc = 0 ;
error :
if ( filebuf ) free ( filebuf ) ;
if ( file > = 0 ) close ( file ) ;
return rc ;
}
2008-10-29 16:54:17 +03:00
static char * cpio_replace_env ( char * new_location )
{
2012-10-26 00:38:14 +04:00
char expanded [ PATH_MAX + 1 ] ;
2013-11-13 03:08:41 +04:00
char * start , * end , * var ;
while ( ( start = strstr ( new_location , " ${ " ) ) & &
( end = strchr ( start + 2 , ' } ' ) ) ) {
* start = * end = 0 ;
var = getenv ( start + 2 ) ;
snprintf ( expanded , sizeof expanded , " %s%s%s " ,
new_location , var ? var : " " , end + 1 ) ;
strcpy ( new_location , expanded ) ;
2012-10-26 00:38:14 +04:00
}
return new_location ;
2008-10-29 16:54:17 +03:00
}
2005-04-17 02:20:36 +04:00
static int cpio_mkfile_line ( const char * line )
{
char name [ PATH_MAX + 1 ] ;
2007-02-10 12:44:45 +03:00
char * dname = NULL ; /* malloc'ed buffer for hard links */
2005-04-17 02:20:36 +04:00
char location [ PATH_MAX + 1 ] ;
unsigned int mode ;
int uid ;
int gid ;
2007-02-10 12:44:45 +03:00
int nlinks = 1 ;
int end = 0 , dname_len = 0 ;
2005-04-17 02:20:36 +04:00
int rc = - 1 ;
2007-02-10 12:44:45 +03:00
if ( 5 > sscanf ( line , " % " str ( PATH_MAX ) " s % " str ( PATH_MAX )
" s %o %d %d %n " ,
name , location , & mode , & uid , & gid , & end ) ) {
2005-04-17 02:20:36 +04:00
fprintf ( stderr , " Unrecognized file format '%s' " , line ) ;
goto fail ;
}
2007-02-10 12:44:45 +03:00
if ( end & & isgraph ( line [ end ] ) ) {
int len ;
int nend ;
dname = malloc ( strlen ( line ) ) ;
if ( ! dname ) {
fprintf ( stderr , " out of memory (%d) \n " , dname_len ) ;
goto fail ;
}
dname_len = strlen ( name ) + 1 ;
memcpy ( dname , name , dname_len ) ;
do {
nend = 0 ;
if ( sscanf ( line + end , " % " str ( PATH_MAX ) " s %n " ,
name , & nend ) < 1 )
break ;
len = strlen ( name ) + 1 ;
memcpy ( dname + dname_len , name , len ) ;
dname_len + = len ;
nlinks + + ;
end + = nend ;
} while ( isgraph ( line [ end ] ) ) ;
} else {
dname = name ;
}
2008-10-29 16:54:17 +03:00
rc = cpio_mkfile ( dname , cpio_replace_env ( location ) ,
mode , uid , gid , nlinks ) ;
2005-04-17 02:20:36 +04:00
fail :
2007-02-10 12:44:45 +03:00
if ( dname_len ) free ( dname ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2009-09-23 03:43:38 +04:00
static void usage ( const char * prog )
2005-04-17 02:20:36 +04:00
{
fprintf ( stderr , " Usage: \n "
2011-04-01 01:16:42 +04:00
" \t %s [-t <timestamp>] <cpio_list> \n "
2005-04-17 02:20:36 +04:00
" \n "
" <cpio_list> is a file containing newline separated entries that \n "
" describe the files to be included in the initramfs archive: \n "
" \n "
" # a comment \n "
2007-02-10 12:44:45 +03:00
" file <name> <location> <mode> <uid> <gid> [<hard links>] \n "
2005-04-17 02:20:36 +04:00
" dir <name> <mode> <uid> <gid> \n "
" nod <name> <mode> <uid> <gid> <dev_type> <maj> <min> \n "
" slink <name> <target> <mode> <uid> <gid> \n "
" pipe <name> <mode> <uid> <gid> \n "
" sock <name> <mode> <uid> <gid> \n "
" \n "
2007-02-10 12:44:45 +03:00
" <name> name of the file/dir/nod/etc in the archive \n "
" <location> location of the file in the current filesystem \n "
2008-10-29 16:54:17 +03:00
" expands shell variables quoted with ${} \n "
2007-02-10 12:44:45 +03:00
" <target> link target \n "
" <mode> mode/permissions of the file \n "
" <uid> user id (0=root) \n "
" <gid> group id (0=root) \n "
" <dev_type> device type (b=block, c=character) \n "
" <maj> major number of nod \n "
" <min> minor number of nod \n "
" <hard links> space separated list of other links to file \n "
2005-04-17 02:20:36 +04:00
" \n "
" example: \n "
" # A simple initramfs \n "
" dir /dev 0755 0 0 \n "
" nod /dev/console 0600 0 0 c 5 1 \n "
" dir /root 0700 0 0 \n "
" dir /sbin 0755 0 0 \n "
2011-04-01 01:16:42 +04:00
" file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0 \n "
" \n "
" <timestamp> is time in seconds since Epoch that will be used \n "
" as mtime for symlinks, special files and directories. The default \n "
" is to use the current time for these entries. \n " ,
2005-04-17 02:20:36 +04:00
prog ) ;
}
struct file_handler file_handler_table [ ] = {
{
. type = " file " ,
. handler = cpio_mkfile_line ,
} , {
. type = " nod " ,
. handler = cpio_mknod_line ,
} , {
. type = " dir " ,
. handler = cpio_mkdir_line ,
} , {
. type = " slink " ,
. handler = cpio_mkslink_line ,
} , {
. type = " pipe " ,
. handler = cpio_mkpipe_line ,
} , {
. type = " sock " ,
. handler = cpio_mksock_line ,
} , {
. type = NULL ,
. handler = NULL ,
}
} ;
# define LINE_SIZE (2 * PATH_MAX + 50)
int main ( int argc , char * argv [ ] )
{
FILE * cpio_list ;
char line [ LINE_SIZE ] ;
char * args , * type ;
int ec = 0 ;
int line_nr = 0 ;
2011-04-01 01:16:42 +04:00
const char * filename ;
default_mtime = time ( NULL ) ;
while ( 1 ) {
int opt = getopt ( argc , argv , " t:h " ) ;
char * invalid ;
if ( opt = = - 1 )
break ;
switch ( opt ) {
case ' t ' :
default_mtime = strtol ( optarg , & invalid , 10 ) ;
if ( ! * optarg | | * invalid ) {
fprintf ( stderr , " Invalid timestamp: %s \n " ,
optarg ) ;
usage ( argv [ 0 ] ) ;
exit ( 1 ) ;
}
break ;
case ' h ' :
case ' ? ' :
usage ( argv [ 0 ] ) ;
exit ( opt = = ' h ' ? 0 : 1 ) ;
}
}
2005-04-17 02:20:36 +04:00
2011-04-01 01:16:42 +04:00
if ( argc - optind ! = 1 ) {
2005-04-17 02:20:36 +04:00
usage ( argv [ 0 ] ) ;
exit ( 1 ) ;
}
2011-04-01 01:16:42 +04:00
filename = argv [ optind ] ;
if ( ! strcmp ( filename , " - " ) )
2007-05-11 09:44:28 +04:00
cpio_list = stdin ;
2011-04-01 01:16:42 +04:00
else if ( ! ( cpio_list = fopen ( filename , " r " ) ) ) {
2005-04-17 02:20:36 +04:00
fprintf ( stderr , " ERROR: unable to open '%s': %s \n \n " ,
2011-04-01 01:16:42 +04:00
filename , strerror ( errno ) ) ;
2005-04-17 02:20:36 +04:00
usage ( argv [ 0 ] ) ;
exit ( 1 ) ;
}
while ( fgets ( line , LINE_SIZE , cpio_list ) ) {
int type_idx ;
size_t slen = strlen ( line ) ;
line_nr + + ;
if ( ' # ' = = * line ) {
/* comment - skip to next line */
continue ;
}
if ( ! ( type = strtok ( line , " \t " ) ) ) {
fprintf ( stderr ,
" ERROR: incorrect format, could not locate file type line %d: '%s' \n " ,
line_nr , line ) ;
ec = - 1 ;
2006-04-19 09:21:54 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
if ( ' \n ' = = * type ) {
/* a blank line */
continue ;
}
if ( slen = = strlen ( type ) ) {
/* must be an empty line */
continue ;
}
if ( ! ( args = strtok ( NULL , " \n " ) ) ) {
fprintf ( stderr ,
" ERROR: incorrect format, newline required line %d: '%s' \n " ,
line_nr , line ) ;
ec = - 1 ;
}
for ( type_idx = 0 ; file_handler_table [ type_idx ] . type ; type_idx + + ) {
int rc ;
if ( ! strcmp ( line , file_handler_table [ type_idx ] . type ) ) {
if ( ( rc = file_handler_table [ type_idx ] . handler ( args ) ) ) {
ec = rc ;
fprintf ( stderr , " line %d \n " , line_nr ) ;
}
break ;
}
}
if ( NULL = = file_handler_table [ type_idx ] . type ) {
fprintf ( stderr , " unknown file type line %d: '%s' \n " ,
line_nr , line ) ;
}
}
2006-04-19 09:21:54 +04:00
if ( ec = = 0 )
cpio_trailer ( ) ;
2005-04-17 02:20:36 +04:00
exit ( ec ) ;
}