aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTristan Schmelcher <tschmelcher@google.com>2013-11-11 13:03:06 -0500
committerRichard Weinberger <richard@nod.at>2014-04-20 17:10:44 -0400
commit0d71832e3004a0833938cc28a096823cd55b8e79 (patch)
treeb0079baf0cee87629cf68bd3cb2e1570a7565bf7
parenta798c10faf62a505d24e5f6213fbaf904a39623f (diff)
uml: Simplify tempdir logic.
Inferring the mount hierarchy correctly from /proc/mounts is hard when MS_MOVE may have been used, and the previous code did it wrongly. This change simplifies the logic to only require that /dev/shm be _on_ tmpfs (which can be checked trivially with statfs) rather than that it be a _mountpoint_ of tmpfs, since there isn't a compelling reason to be that strict. We also now check for tmpfs on whatever directory we ultimately use so that the user is better informed. This change also moves the more standard TMPDIR environment variable check ahead of the others. Applies to 3.12. Signed-off-by: Tristan Schmelcher <tschmelcher@google.com> Signed-off-by: Richard Weinberger <richard@nod.at>
-rw-r--r--arch/um/os-Linux/mem.c372
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. */
20static 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 */
26static char *tempdir = NULL; 21static char *tempdir = NULL;
27 22
28static void __init find_tempdir(void) 23/* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */
24static 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 */
60static 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 */
80static 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 */
105static int decode_path(const char *in, char *out, size_t *len) 47static 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
137out: 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 */
147static 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
158enum {
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". */
165static 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;
84warn:
85 printf("Warning: tempdir %s is not on tmpfs\n", dir);
86done:
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 */
221static 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 */
234static void which_tmpdir(void) 95static 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);
306out1:
307 free(buf);
308out:
309 free(path);
310}
311
312static 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;
347close: 127close:
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
356static int __init create_tmp_file(unsigned long long len) 136static 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
406void __init check_tmpexec(void) 185void __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");