253 lines
6.4 KiB
C
253 lines
6.4 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
|
*
|
|
* Copyright (C) 2014 Colin Walters <walters@verbum.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "ot-fs-utils.h"
|
|
#include "libglnx.h"
|
|
#include <sys/xattr.h>
|
|
#include <gio/gunixinputstream.h>
|
|
|
|
int
|
|
ot_opendirat (int dfd, const char *path, gboolean follow)
|
|
{
|
|
int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY;
|
|
if (!follow)
|
|
flags |= O_NOFOLLOW;
|
|
return openat (dfd, path, flags);
|
|
}
|
|
|
|
gboolean
|
|
ot_gopendirat (int dfd,
|
|
const char *path,
|
|
gboolean follow,
|
|
int *out_fd,
|
|
GError **error)
|
|
{
|
|
int ret = ot_opendirat (dfd, path, follow);
|
|
if (ret == -1)
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
return FALSE;
|
|
}
|
|
*out_fd = ret;
|
|
return TRUE;
|
|
}
|
|
|
|
GBytes *
|
|
ot_lgetxattrat (int dfd,
|
|
const char *path,
|
|
const char *attribute,
|
|
GError **error)
|
|
{
|
|
/* A workaround for the lack of lgetxattrat(), thanks to Florian Weimer:
|
|
* https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html
|
|
*/
|
|
g_autofree char *full_path = g_strdup_printf ("/proc/self/fd/%d/%s", dfd, path);
|
|
GBytes *bytes = NULL;
|
|
ssize_t bytes_read, real_size;
|
|
char *buf;
|
|
|
|
do
|
|
bytes_read = lgetxattr (full_path, attribute, NULL, 0);
|
|
while (G_UNLIKELY (bytes_read < 0 && errno == EINTR));
|
|
if (G_UNLIKELY (bytes_read < 0))
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
goto out;
|
|
}
|
|
|
|
buf = g_malloc (bytes_read);
|
|
do
|
|
real_size = lgetxattr (full_path, attribute, buf, bytes_read);
|
|
while (G_UNLIKELY (real_size < 0 && errno == EINTR));
|
|
if (G_UNLIKELY (real_size < 0))
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
g_free (buf);
|
|
goto out;
|
|
}
|
|
|
|
bytes = g_bytes_new_take (buf, real_size);
|
|
out:
|
|
return bytes;
|
|
}
|
|
|
|
gboolean
|
|
ot_lsetxattrat (int dfd,
|
|
const char *path,
|
|
const char *attribute,
|
|
const void *value,
|
|
gsize value_size,
|
|
int flags,
|
|
GError **error)
|
|
{
|
|
/* A workaround for the lack of lsetxattrat(), thanks to Florian Weimer:
|
|
* https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html
|
|
*/
|
|
g_autofree char *full_path = g_strdup_printf ("/proc/self/fd/%d/%s", dfd, path);
|
|
int res;
|
|
|
|
do
|
|
res = lsetxattr (full_path, "user.ostreemeta", value, value_size, flags);
|
|
while (G_UNLIKELY (res == -1 && errno == EINTR));
|
|
if (G_UNLIKELY (res == -1))
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
ot_readlinkat_gfile_info (int dfd,
|
|
const char *path,
|
|
GFileInfo *target_info,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
gboolean ret = FALSE;
|
|
char targetbuf[PATH_MAX+1];
|
|
ssize_t len;
|
|
|
|
do
|
|
len = readlinkat (dfd, path, targetbuf, sizeof (targetbuf) - 1);
|
|
while (G_UNLIKELY (len == -1 && errno == EINTR));
|
|
if (len == -1)
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
goto out;
|
|
}
|
|
targetbuf[len] = '\0';
|
|
g_file_info_set_symlink_target (target_info, targetbuf);
|
|
|
|
ret = TRUE;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* ot_openat_read_stream:
|
|
* @dfd: Directory file descriptor
|
|
* @path: Subpath
|
|
* @follow: Whether or not to follow symbolic links
|
|
* @out_istream: (out): Return location for input stream
|
|
* @cancellable: Cancellable
|
|
* @error: Error
|
|
*
|
|
* Open a file for reading starting from @dfd for @path.
|
|
* The @follow parameter determines whether or not to follow
|
|
* if the last element of @path is a symbolic link. Intermediate
|
|
* symlink path components are always followed.
|
|
*/
|
|
gboolean
|
|
ot_openat_read_stream (int dfd,
|
|
const char *path,
|
|
gboolean follow,
|
|
GInputStream **out_istream,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
gboolean ret = FALSE;
|
|
int fd = -1;
|
|
int flags = O_RDONLY | O_NOCTTY | O_CLOEXEC;
|
|
|
|
if (!follow)
|
|
flags |= O_NOFOLLOW;
|
|
|
|
do
|
|
fd = openat (dfd, path, flags, 0);
|
|
while (G_UNLIKELY (fd == -1 && errno == EINTR));
|
|
if (fd == -1)
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
goto out;
|
|
}
|
|
|
|
*out_istream = g_unix_input_stream_new (fd, TRUE);
|
|
ret = TRUE;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
ot_ensure_unlinked_at (int dfd,
|
|
const char *path,
|
|
GError **error)
|
|
{
|
|
if (unlinkat (dfd, path, 0) != 0)
|
|
{
|
|
if (G_UNLIKELY (errno != ENOENT))
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
ot_openat_ignore_enoent (int dfd,
|
|
const char *path,
|
|
int *out_fd,
|
|
GError **error)
|
|
{
|
|
gboolean ret = FALSE;
|
|
int target_fd = -1;
|
|
|
|
target_fd = openat (dfd, path, O_CLOEXEC | O_RDONLY);
|
|
if (target_fd < 0)
|
|
{
|
|
if (errno != ENOENT)
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ret = TRUE;
|
|
*out_fd = target_fd;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
GBytes *
|
|
ot_file_mapat_bytes (int dfd,
|
|
const char *path,
|
|
GError **error)
|
|
{
|
|
glnx_fd_close int fd = openat (dfd, path, O_RDONLY | O_CLOEXEC);
|
|
g_autoptr(GMappedFile) mfile = NULL;
|
|
|
|
if (fd < 0)
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
return FALSE;
|
|
}
|
|
|
|
mfile = g_mapped_file_new_from_fd (fd, FALSE, error);
|
|
if (!mfile)
|
|
return FALSE;
|
|
|
|
return g_mapped_file_get_bytes (mfile);
|
|
}
|