|
rpm-build |
0fba15 |
/*
|
|
rpm-build |
0fba15 |
* Copyright (C) 2014 Colin Walters <walters@verbum.org>
|
|
rpm-build |
0fba15 |
*
|
|
rpm-build |
0fba15 |
* SPDX-License-Identifier: LGPL-2.0+
|
|
rpm-build |
0fba15 |
*
|
|
rpm-build |
0fba15 |
* This library is free software; you can redistribute it and/or
|
|
rpm-build |
0fba15 |
* modify it under the terms of the GNU Lesser General Public
|
|
rpm-build |
0fba15 |
* License as published by the Free Software Foundation; either
|
|
rpm-build |
0fba15 |
* version 2 of the License, or (at your option) any later version.
|
|
rpm-build |
0fba15 |
*
|
|
rpm-build |
0fba15 |
* This library is distributed in the hope that it will be useful,
|
|
rpm-build |
0fba15 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
rpm-build |
0fba15 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
rpm-build |
0fba15 |
* Lesser General Public License for more details.
|
|
rpm-build |
0fba15 |
*
|
|
rpm-build |
0fba15 |
* You should have received a copy of the GNU Lesser General Public
|
|
rpm-build |
0fba15 |
* License along with this library; if not, write to the
|
|
rpm-build |
0fba15 |
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
rpm-build |
0fba15 |
* Boston, MA 02111-1307, USA.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
#include "config.h"
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
#include "ot-fs-utils.h"
|
|
rpm-build |
0fba15 |
#include "libglnx.h"
|
|
rpm-build |
0fba15 |
#include <sys/xattr.h>
|
|
rpm-build |
0fba15 |
#include <sys/mman.h>
|
|
rpm-build |
0fba15 |
#include <gio/gunixinputstream.h>
|
|
rpm-build |
0fba15 |
#include <gio/gunixoutputstream.h>
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Convert a fd-relative path to a GFile* - use
|
|
rpm-build |
0fba15 |
* for legacy code.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
GFile *
|
|
rpm-build |
0fba15 |
ot_fdrel_to_gfile (int dfd, const char *path)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
g_autofree char *abspath = glnx_fdrel_abspath (dfd, path);
|
|
rpm-build |
0fba15 |
return g_file_new_for_path (abspath);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Wraps readlinkat(), and sets the `symlink-target` property
|
|
rpm-build |
0fba15 |
* of @target_info.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
gboolean
|
|
rpm-build |
0fba15 |
ot_readlinkat_gfile_info (int dfd,
|
|
rpm-build |
0fba15 |
const char *path,
|
|
rpm-build |
0fba15 |
GFileInfo *target_info,
|
|
rpm-build |
0fba15 |
GCancellable *cancellable,
|
|
rpm-build |
0fba15 |
GError **error)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
char targetbuf[PATH_MAX+1];
|
|
rpm-build |
0fba15 |
ssize_t len;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (TEMP_FAILURE_RETRY (len = readlinkat (dfd, path, targetbuf, sizeof (targetbuf) - 1)) < 0)
|
|
rpm-build |
0fba15 |
return glnx_throw_errno_prefix (error, "readlinkat");
|
|
rpm-build |
0fba15 |
targetbuf[len] = '\0';
|
|
rpm-build |
0fba15 |
g_file_info_set_symlink_target (target_info, targetbuf);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/**
|
|
rpm-build |
0fba15 |
* ot_openat_read_stream:
|
|
rpm-build |
0fba15 |
* @dfd: Directory file descriptor
|
|
rpm-build |
0fba15 |
* @path: Subpath
|
|
rpm-build |
0fba15 |
* @follow: Whether or not to follow symbolic links
|
|
rpm-build |
0fba15 |
* @out_istream: (out): Return location for input stream
|
|
rpm-build |
0fba15 |
* @cancellable: Cancellable
|
|
rpm-build |
0fba15 |
* @error: Error
|
|
rpm-build |
0fba15 |
*
|
|
rpm-build |
0fba15 |
* Open a file for reading starting from @dfd for @path.
|
|
rpm-build |
0fba15 |
* The @follow parameter determines whether or not to follow
|
|
rpm-build |
0fba15 |
* if the last element of @path is a symbolic link. Intermediate
|
|
rpm-build |
0fba15 |
* symlink path components are always followed.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
gboolean
|
|
rpm-build |
0fba15 |
ot_openat_read_stream (int dfd,
|
|
rpm-build |
0fba15 |
const char *path,
|
|
rpm-build |
0fba15 |
gboolean follow,
|
|
rpm-build |
0fba15 |
GInputStream **out_istream,
|
|
rpm-build |
0fba15 |
GCancellable *cancellable,
|
|
rpm-build |
0fba15 |
GError **error)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
glnx_autofd int fd = -1;
|
|
rpm-build |
0fba15 |
if (!glnx_openat_rdonly (dfd, path, follow, &fd, error))
|
|
rpm-build |
0fba15 |
return FALSE;
|
|
rpm-build |
0fba15 |
*out_istream = g_unix_input_stream_new (glnx_steal_fd (&fd), TRUE);
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Like unlinkat() but ignore ENOENT */
|
|
rpm-build |
0fba15 |
gboolean
|
|
rpm-build |
0fba15 |
ot_ensure_unlinked_at (int dfd,
|
|
rpm-build |
0fba15 |
const char *path,
|
|
rpm-build |
0fba15 |
GError **error)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (unlinkat (dfd, path, 0) != 0)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (G_UNLIKELY (errno != ENOENT))
|
|
rpm-build |
0fba15 |
return glnx_throw_errno_prefix (error, "unlink(%s)", path);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
gboolean
|
|
rpm-build |
0fba15 |
ot_openat_ignore_enoent (int dfd,
|
|
rpm-build |
0fba15 |
const char *path,
|
|
rpm-build |
0fba15 |
int *out_fd,
|
|
rpm-build |
0fba15 |
GError **error)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
int target_fd = openat (dfd, path, O_CLOEXEC | O_RDONLY);
|
|
rpm-build |
0fba15 |
if (target_fd < 0)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (errno != ENOENT)
|
|
rpm-build |
0fba15 |
return glnx_throw_errno_prefix (error, "openat(%s)", path);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
*out_fd = target_fd;
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Like glnx_dirfd_iterator_init_at(), but if %ENOENT, then set
|
|
rpm-build |
0fba15 |
* @out_exists to %FALSE, and return successfully.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
gboolean
|
|
rpm-build |
0fba15 |
ot_dfd_iter_init_allow_noent (int dfd,
|
|
rpm-build |
0fba15 |
const char *path,
|
|
rpm-build |
0fba15 |
GLnxDirFdIterator *dfd_iter,
|
|
rpm-build |
0fba15 |
gboolean *out_exists,
|
|
rpm-build |
0fba15 |
GError **error)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
glnx_autofd int fd = glnx_opendirat_with_errno (dfd, path, TRUE);
|
|
rpm-build |
0fba15 |
if (fd < 0)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (errno != ENOENT)
|
|
rpm-build |
0fba15 |
return glnx_throw_errno_prefix (error, "opendirat");
|
|
rpm-build |
0fba15 |
*out_exists = FALSE;
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
if (!glnx_dirfd_iterator_init_take_fd (&fd, dfd_iter, error))
|
|
rpm-build |
0fba15 |
return FALSE;
|
|
rpm-build |
0fba15 |
*out_exists = TRUE;
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
typedef struct {
|
|
rpm-build |
0fba15 |
gpointer addr;
|
|
rpm-build |
0fba15 |
gsize len;
|
|
rpm-build |
0fba15 |
} MapData;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static void
|
|
rpm-build |
0fba15 |
map_data_destroy (gpointer data)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
MapData *mdata = data;
|
|
rpm-build |
0fba15 |
(void) munmap (mdata->addr, mdata->len);
|
|
rpm-build |
0fba15 |
g_free (mdata);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Return a newly-allocated GBytes that refers to the contents of the file
|
|
rpm-build |
0fba15 |
* starting at offset @start. If the file is large enough, mmap() may be used.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
GBytes *
|
|
rpm-build |
0fba15 |
ot_fd_readall_or_mmap (int fd,
|
|
rpm-build |
0fba15 |
goffset start,
|
|
rpm-build |
0fba15 |
GError **error)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
struct stat stbuf;
|
|
rpm-build |
0fba15 |
if (!glnx_fstat (fd, &stbuf, error))
|
|
rpm-build |
0fba15 |
return FALSE;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* http://stackoverflow.com/questions/258091/when-should-i-use-mmap-for-file-access */
|
|
rpm-build |
0fba15 |
if (start > stbuf.st_size)
|
|
rpm-build |
0fba15 |
return g_bytes_new_static (NULL, 0);
|
|
rpm-build |
0fba15 |
const gsize len = stbuf.st_size - start;
|
|
rpm-build |
0fba15 |
if (len > 16*1024)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
/* The reason we don't use g_mapped_file_new_from_fd() here
|
|
rpm-build |
0fba15 |
* is it doesn't support passing an offset, which is actually
|
|
rpm-build |
0fba15 |
* used by the static delta code.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
gpointer map = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, start);
|
|
rpm-build |
0fba15 |
if (map == (void*)-1)
|
|
rpm-build |
0fba15 |
return glnx_null_throw_errno_prefix (error, "mmap");
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
MapData *mdata = g_new (MapData, 1);
|
|
rpm-build |
0fba15 |
mdata->addr = map;
|
|
rpm-build |
0fba15 |
mdata->len = len;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return g_bytes_new_with_free_func (map, len, map_data_destroy, mdata);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Fall through to plain read into a malloc buffer */
|
|
rpm-build |
0fba15 |
if (lseek (fd, start, SEEK_SET) < 0)
|
|
rpm-build |
0fba15 |
return glnx_null_throw_errno_prefix (error, "lseek");
|
|
rpm-build |
0fba15 |
/* Not cancellable since this should be small */
|
|
rpm-build |
0fba15 |
return glnx_fd_readall_bytes (fd, NULL, error);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Given an input stream, splice it to an anonymous file (O_TMPFILE).
|
|
rpm-build |
0fba15 |
* Useful for potentially large but transient files.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
GBytes *
|
|
rpm-build |
0fba15 |
ot_map_anonymous_tmpfile_from_content (GInputStream *instream,
|
|
rpm-build |
0fba15 |
GCancellable *cancellable,
|
|
rpm-build |
0fba15 |
GError **error)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
g_auto(GLnxTmpfile) tmpf = { 0, };
|
|
rpm-build |
0fba15 |
if (!glnx_open_anonymous_tmpfile (O_RDWR | O_CLOEXEC, &tmpf, error))
|
|
rpm-build |
0fba15 |
return NULL;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
g_autoptr(GOutputStream) out = g_unix_output_stream_new (tmpf.fd, FALSE);
|
|
rpm-build |
0fba15 |
gssize n_bytes_written = g_output_stream_splice (out, instream,
|
|
rpm-build |
0fba15 |
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
|
|
rpm-build |
0fba15 |
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
|
|
rpm-build |
0fba15 |
cancellable, error);
|
|
rpm-build |
0fba15 |
if (n_bytes_written < 0)
|
|
rpm-build |
0fba15 |
return NULL;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
g_autoptr(GMappedFile) mfile = g_mapped_file_new_from_fd (tmpf.fd, FALSE, error);
|
|
rpm-build |
0fba15 |
if (!mfile)
|
|
rpm-build |
0fba15 |
return NULL;
|
|
rpm-build |
0fba15 |
return g_mapped_file_get_bytes (mfile);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
gboolean
|
|
rpm-build |
0fba15 |
ot_parse_file_by_line (const char *path,
|
|
rpm-build |
0fba15 |
gboolean (*cb)(const char*, void*, GError**),
|
|
rpm-build |
0fba15 |
void *cbdata,
|
|
rpm-build |
0fba15 |
GCancellable *cancellable,
|
|
rpm-build |
0fba15 |
GError **error)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
g_autofree char *contents =
|
|
rpm-build |
0fba15 |
glnx_file_get_contents_utf8_at (AT_FDCWD, path, NULL, cancellable, error);
|
|
rpm-build |
0fba15 |
if (!contents)
|
|
rpm-build |
0fba15 |
return FALSE;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
g_auto(GStrv) lines = g_strsplit (contents, "\n", -1);
|
|
rpm-build |
0fba15 |
for (char **iter = lines; iter && *iter; iter++)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
/* skip empty lines at least */
|
|
rpm-build |
0fba15 |
if (**iter == '\0')
|
|
rpm-build |
0fba15 |
continue;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (!cb (*iter, cbdata, error))
|
|
rpm-build |
0fba15 |
return FALSE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
}
|