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> 16*37185b33SAl Viro #include <init.h> 17*37185b33SAl 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 /* 565134d8feSJeff Dike * This will return 1, with the first character in buf being the 57966a082fSRob Landley * character following the next instance of c in the file. This will 58966a082fSRob Landley * read the file as needed. If there's an error, -errno is returned; 59966a082fSRob Landley * if the end of the file is reached, 0 is returned. 60966a082fSRob Landley */ 61c0a9290eSWANG Cong static int next(int fd, char *buf, size_t size, char c) 62966a082fSRob Landley { 63c0a9290eSWANG Cong ssize_t n; 64c0a9290eSWANG Cong size_t len; 65966a082fSRob Landley char *ptr; 66966a082fSRob Landley 67966a082fSRob Landley while ((ptr = strchr(buf, c)) == NULL) { 68966a082fSRob Landley n = read(fd, buf, size - 1); 69966a082fSRob Landley if (n == 0) 70966a082fSRob Landley return 0; 71966a082fSRob Landley else if (n < 0) 72966a082fSRob Landley return -errno; 73966a082fSRob Landley 74966a082fSRob Landley buf[n] = '\0'; 75966a082fSRob Landley } 76966a082fSRob Landley 77966a082fSRob Landley ptr++; 78c2b7a4bbSJeff Dike len = strlen(ptr); 79c2b7a4bbSJeff Dike memmove(buf, ptr, len + 1); 80c2b7a4bbSJeff Dike 815134d8feSJeff Dike /* 825134d8feSJeff Dike * Refill the buffer so that if there's a partial string that we care 83c2b7a4bbSJeff Dike * about, it will be completed, and we can recognize it. 84c2b7a4bbSJeff Dike */ 85c2b7a4bbSJeff Dike n = read(fd, &buf[len], size - len - 1); 86c2b7a4bbSJeff Dike if (n < 0) 87c2b7a4bbSJeff Dike return -errno; 88c2b7a4bbSJeff Dike 89c2b7a4bbSJeff Dike buf[len + n] = '\0'; 90966a082fSRob Landley return 1; 91966a082fSRob Landley } 92966a082fSRob Landley 936bf79482SJeff Dike /* which_tmpdir is called only during early boot */ 94966a082fSRob Landley static int checked_tmpdir = 0; 95966a082fSRob Landley 965134d8feSJeff Dike /* 975134d8feSJeff Dike * Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner 98966a082fSRob Landley * way to do this than to parse /proc/mounts. statfs will return the 99966a082fSRob Landley * same filesystem magic number and fs id for both /dev and /dev/shm 100966a082fSRob Landley * when they are both tmpfs, so you can't tell if they are different 101966a082fSRob Landley * filesystems. Also, there seems to be no other way of finding the 102966a082fSRob Landley * mount point of a filesystem from within it. 103966a082fSRob Landley * 104966a082fSRob Landley * If a /dev/shm tmpfs entry is found, then we switch to using it. 105966a082fSRob Landley * Otherwise, we stay with the default /tmp. 106966a082fSRob Landley */ 107966a082fSRob Landley static void which_tmpdir(void) 108966a082fSRob Landley { 109966a082fSRob Landley int fd, found; 110966a082fSRob Landley char buf[128] = { '\0' }; 111966a082fSRob Landley 112966a082fSRob Landley if (checked_tmpdir) 113966a082fSRob Landley return; 114966a082fSRob Landley 115966a082fSRob Landley checked_tmpdir = 1; 116966a082fSRob Landley 117966a082fSRob Landley printf("Checking for tmpfs mount on /dev/shm..."); 118966a082fSRob Landley 119966a082fSRob Landley fd = open("/proc/mounts", O_RDONLY); 120966a082fSRob Landley if (fd < 0) { 121966a082fSRob Landley printf("failed to open /proc/mounts, errno = %d\n", errno); 122966a082fSRob Landley return; 123966a082fSRob Landley } 124966a082fSRob Landley 125966a082fSRob Landley while (1) { 12691b165c0SJeff Dike found = next(fd, buf, ARRAY_SIZE(buf), ' '); 127966a082fSRob Landley if (found != 1) 128966a082fSRob Landley break; 129966a082fSRob Landley 130966a082fSRob Landley if (!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) 131966a082fSRob Landley goto found; 132966a082fSRob Landley 13391b165c0SJeff Dike found = next(fd, buf, ARRAY_SIZE(buf), '\n'); 134966a082fSRob Landley if (found != 1) 135966a082fSRob Landley break; 136966a082fSRob Landley } 137966a082fSRob Landley 138966a082fSRob Landley err: 139966a082fSRob Landley if (found == 0) 140966a082fSRob Landley printf("nothing mounted on /dev/shm\n"); 141966a082fSRob Landley else if (found < 0) 142966a082fSRob Landley printf("read returned errno %d\n", -found); 143966a082fSRob Landley 14480c13749SJeff Dike out: 14580c13749SJeff Dike close(fd); 14680c13749SJeff Dike 147966a082fSRob Landley return; 148966a082fSRob Landley 149966a082fSRob Landley found: 15091b165c0SJeff Dike found = next(fd, buf, ARRAY_SIZE(buf), ' '); 151966a082fSRob Landley if (found != 1) 152966a082fSRob Landley goto err; 153966a082fSRob Landley 154966a082fSRob Landley if (strncmp(buf, "tmpfs", strlen("tmpfs"))) { 155966a082fSRob Landley printf("not tmpfs\n"); 15680c13749SJeff Dike goto out; 157966a082fSRob Landley } 158966a082fSRob Landley 159966a082fSRob Landley printf("OK\n"); 160966a082fSRob Landley default_tmpdir = "/dev/shm"; 16180c13749SJeff Dike goto out; 162966a082fSRob Landley } 163966a082fSRob Landley 1645134d8feSJeff Dike static int __init make_tempfile(const char *template, char **out_tempname, 16536e45463SJeff Dike int do_unlink) 1660f80bc85SJeff Dike { 16787276f72SPaolo 'Blaisorblade' Giarrusso char *tempname; 1680f80bc85SJeff Dike int fd; 1690f80bc85SJeff Dike 170966a082fSRob Landley which_tmpdir(); 17187276f72SPaolo 'Blaisorblade' Giarrusso tempname = malloc(MAXPATHLEN); 17211a7ac23SJim Meyering if (tempname == NULL) 17311a7ac23SJim Meyering return -1; 17487276f72SPaolo 'Blaisorblade' Giarrusso 1750f80bc85SJeff Dike find_tempdir(); 17611a7ac23SJim Meyering if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) 1772a6d0ac1SDavidlohr Bueso goto out; 17811a7ac23SJim Meyering 17987276f72SPaolo 'Blaisorblade' Giarrusso if (template[0] != '/') 1800f80bc85SJeff Dike strcpy(tempname, tempdir); 1810f80bc85SJeff Dike else 18287276f72SPaolo 'Blaisorblade' Giarrusso tempname[0] = '\0'; 183c9a3072dSWANG Cong strncat(tempname, template, MAXPATHLEN-1-strlen(tempname)); 1840f80bc85SJeff Dike fd = mkstemp(tempname); 1850f80bc85SJeff Dike if (fd < 0) { 1860f80bc85SJeff Dike fprintf(stderr, "open - cannot create %s: %s\n", tempname, 1870f80bc85SJeff Dike strerror(errno)); 18887276f72SPaolo 'Blaisorblade' Giarrusso goto out; 1890f80bc85SJeff Dike } 1900f80bc85SJeff Dike if (do_unlink && (unlink(tempname) < 0)) { 1910f80bc85SJeff Dike perror("unlink"); 1922a6d0ac1SDavidlohr Bueso goto close; 1930f80bc85SJeff Dike } 1940f80bc85SJeff Dike if (out_tempname) { 19587276f72SPaolo 'Blaisorblade' Giarrusso *out_tempname = tempname; 19611a7ac23SJim Meyering } else 19787276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 19881999a01SJeff Dike return fd; 1992a6d0ac1SDavidlohr Bueso close: 2002a6d0ac1SDavidlohr Bueso close(fd); 20187276f72SPaolo 'Blaisorblade' Giarrusso out: 20287276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 20387276f72SPaolo 'Blaisorblade' Giarrusso return -1; 2040f80bc85SJeff Dike } 2050f80bc85SJeff Dike 2060f80bc85SJeff Dike #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" 2070f80bc85SJeff Dike 2085134d8feSJeff Dike static int __init create_tmp_file(unsigned long long len) 2090f80bc85SJeff Dike { 2100f80bc85SJeff Dike int fd, err; 2110f80bc85SJeff Dike char zero; 2120f80bc85SJeff Dike 2130f80bc85SJeff Dike fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); 2145134d8feSJeff Dike if (fd < 0) 2150f80bc85SJeff Dike exit(1); 2160f80bc85SJeff Dike 2170f80bc85SJeff Dike err = fchmod(fd, 0777); 2180f80bc85SJeff Dike if (err < 0) { 219512b6fb1SJeff Dike perror("fchmod"); 2200f80bc85SJeff Dike exit(1); 2210f80bc85SJeff Dike } 2220f80bc85SJeff Dike 2235134d8feSJeff Dike /* 2245134d8feSJeff Dike * Seek to len - 1 because writing a character there will 225190f4939SJeff Dike * increase the file size by one byte, to the desired length. 226190f4939SJeff Dike */ 227190f4939SJeff Dike if (lseek64(fd, len - 1, SEEK_SET) < 0) { 228512b6fb1SJeff Dike perror("lseek64"); 2290f80bc85SJeff Dike exit(1); 2300f80bc85SJeff Dike } 2310f80bc85SJeff Dike 2320f80bc85SJeff Dike zero = 0; 2330f80bc85SJeff Dike 234a61f334fSJeff Dike err = write(fd, &zero, 1); 2350f80bc85SJeff Dike if (err != 1) { 236a61f334fSJeff Dike perror("write"); 2370f80bc85SJeff Dike exit(1); 2380f80bc85SJeff Dike } 2390f80bc85SJeff Dike 24081999a01SJeff Dike return fd; 2410f80bc85SJeff Dike } 2420f80bc85SJeff Dike 24336e45463SJeff Dike int __init create_mem_file(unsigned long long len) 2440f80bc85SJeff Dike { 2450f80bc85SJeff Dike int err, fd; 2460f80bc85SJeff Dike 24702dea087SJeff Dike fd = create_tmp_file(len); 2480f80bc85SJeff Dike 249512b6fb1SJeff Dike err = os_set_exec_close(fd); 2500f80bc85SJeff Dike if (err < 0) { 2510f80bc85SJeff Dike errno = -err; 2520f80bc85SJeff Dike perror("exec_close"); 2530f80bc85SJeff Dike } 25481999a01SJeff Dike return fd; 2550f80bc85SJeff Dike } 256966a082fSRob Landley 257966a082fSRob Landley 25836e45463SJeff Dike void __init check_tmpexec(void) 259966a082fSRob Landley { 260966a082fSRob Landley void *addr; 261966a082fSRob Landley int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); 262966a082fSRob Landley 263966a082fSRob Landley addr = mmap(NULL, UM_KERN_PAGE_SIZE, 264966a082fSRob Landley PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 265966a082fSRob Landley printf("Checking PROT_EXEC mmap in %s...",tempdir); 266966a082fSRob Landley fflush(stdout); 267966a082fSRob Landley if (addr == MAP_FAILED) { 268966a082fSRob Landley err = errno; 269966a082fSRob Landley perror("failed"); 270c9a3072dSWANG Cong close(fd); 271966a082fSRob Landley if (err == EPERM) 272966a082fSRob Landley printf("%s must be not mounted noexec\n",tempdir); 273966a082fSRob Landley exit(1); 274966a082fSRob Landley } 275966a082fSRob Landley printf("OK\n"); 276966a082fSRob Landley munmap(addr, UM_KERN_PAGE_SIZE); 277966a082fSRob Landley 278966a082fSRob Landley close(fd); 279966a082fSRob Landley } 280