// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2002 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <init.h>
#include <os.h>
#define UML_DIR "~/.uml/"
#define UMID_LEN 64
/* Changed by set_umid, which is run early in boot */
static char umid[UMID_LEN] = { 0 };
/* 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' };
int len, err;
if (*uml_dir == '~' ) {
char *home = getenv("HOME" );
err = -ENOENT;
if (home == NULL) {
printk(UM_KERN_ERR
"%s: no value in environment for $HOME\n" ,
__func__);
goto err;
}
strscpy(dir, home);
uml_dir++;
}
strlcat(dir, uml_dir, sizeof (dir));
len = strlen(dir);
if (len > 0 && dir[len - 1 ] != '/' )
strlcat(dir, "/" , sizeof (dir));
err = -ENOMEM;
uml_dir = malloc(strlen(dir) + 1 );
if (uml_dir == NULL) {
printk(UM_KERN_ERR "%s : malloc failed, errno = %d\n" ,
__func__, errno);
goto err;
}
strcpy(uml_dir, dir);
if ((mkdir(uml_dir, 0777 ) < 0 ) && (errno != EEXIST)) {
printk(UM_KERN_ERR "Failed to mkdir '%s': %s\n" ,
uml_dir, strerror(errno));
err = -errno;
goto err_free;
}
return 0 ;
err_free:
free(uml_dir);
err:
uml_dir = NULL;
return err;
}
/*
* 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
* ignore ENOENT errors for anything ( they happen , strangely enough - possibly
* due to races between multiple dying UML threads ) .
*/
static int remove_files_and_dir(char *dir)
{
DIR *directory;
struct dirent *ent;
int len;
char file[256 ];
int ret;
directory = opendir(dir);
if (directory == NULL) {
if (errno != ENOENT)
return -errno;
else
return 0 ;
}
while ((ent = readdir(directory)) != NULL) {
if (!strcmp(ent->d_name, "." ) || !strcmp(ent->d_name, ".." ))
continue ;
len = strlen(dir) + strlen("/" ) + strlen(ent->d_name) + 1 ;
if (len > sizeof (file)) {
ret = -E2BIG;
goto out;
}
sprintf(file, "%s/%s" , dir, ent->d_name);
if (unlink(file) < 0 && errno != ENOENT) {
ret = -errno;
goto out;
}
}
if (rmdir(dir) < 0 && errno != ENOENT) {
ret = -errno;
goto out;
}
ret = 0 ;
out:
closedir(directory);
return ret;
}
/*
* This says that there isn ' t already a user of the specified directory even if
* 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 .
*
* Boolean return : 1 if in use , 0 otherwise .
*/
static inline int is_umdir_used(char *dir)
{
char pid[sizeof ("nnnnnnnnn" )], *end, *file;
int fd, p, n, err;
size_t filelen = strlen(dir) + sizeof ("/pid" ) + 1 ;
file = malloc(filelen);
if (!file)
return -ENOMEM;
snprintf(file, filelen, "%s/pid" , dir);
fd = open(file, O_RDONLY);
if (fd < 0 ) {
fd = -errno;
if (fd != -ENOENT) {
printk(UM_KERN_ERR "is_umdir_used : couldn't open pid "
"file '%s', err = %d\n" , file, -fd);
}
goto out;
}
err = 0 ;
n = read(fd, pid, sizeof (pid));
if (n < 0 ) {
printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
"'%s', err = %d\n" , file, errno);
goto out_close;
} else if (n == 0 ) {
printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
"'%s', 0-byte read\n" , file);
goto out_close;
}
p = strtoul(pid, &end, 0 );
if (end == pid) {
printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file "
"'%s', errno = %d\n" , file, errno);
goto out_close;
}
if ((kill(p, 0 ) == 0 ) || (errno != ESRCH)) {
printk(UM_KERN_ERR "umid \" %s\" is already in use by pid %d\n" ,
umid, p);
return 1 ;
}
out_close:
close(fd);
out:
free(file);
return 0 ;
}
/*
* 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;
ret = remove_files_and_dir(dir);
if (ret) {
printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir "
"failed with err = %d\n" , ret);
}
return ret;
}
static void __init create_pid_file(void )
{
char pid[sizeof ("nnnnnnnnn" )], *file;
int fd, n;
n = strlen(uml_dir) + UMID_LEN + sizeof ("/pid" );
file = malloc(n);
if (!file)
return ;
if (umid_file_name("pid" , file, n))
goto out;
fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644 );
if (fd < 0 ) {
printk(UM_KERN_ERR "Open of machine pid file \" %s\" failed: "
"%s\n" , file, strerror(errno));
goto out;
}
snprintf(pid, sizeof (pid), "%d\n" , getpid());
n = write(fd, pid, strlen(pid));
if (n != strlen(pid))
printk(UM_KERN_ERR "Write of pid file failed - err = %d\n" ,
errno);
close(fd);
out:
free(file);
}
int __init set_umid(char *name)
{
if (strlen(name) > UMID_LEN - 1 )
return -E2BIG;
strscpy(umid, name);
return 0 ;
}
/* Changed in make_umid, which is called during early boot */
static int umid_setup = 0 ;
static int __init make_umid(void )
{
int fd, err;
char tmp[256 ];
if (umid_setup)
return 0 ;
make_uml_dir();
if (*umid == '\0' ) {
strscpy(tmp, uml_dir);
strlcat(tmp, "XXXXXX" , sizeof (tmp));
fd = mkstemp(tmp);
if (fd < 0 ) {
printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: "
"%s\n" , tmp, strerror(errno));
err = -errno;
goto err;
}
close(fd);
set_umid(&tmp[strlen(uml_dir)]);
/*
* There ' s a nice tiny little race between this unlink and
* the mkdir below . It ' d be nice if there were a mkstemp
* for directories .
*/
if (unlink(tmp)) {
err = -errno;
goto err;
}
}
snprintf(tmp, sizeof (tmp), "%s%s" , uml_dir, umid);
err = mkdir(tmp, 0777 );
if (err < 0 ) {
err = -errno;
if (err != -EEXIST)
goto err;
if (umdir_take_if_dead(tmp) < 0 )
goto err;
err = mkdir(tmp, 0777 );
}
if (err) {
err = -errno;
printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n" , umid,
errno);
goto err;
}
umid_setup = 1 ;
create_pid_file();
err = 0 ;
err:
return err;
}
static int __init make_umid_init(void )
{
if (!make_umid())
return 0 ;
/*
* If initializing with the given umid failed , then try again with
* a random one .
*/
printk(UM_KERN_ERR "Failed to initialize umid \" %s\", trying with a "
"random umid\n" , umid);
*umid = '\0' ;
make_umid();
return 0 ;
}
__initcall(make_umid_init);
int __init umid_file_name(char *name, char *buf, int len)
{
int n, err;
err = make_umid();
if (err)
return err;
n = snprintf(buf, len, "%s%s/%s" , uml_dir, umid, name);
if (n >= len) {
printk(UM_KERN_ERR "umid_file_name : buffer too short\n" );
return -E2BIG;
}
return 0 ;
}
char *get_umid(void )
{
return umid;
}
static int __init set_uml_dir(char *name, int *add)
{
*add = 0 ;
if (*name == '\0' ) {
os_warn("uml_dir can't be an empty string\n" );
return 0 ;
}
if (name[strlen(name) - 1 ] == '/' ) {
uml_dir = name;
return 0 ;
}
uml_dir = malloc(strlen(name) + 2 );
if (uml_dir == NULL) {
os_warn("Failed to malloc uml_dir - error = %d\n" , errno);
/*
* Return 0 here because do_initcalls doesn ' t look at
* the return value .
*/
return 0 ;
}
sprintf(uml_dir, "%s/" , name);
return 0 ;
}
__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 )
{
char *dir, err;
dir = malloc(strlen(uml_dir) + UMID_LEN + 1 );
if (!dir)
return ;
sprintf(dir, "%s%s" , uml_dir, umid);
err = remove_files_and_dir(dir);
if (err)
os_warn("%s - remove_files_and_dir failed with err = %d\n" ,
__func__, err);
free(dir);
}
__uml_exitcall(remove_umid_dir);
Messung V0.5 in Prozent C=95 H=94 G=94
¤ Dauer der Verarbeitung: 0.7 Sekunden
¤
*© Formatika GbR, Deutschland