2008-02-08 15:22:08 +03:00
/*
* Copyright ( C ) 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
* Licensed under the GPL
*/
2005-09-17 06:27:50 +04:00
# include <stdio.h>
# include <stddef.h>
2008-02-08 15:22:08 +03:00
# include <stdlib.h>
2005-09-17 06:27:50 +04:00
# include <unistd.h>
# include <errno.h>
# include <fcntl.h>
2008-02-08 15:22:08 +03:00
# include <string.h>
2010-06-30 02:05:40 +04:00
# include <sys/stat.h>
2005-09-17 06:27:50 +04:00
# include <sys/mman.h>
2008-02-08 15:22:08 +03:00
# include <sys/param.h>
2012-10-08 06:27:32 +04:00
# include <init.h>
# include <os.h>
2005-09-17 06:27:50 +04:00
2007-02-10 12:44:18 +03:00
/* Modified by which_tmpdir, which is called during early boot */
2006-04-19 09:21:43 +04:00
static char * default_tmpdir = " /tmp " ;
2007-02-10 12:44:18 +03:00
/*
* Modified when creating the physical memory file and when checking
* the tmp filesystem for usability , both happening during early boot .
*/
2005-09-17 06:27:50 +04:00
static char * tempdir = NULL ;
static void __init find_tempdir ( void )
{
2008-02-05 09:30:41 +03:00
const char * dirs [ ] = { " TMP " , " TEMP " , " TMPDIR " , NULL } ;
2005-09-17 06:27:50 +04:00
int i ;
char * dir = NULL ;
2008-02-08 15:22:08 +03:00
if ( tempdir ! = NULL )
/* We've already been called */
2007-02-10 12:44:21 +03:00
return ;
2008-02-08 15:22:08 +03:00
for ( i = 0 ; dirs [ i ] ; i + + ) {
2005-09-17 06:27:50 +04:00
dir = getenv ( dirs [ i ] ) ;
2008-02-08 15:22:08 +03:00
if ( ( dir ! = NULL ) & & ( * dir ! = ' \0 ' ) )
2005-09-17 06:27:50 +04:00
break ;
}
2008-02-08 15:22:08 +03:00
if ( ( dir = = NULL ) | | ( * dir = = ' \0 ' ) )
2006-04-19 09:21:43 +04:00
dir = default_tmpdir ;
2005-09-17 06:27:50 +04:00
tempdir = malloc ( strlen ( dir ) + 2 ) ;
2008-02-08 15:22:08 +03:00
if ( tempdir = = NULL ) {
2005-09-17 06:27:50 +04:00
fprintf ( stderr , " Failed to malloc tempdir, "
" errno = %d \n " , errno ) ;
return ;
}
strcpy ( tempdir , dir ) ;
strcat ( tempdir , " / " ) ;
}
2008-02-08 15:22:08 +03:00
/*
* This will return 1 , with the first character in buf being the
2006-04-19 09:21:43 +04: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-05 09:30:41 +03:00
static int next ( int fd , char * buf , size_t size , char c )
2006-04-19 09:21:43 +04:00
{
2008-02-05 09:30:41 +03:00
ssize_t n ;
size_t len ;
2006-04-19 09:21:43 +04:00
char * ptr ;
2008-02-08 15:22:08 +03:00
while ( ( ptr = strchr ( buf , c ) ) = = NULL ) {
2006-04-19 09:21:43 +04:00
n = read ( fd , buf , size - 1 ) ;
2008-02-08 15:22:08 +03:00
if ( n = = 0 )
2006-04-19 09:21:43 +04:00
return 0 ;
2008-02-08 15:22:08 +03:00
else if ( n < 0 )
2006-04-19 09:21:43 +04:00
return - errno ;
buf [ n ] = ' \0 ' ;
}
ptr + + ;
2006-06-30 12:55:54 +04:00
len = strlen ( ptr ) ;
memmove ( buf , ptr , len + 1 ) ;
2008-02-08 15:22:08 +03:00
/*
* Refill the buffer so that if there ' s a partial string that we care
2006-06-30 12:55:54 +04:00
* about , it will be completed , and we can recognize it .
*/
n = read ( fd , & buf [ len ] , size - len - 1 ) ;
2008-02-08 15:22:08 +03:00
if ( n < 0 )
2006-06-30 12:55:54 +04:00
return - errno ;
buf [ len + n ] = ' \0 ' ;
2006-04-19 09:21:43 +04:00
return 1 ;
}
2007-02-10 12:44:18 +03:00
/* which_tmpdir is called only during early boot */
2006-04-19 09:21:43 +04:00
static int checked_tmpdir = 0 ;
2008-02-08 15:22:08 +03:00
/*
* Look for a tmpfs mounted at / dev / shm . I couldn ' t find a cleaner
2006-04-19 09:21:43 +04: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 )
{
int fd , found ;
char buf [ 128 ] = { ' \0 ' } ;
2008-02-08 15:22:08 +03:00
if ( checked_tmpdir )
2006-04-19 09:21:43 +04:00
return ;
checked_tmpdir = 1 ;
printf ( " Checking for tmpfs mount on /dev/shm... " ) ;
fd = open ( " /proc/mounts " , O_RDONLY ) ;
2008-02-08 15:22:08 +03:00
if ( fd < 0 ) {
2006-04-19 09:21:43 +04:00
printf ( " failed to open /proc/mounts, errno = %d \n " , errno ) ;
return ;
}
2008-02-08 15:22:08 +03:00
while ( 1 ) {
2006-09-26 10:33:00 +04:00
found = next ( fd , buf , ARRAY_SIZE ( buf ) , ' ' ) ;
2008-02-08 15:22:08 +03:00
if ( found ! = 1 )
2006-04-19 09:21:43 +04:00
break ;
2008-02-08 15:22:08 +03:00
if ( ! strncmp ( buf , " /dev/shm " , strlen ( " /dev/shm " ) ) )
2006-04-19 09:21:43 +04:00
goto found ;
2006-09-26 10:33:00 +04:00
found = next ( fd , buf , ARRAY_SIZE ( buf ) , ' \n ' ) ;
2008-02-08 15:22:08 +03:00
if ( found ! = 1 )
2006-04-19 09:21:43 +04:00
break ;
}
err :
2008-02-08 15:22:08 +03:00
if ( found = = 0 )
2006-04-19 09:21:43 +04:00
printf ( " nothing mounted on /dev/shm \n " ) ;
2008-02-08 15:22:08 +03:00
else if ( found < 0 )
2006-04-19 09:21:43 +04:00
printf ( " read returned errno %d \n " , - found ) ;
2006-09-29 12:58:51 +04:00
out :
close ( fd ) ;
2006-04-19 09:21:43 +04:00
return ;
found :
2006-09-26 10:33:00 +04:00
found = next ( fd , buf , ARRAY_SIZE ( buf ) , ' ' ) ;
2008-02-08 15:22:08 +03:00
if ( found ! = 1 )
2006-04-19 09:21:43 +04:00
goto err ;
2008-02-08 15:22:08 +03:00
if ( strncmp ( buf , " tmpfs " , strlen ( " tmpfs " ) ) ) {
2006-04-19 09:21:43 +04:00
printf ( " not tmpfs \n " ) ;
2006-09-29 12:58:51 +04:00
goto out ;
2006-04-19 09:21:43 +04:00
}
printf ( " OK \n " ) ;
default_tmpdir = " /dev/shm " ;
2006-09-29 12:58:51 +04:00
goto out ;
2006-04-19 09:21:43 +04:00
}
2008-02-08 15:22:08 +03:00
static int __init make_tempfile ( const char * template , char * * out_tempname ,
int do_unlink )
2005-09-17 06:27:50 +04:00
{
2006-04-11 09:53:39 +04:00
char * tempname ;
2005-09-17 06:27:50 +04:00
int fd ;
2006-04-19 09:21:43 +04:00
which_tmpdir ( ) ;
2006-04-11 09:53:39 +04:00
tempname = malloc ( MAXPATHLEN ) ;
2008-02-08 15:22:09 +03:00
if ( tempname = = NULL )
return - 1 ;
2006-04-11 09:53:39 +04:00
2005-09-17 06:27:50 +04:00
find_tempdir ( ) ;
2008-02-08 15:22:09 +03:00
if ( ( tempdir = = NULL ) | | ( strlen ( tempdir ) > = MAXPATHLEN ) )
2011-07-26 04:12:52 +04:00
goto out ;
2008-02-08 15:22:09 +03:00
2006-04-11 09:53:39 +04:00
if ( template [ 0 ] ! = ' / ' )
2005-09-17 06:27:50 +04:00
strcpy ( tempname , tempdir ) ;
else
2006-04-11 09:53:39 +04:00
tempname [ 0 ] = ' \0 ' ;
2008-02-05 09:30:35 +03:00
strncat ( tempname , template , MAXPATHLEN - 1 - strlen ( tempname ) ) ;
2005-09-17 06:27:50 +04:00
fd = mkstemp ( tempname ) ;
2008-02-08 15:22:08 +03:00
if ( fd < 0 ) {
2005-09-17 06:27:50 +04:00
fprintf ( stderr , " open - cannot create %s: %s \n " , tempname ,
strerror ( errno ) ) ;
2006-04-11 09:53:39 +04:00
goto out ;
2005-09-17 06:27:50 +04:00
}
2008-02-08 15:22:08 +03:00
if ( do_unlink & & ( unlink ( tempname ) < 0 ) ) {
2005-09-17 06:27:50 +04:00
perror ( " unlink " ) ;
2011-07-26 04:12:52 +04:00
goto close ;
2005-09-17 06:27:50 +04:00
}
2008-02-08 15:22:08 +03:00
if ( out_tempname ) {
2006-04-11 09:53:39 +04:00
* out_tempname = tempname ;
2008-02-08 15:22:09 +03:00
} else
2006-04-11 09:53:39 +04:00
free ( tempname ) ;
2007-02-10 12:44:21 +03:00
return fd ;
2011-07-26 04:12:52 +04:00
close :
close ( fd ) ;
2006-04-11 09:53:39 +04:00
out :
free ( tempname ) ;
return - 1 ;
2005-09-17 06:27:50 +04:00
}
# define TEMPNAME_TEMPLATE "vm_file-XXXXXX"
2008-02-08 15:22:08 +03:00
static int __init create_tmp_file ( unsigned long long len )
2005-09-17 06:27:50 +04:00
{
int fd , err ;
char zero ;
fd = make_tempfile ( TEMPNAME_TEMPLATE , NULL , 1 ) ;
2008-02-08 15:22:08 +03:00
if ( fd < 0 )
2005-09-17 06:27:50 +04:00
exit ( 1 ) ;
err = fchmod ( fd , 0777 ) ;
2008-02-08 15:22:08 +03:00
if ( err < 0 ) {
2007-10-16 12:27:11 +04:00
perror ( " fchmod " ) ;
2005-09-17 06:27:50 +04:00
exit ( 1 ) ;
}
2008-02-08 15:22:08 +03:00
/*
* Seek to len - 1 because writing a character there will
2006-06-30 12:55:55 +04:00
* increase the file size by one byte , to the desired length .
*/
if ( lseek64 ( fd , len - 1 , SEEK_SET ) < 0 ) {
2007-10-16 12:27:11 +04:00
perror ( " lseek64 " ) ;
2005-09-17 06:27:50 +04:00
exit ( 1 ) ;
}
zero = 0 ;
2007-05-07 01:51:35 +04:00
err = write ( fd , & zero , 1 ) ;
2008-02-08 15:22:08 +03:00
if ( err ! = 1 ) {
2007-05-07 01:51:35 +04:00
perror ( " write " ) ;
2005-09-17 06:27:50 +04:00
exit ( 1 ) ;
}
2007-02-10 12:44:21 +03:00
return fd ;
2005-09-17 06:27:50 +04:00
}
2007-05-07 01:51:11 +04:00
int __init create_mem_file ( unsigned long long len )
2005-09-17 06:27:50 +04:00
{
int err , fd ;
2006-03-31 14:30:08 +04:00
fd = create_tmp_file ( len ) ;
2005-09-17 06:27:50 +04:00
2007-10-16 12:27:11 +04:00
err = os_set_exec_close ( fd ) ;
2008-02-08 15:22:08 +03:00
if ( err < 0 ) {
2005-09-17 06:27:50 +04:00
errno = - err ;
perror ( " exec_close " ) ;
}
2007-02-10 12:44:21 +03:00
return fd ;
2005-09-17 06:27:50 +04:00
}
2006-04-19 09:21:43 +04:00
2007-05-07 01:51:11 +04:00
void __init check_tmpexec ( void )
2006-04-19 09:21:43 +04: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 15:22:08 +03:00
if ( addr = = MAP_FAILED ) {
2006-04-19 09:21:43 +04:00
err = errno ;
perror ( " failed " ) ;
2008-02-05 09:30:35 +03:00
close ( fd ) ;
2008-02-08 15:22:08 +03:00
if ( err = = EPERM )
2006-04-19 09:21:43 +04:00
printf ( " %s must be not mounted noexec \n " , tempdir ) ;
exit ( 1 ) ;
}
printf ( " OK \n " ) ;
munmap ( addr , UM_KERN_PAGE_SIZE ) ;
close ( fd ) ;
}