2004-01-20 21:32:43 +03:00
/*
* Guillaume Cottenceau ( gc @ mandrakesoft . com )
*
* Copyright 2000 MandrakeSoft
*
* This software may be freely redistributed under the terms of the GNU
* public license .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
/*
* Portions from Erik Troan ( ewt @ redhat . com )
*
* Copyright 1996 Red Hat Software
*
*/
# include <stdlib.h>
# include <unistd.h>
# include <sys/time.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <string.h>
# include <ctype.h>
# include <stdio.h>
# include <dirent.h>
# include <sys/types.h>
# include <sys/mount.h>
# include <sys/poll.h>
2008-03-01 22:02:06 +03:00
2004-01-20 21:32:43 +03:00
# include "stage1.h"
# include "log.h"
# include "mount.h"
# include "frontend.h"
# include "automatic.h"
2004-11-30 14:47:57 +03:00
# include <errno.h>
# include <linux/loop.h>
2004-01-20 21:32:43 +03:00
# include "tools.h"
2004-11-30 14:47:57 +03:00
# include "modules.h"
2014-04-19 10:50:25 +04:00
# include "sha256.h"
2013-08-21 18:56:14 +04:00
# ifdef SPAWN_SPLASH
# include "init.h"
# endif
2004-01-20 21:32:43 +03:00
static struct param_elem params [ 50 ] ;
static int param_number = 0 ;
void process_cmdline ( void )
{
char buf [ 512 ] ;
int fd , size , i ;
log_message ( " opening /proc/cmdline... " ) ;
if ( ( fd = open ( " /proc/cmdline " , O_RDONLY ) ) = = - 1 )
fatal_error ( " could not open /proc/cmdline " ) ;
size = read ( fd , buf , sizeof ( buf ) ) ;
buf [ size - 1 ] = ' \0 ' ; // -1 to eat the \n
close ( fd ) ;
log_message ( " \t %s " , buf ) ;
i = 0 ;
while ( buf [ i ] ! = ' \0 ' ) {
char * name , * value = NULL ;
int j = i ;
while ( buf [ i ] ! = ' ' & & buf [ i ] ! = ' = ' & & buf [ i ] ! = ' \0 ' )
i + + ;
if ( i = = j ) {
i + + ;
continue ;
}
name = memdup ( & buf [ j ] , i - j + 1 ) ;
name [ i - j ] = ' \0 ' ;
if ( buf [ i ] = = ' = ' ) {
int k = i + 1 ;
i + + ;
while ( buf [ i ] ! = ' ' & & buf [ i ] ! = ' \0 ' )
i + + ;
value = memdup ( & buf [ k ] , i - k + 1 ) ;
value [ i - k ] = ' \0 ' ;
}
params [ param_number ] . name = name ;
params [ param_number ] . value = value ;
param_number + + ;
if ( ! strcmp ( name , " expert " ) ) set_param ( MODE_EXPERT ) ;
if ( ! strcmp ( name , " changedisk " ) ) set_param ( MODE_CHANGEDISK ) ;
if ( ! strcmp ( name , " updatemodules " ) ) set_param ( MODE_UPDATEMODULES ) ;
if ( ! strcmp ( name , " rescue " ) ) set_param ( MODE_RESCUE ) ;
2010-12-06 14:17:21 +03:00
if ( ! strcmp ( name , " splash " ) ) set_param ( MODE_SPLASH ) ;
2004-11-25 00:44:32 +03:00
if ( ! strcmp ( name , " live " ) ) set_param ( MODE_LIVE ) ;
2005-03-05 18:26:22 +03:00
if ( ! strcmp ( name , " stagename " ) ) set_param ( MODE_STAGENAME ) ;
2004-01-20 21:32:43 +03:00
if ( ! strcmp ( name , " lowmem " ) ) set_param ( MODE_LOWMEM ) ;
2014-04-19 10:50:25 +04:00
if ( ! strcmp ( name , " hash " ) ) set_param ( MODE_VERIFICATION ) ;
2004-01-20 21:32:43 +03:00
if ( ! strcmp ( name , " automatic " ) ) {
set_param ( MODE_AUTOMATIC ) ;
grab_automatic_params ( value ) ;
}
if ( buf [ i ] = = ' \0 ' )
break ;
i + + ;
}
log_message ( " \t got %d args " , param_number ) ;
}
int stage1_mode = 0 ;
int get_param ( int i )
{
# ifdef SPAWN_INTERACTIVE
static int fd = 0 ;
char buf [ 5000 ] ;
char * ptr ;
int nb ;
if ( fd < = 0 ) {
fd = open ( interactive_fifo , O_RDONLY ) ;
if ( fd = = - 1 )
return ( stage1_mode & i ) ;
fcntl ( fd , F_SETFL , O_NONBLOCK ) ;
}
if ( fd > 0 ) {
if ( ( nb = read ( fd , buf , sizeof ( buf ) ) ) > 0 ) {
buf [ nb ] = ' \0 ' ;
ptr = buf ;
while ( ( ptr = strstr ( ptr , " + " ) ) ) {
if ( ! strncmp ( ptr + 2 , " expert " , 6 ) ) set_param ( MODE_EXPERT ) ;
if ( ! strncmp ( ptr + 2 , " rescue " , 6 ) ) set_param ( MODE_RESCUE ) ;
ptr + + ;
}
ptr = buf ;
while ( ( ptr = strstr ( ptr , " - " ) ) ) {
if ( ! strncmp ( ptr + 2 , " expert " , 6 ) ) unset_param ( MODE_EXPERT ) ;
if ( ! strncmp ( ptr + 2 , " rescue " , 6 ) ) unset_param ( MODE_RESCUE ) ;
ptr + + ;
}
}
}
# endif
return ( stage1_mode & i ) ;
}
char * get_param_valued ( char * param_name )
{
int i ;
for ( i = 0 ; i < param_number ; i + + )
if ( ! strcmp ( params [ i ] . name , param_name ) )
return params [ i ] . value ;
return NULL ;
}
void set_param_valued ( char * param_name , char * param_value )
{
params [ param_number ] . name = param_name ;
params [ param_number ] . value = param_value ;
param_number + + ;
}
void set_param ( int i )
{
stage1_mode | = i ;
if ( i = = MODE_RESCUE ) {
2005-03-05 18:26:22 +03:00
set_param_valued ( " stagename " , " rescue " ) ;
set_param ( MODE_STAGENAME ) ;
2004-01-20 21:32:43 +03:00
}
}
void unset_param ( int i )
{
stage1_mode & = ~ i ;
}
// warning, many things rely on the fact that:
// - when failing it returns 0
// - it stops on first non-digit char
int charstar_to_int ( char * s )
{
int number = 0 ;
while ( * s & & isdigit ( * s ) ) {
number = ( number * 10 ) + ( * s - ' 0 ' ) ;
s + + ;
}
return number ;
}
int ramdisk_possible ( void )
{
return 1 ;
}
static void save_stuff_for_rescue ( void )
{
void save_this_file ( char * file ) {
char buf [ 5000 ] ;
int fd_r , fd_w , i ;
char location [ 100 ] ;
if ( ( fd_r = open ( file , O_RDONLY ) ) < 0 ) {
log_message ( " can't open %s for read " , file ) ;
return ;
}
strcpy ( location , STAGE2_LOCATION ) ;
strcat ( location , file ) ;
if ( ( fd_w = open ( location , O_WRONLY ) ) < 0 ) {
log_message ( " can't open %s for write " , location ) ;
close ( fd_r ) ;
return ;
}
if ( ( i = read ( fd_r , buf , sizeof ( buf ) ) ) < = 0 ) {
log_message ( " can't read from %s " , file ) ;
close ( fd_r ) ; close ( fd_w ) ;
return ;
}
if ( write ( fd_w , buf , i ) ! = i )
log_message ( " can't write %d bytes to %s " , i , location ) ;
close ( fd_r ) ; close ( fd_w ) ;
log_message ( " saved file %s for rescue (%d bytes) " , file , i ) ;
}
save_this_file ( " /etc/resolv.conf " ) ;
}
2005-01-24 21:12:06 +03:00
enum return_type load_ramdisk_fd ( int source_fd , int size )
2004-01-20 21:32:43 +03:00
{
char * ramdisk = " /dev/ram3 " ; /* warning, verify that this file exists in the initrd, and that root=/dev/ram3 is actually passed to the kernel at boot time */
int ram_fd ;
char buffer [ 32768 ] ;
char * wait_msg = " Loading program into memory... " ;
int bytes_read = 0 ;
int actually ;
int seems_ok = 0 ;
ram_fd = open ( ramdisk , O_WRONLY ) ;
if ( ram_fd = = - 1 ) {
log_perror ( ramdisk ) ;
stg1_error_message ( " Could not open ramdisk device file. " ) ;
return RETURN_ERROR ;
}
init_progression ( wait_msg , size ) ;
2005-01-24 21:12:06 +03:00
while ( ( actually = read ( source_fd , buffer , sizeof ( buffer ) ) ) > 0 ) {
2004-01-20 21:32:43 +03:00
seems_ok = 1 ;
if ( write ( ram_fd , buffer , actually ) ! = actually ) {
log_perror ( " writing ramdisk " ) ;
remove_wait_message ( ) ;
return RETURN_ERROR ;
}
2004-11-30 14:47:57 +03:00
update_progression ( ( int ) ( bytes_read + = actually ) ) ;
2004-01-20 21:32:43 +03:00
}
if ( ! seems_ok ) {
2004-11-30 14:47:57 +03:00
log_message ( " reading compressed ramdisk: %s " , strerror ( errno ) ) ;
2004-01-20 21:32:43 +03:00
close ( ram_fd ) ;
remove_wait_message ( ) ;
2004-11-30 14:47:57 +03:00
stg1_error_message ( " Could not load second stage ramdisk. "
2004-01-20 21:32:43 +03:00
" This is probably an hardware error while reading the data. "
" (this may be caused by a hardware failure or a Linux kernel bug) " ) ;
return RETURN_ERROR ;
}
end_progression ( ) ;
close ( ram_fd ) ;
2004-11-30 14:47:57 +03:00
if ( do_losetup ( LIVE_DEVICE , ramdisk ) = = - 1 )
fatal_error ( " could not setup loopback for loaded second stage " ) ;
2005-01-24 21:12:06 +03:00
if ( my_mount ( LIVE_DEVICE , STAGE2_LOCATION , STAGE2FS , 0 ) )
2004-01-20 21:32:43 +03:00
return RETURN_ERROR ;
set_param ( MODE_RAMDISK ) ;
if ( IS_RESCUE ) {
save_stuff_for_rescue ( ) ;
if ( umount ( STAGE2_LOCATION ) ) {
log_perror ( ramdisk ) ;
return RETURN_ERROR ;
}
return RETURN_OK ; /* fucksike, I lost several hours wondering why the kernel won't see the rescue if it is alreay mounted */
}
return RETURN_OK ;
}
2005-04-21 15:06:44 +04:00
2010-11-30 22:14:42 +03:00
int splash_verbose ( )
2005-04-21 15:06:44 +04:00
{
2010-12-01 19:10:02 +03:00
# ifdef SPAWN_SPLASH
2013-02-21 15:14:16 +04:00
if ( ! IS_SPLASH ) return ( 1 ) ;
struct stat pst ;
if ( stat ( " /bin/plymouth " , & pst ) ) return ( 1 ) ;
2010-12-21 17:56:50 +03:00
char * av [ ] = { " /bin/plymouth " , " plymouth " , " quit " , NULL } ;
2010-11-30 22:14:42 +03:00
log_message ( " %s: %s \n " , av [ 0 ] , av [ 2 ] ) ;
return spawn ( av ) ;
2010-12-01 19:10:02 +03:00
# endif
2005-04-21 15:06:44 +04:00
}
2010-11-22 19:56:35 +03:00
int update_splash ( char * state )
2005-04-21 15:06:44 +04:00
{
2010-12-01 19:10:02 +03:00
# ifdef SPAWN_SPLASH
2013-02-21 15:14:16 +04:00
if ( ! IS_SPLASH ) return ( 1 ) ;
struct stat pst ;
if ( stat ( " /bin/plymouth " , & pst ) ) return ( 1 ) ;
2010-11-30 22:14:42 +03:00
char * av [ ] = { " /bin/plymouth " , " plymouth " , " --update " , state , NULL } ;
log_message ( " %s: %s \n " , av [ 0 ] , av [ 3 ] ) ;
2010-11-22 19:56:35 +03:00
return spawn ( av ) ;
2010-12-01 19:10:02 +03:00
# endif
2005-04-21 15:06:44 +04:00
}
2004-01-20 21:32:43 +03:00
char * get_ramdisk_realname ( void )
{
char img_name [ 500 ] ;
2005-03-05 18:26:22 +03:00
char * stg2_name = get_param_valued ( " stagename " ) ;
2004-01-20 21:32:43 +03:00
char * begin_img = RAMDISK_LOCATION ;
if ( ! stg2_name )
2004-01-30 19:50:12 +03:00
stg2_name = " altinst " ;
2004-01-20 21:32:43 +03:00
strcpy ( img_name , begin_img ) ;
strcat ( img_name , stg2_name ) ;
return strdup ( img_name ) ;
}
2007-03-03 00:48:18 +03:00
char * get_ramdisk_path ( const char * mount_path )
2004-11-30 14:47:57 +03:00
{
2007-03-03 00:48:18 +03:00
char img_name [ 500 ] ;
2004-11-30 14:47:57 +03:00
char * st2_path = get_ramdisk_realname ( ) ;
2004-12-13 01:22:50 +03:00
/* FIXME */
strcpy ( img_name , mount_path ? mount_path : IMAGE_LOCATION ) ;
2004-11-30 14:47:57 +03:00
strcat ( img_name , st2_path ) ;
return strdup ( img_name ) ;
}
2004-01-20 21:32:43 +03:00
2014-04-19 10:50:25 +04:00
/* This function is used to protect against stage2 (aka ramdisk) spoofing */
2014-04-23 01:38:54 +04:00
enum return_type verify_ramdisk_digest ( const char * filename , const char * sha256_hash )
2014-04-19 10:50:25 +04:00
{
2014-04-23 01:38:54 +04:00
if ( ! sha256_hash )
return RETURN_ERROR ;
2014-04-19 10:50:25 +04:00
2014-04-23 01:38:54 +04:00
int fd ;
struct stat st ;
2014-04-19 10:50:25 +04:00
2014-04-23 01:38:54 +04:00
if ( ( fd = open ( filename , O_RDONLY ) ) < 0 | | fstat ( fd , & st ) ) {
log_message ( " verify_ramdisk_digest: %s: %m " , filename ) ;
close ( fd ) ;
return RETURN_ERROR ;
}
2014-04-19 10:50:25 +04:00
2014-04-23 01:38:54 +04:00
init_progression ( " Verifying stage2 authenticity... " , st . st_size ) ;
2014-04-19 10:50:25 +04:00
2014-04-23 01:38:54 +04:00
sha256_context ctx ;
2014-04-19 10:50:25 +04:00
sha256_starts ( & ctx ) ;
2014-04-23 01:38:54 +04:00
ssize_t bytes_read = 0 , total_bytes_read = 0 ;
unsigned char buffer [ 8192 ] ;
while ( ( bytes_read = read ( fd , buffer , sizeof ( buffer ) ) ) ) {
if ( bytes_read < 0 ) {
if ( EINTR = = errno )
continue ;
log_message ( " verify_ramdisk_digest: %s: %m " , filename ) ;
close ( fd ) ;
2014-04-19 10:50:25 +04:00
remove_wait_message ( ) ;
2014-04-23 01:38:54 +04:00
return RETURN_ERROR ;
2014-04-19 10:50:25 +04:00
}
2014-04-23 01:38:54 +04:00
sha256_update ( & ctx , buffer , bytes_read ) ;
total_bytes_read + = bytes_read ;
update_progression ( total_bytes_read ) ;
2014-04-19 10:50:25 +04:00
}
2014-04-23 01:38:54 +04:00
close ( fd ) ;
2014-04-19 10:50:25 +04:00
end_progression ( ) ;
2014-04-23 01:38:54 +04:00
uint8 digest [ 256 / 8 ] ;
sha256_finish ( & ctx , digest ) ;
const char * hex = " 0123456789abcdef " ;
unsigned i ;
char computed_hash [ sizeof ( digest ) * 2 + 1 ] ;
char * dest = computed_hash ;
2014-04-19 10:50:25 +04:00
2014-04-23 01:38:54 +04:00
for ( i = 0 ; i < sizeof ( digest ) ; + + i ) {
* ( dest + + ) = hex [ ( digest [ i ] > > 4 ) & 0xf ] ;
* ( dest + + ) = hex [ digest [ i ] & 0xf ] ;
}
* dest = ' \0 ' ;
2014-04-19 10:50:25 +04:00
2014-04-23 01:38:54 +04:00
return strcmp ( computed_hash , sha256_hash ) ? RETURN_ERROR : RETURN_OK ;
2014-04-19 10:50:25 +04:00
}
2005-03-05 18:26:22 +03:00
enum return_type load_ramdisk ( char * mount_path )
2004-01-20 21:32:43 +03:00
{
int st2_fd ;
struct stat statr ;
char img_name [ 500 ] ;
2005-03-05 18:26:22 +03:00
strcpy ( img_name , mount_path ? mount_path : IMAGE_LOCATION ) ;
2004-01-20 21:32:43 +03:00
strcat ( img_name , get_ramdisk_realname ( ) ) ;
log_message ( " trying to load %s as a ramdisk " , img_name ) ;
st2_fd = open ( img_name , O_RDONLY ) ; /* to be able to see the progression */
if ( st2_fd = = - 1 ) {
log_message ( " open ramdisk file (%s) failed " , img_name ) ;
stg1_error_message ( " Could not open compressed ramdisk file (%s). " , img_name ) ;
return RETURN_ERROR ;
}
if ( stat ( img_name , & statr ) )
return RETURN_ERROR ;
else
return load_ramdisk_fd ( st2_fd , statr . st_size ) ;
}
/* pixel's */
void * memdup ( void * src , size_t size )
{
void * r ;
r = malloc ( size ) ;
2004-04-04 01:19:34 +04:00
if ( ! r )
perror ( " malloc " ) ;
2004-01-20 21:32:43 +03:00
memcpy ( r , src , size ) ;
return r ;
}
static char * * my_env = NULL ;
static int env_size = 0 ;
void handle_env ( char * * env )
{
char * * ptr = env ;
while ( ptr & & * ptr ) {
ptr + + ;
env_size + + ;
}
my_env = malloc ( sizeof ( char * ) * 100 ) ;
2004-04-04 01:19:34 +04:00
if ( ! my_env )
perror ( " malloc " ) ;
2004-01-20 21:32:43 +03:00
memcpy ( my_env , env , sizeof ( char * ) * ( env_size + 1 ) ) ;
}
char * * grab_env ( void ) {
return my_env ;
}
void add_to_env ( char * name , char * value )
{
char tmp [ 500 ] ;
sprintf ( tmp , " %s=%s " , name , value ) ;
my_env [ env_size ] = strdup ( tmp ) ;
env_size + + ;
my_env [ env_size ] = NULL ;
}
2009-07-01 12:21:29 +04:00
char * get_from_env ( const char * key , const char const * * env )
{
int i = 0 ;
if ( key = = NULL | | env = = NULL )
return NULL ;
while ( env [ i ] )
{
if ( strncmp ( env [ i ] , key , strlen ( key ) ) = = 0 ) {
return strdup ( env [ i ] + strlen ( key ) + 1 ) ;
}
i + + ;
}
return NULL ;
}
2007-03-03 00:48:18 +03:00
int pass_env ( int fd )
2005-01-21 16:52:04 +03:00
{
char * * ptr = my_env ;
char * s ;
2007-03-03 00:48:18 +03:00
int i = 0 ;
2005-01-21 16:52:04 +03:00
while ( ptr & & * ptr ) {
s = * ptr ;
while ( * s + + ) ;
2007-03-03 00:48:18 +03:00
i = write ( fd , * ptr , s - * ptr ) ;
2005-01-21 16:52:04 +03:00
ptr + + ;
}
2007-03-03 00:48:18 +03:00
return i ;
2005-01-21 16:52:04 +03:00
}
2004-01-20 21:32:43 +03:00
char * * list_directory ( char * direct )
{
char * tmp [ 50000 ] ; /* in /dev there can be many many files.. */
int i = 0 ;
struct dirent * ep ;
DIR * dp = opendir ( direct ) ;
while ( dp & & ( ep = readdir ( dp ) ) ) {
if ( strcmp ( ep - > d_name , " . " ) & & strcmp ( ep - > d_name , " .. " ) ) {
tmp [ i ] = strdup ( ep - > d_name ) ;
i + + ;
}
}
if ( dp )
closedir ( dp ) ;
tmp [ i ] = NULL ;
return memdup ( tmp , sizeof ( char * ) * ( i + 1 ) ) ;
}
int string_array_length ( char * * a )
{
int i = 0 ;
if ( ! a )
return - 1 ;
while ( a & & * a ) {
a + + ;
i + + ;
}
return i ;
}
2004-11-30 14:47:57 +03:00
int do_losetup ( char * device , char * target )
{
2008-03-01 22:02:06 +03:00
int i , loopfd , targfd ;
2004-11-30 14:47:57 +03:00
struct loop_info loopInfo ;
2005-02-03 18:38:15 +03:00
2008-03-01 22:02:06 +03:00
my_insmod ( " loop " , NULL ) ;
/* wait for udev's dust settles down */
for ( i = 3 ; i & & ( loopfd = open ( device , O_RDONLY ) ) < 0 ; - - i , sleep ( 1 ) ) ;
2004-11-30 14:47:57 +03:00
if ( loopfd < 0 )
{
log_message ( " losetup: error opening %s: %s " , device , strerror ( errno ) ) ;
return - 1 ;
}
targfd = open ( target , O_RDONLY ) ;
if ( targfd < 0 )
{
log_message ( " losetup: error opening %s: %s " , target , strerror ( errno ) ) ;
close ( loopfd ) ;
return - 1 ;
}
if ( ioctl ( loopfd , LOOP_SET_FD , targfd ) < 0 )
{
log_message ( " losetup: error setting up loopback device: %s " , strerror ( errno ) ) ;
close ( loopfd ) ;
close ( targfd ) ;
return - 1 ;
}
memset ( & loopInfo , 0 , sizeof ( loopInfo ) ) ;
strcpy ( loopInfo . lo_name , target ) ;
if ( ioctl ( loopfd , LOOP_SET_STATUS , & loopInfo ) < 0 )
log_message ( " losetup: error setting up loopback device: %s " , strerror ( errno ) ) ;
close ( loopfd ) ;
close ( targfd ) ;
return 0 ;
}