|
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 |
* Portions derived from systemd:
|
|
rpm-build |
c487f7 |
* Copyright 2010 Lennart Poettering
|
|
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 |
#include "config.h"
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
#include <string.h>
|
|
rpm-build |
c487f7 |
#include <stdio.h>
|
|
rpm-build |
c487f7 |
#include <stdlib.h>
|
|
rpm-build |
c487f7 |
#include <stdint.h>
|
|
rpm-build |
c487f7 |
#include <stdbool.h>
|
|
rpm-build |
c487f7 |
#include <sys/ioctl.h>
|
|
rpm-build |
c487f7 |
#include <sys/sendfile.h>
|
|
rpm-build |
c487f7 |
#include <errno.h>
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
#include <glnx-fdio.h>
|
|
rpm-build |
c487f7 |
#include <glnx-dirfd.h>
|
|
rpm-build |
c487f7 |
#include <glnx-errors.h>
|
|
rpm-build |
c487f7 |
#include <glnx-xattrs.h>
|
|
rpm-build |
c487f7 |
#include <glnx-backport-autoptr.h>
|
|
rpm-build |
c487f7 |
#include <glnx-local-alloc.h>
|
|
rpm-build |
c487f7 |
#include <glnx-missing.h>
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* The standardized version of BTRFS_IOC_CLONE */
|
|
rpm-build |
c487f7 |
#ifndef FICLONE
|
|
rpm-build |
c487f7 |
#define FICLONE _IOW(0x94, 9, int)
|
|
rpm-build |
c487f7 |
#endif
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Returns the number of chars needed to format variables of the
|
|
rpm-build |
c487f7 |
* specified type as a decimal string. Adds in extra space for a
|
|
rpm-build |
c487f7 |
* negative '-' prefix (hence works correctly on signed
|
|
rpm-build |
c487f7 |
* types). Includes space for the trailing NUL. */
|
|
rpm-build |
c487f7 |
#define DECIMAL_STR_MAX(type) \
|
|
rpm-build |
c487f7 |
(2+(sizeof(type) <= 1 ? 3 : \
|
|
rpm-build |
c487f7 |
sizeof(type) <= 2 ? 5 : \
|
|
rpm-build |
c487f7 |
sizeof(type) <= 4 ? 10 : \
|
|
rpm-build |
c487f7 |
sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
gboolean
|
|
rpm-build |
c487f7 |
glnx_stdio_file_flush (FILE *f, GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (fflush (f) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fflush");
|
|
rpm-build |
c487f7 |
if (ferror (f) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "ferror");
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* An implementation of renameat2(..., RENAME_NOREPLACE)
|
|
rpm-build |
c487f7 |
* with fallback to a non-atomic version.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
int
|
|
rpm-build |
c487f7 |
glnx_renameat2_noreplace (int olddirfd, const char *oldpath,
|
|
rpm-build |
c487f7 |
int newdirfd, const char *newpath)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
#ifndef ENABLE_WRPSEUDO_COMPAT
|
|
rpm-build |
c487f7 |
if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) < 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (G_IN_SET(errno, EINVAL, ENOSYS))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* Fall through */
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
#endif
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (linkat (olddirfd, oldpath, newdirfd, newpath, 0) < 0)
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (unlinkat (olddirfd, oldpath, 0) < 0)
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return 0;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
static gboolean
|
|
rpm-build |
c487f7 |
rename_file_noreplace_at (int olddirfd, const char *oldpath,
|
|
rpm-build |
c487f7 |
int newdirfd, const char *newpath,
|
|
rpm-build |
c487f7 |
gboolean ignore_eexist,
|
|
rpm-build |
c487f7 |
GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (glnx_renameat2_noreplace (olddirfd, oldpath,
|
|
rpm-build |
c487f7 |
newdirfd, newpath) < 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno == EEXIST && ignore_eexist)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
(void) unlinkat (olddirfd, oldpath, 0);
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "renameat");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* An implementation of renameat2(..., RENAME_EXCHANGE)
|
|
rpm-build |
c487f7 |
* with fallback to a non-atomic version.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
int
|
|
rpm-build |
c487f7 |
glnx_renameat2_exchange (int olddirfd, const char *oldpath,
|
|
rpm-build |
c487f7 |
int newdirfd, const char *newpath)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
#ifndef ENABLE_WRPSEUDO_COMPAT
|
|
rpm-build |
c487f7 |
if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_EXCHANGE) == 0)
|
|
rpm-build |
c487f7 |
return 0;
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (G_IN_SET(errno, ENOSYS, EINVAL))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* Fall through */
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
#endif
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Fallback */
|
|
rpm-build |
c487f7 |
{ char *old_tmp_name_buf = glnx_strjoina (oldpath, ".XXXXXX");
|
|
rpm-build |
c487f7 |
/* This obviously isn't race-free, but doing better gets tricky, since if
|
|
rpm-build |
c487f7 |
* we're here the kernel isn't likely to support RENAME_NOREPLACE either.
|
|
rpm-build |
c487f7 |
* Anyways, upgrade the kernel. Failing that, avoid use of this function in
|
|
rpm-build |
c487f7 |
* shared subdirectories like /tmp.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
glnx_gen_temp_name (old_tmp_name_buf);
|
|
rpm-build |
c487f7 |
const char *old_tmp_name = old_tmp_name_buf;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Move old out of the way */
|
|
rpm-build |
c487f7 |
if (renameat (olddirfd, oldpath, olddirfd, old_tmp_name) < 0)
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
/* Now move new into its place */
|
|
rpm-build |
c487f7 |
if (renameat (newdirfd, newpath, olddirfd, oldpath) < 0)
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
/* And finally old(tmp) into new */
|
|
rpm-build |
c487f7 |
if (renameat (olddirfd, old_tmp_name, newdirfd, newpath) < 0)
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
return 0;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Deallocate a tmpfile, closing the fd and deleting the path, if any. This is
|
|
rpm-build |
c487f7 |
* normally called by default by the autocleanup attribute, but you can also
|
|
rpm-build |
c487f7 |
* invoke this directly.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
void
|
|
rpm-build |
c487f7 |
glnx_tmpfile_clear (GLnxTmpfile *tmpf)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* Support being passed NULL so we work nicely in a GPtrArray */
|
|
rpm-build |
c487f7 |
if (!tmpf)
|
|
rpm-build |
c487f7 |
return;
|
|
rpm-build |
c487f7 |
if (!tmpf->initialized)
|
|
rpm-build |
c487f7 |
return;
|
|
rpm-build |
c487f7 |
glnx_close_fd (&tmpf->fd);
|
|
rpm-build |
c487f7 |
/* If ->path is set, we're likely aborting due to an error. Clean it up */
|
|
rpm-build |
c487f7 |
if (tmpf->path)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
(void) unlinkat (tmpf->src_dfd, tmpf->path, 0);
|
|
rpm-build |
c487f7 |
g_free (tmpf->path);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
tmpf->initialized = FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
static gboolean
|
|
rpm-build |
c487f7 |
open_tmpfile_core (int dfd, 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 |
/* Picked this to match mkstemp() */
|
|
rpm-build |
c487f7 |
const guint mode = 0600;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
dfd = glnx_dirfd_canonicalize (dfd);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Creates a temporary file, that shall be renamed to "target"
|
|
rpm-build |
c487f7 |
* later. If possible, this uses O_TMPFILE – in which case
|
|
rpm-build |
c487f7 |
* "ret_path" will be returned as NULL. If not possible a the
|
|
rpm-build |
c487f7 |
* tempoary path name used is returned in "ret_path". Use
|
|
rpm-build |
c487f7 |
* link_tmpfile() below to rename the result after writing the file
|
|
rpm-build |
c487f7 |
* in full. */
|
|
rpm-build |
c487f7 |
#if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) && !defined(ENABLE_WRPSEUDO_COMPAT)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
glnx_autofd int fd = openat (dfd, subpath, O_TMPFILE|flags, mode);
|
|
rpm-build |
c487f7 |
if (fd == -1 && !(G_IN_SET(errno, ENOSYS, EISDIR, EOPNOTSUPP)))
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "open(O_TMPFILE)");
|
|
rpm-build |
c487f7 |
if (fd != -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17523
|
|
rpm-build |
c487f7 |
* See also https://github.com/ostreedev/ostree/issues/991
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
if (fchmod (fd, mode) < 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fchmod");
|
|
rpm-build |
c487f7 |
out_tmpf->initialized = TRUE;
|
|
rpm-build |
c487f7 |
out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */
|
|
rpm-build |
c487f7 |
out_tmpf->fd = glnx_steal_fd (&fd;;
|
|
rpm-build |
c487f7 |
out_tmpf->path = NULL;
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
/* Fallthrough */
|
|
rpm-build |
c487f7 |
#endif
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
const guint count_max = 100;
|
|
rpm-build |
c487f7 |
{ g_autofree char *tmp = g_strconcat (subpath, "/tmp.XXXXXX", NULL);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
for (int count = 0; count < count_max; count++)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
glnx_gen_temp_name (tmp);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
glnx_autofd int fd = openat (dfd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, mode);
|
|
rpm-build |
c487f7 |
if (fd < 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno == EEXIST)
|
|
rpm-build |
c487f7 |
continue;
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "Creating temp file");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
out_tmpf->initialized = TRUE;
|
|
rpm-build |
c487f7 |
out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */
|
|
rpm-build |
c487f7 |
out_tmpf->fd = glnx_steal_fd (&fd;;
|
|
rpm-build |
c487f7 |
out_tmpf->path = g_steal_pointer (&tmp);
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
|
|
rpm-build |
c487f7 |
"Exhausted %u attempts to create temporary file", count_max);
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Allocate a temporary file, using Linux O_TMPFILE if available. The file mode
|
|
rpm-build |
c487f7 |
* will be 0600.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* The result will be stored in @out_tmpf, which is caller allocated
|
|
rpm-build |
c487f7 |
* so you can store it on the stack in common scenarios.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* The directory fd @dfd must live at least as long as the output @out_tmpf.
|
|
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 |
/* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE;
|
|
rpm-build |
c487f7 |
* it's used for glnx_open_anonymous_tmpfile().
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
g_return_val_if_fail ((flags & O_EXCL) == 0, FALSE);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return open_tmpfile_core (dfd, subpath, flags, out_tmpf, error);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* A variant of `glnx_open_tmpfile_linkable_at()` which doesn't support linking.
|
|
rpm-build |
c487f7 |
* Useful for true temporary storage. The fd will be allocated in /var/tmp to
|
|
rpm-build |
c487f7 |
* ensure maximum storage space.
|
|
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 |
/* Add in O_EXCL */
|
|
rpm-build |
c487f7 |
if (!open_tmpfile_core (AT_FDCWD, "/var/tmp", flags | O_EXCL, out_tmpf, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
if (out_tmpf->path)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
(void) unlinkat (out_tmpf->src_dfd, out_tmpf->path, 0);
|
|
rpm-build |
c487f7 |
g_clear_pointer (&out_tmpf->path, g_free);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
out_tmpf->anonymous = TRUE;
|
|
rpm-build |
c487f7 |
out_tmpf->src_dfd = -1;
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Use this after calling glnx_open_tmpfile_linkable_at() to give
|
|
rpm-build |
c487f7 |
* the file its final name (link into place).
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
gboolean
|
|
rpm-build |
c487f7 |
glnx_link_tmpfile_at (GLnxTmpfile *tmpf,
|
|
rpm-build |
c487f7 |
GLnxLinkTmpfileReplaceMode mode,
|
|
rpm-build |
c487f7 |
int target_dfd,
|
|
rpm-build |
c487f7 |
const char *target,
|
|
rpm-build |
c487f7 |
GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
const gboolean replace = (mode == GLNX_LINK_TMPFILE_REPLACE);
|
|
rpm-build |
c487f7 |
const gboolean ignore_eexist = (mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
g_return_val_if_fail (!tmpf->anonymous, FALSE);
|
|
rpm-build |
c487f7 |
g_return_val_if_fail (tmpf->fd >= 0, FALSE);
|
|
rpm-build |
c487f7 |
g_return_val_if_fail (tmpf->src_dfd == AT_FDCWD || tmpf->src_dfd >= 0, FALSE);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Unlike the original systemd code, this function also supports
|
|
rpm-build |
c487f7 |
* replacing existing files.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* We have `tmpfile_path` for old systems without O_TMPFILE. */
|
|
rpm-build |
c487f7 |
if (tmpf->path)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (replace)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* We have a regular tempfile, we're overwriting - this is a
|
|
rpm-build |
c487f7 |
* simple renameat().
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
if (renameat (tmpf->src_dfd, tmpf->path, target_dfd, target) < 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "renameat");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* We need to use renameat2(..., NOREPLACE) or emulate it */
|
|
rpm-build |
c487f7 |
if (!rename_file_noreplace_at (tmpf->src_dfd, tmpf->path, target_dfd, target,
|
|
rpm-build |
c487f7 |
ignore_eexist,
|
|
rpm-build |
c487f7 |
error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
/* Now, clear the pointer so we don't try to unlink it */
|
|
rpm-build |
c487f7 |
g_clear_pointer (&tmpf->path, g_free);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* This case we have O_TMPFILE, so our reference to it is via /proc/self/fd */
|
|
rpm-build |
c487f7 |
char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(tmpf->fd) + 1];
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
sprintf (proc_fd_path, "/proc/self/fd/%i", tmpf->fd);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (replace)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* In this case, we had our temp file atomically hidden, but now
|
|
rpm-build |
c487f7 |
* we need to make it visible in the FS so we can do a rename.
|
|
rpm-build |
c487f7 |
* Ideally, linkat() would gain AT_REPLACE or so.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
/* TODO - avoid double alloca, we can just alloca a copy of
|
|
rpm-build |
c487f7 |
* the pathname plus space for tmp.XXXXX */
|
|
rpm-build |
c487f7 |
char *dnbuf = strdupa (target);
|
|
rpm-build |
c487f7 |
const char *dn = dirname (dnbuf);
|
|
rpm-build |
c487f7 |
char *tmpname_buf = glnx_strjoina (dn, "/tmp.XXXXXX");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
const guint count_max = 100;
|
|
rpm-build |
c487f7 |
guint count;
|
|
rpm-build |
c487f7 |
for (count = 0; count < count_max; count++)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
glnx_gen_temp_name (tmpname_buf);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (linkat (AT_FDCWD, proc_fd_path, target_dfd, tmpname_buf, AT_SYMLINK_FOLLOW) < 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno == EEXIST)
|
|
rpm-build |
c487f7 |
continue;
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "linkat");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
break;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
if (count == count_max)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
|
|
rpm-build |
c487f7 |
"Exhausted %u attempts to create temporary file", count);
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
if (!glnx_renameat (target_dfd, tmpname_buf, target_dfd, target, error))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* This is currently the only case where we need to have
|
|
rpm-build |
c487f7 |
* a cleanup unlinkat() still with O_TMPFILE.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
(void) unlinkat (target_dfd, tmpname_buf, 0);
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (linkat (AT_FDCWD, proc_fd_path, target_dfd, target, AT_SYMLINK_FOLLOW) < 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno == EEXIST && mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST)
|
|
rpm-build |
c487f7 |
;
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "linkat");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_openat_rdonly:
|
|
rpm-build |
c487f7 |
* @dfd: File descriptor for origin directory
|
|
rpm-build |
c487f7 |
* @path: Pathname, relative to @dfd
|
|
rpm-build |
c487f7 |
* @follow: Whether or not to follow symbolic links in the final component
|
|
rpm-build |
c487f7 |
* @out_fd: (out): File descriptor
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Use openat() to open a file, with flags `O_RDONLY | O_CLOEXEC | O_NOCTTY`.
|
|
rpm-build |
c487f7 |
* Like the other libglnx wrappers, will use `TEMP_FAILURE_RETRY` and
|
|
rpm-build |
c487f7 |
* also includes @path in @error in case of failure.
|
|
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 |
int flags = O_RDONLY | O_CLOEXEC | O_NOCTTY;
|
|
rpm-build |
c487f7 |
if (!follow)
|
|
rpm-build |
c487f7 |
flags |= O_NOFOLLOW;
|
|
rpm-build |
c487f7 |
int fd = TEMP_FAILURE_RETRY (openat (dfd, path, flags));
|
|
rpm-build |
c487f7 |
if (fd == -1)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "openat(%s)", path);
|
|
rpm-build |
c487f7 |
*out_fd = fd;
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
static guint8*
|
|
rpm-build |
c487f7 |
glnx_fd_readall_malloc (int fd,
|
|
rpm-build |
c487f7 |
gsize *out_len,
|
|
rpm-build |
c487f7 |
gboolean nul_terminate,
|
|
rpm-build |
c487f7 |
GCancellable *cancellable,
|
|
rpm-build |
c487f7 |
GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
const guint maxreadlen = 4096;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
struct stat stbuf;
|
|
rpm-build |
c487f7 |
if (!glnx_fstat (fd, &stbuf, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
gsize buf_allocated;
|
|
rpm-build |
c487f7 |
if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0)
|
|
rpm-build |
c487f7 |
buf_allocated = stbuf.st_size;
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
buf_allocated = 16;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
g_autofree guint8* buf = g_malloc (buf_allocated);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
gsize buf_size = 0;
|
|
rpm-build |
c487f7 |
while (TRUE)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
gsize readlen = MIN (buf_allocated - buf_size, maxreadlen);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
gssize bytes_read;
|
|
rpm-build |
c487f7 |
do
|
|
rpm-build |
c487f7 |
bytes_read = read (fd, buf + buf_size, readlen);
|
|
rpm-build |
c487f7 |
while (G_UNLIKELY (bytes_read == -1 && errno == EINTR));
|
|
rpm-build |
c487f7 |
if (G_UNLIKELY (bytes_read == -1))
|
|
rpm-build |
c487f7 |
return glnx_null_throw_errno (error);
|
|
rpm-build |
c487f7 |
if (bytes_read == 0)
|
|
rpm-build |
c487f7 |
break;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
buf_size += bytes_read;
|
|
rpm-build |
c487f7 |
if (buf_allocated - buf_size < maxreadlen)
|
|
rpm-build |
c487f7 |
buf = g_realloc (buf, buf_allocated *= 2);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (nul_terminate)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (buf_allocated - buf_size == 0)
|
|
rpm-build |
c487f7 |
buf = g_realloc (buf, buf_allocated + 1);
|
|
rpm-build |
c487f7 |
buf[buf_size] = '\0';
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
*out_len = buf_size;
|
|
rpm-build |
c487f7 |
return g_steal_pointer (&buf;;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_fd_readall_bytes:
|
|
rpm-build |
c487f7 |
* @fd: A file descriptor
|
|
rpm-build |
c487f7 |
* @cancellable: Cancellable:
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Read all data from file descriptor @fd into a #GBytes. It's
|
|
rpm-build |
c487f7 |
* recommended to only use this for small files.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Returns: (transfer full): A newly allocated #GBytes
|
|
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 |
gsize len;
|
|
rpm-build |
c487f7 |
guint8 *buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error);
|
|
rpm-build |
c487f7 |
if (!buf)
|
|
rpm-build |
c487f7 |
return NULL;
|
|
rpm-build |
c487f7 |
return g_bytes_new_take (buf, len);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_fd_readall_utf8:
|
|
rpm-build |
c487f7 |
* @fd: A file descriptor
|
|
rpm-build |
c487f7 |
* @out_len: (out): Returned length
|
|
rpm-build |
c487f7 |
* @cancellable: Cancellable:
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Read all data from file descriptor @fd, validating
|
|
rpm-build |
c487f7 |
* the result as UTF-8.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Returns: (transfer full): A string validated as UTF-8, or %NULL on 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 |
gsize len;
|
|
rpm-build |
c487f7 |
g_autofree guint8 *buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error);
|
|
rpm-build |
c487f7 |
if (!buf)
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!g_utf8_validate ((char*)buf, len, NULL))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_set_error (error,
|
|
rpm-build |
c487f7 |
G_IO_ERROR,
|
|
rpm-build |
c487f7 |
G_IO_ERROR_INVALID_DATA,
|
|
rpm-build |
c487f7 |
"Invalid UTF-8");
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (out_len)
|
|
rpm-build |
c487f7 |
*out_len = len;
|
|
rpm-build |
c487f7 |
return (char*)g_steal_pointer (&buf;;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_file_get_contents_utf8_at:
|
|
rpm-build |
c487f7 |
* @dfd: Directory file descriptor
|
|
rpm-build |
c487f7 |
* @subpath: Path relative to @dfd
|
|
rpm-build |
c487f7 |
* @out_len: (out) (allow-none): Optional length
|
|
rpm-build |
c487f7 |
* @cancellable: Cancellable
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Read the entire contents of the file referred
|
|
rpm-build |
c487f7 |
* to by @dfd and @subpath, validate the result as UTF-8.
|
|
rpm-build |
c487f7 |
* The length is optionally stored in @out_len.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Returns: (transfer full): UTF-8 validated text, or %NULL on 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 |
dfd = glnx_dirfd_canonicalize (dfd);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
glnx_autofd int fd = -1;
|
|
rpm-build |
c487f7 |
if (!glnx_openat_rdonly (dfd, subpath, TRUE, &fd, error))
|
|
rpm-build |
c487f7 |
return NULL;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
gsize len;
|
|
rpm-build |
c487f7 |
g_autofree char *buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error);
|
|
rpm-build |
c487f7 |
if (G_UNLIKELY(!buf))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (out_len)
|
|
rpm-build |
c487f7 |
*out_len = len;
|
|
rpm-build |
c487f7 |
return g_steal_pointer (&buf;;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_readlinkat_malloc:
|
|
rpm-build |
c487f7 |
* @dfd: Directory file descriptor
|
|
rpm-build |
c487f7 |
* @subpath: Subpath
|
|
rpm-build |
c487f7 |
* @cancellable: Cancellable
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Read the value of a symlink into a dynamically
|
|
rpm-build |
c487f7 |
* allocated buffer.
|
|
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 |
dfd = glnx_dirfd_canonicalize (dfd);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
size_t l = 100;
|
|
rpm-build |
c487f7 |
for (;;)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_autofree char *c = g_malloc (l);
|
|
rpm-build |
c487f7 |
ssize_t n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1));
|
|
rpm-build |
c487f7 |
if (n < 0)
|
|
rpm-build |
c487f7 |
return glnx_null_throw_errno_prefix (error, "readlinkat");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if ((size_t) n < l-1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
c[n] = 0;
|
|
rpm-build |
c487f7 |
return g_steal_pointer (&c);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
l *= 2;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
g_assert_not_reached ();
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
static gboolean
|
|
rpm-build |
c487f7 |
copy_symlink_at (int src_dfd,
|
|
rpm-build |
c487f7 |
const char *src_subpath,
|
|
rpm-build |
c487f7 |
const 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 |
g_autofree char *buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error);
|
|
rpm-build |
c487f7 |
if (!buf)
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (TEMP_FAILURE_RETRY (symlinkat (buf, dest_dfd, dest_subpath)) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "symlinkat");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_autoptr(GVariant) xattrs = NULL;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_dfd_name_get_all_xattrs (src_dfd, src_subpath, &xattrs,
|
|
rpm-build |
c487f7 |
cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_dfd_name_set_all_xattrs (dest_dfd, dest_subpath, xattrs,
|
|
rpm-build |
c487f7 |
cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath,
|
|
rpm-build |
c487f7 |
src_stbuf->st_uid, src_stbuf->st_gid,
|
|
rpm-build |
c487f7 |
AT_SYMLINK_NOFOLLOW)) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fchownat");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
#define COPY_BUFFER_SIZE (16*1024)
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Most of the code below is from systemd, but has been reindented to GNU style,
|
|
rpm-build |
c487f7 |
* and changed to use POSIX error conventions (return -1, set errno) to more
|
|
rpm-build |
c487f7 |
* conveniently fit in with the rest of libglnx.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Like write(), but loop until @nbytes are written, or an error
|
|
rpm-build |
c487f7 |
* occurs.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* On error, -1 is returned an @errno is set. NOTE: This is an
|
|
rpm-build |
c487f7 |
* API change from previous versions of this function.
|
|
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 |
g_return_val_if_fail (fd >= 0, -1);
|
|
rpm-build |
c487f7 |
g_return_val_if_fail (buf, -1);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
errno = 0;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
const uint8_t *p = buf;
|
|
rpm-build |
c487f7 |
while (nbytes > 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
ssize_t k = write(fd, p, nbytes);
|
|
rpm-build |
c487f7 |
if (k < 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno == EINTR)
|
|
rpm-build |
c487f7 |
continue;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (k == 0) /* Can't really happen */
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
errno = EIO;
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
p += k;
|
|
rpm-build |
c487f7 |
nbytes -= k;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return 0;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Read from @fdf until EOF, writing to @fdt. If max_bytes is -1, a full-file
|
|
rpm-build |
c487f7 |
* clone will be attempted. Otherwise Linux copy_file_range(), sendfile()
|
|
rpm-build |
c487f7 |
* syscall will be attempted. If none of those work, this function will do a
|
|
rpm-build |
c487f7 |
* plain read()/write() loop.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* The file descriptor @fdf must refer to a regular file.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* If provided, @max_bytes specifies the maximum number of bytes to read from @fdf.
|
|
rpm-build |
c487f7 |
* On error, this function returns `-1` and @errno will be set.
|
|
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 |
/* Last updates from systemd as of commit 6bda23dd6aaba50cf8e3e6024248cf736cc443ca */
|
|
rpm-build |
c487f7 |
static int have_cfr = -1; /* -1 means unknown */
|
|
rpm-build |
c487f7 |
bool try_cfr = have_cfr != 0;
|
|
rpm-build |
c487f7 |
static int have_sendfile = -1; /* -1 means unknown */
|
|
rpm-build |
c487f7 |
bool try_sendfile = have_sendfile != 0;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
g_return_val_if_fail (fdf >= 0, -1);
|
|
rpm-build |
c487f7 |
g_return_val_if_fail (fdt >= 0, -1);
|
|
rpm-build |
c487f7 |
g_return_val_if_fail (max_bytes >= -1, -1);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* If we've requested to copy the whole range, try a full-file clone first.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
if (max_bytes == (off_t) -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (ioctl (fdt, FICLONE, fdf) == 0)
|
|
rpm-build |
c487f7 |
return 0;
|
|
rpm-build |
c487f7 |
/* Fall through */
|
|
rpm-build |
c487f7 |
struct stat stbuf;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Gather the size so we can provide the whole thing at once to
|
|
rpm-build |
c487f7 |
* copy_file_range() or sendfile().
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
if (fstat (fdf, &stbuf) < 0)
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
max_bytes = stbuf.st_size;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
while (TRUE)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
ssize_t n;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* First, try copy_file_range(). Note this is an inlined version of
|
|
rpm-build |
c487f7 |
* try_copy_file_range() from systemd upstream, which works better since
|
|
rpm-build |
c487f7 |
* we use POSIX errno style.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
if (try_cfr)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
n = copy_file_range (fdf, NULL, fdt, NULL, max_bytes, 0u);
|
|
rpm-build |
c487f7 |
if (n < 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno == ENOSYS)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* No cfr in kernel, mark as permanently unavailable
|
|
rpm-build |
c487f7 |
* and fall through to sendfile().
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
have_cfr = 0;
|
|
rpm-build |
c487f7 |
try_cfr = false;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else if (errno == EXDEV)
|
|
rpm-build |
c487f7 |
/* We won't try cfr again for this run, but let's be
|
|
rpm-build |
c487f7 |
* conservative and not mark it as available/unavailable until
|
|
rpm-build |
c487f7 |
* we know for sure.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
try_cfr = false;
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* cfr worked, mark it as available */
|
|
rpm-build |
c487f7 |
if (have_cfr == -1)
|
|
rpm-build |
c487f7 |
have_cfr = 1;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (n == 0) /* EOF */
|
|
rpm-build |
c487f7 |
break;
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
/* Success! */
|
|
rpm-build |
c487f7 |
goto next;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Next try sendfile(); this version is also changed from systemd upstream
|
|
rpm-build |
c487f7 |
* to match the same logic we have for copy_file_range().
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
if (try_sendfile)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
n = sendfile (fdt, fdf, NULL, max_bytes);
|
|
rpm-build |
c487f7 |
if (n < 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (G_IN_SET (errno, EINVAL, ENOSYS))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* No sendfile(), or it doesn't work on regular files.
|
|
rpm-build |
c487f7 |
* Mark it as permanently unavailable, and fall through
|
|
rpm-build |
c487f7 |
* to plain read()/write().
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
have_sendfile = 0;
|
|
rpm-build |
c487f7 |
try_sendfile = false;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* sendfile() worked, mark it as available */
|
|
rpm-build |
c487f7 |
if (have_sendfile == -1)
|
|
rpm-build |
c487f7 |
have_sendfile = 1;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (n == 0) /* EOF */
|
|
rpm-build |
c487f7 |
break;
|
|
rpm-build |
c487f7 |
else if (n > 0)
|
|
rpm-build |
c487f7 |
/* Succcess! */
|
|
rpm-build |
c487f7 |
goto next;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* As a fallback just copy bits by hand */
|
|
rpm-build |
c487f7 |
{ size_t m = COPY_BUFFER_SIZE;
|
|
rpm-build |
c487f7 |
if (max_bytes != (off_t) -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if ((off_t) m > max_bytes)
|
|
rpm-build |
c487f7 |
m = (size_t) max_bytes;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
char buf[m];
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
n = TEMP_FAILURE_RETRY (read (fdf, buf, m));
|
|
rpm-build |
c487f7 |
if (n < 0)
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
if (n == 0) /* EOF */
|
|
rpm-build |
c487f7 |
break;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (glnx_loop_write (fdt, buf, (size_t) n) < 0)
|
|
rpm-build |
c487f7 |
return -1;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
next:
|
|
rpm-build |
c487f7 |
if (max_bytes != (off_t) -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_assert_cmpint (max_bytes, >=, n);
|
|
rpm-build |
c487f7 |
max_bytes -= n;
|
|
rpm-build |
c487f7 |
if (max_bytes == 0)
|
|
rpm-build |
c487f7 |
break;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return 0;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_file_copy_at:
|
|
rpm-build |
c487f7 |
* @src_dfd: Source directory fd
|
|
rpm-build |
c487f7 |
* @src_subpath: Subpath relative to @src_dfd
|
|
rpm-build |
c487f7 |
* @src_stbuf: (allow-none): Optional stat buffer for source; if a stat() has already been done
|
|
rpm-build |
c487f7 |
* @dest_dfd: Target directory fd
|
|
rpm-build |
c487f7 |
* @dest_subpath: Destination name
|
|
rpm-build |
c487f7 |
* @copyflags: Flags
|
|
rpm-build |
c487f7 |
* @cancellable: cancellable
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Perform a full copy of the regular file or symbolic link from @src_subpath to
|
|
rpm-build |
c487f7 |
* @dest_subpath; if @src_subpath is anything other than a regular file or
|
|
rpm-build |
c487f7 |
* symbolic link, an error will be returned.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* If the source is a regular file and the destination exists as a symbolic
|
|
rpm-build |
c487f7 |
* link, the symbolic link will not be followed; rather the link itself will be
|
|
rpm-build |
c487f7 |
* replaced. Related to this: for regular files, when `GLNX_FILE_COPY_OVERWRITE`
|
|
rpm-build |
c487f7 |
* is specified, this function always uses `O_TMPFILE` (if available) and does a
|
|
rpm-build |
c487f7 |
* rename-into-place rather than `open(O_TRUNC)`.
|
|
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 |
/* Canonicalize dfds */
|
|
rpm-build |
c487f7 |
src_dfd = glnx_dirfd_canonicalize (src_dfd);
|
|
rpm-build |
c487f7 |
dest_dfd = glnx_dirfd_canonicalize (dest_dfd);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Automatically do stat() if no stat buffer was supplied */
|
|
rpm-build |
c487f7 |
struct stat local_stbuf;
|
|
rpm-build |
c487f7 |
if (!src_stbuf)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (!glnx_fstatat (src_dfd, src_subpath, &local_stbuf, AT_SYMLINK_NOFOLLOW, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
src_stbuf = &local_stbuf;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* For symlinks, defer entirely to copy_symlink_at() */
|
|
rpm-build |
c487f7 |
if (S_ISLNK (src_stbuf->st_mode))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
return copy_symlink_at (src_dfd, src_subpath, src_stbuf,
|
|
rpm-build |
c487f7 |
dest_dfd, dest_subpath,
|
|
rpm-build |
c487f7 |
copyflags,
|
|
rpm-build |
c487f7 |
cancellable, error);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else if (!S_ISREG (src_stbuf->st_mode))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
rpm-build |
c487f7 |
"Cannot copy non-regular/non-symlink file: %s", src_subpath);
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Regular file path below here */
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
glnx_autofd int src_fd = -1;
|
|
rpm-build |
c487f7 |
if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Open a tmpfile for dest. Particularly for AT_FDCWD calls, we really want to
|
|
rpm-build |
c487f7 |
* open in the target directory, otherwise we may not be able to link.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
g_auto(GLnxTmpfile) tmp_dest = { 0, };
|
|
rpm-build |
c487f7 |
{ char *dnbuf = strdupa (dest_subpath);
|
|
rpm-build |
c487f7 |
const char *dn = dirname (dnbuf);
|
|
rpm-build |
c487f7 |
if (!glnx_open_tmpfile_linkable_at (dest_dfd, dn, O_WRONLY | O_CLOEXEC,
|
|
rpm-build |
c487f7 |
&tmp_dest, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (glnx_regfile_copy_bytes (src_fd, tmp_dest.fd, (off_t) -1) < 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "regfile copy");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (fchown (tmp_dest.fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fchown");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_autoptr(GVariant) xattrs = NULL;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_fd_get_all_xattrs (src_fd, &xattrs,
|
|
rpm-build |
c487f7 |
cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_fd_set_all_xattrs (tmp_dest.fd, xattrs,
|
|
rpm-build |
c487f7 |
cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Always chmod after setting xattrs, in case the file has mode 0400 or less,
|
|
rpm-build |
c487f7 |
* like /etc/shadow. Linux currently allows write() on non-writable open files
|
|
rpm-build |
c487f7 |
* but not fsetxattr().
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
if (fchmod (tmp_dest.fd, src_stbuf->st_mode & 07777) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fchmod");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
struct timespec ts[2];
|
|
rpm-build |
c487f7 |
ts[0] = src_stbuf->st_atim;
|
|
rpm-build |
c487f7 |
ts[1] = src_stbuf->st_mtim;
|
|
rpm-build |
c487f7 |
(void) futimens (tmp_dest.fd, ts);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (copyflags & GLNX_FILE_COPY_DATASYNC)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (fdatasync (tmp_dest.fd) < 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fdatasync");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
const GLnxLinkTmpfileReplaceMode replacemode =
|
|
rpm-build |
c487f7 |
(copyflags & GLNX_FILE_COPY_OVERWRITE) ?
|
|
rpm-build |
c487f7 |
GLNX_LINK_TMPFILE_REPLACE :
|
|
rpm-build |
c487f7 |
GLNX_LINK_TMPFILE_NOREPLACE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_link_tmpfile_at (&tmp_dest, replacemode, dest_dfd, dest_subpath, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_file_replace_contents_at:
|
|
rpm-build |
c487f7 |
* @dfd: Directory fd
|
|
rpm-build |
c487f7 |
* @subpath: Subpath
|
|
rpm-build |
c487f7 |
* @buf: (array len=len) (element-type guint8): File contents
|
|
rpm-build |
c487f7 |
* @len: Length (if `-1`, assume @buf is `NUL` terminated)
|
|
rpm-build |
c487f7 |
* @flags: Flags
|
|
rpm-build |
c487f7 |
* @cancellable: Cancellable
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Create a new file, atomically replacing the contents of @subpath
|
|
rpm-build |
c487f7 |
* (relative to @dfd) with @buf. By default, if the file already
|
|
rpm-build |
c487f7 |
* existed, fdatasync() will be used before rename() to ensure stable
|
|
rpm-build |
c487f7 |
* contents. This and other behavior can be controlled via @flags.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Note that no metadata from the existing file is preserved, such as
|
|
rpm-build |
c487f7 |
* uid/gid or extended attributes. The default mode will be `0666`,
|
|
rpm-build |
c487f7 |
* modified by umask.
|
|
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 |
return glnx_file_replace_contents_with_perms_at (dfd, subpath, buf, len,
|
|
rpm-build |
c487f7 |
(mode_t) -1, (uid_t) -1, (gid_t) -1,
|
|
rpm-build |
c487f7 |
flags, cancellable, error);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_file_replace_contents_with_perms_at:
|
|
rpm-build |
c487f7 |
* @dfd: Directory fd
|
|
rpm-build |
c487f7 |
* @subpath: Subpath
|
|
rpm-build |
c487f7 |
* @buf: (array len=len) (element-type guint8): File contents
|
|
rpm-build |
c487f7 |
* @len: Length (if `-1`, assume @buf is `NUL` terminated)
|
|
rpm-build |
c487f7 |
* @mode: File mode; if `-1`, use `0666 - umask`
|
|
rpm-build |
c487f7 |
* @flags: Flags
|
|
rpm-build |
c487f7 |
* @cancellable: Cancellable
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Like glnx_file_replace_contents_at(), but also supports
|
|
rpm-build |
c487f7 |
* setting mode, and uid/gid.
|
|
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 *dnbuf = strdupa (subpath);
|
|
rpm-build |
c487f7 |
const char *dn = dirname (dnbuf);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
dfd = glnx_dirfd_canonicalize (dfd);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* With O_TMPFILE we can't use umask, and we can't sanely query the
|
|
rpm-build |
c487f7 |
* umask...let's assume something relatively standard.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
if (mode == (mode_t) -1)
|
|
rpm-build |
c487f7 |
mode = 0644;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
g_auto(GLnxTmpfile) tmpf = { 0, };
|
|
rpm-build |
c487f7 |
if (!glnx_open_tmpfile_linkable_at (dfd, dn, O_WRONLY | O_CLOEXEC,
|
|
rpm-build |
c487f7 |
&tmpf, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (len == -1)
|
|
rpm-build |
c487f7 |
len = strlen ((char*)buf);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_try_fallocate (tmpf.fd, 0, len, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (glnx_loop_write (tmpf.fd, buf, len) < 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "write");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!(flags & GLNX_FILE_REPLACE_NODATASYNC))
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
struct stat stbuf;
|
|
rpm-build |
c487f7 |
gboolean do_sync;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_fstatat_allow_noent (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
if (errno == ENOENT)
|
|
rpm-build |
c487f7 |
do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0;
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
do_sync = TRUE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (do_sync)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (fdatasync (tmpf.fd) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fdatasync");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (uid != (uid_t) -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (fchown (tmpf.fd, uid, gid) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fchown");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (fchmod (tmpf.fd, mode) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "fchmod");
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE,
|
|
rpm-build |
c487f7 |
dfd, subpath, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|