diff --git a/src/cairo-misc.c b/src/cairo-misc.c index 23e3eaf0b..59cf76fc9 100644 --- a/src/cairo-misc.c +++ b/src/cairo-misc.c @@ -48,6 +48,9 @@ #ifdef HAVE_XLOCALE_H #include #endif +#if HAVE_FCNTL_H +#include +#endif COMPILE_TIME_ASSERT ((int)CAIRO_STATUS_LAST_STATUS < (int)CAIRO_INT_STATUS_UNSUPPORTED); COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); @@ -956,14 +959,40 @@ _cairo_fopen (const char *filename, const char *mode, FILE **file_out) return status; } - result = _wfopen(filename_w, mode_w); + result = _wfopen (filename_w, mode_w); free (filename_w); free (mode_w); #else /* Use fopen directly */ + +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 7) + /* Glibc 2.7 supports the "e" mode flag that opens the file with O_CLOEXEC. + * this avoid the race condition in the fcntl fallback below. */ + + char new_mode[20]; + snprintf (new_mode, sizeof (new_mode), "%s%s", mode, "e"); + result = fopen (filename, new_mode); + +#else /* fopen "e" not available */ + result = fopen (filename, mode); -#endif + +#if defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC) + /* Manually set CLOEXEC */ + if (result != NULL) { + int fd = fileno (result); + if (fd != -1) { + int flags = fcntl (fd, F_GETFD); + if (flags >= 0) + flags = fcntl (fd, F_SETFD, flags | FD_CLOEXEC); + } + } +#endif /* defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC) */ + +#endif /* fopen "e" not available */ + +#endif /* !_WIN32 */ *file_out = result; @@ -971,18 +1000,15 @@ _cairo_fopen (const char *filename, const char *mode, FILE **file_out) } #ifdef _WIN32 - #include #include -#if !_WIN32_WCE /* tmpfile() replacement for Windows. * * On Windows tmpfile() creates the file in the root directory. This - * may fail due to insufficient privileges. However, this isn't a - * problem on Windows CE so we don't use it there. + * may fail due to insufficient privileges. */ -FILE * +static FILE * _cairo_win32_tmpfile (void) { DWORD path_len; @@ -996,7 +1022,7 @@ _cairo_win32_tmpfile (void) if (path_len <= 0 || path_len >= MAX_PATH) return NULL; - if (GetTempFileNameW (path_name, L"ps_", 0, file_name) == 0) + if (GetTempFileNameW (path_name, L"cairo_", 0, file_name) == 0) return NULL; handle = CreateFileW (file_name, @@ -1025,10 +1051,60 @@ _cairo_win32_tmpfile (void) return fp; } -#endif /* !_WIN32_WCE */ - #endif /* _WIN32 */ +/** + * _cairo_tmpfile: + * + * Exactly like the C library function. On platforms that support + * O_CLOEXEC, the file will be opened with this flag. On Windows, the + * file is opened in the temp directory instead of the root directory. + * + * Return value: a file handle or NULL on error. + **/ +FILE * +_cairo_tmpfile (void) +{ +#ifdef _WIN32 + return _cairo_win32_tmpfile (); +#else /* !_WIN32 */ + int fd; + FILE *file; + int flags; + +#ifdef O_TMPFILE + fd = open(P_tmpdir, + O_TMPFILE | O_EXCL | O_RDWR | O_NOATIME | O_CLOEXEC, + 0600); + if (fd == -1 && errno == ENOENT) { + fd = open("/tmp", + O_TMPFILE | O_EXCL | O_RDWR | O_NOATIME | O_CLOEXEC, + 0600); + } + if (fd != -1) + return fdopen (fd, "wb+"); + + /* Fallback */ +#endif /* O_TMPFILE */ + + file = tmpfile(); + +#if defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC) + /* Manually set CLOEXEC */ + if (file != NULL) { + fd = fileno(file); + if (fd != -1) { + flags = fcntl(fd, F_GETFD); + if (flags >= 0 && !(flags & FD_CLOEXEC)) + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } + } +#endif /* defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC) */ + + return file; +#endif /* !_WIN32 */ +} + typedef struct _cairo_intern_string { cairo_hash_entry_t hash_entry; int len; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index cdf06344d..b08955092 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -1115,7 +1115,7 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->final_stream = stream; - surface->tmpfile = tmpfile (); + surface->tmpfile = _cairo_tmpfile (); if (surface->tmpfile == NULL) { switch (errno) { case ENOMEM: diff --git a/src/cairoint.h b/src/cairoint.h index cac7f56d4..be1a4ba2a 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -89,12 +89,6 @@ CAIRO_BEGIN_DECLS -#if _WIN32 && !_WIN32_WCE /* Permissions on WinCE? No worries! */ -cairo_private FILE * -_cairo_win32_tmpfile (void); -#define tmpfile() _cairo_win32_tmpfile() -#endif - #undef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -1962,6 +1956,9 @@ _cairo_observers_notify (cairo_list_t *observers, void *arg); cairo_private cairo_status_t _cairo_fopen (const char *filename, const char *mode, FILE **file_out); +cairo_private FILE * +_cairo_tmpfile (void); + #include "cairo-mutex-private.h" #include "cairo-fixed-private.h" #include "cairo-wideint-private.h"