Blame libglnx/glnx-fdio.h

rpm-build c487f7
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
rpm-build c487f7
 *
rpm-build c487f7
 * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
rpm-build c487f7
 *
rpm-build c487f7
 * This library is free software; you can redistribute it and/or
rpm-build c487f7
 * modify it under the terms of the GNU Lesser General Public
rpm-build c487f7
 * License as published by the Free Software Foundation; either
rpm-build c487f7
 * version 2 of the License, or (at your option) any later version.
rpm-build c487f7
 *
rpm-build c487f7
 * This library is distributed in the hope that it will be useful,
rpm-build c487f7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rpm-build c487f7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
rpm-build c487f7
 * Lesser General Public License for more details.
rpm-build c487f7
 *
rpm-build c487f7
 * You should have received a copy of the GNU Lesser General Public
rpm-build c487f7
 * License along with this library; if not, write to the
rpm-build c487f7
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
rpm-build c487f7
 * Boston, MA 02111-1307, USA.
rpm-build c487f7
 */
rpm-build c487f7
rpm-build c487f7
#pragma once
rpm-build c487f7
rpm-build c487f7
#include <glnx-backport-autocleanups.h>
rpm-build c487f7
#include <gio/gfiledescriptorbased.h>
rpm-build c487f7
#include <limits.h>
rpm-build c487f7
#include <dirent.h>
rpm-build c487f7
#include <sys/stat.h>
rpm-build c487f7
#include <fcntl.h>
rpm-build c487f7
#include <string.h>
rpm-build c487f7
#include <stdio.h>
rpm-build c487f7
#include <sys/xattr.h>
rpm-build c487f7
// For dirname(), and previously basename()
rpm-build c487f7
#include <libgen.h>
rpm-build c487f7
rpm-build c487f7
#include <glnx-macros.h>
rpm-build c487f7
#include <glnx-errors.h>
rpm-build c487f7
rpm-build c487f7
G_BEGIN_DECLS
rpm-build c487f7
rpm-build c487f7
/* Irritatingly, g_basename() which is what we want
rpm-build c487f7
 * is deprecated.
rpm-build c487f7
 */
rpm-build c487f7
static inline
rpm-build c487f7
const char *glnx_basename (const char *path)
rpm-build c487f7
{
rpm-build c487f7
  gchar *base = strrchr (path, G_DIR_SEPARATOR);
rpm-build c487f7
rpm-build c487f7
  if (base)
rpm-build c487f7
    return base + 1;
rpm-build c487f7
rpm-build c487f7
  return path;
rpm-build c487f7
}
rpm-build c487f7
rpm-build c487f7
/* Utilities for standard FILE* */
rpm-build c487f7
static inline void
rpm-build c487f7
glnx_stdio_file_cleanup (void *filep)
rpm-build c487f7
{
rpm-build c487f7
  FILE *f = filep;
rpm-build c487f7
  if (f)
rpm-build c487f7
    fclose (f);
rpm-build c487f7
}
rpm-build c487f7
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FILE, glnx_stdio_file_cleanup)
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * glnx_stdio_file_flush:
rpm-build c487f7
 * Call fflush() and check ferror().
rpm-build c487f7
 */
rpm-build c487f7
gboolean
rpm-build c487f7
glnx_stdio_file_flush (FILE *f, GError **error);
rpm-build c487f7
rpm-build c487f7
typedef struct {
rpm-build c487f7
  gboolean initialized;
rpm-build c487f7
  gboolean anonymous;
rpm-build c487f7
  int src_dfd;
rpm-build c487f7
  int fd;
rpm-build c487f7
  char *path;
rpm-build c487f7
} GLnxTmpfile;
rpm-build c487f7
void glnx_tmpfile_clear (GLnxTmpfile *tmpf);
rpm-build c487f7
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpfile, glnx_tmpfile_clear)
rpm-build c487f7
rpm-build c487f7
gboolean
rpm-build c487f7
glnx_open_anonymous_tmpfile (int flags,
rpm-build c487f7
                             GLnxTmpfile *out_tmpf,
rpm-build c487f7
                             GError **error);
