2008-02-08 04:22:08 -08:00
/*
* Copyright ( C ) 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
* Licensed under the GPL
*/
2005-09-16 19:27:50 -07:00
# include <stdio.h>
# include <stddef.h>
2008-02-08 04:22:08 -08:00
# include <stdlib.h>
2005-09-16 19:27:50 -07:00
# include <unistd.h>
# include <errno.h>
# include <fcntl.h>
2008-02-08 04:22:08 -08:00
# include <string.h>
2010-06-29 15:05:40 -07:00
# include <sys/stat.h>
2005-09-16 19:27:50 -07:00
# include <sys/mman.h>
2008-02-08 04:22:08 -08:00
# include <sys/param.h>
2012-10-08 03:27:32 +01:00
# include <init.h>
# include <os.h>
2005-09-16 19:27:50 -07:00
2007-02-10 01:44:18 -08:00
/* Modified by which_tmpdir, which is called during early boot */
2006-04-18 22:21:43 -07:00
static char * default_tmpdir = " /tmp " ;
2007-02-10 01:44:18 -08:00
/*
* Modified when creating the physical memory file and when checking
* the tmp filesystem for usability , both happening during early boot .
*/
2005-09-16 19:27:50 -07:00
static char * tempdir = NULL ;
static void __init find_tempdir ( void )
{
2008-02-04 22:30:41 -08:00
const char * dirs [ ] = { " TMP " , " TEMP " , " TMPDIR " , NULL } ;
2005-09-16 19:27:50 -07:00
int i ;
char * dir = NULL ;
2008-02-08 04:22:08 -08:00
if ( tempdir ! = NULL )
/* We've already been called */
2007-02-10 01:44:21 -08:00
return ;
2008-02-08 04:22:08 -08:00
for ( i = 0 ; dirs [ i ] ; i + + ) {
2005-09-16 19:27:50 -07:00
dir = getenv ( dirs [ i ] ) ;
2008-02-08 04:22:08 -08:00
if ( ( dir ! = NULL ) & & ( * dir ! = ' \0 ' ) )
2005-09-16 19:27:50 -07:00
break ;
}
2008-02-08 04:22:08 -08:00
if ( ( dir = = NULL ) | | ( * dir = = ' \0 ' ) )
2006-04-18 22:21:43 -07:00
dir = default_tmpdir ;
2005-09-16 19:27:50 -07:00
tempdir = malloc ( strlen ( dir ) + 2 ) ;
2008-02-08 04:22:08 -08:00
if ( tempdir = = NULL ) {
2005-09-16 19:27:50 -07:00
fprintf ( stderr , " Failed to malloc tempdir, "
" errno = %d \n " , errno ) ;
return ;
}
strcpy ( tempdir , dir ) ;
strcat ( tempdir , " / " ) ;
}
2013-07-08 16:19:49 -04:00
/*
* Remove bytes from the front of the buffer and refill it so that if there ' s a
* partial string that we care about , it will be completed , and we can recognize
* it .
*/
static int pop ( int fd , char * buf , size_t size , size_t npop )
{
ssize_t n ;
size_t len = strlen ( & buf [ npop ] ) ;
memmove ( buf , & buf [ npop ] , len + 1 ) ;
n = read ( fd , & buf [ len ] , size - len - 1 ) ;
if ( n < 0 )
return - errno ;
buf [ len + n ] = ' \0 ' ;
return 1 ;
}
2008-02-08 04:22:08 -08:00
/*
* This will return 1 , with the first character in buf being the
2006-04-18 22:21:43 -07:00
* character following the next instance of c in the file . This will
* read the file as needed . If there ' s an error , - errno is returned ;
* if the end of the file is reached , 0 is returned .
*/
2008-02-04 22:30:41 -08:00
static int next ( int fd , char * buf , size_t size , char c )
2006-04-18 22:21:43 -07:00
{
2008-02-04 22:30:41 -08:00
ssize_t n ;
2006-04-18 22:21:43 -07:00
char * ptr ;
2008-02-08 04:22:08 -08:00
while ( ( ptr = strchr ( buf , c ) ) = = NULL ) {
2006-04-18 22:21:43 -07:00
n = read ( fd , buf , size - 1 ) ;
2008-02-08 04:22:08 -08:00
if ( n = = 0 )
2006-04-18 22:21:43 -07:00
return 0 ;
2008-02-08 04:22:08 -08:00
else if ( n < 0 )
2006-04-18 22:21:43 -07:00
return - errno ;
buf [ n ] = ' \0 ' ;
}
2013-07-08 16:19:49 -04:00
return pop ( fd , buf , size , ptr - buf + 1 ) ;
}
/*
* Decode an octal - escaped and space - terminated path of the form used by
* / proc / mounts . May be used to decode a path in - place . " out " must be at least
* as large as the input . The output is always null - terminated . " len " gets the
* length of the output , excluding the trailing null . Returns 0 if a full path
* was successfully decoded , otherwise an error .
*/
static int decode_path ( const char * in , char * out , size_t * len )
{
char * first = out ;
int c ;
int i ;
int ret = - EINVAL ;
while ( 1 ) {
switch ( * in ) {
case ' \0 ' :
goto out ;
case ' ' :
ret = 0 ;
goto out ;
case ' \\ ' :
in + + ;
c = 0 ;
for ( i = 0 ; i < 3 ; i + + ) {
if ( * in < ' 0 ' | | * in > ' 7 ' )
goto out ;
c = ( c < < 3 ) | ( * in + + - ' 0 ' ) ;
}
* ( unsigned char * ) out + + = ( unsigned char ) c ;
break ;
default :
* out + + = * in + + ;
break ;
}
}
out :
* out = ' \0 ' ;
* len = out - first ;
return ret ;
}
/*
* Computes the length of s when encoded with three - digit octal escape sequences
* for the characters in chars .
*/
static size_t octal_encoded_length ( const char * s , const char * chars )
{
size_t len = strlen ( s ) ;
while ( ( s = strpbrk ( s , chars ) ) ! = NULL ) {
len + = 3 ;
s + + ;
}
return len ;
}
enum {
OUTCOME_NOTHING_MOUNTED ,
OUTCOME_TMPFS_MOUNT ,
OUTCOME_NON_TMPFS_MOUNT ,
} ;
/* Read a line of /proc/mounts data looking for a tmpfs mount at "path". */
static int read_mount ( int fd , char * buf , size_t bufsize , const char * path ,
int * outcome )
{
int found ;
int match ;
char * space ;
size_t len ;
enum {
MATCH_NONE ,
MATCH_EXACT ,
MATCH_PARENT ,
} ;
found = next ( fd , buf , bufsize , ' ' ) ;
if ( found ! = 1 )
return found ;
2006-06-30 01:55:54 -07:00
2008-02-08 04:22:08 -08:00
/*
2013-07-08 16:19:49 -04:00
* If there ' s no following space in the buffer , then this path is
* truncated , so it can ' t be the one we ' re looking for .
2006-06-30 01:55:54 -07:00
*/
2013-07-08 16:19:49 -04:00
space = strchr ( buf , ' ' ) ;
if ( space ) {
match = MATCH_NONE ;
if ( ! decode_path ( buf , buf , & len ) ) {
if ( ! strcmp ( buf , path ) )
match = MATCH_EXACT ;
else if ( ! strncmp ( buf , path , len )
& & ( path [ len ] = = ' / ' | | ! strcmp ( buf , " / " ) ) )
match = MATCH_PARENT ;
}
2006-06-30 01:55:54 -07:00
2013-07-08 16:19:49 -04:00
found = pop ( fd , buf , bufsize , space - buf + 1 ) ;
if ( found ! = 1 )
return found ;
switch ( match ) {
case MATCH_EXACT :
if ( ! strncmp ( buf , " tmpfs " , strlen ( " tmpfs " ) ) )
* outcome = OUTCOME_TMPFS_MOUNT ;
else
* outcome = OUTCOME_NON_TMPFS_MOUNT ;
break ;
case MATCH_PARENT :
/* This mount obscures any previous ones. */
* outcome = OUTCOME_NOTHING_MOUNTED ;
break ;
}
}
return next ( fd , buf , bufsize , ' \n ' ) ;
2006-04-18 22:21:43 -07:00
}
2007-02-10 01:44:18 -08:00
/* which_tmpdir is called only during early boot */
2006-04-18 22:21:43 -07:00
static int checked_tmpdir = 0 ;
2008-02-08 04:22:08 -08:00
/*
* Look for a tmpfs mounted at / dev / shm . I couldn ' t find a cleaner
2006-04-18 22:21:43 -07:00
* way to do this than to parse / proc / mounts . statfs will return the
* same filesystem magic number and fs id for both / dev and / dev / shm
* when they are both tmpfs , so you can ' t tell if they are different
* filesystems . Also , there seems to be no other way of finding the
* mount point of a filesystem from within it .
*
* If a / dev / shm tmpfs entry is found , then we switch to using it .
* Otherwise , we stay with the default / tmp .
*/
static void which_tmpdir ( void )
{
2013-07-08 16:19:49 -04:00
int fd ;
int found ;
int outcome ;
char * path ;
char * buf ;
size_t bufsize ;
2006-04-18 22:21:43 -07:00
2008-02-08 04:22:08 -08:00
if ( checked_tmpdir )
2006-04-18 22:21:43 -07:00
return ;
checked_tmpdir = 1 ;
printf ( " Checking for tmpfs mount on /dev/shm... " ) ;
2013-07-08 16:19:49 -04:00
path = realpath ( " /dev/shm " , NULL ) ;
if ( ! path ) {
printf ( " failed to check real path, errno = %d \n " , errno ) ;
return ;
}
printf ( " %s... " , path ) ;
/*
* The buffer needs to be able to fit the full octal - escaped path , a
* space , and a trailing null in order to successfully decode it .
*/
bufsize = octal_encoded_length ( path , " \t \n \\ " ) + 2 ;
if ( bufsize < 128 )
bufsize = 128 ;
buf = malloc ( bufsize ) ;
if ( ! buf ) {
printf ( " malloc failed, errno = %d \n " , errno ) ;
goto out ;
}
buf [ 0 ] = ' \0 ' ;
2006-04-18 22:21:43 -07:00
fd = open ( " /proc/mounts " , O_RDONLY ) ;
2008-02-08 04:22:08 -08:00
if ( fd < 0 ) {
2006-04-18 22:21:43 -07:00
printf ( " failed to open /proc/mounts, errno = %d \n " , errno ) ;
2013-07-08 16:19:49 -04:00
goto out1 ;
2006-04-18 22:21:43 -07:00
}
2013-07-08 16:19:49 -04:00
outcome = OUTCOME_NOTHING_MOUNTED ;
2008-02-08 04:22:08 -08:00
while ( 1 ) {
2013-07-08 16:19:49 -04:00
found = read_mount ( fd , buf , bufsize , path , & outcome ) ;
2008-02-08 04:22:08 -08:00
if ( found ! = 1 )
2006-04-18 22:21:43 -07:00
break ;
}
2013-07-08 16:19:49 -04:00
if ( found < 0 ) {
2006-04-18 22:21:43 -07:00
printf ( " read returned errno %d \n " , - found ) ;
2013-07-08 16:19:49 -04:00
} else {
switch ( outcome ) {
case OUTCOME_TMPFS_MOUNT :
printf ( " OK \n " ) ;
default_tmpdir = " /dev/shm " ;
break ;
2006-04-18 22:21:43 -07:00
2013-07-08 16:19:49 -04:00
case OUTCOME_NON_TMPFS_MOUNT :
printf ( " not tmpfs \n " ) ;
break ;
2006-04-18 22:21:43 -07:00
2013-07-08 16:19:49 -04:00
default :
printf ( " nothing mounted on /dev/shm \n " ) ;
break ;
}
2006-04-18 22:21:43 -07:00
}
2013-07-08 16:19:49 -04:00
close ( fd ) ;
out1 :
free ( buf ) ;
out :
free ( path ) ;
2006-04-18 22:21:43 -07:00
}
2008-02-08 04:22:08 -08:00
static int __init make_tempfile ( const char * template , char * * out_tempname ,
int do_unlink )
2005-09-16 19:27:50 -07:00
{
2006-04-10 22:53:39 -07:00
char * tempname ;
2005-09-16 19:27:50 -07:00
int fd ;
2006-04-18 22:21:43 -07:00
which_tmpdir ( ) ;
2006-04-10 22:53:39 -07:00
tempname = malloc ( MAXPATHLEN ) ;
2008-02-08 04:22:09 -08:00
if ( tempname = = NULL )
return - 1 ;
2006-04-10 22:53:39 -07:00
2005-09-16 19:27:50 -07:00
find_tempdir ( ) ;
2008-02-08 04:22:09 -08:00
if ( ( tempdir = = NULL ) | | ( strlen ( tempdir ) > = MAXPATHLEN ) )
2011-07-25 17:12:52 -07:00
goto out ;
2008-02-08 04:22:09 -08:00
2006-04-10 22:53:39 -07:00
if ( template [ 0 ] ! = ' / ' )
2005-09-16 19:27:50 -07:00
strcpy ( tempname , tempdir ) ;
else
2006-04-10 22:53:39 -07:00
tempname [ 0 ] = ' \0 ' ;
2008-02-04 22:30:35 -08:00
strncat ( tempname , template , MAXPATHLEN - 1 - strlen ( tempname ) ) ;
2005-09-16 19:27:50 -07:00
fd = mkstemp ( tempname ) ;
2008-02-08 04:22:08 -08:00
if ( fd < 0 ) {
2005-09-16 19:27:50 -07:00
fprintf ( stderr , " open - cannot create %s: %s \n " , tempname ,
strerror ( errno ) ) ;
2006-04-10 22:53:39 -07:00
goto out ;
2005-09-16 19:27:50 -07:00
}
2008-02-08 04:22:08 -08:00
if ( do_unlink & & ( unlink ( tempname ) < 0 ) ) {
2005-09-16 19:27:50 -07:00
perror ( " unlink " ) ;
2011-07-25 17:12:52 -07:00
goto close ;
2005-09-16 19:27:50 -07:00
}
2008-02-08 04:22:08 -08:00
if ( out_tempname ) {
2006-04-10 22:53:39 -07:00
* out_tempname = tempname ;
2008-02-08 04:22:09 -08:00
} else
2006-04-10 22:53:39 -07:00
free ( tempname ) ;
2007-02-10 01:44:21 -08:00
return fd ;
2011-07-25 17:12:52 -07:00
close :
close ( fd ) ;
2006-04-10 22:53:39 -07:00
out :
free ( tempname ) ;
return - 1 ;
2005-09-16 19:27:50 -07:00
}
# define TEMPNAME_TEMPLATE "vm_file-XXXXXX"
2008-02-08 04:22:08 -08:00
static int __init create_tmp_file ( unsigned long long len )
2005-09-16 19:27:50 -07:00
{
int fd , err ;
char zero ;
fd = make_tempfile ( TEMPNAME_TEMPLATE , NULL , 1 ) ;
2008-02-08 04:22:08 -08:00
if ( fd < 0 )
2005-09-16 19:27:50 -07:00
exit ( 1 ) ;
err = fchmod ( fd , 0777 ) ;
2008-02-08 04:22:08 -08:00
if ( err < 0 ) {
2007-10-16 01:27:11 -07:00
perror ( " fchmod " ) ;
2005-09-16 19:27:50 -07:00
exit ( 1 ) ;
}
2008-02-08 04:22:08 -08:00
/*
* Seek to len - 1 because writing a character there will
2006-06-30 01:55:55 -07:00
* increase the file size by one byte , to the desired length .
*/
if ( lseek64 ( fd , len - 1 , SEEK_SET ) < 0 ) {
2007-10-16 01:27:11 -07:00
perror ( " lseek64 " ) ;
2005-09-16 19:27:50 -07:00
exit ( 1 ) ;
}
zero = 0 ;
2007-05-06 14:51:35 -07:00
err = write ( fd , & zero , 1 ) ;
2008-02-08 04:22:08 -08:00
if ( err ! = 1 ) {
2007-05-06 14:51:35 -07:00
perror ( " write " ) ;
2005-09-16 19:27:50 -07:00
exit ( 1 ) ;
}
2007-02-10 01:44:21 -08:00
return fd ;
2005-09-16 19:27:50 -07:00
}
2007-05-06 14:51:11 -07:00
int __init create_mem_file ( unsigned long long len )
2005-09-16 19:27:50 -07:00
{
int err , fd ;
2006-03-31 02:30:08 -08:00
fd = create_tmp_file ( len ) ;
2005-09-16 19:27:50 -07:00
2007-10-16 01:27:11 -07:00
err = os_set_exec_close ( fd ) ;
2008-02-08 04:22:08 -08:00
if ( err < 0 ) {
2005-09-16 19:27:50 -07:00
errno = - err ;
perror ( " exec_close " ) ;
}
2007-02-10 01:44:21 -08:00
return fd ;
2005-09-16 19:27:50 -07:00
}
2006-04-18 22:21:43 -07:00
2007-05-06 14:51:11 -07:00
void __init check_tmpexec ( void )
2006-04-18 22:21:43 -07:00
{
void * addr ;
int err , fd = create_tmp_file ( UM_KERN_PAGE_SIZE ) ;
addr = mmap ( NULL , UM_KERN_PAGE_SIZE ,
PROT_READ | PROT_WRITE | PROT_EXEC , MAP_PRIVATE , fd , 0 ) ;
printf ( " Checking PROT_EXEC mmap in %s... " , tempdir ) ;
fflush ( stdout ) ;
2008-02-08 04:22:08 -08:00
if ( addr = = MAP_FAILED ) {
2006-04-18 22:21:43 -07:00
err = errno ;
perror ( " failed " ) ;
2008-02-04 22:30:35 -08:00
close ( fd ) ;
2008-02-08 04:22:08 -08:00
if ( err = = EPERM )
2006-04-18 22:21:43 -07:00
printf ( " %s must be not mounted noexec \n " , tempdir ) ;
exit ( 1 ) ;
}
printf ( " OK \n " ) ;
munmap ( addr , UM_KERN_PAGE_SIZE ) ;
close ( fd ) ;
}