15134d8feSJeff Dike /* 25134d8feSJeff Dike * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 35134d8feSJeff Dike * Licensed under the GPL 45134d8feSJeff Dike */ 55134d8feSJeff Dike 60f80bc85SJeff Dike #include <stdio.h> 70f80bc85SJeff Dike #include <stddef.h> 85134d8feSJeff Dike #include <stdlib.h> 90f80bc85SJeff Dike #include <unistd.h> 100f80bc85SJeff Dike #include <errno.h> 110f80bc85SJeff Dike #include <fcntl.h> 125134d8feSJeff Dike #include <string.h> 13fb967eccSLiu Aleaxander #include <sys/stat.h> 140f80bc85SJeff Dike #include <sys/mman.h> 150d71832eSTristan Schmelcher #include <sys/vfs.h> 160d71832eSTristan Schmelcher #include <linux/magic.h> 1737185b33SAl Viro #include <init.h> 1837185b33SAl Viro #include <os.h> 190f80bc85SJeff Dike 200d71832eSTristan Schmelcher /* Set by make_tempfile() during early boot. */ 210f80bc85SJeff Dike static char *tempdir = NULL; 220f80bc85SJeff Dike 230d71832eSTristan Schmelcher /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ 240d71832eSTristan Schmelcher static int __init check_tmpfs(const char *dir) 250f80bc85SJeff Dike { 260d71832eSTristan Schmelcher struct statfs st; 270f80bc85SJeff Dike 28d3878bb8SMasami Hiramatsu os_info("Checking if %s is on tmpfs...", dir); 290d71832eSTristan Schmelcher if (statfs(dir, &st) < 0) { 30d3878bb8SMasami Hiramatsu os_info("%s\n", strerror(errno)); 310d71832eSTristan Schmelcher } else if (st.f_type != TMPFS_MAGIC) { 32d3878bb8SMasami Hiramatsu os_info("no\n"); 3374735341STristan Schmelcher } else { 34d3878bb8SMasami Hiramatsu os_info("OK\n"); 350d71832eSTristan Schmelcher return 0; 3674735341STristan Schmelcher } 370d71832eSTristan Schmelcher return -1; 3874735341STristan Schmelcher } 3974735341STristan Schmelcher 400d71832eSTristan Schmelcher /* 410d71832eSTristan Schmelcher * Choose the tempdir to use. We want something on tmpfs so that our memory is 420d71832eSTristan Schmelcher * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the 430d71832eSTristan Schmelcher * environment, we use that even if it's not on tmpfs, but we warn the user. 440d71832eSTristan Schmelcher * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found 450d71832eSTristan Schmelcher * then we fall back to /tmp. 460d71832eSTristan Schmelcher */ 470d71832eSTristan Schmelcher static char * __init choose_tempdir(void) 480d71832eSTristan Schmelcher { 490d71832eSTristan Schmelcher static const char * const vars[] = { 500d71832eSTristan Schmelcher "TMPDIR", 510d71832eSTristan Schmelcher "TMP", 520d71832eSTristan Schmelcher "TEMP", 530d71832eSTristan Schmelcher NULL 540d71832eSTristan Schmelcher }; 550d71832eSTristan Schmelcher static const char fallback_dir[] = "/tmp"; 560d71832eSTristan Schmelcher static const char * const tmpfs_dirs[] = { 570d71832eSTristan Schmelcher "/dev/shm", 580d71832eSTristan Schmelcher fallback_dir, 590d71832eSTristan Schmelcher NULL 600d71832eSTristan Schmelcher }; 610d71832eSTristan Schmelcher int i; 620d71832eSTristan Schmelcher const char *dir; 630d71832eSTristan Schmelcher 64d3878bb8SMasami Hiramatsu os_info("Checking environment variables for a tempdir..."); 650d71832eSTristan Schmelcher for (i = 0; vars[i]; i++) { 660d71832eSTristan Schmelcher dir = getenv(vars[i]); 670d71832eSTristan Schmelcher if ((dir != NULL) && (*dir != '\0')) { 68d3878bb8SMasami Hiramatsu os_info("%s\n", dir); 690d71832eSTristan Schmelcher if (check_tmpfs(dir) >= 0) 700d71832eSTristan Schmelcher goto done; 710d71832eSTristan Schmelcher else 720d71832eSTristan Schmelcher goto warn; 730d71832eSTristan Schmelcher } 740d71832eSTristan Schmelcher } 75d3878bb8SMasami Hiramatsu os_info("none found\n"); 760d71832eSTristan Schmelcher 770d71832eSTristan Schmelcher for (i = 0; tmpfs_dirs[i]; i++) { 780d71832eSTristan Schmelcher dir = tmpfs_dirs[i]; 790d71832eSTristan Schmelcher if (check_tmpfs(dir) >= 0) 800d71832eSTristan Schmelcher goto done; 81966a082fSRob Landley } 82966a082fSRob Landley 830d71832eSTristan Schmelcher dir = fallback_dir; 840d71832eSTristan Schmelcher warn: 85*0936d4f3SMasami Hiramatsu os_warn("Warning: tempdir %s is not on tmpfs\n", dir); 860d71832eSTristan Schmelcher done: 870d71832eSTristan Schmelcher /* Make a copy since getenv results may not remain valid forever. */ 880d71832eSTristan Schmelcher return strdup(dir); 890d71832eSTristan Schmelcher } 900d71832eSTristan Schmelcher 910d71832eSTristan Schmelcher /* 920d71832eSTristan Schmelcher * Create an unlinked tempfile in a suitable tempdir. template must be the 930d71832eSTristan Schmelcher * basename part of the template with a leading '/'. 940d71832eSTristan Schmelcher */ 950d71832eSTristan Schmelcher static int __init make_tempfile(const char *template) 960f80bc85SJeff Dike { 9787276f72SPaolo 'Blaisorblade' Giarrusso char *tempname; 980f80bc85SJeff Dike int fd; 990f80bc85SJeff Dike 1000d71832eSTristan Schmelcher if (tempdir == NULL) { 1010d71832eSTristan Schmelcher tempdir = choose_tempdir(); 1020d71832eSTristan Schmelcher if (tempdir == NULL) { 103*0936d4f3SMasami Hiramatsu os_warn("Failed to choose tempdir: %s\n", 1040d71832eSTristan Schmelcher strerror(errno)); 1050d71832eSTristan Schmelcher return -1; 1060d71832eSTristan Schmelcher } 1070d71832eSTristan Schmelcher } 1080d71832eSTristan Schmelcher 1093e46b253SMickaël Salaün #ifdef O_TMPFILE 1103e46b253SMickaël Salaün fd = open(tempdir, O_CLOEXEC | O_RDWR | O_EXCL | O_TMPFILE, 0700); 1113e46b253SMickaël Salaün /* 1123e46b253SMickaël Salaün * If the running system does not support O_TMPFILE flag then retry 1133e46b253SMickaël Salaün * without it. 1143e46b253SMickaël Salaün */ 1153e46b253SMickaël Salaün if (fd != -1 || (errno != EINVAL && errno != EISDIR && 1163e46b253SMickaël Salaün errno != EOPNOTSUPP)) 1173e46b253SMickaël Salaün return fd; 1183e46b253SMickaël Salaün #endif 1193e46b253SMickaël Salaün 1200d71832eSTristan Schmelcher tempname = malloc(strlen(tempdir) + strlen(template) + 1); 12111a7ac23SJim Meyering if (tempname == NULL) 12211a7ac23SJim Meyering return -1; 12387276f72SPaolo 'Blaisorblade' Giarrusso 1240f80bc85SJeff Dike strcpy(tempname, tempdir); 1250d71832eSTristan Schmelcher strcat(tempname, template); 1260f80bc85SJeff Dike fd = mkstemp(tempname); 1270f80bc85SJeff Dike if (fd < 0) { 128*0936d4f3SMasami Hiramatsu os_warn("open - cannot create %s: %s\n", tempname, 1290f80bc85SJeff Dike strerror(errno)); 13087276f72SPaolo 'Blaisorblade' Giarrusso goto out; 1310f80bc85SJeff Dike } 1320d71832eSTristan Schmelcher if (unlink(tempname) < 0) { 1330f80bc85SJeff Dike perror("unlink"); 1342a6d0ac1SDavidlohr Bueso goto close; 1350f80bc85SJeff Dike } 13687276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 13781999a01SJeff Dike return fd; 1382a6d0ac1SDavidlohr Bueso close: 1392a6d0ac1SDavidlohr Bueso close(fd); 14087276f72SPaolo 'Blaisorblade' Giarrusso out: 14187276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 14287276f72SPaolo 'Blaisorblade' Giarrusso return -1; 1430f80bc85SJeff Dike } 1440f80bc85SJeff Dike 1450d71832eSTristan Schmelcher #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" 1460f80bc85SJeff Dike 1475134d8feSJeff Dike static int __init create_tmp_file(unsigned long long len) 1480f80bc85SJeff Dike { 1490f80bc85SJeff Dike int fd, err; 1500f80bc85SJeff Dike char zero; 1510f80bc85SJeff Dike 1520d71832eSTristan Schmelcher fd = make_tempfile(TEMPNAME_TEMPLATE); 1535134d8feSJeff Dike if (fd < 0) 1540f80bc85SJeff Dike exit(1); 1550f80bc85SJeff Dike 1565134d8feSJeff Dike /* 1575134d8feSJeff Dike * Seek to len - 1 because writing a character there will 158190f4939SJeff Dike * increase the file size by one byte, to the desired length. 159190f4939SJeff Dike */ 160190f4939SJeff Dike if (lseek64(fd, len - 1, SEEK_SET) < 0) { 161512b6fb1SJeff Dike perror("lseek64"); 1620f80bc85SJeff Dike exit(1); 1630f80bc85SJeff Dike } 1640f80bc85SJeff Dike 1650f80bc85SJeff Dike zero = 0; 1660f80bc85SJeff Dike 167a61f334fSJeff Dike err = write(fd, &zero, 1); 1680f80bc85SJeff Dike if (err != 1) { 169a61f334fSJeff Dike perror("write"); 1700f80bc85SJeff Dike exit(1); 1710f80bc85SJeff Dike } 1720f80bc85SJeff Dike 17381999a01SJeff Dike return fd; 1740f80bc85SJeff Dike } 1750f80bc85SJeff Dike 17636e45463SJeff Dike int __init create_mem_file(unsigned long long len) 1770f80bc85SJeff Dike { 1780f80bc85SJeff Dike int err, fd; 1790f80bc85SJeff Dike 18002dea087SJeff Dike fd = create_tmp_file(len); 1810f80bc85SJeff Dike 182512b6fb1SJeff Dike err = os_set_exec_close(fd); 1830f80bc85SJeff Dike if (err < 0) { 1840f80bc85SJeff Dike errno = -err; 1850f80bc85SJeff Dike perror("exec_close"); 1860f80bc85SJeff Dike } 18781999a01SJeff Dike return fd; 1880f80bc85SJeff Dike } 189966a082fSRob Landley 19036e45463SJeff Dike void __init check_tmpexec(void) 191966a082fSRob Landley { 192966a082fSRob Landley void *addr; 193966a082fSRob Landley int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); 194966a082fSRob Landley 195966a082fSRob Landley addr = mmap(NULL, UM_KERN_PAGE_SIZE, 196966a082fSRob Landley PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 197d3878bb8SMasami Hiramatsu os_info("Checking PROT_EXEC mmap in %s...", tempdir); 198966a082fSRob Landley if (addr == MAP_FAILED) { 199966a082fSRob Landley err = errno; 200*0936d4f3SMasami Hiramatsu os_warn("%s\n", strerror(err)); 201c9a3072dSWANG Cong close(fd); 202966a082fSRob Landley if (err == EPERM) 203*0936d4f3SMasami Hiramatsu os_warn("%s must be not mounted noexec\n", tempdir); 204966a082fSRob Landley exit(1); 205966a082fSRob Landley } 206d3878bb8SMasami Hiramatsu os_info("OK\n"); 207966a082fSRob Landley munmap(addr, UM_KERN_PAGE_SIZE); 208966a082fSRob Landley 209966a082fSRob Landley close(fd); 210966a082fSRob Landley } 211