Commit c089b229dfdd09d59a11d8bc2344bf8196d575ce
Exists in
master
and in
13 other branches
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
Pull UML fixes from Richard Weinberger: "Assorted fixes for UML" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml: um: Memory corruption on startup um: Missing pipe handling uml: Simplify tempdir logic.
Showing 5 changed files Side-by-side Diff
arch/um/include/shared/os.h
... | ... | @@ -136,6 +136,7 @@ |
136 | 136 | extern int os_get_ifname(int fd, char *namebuf); |
137 | 137 | extern int os_set_slip(int fd); |
138 | 138 | extern int os_mode_fd(int fd, int mode); |
139 | +extern int os_fsync_file(int fd); | |
139 | 140 | |
140 | 141 | extern int os_seek_file(int fd, unsigned long long offset); |
141 | 142 | extern int os_open_file(const char *file, struct openflags flags, int mode); |
arch/um/kernel/physmem.c
... | ... | @@ -103,6 +103,7 @@ |
103 | 103 | */ |
104 | 104 | os_seek_file(physmem_fd, __pa(&__syscall_stub_start)); |
105 | 105 | os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE); |
106 | + os_fsync_file(physmem_fd); | |
106 | 107 | |
107 | 108 | bootmap_size = init_bootmem(pfn, pfn + delta); |
108 | 109 | free_bootmem(__pa(reserve_end) + bootmap_size, |
arch/um/os-Linux/file.c
arch/um/os-Linux/main.c
arch/um/os-Linux/mem.c
... | ... | @@ -12,337 +12,117 @@ |
12 | 12 | #include <string.h> |
13 | 13 | #include <sys/stat.h> |
14 | 14 | #include <sys/mman.h> |
15 | -#include <sys/param.h> | |
15 | +#include <sys/vfs.h> | |
16 | +#include <linux/magic.h> | |
16 | 17 | #include <init.h> |
17 | 18 | #include <os.h> |
18 | 19 | |
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 | - */ | |
20 | +/* Set by make_tempfile() during early boot. */ | |
26 | 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 }; | |
31 | - int i; | |
32 | - char *dir = NULL; | |
26 | + struct statfs st; | |
33 | 27 | |
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; | |
28 | + printf("Checking if %s is on tmpfs...", dir); | |
29 | + if (statfs(dir, &st) < 0) { | |
30 | + printf("%s\n", strerror(errno)); | |
31 | + } else if (st.f_type != TMPFS_MAGIC) { | |
32 | + printf("no\n"); | |
33 | + } else { | |
34 | + printf("OK\n"); | |
35 | + return 0; | |
41 | 36 | } |
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, "/"); | |
37 | + return -1; | |
53 | 38 | } |
54 | 39 | |
55 | 40 | /* |
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. | |
41 | + * Choose the tempdir to use. We want something on tmpfs so that our memory is | |
42 | + * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the | |
43 | + * environment, we use that even if it's not on tmpfs, but we warn the user. | |
44 | + * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found | |
45 | + * then we fall back to /tmp. | |
59 | 46 | */ |
60 | -static int pop(int fd, char *buf, size_t size, size_t npop) | |
47 | +static char * __init choose_tempdir(void) | |
61 | 48 | { |
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 | - } | |
94 | - | |
95 | - return pop(fd, buf, size, ptr - buf + 1); | |
96 | -} | |
97 | - | |
98 | -/* | |
99 | - * Decode an octal-escaped and space-terminated path of the form used by | |
100 | - * /proc/mounts. May be used to decode a path in-place. "out" must be at least | |
101 | - * as large as the input. The output is always null-terminated. "len" gets the | |
102 | - * length of the output, excluding the trailing null. Returns 0 if a full path | |
103 | - * was successfully decoded, otherwise an error. | |
104 | - */ | |
105 | -static int decode_path(const char *in, char *out, size_t *len) | |
106 | -{ | |
107 | - char *first = out; | |
108 | - int c; | |
49 | + static const char * const vars[] = { | |
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 | 61 | int i; |
110 | - int ret = -EINVAL; | |
111 | - while (1) { | |
112 | - switch (*in) { | |
113 | - case '\0': | |
114 | - goto out; | |
62 | + const char *dir; | |
115 | 63 | |
116 | - case ' ': | |
117 | - ret = 0; | |
118 | - goto out; | |
119 | - | |
120 | - case '\\': | |
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; | |
64 | + printf("Checking environment variables for a tempdir..."); | |
65 | + for (i = 0; vars[i]; i++) { | |
66 | + dir = getenv(vars[i]); | |
67 | + if ((dir != NULL) && (*dir != '\0')) { | |
68 | + printf("%s\n", dir); | |
69 | + if (check_tmpfs(dir) >= 0) | |
70 | + goto done; | |
71 | + else | |
72 | + goto warn; | |
134 | 73 | } |
135 | 74 | } |
75 | + printf("none found\n"); | |
136 | 76 | |
137 | -out: | |
138 | - *out = '\0'; | |
139 | - *len = out - first; | |
140 | - return ret; | |
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++; | |
77 | + for (i = 0; tmpfs_dirs[i]; i++) { | |
78 | + dir = tmpfs_dirs[i]; | |
79 | + if (check_tmpfs(dir) >= 0) | |
80 | + goto done; | |
153 | 81 | } |
154 | 82 | |
155 | - return len; | |
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); | |
156 | 89 | } |
157 | 90 | |
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 | - } | |
216 | - | |
217 | - return next(fd, buf, bufsize, '\n'); | |
218 | -} | |
219 | - | |
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 | |
225 | - * way to do this than to parse /proc/mounts. statfs will return the | |
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. | |
92 | + * Create an unlinked tempfile in a suitable tempdir. template must be the | |
93 | + * basename part of the template with a leading '/'. | |
233 | 94 | */ |
234 | -static void which_tmpdir(void) | |
95 | +static int __init make_tempfile(const char *template) | |
235 | 96 | { |
97 | + char *tempname; | |
236 | 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) | |
244 | - return; | |
245 | - | |
246 | - checked_tmpdir = 1; | |
247 | - | |
248 | - printf("Checking for tmpfs mount on /dev/shm..."); | |
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; | |
100 | + if (tempdir == NULL) { | |
101 | + tempdir = choose_tempdir(); | |
102 | + if (tempdir == NULL) { | |
103 | + fprintf(stderr, "Failed to choose tempdir: %s\n", | |
104 | + strerror(errno)); | |
105 | + return -1; | |
302 | 106 | } |
303 | 107 | } |
304 | 108 | |
305 | - close(fd); | |
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); | |
109 | + tempname = malloc(strlen(tempdir) + strlen(template) + 1); | |
320 | 110 | if (tempname == NULL) |
321 | 111 | return -1; |
322 | 112 | |
323 | - find_tempdir(); | |
324 | - if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) | |
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)); | |
113 | + strcpy(tempname, tempdir); | |
114 | + strcat(tempname, template); | |
332 | 115 | fd = mkstemp(tempname); |
333 | 116 | if (fd < 0) { |
334 | 117 | fprintf(stderr, "open - cannot create %s: %s\n", tempname, |
335 | 118 | strerror(errno)); |
336 | 119 | goto out; |
337 | 120 | } |
338 | - if (do_unlink && (unlink(tempname) < 0)) { | |
121 | + if (unlink(tempname) < 0) { | |
339 | 122 | perror("unlink"); |
340 | 123 | goto close; |
341 | 124 | } |
342 | - if (out_tempname) { | |
343 | - *out_tempname = tempname; | |
344 | - } else | |
345 | - free(tempname); | |
125 | + free(tempname); | |
346 | 126 | return fd; |
347 | 127 | close: |
348 | 128 | close(fd); |
349 | 129 | |
... | ... | @@ -351,14 +131,14 @@ |
351 | 131 | return -1; |
352 | 132 | } |
353 | 133 | |
354 | -#define TEMPNAME_TEMPLATE "vm_file-XXXXXX" | |
134 | +#define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" | |
355 | 135 | |
356 | 136 | static int __init create_tmp_file(unsigned long long len) |
357 | 137 | { |
358 | 138 | int fd, err; |
359 | 139 | char zero; |
360 | 140 | |
361 | - fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); | |
141 | + fd = make_tempfile(TEMPNAME_TEMPLATE); | |
362 | 142 | if (fd < 0) |
363 | 143 | exit(1); |
364 | 144 | |
... | ... | @@ -402,7 +182,6 @@ |
402 | 182 | return fd; |
403 | 183 | } |
404 | 184 | |
405 | - | |
406 | 185 | void __init check_tmpexec(void) |
407 | 186 | { |
408 | 187 | void *addr; |
409 | 188 | |
410 | 189 | |
... | ... | @@ -410,14 +189,13 @@ |
410 | 189 | |
411 | 190 | addr = mmap(NULL, UM_KERN_PAGE_SIZE, |
412 | 191 | PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); |
413 | - printf("Checking PROT_EXEC mmap in %s...",tempdir); | |
414 | - fflush(stdout); | |
192 | + printf("Checking PROT_EXEC mmap in %s...", tempdir); | |
415 | 193 | if (addr == MAP_FAILED) { |
416 | 194 | err = errno; |
417 | - perror("failed"); | |
195 | + printf("%s\n", strerror(err)); | |
418 | 196 | close(fd); |
419 | 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 | 199 | exit(1); |
422 | 200 | } |
423 | 201 | printf("OK\n"); |