1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <dirent.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <signal.h> 12 #include <string.h> 13 #include <unistd.h> 14 #include <sys/stat.h> 15 #include <init.h> 16 #include <os.h> 17 18 #define UML_DIR "~/.uml/" 19 20 #define UMID_LEN 64 21 22 /* Changed by set_umid, which is run early in boot */ 23 static char umid[UMID_LEN] = { 0 }; 24 25 /* Changed by set_uml_dir and make_uml_dir, which are run early in boot */ 26 static char *uml_dir = UML_DIR; 27 28 static int __init make_uml_dir(void) 29 { 30 char dir[512] = { '\0' }; 31 int len, err; 32 33 if (*uml_dir == '~') { 34 char *home = getenv("HOME"); 35 36 err = -ENOENT; 37 if (home == NULL) { 38 printk(UM_KERN_ERR 39 "%s: no value in environment for $HOME\n", 40 __func__); 41 goto err; 42 } 43 strlcpy(dir, home, sizeof(dir)); 44 uml_dir++; 45 } 46 strlcat(dir, uml_dir, sizeof(dir)); 47 len = strlen(dir); 48 if (len > 0 && dir[len - 1] != '/') 49 strlcat(dir, "/", sizeof(dir)); 50 51 err = -ENOMEM; 52 uml_dir = malloc(strlen(dir) + 1); 53 if (uml_dir == NULL) { 54 printk(UM_KERN_ERR "%s : malloc failed, errno = %d\n", 55 __func__, errno); 56 goto err; 57 } 58 strcpy(uml_dir, dir); 59 60 if ((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)) { 61 printk(UM_KERN_ERR "Failed to mkdir '%s': %s\n", 62 uml_dir, strerror(errno)); 63 err = -errno; 64 goto err_free; 65 } 66 return 0; 67 68 err_free: 69 free(uml_dir); 70 err: 71 uml_dir = NULL; 72 return err; 73 } 74 75 /* 76 * Unlinks the files contained in @dir and then removes @dir. 77 * Doesn't handle directory trees, so it's not like rm -rf, but almost such. We 78 * ignore ENOENT errors for anything (they happen, strangely enough - possibly 79 * due to races between multiple dying UML threads). 80 */ 81 static int remove_files_and_dir(char *dir) 82 { 83 DIR *directory; 84 struct dirent *ent; 85 int len; 86 char file[256]; 87 int ret; 88 89 directory = opendir(dir); 90 if (directory == NULL) { 91 if (errno != ENOENT) 92 return -errno; 93 else 94 return 0; 95 } 96 97 while ((ent = readdir(directory)) != NULL) { 98 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) 99 continue; 100 len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1; 101 if (len > sizeof(file)) { 102 ret = -E2BIG; 103 goto out; 104 } 105 106 sprintf(file, "%s/%s", dir, ent->d_name); 107 if (unlink(file) < 0 && errno != ENOENT) { 108 ret = -errno; 109 goto out; 110 } 111 } 112 113 if (rmdir(dir) < 0 && errno != ENOENT) { 114 ret = -errno; 115 goto out; 116 } 117 118 ret = 0; 119 out: 120 closedir(directory); 121 return ret; 122 } 123 124 /* 125 * This says that there isn't already a user of the specified directory even if 126 * there are errors during the checking. This is because if these errors 127 * happen, the directory is unusable by the pre-existing UML, so we might as 128 * well take it over. This could happen either by 129 * the existing UML somehow corrupting its umid directory 130 * something other than UML sticking stuff in the directory 131 * this boot racing with a shutdown of the other UML 132 * In any of these cases, the directory isn't useful for anything else. 133 * 134 * Boolean return: 1 if in use, 0 otherwise. 135 */ 136 static inline int is_umdir_used(char *dir) 137 { 138 char pid[sizeof("nnnnn\0")], *end, *file; 139 int dead, fd, p, n, err; 140 size_t filelen; 141 142 err = asprintf(&file, "%s/pid", dir); 143 if (err < 0) 144 return 0; 145 146 filelen = strlen(file); 147 148 n = snprintf(file, filelen, "%s/pid", dir); 149 if (n >= filelen) { 150 printk(UM_KERN_ERR "is_umdir_used - pid filename too long\n"); 151 err = -E2BIG; 152 goto out; 153 } 154 155 dead = 0; 156 fd = open(file, O_RDONLY); 157 if (fd < 0) { 158 fd = -errno; 159 if (fd != -ENOENT) { 160 printk(UM_KERN_ERR "is_umdir_used : couldn't open pid " 161 "file '%s', err = %d\n", file, -fd); 162 } 163 goto out; 164 } 165 166 err = 0; 167 n = read(fd, pid, sizeof(pid)); 168 if (n < 0) { 169 printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file " 170 "'%s', err = %d\n", file, errno); 171 goto out_close; 172 } else if (n == 0) { 173 printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file " 174 "'%s', 0-byte read\n", file); 175 goto out_close; 176 } 177 178 p = strtoul(pid, &end, 0); 179 if (end == pid) { 180 printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file " 181 "'%s', errno = %d\n", file, errno); 182 goto out_close; 183 } 184 185 if ((kill(p, 0) == 0) || (errno != ESRCH)) { 186 printk(UM_KERN_ERR "umid \"%s\" is already in use by pid %d\n", 187 umid, p); 188 return 1; 189 } 190 191 out_close: 192 close(fd); 193 out: 194 free(file); 195 return 0; 196 } 197 198 /* 199 * Try to remove the directory @dir unless it's in use. 200 * Precondition: @dir exists. 201 * Returns 0 for success, < 0 for failure in removal or if the directory is in 202 * use. 203 */ 204 static int umdir_take_if_dead(char *dir) 205 { 206 int ret; 207 if (is_umdir_used(dir)) 208 return -EEXIST; 209 210 ret = remove_files_and_dir(dir); 211 if (ret) { 212 printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir " 213 "failed with err = %d\n", ret); 214 } 215 return ret; 216 } 217 218 static void __init create_pid_file(void) 219 { 220 char pid[sizeof("nnnnn\0")], *file; 221 int fd, n; 222 223 n = strlen(uml_dir) + UMID_LEN + sizeof("/pid\0"); 224 file = malloc(n); 225 if (!file) 226 return; 227 228 if (umid_file_name("pid", file, n)) 229 goto out; 230 231 fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644); 232 if (fd < 0) { 233 printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: " 234 "%s\n", file, strerror(errno)); 235 goto out; 236 } 237 238 snprintf(pid, sizeof(pid), "%d\n", getpid()); 239 n = write(fd, pid, strlen(pid)); 240 if (n != strlen(pid)) 241 printk(UM_KERN_ERR "Write of pid file failed - err = %d\n", 242 errno); 243 244 close(fd); 245 out: 246 free(file); 247 } 248 249 int __init set_umid(char *name) 250 { 251 if (strlen(name) > UMID_LEN - 1) 252 return -E2BIG; 253 254 strlcpy(umid, name, sizeof(umid)); 255 256 return 0; 257 } 258 259 /* Changed in make_umid, which is called during early boot */ 260 static int umid_setup = 0; 261 262 static int __init make_umid(void) 263 { 264 int fd, err; 265 char tmp[256]; 266 267 if (umid_setup) 268 return 0; 269 270 make_uml_dir(); 271 272 if (*umid == '\0') { 273 strlcpy(tmp, uml_dir, sizeof(tmp)); 274 strlcat(tmp, "XXXXXX", sizeof(tmp)); 275 fd = mkstemp(tmp); 276 if (fd < 0) { 277 printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: " 278 "%s\n", tmp, strerror(errno)); 279 err = -errno; 280 goto err; 281 } 282 283 close(fd); 284 285 set_umid(&tmp[strlen(uml_dir)]); 286 287 /* 288 * There's a nice tiny little race between this unlink and 289 * the mkdir below. It'd be nice if there were a mkstemp 290 * for directories. 291 */ 292 if (unlink(tmp)) { 293 err = -errno; 294 goto err; 295 } 296 } 297 298 snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid); 299 err = mkdir(tmp, 0777); 300 if (err < 0) { 301 err = -errno; 302 if (err != -EEXIST) 303 goto err; 304 305 if (umdir_take_if_dead(tmp) < 0) 306 goto err; 307 308 err = mkdir(tmp, 0777); 309 } 310 if (err) { 311 err = -errno; 312 printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid, 313 errno); 314 goto err; 315 } 316 317 umid_setup = 1; 318 319 create_pid_file(); 320 321 err = 0; 322 err: 323 return err; 324 } 325 326 static int __init make_umid_init(void) 327 { 328 if (!make_umid()) 329 return 0; 330 331 /* 332 * If initializing with the given umid failed, then try again with 333 * a random one. 334 */ 335 printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a " 336 "random umid\n", umid); 337 *umid = '\0'; 338 make_umid(); 339 340 return 0; 341 } 342 343 __initcall(make_umid_init); 344 345 int __init umid_file_name(char *name, char *buf, int len) 346 { 347 int n, err; 348 349 err = make_umid(); 350 if (err) 351 return err; 352 353 n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name); 354 if (n >= len) { 355 printk(UM_KERN_ERR "umid_file_name : buffer too short\n"); 356 return -E2BIG; 357 } 358 359 return 0; 360 } 361 362 char *get_umid(void) 363 { 364 return umid; 365 } 366 367 static int __init set_uml_dir(char *name, int *add) 368 { 369 if (*name == '\0') { 370 os_warn("uml_dir can't be an empty string\n"); 371 return 0; 372 } 373 374 if (name[strlen(name) - 1] == '/') { 375 uml_dir = name; 376 return 0; 377 } 378 379 uml_dir = malloc(strlen(name) + 2); 380 if (uml_dir == NULL) { 381 os_warn("Failed to malloc uml_dir - error = %d\n", errno); 382 383 /* 384 * Return 0 here because do_initcalls doesn't look at 385 * the return value. 386 */ 387 return 0; 388 } 389 sprintf(uml_dir, "%s/", name); 390 391 return 0; 392 } 393 394 __uml_setup("uml_dir=", set_uml_dir, 395 "uml_dir=<directory>\n" 396 " The location to place the pid and umid files.\n\n" 397 ); 398 399 static void remove_umid_dir(void) 400 { 401 char *dir, err; 402 403 dir = malloc(strlen(uml_dir) + UMID_LEN + 1); 404 if (!dir) 405 return; 406 407 sprintf(dir, "%s%s", uml_dir, umid); 408 err = remove_files_and_dir(dir); 409 if (err) 410 os_warn("%s - remove_files_and_dir failed with err = %d\n", 411 __func__, err); 412 413 free(dir); 414 } 415 416 __uml_exitcall(remove_umid_dir); 417