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> 15*0d71832eSTristan Schmelcher #include <sys/vfs.h> 16*0d71832eSTristan Schmelcher #include <linux/magic.h> 1737185b33SAl Viro #include <init.h> 1837185b33SAl Viro #include <os.h> 190f80bc85SJeff Dike 20*0d71832eSTristan Schmelcher /* Set by make_tempfile() during early boot. */ 210f80bc85SJeff Dike static char *tempdir = NULL; 220f80bc85SJeff Dike 23*0d71832eSTristan Schmelcher /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ 24*0d71832eSTristan Schmelcher static int __init check_tmpfs(const char *dir) 250f80bc85SJeff Dike { 26*0d71832eSTristan Schmelcher struct statfs st; 270f80bc85SJeff Dike 28*0d71832eSTristan Schmelcher printf("Checking if %s is on tmpfs...", dir); 29*0d71832eSTristan Schmelcher if (statfs(dir, &st) < 0) { 30*0d71832eSTristan Schmelcher printf("%s\n", strerror(errno)); 31*0d71832eSTristan Schmelcher } else if (st.f_type != TMPFS_MAGIC) { 32*0d71832eSTristan Schmelcher printf("no\n"); 3374735341STristan Schmelcher } else { 34966a082fSRob Landley printf("OK\n"); 35*0d71832eSTristan Schmelcher return 0; 3674735341STristan Schmelcher } 37*0d71832eSTristan Schmelcher return -1; 3874735341STristan Schmelcher } 3974735341STristan Schmelcher 40*0d71832eSTristan Schmelcher /* 41*0d71832eSTristan Schmelcher * Choose the tempdir to use. We want something on tmpfs so that our memory is 42*0d71832eSTristan Schmelcher * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the 43*0d71832eSTristan Schmelcher * environment, we use that even if it's not on tmpfs, but we warn the user. 44*0d71832eSTristan Schmelcher * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found 45*0d71832eSTristan Schmelcher * then we fall back to /tmp. 46*0d71832eSTristan Schmelcher */ 47*0d71832eSTristan Schmelcher static char * __init choose_tempdir(void) 48*0d71832eSTristan Schmelcher { 49*0d71832eSTristan Schmelcher static const char * const vars[] = { 50*0d71832eSTristan Schmelcher "TMPDIR", 51*0d71832eSTristan Schmelcher "TMP", 52*0d71832eSTristan Schmelcher "TEMP", 53*0d71832eSTristan Schmelcher NULL 54*0d71832eSTristan Schmelcher }; 55*0d71832eSTristan Schmelcher static const char fallback_dir[] = "/tmp"; 56*0d71832eSTristan Schmelcher static const char * const tmpfs_dirs[] = { 57*0d71832eSTristan Schmelcher "/dev/shm", 58*0d71832eSTristan Schmelcher fallback_dir, 59*0d71832eSTristan Schmelcher NULL 60*0d71832eSTristan Schmelcher }; 61*0d71832eSTristan Schmelcher int i; 62*0d71832eSTristan Schmelcher const char *dir; 63*0d71832eSTristan Schmelcher 64*0d71832eSTristan Schmelcher printf("Checking environment variables for a tempdir..."); 65*0d71832eSTristan Schmelcher for (i = 0; vars[i]; i++) { 66*0d71832eSTristan Schmelcher dir = getenv(vars[i]); 67*0d71832eSTristan Schmelcher if ((dir != NULL) && (*dir != '\0')) { 68*0d71832eSTristan Schmelcher printf("%s\n", dir); 69*0d71832eSTristan Schmelcher if (check_tmpfs(dir) >= 0) 70*0d71832eSTristan Schmelcher goto done; 71*0d71832eSTristan Schmelcher else 72*0d71832eSTristan Schmelcher goto warn; 73*0d71832eSTristan Schmelcher } 74*0d71832eSTristan Schmelcher } 75*0d71832eSTristan Schmelcher printf("none found\n"); 76*0d71832eSTristan Schmelcher 77*0d71832eSTristan Schmelcher for (i = 0; tmpfs_dirs[i]; i++) { 78*0d71832eSTristan Schmelcher dir = tmpfs_dirs[i]; 79*0d71832eSTristan Schmelcher if (check_tmpfs(dir) >= 0) 80*0d71832eSTristan Schmelcher goto done; 81966a082fSRob Landley } 82966a082fSRob Landley 83*0d71832eSTristan Schmelcher dir = fallback_dir; 84*0d71832eSTristan Schmelcher warn: 85*0d71832eSTristan Schmelcher printf("Warning: tempdir %s is not on tmpfs\n", dir); 86*0d71832eSTristan Schmelcher done: 87*0d71832eSTristan Schmelcher /* Make a copy since getenv results may not remain valid forever. */ 88*0d71832eSTristan Schmelcher return strdup(dir); 89*0d71832eSTristan Schmelcher } 90*0d71832eSTristan Schmelcher 91*0d71832eSTristan Schmelcher /* 92*0d71832eSTristan Schmelcher * Create an unlinked tempfile in a suitable tempdir. template must be the 93*0d71832eSTristan Schmelcher * basename part of the template with a leading '/'. 94*0d71832eSTristan Schmelcher */ 95*0d71832eSTristan Schmelcher static int __init make_tempfile(const char *template) 960f80bc85SJeff Dike { 9787276f72SPaolo 'Blaisorblade' Giarrusso char *tempname; 980f80bc85SJeff Dike int fd; 990f80bc85SJeff Dike 100*0d71832eSTristan Schmelcher if (tempdir == NULL) { 101*0d71832eSTristan Schmelcher tempdir = choose_tempdir(); 102*0d71832eSTristan Schmelcher if (tempdir == NULL) { 103*0d71832eSTristan Schmelcher fprintf(stderr, "Failed to choose tempdir: %s\n", 104*0d71832eSTristan Schmelcher strerror(errno)); 105*0d71832eSTristan Schmelcher return -1; 106*0d71832eSTristan Schmelcher } 107*0d71832eSTristan Schmelcher } 108*0d71832eSTristan Schmelcher 109*0d71832eSTristan Schmelcher tempname = malloc(strlen(tempdir) + strlen(template) + 1); 11011a7ac23SJim Meyering if (tempname == NULL) 11111a7ac23SJim Meyering return -1; 11287276f72SPaolo 'Blaisorblade' Giarrusso 1130f80bc85SJeff Dike strcpy(tempname, tempdir); 114*0d71832eSTristan Schmelcher strcat(tempname, template); 1150f80bc85SJeff Dike fd = mkstemp(tempname); 1160f80bc85SJeff Dike if (fd < 0) { 1170f80bc85SJeff Dike fprintf(stderr, "open - cannot create %s: %s\n", tempname, 1180f80bc85SJeff Dike strerror(errno)); 11987276f72SPaolo 'Blaisorblade' Giarrusso goto out; 1200f80bc85SJeff Dike } 121*0d71832eSTristan Schmelcher if (unlink(tempname) < 0) { 1220f80bc85SJeff Dike perror("unlink"); 1232a6d0ac1SDavidlohr Bueso goto close; 1240f80bc85SJeff Dike } 12587276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 12681999a01SJeff Dike return fd; 1272a6d0ac1SDavidlohr Bueso close: 1282a6d0ac1SDavidlohr Bueso close(fd); 12987276f72SPaolo 'Blaisorblade' Giarrusso out: 13087276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 13187276f72SPaolo 'Blaisorblade' Giarrusso return -1; 1320f80bc85SJeff Dike } 1330f80bc85SJeff Dike 134*0d71832eSTristan Schmelcher #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" 1350f80bc85SJeff Dike 1365134d8feSJeff Dike static int __init create_tmp_file(unsigned long long len) 1370f80bc85SJeff Dike { 1380f80bc85SJeff Dike int fd, err; 1390f80bc85SJeff Dike char zero; 1400f80bc85SJeff Dike 141*0d71832eSTristan Schmelcher fd = make_tempfile(TEMPNAME_TEMPLATE); 1425134d8feSJeff Dike if (fd < 0) 1430f80bc85SJeff Dike exit(1); 1440f80bc85SJeff Dike 1450f80bc85SJeff Dike err = fchmod(fd, 0777); 1460f80bc85SJeff Dike if (err < 0) { 147512b6fb1SJeff Dike perror("fchmod"); 1480f80bc85SJeff Dike exit(1); 1490f80bc85SJeff Dike } 1500f80bc85SJeff Dike 1515134d8feSJeff Dike /* 1525134d8feSJeff Dike * Seek to len - 1 because writing a character there will 153190f4939SJeff Dike * increase the file size by one byte, to the desired length. 154190f4939SJeff Dike */ 155190f4939SJeff Dike if (lseek64(fd, len - 1, SEEK_SET) < 0) { 156512b6fb1SJeff Dike perror("lseek64"); 1570f80bc85SJeff Dike exit(1); 1580f80bc85SJeff Dike } 1590f80bc85SJeff Dike 1600f80bc85SJeff Dike zero = 0; 1610f80bc85SJeff Dike 162a61f334fSJeff Dike err = write(fd, &zero, 1); 1630f80bc85SJeff Dike if (err != 1) { 164a61f334fSJeff Dike perror("write"); 1650f80bc85SJeff Dike exit(1); 1660f80bc85SJeff Dike } 1670f80bc85SJeff Dike 16881999a01SJeff Dike return fd; 1690f80bc85SJeff Dike } 1700f80bc85SJeff Dike 17136e45463SJeff Dike int __init create_mem_file(unsigned long long len) 1720f80bc85SJeff Dike { 1730f80bc85SJeff Dike int err, fd; 1740f80bc85SJeff Dike 17502dea087SJeff Dike fd = create_tmp_file(len); 1760f80bc85SJeff Dike 177512b6fb1SJeff Dike err = os_set_exec_close(fd); 1780f80bc85SJeff Dike if (err < 0) { 1790f80bc85SJeff Dike errno = -err; 1800f80bc85SJeff Dike perror("exec_close"); 1810f80bc85SJeff Dike } 18281999a01SJeff Dike return fd; 1830f80bc85SJeff Dike } 184966a082fSRob Landley 18536e45463SJeff Dike void __init check_tmpexec(void) 186966a082fSRob Landley { 187966a082fSRob Landley void *addr; 188966a082fSRob Landley int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); 189966a082fSRob Landley 190966a082fSRob Landley addr = mmap(NULL, UM_KERN_PAGE_SIZE, 191966a082fSRob Landley PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 192966a082fSRob Landley printf("Checking PROT_EXEC mmap in %s...", tempdir); 193966a082fSRob Landley if (addr == MAP_FAILED) { 194966a082fSRob Landley err = errno; 195*0d71832eSTristan Schmelcher printf("%s\n", strerror(err)); 196c9a3072dSWANG Cong close(fd); 197966a082fSRob Landley if (err == EPERM) 198966a082fSRob Landley printf("%s must be not mounted noexec\n", tempdir); 199966a082fSRob Landley exit(1); 200966a082fSRob Landley } 201966a082fSRob Landley printf("OK\n"); 202966a082fSRob Landley munmap(addr, UM_KERN_PAGE_SIZE); 203966a082fSRob Landley 204966a082fSRob Landley close(fd); 205966a082fSRob Landley } 206