linux/arch/um/os-Linux/mem.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
   3 * Licensed under the GPL
   4 */
   5
   6#include <stdio.h>
   7#include <stddef.h>
   8#include <stdlib.h>
   9#include <unistd.h>
  10#include <errno.h>
  11#include <fcntl.h>
  12#include <string.h>
  13#include <sys/stat.h>
  14#include <sys/mman.h>
  15#include <sys/param.h>
  16#include <init.h>
  17#include <os.h>
  18
  19/* Modified by which_tmpdir, which is called during early boot */
  20static char *default_tmpdir = "/tmp";
  21
  22/*
  23 *  Modified when creating the physical memory file and when checking
  24 * the tmp filesystem for usability, both happening during early boot.
  25 */
  26static char *tempdir = NULL;
  27
  28static void __init find_tempdir(void)
  29{
  30        const char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL };
  31        int i;
  32        char *dir = NULL;
  33
  34        if (tempdir != NULL)
  35                /* We've already been called */
  36                return;
  37        for (i = 0; dirs[i]; i++) {
  38                dir = getenv(dirs[i]);
  39                if ((dir != NULL) && (*dir != '\0'))
  40                        break;
  41        }
  42        if ((dir == NULL) || (*dir == '\0'))
  43                dir = default_tmpdir;
  44
  45        tempdir = malloc(strlen(dir) + 2);
  46        if (tempdir == NULL) {
  47                fprintf(stderr, "Failed to malloc tempdir, "
  48                        "errno = %d\n", errno);
  49                return;
  50        }
  51        strcpy(tempdir, dir);
  52        strcat(tempdir, "/");
  53}
  54
  55/*
  56 * Remove bytes from the front of the buffer and refill it so that if there's a
  57 * partial string that we care about, it will be completed, and we can recognize
  58 * it.
  59 */
  60static int pop(int fd, char *buf, size_t size, size_t npop)
  61{
  62        ssize_t n;
  63        size_t len = strlen(&buf[npop]);
  64
  65        memmove(buf, &buf[npop], len + 1);
  66        n = read(fd, &buf[len], size - len - 1);
  67        if (n < 0)
  68                return -errno;
  69
  70        buf[len + n] = '\0';
  71        return 1;
  72}
  73
  74/*
  75 * This will return 1, with the first character in buf being the
  76 * character following the next instance of c in the file.  This will
  77 * read the file as needed.  If there's an error, -errno is returned;
  78 * if the end of the file is reached, 0 is returned.
  79 */
  80static int next(int fd, char *buf, size_t size, char c)
  81{
  82        ssize_t n;
  83        char *ptr;
  84
  85        while ((ptr = strchr(buf, c)) == NULL) {
  86                n = read(fd, buf, size - 1);
  87                if (n == 0)
  88                        return 0;
  89                else if (n < 0)
  90                        return -errno;
  91
  92                buf[n] = '\0';
  93        }
  94
  95        return pop(fd, buf, size, ptr - buf + 1);
  96}
  97
  98/*
  99 * Decode an octal-escaped and space-terminated path of the form used by
 100 * /proc/mounts. May be used to decode a path in-place. "out" must be at least
 101 * as large as the input. The output is always null-terminated. "len" gets the
 102 * length of the output, excluding the trailing null. Returns 0 if a full path
 103 * was successfully decoded, otherwise an error.
 104 */
 105static int decode_path(const char *in, char *out, size_t *len)
 106{
 107        char *first = out;
 108        int c;
 109        int i;
 110        int ret = -EINVAL;
 111        while (1) {
 112                switch (*in) {
 113                case '\0':
 114                        goto out;
 115
 116                case ' ':
 117                        ret = 0;
 118                        goto out;
 119
 120                case '\\':
 121                        in++;
 122                        c = 0;
 123                        for (i = 0; i < 3; i++) {
 124                                if (*in < '0' || *in > '7')
 125                                        goto out;
 126                                c = (c << 3) | (*in++ - '0');
 127                        }
 128                        *(unsigned char *)out++ = (unsigned char) c;
 129                        break;
 130
 131                default:
 132                        *out++ = *in++;
 133                        break;
 134                }
 135        }
 136
 137out:
 138        *out = '\0';
 139        *len = out - first;
 140        return ret;
 141}
 142
 143/*
 144 * Computes the length of s when encoded with three-digit octal escape sequences
 145 * for the characters in chars.
 146 */
 147static size_t octal_encoded_length(const char *s, const char *chars)
 148{
 149        size_t len = strlen(s);
 150        while ((s = strpbrk(s, chars)) != NULL) {
 151                len += 3;
 152                s++;
 153        }
 154
 155        return len;
 156}
 157
 158enum {
 159        OUTCOME_NOTHING_MOUNTED,
 160        OUTCOME_TMPFS_MOUNT,
 161        OUTCOME_NON_TMPFS_MOUNT,
 162};
 163
 164/* Read a line of /proc/mounts data looking for a tmpfs mount at "path". */
 165static int read_mount(int fd, char *buf, size_t bufsize, const char *path,
 166                      int *outcome)
 167{
 168        int found;
 169        int match;
 170        char *space;
 171        size_t len;
 172
 173        enum {
 174                MATCH_NONE,
 175                MATCH_EXACT,
 176                MATCH_PARENT,
 177        };
 178
 179        found = next(fd, buf, bufsize, ' ');
 180        if (found != 1)
 181                return found;
 182
 183        /*
 184         * If there's no following space in the buffer, then this path is
 185         * truncated, so it can't be the one we're looking for.
 186         */
 187        space = strchr(buf, ' ');
 188        if (space) {
 189                match = MATCH_NONE;
 190                if (!decode_path(buf, buf, &len)) {
 191                        if (!strcmp(buf, path))
 192                                match = MATCH_EXACT;
 193                        else if (!strncmp(buf, path, len)
 194                                 && (path[len] == '/' || !strcmp(buf, "/")))
 195                                match = MATCH_PARENT;
 196                }
 197
 198                found = pop(fd, buf, bufsize, space - buf + 1);
 199                if (found != 1)
 200                        return found;
 201
 202                switch (match) {
 203                case MATCH_EXACT:
 204                        if (!strncmp(buf, "tmpfs", strlen("tmpfs")))
 205                                *outcome = OUTCOME_TMPFS_MOUNT;
 206                        else
 207                                *outcome = OUTCOME_NON_TMPFS_MOUNT;
 208                        break;
 209
 210                case MATCH_PARENT:
 211                        /* This mount obscures any previous ones. */
 212                        *outcome = OUTCOME_NOTHING_MOUNTED;
 213                        break;
 214                }
 215        }
 216
 217        return next(fd, buf, bufsize, '\n');
 218}
 219
 220/* which_tmpdir is called only during early boot */
 221static int checked_tmpdir = 0;
 222
 223/*
 224 * Look for a tmpfs mounted at /dev/shm.  I couldn't find a cleaner
 225 * way to do this than to parse /proc/mounts.  statfs will return the
 226 * same filesystem magic number and fs id for both /dev and /dev/shm
 227 * when they are both tmpfs, so you can't tell if they are different
 228 * filesystems.  Also, there seems to be no other way of finding the
 229 * mount point of a filesystem from within it.
 230 *
 231 * If a /dev/shm tmpfs entry is found, then we switch to using it.
 232 * Otherwise, we stay with the default /tmp.
 233 */
 234static void which_tmpdir(void)
 235{
 236        int fd;
 237        int found;
 238        int outcome;
 239        char *path;
 240        char *buf;
 241        size_t bufsize;
 242
 243        if (checked_tmpdir)
 244                return;
 245
 246        checked_tmpdir = 1;
 247
 248        printf("Checking for tmpfs mount on /dev/shm...");
 249
 250        path = realpath("/dev/shm", NULL);
 251        if (!path) {
 252                printf("failed to check real path, errno = %d\n", errno);
 253                return;
 254        }
 255        printf("%s...", path);
 256
 257        /*
 258         * The buffer needs to be able to fit the full octal-escaped path, a
 259         * space, and a trailing null in order to successfully decode it.
 260         */
 261        bufsize = octal_encoded_length(path, " \t\n\\") + 2;
 262
 263        if (bufsize < 128)
 264                bufsize = 128;
 265
 266        buf = malloc(bufsize);
 267        if (!buf) {
 268                printf("malloc failed, errno = %d\n", errno);
 269                goto out;
 270        }
 271        buf[0] = '\0';
 272
 273        fd = open("/proc/mounts", O_RDONLY);
 274        if (fd < 0) {
 275                printf("failed to open /proc/mounts, errno = %d\n", errno);
 276                goto out1;
 277        }
 278
 279        outcome = OUTCOME_NOTHING_MOUNTED;
 280        while (1) {
 281                found = read_mount(fd, buf, bufsize, path, &outcome);
 282                if (found != 1)
 283                        break;
 284        }
 285
 286        if (found < 0) {
 287                printf("read returned errno %d\n", -found);
 288        } else {
 289                switch (outcome) {
 290                case OUTCOME_TMPFS_MOUNT:
 291                        printf("OK\n");
 292                        default_tmpdir = "/dev/shm";
 293                        break;
 294
 295                case OUTCOME_NON_TMPFS_MOUNT:
 296                        printf("not tmpfs\n");
 297                        break;
 298
 299                default:
 300                        printf("nothing mounted on /dev/shm\n");
 301                        break;
 302                }
 303        }
 304
 305        close(fd);
 306out1:
 307        free(buf);
 308out:
 309        free(path);
 310}
 311
 312static int __init make_tempfile(const char *template, char **out_tempname,
 313                                int do_unlink)
 314{
 315        char *tempname;
 316        int fd;
 317
 318        which_tmpdir();
 319        tempname = malloc(MAXPATHLEN);
 320        if (tempname == NULL)
 321                return -1;
 322
 323        find_tempdir();
 324        if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN))
 325                goto out;
 326
 327        if (template[0] != '/')
 328                strcpy(tempname, tempdir);
 329        else
 330                tempname[0] = '\0';
 331        strncat(tempname, template, MAXPATHLEN-1-strlen(tempname));
 332        fd = mkstemp(tempname);
 333        if (fd < 0) {
 334                fprintf(stderr, "open - cannot create %s: %s\n", tempname,
 335                        strerror(errno));
 336                goto out;
 337        }
 338        if (do_unlink && (unlink(tempname) < 0)) {
 339                perror("unlink");
 340                goto close;
 341        }
 342        if (out_tempname) {
 343                *out_tempname = tempname;
 344        } else
 345                free(tempname);
 346        return fd;
 347close:
 348        close(fd);
 349out:
 350        free(tempname);
 351        return -1;
 352}
 353
 354#define TEMPNAME_TEMPLATE "vm_file-XXXXXX"
 355
 356static int __init create_tmp_file(unsigned long long len)
 357{
 358        int fd, err;
 359        char zero;
 360
 361        fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1);
 362        if (fd < 0)
 363                exit(1);
 364
 365        err = fchmod(fd, 0777);
 366        if (err < 0) {
 367                perror("fchmod");
 368                exit(1);
 369        }
 370
 371        /*
 372         * Seek to len - 1 because writing a character there will
 373         * increase the file size by one byte, to the desired length.
 374         */
 375        if (lseek64(fd, len - 1, SEEK_SET) < 0) {
 376                perror("lseek64");
 377                exit(1);
 378        }
 379
 380        zero = 0;
 381
 382        err = write(fd, &zero, 1);
 383        if (err != 1) {
 384                perror("write");
 385                exit(1);
 386        }
 387
 388        return fd;
 389}
 390
 391int __init create_mem_file(unsigned long long len)
 392{
 393        int err, fd;
 394
 395        fd = create_tmp_file(len);
 396
 397        err = os_set_exec_close(fd);
 398        if (err < 0) {
 399                errno = -err;
 400                perror("exec_close");
 401        }
 402        return fd;
 403}
 404
 405
 406void __init check_tmpexec(void)
 407{
 408        void *addr;
 409        int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE);
 410
 411        addr = mmap(NULL, UM_KERN_PAGE_SIZE,
 412                    PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
 413        printf("Checking PROT_EXEC mmap in %s...",tempdir);
 414        fflush(stdout);
 415        if (addr == MAP_FAILED) {
 416                err = errno;
 417                perror("failed");
 418                close(fd);
 419                if (err == EPERM)
 420                        printf("%s must be not mounted noexec\n",tempdir);
 421                exit(1);
 422        }
 423        printf("OK\n");
 424        munmap(addr, UM_KERN_PAGE_SIZE);
 425
 426        close(fd);
 427}
 428