diff options
Diffstat (limited to 'arch/um/os-Linux/mem.c')
| -rw-r--r-- | arch/um/os-Linux/mem.c | 246 | 
1 files changed, 86 insertions, 160 deletions
diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c index e696144d2be..897e9ad0c10 100644 --- a/arch/um/os-Linux/mem.c +++ b/arch/um/os-Linux/mem.c @@ -12,205 +12,133 @@  #include <string.h>  #include <sys/stat.h>  #include <sys/mman.h> -#include <sys/param.h> -#include "init.h" -#include "kern_constants.h" -#include "os.h" -#include "user.h" +#include <sys/vfs.h> +#include <linux/magic.h> +#include <init.h> +#include <os.h> -/* Modified by which_tmpdir, which is called during early boot */ -static char *default_tmpdir = "/tmp"; - -/* - *  Modified when creating the physical memory file and when checking - * the tmp filesystem for usability, both happening during early boot. - */ +/* Set by make_tempfile() during early boot. */  static char *tempdir = NULL; -static void __init find_tempdir(void) +/* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ +static int __init check_tmpfs(const char *dir)  { -	const char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL }; -	int i; -	char *dir = NULL; - -	if (tempdir != NULL) -		/* We've already been called */ -		return; -	for (i = 0; dirs[i]; i++) { -		dir = getenv(dirs[i]); -		if ((dir != NULL) && (*dir != '\0')) -			break; +	struct statfs st; + +	printf("Checking if %s is on tmpfs...", dir); +	if (statfs(dir, &st) < 0) { +		printf("%s\n", strerror(errno)); +	} else if (st.f_type != TMPFS_MAGIC) { +		printf("no\n"); +	} else { +		printf("OK\n"); +		return 0;  	} -	if ((dir == NULL) || (*dir == '\0')) -		dir = default_tmpdir; - -	tempdir = malloc(strlen(dir) + 2); -	if (tempdir == NULL) { -		fprintf(stderr, "Failed to malloc tempdir, " -			"errno = %d\n", errno); -		return; -	} -	strcpy(tempdir, dir); -	strcat(tempdir, "/"); +	return -1;  }  /* - * This will return 1, with the first character in buf being the - * 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. + * Choose the tempdir to use. We want something on tmpfs so that our memory is + * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the + * environment, we use that even if it's not on tmpfs, but we warn the user. + * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found + * then we fall back to /tmp.   */ -static int next(int fd, char *buf, size_t size, char c) +static char * __init choose_tempdir(void)  { -	ssize_t n; -	size_t len; -	char *ptr; - -	while ((ptr = strchr(buf, c)) == NULL) { -		n = read(fd, buf, size - 1); -		if (n == 0) -			return 0; -		else if (n < 0) -			return -errno; - -		buf[n] = '\0'; +	static const char * const vars[] = { +		"TMPDIR", +		"TMP", +		"TEMP", +		NULL +	}; +	static const char fallback_dir[] = "/tmp"; +	static const char * const tmpfs_dirs[] = { +		"/dev/shm", +		fallback_dir, +		NULL +	}; +	int i; +	const char *dir; + +	printf("Checking environment variables for a tempdir..."); +	for (i = 0; vars[i]; i++) { +		dir = getenv(vars[i]); +		if ((dir != NULL) && (*dir != '\0')) { +			printf("%s\n", dir); +			if (check_tmpfs(dir) >= 0) +				goto done; +			else +				goto warn; +		}  	} +	printf("none found\n"); -	ptr++; -	len = strlen(ptr); -	memmove(buf, ptr, len + 1); - -	/* -	 * Refill the buffer so that if there's a partial string that we care -	 * about, it will be completed, and we can recognize it. -	 */ -	n = read(fd, &buf[len], size - len - 1); -	if (n < 0) -		return -errno; +	for (i = 0; tmpfs_dirs[i]; i++) { +		dir = tmpfs_dirs[i]; +		if (check_tmpfs(dir) >= 0) +			goto done; +	} -	buf[len + n] = '\0'; -	return 1; +	dir = fallback_dir; +warn: +	printf("Warning: tempdir %s is not on tmpfs\n", dir); +done: +	/* Make a copy since getenv results may not remain valid forever. */ +	return strdup(dir);  } -/* which_tmpdir is called only during early boot */ -static int checked_tmpdir = 0; -  /* - * Look for a tmpfs mounted at /dev/shm.  I couldn't find a cleaner - * 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. + * Create an unlinked tempfile in a suitable tempdir. template must be the + * basename part of the template with a leading '/'.   */ -static void which_tmpdir(void) -{ -	int fd, found; -	char buf[128] = { '\0' }; - -	if (checked_tmpdir) -		return; - -	checked_tmpdir = 1; - -	printf("Checking for tmpfs mount on /dev/shm..."); - -	fd = open("/proc/mounts", O_RDONLY); -	if (fd < 0) { -		printf("failed to open /proc/mounts, errno = %d\n", errno); -		return; -	} - -	while (1) { -		found = next(fd, buf, ARRAY_SIZE(buf), ' '); -		if (found != 1) -			break; - -		if (!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) -			goto found; - -		found = next(fd, buf, ARRAY_SIZE(buf), '\n'); -		if (found != 1) -			break; -	} - -err: -	if (found == 0) -		printf("nothing mounted on /dev/shm\n"); -	else if (found < 0) -		printf("read returned errno %d\n", -found); - -out: -	close(fd); - -	return; - -found: -	found = next(fd, buf, ARRAY_SIZE(buf), ' '); -	if (found != 1) -		goto err; - -	if (strncmp(buf, "tmpfs", strlen("tmpfs"))) { -		printf("not tmpfs\n"); -		goto out; -	} - -	printf("OK\n"); -	default_tmpdir = "/dev/shm"; -	goto out; -} - -static int __init make_tempfile(const char *template, char **out_tempname, -				int do_unlink) +static int __init make_tempfile(const char *template)  {  	char *tempname;  	int fd; -	which_tmpdir(); -	tempname = malloc(MAXPATHLEN); -	if (tempname == NULL) -		return -1; +	if (tempdir == NULL) { +		tempdir = choose_tempdir(); +		if (tempdir == NULL) { +			fprintf(stderr, "Failed to choose tempdir: %s\n", +				strerror(errno)); +			return -1; +		} +	} -	find_tempdir(); -	if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) +	tempname = malloc(strlen(tempdir) + strlen(template) + 1); +	if (tempname == NULL)  		return -1; -	if (template[0] != '/') -		strcpy(tempname, tempdir); -	else -		tempname[0] = '\0'; -	strncat(tempname, template, MAXPATHLEN-1-strlen(tempname)); +	strcpy(tempname, tempdir); +	strcat(tempname, template);  	fd = mkstemp(tempname);  	if (fd < 0) {  		fprintf(stderr, "open - cannot create %s: %s\n", tempname,  			strerror(errno));  		goto out;  	} -	if (do_unlink && (unlink(tempname) < 0)) { +	if (unlink(tempname) < 0) {  		perror("unlink"); -		goto out; +		goto close;  	} -	if (out_tempname) { -		*out_tempname = tempname; -	} else -		free(tempname); +	free(tempname);  	return fd; +close: +	close(fd);  out:  	free(tempname);  	return -1;  } -#define TEMPNAME_TEMPLATE "vm_file-XXXXXX" +#define TEMPNAME_TEMPLATE "/vm_file-XXXXXX"  static int __init create_tmp_file(unsigned long long len)  {  	int fd, err;  	char zero; -	fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); +	fd = make_tempfile(TEMPNAME_TEMPLATE);  	if (fd < 0)  		exit(1); @@ -254,7 +182,6 @@ int __init create_mem_file(unsigned long long len)  	return fd;  } -  void __init check_tmpexec(void)  {  	void *addr; @@ -262,14 +189,13 @@ void __init check_tmpexec(void)  	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); +	printf("Checking PROT_EXEC mmap in %s...", tempdir);  	if (addr == MAP_FAILED) {  		err = errno; -		perror("failed"); +		printf("%s\n", strerror(err));  		close(fd);  		if (err == EPERM) -			printf("%s must be not mounted noexec\n",tempdir); +			printf("%s must be not mounted noexec\n", tempdir);  		exit(1);  	}  	printf("OK\n");  | 
