162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <stdio.h> 762306a36Sopenharmony_ci#include <stdlib.h> 862306a36Sopenharmony_ci#include <dirent.h> 962306a36Sopenharmony_ci#include <errno.h> 1062306a36Sopenharmony_ci#include <fcntl.h> 1162306a36Sopenharmony_ci#include <signal.h> 1262306a36Sopenharmony_ci#include <string.h> 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <sys/stat.h> 1562306a36Sopenharmony_ci#include <init.h> 1662306a36Sopenharmony_ci#include <os.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define UML_DIR "~/.uml/" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define UMID_LEN 64 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Changed by set_umid, which is run early in boot */ 2362306a36Sopenharmony_cistatic char umid[UMID_LEN] = { 0 }; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */ 2662306a36Sopenharmony_cistatic char *uml_dir = UML_DIR; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int __init make_uml_dir(void) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci char dir[512] = { '\0' }; 3162306a36Sopenharmony_ci int len, err; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (*uml_dir == '~') { 3462306a36Sopenharmony_ci char *home = getenv("HOME"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci err = -ENOENT; 3762306a36Sopenharmony_ci if (home == NULL) { 3862306a36Sopenharmony_ci printk(UM_KERN_ERR 3962306a36Sopenharmony_ci "%s: no value in environment for $HOME\n", 4062306a36Sopenharmony_ci __func__); 4162306a36Sopenharmony_ci goto err; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci strscpy(dir, home, sizeof(dir)); 4462306a36Sopenharmony_ci uml_dir++; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci strlcat(dir, uml_dir, sizeof(dir)); 4762306a36Sopenharmony_ci len = strlen(dir); 4862306a36Sopenharmony_ci if (len > 0 && dir[len - 1] != '/') 4962306a36Sopenharmony_ci strlcat(dir, "/", sizeof(dir)); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci err = -ENOMEM; 5262306a36Sopenharmony_ci uml_dir = malloc(strlen(dir) + 1); 5362306a36Sopenharmony_ci if (uml_dir == NULL) { 5462306a36Sopenharmony_ci printk(UM_KERN_ERR "%s : malloc failed, errno = %d\n", 5562306a36Sopenharmony_ci __func__, errno); 5662306a36Sopenharmony_ci goto err; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci strcpy(uml_dir, dir); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if ((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)) { 6162306a36Sopenharmony_ci printk(UM_KERN_ERR "Failed to mkdir '%s': %s\n", 6262306a36Sopenharmony_ci uml_dir, strerror(errno)); 6362306a36Sopenharmony_ci err = -errno; 6462306a36Sopenharmony_ci goto err_free; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cierr_free: 6962306a36Sopenharmony_ci free(uml_dir); 7062306a36Sopenharmony_cierr: 7162306a36Sopenharmony_ci uml_dir = NULL; 7262306a36Sopenharmony_ci return err; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * Unlinks the files contained in @dir and then removes @dir. 7762306a36Sopenharmony_ci * Doesn't handle directory trees, so it's not like rm -rf, but almost such. We 7862306a36Sopenharmony_ci * ignore ENOENT errors for anything (they happen, strangely enough - possibly 7962306a36Sopenharmony_ci * due to races between multiple dying UML threads). 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic int remove_files_and_dir(char *dir) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci DIR *directory; 8462306a36Sopenharmony_ci struct dirent *ent; 8562306a36Sopenharmony_ci int len; 8662306a36Sopenharmony_ci char file[256]; 8762306a36Sopenharmony_ci int ret; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci directory = opendir(dir); 9062306a36Sopenharmony_ci if (directory == NULL) { 9162306a36Sopenharmony_ci if (errno != ENOENT) 9262306a36Sopenharmony_ci return -errno; 9362306a36Sopenharmony_ci else 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci while ((ent = readdir(directory)) != NULL) { 9862306a36Sopenharmony_ci if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) 9962306a36Sopenharmony_ci continue; 10062306a36Sopenharmony_ci len = strlen(dir) + strlen("/") + strlen(ent->d_name) + 1; 10162306a36Sopenharmony_ci if (len > sizeof(file)) { 10262306a36Sopenharmony_ci ret = -E2BIG; 10362306a36Sopenharmony_ci goto out; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci sprintf(file, "%s/%s", dir, ent->d_name); 10762306a36Sopenharmony_ci if (unlink(file) < 0 && errno != ENOENT) { 10862306a36Sopenharmony_ci ret = -errno; 10962306a36Sopenharmony_ci goto out; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (rmdir(dir) < 0 && errno != ENOENT) { 11462306a36Sopenharmony_ci ret = -errno; 11562306a36Sopenharmony_ci goto out; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ret = 0; 11962306a36Sopenharmony_ciout: 12062306a36Sopenharmony_ci closedir(directory); 12162306a36Sopenharmony_ci return ret; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * This says that there isn't already a user of the specified directory even if 12662306a36Sopenharmony_ci * there are errors during the checking. This is because if these errors 12762306a36Sopenharmony_ci * happen, the directory is unusable by the pre-existing UML, so we might as 12862306a36Sopenharmony_ci * well take it over. This could happen either by 12962306a36Sopenharmony_ci * the existing UML somehow corrupting its umid directory 13062306a36Sopenharmony_ci * something other than UML sticking stuff in the directory 13162306a36Sopenharmony_ci * this boot racing with a shutdown of the other UML 13262306a36Sopenharmony_ci * In any of these cases, the directory isn't useful for anything else. 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * Boolean return: 1 if in use, 0 otherwise. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_cistatic inline int is_umdir_used(char *dir) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci char pid[sizeof("nnnnnnnnn")], *end, *file; 13962306a36Sopenharmony_ci int fd, p, n, err; 14062306a36Sopenharmony_ci size_t filelen = strlen(dir) + sizeof("/pid") + 1; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci file = malloc(filelen); 14362306a36Sopenharmony_ci if (!file) 14462306a36Sopenharmony_ci return -ENOMEM; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci snprintf(file, filelen, "%s/pid", dir); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci fd = open(file, O_RDONLY); 14962306a36Sopenharmony_ci if (fd < 0) { 15062306a36Sopenharmony_ci fd = -errno; 15162306a36Sopenharmony_ci if (fd != -ENOENT) { 15262306a36Sopenharmony_ci printk(UM_KERN_ERR "is_umdir_used : couldn't open pid " 15362306a36Sopenharmony_ci "file '%s', err = %d\n", file, -fd); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci goto out; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci err = 0; 15962306a36Sopenharmony_ci n = read(fd, pid, sizeof(pid)); 16062306a36Sopenharmony_ci if (n < 0) { 16162306a36Sopenharmony_ci printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file " 16262306a36Sopenharmony_ci "'%s', err = %d\n", file, errno); 16362306a36Sopenharmony_ci goto out_close; 16462306a36Sopenharmony_ci } else if (n == 0) { 16562306a36Sopenharmony_ci printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file " 16662306a36Sopenharmony_ci "'%s', 0-byte read\n", file); 16762306a36Sopenharmony_ci goto out_close; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci p = strtoul(pid, &end, 0); 17162306a36Sopenharmony_ci if (end == pid) { 17262306a36Sopenharmony_ci printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file " 17362306a36Sopenharmony_ci "'%s', errno = %d\n", file, errno); 17462306a36Sopenharmony_ci goto out_close; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if ((kill(p, 0) == 0) || (errno != ESRCH)) { 17862306a36Sopenharmony_ci printk(UM_KERN_ERR "umid \"%s\" is already in use by pid %d\n", 17962306a36Sopenharmony_ci umid, p); 18062306a36Sopenharmony_ci return 1; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ciout_close: 18462306a36Sopenharmony_ci close(fd); 18562306a36Sopenharmony_ciout: 18662306a36Sopenharmony_ci free(file); 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * Try to remove the directory @dir unless it's in use. 19262306a36Sopenharmony_ci * Precondition: @dir exists. 19362306a36Sopenharmony_ci * Returns 0 for success, < 0 for failure in removal or if the directory is in 19462306a36Sopenharmony_ci * use. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic int umdir_take_if_dead(char *dir) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci int ret; 19962306a36Sopenharmony_ci if (is_umdir_used(dir)) 20062306a36Sopenharmony_ci return -EEXIST; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = remove_files_and_dir(dir); 20362306a36Sopenharmony_ci if (ret) { 20462306a36Sopenharmony_ci printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir " 20562306a36Sopenharmony_ci "failed with err = %d\n", ret); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void __init create_pid_file(void) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci char pid[sizeof("nnnnnnnnn")], *file; 21362306a36Sopenharmony_ci int fd, n; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci n = strlen(uml_dir) + UMID_LEN + sizeof("/pid"); 21662306a36Sopenharmony_ci file = malloc(n); 21762306a36Sopenharmony_ci if (!file) 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (umid_file_name("pid", file, n)) 22162306a36Sopenharmony_ci goto out; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644); 22462306a36Sopenharmony_ci if (fd < 0) { 22562306a36Sopenharmony_ci printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: " 22662306a36Sopenharmony_ci "%s\n", file, strerror(errno)); 22762306a36Sopenharmony_ci goto out; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci snprintf(pid, sizeof(pid), "%d\n", getpid()); 23162306a36Sopenharmony_ci n = write(fd, pid, strlen(pid)); 23262306a36Sopenharmony_ci if (n != strlen(pid)) 23362306a36Sopenharmony_ci printk(UM_KERN_ERR "Write of pid file failed - err = %d\n", 23462306a36Sopenharmony_ci errno); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci close(fd); 23762306a36Sopenharmony_ciout: 23862306a36Sopenharmony_ci free(file); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciint __init set_umid(char *name) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci if (strlen(name) > UMID_LEN - 1) 24462306a36Sopenharmony_ci return -E2BIG; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci strscpy(umid, name, sizeof(umid)); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/* Changed in make_umid, which is called during early boot */ 25262306a36Sopenharmony_cistatic int umid_setup = 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int __init make_umid(void) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int fd, err; 25762306a36Sopenharmony_ci char tmp[256]; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (umid_setup) 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci make_uml_dir(); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (*umid == '\0') { 26562306a36Sopenharmony_ci strscpy(tmp, uml_dir, sizeof(tmp)); 26662306a36Sopenharmony_ci strlcat(tmp, "XXXXXX", sizeof(tmp)); 26762306a36Sopenharmony_ci fd = mkstemp(tmp); 26862306a36Sopenharmony_ci if (fd < 0) { 26962306a36Sopenharmony_ci printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: " 27062306a36Sopenharmony_ci "%s\n", tmp, strerror(errno)); 27162306a36Sopenharmony_ci err = -errno; 27262306a36Sopenharmony_ci goto err; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci close(fd); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci set_umid(&tmp[strlen(uml_dir)]); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * There's a nice tiny little race between this unlink and 28162306a36Sopenharmony_ci * the mkdir below. It'd be nice if there were a mkstemp 28262306a36Sopenharmony_ci * for directories. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci if (unlink(tmp)) { 28562306a36Sopenharmony_ci err = -errno; 28662306a36Sopenharmony_ci goto err; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid); 29162306a36Sopenharmony_ci err = mkdir(tmp, 0777); 29262306a36Sopenharmony_ci if (err < 0) { 29362306a36Sopenharmony_ci err = -errno; 29462306a36Sopenharmony_ci if (err != -EEXIST) 29562306a36Sopenharmony_ci goto err; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (umdir_take_if_dead(tmp) < 0) 29862306a36Sopenharmony_ci goto err; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci err = mkdir(tmp, 0777); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci if (err) { 30362306a36Sopenharmony_ci err = -errno; 30462306a36Sopenharmony_ci printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid, 30562306a36Sopenharmony_ci errno); 30662306a36Sopenharmony_ci goto err; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci umid_setup = 1; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci create_pid_file(); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci err = 0; 31462306a36Sopenharmony_ci err: 31562306a36Sopenharmony_ci return err; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int __init make_umid_init(void) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci if (!make_umid()) 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * If initializing with the given umid failed, then try again with 32562306a36Sopenharmony_ci * a random one. 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a " 32862306a36Sopenharmony_ci "random umid\n", umid); 32962306a36Sopenharmony_ci *umid = '\0'; 33062306a36Sopenharmony_ci make_umid(); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci__initcall(make_umid_init); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ciint __init umid_file_name(char *name, char *buf, int len) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci int n, err; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci err = make_umid(); 34262306a36Sopenharmony_ci if (err) 34362306a36Sopenharmony_ci return err; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name); 34662306a36Sopenharmony_ci if (n >= len) { 34762306a36Sopenharmony_ci printk(UM_KERN_ERR "umid_file_name : buffer too short\n"); 34862306a36Sopenharmony_ci return -E2BIG; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cichar *get_umid(void) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci return umid; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int __init set_uml_dir(char *name, int *add) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci if (*name == '\0') { 36262306a36Sopenharmony_ci os_warn("uml_dir can't be an empty string\n"); 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (name[strlen(name) - 1] == '/') { 36762306a36Sopenharmony_ci uml_dir = name; 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci uml_dir = malloc(strlen(name) + 2); 37262306a36Sopenharmony_ci if (uml_dir == NULL) { 37362306a36Sopenharmony_ci os_warn("Failed to malloc uml_dir - error = %d\n", errno); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Return 0 here because do_initcalls doesn't look at 37762306a36Sopenharmony_ci * the return value. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci sprintf(uml_dir, "%s/", name); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci__uml_setup("uml_dir=", set_uml_dir, 38762306a36Sopenharmony_ci"uml_dir=<directory>\n" 38862306a36Sopenharmony_ci" The location to place the pid and umid files.\n\n" 38962306a36Sopenharmony_ci); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void remove_umid_dir(void) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci char *dir, err; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci dir = malloc(strlen(uml_dir) + UMID_LEN + 1); 39662306a36Sopenharmony_ci if (!dir) 39762306a36Sopenharmony_ci return; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci sprintf(dir, "%s%s", uml_dir, umid); 40062306a36Sopenharmony_ci err = remove_files_and_dir(dir); 40162306a36Sopenharmony_ci if (err) 40262306a36Sopenharmony_ci os_warn("%s - remove_files_and_dir failed with err = %d\n", 40362306a36Sopenharmony_ci __func__, err); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci free(dir); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci__uml_exitcall(remove_umid_dir); 409