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 "kern_constants.h" 18 #include "os.h" 19 #include "user.h" 20 21 /* Modified by which_tmpdir, which is called during early boot */ 22 static char *default_tmpdir = "/tmp"; 23 24 /* 25 * Modified when creating the physical memory file and when checking 26 * the tmp filesystem for usability, both happening during early boot. 27 */ 28 static char *tempdir = NULL; 29 30 static void __init find_tempdir(void) 31 { 32 const char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL }; 33 int i; 34 char *dir = NULL; 35 36 if (tempdir != NULL) 37 /* We've already been called */ 38 return; 39 for (i = 0; dirs[i]; i++) { 40 dir = getenv(dirs[i]); 41 if ((dir != NULL) && (*dir != '\0')) 42 break; 43 } 44 if ((dir == NULL) || (*dir == '\0')) 45 dir = default_tmpdir; 46 47 tempdir = malloc(strlen(dir) + 2); 48 if (tempdir == NULL) { 49 fprintf(stderr, "Failed to malloc tempdir, " 50 "errno = %d\n", errno); 51 return; 52 } 53 strcpy(tempdir, dir); 54 strcat(tempdir, "/"); 55 } 56 57 /* 58 * This will return 1, with the first character in buf being the 59 * character following the next instance of c in the file. This will 60 * read the file as needed. If there's an error, -errno is returned; 61 * if the end of the file is reached, 0 is returned. 62 */ 63 static int next(int fd, char *buf, size_t size, char c) 64 { 65 ssize_t n; 66 size_t len; 67 char *ptr; 68 69 while ((ptr = strchr(buf, c)) == NULL) { 70 n = read(fd, buf, size - 1); 71 if (n == 0) 72 return 0; 73 else if (n < 0) 74 return -errno; 75 76 buf[n] = '\0'; 77 } 78 79 ptr++; 80 len = strlen(ptr); 81 memmove(buf, ptr, len + 1); 82 83 /* 84 * Refill the buffer so that if there's a partial string that we care 85 * about, it will be completed, and we can recognize it. 86 */ 87 n = read(fd, &buf[len], size - len - 1); 88 if (n < 0) 89 return -errno; 90 91 buf[len + n] = '\0'; 92 return 1; 93 } 94 95 /* which_tmpdir is called only during early boot */ 96 static int checked_tmpdir = 0; 97 98 /* 99 * Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner 100 * way to do this than to parse /proc/mounts. statfs will return the 101 * same filesystem magic number and fs id for both /dev and /dev/shm 102 * when they are both tmpfs, so you can't tell if they are different 103 * filesystems. Also, there seems to be no other way of finding the 104 * mount point of a filesystem from within it. 105 * 106 * If a /dev/shm tmpfs entry is found, then we switch to using it. 107 * Otherwise, we stay with the default /tmp. 108 */ 109 static void which_tmpdir(void) 110 { 111 int fd, found; 112 char buf[128] = { '\0' }; 113 114 if (checked_tmpdir) 115 return; 116 117 checked_tmpdir = 1; 118 119 printf("Checking for tmpfs mount on /dev/shm..."); 120 121 fd = open("/proc/mounts", O_RDONLY); 122 if (fd < 0) { 123 printf("failed to open /proc/mounts, errno = %d\n", errno); 124 return; 125 } 126 127 while (1) { 128 found = next(fd, buf, ARRAY_SIZE(buf), ' '); 129 if (found != 1) 130 break; 131 132 if (!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) 133 goto found; 134 135 found = next(fd, buf, ARRAY_SIZE(buf), '\n'); 136 if (found != 1) 137 break; 138 } 139 140 err: 141 if (found == 0) 142 printf("nothing mounted on /dev/shm\n"); 143 else if (found < 0) 144 printf("read returned errno %d\n", -found); 145 146 out: 147 close(fd); 148 149 return; 150 151 found: 152 found = next(fd, buf, ARRAY_SIZE(buf), ' '); 153 if (found != 1) 154 goto err; 155 156 if (strncmp(buf, "tmpfs", strlen("tmpfs"))) { 157 printf("not tmpfs\n"); 158 goto out; 159 } 160 161 printf("OK\n"); 162 default_tmpdir = "/dev/shm"; 163 goto out; 164 } 165 166 static int __init make_tempfile(const char *template, char **out_tempname, 167 int do_unlink) 168 { 169 char *tempname; 170 int fd; 171 172 which_tmpdir(); 173 tempname = malloc(MAXPATHLEN); 174 if (tempname == NULL) 175 return -1; 176 177 find_tempdir(); 178 if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) 179 return -1; 180 181 if (template[0] != '/') 182 strcpy(tempname, tempdir); 183 else 184 tempname[0] = '\0'; 185 strncat(tempname, template, MAXPATHLEN-1-strlen(tempname)); 186 fd = mkstemp(tempname); 187 if (fd < 0) { 188 fprintf(stderr, "open - cannot create %s: %s\n", tempname, 189 strerror(errno)); 190 goto out; 191 } 192 if (do_unlink && (unlink(tempname) < 0)) { 193 perror("unlink"); 194 goto out; 195 } 196 if (out_tempname) { 197 *out_tempname = tempname; 198 } else 199 free(tempname); 200 return fd; 201 out: 202 free(tempname); 203 return -1; 204 } 205 206 #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" 207 208 static int __init create_tmp_file(unsigned long long len) 209 { 210 int fd, err; 211 char zero; 212 213 fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); 214 if (fd < 0) 215 exit(1); 216 217 err = fchmod(fd, 0777); 218 if (err < 0) { 219 perror("fchmod"); 220 exit(1); 221 } 222 223 /* 224 * Seek to len - 1 because writing a character there will 225 * increase the file size by one byte, to the desired length. 226 */ 227 if (lseek64(fd, len - 1, SEEK_SET) < 0) { 228 perror("lseek64"); 229 exit(1); 230 } 231 232 zero = 0; 233 234 err = write(fd, &zero, 1); 235 if (err != 1) { 236 perror("write"); 237 exit(1); 238 } 239 240 return fd; 241 } 242 243 int __init create_mem_file(unsigned long long len) 244 { 245 int err, fd; 246 247 fd = create_tmp_file(len); 248 249 err = os_set_exec_close(fd); 250 if (err < 0) { 251 errno = -err; 252 perror("exec_close"); 253 } 254 return fd; 255 } 256 257 258 void __init check_tmpexec(void) 259 { 260 void *addr; 261 int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); 262 263 addr = mmap(NULL, UM_KERN_PAGE_SIZE, 264 PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 265 printf("Checking PROT_EXEC mmap in %s...",tempdir); 266 fflush(stdout); 267 if (addr == MAP_FAILED) { 268 err = errno; 269 perror("failed"); 270 close(fd); 271 if (err == EPERM) 272 printf("%s must be not mounted noexec\n",tempdir); 273 exit(1); 274 } 275 printf("OK\n"); 276 munmap(addr, UM_KERN_PAGE_SIZE); 277 278 close(fd); 279 } 280