2007-10-16 01:27:00 -07:00
/*
* Copyright ( C ) 2002 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
* Licensed under the GPL
*/
2006-01-06 00:18:59 -08:00
# include <stdio.h>
# include <stdlib.h>
2007-10-16 01:27:00 -07:00
# include <dirent.h>
2006-01-06 00:18:59 -08:00
# include <errno.h>
2007-10-16 01:27:00 -07:00
# include <fcntl.h>
2006-01-06 00:18:59 -08:00
# include <signal.h>
2007-10-16 01:27:00 -07:00
# include <string.h>
# include <unistd.h>
2006-01-06 00:18:59 -08:00
# include <sys/stat.h>
2012-10-08 03:27:32 +01:00
# include <init.h>
# include <os.h>
2006-01-06 00:18:59 -08:00
# define UML_DIR "~ / .uml / "
# define UMID_LEN 64
/* Changed by set_umid, which is run early in boot */
2007-02-10 01:44:25 -08:00
static char umid [ UMID_LEN ] = { 0 } ;
2006-01-06 00:18:59 -08:00
/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
static char * uml_dir = UML_DIR ;
static int __init make_uml_dir ( void )
{
char dir [ 512 ] = { ' \0 ' } ;
2006-01-06 00:19:01 -08:00
int len , err ;
2006-01-06 00:18:59 -08:00
2007-10-16 01:27:00 -07:00
if ( * uml_dir = = ' ~ ' ) {
2006-01-06 00:18:59 -08:00
char * home = getenv ( " HOME " ) ;
2006-01-06 00:19:01 -08:00
err = - ENOENT ;
2007-10-16 01:27:00 -07:00
if ( home = = NULL ) {
printk ( UM_KERN_ERR " make_uml_dir : no value in "
" environment for $HOME \n " ) ;
2006-01-06 00:19:01 -08:00
goto err ;
2006-01-06 00:18:59 -08:00
}
strlcpy ( dir , home , sizeof ( dir ) ) ;
uml_dir + + ;
}
strlcat ( dir , uml_dir , sizeof ( dir ) ) ;
len = strlen ( dir ) ;
if ( len > 0 & & dir [ len - 1 ] ! = ' / ' )
strlcat ( dir , " / " , sizeof ( dir ) ) ;
2006-01-06 00:19:01 -08:00
err = - ENOMEM ;
2006-01-06 00:18:59 -08:00
uml_dir = malloc ( strlen ( dir ) + 1 ) ;
if ( uml_dir = = NULL ) {
printf ( " make_uml_dir : malloc failed, errno = %d \n " , errno ) ;
2006-01-06 00:19:01 -08:00
goto err ;
2006-01-06 00:18:59 -08:00
}
strcpy ( uml_dir , dir ) ;
2007-10-16 01:27:00 -07:00
if ( ( mkdir ( uml_dir , 0777 ) < 0 ) & & ( errno ! = EEXIST ) ) {
2006-01-06 00:18:59 -08:00
printf ( " Failed to mkdir '%s': %s \n " , uml_dir , strerror ( errno ) ) ;
2006-01-06 00:19:01 -08:00
err = - errno ;
goto err_free ;
2006-01-06 00:18:59 -08:00
}
return 0 ;
2006-01-06 00:19:01 -08:00
err_free :
free ( uml_dir ) ;
err :
uml_dir = NULL ;
return err ;
2006-01-06 00:18:59 -08:00
}
2006-07-01 04:36:24 -07:00
/*
* Unlinks the files contained in @ dir and then removes @ dir .
* Doesn ' t handle directory trees , so it ' s not like rm - rf , but almost such . We
2007-10-16 01:27:00 -07:00
* ignore ENOENT errors for anything ( they happen , strangely enough - possibly
* due to races between multiple dying UML threads ) .
2006-07-01 04:36:24 -07:00
*/
static int remove_files_and_dir ( char * dir )
2006-01-06 00:18:59 -08:00
{
DIR * directory ;
struct dirent * ent ;
int len ;
char file [ 256 ] ;
2006-07-01 04:36:24 -07:00
int ret ;
2006-01-06 00:18:59 -08:00
directory = opendir ( dir ) ;
2006-07-01 04:36:24 -07:00
if ( directory = = NULL ) {
if ( errno ! = ENOENT )
return - errno ;
else
return 0 ;
}
2006-01-06 00:19:01 -08:00
2006-07-01 04:36:24 -07:00
while ( ( ent = readdir ( directory ) ) ! = NULL ) {
if ( ! strcmp ( ent - > d_name , " . " ) | | ! strcmp ( ent - > d_name , " .. " ) )
2006-01-06 00:18:59 -08:00
continue ;
len = strlen ( dir ) + sizeof ( " / " ) + strlen ( ent - > d_name ) + 1 ;
2006-07-01 04:36:24 -07:00
if ( len > sizeof ( file ) ) {
ret = - E2BIG ;
goto out ;
}
2006-01-06 00:19:01 -08:00
2006-01-06 00:18:59 -08:00
sprintf ( file , " %s/%s " , dir , ent - > d_name ) ;
2006-07-01 04:36:24 -07:00
if ( unlink ( file ) < 0 & & errno ! = ENOENT ) {
ret = - errno ;
goto out ;
}
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
2006-07-01 04:36:24 -07:00
if ( rmdir ( dir ) < 0 & & errno ! = ENOENT ) {
ret = - errno ;
goto out ;
}
ret = 0 ;
out :
closedir ( directory ) ;
return ret ;
2006-01-06 00:18:59 -08:00
}
2007-10-16 01:27:00 -07:00
/*
* This says that there isn ' t already a user of the specified directory even if
2006-01-06 00:19:01 -08:00
* there are errors during the checking . This is because if these errors
* happen , the directory is unusable by the pre - existing UML , so we might as
* well take it over . This could happen either by
* the existing UML somehow corrupting its umid directory
* something other than UML sticking stuff in the directory
* this boot racing with a shutdown of the other UML
* In any of these cases , the directory isn ' t useful for anything else .
2006-07-01 04:36:23 -07:00
*
* Boolean return : 1 if in use , 0 otherwise .
2006-01-06 00:19:01 -08:00
*/
2006-07-01 04:36:23 -07:00
static inline int is_umdir_used ( char * dir )
2006-01-06 00:18:59 -08:00
{
char file [ strlen ( uml_dir ) + UMID_LEN + sizeof ( " /pid \0 " ) ] ;
char pid [ sizeof ( " nnnnn \0 " ) ] , * end ;
2006-01-06 00:19:01 -08:00
int dead , fd , p , n , err ;
n = snprintf ( file , sizeof ( file ) , " %s/pid " , dir ) ;
2007-10-16 01:27:00 -07:00
if ( n > = sizeof ( file ) ) {
printk ( UM_KERN_ERR " is_umdir_used - pid filename too long \n " ) ;
2006-01-06 00:19:01 -08:00
err = - E2BIG ;
goto out ;
}
2006-01-06 00:18:59 -08:00
dead = 0 ;
2006-01-06 00:19:01 -08:00
fd = open ( file , O_RDONLY ) ;
2007-10-16 01:27:00 -07:00
if ( fd < 0 ) {
2006-04-10 22:53:38 -07:00
fd = - errno ;
2007-10-16 01:27:00 -07:00
if ( fd ! = - ENOENT ) {
printk ( UM_KERN_ERR " is_umdir_used : couldn't open pid "
" file '%s', err = %d \n " , file , - fd ) ;
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
goto out ;
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
err = 0 ;
n = read ( fd , pid , sizeof ( pid ) ) ;
2007-10-16 01:27:00 -07:00
if ( n < 0 ) {
printk ( UM_KERN_ERR " is_umdir_used : couldn't read pid file "
" '%s', err = %d \n " , file , errno ) ;
2006-04-10 22:53:38 -07:00
goto out_close ;
2007-10-16 01:27:00 -07:00
} else if ( n = = 0 ) {
printk ( UM_KERN_ERR " is_umdir_used : couldn't read pid file "
" '%s', 0-byte read \n " , file ) ;
2006-01-06 00:19:01 -08:00
goto out_close ;
}
p = strtoul ( pid , & end , 0 ) ;
2007-10-16 01:27:00 -07:00
if ( end = = pid ) {
printk ( UM_KERN_ERR " is_umdir_used : couldn't parse pid file "
" '%s', errno = %d \n " , file , errno ) ;
2006-01-06 00:19:01 -08:00
goto out_close ;
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
2007-10-16 01:27:00 -07:00
if ( ( kill ( p , 0 ) = = 0 ) | | ( errno ! = ESRCH ) ) {
printk ( UM_KERN_ERR " umid \" %s \" is already in use by pid %d \n " ,
umid , p ) ;
2006-01-06 00:19:01 -08:00
return 1 ;
2006-03-27 01:14:39 -08:00
}
2006-01-06 00:19:01 -08:00
2006-04-10 22:53:38 -07:00
out_close :
2006-01-06 00:19:01 -08:00
close ( fd ) ;
2006-04-10 22:53:38 -07:00
out :
2006-01-06 00:19:01 -08:00
return 0 ;
2006-01-06 00:18:59 -08:00
}
2006-07-01 04:36:23 -07:00
/*
* Try to remove the directory @ dir unless it ' s in use .
* Precondition : @ dir exists .
* Returns 0 for success , < 0 for failure in removal or if the directory is in
* use .
*/
static int umdir_take_if_dead ( char * dir )
{
int ret ;
if ( is_umdir_used ( dir ) )
return - EEXIST ;
2006-07-01 04:36:24 -07:00
ret = remove_files_and_dir ( dir ) ;
2006-07-01 04:36:23 -07:00
if ( ret ) {
2007-10-16 01:27:00 -07:00
printk ( UM_KERN_ERR " is_umdir_used - remove_files_and_dir "
" failed with err = %d \n " , ret ) ;
2006-07-01 04:36:23 -07:00
}
return ret ;
}
2006-01-06 00:18:59 -08:00
static void __init create_pid_file ( void )
{
char file [ strlen ( uml_dir ) + UMID_LEN + sizeof ( " /pid \0 " ) ] ;
char pid [ sizeof ( " nnnnn \0 " ) ] ;
int fd , n ;
2007-10-16 01:27:00 -07:00
if ( umid_file_name ( " pid " , file , sizeof ( file ) ) )
2006-01-06 00:18:59 -08:00
return ;
2006-01-06 00:19:01 -08:00
fd = open ( file , O_RDWR | O_CREAT | O_EXCL , 0644 ) ;
2007-10-16 01:27:00 -07:00
if ( fd < 0 ) {
printk ( UM_KERN_ERR " Open of machine pid file \" %s \" failed: "
" %s \n " , file , strerror ( errno ) ) ;
2006-01-06 00:18:59 -08:00
return ;
}
2006-01-06 00:19:01 -08:00
snprintf ( pid , sizeof ( pid ) , " %d \n " , getpid ( ) ) ;
n = write ( fd , pid , strlen ( pid ) ) ;
2007-10-16 01:27:00 -07:00
if ( n ! = strlen ( pid ) )
printk ( UM_KERN_ERR " Write of pid file failed - err = %d \n " ,
errno ) ;
2006-01-06 00:19:01 -08:00
close ( fd ) ;
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
int __init set_umid ( char * name )
2006-01-06 00:18:59 -08:00
{
2007-10-16 01:27:00 -07:00
if ( strlen ( name ) > UMID_LEN - 1 )
2006-01-06 00:19:01 -08:00
return - E2BIG ;
2006-01-06 00:18:59 -08:00
strlcpy ( umid , name , sizeof ( umid ) ) ;
return 0 ;
}
2007-02-10 01:44:25 -08:00
/* Changed in make_umid, which is called during early boot */
2006-01-06 00:18:59 -08:00
static int umid_setup = 0 ;
2008-07-23 21:28:49 -07:00
static int __init make_umid ( void )
2006-01-06 00:18:59 -08:00
{
int fd , err ;
char tmp [ 256 ] ;
2007-10-16 01:27:00 -07:00
if ( umid_setup )
2006-01-06 00:19:01 -08:00
return 0 ;
2006-01-06 00:18:59 -08:00
make_uml_dir ( ) ;
2007-10-16 01:27:00 -07:00
if ( * umid = = ' \0 ' ) {
2006-01-06 00:18:59 -08:00
strlcpy ( tmp , uml_dir , sizeof ( tmp ) ) ;
2006-01-06 00:19:01 -08:00
strlcat ( tmp , " XXXXXX " , sizeof ( tmp ) ) ;
2006-01-06 00:18:59 -08:00
fd = mkstemp ( tmp ) ;
2007-10-16 01:27:00 -07:00
if ( fd < 0 ) {
printk ( UM_KERN_ERR " make_umid - mkstemp(%s) failed: "
" %s \n " , tmp , strerror ( errno ) ) ;
2006-01-06 00:19:01 -08:00
err = - errno ;
goto err ;
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
close ( fd ) ;
set_umid ( & tmp [ strlen ( uml_dir ) ] ) ;
2007-10-16 01:27:00 -07:00
/*
* There ' s a nice tiny little race between this unlink and
2006-01-06 00:18:59 -08:00
* the mkdir below . It ' d be nice if there were a mkstemp
* for directories .
*/
2007-10-16 01:27:00 -07:00
if ( unlink ( tmp ) ) {
2006-01-06 00:19:01 -08:00
err = - errno ;
goto err ;
}
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
snprintf ( tmp , sizeof ( tmp ) , " %s%s " , uml_dir , umid ) ;
2006-01-06 00:18:59 -08:00
err = mkdir ( tmp , 0777 ) ;
2007-10-16 01:27:00 -07:00
if ( err < 0 ) {
2006-01-06 00:19:01 -08:00
err = - errno ;
2007-10-16 01:27:00 -07:00
if ( err ! = - EEXIST )
2006-01-06 00:19:01 -08:00
goto err ;
2006-07-01 04:36:23 -07:00
if ( umdir_take_if_dead ( tmp ) < 0 )
2006-01-06 00:19:01 -08:00
goto err ;
err = mkdir ( tmp , 0777 ) ;
2006-01-06 00:18:59 -08:00
}
2007-10-16 01:27:00 -07:00
if ( err ) {
2006-03-27 01:14:39 -08:00
err = - errno ;
2007-10-16 01:27:00 -07:00
printk ( UM_KERN_ERR " Failed to create '%s' - err = %d \n " , umid ,
errno ) ;
2006-03-27 01:14:39 -08:00
goto err ;
2006-01-06 00:18:59 -08:00
}
umid_setup = 1 ;
create_pid_file ( ) ;
2006-03-27 01:14:39 -08:00
err = 0 ;
2006-01-06 00:19:01 -08:00
err :
return err ;
2006-01-06 00:18:59 -08:00
}
static int __init make_umid_init ( void )
{
2007-10-16 01:27:00 -07:00
if ( ! make_umid ( ) )
2006-03-27 01:14:39 -08:00
return 0 ;
2007-10-16 01:27:00 -07:00
/*
* If initializing with the given umid failed , then try again with
2006-03-27 01:14:39 -08:00
* a random one .
*/
2007-10-16 01:27:00 -07:00
printk ( UM_KERN_ERR " Failed to initialize umid \" %s \" , trying with a "
" random umid \n " , umid ) ;
2006-03-27 01:14:39 -08:00
* umid = ' \0 ' ;
2006-01-06 00:19:01 -08:00
make_umid ( ) ;
2006-01-06 00:18:59 -08:00
2006-01-06 00:19:01 -08:00
return 0 ;
2006-01-06 00:18:59 -08:00
}
__initcall ( make_umid_init ) ;
int __init umid_file_name ( char * name , char * buf , int len )
{
int n , err ;
2006-01-06 00:19:01 -08:00
err = make_umid ( ) ;
2007-10-16 01:27:00 -07:00
if ( err )
2006-01-06 00:19:01 -08:00
return err ;
2006-01-06 00:18:59 -08:00
2006-01-06 00:19:01 -08:00
n = snprintf ( buf , len , " %s%s/%s " , uml_dir , umid , name ) ;
2007-10-16 01:27:00 -07:00
if ( n > = len ) {
printk ( UM_KERN_ERR " umid_file_name : buffer too short \n " ) ;
2006-01-06 00:19:01 -08:00
return - E2BIG ;
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
return 0 ;
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
char * get_umid ( void )
2006-01-06 00:18:59 -08:00
{
return umid ;
}
static int __init set_uml_dir ( char * name , int * add )
{
2007-10-16 01:27:00 -07:00
if ( * name = = ' \0 ' ) {
2006-01-06 00:19:01 -08:00
printf ( " uml_dir can't be an empty string \n " ) ;
return 0 ;
2006-01-06 00:18:59 -08:00
}
2006-01-06 00:19:01 -08:00
2007-10-16 01:27:00 -07:00
if ( name [ strlen ( name ) - 1 ] = = ' / ' ) {
2006-01-06 00:19:01 -08:00
uml_dir = name ;
return 0 ;
}
uml_dir = malloc ( strlen ( name ) + 2 ) ;
2007-10-16 01:27:00 -07:00
if ( uml_dir = = NULL ) {
2006-01-06 00:19:01 -08:00
printf ( " Failed to malloc uml_dir - error = %d \n " , errno ) ;
2007-10-16 01:27:00 -07:00
/*
* Return 0 here because do_initcalls doesn ' t look at
2006-01-06 00:19:01 -08:00
* the return value .
*/
return 0 ;
}
sprintf ( uml_dir , " %s/ " , name ) ;
return 0 ;
2006-01-06 00:18:59 -08:00
}
__uml_setup ( " uml_dir= " , set_uml_dir ,
" uml_dir=<directory> \n "
" The location to place the pid and umid files. \n \n "
) ;
static void remove_umid_dir ( void )
{
2006-01-06 00:19:01 -08:00
char dir [ strlen ( uml_dir ) + UMID_LEN + 1 ] , err ;
2006-01-06 00:18:59 -08:00
sprintf ( dir , " %s%s " , uml_dir , umid ) ;
2006-07-01 04:36:24 -07:00
err = remove_files_and_dir ( dir ) ;
2007-10-16 01:27:00 -07:00
if ( err )
2006-07-01 04:36:24 -07:00
printf ( " remove_umid_dir - remove_files_and_dir failed with "
2006-01-06 00:19:01 -08:00
" err = %d \n " , err ) ;
2006-01-06 00:18:59 -08:00
}
__uml_exitcall ( remove_umid_dir ) ;