root/arch/um/os-Linux/umid.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. make_uml_dir
  2. remove_files_and_dir
  3. is_umdir_used
  4. umdir_take_if_dead
  5. create_pid_file
  6. set_umid
  7. make_umid
  8. make_umid_init
  9. umid_file_name
  10. get_umid
  11. set_uml_dir
  12. remove_umid_dir

   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         file = malloc(strlen(uml_dir) + UMID_LEN + sizeof("/pid\0"));
 224         if (!file)
 225                 return;
 226 
 227         if (umid_file_name("pid", file, sizeof(file)))
 228                 goto out;
 229 
 230         fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
 231         if (fd < 0) {
 232                 printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: "
 233                        "%s\n", file, strerror(errno));
 234                 goto out;
 235         }
 236 
 237         snprintf(pid, sizeof(pid), "%d\n", getpid());
 238         n = write(fd, pid, strlen(pid));
 239         if (n != strlen(pid))
 240                 printk(UM_KERN_ERR "Write of pid file failed - err = %d\n",
 241                        errno);
 242 
 243         close(fd);
 244 out:
 245         free(file);
 246 }
 247 
 248 int __init set_umid(char *name)
 249 {
 250         if (strlen(name) > UMID_LEN - 1)
 251                 return -E2BIG;
 252 
 253         strlcpy(umid, name, sizeof(umid));
 254 
 255         return 0;
 256 }
 257 
 258 /* Changed in make_umid, which is called during early boot */
 259 static int umid_setup = 0;
 260 
 261 static int __init make_umid(void)
 262 {
 263         int fd, err;
 264         char tmp[256];
 265 
 266         if (umid_setup)
 267                 return 0;
 268 
 269         make_uml_dir();
 270 
 271         if (*umid == '\0') {
 272                 strlcpy(tmp, uml_dir, sizeof(tmp));
 273                 strlcat(tmp, "XXXXXX", sizeof(tmp));
 274                 fd = mkstemp(tmp);
 275                 if (fd < 0) {
 276                         printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: "
 277                                "%s\n", tmp, strerror(errno));
 278                         err = -errno;
 279                         goto err;
 280                 }
 281 
 282                 close(fd);
 283 
 284                 set_umid(&tmp[strlen(uml_dir)]);
 285 
 286                 /*
 287                  * There's a nice tiny little race between this unlink and
 288                  * the mkdir below.  It'd be nice if there were a mkstemp
 289                  * for directories.
 290                  */
 291                 if (unlink(tmp)) {
 292                         err = -errno;
 293                         goto err;
 294                 }
 295         }
 296 
 297         snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid);
 298         err = mkdir(tmp, 0777);
 299         if (err < 0) {
 300                 err = -errno;
 301                 if (err != -EEXIST)
 302                         goto err;
 303 
 304                 if (umdir_take_if_dead(tmp) < 0)
 305                         goto err;
 306 
 307                 err = mkdir(tmp, 0777);
 308         }
 309         if (err) {
 310                 err = -errno;
 311                 printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid,
 312                        errno);
 313                 goto err;
 314         }
 315 
 316         umid_setup = 1;
 317 
 318         create_pid_file();
 319 
 320         err = 0;
 321  err:
 322         return err;
 323 }
 324 
 325 static int __init make_umid_init(void)
 326 {
 327         if (!make_umid())
 328                 return 0;
 329 
 330         /*
 331          * If initializing with the given umid failed, then try again with
 332          * a random one.
 333          */
 334         printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a "
 335                "random umid\n", umid);
 336         *umid = '\0';
 337         make_umid();
 338 
 339         return 0;
 340 }
 341 
 342 __initcall(make_umid_init);
 343 
 344 int __init umid_file_name(char *name, char *buf, int len)
 345 {
 346         int n, err;
 347 
 348         err = make_umid();
 349         if (err)
 350                 return err;
 351 
 352         n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name);
 353         if (n >= len) {
 354                 printk(UM_KERN_ERR "umid_file_name : buffer too short\n");
 355                 return -E2BIG;
 356         }
 357 
 358         return 0;
 359 }
 360 
 361 char *get_umid(void)
 362 {
 363         return umid;
 364 }
 365 
 366 static int __init set_uml_dir(char *name, int *add)
 367 {
 368         if (*name == '\0') {
 369                 os_warn("uml_dir can't be an empty string\n");
 370                 return 0;
 371         }
 372 
 373         if (name[strlen(name) - 1] == '/') {
 374                 uml_dir = name;
 375                 return 0;
 376         }
 377 
 378         uml_dir = malloc(strlen(name) + 2);
 379         if (uml_dir == NULL) {
 380                 os_warn("Failed to malloc uml_dir - error = %d\n", errno);
 381 
 382                 /*
 383                  * Return 0 here because do_initcalls doesn't look at
 384                  * the return value.
 385                  */
 386                 return 0;
 387         }
 388         sprintf(uml_dir, "%s/", name);
 389 
 390         return 0;
 391 }
 392 
 393 __uml_setup("uml_dir=", set_uml_dir,
 394 "uml_dir=<directory>\n"
 395 "    The location to place the pid and umid files.\n\n"
 396 );
 397 
 398 static void remove_umid_dir(void)
 399 {
 400         char *dir, err;
 401 
 402         dir = malloc(strlen(uml_dir) + UMID_LEN + 1);
 403         if (!dir)
 404                 return;
 405 
 406         sprintf(dir, "%s%s", uml_dir, umid);
 407         err = remove_files_and_dir(dir);
 408         if (err)
 409                 os_warn("%s - remove_files_and_dir failed with err = %d\n",
 410                         __func__, err);
 411 
 412         free(dir);
 413 }
 414 
 415 __uml_exitcall(remove_umid_dir);

/* [<][>][^][v][top][bottom][index][help] */