rpm-build c487f7
rpm-build c487f7
gboolean
rpm-build c487f7
glnx_open_tmpfile_linkable_at (int dfd,
rpm-build c487f7
                               const char *subpath,
rpm-build c487f7
                               int flags,
rpm-build c487f7
                               GLnxTmpfile *out_tmpf,
rpm-build c487f7
                               GError **error);
rpm-build c487f7
rpm-build c487f7
typedef enum {
rpm-build c487f7
  GLNX_LINK_TMPFILE_REPLACE,
rpm-build c487f7
  GLNX_LINK_TMPFILE_NOREPLACE,
rpm-build c487f7
  GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST
rpm-build c487f7
} GLnxLinkTmpfileReplaceMode;
rpm-build c487f7
rpm-build c487f7
gboolean
rpm-build c487f7
glnx_link_tmpfile_at (GLnxTmpfile *tmpf,
rpm-build c487f7
                      GLnxLinkTmpfileReplaceMode flags,
rpm-build c487f7
                      int target_dfd,
rpm-build c487f7
                      const char *target,
rpm-build c487f7
                      GError **error);
rpm-build c487f7
rpm-build c487f7
gboolean
rpm-build c487f7
glnx_openat_rdonly (int             dfd,
rpm-build c487f7
                    const char     *path,
rpm-build c487f7
                    gboolean        follow,
rpm-build c487f7
                    int            *out_fd,
rpm-build c487f7
                    GError        **error);
rpm-build c487f7
rpm-build c487f7
GBytes *
rpm-build c487f7
glnx_fd_readall_bytes (int               fd,
rpm-build c487f7
                       GCancellable     *cancellable,
rpm-build c487f7
                       GError          **error);
rpm-build c487f7
rpm-build c487f7
char *
rpm-build c487f7
glnx_fd_readall_utf8 (int               fd,
rpm-build c487f7
                      gsize            *out_len,
rpm-build c487f7
                      GCancellable     *cancellable,
rpm-build c487f7
                      GError          **error);
rpm-build c487f7
rpm-build c487f7
char *
rpm-build c487f7
glnx_file_get_contents_utf8_at (int                   dfd,
rpm-build c487f7
                                const char           *subpath,
rpm-build c487f7
                                gsize                *out_len,
rpm-build c487f7
                                GCancellable         *cancellable,
rpm-build c487f7
                                GError              **error);
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * GLnxFileReplaceFlags:
rpm-build c487f7
 * @GLNX_FILE_REPLACE_DATASYNC_NEW: Call fdatasync() even if the file did not exist
rpm-build c487f7
 * @GLNX_FILE_REPLACE_NODATASYNC: Never call fdatasync()
rpm-build c487f7
 *
rpm-build c487f7
 * Flags controlling file replacement.
rpm-build c487f7
 */
rpm-build c487f7
typedef enum {
rpm-build c487f7
  GLNX_FILE_REPLACE_DATASYNC_NEW = (1 << 0),
rpm-build c487f7
  GLNX_FILE_REPLACE_NODATASYNC = (1 << 1),
rpm-build c487f7
} GLnxFileReplaceFlags;
rpm-build c487f7
rpm-build c487f7
gboolean
rpm-build c487f7
glnx_file_replace_contents_at (int                   dfd,
rpm-build c487f7
                               const char           *subpath,
rpm-build c487f7
                               const guint8         *buf,
rpm-build c487f7
                               gsize                 len,
rpm-build c487f7
                               GLnxFileReplaceFlags  flags,
rpm-build c487f7
                               GCancellable         *cancellable,
rpm-build c487f7
                               GError              **error);
