Merge branch 'fopen-cloexec' into 'main'

gstdio: Add support for the `e` flag (O_CLOEXEC) to g_fopen()

See merge request GNOME/glib!4564
This commit is contained in:
Philip Withnall
2025-05-29 10:59:56 +00:00
24 changed files with 127 additions and 36 deletions

View File

@@ -34,6 +34,7 @@
#endif
#include <gi18n.h>
#include <glib/gstdio.h>
#ifdef G_OS_WIN32
#include "glib/glib-private.h"
@@ -71,7 +72,7 @@ completion_debug (const gchar *format, ...)
s = g_strdup_vprintf (format, var_args);
if (f == NULL)
{
f = fopen ("/tmp/gdbus-completion-debug.txt", "a+");
f = g_fopen ("/tmp/gdbus-completion-debug.txt", "a+e");
}
fprintf (f, "%s\n", s);
g_free (s);

View File

@@ -53,12 +53,6 @@
#include <windows.h>
#endif
#ifdef G_OS_WIN32
#define FO_CLOEXEC ""
#else
#define FO_CLOEXEC "e"
#endif
#include "glibintl.h"
/**
@@ -720,7 +714,7 @@ g_dbus_address_connect (const gchar *address_entry,
int errsv;
/* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
f = fopen (nonce_file, "rb" FO_CLOEXEC);
f = g_fopen (nonce_file, "rbe");
errsv = errno;
if (f == NULL)
{

View File

@@ -2195,7 +2195,7 @@ unpublish_session_bus (void)
static void
wait_console_window (void)
{
FILE *console = fopen ("CONOUT$", "w");
FILE *console = g_fopen ("CONOUT$", "we");
SetConsoleTitleW (L"gdbus-daemon output. Type any character to close this window.");
fprintf (console, _("(Type any character to close this window)\n"));

View File

@@ -1131,7 +1131,7 @@ main (int argc, char **argv)
{
FILE *file;
file = fopen (target, "w");
file = g_fopen (target, "we");
if (file == NULL)
{
g_printerr ("can't write to file %s", target);
@@ -1180,7 +1180,7 @@ main (int argc, char **argv)
}
g_unlink (binary_target);
file = fopen (target, "w");
file = g_fopen (target, "we");
if (file == NULL)
{
g_printerr ("can't write to file %s", target);

View File

@@ -79,6 +79,7 @@ extern char* hasmntopt(const struct mntent* mnt, const char* opt);
#include "gfilemonitor.h"
#include "glibintl.h"
#include "glocalfile.h"
#include "gstdio.h"
#include "gthemedicon.h"
#include "gcontextspecificgroup.h"
@@ -202,7 +203,7 @@ static GSource *proc_mounts_watch_source = NULL;
#endif
#ifndef HAVE_SETMNTENT
#define setmntent(f,m) fopen(f,m)
#define setmntent(f,m) g_fopen (f, m)
#endif
#ifndef HAVE_ENDMNTENT
#define endmntent(f) fclose(f)
@@ -3965,7 +3966,7 @@ _resolve_dev_root (void)
/* see if device with similar major:minor as /dev/root is mention
* in /etc/mtab (it usually is)
*/
f = fopen ("/etc/mtab", "re");
f = g_fopen ("/etc/mtab", "re");
if (f != NULL)
{
struct mntent *entp;

View File

@@ -792,7 +792,7 @@ test_internal_enhanced_stdio (void)
g_remove (ps);
f = g_fopen (ps, "wb");
f = g_fopen (ps, "wbe");
g_assert_nonnull (f);
h = (HANDLE) _get_osfhandle (fileno (f));
@@ -875,7 +875,7 @@ test_internal_enhanced_stdio (void)
g_assert_true (SystemTimeToFileTime (&st, &ft));
f = g_fopen (p0, "w");
f = g_fopen (p0, "we");
g_assert_nonnull (f);
h = (HANDLE) _get_osfhandle (fileno (f));
@@ -888,7 +888,7 @@ test_internal_enhanced_stdio (void)
fclose (f);
f = g_fopen (p1, "w");
f = g_fopen (p1, "we");
g_assert_nonnull (f);
fclose (f);

View File

@@ -124,6 +124,12 @@ sleep_forever_mode (int argc,
return 0;
}
#ifdef G_OS_UNIX
#define GLIB_FD_CLOEXEC "e"
#else
#define GLIB_FD_CLOEXEC ""
#endif
static int
write_to_fds (int argc, char **argv)
{
@@ -132,7 +138,7 @@ write_to_fds (int argc, char **argv)
for (i = 2; i < argc; i++)
{
int fd = atoi (argv[i]);
FILE *f = fdopen (fd, "w");
FILE *f = fdopen (fd, "w" GLIB_FD_CLOEXEC);
const char buf[] = "hello world\n";
size_t bytes_written;
@@ -170,7 +176,7 @@ read_from_fd (int argc, char **argv)
return 1;
}
f = fdopen (fd, "r");
f = fdopen (fd, "r" GLIB_FD_CLOEXEC);
if (f == NULL)
{
g_warning ("Failed to open fd %d: %s", fd, g_strerror (errno));

View File

@@ -23,6 +23,7 @@
*/
#include <glib/glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <errno.h>
#include <stdlib.h>
@@ -305,7 +306,7 @@ test_create_structure (gconstpointer test_data)
basename = g_path_get_basename (item.filename);
path = g_build_filename (test_data, dir, ".hidden", NULL);
f = fopen (path, "a");
f = g_fopen (path, "ae");
fprintf (f, "%s\n", basename);
fclose (f);

View File

@@ -1176,6 +1176,9 @@ endforeach
python_test_env = test_env
python_test_env.prepend('PYTHONPATH', python_test_libraries_path)
python_test_env.prepend('PYTHONPATH', python_test_libraries_built)
if python_test_env_common_path.length() > 0
python_test_env.prepend('PATH', python_test_env_common_path)
endif
foreach test_name, extra_args : python_tests
depends = [extra_args.get('depends', [])]

View File

@@ -86,7 +86,7 @@ write_out_typelib (gchar *prefix,
file_obj = g_file_new_for_path (filename);
tmp_filename = g_strdup_printf ("%s.tmp", filename);
tmp_file_obj = g_file_new_for_path (tmp_filename);
file = g_fopen (tmp_filename, "wb");
file = g_fopen (tmp_filename, "wbe");
if (file == NULL)
{

View File

@@ -33,6 +33,7 @@
#include <glib.h>
#include <glib-object.h>
#include <glib/gstdio.h>
#include <gmodule.h>
#include <stdlib.h>
@@ -637,7 +638,7 @@ gi_repository_dump (const char *input_filename,
return FALSE;
}
input = fopen (input_filename, "rb");
input = g_fopen (input_filename, "rbe");
if (input == NULL)
{
int saved_errno = errno;
@@ -649,7 +650,7 @@ gi_repository_dump (const char *input_filename,
return FALSE;
}
output = fopen (output_filename, "wb");
output = g_fopen (output_filename, "wbe");
if (output == NULL)
{
int saved_errno = errno;

View File

@@ -1368,7 +1368,7 @@ gi_ir_writer_write (GIRepository *repository,
full_filename = g_strdup_printf ("%s-%s", ns, filename);
else
full_filename = g_strdup (filename);
ofile = g_fopen (filename, "w");
ofile = g_fopen (filename, "we");
if (ofile == NULL)
{

View File

@@ -187,6 +187,9 @@ endif
python_test_env = test_env
python_test_env.prepend('PYTHONPATH', python_test_libraries_path)
python_test_env.prepend('PYTHONPATH', python_test_libraries_built)
if python_test_env_common_path.length() > 0
python_test_env.prepend('PATH', python_test_env_common_path)
endif
foreach test_name, extra_args : python_tests
depends = [extra_args.get('depends', [])]

View File

@@ -39,6 +39,7 @@
#include <glib/gstrfuncs.h>
#include <glib/gmessages.h>
#include <glib/gstdio.h>
#include <glib/gunicode.h>
#include <string.h>
@@ -467,7 +468,7 @@ main (int argc,
return 1;
}
file = fopen (argv[1], "r");
file = g_fopen (argv[1], "re");
if (!file)
{
g_warning ("Cannot open %s", argv[1]);

View File

@@ -28,6 +28,7 @@
#include "ghash.h"
#include "glib-private.h"
#include "gmessages.h"
#include "gstdio.h"
#include "gstrfuncs.h"
#include "gthread.h"
#include "gthreadprivate.h"
@@ -447,7 +448,7 @@ read_aliases (const gchar *file,
FILE *fp;
char buf[256];
fp = fopen (file, "re");
fp = g_fopen (file, "re");
if (!fp)
return;
while (fgets (buf, 256, fp))

View File

@@ -938,7 +938,7 @@ get_contents_posix (const gchar *filename,
FILE *f;
gboolean retval;
f = fdopen (fd, "r");
f = fdopen (fd, "re");
if (f == NULL)
{
@@ -969,7 +969,7 @@ get_contents_win32 (const gchar *filename,
FILE *f;
gboolean retval;
f = g_fopen (filename, "rb");
f = g_fopen (filename, "rbe");
if (f == NULL)
{

View File

@@ -30,6 +30,7 @@
*/
#include <glib.h>
#include <glib/gstdio.h>
#include <stdlib.h>
#include <stdio.h>
@@ -156,7 +157,7 @@ read_data (
FILE *f;
fprintf (stderr, "Reading '%s'\n", data_file_name);
if (!(f = fopen (data_file_name, "rt")))
if (!(f = g_fopen (data_file_name, "rte")))
die2 ("error: cannot open '%s' for reading", data_file_name);
if (!strcmp (data_file_type, "BidiMirroring.txt"))

View File

@@ -49,6 +49,7 @@
#include "genviron.h"
#include "gmain.h"
#include "gmem.h"
#include "gstdio.h"
#include "gtestutils.h"
#include "gthread.h"
#include "gtimer.h"
@@ -179,7 +180,7 @@ g_rand_new (void)
do
{
dev_urandom = fopen ("/dev/urandom", "rbe");
dev_urandom = g_fopen ("/dev/urandom", "rbe");
}
while G_UNLIKELY (dev_urandom == NULL && errno == EINTR);

View File

@@ -145,11 +145,15 @@ w32_error_to_errno (DWORD error_code)
* "wb+". The 'b' needs to be appended to "w+", i.e. "w+b". Note
* that otherwise these 2 modes are supposed to be aliases, hence
* swappable at will. TODO: Is this still true?
*
* It also doesnt accept `e`, which Unix uses for O_CLOEXEC. This function
* rewrites that to `N` — see
* https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170
*/
static void
_g_win32_fix_mode (wchar_t *mode)
{
wchar_t *ptr;
wchar_t *ptr, *e_ptr, *comma_ptr;
wchar_t temp;
ptr = wcschr (mode, L'+');
@@ -159,6 +163,13 @@ _g_win32_fix_mode (wchar_t *mode)
mode[1] = *ptr;
*ptr = temp;
}
/* Rewrite `e` (O_CLOEXEC) to `N`, if it occurs before any extended attributes
* (https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170#unicode-support) */
e_ptr = wcschr (mode, L'e');
comma_ptr = wcschr (mode, L',');
if (e_ptr != NULL && (comma_ptr == NULL || e_ptr < comma_ptr))
*e_ptr = L'N';
}
/* From
@@ -1562,6 +1573,13 @@ g_rmdir (const gchar *filename)
* used by GLib are different. Convenience functions like g_file_set_contents_full()
* avoid this problem.
*
* Since GLib 2.86, the `e` option is supported in @mode on all platforms. On
* Unix platforms it will set `O_CLOEXEC` on the opened file descriptor. On
* Windows platforms it will be converted to the
* [`N` modifier](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170).
* It is recommended to set `e` unconditionally, unless you know the returned
* file should be shared between this process and a new fork.
*
* Returns: A `FILE*` if the file was successfully opened, or %NULL if
* an error occurred
*
@@ -1618,6 +1636,9 @@ g_fopen (const gchar *filename,
*
* See your C library manual for more details about freopen().
*
* Since GLib 2.86, the `e` option is supported in @mode on all platforms. See
* the documentation for [func@GLib.fopen] for more details.
*
* Returns: A FILE* if the file was successfully opened, or %NULL if
* an error occurred.
*

View File

@@ -705,7 +705,7 @@ test_mkdir_with_parents_1 (const gchar *base)
if (g_file_test (p1, G_FILE_TEST_EXISTS))
g_error ("failed, did g_rmdir(%s), but %s is still there", p1, p1);
f = g_fopen (p1, "w");
f = g_fopen (p1, "we");
if (f == NULL)
g_error ("failed, couldn't create file %s", p1);
fclose (f);
@@ -1446,7 +1446,7 @@ test_get_contents (void)
char *filename = g_build_filename (g_get_tmp_dir (), "file-test-get-contents", NULL);
gsize bytes_written;
f = g_fopen (filename, "w");
f = g_fopen (filename, "we");
bytes_written = fwrite (text, 1, strlen (text), f);
g_assert_cmpint (bytes_written, ==, strlen (text));
fclose (f);
@@ -2042,7 +2042,7 @@ test_read_link (void)
g_free (newpath);
g_free (badpath);
file = fopen (filename, "w");
file = g_fopen (filename, "we");
g_assert_nonnull (file);
fclose (file);
@@ -2219,7 +2219,36 @@ test_fopen_modes (void)
"a+b",
"wb+",
"rb+",
"ab+"
"ab+",
"we",
"re",
"ae",
"w+e",
"r+e",
"a+e",
"wbe",
"rbe",
"abe",
"w+be",
"r+be",
"a+be",
"wb+e",
"rb+e",
"ab+e",
"web",
"reb",
"aeb",
"w+eb",
"r+eb",
"a+eb",
"web+",
"reb+",
"aeb+",
#ifdef G_OS_WIN32
/* https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170#unicode-support */
"w, ccs=utf-16le",
"we, ccs=utf-16le",
#endif
};
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/merge_requests/119");

View File

@@ -515,6 +515,9 @@ endif
python_test_env = test_env
python_test_env.prepend('PYTHONPATH', python_test_libraries_path)
python_test_env.prepend('PYTHONPATH', python_test_libraries_built)
if python_test_env_common_path.length() > 0
python_test_env.prepend('PATH', python_test_env_common_path)
endif
foreach test_name, extra_args : python_tests
depends = [extra_args.get('depends', [])]

View File

@@ -33,6 +33,7 @@
#include <stdio.h>
#include "glib.h"
#include "glib/gstdio.h"
#include "glib/gunidecomp.h"
@@ -636,7 +637,7 @@ test_casemap_and_casefold (void)
save_and_clear_env ("LANG", &old_lang);
filename = g_test_build_filename (G_TEST_DIST, "casemap.txt", NULL);
infile = fopen (filename, "r");
infile = g_fopen (filename, "re");
g_assert (infile != NULL);
while (fgets (buffer, sizeof (buffer), infile))
@@ -700,7 +701,7 @@ test_casemap_and_casefold (void)
g_free (filename);
filename = g_test_build_filename (G_TEST_DIST, "casefold.txt", NULL);
infile = fopen (filename, "r");
infile = g_fopen (filename, "re");
g_assert (infile != NULL);
while (fgets (buffer, sizeof (buffer), infile))

View File

@@ -235,6 +235,9 @@ endforeach
python_test_env = test_env
python_test_env.prepend('PYTHONPATH', python_test_libraries_path)
python_test_env.prepend('PYTHONPATH', python_test_libraries_built)
if python_test_env_common_path.length() > 0
python_test_env.prepend('PATH', python_test_env_common_path)
endif
foreach test_name, extra_args : python_tests
depends = [extra_args.get('depends', [])]

View File

@@ -2516,6 +2516,26 @@ if not python_version.version_compare(python_version_req)
endif
python_test_libraries_built = meson.project_build_root() / 'tests' / 'lib'
# FIXME: https://github.com/mesonbuild/meson/issues/4668
#
# If we have a Python test which spawns a built native binary, that binary is
# listed in the `depends` argument of the `test()`. On Linux, this results in
# the directories containing the built libraries which the binary depends on
# being added to the `LD_LIBRARY_PATH` of the test invocation. On Windows,
# however, Meson currently doesnt add those directories to `PATH` (which is the
# equivalent of `LD_LIBRARY_PATH`), so we have to do it manually.
python_test_env_common_path = []
if host_system == 'windows'
build_root = meson.project_build_root()
python_test_env_common_path += [
build_root / 'gmodule',
build_root / 'girepository',
build_root / 'gio',
build_root / 'glib',
build_root / 'gobject',
]
endif
# Determine which user environment-dependent files that we want to install
bash = find_program('bash', required : false)
have_bash = bash.found() # For completion scripts