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 = strlen(dir) + sizeof("/pid") + 1;
 141
 142        file = malloc(filelen);
 143        if (!file)
 144                return -ENOMEM;
 145
 146        snprintf(file, filelen, "%s/pid", dir);
 147
 148        dead = 0;
 149        fd = open(file, O_RDONLY);
 150        if (fd < 0) {
 151                fd = -errno;
 152                if (fd != -ENOENT) {
 153                        printk(UM_KERN_ERR "is_umdir_used : couldn't open pid "
 154                               "file '%s', err = %d\n", file, -fd);
 155                }
 156                goto out;
 157        }
 158
 159        err = 0;
 160        n = read(fd, pid, sizeof(pid));
 161        if (n < 0) {
 162                printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
 163                       "'%s', err = %d\n", file, errno);
 164                goto out_close;
 165        } else if (n == 0) {
 166                printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
 167                       "'%s', 0-byte read\n", file);
 168                goto out_close;
 169        }
 170
 171        p = strtoul(pid, &end, 0);
 172        if (end == pid) {
 173                printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file "
 174                       "'%s', errno = %d\n", file, errno);
 175                goto out_close;
 176        }
 177
 178        if ((kill(p, 0) == 0) || (errno != ESRCH)) {
 179                printk(UM_KERN_ERR "umid \"%s\" is already in use by pid %d\n",
 180                       umid, p);
 181                return 1;
 182        }
 183
 184out_close:
 185        close(fd);
 186out:
 187        free(file);
 188        return 0;
 189}
 190
 191/*
 192 * Try to remove the directory @dir unless it's in use.
 193 * Precondition: @dir exists.
 194 * Returns 0 for success, < 0 for failure in removal or if the directory is in
 195 * use.
 196 */
 197static int umdir_take_if_dead(char *dir)
 198{
 199        int ret;
 200        if (is_umdir_used(dir))
 201                return -EEXIST;
 202
 203        ret = remove_files_and_dir(dir);
 204        if (ret) {
 205                printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir "
 206                       "failed with err = %d\n", ret);
 207        }
 208        return ret;
 209}
 210
 211static void __init create_pid_file(void)
 212{
 213        char pid[sizeof("nnnnnnnnn")], *file;
 214        int fd, n;
 215
 216        n = strlen(uml_dir) + UMID_LEN + sizeof("/pid");
 217        file = malloc(n);
 218        if (!file)
 219                return;
 220
 221        if (umid_file_name("pid", file, n))
 222                goto out;
 223
 224        fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
 225        if (fd < 0) {
 226                printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: "
 227                       "%s\n", file, strerror(errno));
 228                goto out;
 229        }
 230
 231        snprintf(pid, sizeof(pid), "%d\n", getpid());
 232        n = write(fd, pid, strlen(pid));
 233        if (n != strlen(pid))
 234                printk(UM_KERN_ERR "Write of pid file failed - err = %d\n",
 235                       errno);
 236
 237        close(fd);
 238out:
 239        free(file);
 240}
 241
 242int __init set_umid(char *name)
 243{
 244        if (strlen(name) > UMID_LEN - 1)
 245                return -E2BIG;
 246
 247        strlcpy(umid, name, sizeof(umid));
 248
 249        return 0;
 250}
 251
 252/* Changed in make_umid, which is called during early boot */
 253static int umid_setup = 0;
 254
 255static int __init make_umid(void)
 256{
 257        int fd, err;
 258        char tmp[256];
 259
 260        if (umid_setup)
 261                return 0;
 262
 263        make_uml_dir();
 264
 265        if (*umid == '\0') {
 266                strlcpy(tmp, uml_dir, sizeof(tmp));
 267                strlcat(tmp, "XXXXXX", sizeof(tmp));
 268                fd = mkstemp(tmp);
 269                if (fd < 0) {
 270                        printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: "
 271                               "%s\n", tmp, strerror(errno));
 272                        err = -errno;
 273                        goto err;
 274                }
 275
 276                close(fd);
 277
 278                set_umid(&tmp[strlen(uml_dir)]);
 279
 280                /*
 281                 * There's a nice tiny little race between this unlink and
 282                 * the mkdir below.  It'd be nice if there were a mkstemp
 283                 * for directories.
 284                 */
 285                if (unlink(tmp)) {
 286                        err = -errno;
 287                        goto err;
 288                }
 289        }
 290
 291        snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid);
 292        err = mkdir(tmp, 0777);
 293        if (err < 0) {
 294                err = -errno;
 295                if (err != -EEXIST)
 296                        goto err;
 297
 298                if (umdir_take_if_dead(tmp) < 0)
 299                        goto err;
 300
 301                err = mkdir(tmp, 0777);
 302        }
 303        if (err) {
 304                err = -errno;
 305                printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid,
 306                       errno);
 307                goto err;
 308        }
 309
 310        umid_setup = 1;
 311
 312        create_pid_file();
 313
 314        err = 0;
 315 err:
 316        return err;
 317}
 318
 319static int __init make_umid_init(void)
 320{
 321        if (!make_umid())
 322                return 0;
 323
 324        /*
 325         * If initializing with the given umid failed, then try again with
 326         * a random one.
 327         */
 328        printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a "
 329               "random umid\n", umid);
 330        *umid = '\0';
 331        make_umid();
 332
 333        return 0;
 334}
 335
 336__initcall(make_umid_init);
 337
 338int __init umid_file_name(char *name, char *buf, int len)
 339{
 340        int n, err;
 341
 342        err = make_umid();
 343        if (err)
 344                return err;
 345
 346        n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name);
 347        if (n >= len) {
 348                printk(UM_KERN_ERR "umid_file_name : buffer too short\n");
 349                return -E2BIG;
 350        }
 351
 352        return 0;
 353}
 354
 355char *get_umid(void)
 356{
 357        return umid;
 358}
 359
 360static int __init set_uml_dir(char *name, int *add)
 361{
 362        if (*name == '\0') {
 363                os_warn("uml_dir can't be an empty string\n");
 364                return 0;
 365        }
 366
 367        if (name[strlen(name) - 1] == '/') {
 368                uml_dir = name;
 369                return 0;
 370        }
 371
 372        uml_dir = malloc(strlen(name) + 2);
 373        if (uml_dir == NULL) {
 374                os_warn("Failed to malloc uml_dir - error = %d\n", errno);
 375
 376                /*
 377                 * Return 0 here because do_initcalls doesn't look at
 378                 * the return value.
 379                 */
 380                return 0;
 381        }
 382        sprintf(uml_dir, "%s/", name);
 383
 384        return 0;
 385}
 386
 387__uml_setup("uml_dir=", set_uml_dir,
 388"uml_dir=<directory>\n"
 389"    The location to place the pid and umid files.\n\n"
 390);
 391
 392static void remove_umid_dir(void)
 393{
 394        char *dir, err;
 395
 396        dir = malloc(strlen(uml_dir) + UMID_LEN + 1);
 397        if (!dir)
 398                return;
 399
 400        sprintf(dir, "%s%s", uml_dir, umid);
 401        err = remove_files_and_dir(dir);
 402        if (err)
 403                os_warn("%s - remove_files_and_dir failed with err = %d\n",
 404                        __func__, err);
 405
 406        free(dir);
 407}
 408
 409__uml_exitcall(remove_umid_dir);
 410