rpm-build c487f7
rpm-build c487f7
gboolean
rpm-build c487f7
glnx_file_replace_contents_with_perms_at (int                   dfd,
rpm-build c487f7
                                          const char           *subpath,
rpm-build c487f7
                                          const guint8         *buf,
rpm-build c487f7
                                          gsize                 len,
rpm-build c487f7
                                          mode_t                mode,
rpm-build c487f7
                                          uid_t                 uid,
rpm-build c487f7
                                          gid_t                 gid,
rpm-build c487f7
                                          GLnxFileReplaceFlags  flags,
rpm-build c487f7
                                          GCancellable         *cancellable,
rpm-build c487f7
                                          GError              **error);
rpm-build c487f7
rpm-build c487f7
char *
rpm-build c487f7
glnx_readlinkat_malloc (int            dfd,
rpm-build c487f7
                        const char    *subpath,
rpm-build c487f7
                        GCancellable  *cancellable,
rpm-build c487f7
                        GError       **error);
rpm-build c487f7
rpm-build c487f7
int
rpm-build c487f7
glnx_loop_write (int fd, const void *buf, size_t nbytes);
rpm-build c487f7
rpm-build c487f7
int
rpm-build c487f7
glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes);
rpm-build c487f7
rpm-build c487f7
typedef enum {
rpm-build c487f7
  GLNX_FILE_COPY_OVERWRITE = (1 << 0),
rpm-build c487f7
  GLNX_FILE_COPY_NOXATTRS = (1 << 1),
rpm-build c487f7
  GLNX_FILE_COPY_DATASYNC = (1 << 2)
rpm-build c487f7
} GLnxFileCopyFlags;
rpm-build c487f7
rpm-build c487f7
gboolean
rpm-build c487f7
glnx_file_copy_at (int                   src_dfd,
rpm-build c487f7
                   const char           *src_subpath,
rpm-build c487f7
                   struct stat          *src_stbuf,
rpm-build c487f7
                   int                   dest_dfd,
rpm-build c487f7
                   const char           *dest_subpath,
rpm-build c487f7
                   GLnxFileCopyFlags     copyflags,
rpm-build c487f7
                   GCancellable         *cancellable,
rpm-build c487f7
                   GError              **error);
rpm-build c487f7
rpm-build c487f7
int glnx_renameat2_noreplace (int olddirfd, const char *oldpath,
rpm-build c487f7
                              int newdirfd, const char *newpath);
rpm-build c487f7
int glnx_renameat2_exchange (int olddirfd, const char *oldpath,
rpm-build c487f7
                             int newdirfd, const char *newpath);
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * glnx_try_fallocate:
rpm-build c487f7
 * @fd: File descriptor
rpm-build c487f7
 * @size: Size
rpm-build c487f7
 * @error: Error
rpm-build c487f7
 *
rpm-build c487f7
 * Wrapper for Linux fallocate().  Explicitly ignores a @size of zero.
rpm-build c487f7
 * Also, will silently do nothing if the underlying filesystem doesn't
rpm-build c487f7
 * support it.  Use this instead of posix_fallocate(), since the glibc fallback
rpm-build c487f7
 * is bad: https://sourceware.org/bugzilla/show_bug.cgi?id=18515
rpm-build c487f7
 */
rpm-build c487f7
static inline gboolean
rpm-build c487f7
glnx_try_fallocate (int      fd,
rpm-build c487f7
                    off_t    offset,
rpm-build c487f7
                    off_t    size,
rpm-build c487f7
                    GError **error)
rpm-build c487f7
{
rpm-build c487f7
  /* This is just nicer than throwing an error */
rpm-build c487f7
  if (size == 0)
rpm-build c487f7
    return TRUE;
rpm-build c487f7
rpm-build c487f7
  if (fallocate (fd, 0, offset, size) < 0)
rpm-build c487f7
    {
rpm-build c487f7
      if (G_IN_SET(errno, ENOSYS, EOPNOTSUPP))
rpm-build c487f7
        ; /* Ignore */
rpm-build c487f7
      else
rpm-build c487f7
        return glnx_throw_errno_prefix (error, "fallocate");
rpm-build c487f7
    }
rpm-build c487f7
rpm-build c487f7
  return TRUE;
rpm-build c487f7
}
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * glnx_fstat:
rpm-build c487f7
 * @fd: FD to stat
