Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | /* * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ #include <stdio.h> #include <stddef.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <sys/mman.h> #include <sys/vfs.h> #include <linux/magic.h> #include <init.h> #include <os.h> /* Set by make_tempfile() during early boot. */ static char *tempdir = NULL; /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ static int __init check_tmpfs(const char *dir) { struct statfs st; os_info("Checking if %s is on tmpfs...", dir); if (statfs(dir, &st) < 0) { os_info("%s\n", strerror(errno)); } else if (st.f_type != TMPFS_MAGIC) { os_info("no\n"); } else { os_info("OK\n"); return 0; } return -1; } /* * Choose the tempdir to use. We want something on tmpfs so that our memory is * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the * environment, we use that even if it's not on tmpfs, but we warn the user. * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found * then we fall back to /tmp. */ static char * __init choose_tempdir(void) { static const char * const vars[] = { "TMPDIR", "TMP", "TEMP", NULL }; static const char fallback_dir[] = "/tmp"; static const char * const tmpfs_dirs[] = { "/dev/shm", fallback_dir, NULL }; int i; const char *dir; os_info("Checking environment variables for a tempdir..."); for (i = 0; vars[i]; i++) { dir = getenv(vars[i]); if ((dir != NULL) && (*dir != '\0')) { os_info("%s\n", dir); if (check_tmpfs(dir) >= 0) goto done; else goto warn; } } os_info("none found\n"); for (i = 0; tmpfs_dirs[i]; i++) { dir = tmpfs_dirs[i]; if (check_tmpfs(dir) >= 0) goto done; } dir = fallback_dir; warn: os_warn("Warning: tempdir %s is not on tmpfs\n", dir); done: /* Make a copy since getenv results may not remain valid forever. */ return strdup(dir); } /* * Create an unlinked tempfile in a suitable tempdir. template must be the * basename part of the template with a leading '/'. */ static int __init make_tempfile(const char *template) { char *tempname; int fd; if (tempdir == NULL) { tempdir = choose_tempdir(); if (tempdir == NULL) { os_warn("Failed to choose tempdir: %s\n", strerror(errno)); return -1; } } #ifdef O_TMPFILE fd = open(tempdir, O_CLOEXEC | O_RDWR | O_EXCL | O_TMPFILE, 0700); /* * If the running system does not support O_TMPFILE flag then retry * without it. */ if (fd != -1 || (errno != EINVAL && errno != EISDIR && errno != EOPNOTSUPP)) return fd; #endif tempname = malloc(strlen(tempdir) + strlen(template) + 1); if (tempname == NULL) return -1; strcpy(tempname, tempdir); strcat(tempname, template); fd = mkstemp(tempname); if (fd < 0) { os_warn("open - cannot create %s: %s\n", tempname, strerror(errno)); goto out; } if (unlink(tempname) < 0) { perror("unlink"); goto close; } free(tempname); return fd; close: close(fd); out: free(tempname); return -1; } #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" static int __init create_tmp_file(unsigned long long len) { int fd, err; char zero; fd = make_tempfile(TEMPNAME_TEMPLATE); if (fd < 0) exit(1); /* * Seek to len - 1 because writing a character there will * increase the file size by one byte, to the desired length. */ if (lseek64(fd, len - 1, SEEK_SET) < 0) { perror("lseek64"); exit(1); } zero = 0; err = write(fd, &zero, 1); if (err != 1) { perror("write"); exit(1); } return fd; } int __init create_mem_file(unsigned long long len) { int err, fd; fd = create_tmp_file(len); err = os_set_exec_close(fd); if (err < 0) { errno = -err; perror("exec_close"); } return fd; } void __init check_tmpexec(void) { void *addr; int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); addr = mmap(NULL, UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); os_info("Checking PROT_EXEC mmap in %s...", tempdir); if (addr == MAP_FAILED) { err = errno; os_warn("%s\n", strerror(err)); close(fd); if (err == EPERM) os_warn("%s must be not mounted noexec\n", tempdir); exit(1); } os_info("OK\n"); munmap(addr, UM_KERN_PAGE_SIZE); close(fd); } |