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