15134d8feSJeff Dike /* 25134d8feSJeff Dike * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 35134d8feSJeff Dike * Licensed under the GPL 45134d8feSJeff Dike */ 55134d8feSJeff Dike 60f80bc85SJeff Dike #include <stdio.h> 70f80bc85SJeff Dike #include <stddef.h> 85134d8feSJeff Dike #include <stdlib.h> 90f80bc85SJeff Dike #include <unistd.h> 100f80bc85SJeff Dike #include <errno.h> 110f80bc85SJeff Dike #include <fcntl.h> 125134d8feSJeff Dike #include <string.h> 13fb967eccSLiu Aleaxander #include <sys/stat.h> 140f80bc85SJeff Dike #include <sys/mman.h> 150f80bc85SJeff Dike #include <sys/param.h> 1637185b33SAl Viro #include <init.h> 1737185b33SAl Viro #include <os.h> 180f80bc85SJeff Dike 196bf79482SJeff Dike /* Modified by which_tmpdir, which is called during early boot */ 20966a082fSRob Landley static char *default_tmpdir = "/tmp"; 216bf79482SJeff Dike 226bf79482SJeff Dike /* 236bf79482SJeff Dike * Modified when creating the physical memory file and when checking 246bf79482SJeff Dike * the tmp filesystem for usability, both happening during early boot. 256bf79482SJeff Dike */ 260f80bc85SJeff Dike static char *tempdir = NULL; 270f80bc85SJeff Dike 280f80bc85SJeff Dike static void __init find_tempdir(void) 290f80bc85SJeff Dike { 30c0a9290eSWANG Cong const char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL }; 310f80bc85SJeff Dike int i; 320f80bc85SJeff Dike char *dir = NULL; 330f80bc85SJeff Dike 345134d8feSJeff Dike if (tempdir != NULL) 355134d8feSJeff Dike /* We've already been called */ 3681999a01SJeff Dike return; 370f80bc85SJeff Dike for (i = 0; dirs[i]; i++) { 380f80bc85SJeff Dike dir = getenv(dirs[i]); 390f80bc85SJeff Dike if ((dir != NULL) && (*dir != '\0')) 400f80bc85SJeff Dike break; 410f80bc85SJeff Dike } 420f80bc85SJeff Dike if ((dir == NULL) || (*dir == '\0')) 43966a082fSRob Landley dir = default_tmpdir; 440f80bc85SJeff Dike 450f80bc85SJeff Dike tempdir = malloc(strlen(dir) + 2); 460f80bc85SJeff Dike if (tempdir == NULL) { 470f80bc85SJeff Dike fprintf(stderr, "Failed to malloc tempdir, " 480f80bc85SJeff Dike "errno = %d\n", errno); 490f80bc85SJeff Dike return; 500f80bc85SJeff Dike } 510f80bc85SJeff Dike strcpy(tempdir, dir); 520f80bc85SJeff Dike strcat(tempdir, "/"); 530f80bc85SJeff Dike } 540f80bc85SJeff Dike 555134d8feSJeff Dike /* 56*74735341STristan Schmelcher * Remove bytes from the front of the buffer and refill it so that if there's a 57*74735341STristan Schmelcher * partial string that we care about, it will be completed, and we can recognize 58*74735341STristan Schmelcher * it. 59*74735341STristan Schmelcher */ 60*74735341STristan Schmelcher static int pop(int fd, char *buf, size_t size, size_t npop) 61*74735341STristan Schmelcher { 62*74735341STristan Schmelcher ssize_t n; 63*74735341STristan Schmelcher size_t len = strlen(&buf[npop]); 64*74735341STristan Schmelcher 65*74735341STristan Schmelcher memmove(buf, &buf[npop], len + 1); 66*74735341STristan Schmelcher n = read(fd, &buf[len], size - len - 1); 67*74735341STristan Schmelcher if (n < 0) 68*74735341STristan Schmelcher return -errno; 69*74735341STristan Schmelcher 70*74735341STristan Schmelcher buf[len + n] = '\0'; 71*74735341STristan Schmelcher return 1; 72*74735341STristan Schmelcher } 73*74735341STristan Schmelcher 74*74735341STristan Schmelcher /* 755134d8feSJeff Dike * This will return 1, with the first character in buf being the 76966a082fSRob Landley * character following the next instance of c in the file. This will 77966a082fSRob Landley * read the file as needed. If there's an error, -errno is returned; 78966a082fSRob Landley * if the end of the file is reached, 0 is returned. 79966a082fSRob Landley */ 80c0a9290eSWANG Cong static int next(int fd, char *buf, size_t size, char c) 81966a082fSRob Landley { 82c0a9290eSWANG Cong ssize_t n; 83966a082fSRob Landley char *ptr; 84966a082fSRob Landley 85966a082fSRob Landley while ((ptr = strchr(buf, c)) == NULL) { 86966a082fSRob Landley n = read(fd, buf, size - 1); 87966a082fSRob Landley if (n == 0) 88966a082fSRob Landley return 0; 89966a082fSRob Landley else if (n < 0) 90966a082fSRob Landley return -errno; 91966a082fSRob Landley 92966a082fSRob Landley buf[n] = '\0'; 93966a082fSRob Landley } 94966a082fSRob Landley 95*74735341STristan Schmelcher return pop(fd, buf, size, ptr - buf + 1); 96*74735341STristan Schmelcher } 97c2b7a4bbSJeff Dike 985134d8feSJeff Dike /* 99*74735341STristan Schmelcher * Decode an octal-escaped and space-terminated path of the form used by 100*74735341STristan Schmelcher * /proc/mounts. May be used to decode a path in-place. "out" must be at least 101*74735341STristan Schmelcher * as large as the input. The output is always null-terminated. "len" gets the 102*74735341STristan Schmelcher * length of the output, excluding the trailing null. Returns 0 if a full path 103*74735341STristan Schmelcher * was successfully decoded, otherwise an error. 104c2b7a4bbSJeff Dike */ 105*74735341STristan Schmelcher static int decode_path(const char *in, char *out, size_t *len) 106*74735341STristan Schmelcher { 107*74735341STristan Schmelcher char *first = out; 108*74735341STristan Schmelcher int c; 109*74735341STristan Schmelcher int i; 110*74735341STristan Schmelcher int ret = -EINVAL; 111*74735341STristan Schmelcher while (1) { 112*74735341STristan Schmelcher switch (*in) { 113*74735341STristan Schmelcher case '\0': 114*74735341STristan Schmelcher goto out; 115c2b7a4bbSJeff Dike 116*74735341STristan Schmelcher case ' ': 117*74735341STristan Schmelcher ret = 0; 118*74735341STristan Schmelcher goto out; 119*74735341STristan Schmelcher 120*74735341STristan Schmelcher case '\\': 121*74735341STristan Schmelcher in++; 122*74735341STristan Schmelcher c = 0; 123*74735341STristan Schmelcher for (i = 0; i < 3; i++) { 124*74735341STristan Schmelcher if (*in < '0' || *in > '7') 125*74735341STristan Schmelcher goto out; 126*74735341STristan Schmelcher c = (c << 3) | (*in++ - '0'); 127*74735341STristan Schmelcher } 128*74735341STristan Schmelcher *(unsigned char *)out++ = (unsigned char) c; 129*74735341STristan Schmelcher break; 130*74735341STristan Schmelcher 131*74735341STristan Schmelcher default: 132*74735341STristan Schmelcher *out++ = *in++; 133*74735341STristan Schmelcher break; 134*74735341STristan Schmelcher } 135*74735341STristan Schmelcher } 136*74735341STristan Schmelcher 137*74735341STristan Schmelcher out: 138*74735341STristan Schmelcher *out = '\0'; 139*74735341STristan Schmelcher *len = out - first; 140*74735341STristan Schmelcher return ret; 141*74735341STristan Schmelcher } 142*74735341STristan Schmelcher 143*74735341STristan Schmelcher /* 144*74735341STristan Schmelcher * Computes the length of s when encoded with three-digit octal escape sequences 145*74735341STristan Schmelcher * for the characters in chars. 146*74735341STristan Schmelcher */ 147*74735341STristan Schmelcher static size_t octal_encoded_length(const char *s, const char *chars) 148*74735341STristan Schmelcher { 149*74735341STristan Schmelcher size_t len = strlen(s); 150*74735341STristan Schmelcher while ((s = strpbrk(s, chars)) != NULL) { 151*74735341STristan Schmelcher len += 3; 152*74735341STristan Schmelcher s++; 153*74735341STristan Schmelcher } 154*74735341STristan Schmelcher 155*74735341STristan Schmelcher return len; 156*74735341STristan Schmelcher } 157*74735341STristan Schmelcher 158*74735341STristan Schmelcher enum { 159*74735341STristan Schmelcher OUTCOME_NOTHING_MOUNTED, 160*74735341STristan Schmelcher OUTCOME_TMPFS_MOUNT, 161*74735341STristan Schmelcher OUTCOME_NON_TMPFS_MOUNT, 162*74735341STristan Schmelcher }; 163*74735341STristan Schmelcher 164*74735341STristan Schmelcher /* Read a line of /proc/mounts data looking for a tmpfs mount at "path". */ 165*74735341STristan Schmelcher static int read_mount(int fd, char *buf, size_t bufsize, const char *path, 166*74735341STristan Schmelcher int *outcome) 167*74735341STristan Schmelcher { 168*74735341STristan Schmelcher int found; 169*74735341STristan Schmelcher int match; 170*74735341STristan Schmelcher char *space; 171*74735341STristan Schmelcher size_t len; 172*74735341STristan Schmelcher 173*74735341STristan Schmelcher enum { 174*74735341STristan Schmelcher MATCH_NONE, 175*74735341STristan Schmelcher MATCH_EXACT, 176*74735341STristan Schmelcher MATCH_PARENT, 177*74735341STristan Schmelcher }; 178*74735341STristan Schmelcher 179*74735341STristan Schmelcher found = next(fd, buf, bufsize, ' '); 180*74735341STristan Schmelcher if (found != 1) 181*74735341STristan Schmelcher return found; 182*74735341STristan Schmelcher 183*74735341STristan Schmelcher /* 184*74735341STristan Schmelcher * If there's no following space in the buffer, then this path is 185*74735341STristan Schmelcher * truncated, so it can't be the one we're looking for. 186*74735341STristan Schmelcher */ 187*74735341STristan Schmelcher space = strchr(buf, ' '); 188*74735341STristan Schmelcher if (space) { 189*74735341STristan Schmelcher match = MATCH_NONE; 190*74735341STristan Schmelcher if (!decode_path(buf, buf, &len)) { 191*74735341STristan Schmelcher if (!strcmp(buf, path)) 192*74735341STristan Schmelcher match = MATCH_EXACT; 193*74735341STristan Schmelcher else if (!strncmp(buf, path, len) 194*74735341STristan Schmelcher && (path[len] == '/' || !strcmp(buf, "/"))) 195*74735341STristan Schmelcher match = MATCH_PARENT; 196*74735341STristan Schmelcher } 197*74735341STristan Schmelcher 198*74735341STristan Schmelcher found = pop(fd, buf, bufsize, space - buf + 1); 199*74735341STristan Schmelcher if (found != 1) 200*74735341STristan Schmelcher return found; 201*74735341STristan Schmelcher 202*74735341STristan Schmelcher switch (match) { 203*74735341STristan Schmelcher case MATCH_EXACT: 204*74735341STristan Schmelcher if (!strncmp(buf, "tmpfs", strlen("tmpfs"))) 205*74735341STristan Schmelcher *outcome = OUTCOME_TMPFS_MOUNT; 206*74735341STristan Schmelcher else 207*74735341STristan Schmelcher *outcome = OUTCOME_NON_TMPFS_MOUNT; 208*74735341STristan Schmelcher break; 209*74735341STristan Schmelcher 210*74735341STristan Schmelcher case MATCH_PARENT: 211*74735341STristan Schmelcher /* This mount obscures any previous ones. */ 212*74735341STristan Schmelcher *outcome = OUTCOME_NOTHING_MOUNTED; 213*74735341STristan Schmelcher break; 214*74735341STristan Schmelcher } 215*74735341STristan Schmelcher } 216*74735341STristan Schmelcher 217*74735341STristan Schmelcher return next(fd, buf, bufsize, '\n'); 218966a082fSRob Landley } 219966a082fSRob Landley 2206bf79482SJeff Dike /* which_tmpdir is called only during early boot */ 221966a082fSRob Landley static int checked_tmpdir = 0; 222966a082fSRob Landley 2235134d8feSJeff Dike /* 2245134d8feSJeff Dike * Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner 225966a082fSRob Landley * way to do this than to parse /proc/mounts. statfs will return the 226966a082fSRob Landley * same filesystem magic number and fs id for both /dev and /dev/shm 227966a082fSRob Landley * when they are both tmpfs, so you can't tell if they are different 228966a082fSRob Landley * filesystems. Also, there seems to be no other way of finding the 229966a082fSRob Landley * mount point of a filesystem from within it. 230966a082fSRob Landley * 231966a082fSRob Landley * If a /dev/shm tmpfs entry is found, then we switch to using it. 232966a082fSRob Landley * Otherwise, we stay with the default /tmp. 233966a082fSRob Landley */ 234966a082fSRob Landley static void which_tmpdir(void) 235966a082fSRob Landley { 236*74735341STristan Schmelcher int fd; 237*74735341STristan Schmelcher int found; 238*74735341STristan Schmelcher int outcome; 239*74735341STristan Schmelcher char *path; 240*74735341STristan Schmelcher char *buf; 241*74735341STristan Schmelcher size_t bufsize; 242966a082fSRob Landley 243966a082fSRob Landley if (checked_tmpdir) 244966a082fSRob Landley return; 245966a082fSRob Landley 246966a082fSRob Landley checked_tmpdir = 1; 247966a082fSRob Landley 248966a082fSRob Landley printf("Checking for tmpfs mount on /dev/shm..."); 249966a082fSRob Landley 250*74735341STristan Schmelcher path = realpath("/dev/shm", NULL); 251*74735341STristan Schmelcher if (!path) { 252*74735341STristan Schmelcher printf("failed to check real path, errno = %d\n", errno); 253*74735341STristan Schmelcher return; 254*74735341STristan Schmelcher } 255*74735341STristan Schmelcher printf("%s...", path); 256*74735341STristan Schmelcher 257*74735341STristan Schmelcher /* 258*74735341STristan Schmelcher * The buffer needs to be able to fit the full octal-escaped path, a 259*74735341STristan Schmelcher * space, and a trailing null in order to successfully decode it. 260*74735341STristan Schmelcher */ 261*74735341STristan Schmelcher bufsize = octal_encoded_length(path, " \t\n\\") + 2; 262*74735341STristan Schmelcher 263*74735341STristan Schmelcher if (bufsize < 128) 264*74735341STristan Schmelcher bufsize = 128; 265*74735341STristan Schmelcher 266*74735341STristan Schmelcher buf = malloc(bufsize); 267*74735341STristan Schmelcher if (!buf) { 268*74735341STristan Schmelcher printf("malloc failed, errno = %d\n", errno); 269*74735341STristan Schmelcher goto out; 270*74735341STristan Schmelcher } 271*74735341STristan Schmelcher buf[0] = '\0'; 272*74735341STristan Schmelcher 273966a082fSRob Landley fd = open("/proc/mounts", O_RDONLY); 274966a082fSRob Landley if (fd < 0) { 275966a082fSRob Landley printf("failed to open /proc/mounts, errno = %d\n", errno); 276*74735341STristan Schmelcher goto out1; 277966a082fSRob Landley } 278966a082fSRob Landley 279*74735341STristan Schmelcher outcome = OUTCOME_NOTHING_MOUNTED; 280966a082fSRob Landley while (1) { 281*74735341STristan Schmelcher found = read_mount(fd, buf, bufsize, path, &outcome); 282966a082fSRob Landley if (found != 1) 283966a082fSRob Landley break; 284966a082fSRob Landley } 285966a082fSRob Landley 286*74735341STristan Schmelcher if (found < 0) { 287966a082fSRob Landley printf("read returned errno %d\n", -found); 288*74735341STristan Schmelcher } else { 289*74735341STristan Schmelcher switch (outcome) { 290*74735341STristan Schmelcher case OUTCOME_TMPFS_MOUNT: 291966a082fSRob Landley printf("OK\n"); 292966a082fSRob Landley default_tmpdir = "/dev/shm"; 293*74735341STristan Schmelcher break; 294*74735341STristan Schmelcher 295*74735341STristan Schmelcher case OUTCOME_NON_TMPFS_MOUNT: 296*74735341STristan Schmelcher printf("not tmpfs\n"); 297*74735341STristan Schmelcher break; 298*74735341STristan Schmelcher 299*74735341STristan Schmelcher default: 300*74735341STristan Schmelcher printf("nothing mounted on /dev/shm\n"); 301*74735341STristan Schmelcher break; 302*74735341STristan Schmelcher } 303*74735341STristan Schmelcher } 304*74735341STristan Schmelcher 305*74735341STristan Schmelcher close(fd); 306*74735341STristan Schmelcher out1: 307*74735341STristan Schmelcher free(buf); 308*74735341STristan Schmelcher out: 309*74735341STristan Schmelcher free(path); 310966a082fSRob Landley } 311966a082fSRob Landley 3125134d8feSJeff Dike static int __init make_tempfile(const char *template, char **out_tempname, 31336e45463SJeff Dike int do_unlink) 3140f80bc85SJeff Dike { 31587276f72SPaolo 'Blaisorblade' Giarrusso char *tempname; 3160f80bc85SJeff Dike int fd; 3170f80bc85SJeff Dike 318966a082fSRob Landley which_tmpdir(); 31987276f72SPaolo 'Blaisorblade' Giarrusso tempname = malloc(MAXPATHLEN); 32011a7ac23SJim Meyering if (tempname == NULL) 32111a7ac23SJim Meyering return -1; 32287276f72SPaolo 'Blaisorblade' Giarrusso 3230f80bc85SJeff Dike find_tempdir(); 32411a7ac23SJim Meyering if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) 3252a6d0ac1SDavidlohr Bueso goto out; 32611a7ac23SJim Meyering 32787276f72SPaolo 'Blaisorblade' Giarrusso if (template[0] != '/') 3280f80bc85SJeff Dike strcpy(tempname, tempdir); 3290f80bc85SJeff Dike else 33087276f72SPaolo 'Blaisorblade' Giarrusso tempname[0] = '\0'; 331c9a3072dSWANG Cong strncat(tempname, template, MAXPATHLEN-1-strlen(tempname)); 3320f80bc85SJeff Dike fd = mkstemp(tempname); 3330f80bc85SJeff Dike if (fd < 0) { 3340f80bc85SJeff Dike fprintf(stderr, "open - cannot create %s: %s\n", tempname, 3350f80bc85SJeff Dike strerror(errno)); 33687276f72SPaolo 'Blaisorblade' Giarrusso goto out; 3370f80bc85SJeff Dike } 3380f80bc85SJeff Dike if (do_unlink && (unlink(tempname) < 0)) { 3390f80bc85SJeff Dike perror("unlink"); 3402a6d0ac1SDavidlohr Bueso goto close; 3410f80bc85SJeff Dike } 3420f80bc85SJeff Dike if (out_tempname) { 34387276f72SPaolo 'Blaisorblade' Giarrusso *out_tempname = tempname; 34411a7ac23SJim Meyering } else 34587276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 34681999a01SJeff Dike return fd; 3472a6d0ac1SDavidlohr Bueso close: 3482a6d0ac1SDavidlohr Bueso close(fd); 34987276f72SPaolo 'Blaisorblade' Giarrusso out: 35087276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 35187276f72SPaolo 'Blaisorblade' Giarrusso return -1; 3520f80bc85SJeff Dike } 3530f80bc85SJeff Dike 3540f80bc85SJeff Dike #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" 3550f80bc85SJeff Dike 3565134d8feSJeff Dike static int __init create_tmp_file(unsigned long long len) 3570f80bc85SJeff Dike { 3580f80bc85SJeff Dike int fd, err; 3590f80bc85SJeff Dike char zero; 3600f80bc85SJeff Dike 3610f80bc85SJeff Dike fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); 3625134d8feSJeff Dike if (fd < 0) 3630f80bc85SJeff Dike exit(1); 3640f80bc85SJeff Dike 3650f80bc85SJeff Dike err = fchmod(fd, 0777); 3660f80bc85SJeff Dike if (err < 0) { 367512b6fb1SJeff Dike perror("fchmod"); 3680f80bc85SJeff Dike exit(1); 3690f80bc85SJeff Dike } 3700f80bc85SJeff Dike 3715134d8feSJeff Dike /* 3725134d8feSJeff Dike * Seek to len - 1 because writing a character there will 373190f4939SJeff Dike * increase the file size by one byte, to the desired length. 374190f4939SJeff Dike */ 375190f4939SJeff Dike if (lseek64(fd, len - 1, SEEK_SET) < 0) { 376512b6fb1SJeff Dike perror("lseek64"); 3770f80bc85SJeff Dike exit(1); 3780f80bc85SJeff Dike } 3790f80bc85SJeff Dike 3800f80bc85SJeff Dike zero = 0; 3810f80bc85SJeff Dike 382a61f334fSJeff Dike err = write(fd, &zero, 1); 3830f80bc85SJeff Dike if (err != 1) { 384a61f334fSJeff Dike perror("write"); 3850f80bc85SJeff Dike exit(1); 3860f80bc85SJeff Dike } 3870f80bc85SJeff Dike 38881999a01SJeff Dike return fd; 3890f80bc85SJeff Dike } 3900f80bc85SJeff Dike 39136e45463SJeff Dike int __init create_mem_file(unsigned long long len) 3920f80bc85SJeff Dike { 3930f80bc85SJeff Dike int err, fd; 3940f80bc85SJeff Dike 39502dea087SJeff Dike fd = create_tmp_file(len); 3960f80bc85SJeff Dike 397512b6fb1SJeff Dike err = os_set_exec_close(fd); 3980f80bc85SJeff Dike if (err < 0) { 3990f80bc85SJeff Dike errno = -err; 4000f80bc85SJeff Dike perror("exec_close"); 4010f80bc85SJeff Dike } 40281999a01SJeff Dike return fd; 4030f80bc85SJeff Dike } 404966a082fSRob Landley 405966a082fSRob Landley 40636e45463SJeff Dike void __init check_tmpexec(void) 407966a082fSRob Landley { 408966a082fSRob Landley void *addr; 409966a082fSRob Landley int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); 410966a082fSRob Landley 411966a082fSRob Landley addr = mmap(NULL, UM_KERN_PAGE_SIZE, 412966a082fSRob Landley PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 413966a082fSRob Landley printf("Checking PROT_EXEC mmap in %s...",tempdir); 414966a082fSRob Landley fflush(stdout); 415966a082fSRob Landley if (addr == MAP_FAILED) { 416966a082fSRob Landley err = errno; 417966a082fSRob Landley perror("failed"); 418c9a3072dSWANG Cong close(fd); 419966a082fSRob Landley if (err == EPERM) 420966a082fSRob Landley printf("%s must be not mounted noexec\n",tempdir); 421966a082fSRob Landley exit(1); 422966a082fSRob Landley } 423966a082fSRob Landley printf("OK\n"); 424966a082fSRob Landley munmap(addr, UM_KERN_PAGE_SIZE); 425966a082fSRob Landley 426966a082fSRob Landley close(fd); 427966a082fSRob Landley } 428