diff options
Diffstat (limited to 'arch/um/os-Linux/mem.c')
-rw-r--r-- | arch/um/os-Linux/mem.c | 372 |
1 files changed, 75 insertions, 297 deletions
diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c index 3c4af77e51a2..897e9ad0c108 100644 --- a/arch/um/os-Linux/mem.c +++ b/arch/um/os-Linux/mem.c | |||
@@ -12,337 +12,117 @@ | |||
12 | #include <string.h> | 12 | #include <string.h> |
13 | #include <sys/stat.h> | 13 | #include <sys/stat.h> |
14 | #include <sys/mman.h> | 14 | #include <sys/mman.h> |
15 | #include <sys/param.h> | 15 | #include <sys/vfs.h> |
16 | #include <linux/magic.h> | ||
16 | #include <init.h> | 17 | #include <init.h> |
17 | #include <os.h> | 18 | #include <os.h> |
18 | 19 | ||
19 | /* Modified by which_tmpdir, which is called during early boot */ | 20 | /* Set by make_tempfile() 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; | 21 | static char *tempdir = NULL; |
27 | 22 | ||
28 | static void __init find_tempdir(void) | 23 | /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ |
24 | static int __init check_tmpfs(const char *dir) | ||
29 | { | 25 | { |
30 | const char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL }; | 26 | struct statfs st; |
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 | 27 | ||
45 | tempdir = malloc(strlen(dir) + 2); | 28 | printf("Checking if %s is on tmpfs...", dir); |
46 | if (tempdir == NULL) { | 29 | if (statfs(dir, &st) < 0) { |
47 | fprintf(stderr, "Failed to malloc tempdir, " | 30 | printf("%s\n", strerror(errno)); |
48 | "errno = %d\n", errno); | 31 | } else if (st.f_type != TMPFS_MAGIC) { |
49 | return; | 32 | printf("no\n"); |
50 | } | 33 | } else { |
51 | strcpy(tempdir, dir); | 34 | printf("OK\n"); |
52 | strcat(tempdir, "/"); | 35 | return 0; |
53 | } | ||
54 | |||
55 | /* | ||
56 | * Remove bytes from the front of the buffer and refill it so that if there's a | ||
57 | * partial string that we care about, it will be completed, and we can recognize | ||
58 | * it. | ||
59 | */ | ||
60 | static int pop(int fd, char *buf, size_t size, size_t npop) | ||
61 | { | ||
62 | ssize_t n; | ||
63 | size_t len = strlen(&buf[npop]); | ||
64 | |||
65 | memmove(buf, &buf[npop], len + 1); | ||
66 | n = read(fd, &buf[len], size - len - 1); | ||
67 | if (n < 0) | ||
68 | return -errno; | ||
69 | |||
70 | buf[len + n] = '\0'; | ||
71 | return 1; | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | * This will return 1, with the first character in buf being the | ||
76 | * character following the next instance of c in the file. This will | ||
77 | * read the file as needed. If there's an error, -errno is returned; | ||
78 | * if the end of the file is reached, 0 is returned. | ||
79 | */ | ||
80 | static int next(int fd, char *buf, size_t size, char c) | ||
81 | { | ||
82 | ssize_t n; | ||
83 | char *ptr; | ||
84 | |||
85 | while ((ptr = strchr(buf, c)) == NULL) { | ||
86 | n = read(fd, buf, size - 1); | ||
87 | if (n == 0) | ||
88 | return 0; | ||
89 | else if (n < 0) | ||
90 | return -errno; | ||
91 | |||
92 | buf[n] = '\0'; | ||
93 | } | 36 | } |
94 | 37 | return -1; | |
95 | return pop(fd, buf, size, ptr - buf + 1); | ||
96 | } | 38 | } |
97 | 39 | ||
98 | /* | 40 | /* |
99 | * Decode an octal-escaped and space-terminated path of the form used by | 41 | * Choose the tempdir to use. We want something on tmpfs so that our memory is |
100 | * /proc/mounts. May be used to decode a path in-place. "out" must be at least | 42 | * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the |
101 | * as large as the input. The output is always null-terminated. "len" gets the | 43 | * environment, we use that even if it's not on tmpfs, but we warn the user. |
102 | * length of the output, excluding the trailing null. Returns 0 if a full path | 44 | * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found |
103 | * was successfully decoded, otherwise an error. | 45 | * then we fall back to /tmp. |
104 | */ | 46 | */ |
105 | static int decode_path(const char *in, char *out, size_t *len) | 47 | static char * __init choose_tempdir(void) |
106 | { | 48 | { |
107 | char *first = out; | 49 | static const char * const vars[] = { |
108 | int c; | 50 | "TMPDIR", |
51 | "TMP", | ||
52 | "TEMP", | ||
53 | NULL | ||
54 | }; | ||
55 | static const char fallback_dir[] = "/tmp"; | ||
56 | static const char * const tmpfs_dirs[] = { | ||
57 | "/dev/shm", | ||
58 | fallback_dir, | ||
59 | NULL | ||
60 | }; | ||
109 | int i; | 61 | int i; |
110 | int ret = -EINVAL; | 62 | const char *dir; |
111 | while (1) { | 63 | |
112 | switch (*in) { | 64 | printf("Checking environment variables for a tempdir..."); |
113 | case '\0': | 65 | for (i = 0; vars[i]; i++) { |
114 | goto out; | 66 | dir = getenv(vars[i]); |
115 | 67 | if ((dir != NULL) && (*dir != '\0')) { | |
116 | case ' ': | 68 | printf("%s\n", dir); |
117 | ret = 0; | 69 | if (check_tmpfs(dir) >= 0) |
118 | goto out; | 70 | goto done; |
119 | 71 | else | |
120 | case '\\': | 72 | goto warn; |
121 | in++; | ||
122 | c = 0; | ||
123 | for (i = 0; i < 3; i++) { | ||
124 | if (*in < '0' || *in > '7') | ||
125 | goto out; | ||
126 | c = (c << 3) | (*in++ - '0'); | ||
127 | } | ||
128 | *(unsigned char *)out++ = (unsigned char) c; | ||
129 | break; | ||
130 | |||
131 | default: | ||
132 | *out++ = *in++; | ||
133 | break; | ||
134 | } | 73 | } |
135 | } | 74 | } |
75 | printf("none found\n"); | ||
136 | 76 | ||
137 | out: | 77 | for (i = 0; tmpfs_dirs[i]; i++) { |
138 | *out = '\0'; | 78 | dir = tmpfs_dirs[i]; |
139 | *len = out - first; | 79 | if (check_tmpfs(dir) >= 0) |
140 | return ret; | 80 | goto done; |
141 | } | ||
142 | |||
143 | /* | ||
144 | * Computes the length of s when encoded with three-digit octal escape sequences | ||
145 | * for the characters in chars. | ||
146 | */ | ||
147 | static size_t octal_encoded_length(const char *s, const char *chars) | ||
148 | { | ||
149 | size_t len = strlen(s); | ||
150 | while ((s = strpbrk(s, chars)) != NULL) { | ||
151 | len += 3; | ||
152 | s++; | ||
153 | } | ||
154 | |||
155 | return len; | ||
156 | } | ||
157 | |||
158 | enum { | ||
159 | OUTCOME_NOTHING_MOUNTED, | ||
160 | OUTCOME_TMPFS_MOUNT, | ||
161 | OUTCOME_NON_TMPFS_MOUNT, | ||
162 | }; | ||
163 | |||
164 | /* Read a line of /proc/mounts data looking for a tmpfs mount at "path". */ | ||
165 | static int read_mount(int fd, char *buf, size_t bufsize, const char *path, | ||
166 | int *outcome) | ||
167 | { | ||
168 | int found; | ||
169 | int match; | ||
170 | char *space; | ||
171 | size_t len; | ||
172 | |||
173 | enum { | ||
174 | MATCH_NONE, | ||
175 | MATCH_EXACT, | ||
176 | MATCH_PARENT, | ||
177 | }; | ||
178 | |||
179 | found = next(fd, buf, bufsize, ' '); | ||
180 | if (found != 1) | ||
181 | return found; | ||
182 | |||
183 | /* | ||
184 | * If there's no following space in the buffer, then this path is | ||
185 | * truncated, so it can't be the one we're looking for. | ||
186 | */ | ||
187 | space = strchr(buf, ' '); | ||
188 | if (space) { | ||
189 | match = MATCH_NONE; | ||
190 | if (!decode_path(buf, buf, &len)) { | ||
191 | if (!strcmp(buf, path)) | ||
192 | match = MATCH_EXACT; | ||
193 | else if (!strncmp(buf, path, len) | ||
194 | && (path[len] == '/' || !strcmp(buf, "/"))) | ||
195 | match = MATCH_PARENT; | ||
196 | } | ||
197 | |||
198 | found = pop(fd, buf, bufsize, space - buf + 1); | ||
199 | if (found != 1) | ||
200 | return found; | ||
201 | |||
202 | switch (match) { | ||
203 | case MATCH_EXACT: | ||
204 | if (!strncmp(buf, "tmpfs", strlen("tmpfs"))) | ||
205 | *outcome = OUTCOME_TMPFS_MOUNT; | ||
206 | else | ||
207 | *outcome = OUTCOME_NON_TMPFS_MOUNT; | ||
208 | break; | ||
209 | |||
210 | case MATCH_PARENT: | ||
211 | /* This mount obscures any previous ones. */ | ||
212 | *outcome = OUTCOME_NOTHING_MOUNTED; | ||
213 | break; | ||
214 | } | ||
215 | } | 81 | } |
216 | 82 | ||
217 | return next(fd, buf, bufsize, '\n'); | 83 | dir = fallback_dir; |
84 | warn: | ||
85 | printf("Warning: tempdir %s is not on tmpfs\n", dir); | ||
86 | done: | ||
87 | /* Make a copy since getenv results may not remain valid forever. */ | ||
88 | return strdup(dir); | ||
218 | } | 89 | } |
219 | 90 | ||
220 | /* which_tmpdir is called only during early boot */ | ||
221 | static int checked_tmpdir = 0; | ||
222 | |||
223 | /* | 91 | /* |
224 | * Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner | 92 | * Create an unlinked tempfile in a suitable tempdir. template must be the |
225 | * way to do this than to parse /proc/mounts. statfs will return the | 93 | * basename part of the template with a leading '/'. |
226 | * same filesystem magic number and fs id for both /dev and /dev/shm | ||
227 | * when they are both tmpfs, so you can't tell if they are different | ||
228 | * filesystems. Also, there seems to be no other way of finding the | ||
229 | * mount point of a filesystem from within it. | ||
230 | * | ||
231 | * If a /dev/shm tmpfs entry is found, then we switch to using it. | ||
232 | * Otherwise, we stay with the default /tmp. | ||
233 | */ | 94 | */ |
234 | static void which_tmpdir(void) | 95 | static int __init make_tempfile(const char *template) |
235 | { | 96 | { |
97 | char *tempname; | ||
236 | int fd; | 98 | int fd; |
237 | int found; | ||
238 | int outcome; | ||
239 | char *path; | ||
240 | char *buf; | ||
241 | size_t bufsize; | ||
242 | 99 | ||
243 | if (checked_tmpdir) | 100 | if (tempdir == NULL) { |
244 | return; | 101 | tempdir = choose_tempdir(); |
245 | 102 | if (tempdir == NULL) { | |
246 | checked_tmpdir = 1; | 103 | fprintf(stderr, "Failed to choose tempdir: %s\n", |
247 | 104 | strerror(errno)); | |
248 | printf("Checking for tmpfs mount on /dev/shm..."); | 105 | return -1; |
249 | |||
250 | path = realpath("/dev/shm", NULL); | ||
251 | if (!path) { | ||
252 | printf("failed to check real path, errno = %d\n", errno); | ||
253 | return; | ||
254 | } | ||
255 | printf("%s...", path); | ||
256 | |||
257 | /* | ||
258 | * The buffer needs to be able to fit the full octal-escaped path, a | ||
259 | * space, and a trailing null in order to successfully decode it. | ||
260 | */ | ||
261 | bufsize = octal_encoded_length(path, " \t\n\\") + 2; | ||
262 | |||
263 | if (bufsize < 128) | ||
264 | bufsize = 128; | ||
265 | |||
266 | buf = malloc(bufsize); | ||
267 | if (!buf) { | ||
268 | printf("malloc failed, errno = %d\n", errno); | ||
269 | goto out; | ||
270 | } | ||
271 | buf[0] = '\0'; | ||
272 | |||
273 | fd = open("/proc/mounts", O_RDONLY); | ||
274 | if (fd < 0) { | ||
275 | printf("failed to open /proc/mounts, errno = %d\n", errno); | ||
276 | goto out1; | ||
277 | } | ||
278 | |||
279 | outcome = OUTCOME_NOTHING_MOUNTED; | ||
280 | while (1) { | ||
281 | found = read_mount(fd, buf, bufsize, path, &outcome); | ||
282 | if (found != 1) | ||
283 | break; | ||
284 | } | ||
285 | |||
286 | if (found < 0) { | ||
287 | printf("read returned errno %d\n", -found); | ||
288 | } else { | ||
289 | switch (outcome) { | ||
290 | case OUTCOME_TMPFS_MOUNT: | ||
291 | printf("OK\n"); | ||
292 | default_tmpdir = "/dev/shm"; | ||
293 | break; | ||
294 | |||
295 | case OUTCOME_NON_TMPFS_MOUNT: | ||
296 | printf("not tmpfs\n"); | ||
297 | break; | ||
298 | |||
299 | default: | ||
300 | printf("nothing mounted on /dev/shm\n"); | ||
301 | break; | ||
302 | } | 106 | } |
303 | } | 107 | } |
304 | 108 | ||
305 | close(fd); | 109 | tempname = malloc(strlen(tempdir) + strlen(template) + 1); |
306 | out1: | ||
307 | free(buf); | ||
308 | out: | ||
309 | free(path); | ||
310 | } | ||
311 | |||
312 | static int __init make_tempfile(const char *template, char **out_tempname, | ||
313 | int do_unlink) | ||
314 | { | ||
315 | char *tempname; | ||
316 | int fd; | ||
317 | |||
318 | which_tmpdir(); | ||
319 | tempname = malloc(MAXPATHLEN); | ||
320 | if (tempname == NULL) | 110 | if (tempname == NULL) |
321 | return -1; | 111 | return -1; |
322 | 112 | ||
323 | find_tempdir(); | 113 | strcpy(tempname, tempdir); |
324 | if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) | 114 | strcat(tempname, template); |
325 | goto out; | ||
326 | |||
327 | if (template[0] != '/') | ||
328 | strcpy(tempname, tempdir); | ||
329 | else | ||
330 | tempname[0] = '\0'; | ||
331 | strncat(tempname, template, MAXPATHLEN-1-strlen(tempname)); | ||
332 | fd = mkstemp(tempname); | 115 | fd = mkstemp(tempname); |
333 | if (fd < 0) { | 116 | if (fd < 0) { |
334 | fprintf(stderr, "open - cannot create %s: %s\n", tempname, | 117 | fprintf(stderr, "open - cannot create %s: %s\n", tempname, |
335 | strerror(errno)); | 118 | strerror(errno)); |
336 | goto out; | 119 | goto out; |
337 | } | 120 | } |
338 | if (do_unlink && (unlink(tempname) < 0)) { | 121 | if (unlink(tempname) < 0) { |
339 | perror("unlink"); | 122 | perror("unlink"); |
340 | goto close; | 123 | goto close; |
341 | } | 124 | } |
342 | if (out_tempname) { | 125 | free(tempname); |
343 | *out_tempname = tempname; | ||
344 | } else | ||
345 | free(tempname); | ||
346 | return fd; | 126 | return fd; |
347 | close: | 127 | close: |
348 | close(fd); | 128 | close(fd); |
@@ -351,14 +131,14 @@ out: | |||
351 | return -1; | 131 | return -1; |
352 | } | 132 | } |
353 | 133 | ||
354 | #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" | 134 | #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" |
355 | 135 | ||
356 | static int __init create_tmp_file(unsigned long long len) | 136 | static int __init create_tmp_file(unsigned long long len) |
357 | { | 137 | { |
358 | int fd, err; | 138 | int fd, err; |
359 | char zero; | 139 | char zero; |
360 | 140 | ||
361 | fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); | 141 | fd = make_tempfile(TEMPNAME_TEMPLATE); |
362 | if (fd < 0) | 142 | if (fd < 0) |
363 | exit(1); | 143 | exit(1); |
364 | 144 | ||
@@ -402,7 +182,6 @@ int __init create_mem_file(unsigned long long len) | |||
402 | return fd; | 182 | return fd; |
403 | } | 183 | } |
404 | 184 | ||
405 | |||
406 | void __init check_tmpexec(void) | 185 | void __init check_tmpexec(void) |
407 | { | 186 | { |
408 | void *addr; | 187 | void *addr; |
@@ -410,14 +189,13 @@ void __init check_tmpexec(void) | |||
410 | 189 | ||
411 | addr = mmap(NULL, UM_KERN_PAGE_SIZE, | 190 | addr = mmap(NULL, UM_KERN_PAGE_SIZE, |
412 | PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); | 191 | PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); |
413 | printf("Checking PROT_EXEC mmap in %s...",tempdir); | 192 | printf("Checking PROT_EXEC mmap in %s...", tempdir); |
414 | fflush(stdout); | ||
415 | if (addr == MAP_FAILED) { | 193 | if (addr == MAP_FAILED) { |
416 | err = errno; | 194 | err = errno; |
417 | perror("failed"); | 195 | printf("%s\n", strerror(err)); |
418 | close(fd); | 196 | close(fd); |
419 | if (err == EPERM) | 197 | if (err == EPERM) |
420 | printf("%s must be not mounted noexec\n",tempdir); | 198 | printf("%s must be not mounted noexec\n", tempdir); |
421 | exit(1); | 199 | exit(1); |
422 | } | 200 | } |
423 | printf("OK\n"); | 201 | printf("OK\n"); |