linux/arch/um/os-Linux/umid.c
<<
>>
Prefs
   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 */
  23static char umid[UMID_LEN] = { 0 };
  24
  25/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
  26static char *uml_dir = UML_DIR;
  27
  28static 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
  68err_free:
  69        free(uml_dir);
  70err:
  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 */
  81static 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) + strlen("/") + 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;
 119out:
 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 */
 136static inline int is_umdir_used(char *dir)
 137{
 138        char pid[sizeof("nnnnnnnnn")], *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
 191out_close:
 192        close(fd);
 193out:
 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 */
 204static 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
 218static void __init create_pid_file(void)
 219{
 220        char pid[sizeof("nnnnnnnnn")], *file;
 221        int fd, n;
 222
 223        n = strlen(uml_dir) + UMID_LEN + sizeof("/pid");
 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);
 245out:
 246        free(file);
 247}
 248
 249int __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 */
 260static int umid_setup = 0;
 261
 262static 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
 326static 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
 345int __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
 362char *get_umid(void)
 363{
 364        return umid;
 365}
 366
 367static 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
 399static 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