|
rpm-build |
0fba15 |
/*
|
|
rpm-build |
0fba15 |
* Copyright (C) 2015,2016 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 |
#define FUSE_USE_VERSION 26
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
#include <sys/types.h>
|
|
rpm-build |
0fba15 |
#include <sys/stat.h>
|
|
rpm-build |
0fba15 |
#include <sys/statvfs.h>
|
|
rpm-build |
0fba15 |
#include <stdio.h>
|
|
rpm-build |
0fba15 |
#include <err.h>
|
|
rpm-build |
0fba15 |
#include <stdlib.h>
|
|
rpm-build |
0fba15 |
#include <string.h>
|
|
rpm-build |
0fba15 |
#include <errno.h>
|
|
rpm-build |
0fba15 |
#include <fcntl.h>
|
|
rpm-build |
0fba15 |
#include <sys/xattr.h>
|
|
rpm-build |
0fba15 |
#include <dirent.h>
|
|
rpm-build |
0fba15 |
#include <unistd.h>
|
|
rpm-build |
0fba15 |
#include <fuse.h>
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
#include <glib.h>
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
#include "libglnx.h"
|
|
rpm-build |
0fba15 |
#include "ostree.h"
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
// Global to store our read-write path
|
|
rpm-build |
0fba15 |
static int basefd = -1;
|
|
rpm-build |
0fba15 |
/* Whether or not to automatically "copyup" (in overlayfs terms).
|
|
rpm-build |
0fba15 |
* What we're really doing is breaking hardlinks.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
static gboolean opt_copyup;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static inline const char *
|
|
rpm-build |
0fba15 |
ENSURE_RELPATH (const char *path)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
path = path + strspn (path, "/");
|
|
rpm-build |
0fba15 |
if (*path == 0)
|
|
rpm-build |
0fba15 |
return ".";
|
|
rpm-build |
0fba15 |
return path;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_getattr (const char *path, struct stat *st_data)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
if (!*path)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (fstat (basefd, st_data) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
else
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (fstatat (basefd, path, st_data, AT_SYMLINK_NOFOLLOW) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_readlink (const char *path, char *buf, size_t size)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
int r;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Note FUSE wants the string to be always nul-terminated, even if
|
|
rpm-build |
0fba15 |
* truncated.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
r = readlinkat (basefd, path, buf, size - 1);
|
|
rpm-build |
0fba15 |
if (r == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
buf[r] = '\0';
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_readdir (const char *path, void *buf, fuse_fill_dir_t filler,
|
|
rpm-build |
0fba15 |
off_t offset, struct fuse_file_info *fi)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
DIR *dp;
|
|
rpm-build |
0fba15 |
struct dirent *de;
|
|
rpm-build |
0fba15 |
int dfd;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (!*path)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
dfd = fcntl (basefd, F_DUPFD_CLOEXEC, 3);
|
|
rpm-build |
0fba15 |
if (dfd < 0)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
lseek (dfd, 0, SEEK_SET);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
else
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
dfd = openat (basefd, path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
|
|
rpm-build |
0fba15 |
if (dfd == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Transfers ownership of fd */
|
|
rpm-build |
0fba15 |
dp = fdopendir (dfd);
|
|
rpm-build |
0fba15 |
if (dp == NULL)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
while ((de = readdir (dp)) != NULL)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
struct stat st;
|
|
rpm-build |
0fba15 |
memset (&st, 0, sizeof (st));
|
|
rpm-build |
0fba15 |
st.st_ino = de->d_ino;
|
|
rpm-build |
0fba15 |
st.st_mode = de->d_type << 12;
|
|
rpm-build |
0fba15 |
if (filler (buf, de->d_name, &st, 0))
|
|
rpm-build |
0fba15 |
break;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
(void) closedir (dp);
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_mknod (const char *path, mode_t mode, dev_t rdev)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
return -EROFS;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_mkdir (const char *path, mode_t mode)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
if (mkdirat (basefd, path, mode) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_unlink (const char *path)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
if (unlinkat (basefd, path, 0) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_rmdir (const char *path)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
if (unlinkat (basefd, path, AT_REMOVEDIR) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_symlink (const char *from, const char *to)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
struct stat stbuf;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
to = ENSURE_RELPATH (to);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (symlinkat (from, basefd, to) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (fstatat (basefd, to, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
fprintf (stderr, "Failed to find newly created symlink '%s': %s\n",
|
|
rpm-build |
0fba15 |
to, g_strerror (errno));
|
|
rpm-build |
0fba15 |
exit (EXIT_FAILURE);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_rename (const char *from, const char *to)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
from = ENSURE_RELPATH (from);
|
|
rpm-build |
0fba15 |
to = ENSURE_RELPATH (to);
|
|
rpm-build |
0fba15 |
if (renameat (basefd, from, basefd, to) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_link (const char *from, const char *to)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
from = ENSURE_RELPATH (from);
|
|
rpm-build |
0fba15 |
to = ENSURE_RELPATH (to);
|
|
rpm-build |
0fba15 |
if (linkat (basefd, from, basefd, to, 0) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Check whether @stbuf refers to a hardlinked regfile or symlink, and if so
|
|
rpm-build |
0fba15 |
* return -EROFS. Otherwise return 0.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
static gboolean
|
|
rpm-build |
0fba15 |
can_write_stbuf (const struct stat *stbuf)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
/* If it's not a regular file or symlink, ostree won't hardlink it, so allow
|
|
rpm-build |
0fba15 |
* writes - it might be a FIFO or device that somehow
|
|
rpm-build |
0fba15 |
* ended up underneath our mount.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
if (!(S_ISREG (stbuf->st_mode) || S_ISLNK (stbuf->st_mode)))
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
/* If the object isn't hardlinked, it's OK to write */
|
|
rpm-build |
0fba15 |
if (stbuf->st_nlink <= 1)
|
|
rpm-build |
0fba15 |
return TRUE;
|
|
rpm-build |
0fba15 |
/* Otherwise, it's a hardlinked file or symlink; it must be
|
|
rpm-build |
0fba15 |
* immutable.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
return FALSE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
gioerror_to_errno (GIOErrorEnum e)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
/* It's obviously crappy to have to do this but
|
|
rpm-build |
0fba15 |
* we also don't want to try to have "raw errno" versions
|
|
rpm-build |
0fba15 |
* of everything down in ostree_break_hardlink() so...
|
|
rpm-build |
0fba15 |
* let's just reverse map a few ones I think are going to be common.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
switch (e)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
case G_IO_ERROR_NOT_FOUND:
|
|
rpm-build |
0fba15 |
return ENOENT;
|
|
rpm-build |
0fba15 |
case G_IO_ERROR_IS_DIRECTORY:
|
|
rpm-build |
0fba15 |
return EISDIR;
|
|
rpm-build |
0fba15 |
case G_IO_ERROR_PERMISSION_DENIED:
|
|
rpm-build |
0fba15 |
return EPERM;
|
|
rpm-build |
0fba15 |
case G_IO_ERROR_NO_SPACE:
|
|
rpm-build |
0fba15 |
return ENOSPC;
|
|
rpm-build |
0fba15 |
default:
|
|
rpm-build |
0fba15 |
return EIO;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
verify_write_or_copyup (const char *path, const struct stat *stbuf,
|
|
rpm-build |
0fba15 |
gboolean *out_did_copyup)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
struct stat stbuf_local;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (out_did_copyup)
|
|
rpm-build |
0fba15 |
*out_did_copyup = FALSE;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* If a stbuf wasn't provided, gather it now */
|
|
rpm-build |
0fba15 |
if (!stbuf)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (fstatat (basefd, path, &stbuf_local, AT_SYMLINK_NOFOLLOW) == -1)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (errno == ENOENT)
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
else
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
stbuf = &stbuf_local;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Verify writability, if that fails, perform copy-up if enabled */
|
|
rpm-build |
0fba15 |
if (!can_write_stbuf (stbuf))
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (opt_copyup)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
g_autoptr(GError) tmp_error = NULL;
|
|
rpm-build |
0fba15 |
if (!ostree_break_hardlink (basefd, path, FALSE, NULL, &tmp_error))
|
|
rpm-build |
0fba15 |
return -gioerror_to_errno ((GIOErrorEnum)tmp_error->code);
|
|
rpm-build |
0fba15 |
if (out_did_copyup)
|
|
rpm-build |
0fba15 |
*out_did_copyup = TRUE;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
else
|
|
rpm-build |
0fba15 |
return -EROFS;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Given a path (which is absolute), convert it
|
|
rpm-build |
0fba15 |
* to a relative path (even for the caller) and
|
|
rpm-build |
0fba15 |
* perform either write verification or copy-up.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
#define PATH_WRITE_ENTRYPOINT(path) do { \
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path); \
|
|
rpm-build |
0fba15 |
int r = verify_write_or_copyup (path, NULL, NULL); \
|
|
rpm-build |
0fba15 |
if (r != 0) \
|
|
rpm-build |
0fba15 |
return r; \
|
|
rpm-build |
0fba15 |
} while (0)
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_chmod (const char *path, mode_t mode)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
PATH_WRITE_ENTRYPOINT (path);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Note we can't use AT_SYMLINK_NOFOLLOW yet;
|
|
rpm-build |
0fba15 |
* https://marc.info/?l=linux-kernel&m=148830147803162&w=2
|
|
rpm-build |
0fba15 |
* https://marc.info/?l=linux-fsdevel&m=149193779929561&w=2
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
if (fchmodat (basefd, path, mode, 0) != 0)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_chown (const char *path, uid_t uid, gid_t gid)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
PATH_WRITE_ENTRYPOINT (path);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (fchownat (basefd, path, uid, gid, AT_SYMLINK_NOFOLLOW) != 0)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_truncate (const char *path, off_t size)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
PATH_WRITE_ENTRYPOINT (path);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
glnx_autofd int fd = openat (basefd, path, O_NOFOLLOW|O_WRONLY);
|
|
rpm-build |
0fba15 |
if (fd == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (ftruncate (fd, size) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_utimens (const char *path, const struct timespec tv[2])
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
/* This one isn't write-verified, we support changing times
|
|
rpm-build |
0fba15 |
* even for hardlinked files.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (utimensat (basefd, path, tv, AT_SYMLINK_NOFOLLOW) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
do_open (const char *path, mode_t mode, struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
int fd;
|
|
rpm-build |
0fba15 |
struct stat stbuf;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if ((finfo->flags & O_ACCMODE) == O_RDONLY)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
/* Read */
|
|
rpm-build |
0fba15 |
fd = openat (basefd, path, finfo->flags, mode);
|
|
rpm-build |
0fba15 |
if (fd == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
else
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
/* Write */
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* We need to specially handle O_TRUNC */
|
|
rpm-build |
0fba15 |
fd = openat (basefd, path, finfo->flags & ~O_TRUNC, mode);
|
|
rpm-build |
0fba15 |
if (fd == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
if (fstat (fd, &stbuf) == -1)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
(void) close (fd);
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
gboolean did_copyup;
|
|
rpm-build |
0fba15 |
int r = verify_write_or_copyup (path, &stbuf, &did_copyup);
|
|
rpm-build |
0fba15 |
if (r != 0)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
(void) close (fd);
|
|
rpm-build |
0fba15 |
return r;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* In the copyup case, we need to re-open */
|
|
rpm-build |
0fba15 |
if (did_copyup)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
(void) close (fd);
|
|
rpm-build |
0fba15 |
/* Note that unlike the initial open, we will pass through
|
|
rpm-build |
0fba15 |
* O_TRUNC. More ideally in this copyup case we'd avoid copying
|
|
rpm-build |
0fba15 |
* the whole file in the first place, but eh. It's not like we're
|
|
rpm-build |
0fba15 |
* high performance anyways.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
fd = openat (basefd, path, finfo->flags & ~(O_EXCL|O_CREAT), mode);
|
|
rpm-build |
0fba15 |
if (fd == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
else
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
/* In the non-copyup case we handle O_TRUNC here, after we've verified
|
|
rpm-build |
0fba15 |
* the hardlink state above with verify_write_or_copyup().
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
if (finfo->flags & O_TRUNC)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (ftruncate (fd, 0) == -1)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
(void) close (fd);
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
finfo->fh = fd;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_open (const char *path, struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
return do_open (path, 0, finfo);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_create(const char *path, mode_t mode, struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
return do_open (path, mode, finfo);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_read_buf (const char *path, struct fuse_bufvec **bufp,
|
|
rpm-build |
0fba15 |
size_t size, off_t offset, struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
struct fuse_bufvec *src;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
src = malloc (sizeof (struct fuse_bufvec));
|
|
rpm-build |
0fba15 |
if (src == NULL)
|
|
rpm-build |
0fba15 |
return -ENOMEM;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
*src = FUSE_BUFVEC_INIT (size);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
|
|
rpm-build |
0fba15 |
src->buf[0].fd = finfo->fh;
|
|
rpm-build |
0fba15 |
src->buf[0].pos = offset;
|
|
rpm-build |
0fba15 |
*bufp = src;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_read (const char *path, char *buf, size_t size, off_t offset,
|
|
rpm-build |
0fba15 |
struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
int r;
|
|
rpm-build |
0fba15 |
r = pread (finfo->fh, buf, size, offset);
|
|
rpm-build |
0fba15 |
if (r == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return r;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_write_buf (const char *path, struct fuse_bufvec *buf, off_t offset,
|
|
rpm-build |
0fba15 |
struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
struct fuse_bufvec dst = FUSE_BUFVEC_INIT (fuse_buf_size (buf));
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
|
|
rpm-build |
0fba15 |
dst.buf[0].fd = finfo->fh;
|
|
rpm-build |
0fba15 |
dst.buf[0].pos = offset;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return fuse_buf_copy (&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_write (const char *path, const char *buf, size_t size, off_t offset,
|
|
rpm-build |
0fba15 |
struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
int r;
|
|
rpm-build |
0fba15 |
r = pwrite (finfo->fh, buf, size, offset);
|
|
rpm-build |
0fba15 |
if (r == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return r;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_statfs (const char *path, struct statvfs *st_buf)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (fstatvfs (basefd, st_buf) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_release (const char *path, struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
(void) close (finfo->fh);
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_fsync (const char *path, int crap, struct fuse_file_info *finfo)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
if (fsync (finfo->fh) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_access (const char *path, int mode)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
path = ENSURE_RELPATH (path);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Apparently at least GNU coreutils rm calls `faccessat(W_OK)`
|
|
rpm-build |
0fba15 |
* before trying to do an unlink. So...we'll just lie about
|
|
rpm-build |
0fba15 |
* writable access here.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
if (faccessat (basefd, path, mode, AT_SYMLINK_NOFOLLOW) == -1)
|
|
rpm-build |
0fba15 |
return -errno;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_setxattr (const char *path, const char *name, const char *value,
|
|
rpm-build |
0fba15 |
size_t size, int flags)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
return -ENOTSUP;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_getxattr (const char *path, const char *name, char *value,
|
|
rpm-build |
0fba15 |
size_t size)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
return -ENOTSUP;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/*
|
|
rpm-build |
0fba15 |
* List the supported extended attributes.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_listxattr (const char *path, char *list, size_t size)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
return -ENOTSUP;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/*
|
|
rpm-build |
0fba15 |
* Remove an extended attribute.
|
|
rpm-build |
0fba15 |
*/
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
callback_removexattr (const char *path, const char *name)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
return -ENOTSUP;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
struct fuse_operations callback_oper = {
|
|
rpm-build |
0fba15 |
.getattr = callback_getattr,
|
|
rpm-build |
0fba15 |
.readlink = callback_readlink,
|
|
rpm-build |
0fba15 |
.readdir = callback_readdir,
|
|
rpm-build |
0fba15 |
.mknod = callback_mknod,
|
|
rpm-build |
0fba15 |
.mkdir = callback_mkdir,
|
|
rpm-build |
0fba15 |
.symlink = callback_symlink,
|
|
rpm-build |
0fba15 |
.unlink = callback_unlink,
|
|
rpm-build |
0fba15 |
.rmdir = callback_rmdir,
|
|
rpm-build |
0fba15 |
.rename = callback_rename,
|
|
rpm-build |
0fba15 |
.link = callback_link,
|
|
rpm-build |
0fba15 |
.chmod = callback_chmod,
|
|
rpm-build |
0fba15 |
.chown = callback_chown,
|
|
rpm-build |
0fba15 |
.truncate = callback_truncate,
|
|
rpm-build |
0fba15 |
.utimens = callback_utimens,
|
|
rpm-build |
0fba15 |
.create = callback_create,
|
|
rpm-build |
0fba15 |
.open = callback_open,
|
|
rpm-build |
0fba15 |
.read_buf = callback_read_buf,
|
|
rpm-build |
0fba15 |
.read = callback_read,
|
|
rpm-build |
0fba15 |
.write_buf = callback_write_buf,
|
|
rpm-build |
0fba15 |
.write = callback_write,
|
|
rpm-build |
0fba15 |
.statfs = callback_statfs,
|
|
rpm-build |
0fba15 |
.release = callback_release,
|
|
rpm-build |
0fba15 |
.fsync = callback_fsync,
|
|
rpm-build |
0fba15 |
.access = callback_access,
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
/* Extended attributes support for userland interaction */
|
|
rpm-build |
0fba15 |
.setxattr = callback_setxattr,
|
|
rpm-build |
0fba15 |
.getxattr = callback_getxattr,
|
|
rpm-build |
0fba15 |
.listxattr = callback_listxattr,
|
|
rpm-build |
0fba15 |
.removexattr = callback_removexattr
|
|
rpm-build |
0fba15 |
};
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
enum {
|
|
rpm-build |
0fba15 |
KEY_HELP,
|
|
rpm-build |
0fba15 |
KEY_VERSION,
|
|
rpm-build |
0fba15 |
KEY_COPYUP,
|
|
rpm-build |
0fba15 |
};
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static void
|
|
rpm-build |
0fba15 |
usage (const char *progname)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
fprintf (stdout,
|
|
rpm-build |
0fba15 |
"usage: %s basepath mountpoint [options]\n"
|
|
rpm-build |
0fba15 |
"\n"
|
|
rpm-build |
0fba15 |
" Makes basepath visible at mountpoint such that files are read-only, directories are writable\n"
|
|
rpm-build |
0fba15 |
"\n"
|
|
rpm-build |
0fba15 |
"general options:\n"
|
|
rpm-build |
0fba15 |
" -o opt,[opt...] mount options\n"
|
|
rpm-build |
0fba15 |
" -h --help print help\n"
|
|
rpm-build |
0fba15 |
"\n", progname);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static int
|
|
rpm-build |
0fba15 |
rofs_parse_opt (void *data, const char *arg, int key,
|
|
rpm-build |
0fba15 |
struct fuse_args *outargs)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
(void) data;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
switch (key)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
case FUSE_OPT_KEY_NONOPT:
|
|
rpm-build |
0fba15 |
if (basefd == -1)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
basefd = openat (AT_FDCWD, arg, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
|
|
rpm-build |
0fba15 |
if (basefd == -1)
|
|
rpm-build |
0fba15 |
err (1, "opening rootfs %s", arg);
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
else
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
return 1;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
case FUSE_OPT_KEY_OPT:
|
|
rpm-build |
0fba15 |
return 1;
|
|
rpm-build |
0fba15 |
case KEY_HELP:
|
|
rpm-build |
0fba15 |
usage (outargs->argv[0]);
|
|
rpm-build |
0fba15 |
exit (EXIT_SUCCESS);
|
|
rpm-build |
0fba15 |
case KEY_COPYUP:
|
|
rpm-build |
0fba15 |
opt_copyup = TRUE;
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
default:
|
|
rpm-build |
0fba15 |
fprintf (stderr, "see `%s -h' for usage\n", outargs->argv[0]);
|
|
rpm-build |
0fba15 |
exit (EXIT_FAILURE);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
return 1;
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
static struct fuse_opt rofs_opts[] = {
|
|
rpm-build |
0fba15 |
FUSE_OPT_KEY ("-h", KEY_HELP),
|
|
rpm-build |
0fba15 |
FUSE_OPT_KEY ("--help", KEY_HELP),
|
|
rpm-build |
0fba15 |
FUSE_OPT_KEY ("-V", KEY_VERSION),
|
|
rpm-build |
0fba15 |
FUSE_OPT_KEY ("--version", KEY_VERSION),
|
|
rpm-build |
0fba15 |
FUSE_OPT_KEY ("--copyup", KEY_COPYUP),
|
|
rpm-build |
0fba15 |
FUSE_OPT_END
|
|
rpm-build |
0fba15 |
};
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
int
|
|
rpm-build |
0fba15 |
main (int argc, char *argv[])
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
struct fuse_args args = FUSE_ARGS_INIT (argc, argv);
|
|
rpm-build |
0fba15 |
int res;
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
res = fuse_opt_parse (&args, &basefd, rofs_opts, rofs_parse_opt);
|
|
rpm-build |
0fba15 |
if (res != 0)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
fprintf (stderr, "Invalid arguments\n");
|
|
rpm-build |
0fba15 |
fprintf (stderr, "see `%s -h' for usage\n", argv[0]);
|
|
rpm-build |
0fba15 |
exit (EXIT_FAILURE);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
if (basefd == -1)
|
|
rpm-build |
0fba15 |
{
|
|
rpm-build |
0fba15 |
fprintf (stderr, "Missing basepath\n");
|
|
rpm-build |
0fba15 |
fprintf (stderr, "see `%s -h' for usage\n", argv[0]);
|
|
rpm-build |
0fba15 |
exit (EXIT_FAILURE);
|
|
rpm-build |
0fba15 |
}
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
fuse_main (args.argc, args.argv, &callback_oper, NULL);
|
|
rpm-build |
0fba15 |
|
|
rpm-build |
0fba15 |
return 0;
|
|
rpm-build |
0fba15 |
}
|