mirror of
https://github.com/flatpak/flatpak.git
synced 2026-03-30 12:55:19 -04:00
fdio: Add cleanup+flush API for FILE*
Mostly in ostree/rpm-ostree, we work in either raw `int fd`, or
`G{Input,Output}Stream`. One exception is the rpm-ostree `/etc/passwd`
handling, which uses `FILE*` since that's what glibc exposes.
And in general, there are use cases for `FILE*`; the raw `GUnixOutputStream` for
example isn't buffered, and doing so via e.g. `GBufferedOutputStream` means
allocating *two* GObjects and even worse going through multiple vfuncs for every
write.
`FILE*` is used heavily in systemd, and provides buffering. It is a bit cheaper
than gobjects, but has its own trap; by default every operation locks a mutex.
For more information on that, see `unlocked_stdio(3)`. However, callers can
avoid that by using e.g. `fwrite_unlocked`, which I plan to do for most users of
`FILE*` that aren't writing to one of the standard streams like `stdout` etc.
This commit is contained in:
@@ -53,6 +53,15 @@
|
||||
sizeof(type) <= 4 ? 10 : \
|
||||
sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
|
||||
|
||||
gboolean
|
||||
glnx_stdio_file_flush (FILE *f, GError **error)
|
||||
{
|
||||
if (fflush (f) != 0)
|
||||
return glnx_throw_errno_prefix (error, "fflush");
|
||||
if (ferror (f) != 0)
|
||||
return glnx_throw_errno_prefix (error, "ferror");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* An implementation of renameat2(..., RENAME_NOREPLACE)
|
||||
* with fallback to a non-atomic version.
|
||||
|
||||
17
glnx-fdio.h
17
glnx-fdio.h
@@ -50,6 +50,23 @@ const char *glnx_basename (const char *path)
|
||||
return (basename) (path);
|
||||
}
|
||||
|
||||
/* Utilities for standard FILE* */
|
||||
static inline void
|
||||
glnx_stdio_file_cleanup (void *filep)
|
||||
{
|
||||
FILE *f = filep;
|
||||
if (f)
|
||||
fclose (f);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FILE, glnx_stdio_file_cleanup)
|
||||
|
||||
/**
|
||||
* glnx_stdio_file_flush:
|
||||
* Call fflush() and check ferror().
|
||||
*/
|
||||
gboolean
|
||||
glnx_stdio_file_flush (FILE *f, GError **error);
|
||||
|
||||
typedef struct {
|
||||
gboolean initialized;
|
||||
gboolean anonymous;
|
||||
|
||||
@@ -160,6 +160,28 @@ test_tmpfile (void)
|
||||
g_assert_no_error (local_error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_stdio_file (void)
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
GError **error = &local_error;
|
||||
g_auto(GLnxTmpfile) tmpf = { 0, };
|
||||
if (!glnx_open_anonymous_tmpfile (O_RDWR|O_CLOEXEC, &tmpf, error))
|
||||
goto out;
|
||||
|
||||
g_autoptr(FILE) f = fdopen (tmpf.fd, "w");
|
||||
if (fwrite ("hello", 1, strlen ("hello"), f) != strlen ("hello"))
|
||||
{
|
||||
(void)glnx_throw_errno_prefix (error, "fwrite");
|
||||
goto out;
|
||||
}
|
||||
if (!glnx_stdio_file_flush (f, error))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
g_assert_no_error (local_error);
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
@@ -167,6 +189,7 @@ int main (int argc, char **argv)
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/tmpfile", test_tmpfile);
|
||||
g_test_add_func ("/stdio-file", test_stdio_file);
|
||||
g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace);
|
||||
g_test_add_func ("/renameat2-exchange", test_renameat2_exchange);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user