rpm-build c487f7
 * @buf: (out caller-allocates): Return location for stat details
rpm-build c487f7
 * @error: Return location for a #GError, or %NULL
rpm-build c487f7
 *
rpm-build c487f7
 * Wrapper around fstat() which adds #GError support and ensures that it retries
rpm-build c487f7
 * on %EINTR.
rpm-build c487f7
 *
rpm-build c487f7
 * Returns: %TRUE on success, %FALSE otherwise
rpm-build c487f7
 * Since: UNRELEASED
rpm-build c487f7
 */
rpm-build c487f7
static inline gboolean
rpm-build c487f7
glnx_fstat (int           fd,
rpm-build c487f7
            struct stat  *buf,
rpm-build c487f7
            GError      **error)
rpm-build c487f7
{
rpm-build c487f7
  if (TEMP_FAILURE_RETRY (fstat (fd, buf)) != 0)
rpm-build c487f7
    return glnx_throw_errno_prefix (error, "fstat");
rpm-build c487f7
  return TRUE;
rpm-build c487f7
}
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * glnx_fchmod:
rpm-build c487f7
 * @fd: FD
rpm-build c487f7
 * @mode: Mode
rpm-build c487f7
 * @error: Return location for a #GError, or %NULL
rpm-build c487f7
 *
rpm-build c487f7
 * Wrapper around fchmod() which adds #GError support and ensures that it
rpm-build c487f7
 * retries on %EINTR.
rpm-build c487f7
 *
rpm-build c487f7
 * Returns: %TRUE on success, %FALSE otherwise
rpm-build c487f7
 * Since: UNRELEASED
rpm-build c487f7
 */
rpm-build c487f7
static inline gboolean
rpm-build c487f7
glnx_fchmod (int           fd,
rpm-build c487f7
             mode_t        mode,
rpm-build c487f7
             GError      **error)
rpm-build c487f7
{
rpm-build c487f7
  if (TEMP_FAILURE_RETRY (fchmod (fd, mode)) != 0)
rpm-build c487f7
    return glnx_throw_errno_prefix (error, "fchmod");
rpm-build c487f7
  return TRUE;
rpm-build c487f7
}
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * glnx_fstatat:
rpm-build c487f7
 * @dfd: Directory FD to stat beneath
rpm-build c487f7
 * @path: Path to stat beneath @dfd
rpm-build c487f7
 * @buf: (out caller-allocates): Return location for stat details
rpm-build c487f7
 * @flags: Flags to pass to fstatat()
rpm-build c487f7
 * @error: Return location for a #GError, or %NULL
rpm-build c487f7
 *
rpm-build c487f7
 * Wrapper around fstatat() which adds #GError support and ensures that it
rpm-build c487f7
 * retries on %EINTR.
rpm-build c487f7
 *
rpm-build c487f7
 * Returns: %TRUE on success, %FALSE otherwise
rpm-build c487f7
 * Since: UNRELEASED
rpm-build c487f7
 */
rpm-build c487f7
static inline gboolean
rpm-build c487f7
glnx_fstatat (int           dfd,
rpm-build c487f7
              const gchar  *path,
rpm-build c487f7
              struct stat  *buf,
rpm-build c487f7
              int           flags,
rpm-build c487f7
              GError      **error)
rpm-build c487f7
{
rpm-build c487f7
  if (TEMP_FAILURE_RETRY (fstatat (dfd, path, buf, flags)) != 0)
rpm-build c487f7
    return glnx_throw_errno_prefix (error, "fstatat(%s)", path);
rpm-build c487f7
  return TRUE;
rpm-build c487f7
}
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * glnx_fstatat_allow_noent:
rpm-build c487f7
 * @dfd: Directory FD to stat beneath
rpm-build c487f7
 * @path: Path to stat beneath @dfd
