mirror of
https://github.com/mirror/make.git
synced 2024-12-27 13:20:34 +08:00
Add get_tmpfd() and allow anonymous temp files
The output sync feature wants a file descriptor not a FILE*. We were using tmpfile() but this returns FILE* which means we needed to dup() the descriptor then fclose() the original, which is just unnecessary overhead for every command we run. Create a get_tmpfd() method that returns a file descriptor directly by using mkstemp() if available, else do the best we can. Also allow anonymous temp files if the filename pointer is NULL. This causes the file to be unlinked. On Windows this requires a special open so add an os_anontmp() method to handle this. * src/makeint.h: Add prototype for get_tmpfd(). * src/misc.c (get_tmpfd): If we have mkstemp() use that, else just open(2). If we don't want to keep the filename, unlink the file. (get_tmpfile): Use get_tmpfd() if we have fdopen(), else use fopen(). * src/output.c (output_tmpfd): Call get_tmpfd() with NULL. * src/os.h (os_anontmp): On Windows make this a function, else fails. * src/w32/compat/posixcfn.c (tmpfile): Move to w32os.c:os_anontmp(). * src/w32/w32os.c (os_anontmp): Create a temp file that will be deleted when the process exits, and return a file descriptor to it.
This commit is contained in:
parent
5eff618c8c
commit
a2ba5ccbda
@ -547,6 +547,7 @@ void print_spaces (unsigned int);
|
||||
char *find_percent (char *);
|
||||
const char *find_percent_cached (const char **);
|
||||
char *get_tmppath ();
|
||||
int get_tmpfd (char **);
|
||||
FILE *get_tmpfile (char **);
|
||||
ssize_t writebuf (int, const void *, size_t);
|
||||
ssize_t readbuf (int, void *, size_t);
|
||||
|
84
src/misc.c
84
src/misc.c
@ -17,6 +17,7 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
#include "makeint.h"
|
||||
#include "filedef.h"
|
||||
#include "dep.h"
|
||||
#include "os.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* GNU make no longer supports pre-ANSI89 environments. */
|
||||
@ -573,44 +574,79 @@ get_tmppath ()
|
||||
return path;
|
||||
}
|
||||
|
||||
FILE *
|
||||
get_tmpfile (char **name)
|
||||
/* Generate a temporary file and return an fd for it. If name is NULL then
|
||||
the temp file is anonymous and will be deleted when the process exits. */
|
||||
int
|
||||
get_tmpfd (char **name)
|
||||
{
|
||||
FILE *file;
|
||||
#ifdef HAVE_FDOPEN
|
||||
int fd;
|
||||
#endif
|
||||
int fd = -1;
|
||||
char *tmpnm;
|
||||
mode_t mask;
|
||||
|
||||
/* If there's an os-specific way to get an anoymous temp file use it. */
|
||||
if (!name)
|
||||
{
|
||||
fd = os_anontmp ();
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Preserve the current umask, and set a restrictive one for temp files. */
|
||||
mode_t mask = umask (0077);
|
||||
mask = umask (0077);
|
||||
|
||||
#if defined(HAVE_MKSTEMP) && defined(HAVE_FDOPEN)
|
||||
*name = get_tmptemplate ();
|
||||
#if defined(HAVE_MKSTEMP)
|
||||
tmpnm = get_tmptemplate ();
|
||||
|
||||
/* It's safest to use mkstemp(), if we can. */
|
||||
EINTRLOOP (fd, mkstemp (*name));
|
||||
if (fd == -1)
|
||||
file = NULL;
|
||||
else
|
||||
file = fdopen (fd, "w");
|
||||
EINTRLOOP (fd, mkstemp (tmpnm));
|
||||
#else
|
||||
*name = get_tmppath ();
|
||||
tmpnm = get_tmppath ();
|
||||
|
||||
# ifdef HAVE_FDOPEN
|
||||
/* Can't use mkstemp(), but try to guard against a race condition. */
|
||||
EINTRLOOP (fd, open (*name, O_CREAT|O_EXCL|O_WRONLY, 0600));
|
||||
if (fd == -1)
|
||||
return 0;
|
||||
file = fdopen (fd, "w");
|
||||
# else
|
||||
/* Not secure, but what can we do? */
|
||||
file = fopen (*name, "w");
|
||||
# endif
|
||||
EINTRLOOP (fd, open (tmpnm, O_CREAT|O_EXCL|O_RDWR, 0600));
|
||||
#endif
|
||||
|
||||
umask (mask);
|
||||
|
||||
if (name)
|
||||
*name = tmpnm;
|
||||
else
|
||||
{
|
||||
unlink (tmpnm);
|
||||
free (tmpnm);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
FILE *
|
||||
get_tmpfile (char **name)
|
||||
{
|
||||
#if defined(HAVE_FDOPEN)
|
||||
int fd = get_tmpfd (name);
|
||||
|
||||
return fd < 0 ? NULL : fdopen (fd, "w");
|
||||
#else
|
||||
/* Preserve the current umask, and set a restrictive one for temp files. */
|
||||
mode_t mask = umask (0077);
|
||||
|
||||
char *tmpnm = get_tmppath ();
|
||||
|
||||
/* Not secure, but...? If name is NULL we could use tmpfile()... */
|
||||
FILE *file = fopen (tmpnm, "w");
|
||||
|
||||
umask (mask);
|
||||
|
||||
if (name)
|
||||
*name = tmpnm;
|
||||
else
|
||||
{
|
||||
unlink (tmpnm);
|
||||
free (tmpnm);
|
||||
}
|
||||
|
||||
return file;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
7
src/os.h
7
src/os.h
@ -15,6 +15,13 @@ You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
/* Return a file descriptor for a new anonymous temp file, or -1. */
|
||||
#if defined(WINDOWS32)
|
||||
int os_anontmp ();
|
||||
#else
|
||||
# define os_anontmp() (-1)
|
||||
#endif
|
||||
|
||||
/* This section provides OS-specific functions to support the jobserver. */
|
||||
|
||||
#ifdef MAKE_JOBSERVER
|
||||
|
18
src/output.c
18
src/output.c
@ -285,24 +285,8 @@ release_semaphore (void *sem)
|
||||
int
|
||||
output_tmpfd (void)
|
||||
{
|
||||
mode_t mask = umask (0077);
|
||||
int fd = -1;
|
||||
FILE *tfile = tmpfile ();
|
||||
|
||||
if (! tfile)
|
||||
pfatal_with_name ("tmpfile");
|
||||
|
||||
/* Create a duplicate so we can close the stream. */
|
||||
fd = dup (fileno (tfile));
|
||||
if (fd < 0)
|
||||
pfatal_with_name ("dup");
|
||||
|
||||
fclose (tfile);
|
||||
|
||||
int fd = get_tmpfd (NULL);
|
||||
set_append_mode (fd);
|
||||
|
||||
umask (mask);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
@ -260,108 +260,6 @@ same_stream (FILE *f1, FILE *f2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A replacement for tmpfile, since the MSVCRT implementation creates
|
||||
the file in the root directory of the current drive, which might
|
||||
not be writable by our user. Most of the code borrowed from
|
||||
create_batch_file, see job.c. */
|
||||
FILE *
|
||||
tmpfile (void)
|
||||
{
|
||||
char temp_path[MAXPATHLEN];
|
||||
unsigned path_size = GetTempPath (sizeof temp_path, temp_path);
|
||||
int path_is_dot = 0;
|
||||
/* The following variable is static so we won't try to reuse a name
|
||||
that was generated a little while ago, because that file might
|
||||
not be on disk yet, since we use FILE_ATTRIBUTE_TEMPORARY below,
|
||||
which tells the OS it doesn't need to flush the cache to disk.
|
||||
If the file is not yet on disk, we might think the name is
|
||||
available, while it really isn't. This happens in parallel
|
||||
builds, where Make doesn't wait for one job to finish before it
|
||||
launches the next one. */
|
||||
static unsigned uniq = 0;
|
||||
static int second_loop = 0;
|
||||
const char base[] = "gmake_tmpf";
|
||||
const unsigned sizemax = sizeof base - 1 + 4 + 10 + 10;
|
||||
unsigned pid = GetCurrentProcessId ();
|
||||
|
||||
if (path_size == 0)
|
||||
{
|
||||
path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
|
||||
path_is_dot = 1;
|
||||
}
|
||||
|
||||
++uniq;
|
||||
if (uniq >= 0x10000 && !second_loop)
|
||||
{
|
||||
/* If we already had 64K batch files in this
|
||||
process, make a second loop through the numbers,
|
||||
looking for free slots, i.e. files that were
|
||||
deleted in the meantime. */
|
||||
second_loop = 1;
|
||||
uniq = 1;
|
||||
}
|
||||
while (path_size > 0 &&
|
||||
path_size + sizemax < sizeof temp_path &&
|
||||
!(uniq >= 0x10000 && second_loop))
|
||||
{
|
||||
HANDLE h;
|
||||
|
||||
sprintf (temp_path + path_size,
|
||||
"%s%s%u-%x.tmp",
|
||||
temp_path[path_size - 1] == '\\' ? "" : "\\",
|
||||
base, pid, uniq);
|
||||
h = CreateFile (temp_path, /* file name */
|
||||
GENERIC_READ | GENERIC_WRITE | DELETE, /* desired access */
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
|
||||
NULL, /* default security attributes */
|
||||
CREATE_NEW, /* creation disposition */
|
||||
FILE_ATTRIBUTE_NORMAL | /* flags and attributes */
|
||||
FILE_ATTRIBUTE_TEMPORARY |
|
||||
FILE_FLAG_DELETE_ON_CLOSE,
|
||||
NULL); /* no template file */
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
const DWORD er = GetLastError ();
|
||||
|
||||
if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
++uniq;
|
||||
if (uniq == 0x10000 && !second_loop)
|
||||
{
|
||||
second_loop = 1;
|
||||
uniq = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* The temporary path is not guaranteed to exist, or might
|
||||
not be writable by user. Use the current directory as
|
||||
fallback. */
|
||||
else if (path_is_dot == 0)
|
||||
{
|
||||
path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
|
||||
path_is_dot = 1;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
errno = EACCES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int fd = _open_osfhandle ((intptr_t)h, 0);
|
||||
|
||||
return _fdopen (fd, "w+b");
|
||||
}
|
||||
}
|
||||
|
||||
if (uniq >= 0x10000)
|
||||
errno = EEXIST;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* !NO_OUTPUT_SYNC */
|
||||
|
||||
#if MAKE_LOAD
|
||||
|
@ -28,6 +28,101 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
#include "os.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* A replacement for tmpfile, since the MSVCRT implementation creates
|
||||
the file in the root directory of the current drive, which might
|
||||
not be writable by our user, and also it returns a FILE* and we want a file
|
||||
descriptor. Mostly borrowed from create_batch_file, see job.c. */
|
||||
int
|
||||
os_anontmp ()
|
||||
{
|
||||
char temp_path[MAXPATHLEN];
|
||||
unsigned path_size = GetTempPath (sizeof (temp_path), temp_path);
|
||||
int using_cwd = 0;
|
||||
|
||||
/* These variables are static so we won't try to reuse a name that was
|
||||
generated a little while ago, because that file might not be on disk yet,
|
||||
since we use FILE_ATTRIBUTE_TEMPORARY below, which tells the OS it
|
||||
doesn't need to flush the cache to disk. If the file is not yet on disk,
|
||||
we might think the name is available, while it really isn't. This
|
||||
happens in parallel builds. */
|
||||
static unsigned uniq = 0;
|
||||
static int second_loop = 0;
|
||||
|
||||
const char base[] = "gmake_tmpf";
|
||||
const unsigned sizemax = sizeof (base) - 1 + 4 + 10 + 10;
|
||||
unsigned pid = GetCurrentProcessId ();
|
||||
|
||||
if (path_size == 0)
|
||||
{
|
||||
path_size = GetCurrentDirectory (sizeof (temp_path), temp_path);
|
||||
using_cwd = 1;
|
||||
}
|
||||
|
||||
++uniq;
|
||||
if (uniq >= 0x10000 && !second_loop)
|
||||
{
|
||||
/* If we already had 64K batch files in this
|
||||
process, make a second loop through the numbers,
|
||||
looking for free slots, i.e. files that were
|
||||
deleted in the meantime. */
|
||||
second_loop = 1;
|
||||
uniq = 1;
|
||||
}
|
||||
|
||||
while (path_size > 0 && path_size + sizemax < sizeof (temp_path)
|
||||
&& (uniq < 0x10000 || !second_loop))
|
||||
{
|
||||
HANDLE h;
|
||||
|
||||
sprintf (temp_path + path_size,
|
||||
"%s%s%u-%x.tmp",
|
||||
temp_path[path_size - 1] == '\\' ? "" : "\\",
|
||||
base, pid, uniq);
|
||||
h = CreateFile (temp_path, /* file name */
|
||||
GENERIC_READ | GENERIC_WRITE | DELETE, /* desired access */
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
|
||||
NULL, /* default security attributes */
|
||||
CREATE_NEW, /* creation disposition */
|
||||
FILE_ATTRIBUTE_NORMAL | /* flags and attributes */
|
||||
FILE_ATTRIBUTE_TEMPORARY |
|
||||
FILE_FLAG_DELETE_ON_CLOSE,
|
||||
NULL); /* no template file */
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
return _open_osfhandle ((intptr_t)h, 0);
|
||||
|
||||
{
|
||||
const DWORD er = GetLastError ();
|
||||
|
||||
if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
++uniq;
|
||||
if (uniq == 0x10000 && !second_loop)
|
||||
{
|
||||
second_loop = 1;
|
||||
uniq = 1;
|
||||
}
|
||||
}
|
||||
/* The temporary path is not guaranteed to exist, or might not be
|
||||
writable by user. Use the current directory as fallback. */
|
||||
else if (!using_cwd)
|
||||
{
|
||||
path_size = GetCurrentDirectory (sizeof (temp_path), temp_path);
|
||||
using_cwd = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uniq >= 0x10000)
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This section provides OS-specific functions to support the jobserver. */
|
||||
|
||||
static char jobserver_semaphore_name[MAX_PATH + 1];
|
||||
|
Loading…
Reference in New Issue
Block a user