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 */ 20 static 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 */ 26 static char *tempdir = NULL; 27 28 static 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 * This will return 1, with the first character in buf being the 57 * character following the next instance of c in the file. This will 58 * read the file as needed. If there's an error, -errno is returned; 59 * if the end of the file is reached, 0 is returned. 60 */ 61 static int next(int fd, char *buf, size_t size, char c) 62 { 63 ssize_t n; 64 size_t len; 65 char *ptr; 66 67 while ((ptr = strchr(buf, c)) == NULL) { 68 n = read(fd, buf, size - 1); 69 if (n == 0) 70 return 0; 71 else if (n < 0) 72 return -errno; 73 74 buf[n] = '\0'; 75 } 76 77 ptr++; 78 len = strlen(ptr); 79 memmove(buf, ptr, len + 1); 80 81 /* 82 * Refill the buffer so that if there's a partial string that we care 83 * about, it will be completed, and we can recognize it. 84 */ 85 n = read(fd, &buf[len], size - len - 1); 86 if (n < 0) 87 return -errno; 88 89 buf[len + n] = '\0'; 90 return 1; 91 } 92 93 /* which_tmpdir is called only during early boot */ 94 static int checked_tmpdir = 0; 95 96 /* 97 * Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner 98 * way to do this than to parse /proc/mounts. statfs will return the 99 * same filesystem magic number and fs id for both /dev and /dev/shm 100 * when they are both tmpfs, so you can't tell if they are different 101 * filesystems. Also, there seems to be no other way of finding the 102 * mount point of a filesystem from within it. 103 * 104 * If a /dev/shm tmpfs entry is found, then we switch to using it. 105 * Otherwise, we stay with the default /tmp. 106 */ 107 static void which_tmpdir(void) 108 { 109 int fd, found; 110 char buf[128] = { '\0' }; 111 112 if (checked_tmpdir) 113 return; 114 115 checked_tmpdir = 1; 116 117 printf("Checking for tmpfs mount on /dev/shm..."); 118 119 fd = open("/proc/mounts", O_RDONLY); 120 if (fd < 0) { 121 printf("failed to open /proc/mounts, errno = %d\n", errno); 122 return; 123 } 124 125 while (1) { 126 found = next(fd, buf, ARRAY_SIZE(buf), ' '); 127 if (found != 1) 128 break; 129 130 if (!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) 131 goto found; 132 133 found = next(fd, buf, ARRAY_SIZE(buf), '\n'); 134 if (found != 1) 135 break; 136 } 137 138 err: 139 if (found == 0) 140 printf("nothing mounted on /dev/shm\n"); 141 else if (found < 0) 142 printf("read returned errno %d\n", -found); 143 144 out: 145 close(fd); 146 147 return; 148 149 found: 150 found = next(fd, buf, ARRAY_SIZE(buf), ' '); 151 if (found != 1) 152 goto err; 153 154 if (strncmp(buf, "tmpfs", strlen("tmpfs"))) { 155 printf("not tmpfs\n"); 156 goto out; 157 } 158 159 printf("OK\n"); 160 default_tmpdir = "/dev/shm"; 161 goto out; 162 } 163 164 static int __init make_tempfile(const char *template, char **out_tempname, 165 int do_unlink) 166 { 167 char *tempname; 168 int fd; 169 170 which_tmpdir(); 171 tempname = malloc(MAXPATHLEN); 172 if (tempname == NULL) 173 return -1; 174 175 find_tempdir(); 176 if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) 177 goto out; 178 179 if (template[0] != '/') 180 strcpy(tempname, tempdir); 181 else 182 tempname[0] = '\0'; 183 strncat(tempname, template, MAXPATHLEN-1-strlen(tempname)); 184 fd = mkstemp(tempname); 185 if (fd < 0) { 186 fprintf(stderr, "open - cannot create %s: %s\n", tempname, 187 strerror(errno)); 188 goto out; 189 } 190 if (do_unlink && (unlink(tempname) < 0)) { 191 perror("unlink"); 192 goto close; 193 } 194 if (out_tempname) { 195 *out_tempname = tempname; 196 } else 197 free(tempname); 198 return fd; 199 close: 200 close(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