rpm-build c487f7
 * @buf: (out caller-allocates) (allow-none): Return location for stat details
rpm-build c487f7
 * @flags: Flags to pass to fstatat()
rpm-build c487f7
 * @error: Return location for a #GError, or %NULL
rpm-build c487f7
 *
rpm-build c487f7
 * Like glnx_fstatat(), but handles `ENOENT` in a non-error way.  Instead,
rpm-build c487f7
 * on success `errno` will be zero, otherwise it will be preserved.  Hence
rpm-build c487f7
 * you can test `if (errno == 0)` to conditionalize on the file existing,
rpm-build c487f7
 * or `if (errno == ENOENT)` for non-existence.
rpm-build c487f7
 *
rpm-build c487f7
 * Returns: %TRUE on success, %FALSE otherwise (errno is preserved)
rpm-build c487f7
 * Since: UNRELEASED
rpm-build c487f7
 */
rpm-build c487f7
static inline gboolean
rpm-build c487f7
glnx_fstatat_allow_noent (int               dfd,
rpm-build c487f7
                          const char       *path,
rpm-build c487f7
                          struct stat      *out_buf,
rpm-build c487f7
                          int               flags,
rpm-build c487f7
                          GError          **error)
rpm-build c487f7
{
rpm-build c487f7
  G_GNUC_UNUSED struct stat unused_stbuf;
rpm-build c487f7
  if (TEMP_FAILURE_RETRY (fstatat (dfd, path, out_buf ? out_buf : &unused_stbuf, flags)) != 0)
rpm-build c487f7
    {
rpm-build c487f7
      if (errno != ENOENT)
rpm-build c487f7
        return glnx_throw_errno_prefix (error, "fstatat(%s)", path);
rpm-build c487f7
      /* Note we preserve errno as ENOENT */
rpm-build c487f7
    }
rpm-build c487f7
  else
rpm-build c487f7
    errno = 0;
rpm-build c487f7
  return TRUE;
rpm-build c487f7
}
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * glnx_renameat:
rpm-build c487f7
 *
rpm-build c487f7
 * Wrapper around renameat() which adds #GError support and ensures that it
rpm-build c487f7
 * retries on %EINTR.
rpm-build c487f7
 */
rpm-build c487f7
static inline gboolean
rpm-build c487f7
glnx_renameat (int           src_dfd,
rpm-build c487f7
               const gchar  *src_path,
rpm-build c487f7
               int           dest_dfd,
rpm-build c487f7
               const gchar  *dest_path,
rpm-build c487f7
               GError      **error)
rpm-build c487f7
{
rpm-build c487f7
  if (TEMP_FAILURE_RETRY (renameat (src_dfd, src_path, dest_dfd, dest_path)) != 0)
rpm-build c487f7
    return glnx_throw_errno_prefix (error, "renameat(%s, %s)", src_path, dest_path);
rpm-build c487f7
  return TRUE;
rpm-build c487f7
}
rpm-build c487f7
rpm-build c487f7
/**
rpm-build c487f7
 * glnx_unlinkat:
rpm-build c487f7
 *
rpm-build c487f7
 * Wrapper around unlinkat() which adds #GError support and ensures that it
rpm-build c487f7
 * retries on %EINTR.
rpm-build c487f7
 */
rpm-build c487f7
static inline gboolean
rpm-build c487f7
glnx_unlinkat (int           dfd,
rpm-build c487f7
               const gchar  *path,
rpm-build c487f7
               int           flags,
rpm-build c487f7
               GError      **error)
rpm-build c487f7
{
rpm-build c487f7
  if (TEMP_FAILURE_RETRY (unlinkat (dfd, path, flags)) != 0)
rpm-build c487f7
    return glnx_throw_errno_prefix (error, "unlinkat(%s)", path);
rpm-build c487f7
  return TRUE;
rpm-build c487f7
}
rpm-build c487f7
rpm-build c487f7
G_END_DECLS