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);
#include "makeint.h"
#include "filedef.h"
#include "dep.h"
+#include "os.h"
#include "debug.h"
/* GNU make no longer supports pre-ANSI89 environments. */
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
}
\f
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
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;
}
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
#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];