mem.c 5.5 KB
Newer Older
J
Jeff Dike 已提交
1 2 3 4 5
/*
 * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
 * Licensed under the GPL
 */

6 7
#include <stdio.h>
#include <stddef.h>
J
Jeff Dike 已提交
8
#include <stdlib.h>
9 10 11
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
J
Jeff Dike 已提交
12
#include <string.h>
13
#include <sys/mman.h>
J
Jeff Dike 已提交
14
#include <sys/param.h>
15 16
#include "init.h"
#include "kern_constants.h"
J
Jeff Dike 已提交
17 18
#include "os.h"
#include "user.h"
19

20
/* Modified by which_tmpdir, which is called during early boot */
21
static char *default_tmpdir = "/tmp";
22 23 24 25 26

/*
 *  Modified when creating the physical memory file and when checking
 * the tmp filesystem for usability, both happening during early boot.
 */
27 28 29 30
static char *tempdir = NULL;

static void __init find_tempdir(void)
{
W
WANG Cong 已提交
31
	const char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL };
32 33 34
	int i;
	char *dir = NULL;

J
Jeff Dike 已提交
35 36
	if (tempdir != NULL)
		/* We've already been called */
J
Jeff Dike 已提交
37
		return;
J
Jeff Dike 已提交
38
	for (i = 0; dirs[i]; i++) {
39
		dir = getenv(dirs[i]);
J
Jeff Dike 已提交
40
		if ((dir != NULL) && (*dir != '\0'))
41 42
			break;
	}
J
Jeff Dike 已提交
43
	if ((dir == NULL) || (*dir == '\0'))
44
		dir = default_tmpdir;
45 46

	tempdir = malloc(strlen(dir) + 2);
J
Jeff Dike 已提交
47
	if (tempdir == NULL) {
48 49 50 51 52 53 54 55
		fprintf(stderr, "Failed to malloc tempdir, "
			"errno = %d\n", errno);
		return;
	}
	strcpy(tempdir, dir);
	strcat(tempdir, "/");
}

J
Jeff Dike 已提交
56 57
/*
 * This will return 1, with the first character in buf being the
58 59 60 61
 * character following the next instance of c in the file.  This will
 * read the file as needed.  If there's an error, -errno is returned;
 * if the end of the file is reached, 0 is returned.
 */
W
WANG Cong 已提交
62
static int next(int fd, char *buf, size_t size, char c)
63
{
W
WANG Cong 已提交
64 65
	ssize_t n;
	size_t len;
66 67
	char *ptr;

J
Jeff Dike 已提交
68
	while ((ptr = strchr(buf, c)) == NULL) {
69
		n = read(fd, buf, size - 1);
J
Jeff Dike 已提交
70
		if (n == 0)
71
			return 0;
J
Jeff Dike 已提交
72
		else if (n < 0)
73 74 75 76 77 78
			return -errno;

		buf[n] = '\0';
	}

	ptr++;
79 80 81
	len = strlen(ptr);
	memmove(buf, ptr, len + 1);

J
Jeff Dike 已提交
82 83
	/*
	 * Refill the buffer so that if there's a partial string that we care
84 85 86
	 * about, it will be completed, and we can recognize it.
	 */
	n = read(fd, &buf[len], size - len - 1);
J
Jeff Dike 已提交
87
	if (n < 0)
88 89 90
		return -errno;

	buf[len + n] = '\0';
91 92 93
	return 1;
}

94
/* which_tmpdir is called only during early boot */
95 96
static int checked_tmpdir = 0;

J
Jeff Dike 已提交
97 98
/*
 * Look for a tmpfs mounted at /dev/shm.  I couldn't find a cleaner
99 100 101 102 103 104 105 106 107 108 109 110 111 112
 * way to do this than to parse /proc/mounts.  statfs will return the
 * same filesystem magic number and fs id for both /dev and /dev/shm
 * when they are both tmpfs, so you can't tell if they are different
 * filesystems.  Also, there seems to be no other way of finding the
 * mount point of a filesystem from within it.
 *
 * If a /dev/shm tmpfs entry is found, then we switch to using it.
 * Otherwise, we stay with the default /tmp.
 */
static void which_tmpdir(void)
{
	int fd, found;
	char buf[128] = { '\0' };

J
Jeff Dike 已提交
113
	if (checked_tmpdir)
114 115 116 117 118 119 120
		return;

	checked_tmpdir = 1;

	printf("Checking for tmpfs mount on /dev/shm...");

	fd = open("/proc/mounts", O_RDONLY);
J
Jeff Dike 已提交
121
	if (fd < 0) {
122 123 124 125
		printf("failed to open /proc/mounts, errno = %d\n", errno);
		return;
	}

J
Jeff Dike 已提交
126
	while (1) {
127
		found = next(fd, buf, ARRAY_SIZE(buf), ' ');
J
Jeff Dike 已提交
128
		if (found != 1)
129 130
			break;

J
Jeff Dike 已提交
131
		if (!strncmp(buf, "/dev/shm", strlen("/dev/shm")))
132 133
			goto found;

134
		found = next(fd, buf, ARRAY_SIZE(buf), '\n');
J
Jeff Dike 已提交
135
		if (found != 1)
136 137 138 139
			break;
	}

err:
J
Jeff Dike 已提交
140
	if (found == 0)
141
		printf("nothing mounted on /dev/shm\n");
J
Jeff Dike 已提交
142
	else if (found < 0)
143 144
		printf("read returned errno %d\n", -found);

145 146 147
out:
	close(fd);

148 149 150
	return;

found:
151
	found = next(fd, buf, ARRAY_SIZE(buf), ' ');
J
Jeff Dike 已提交
152
	if (found != 1)
153 154
		goto err;

J
Jeff Dike 已提交
155
	if (strncmp(buf, "tmpfs", strlen("tmpfs"))) {
156
		printf("not tmpfs\n");
157
		goto out;
158 159 160 161
	}

	printf("OK\n");
	default_tmpdir = "/dev/shm";
162
	goto out;
163 164
}

J
Jeff Dike 已提交
165 166
static int __init make_tempfile(const char *template, char **out_tempname,
				int do_unlink)
167
{
168
	char *tempname;
169 170
	int fd;

171
	which_tmpdir();
172
	tempname = malloc(MAXPATHLEN);
173 174
	if (tempname == NULL)
		return -1;
175

176
	find_tempdir();
177 178 179
	if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN))
		return -1;

180
	if (template[0] != '/')
181 182
		strcpy(tempname, tempdir);
	else
183
		tempname[0] = '\0';
184
	strncat(tempname, template, MAXPATHLEN-1-strlen(tempname));
185
	fd = mkstemp(tempname);
J
Jeff Dike 已提交
186
	if (fd < 0) {
187 188
		fprintf(stderr, "open - cannot create %s: %s\n", tempname,
			strerror(errno));
189
		goto out;
190
	}
J
Jeff Dike 已提交
191
	if (do_unlink && (unlink(tempname) < 0)) {
192
		perror("unlink");
193
		goto out;
194
	}
J
Jeff Dike 已提交
195
	if (out_tempname) {
196
		*out_tempname = tempname;
197
	} else
198
		free(tempname);
J
Jeff Dike 已提交
199
	return fd;
200 201 202
out:
	free(tempname);
	return -1;
203 204 205 206
}

#define TEMPNAME_TEMPLATE "vm_file-XXXXXX"

J
Jeff Dike 已提交
207
static int __init create_tmp_file(unsigned long long len)
208 209 210 211 212
{
	int fd, err;
	char zero;

	fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1);
J
Jeff Dike 已提交
213
	if (fd < 0)
214 215 216
		exit(1);

	err = fchmod(fd, 0777);
J
Jeff Dike 已提交
217
	if (err < 0) {
218
		perror("fchmod");
219 220 221
		exit(1);
	}

J
Jeff Dike 已提交
222 223
	/*
	 * Seek to len - 1 because writing a character there will
224 225 226
	 * increase the file size by one byte, to the desired length.
	 */
	if (lseek64(fd, len - 1, SEEK_SET) < 0) {
227
		perror("lseek64");
228 229 230 231 232
		exit(1);
	}

	zero = 0;

233
	err = write(fd, &zero, 1);
J
Jeff Dike 已提交
234
	if (err != 1) {
235
		perror("write");
236 237 238
		exit(1);
	}

J
Jeff Dike 已提交
239
	return fd;
240 241
}

J
Jeff Dike 已提交
242
int __init create_mem_file(unsigned long long len)
243 244 245
{
	int err, fd;

J
Jeff Dike 已提交
246
	fd = create_tmp_file(len);
247

248
	err = os_set_exec_close(fd);
J
Jeff Dike 已提交
249
	if (err < 0) {
250 251 252
		errno = -err;
		perror("exec_close");
	}
J
Jeff Dike 已提交
253
	return fd;
254
}
255 256


J
Jeff Dike 已提交
257
void __init check_tmpexec(void)
258 259 260 261 262 263 264 265
{
	void *addr;
	int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE);

	addr = mmap(NULL, UM_KERN_PAGE_SIZE,
		    PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
	printf("Checking PROT_EXEC mmap in %s...",tempdir);
	fflush(stdout);
J
Jeff Dike 已提交
266
	if (addr == MAP_FAILED) {
267 268
		err = errno;
		perror("failed");
269
		close(fd);
J
Jeff Dike 已提交
270
		if (err == EPERM)
271 272 273 274 275 276 277 278
			printf("%s must be not mounted noexec\n",tempdir);
		exit(1);
	}
	printf("OK\n");
	munmap(addr, UM_KERN_PAGE_SIZE);

	close(fd);
}