mirror of
https://github.com/flatpak/flatpak.git
synced 2025-12-31 11:58:03 -05:00
revokefs: Split out the writing part from the fuse implementation
By default we just spawn a writer from the fuse implementation, but you can also do it manually with --backend and --socket, allowing you to start the two in different contexts. Closes: #2657 Approved by: alexlarsson
This commit is contained in:
committed by
Atomic Bot
parent
4716ce8f2e
commit
aeecbb7d2b
@@ -20,7 +20,7 @@
|
||||
|
||||
libexec_PROGRAMS += revokefs-fuse
|
||||
|
||||
revokefs_fuse_SOURCES = revokefs/main.c
|
||||
revokefs_fuse_SOURCES = revokefs/main.c revokefs/writer.c revokefs/writer.h
|
||||
|
||||
revokefs_fuse_CFLAGS = $(BASE_CFLAGS) -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 $(FUSE_CFLAGS) -I$(srcdir)/libglnx
|
||||
revokefs_fuse_LDADD = libglnx.la $(BASE_LIBS) $(FUSE_LIBS)
|
||||
|
||||
275
revokefs/main.c
275
revokefs/main.c
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 Colin Walters <walters@verbum.org>
|
||||
* Copyright (C) 2018 Alexander Larsson <alexl@redhat.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
@@ -37,10 +39,16 @@
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "writer.h"
|
||||
#include "libglnx.h"
|
||||
|
||||
/* fh >= REMOTE_FD_OFFSET means the fd is in the writer side, otherwise it is local */
|
||||
#define REMOTE_FD_OFFSET ((guint64)G_MAXUINT32)
|
||||
|
||||
// Global to store our read-write path
|
||||
static char *base_path = NULL;
|
||||
static int basefd = -1;
|
||||
static int writer_socket = -1;
|
||||
|
||||
static inline const char *
|
||||
ENSURE_RELPATH (const char *path)
|
||||
@@ -138,38 +146,34 @@ static int
|
||||
callback_mkdir (const char *path, mode_t mode)
|
||||
{
|
||||
path = ENSURE_RELPATH (path);
|
||||
if (mkdirat (basefd, path, mode) == -1)
|
||||
return -errno;
|
||||
return 0;
|
||||
return request_mkdir (writer_socket, path, mode);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_unlink (const char *path)
|
||||
{
|
||||
path = ENSURE_RELPATH (path);
|
||||
if (unlinkat (basefd, path, 0) == -1)
|
||||
return -errno;
|
||||
return 0;
|
||||
return request_unlink (writer_socket, path);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_rmdir (const char *path)
|
||||
{
|
||||
path = ENSURE_RELPATH (path);
|
||||
if (unlinkat (basefd, path, AT_REMOVEDIR) == -1)
|
||||
return -errno;
|
||||
return 0;
|
||||
return request_rmdir (writer_socket, path);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_symlink (const char *from, const char *to)
|
||||
{
|
||||
struct stat stbuf;
|
||||
int res;
|
||||
|
||||
to = ENSURE_RELPATH (to);
|
||||
|
||||
if (symlinkat (from, basefd, to) == -1)
|
||||
return -errno;
|
||||
res = request_symlink (writer_socket, from, to);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
if (fstatat (basefd, to, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
|
||||
{
|
||||
@@ -185,9 +189,8 @@ callback_rename (const char *from, const char *to)
|
||||
{
|
||||
from = ENSURE_RELPATH (from);
|
||||
to = ENSURE_RELPATH (to);
|
||||
if (renameat (basefd, from, basefd, to) == -1)
|
||||
return -errno;
|
||||
return 0;
|
||||
|
||||
return request_rename (writer_socket, from, to);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -195,56 +198,37 @@ callback_link (const char *from, const char *to)
|
||||
{
|
||||
from = ENSURE_RELPATH (from);
|
||||
to = ENSURE_RELPATH (to);
|
||||
if (linkat (basefd, from, basefd, to, 0) == -1)
|
||||
return -errno;
|
||||
return 0;
|
||||
|
||||
return request_link (writer_socket, from, to);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_chmod (const char *path, mode_t mode)
|
||||
{
|
||||
/* Note we can't use AT_SYMLINK_NOFOLLOW yet;
|
||||
* https://marc.info/?l=linux-kernel&m=148830147803162&w=2
|
||||
* https://marc.info/?l=linux-fsdevel&m=149193779929561&w=2
|
||||
*/
|
||||
if (fchmodat (basefd, path, mode, 0) != 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
path = ENSURE_RELPATH (path);
|
||||
return request_chmod (writer_socket, path, mode);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_chown (const char *path, uid_t uid, gid_t gid)
|
||||
{
|
||||
if (fchownat (basefd, path, uid, gid, AT_SYMLINK_NOFOLLOW) != 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
path = ENSURE_RELPATH (path);
|
||||
return request_chown (writer_socket, path, uid, gid);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_truncate (const char *path, off_t size)
|
||||
{
|
||||
glnx_autofd int fd = openat (basefd, path, O_NOFOLLOW|O_WRONLY);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
if (ftruncate (fd, size) == -1)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
path = ENSURE_RELPATH (path);
|
||||
return request_truncate (writer_socket, path, size);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_utimens (const char *path, const struct timespec tv[2])
|
||||
{
|
||||
/* This one isn't write-verified, we support changing times
|
||||
* even for hardlinked files.
|
||||
*/
|
||||
path = ENSURE_RELPATH (path);
|
||||
|
||||
if (utimensat (basefd, path, tv, AT_SYMLINK_NOFOLLOW) == -1)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
return request_utimens (writer_socket, path, tv);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -260,28 +244,20 @@ do_open (const char *path, mode_t mode, struct fuse_file_info *finfo)
|
||||
fd = openat (basefd, path, finfo->flags, mode);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
finfo->fh = fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Write */
|
||||
|
||||
/* We need to specially handle O_TRUNC */
|
||||
fd = openat (basefd, path, finfo->flags & ~O_TRUNC, mode);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
fd = request_open (writer_socket, path, mode, finfo->flags);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (finfo->flags & O_TRUNC)
|
||||
{
|
||||
if (ftruncate (fd, 0) == -1)
|
||||
{
|
||||
(void) close (fd);
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
finfo->fh = fd + REMOTE_FD_OFFSET;
|
||||
}
|
||||
|
||||
finfo->fh = fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -297,48 +273,22 @@ callback_create(const char *path, mode_t mode, struct fuse_file_info *finfo)
|
||||
return do_open (path, mode, finfo);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_read_buf (const char *path, struct fuse_bufvec **bufp,
|
||||
size_t size, off_t offset, struct fuse_file_info *finfo)
|
||||
{
|
||||
struct fuse_bufvec *src;
|
||||
|
||||
src = malloc (sizeof (struct fuse_bufvec));
|
||||
if (src == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
*src = FUSE_BUFVEC_INIT (size);
|
||||
|
||||
src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
|
||||
src->buf[0].fd = finfo->fh;
|
||||
src->buf[0].pos = offset;
|
||||
*bufp = src;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
callback_read (const char *path, char *buf, size_t size, off_t offset,
|
||||
struct fuse_file_info *finfo)
|
||||
{
|
||||
int r;
|
||||
r = pread (finfo->fh, buf, size, offset);
|
||||
if (r == -1)
|
||||
return -errno;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
callback_write_buf (const char *path, struct fuse_bufvec *buf, off_t offset,
|
||||
struct fuse_file_info *finfo)
|
||||
{
|
||||
struct fuse_bufvec dst = FUSE_BUFVEC_INIT (fuse_buf_size (buf));
|
||||
|
||||
dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
|
||||
dst.buf[0].fd = finfo->fh;
|
||||
dst.buf[0].pos = offset;
|
||||
|
||||
return fuse_buf_copy (&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
|
||||
if (finfo->fh >= REMOTE_FD_OFFSET)
|
||||
{
|
||||
return request_read (writer_socket, finfo->fh - REMOTE_FD_OFFSET, buf, size, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = pread (finfo->fh, buf, size, offset);
|
||||
if (r == -1)
|
||||
return -errno;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -346,10 +296,18 @@ callback_write (const char *path, const char *buf, size_t size, off_t offset,
|
||||
struct fuse_file_info *finfo)
|
||||
{
|
||||
int r;
|
||||
r = pwrite (finfo->fh, buf, size, offset);
|
||||
if (r == -1)
|
||||
return -errno;
|
||||
return r;
|
||||
|
||||
if (finfo->fh >= REMOTE_FD_OFFSET)
|
||||
{
|
||||
return request_write (writer_socket, finfo->fh - REMOTE_FD_OFFSET, buf, size, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = pwrite (finfo->fh, buf, size, offset);
|
||||
if (r == -1)
|
||||
return -errno;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -363,16 +321,30 @@ callback_statfs (const char *path, struct statvfs *st_buf)
|
||||
static int
|
||||
callback_release (const char *path, struct fuse_file_info *finfo)
|
||||
{
|
||||
(void) close (finfo->fh);
|
||||
return 0;
|
||||
if (finfo->fh >= REMOTE_FD_OFFSET)
|
||||
{
|
||||
return request_close (writer_socket, finfo->fh - REMOTE_FD_OFFSET);
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) close (finfo->fh);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
callback_fsync (const char *path, int crap, struct fuse_file_info *finfo)
|
||||
{
|
||||
if (fsync (finfo->fh) == -1)
|
||||
return -errno;
|
||||
return 0;
|
||||
if (finfo->fh >= REMOTE_FD_OFFSET)
|
||||
{
|
||||
return request_fsync (writer_socket, finfo->fh - REMOTE_FD_OFFSET);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fsync (finfo->fh) == -1)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -440,9 +412,7 @@ struct fuse_operations callback_oper = {
|
||||
.utimens = callback_utimens,
|
||||
.create = callback_create,
|
||||
.open = callback_open,
|
||||
.read_buf = callback_read_buf,
|
||||
.read = callback_read,
|
||||
.write_buf = callback_write_buf,
|
||||
.write = callback_write,
|
||||
.statfs = callback_statfs,
|
||||
.release = callback_release,
|
||||
@@ -458,7 +428,6 @@ struct fuse_operations callback_oper = {
|
||||
|
||||
enum {
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -467,34 +436,34 @@ usage (const char *progname)
|
||||
fprintf (stdout,
|
||||
"usage: %s basepath mountpoint [options]\n"
|
||||
"\n"
|
||||
" Makes basepath visible at mountpoint such that files are read-only, directories are writable\n"
|
||||
" Makes basepath visible at mountpoint such that files are writeable only through\n"
|
||||
" fd passed in the --socket argument.\n"
|
||||
"\n"
|
||||
"general options:\n"
|
||||
" -o opt,[opt...] mount options\n"
|
||||
" -h --help print help\n"
|
||||
" --socket=fd Pass in the socket fd\n"
|
||||
" --backend Run the backend instead of fuse\n"
|
||||
"\n", progname);
|
||||
}
|
||||
|
||||
static int
|
||||
rofs_parse_opt (void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
revokefs_opt_proc (void *data,
|
||||
const char *arg,
|
||||
int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
(void) data;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if (basefd == -1)
|
||||
if (base_path == NULL)
|
||||
{
|
||||
basefd = openat (AT_FDCWD, arg, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
|
||||
if (basefd == -1)
|
||||
err (1, "opening rootfs %s", arg);
|
||||
base_path = g_strdup (arg);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
case FUSE_OPT_KEY_OPT:
|
||||
return 1;
|
||||
case KEY_HELP:
|
||||
@@ -507,11 +476,19 @@ rofs_parse_opt (void *data, const char *arg, int key,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct fuse_opt rofs_opts[] = {
|
||||
struct revokefs_config {
|
||||
int socket_fd;
|
||||
int backend;
|
||||
};
|
||||
|
||||
#define REVOKEFS_OPT(t, p, v) { t, offsetof(struct revokefs_config, p), v }
|
||||
|
||||
static struct fuse_opt revokefs_opts[] = {
|
||||
REVOKEFS_OPT ("--socket=%i", socket_fd, -1),
|
||||
REVOKEFS_OPT ("--backend", backend, 1),
|
||||
|
||||
FUSE_OPT_KEY ("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY ("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY ("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY ("--version", KEY_VERSION),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
@@ -520,21 +497,77 @@ main (int argc, char *argv[])
|
||||
{
|
||||
struct fuse_args args = FUSE_ARGS_INIT (argc, argv);
|
||||
int res;
|
||||
struct revokefs_config conf = { -1 };
|
||||
|
||||
res = fuse_opt_parse (&args, &basefd, rofs_opts, rofs_parse_opt);
|
||||
res = fuse_opt_parse (&args, &conf, revokefs_opts, revokefs_opt_proc);
|
||||
if (res != 0)
|
||||
{
|
||||
fprintf (stderr, "Invalid arguments\n");
|
||||
fprintf (stderr, "see `%s -h' for usage\n", argv[0]);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (basefd == -1)
|
||||
|
||||
if (base_path == NULL)
|
||||
{
|
||||
fprintf (stderr, "Missing basepath\n");
|
||||
fprintf (stderr, "see `%s -h' for usage\n", argv[0]);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
basefd = openat (AT_FDCWD, base_path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
|
||||
if (basefd == -1)
|
||||
{
|
||||
perror ("opening basepath: ");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (conf.backend)
|
||||
{
|
||||
if (conf.socket_fd == -1)
|
||||
{
|
||||
fprintf (stderr, "No --socket passed, required for --backend\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
do_writer (basefd, conf.socket_fd);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
if (conf.socket_fd != -1)
|
||||
{
|
||||
writer_socket = conf.socket_fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
int sockets[2];
|
||||
pid_t pid;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets))
|
||||
{
|
||||
perror ("Failed to create socket pair");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pid = fork ();
|
||||
if (pid == -1)
|
||||
{
|
||||
perror ("Failed to fork writer");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
/* writer process */
|
||||
close (sockets[0]);
|
||||
do_writer (basefd, sockets[1]);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
/* Main process */
|
||||
close (sockets[1]);
|
||||
writer_socket = sockets[0];
|
||||
}
|
||||
|
||||
fuse_main (args.argc, args.argv, &callback_oper, NULL);
|
||||
|
||||
return 0;
|
||||
|
||||
875
revokefs/writer.c
Normal file
875
revokefs/writer.c
Normal file
@@ -0,0 +1,875 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Alexander Larsson <alexl@redhat.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
* 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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <fuse.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "writer.h"
|
||||
#include "libglnx.h"
|
||||
|
||||
static int basefd = -1;
|
||||
|
||||
static GHashTable *outstanding_fds;
|
||||
|
||||
static GMutex mutex;
|
||||
|
||||
static ssize_t
|
||||
do_request (int writer_socket,
|
||||
RevokefsRequest *request,
|
||||
const guchar *data,
|
||||
size_t data_size,
|
||||
const guchar *data2,
|
||||
size_t data2_size,
|
||||
RevokefsResponse *response,
|
||||
guchar *response_data,
|
||||
size_t response_data_size)
|
||||
{
|
||||
size_t request_size;
|
||||
size_t response_max_size;
|
||||
ssize_t written_size, read_size;
|
||||
struct iovec write_vecs[2] = {};
|
||||
int n_write_vecs = 0;
|
||||
struct iovec read_vecs[2] = {};
|
||||
int n_read_vecs = 0;
|
||||
g_autoptr(GMutexLocker) locker = NULL;
|
||||
|
||||
request_size = sizeof (RevokefsRequest);
|
||||
write_vecs[n_write_vecs].iov_base = (char *)request;
|
||||
write_vecs[n_write_vecs++].iov_len = request_size;
|
||||
|
||||
if (data)
|
||||
{
|
||||
write_vecs[n_write_vecs].iov_base = (char *)data;
|
||||
write_vecs[n_write_vecs++].iov_len = data_size;
|
||||
request_size += data_size;
|
||||
}
|
||||
|
||||
if (data2)
|
||||
{
|
||||
write_vecs[n_write_vecs].iov_base = (char *)data2;
|
||||
write_vecs[n_write_vecs++].iov_len = data2_size;
|
||||
request_size += data2_size;
|
||||
}
|
||||
|
||||
locker = g_mutex_locker_new (&mutex);
|
||||
written_size = TEMP_FAILURE_RETRY (writev (writer_socket, write_vecs, n_write_vecs));
|
||||
if (written_size == -1)
|
||||
{
|
||||
g_printerr ("Write to socket returned error %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
if (written_size != request_size)
|
||||
{
|
||||
g_printerr ("Partial Write to socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
response_max_size = sizeof (RevokefsResponse);
|
||||
read_vecs[n_read_vecs].iov_base = (char *)response;
|
||||
read_vecs[n_read_vecs++].iov_len = response_max_size;
|
||||
|
||||
if (response_data)
|
||||
{
|
||||
read_vecs[n_read_vecs].iov_base = response_data;
|
||||
read_vecs[n_read_vecs++].iov_len = response_data_size;
|
||||
response_max_size += response_data_size;
|
||||
}
|
||||
|
||||
read_size = TEMP_FAILURE_RETRY (readv (writer_socket, read_vecs, n_read_vecs));
|
||||
if (read_size == -1)
|
||||
{
|
||||
g_printerr ("Read from socket returned error %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_size < sizeof (RevokefsResponse))
|
||||
{
|
||||
g_printerr ("Invalid read size %zd\n", read_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return read_size - sizeof (RevokefsResponse);
|
||||
}
|
||||
|
||||
static int
|
||||
request_path_i64_i64 (int writer_socket, RevokefsOps op, const char *path, guint64 arg1, guint64 arg2)
|
||||
{
|
||||
RevokefsRequest request = { op };
|
||||
RevokefsResponse response;
|
||||
size_t path_len = strlen (path);
|
||||
ssize_t response_data_len;
|
||||
|
||||
if (path_len > MAX_DATA_SIZE)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
request.arg1 = arg1;
|
||||
request.arg2 = arg2;
|
||||
|
||||
response_data_len = do_request (writer_socket, &request, path, path_len, NULL, 0,
|
||||
&response, NULL, 0);
|
||||
if (response_data_len != 0)
|
||||
return -EIO;
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
static int
|
||||
request_path_int_int (int writer_socket, RevokefsOps op, const char *path, int arg1, int arg2)
|
||||
{
|
||||
return request_path_i64_i64 (writer_socket, op, path, arg1, arg2);
|
||||
}
|
||||
|
||||
static int
|
||||
request_path_int (int writer_socket, RevokefsOps op, const char *path, int arg1)
|
||||
{
|
||||
return request_path_int_int (writer_socket, op, path, arg1, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
request_path (int writer_socket, RevokefsOps op, const char *path)
|
||||
{
|
||||
return request_path_int_int (writer_socket, op, path, 0, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
request_path_data (int writer_socket, RevokefsOps op, const char *path,
|
||||
const char *data, size_t data_len)
|
||||
{
|
||||
RevokefsRequest request = { op };
|
||||
RevokefsResponse response;
|
||||
size_t path_len = strlen (path);
|
||||
size_t total_len = path_len + data_len;
|
||||
ssize_t response_data_len;
|
||||
|
||||
if (total_len > MAX_DATA_SIZE)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
request.arg1 = strlen(path);
|
||||
|
||||
response_data_len = do_request (writer_socket, &request, path, path_len, data, data_len,
|
||||
&response, NULL, 0);
|
||||
if (response_data_len != 0)
|
||||
return -EIO;
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
static int
|
||||
request_path_path (int writer_socket, RevokefsOps op, const char *path1, const char *path2)
|
||||
{
|
||||
return request_path_data (writer_socket, op, path1, path2, strlen(path2));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
validate_path (char *path)
|
||||
{
|
||||
char *end_segment;
|
||||
|
||||
/* No absolute or empty paths */
|
||||
if (*path == '/' || *path == 0)
|
||||
return FALSE;
|
||||
|
||||
while (*path != 0)
|
||||
{
|
||||
end_segment = strchr (path, '/');
|
||||
if (end_segment == NULL)
|
||||
end_segment = path + strlen (path);
|
||||
|
||||
if (strncmp (path, "..", 2) == 0)
|
||||
return FALSE;
|
||||
|
||||
path = end_segment;
|
||||
while (*path == '/')
|
||||
path++;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_valid_path (guchar *data, size_t len)
|
||||
{
|
||||
char *path = g_strndup (data, len);
|
||||
|
||||
if (!validate_path (path))
|
||||
{
|
||||
g_printerr ("Invalid path argument %s\n", path);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static int
|
||||
mask_mode (int mode)
|
||||
{
|
||||
/* mask setuid, setgid and world-writable permissions bits */
|
||||
return mode & ~S_ISUID & ~S_ISGID & ~(S_IWGRP | S_IWOTH);
|
||||
}
|
||||
|
||||
static void
|
||||
get_any_path_and_valid_path (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
char **any_path1,
|
||||
char **valid_path2)
|
||||
{
|
||||
if (request->arg1 >= data_size)
|
||||
{
|
||||
g_printerr ("Invalid path1 size\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
*any_path1 = g_strndup (request->data, request->arg1);
|
||||
*valid_path2 = get_valid_path (request->data + request->arg1, data_size - request->arg1);
|
||||
}
|
||||
|
||||
static void
|
||||
get_valid_2path (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
char **path1,
|
||||
char **path2)
|
||||
{
|
||||
if (request->arg1 >= data_size)
|
||||
{
|
||||
g_printerr ("Invalid path1 size\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
*path1 = get_valid_path (request->data, request->arg1);
|
||||
*path2 = get_valid_path (request->data + request->arg1, data_size - request->arg1);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_mkdir (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = get_valid_path (request->data, data_size);
|
||||
int mode = request->arg1;
|
||||
|
||||
if (mkdirat (basefd, path, mask_mode (mode)) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_mkdir (int writer_socket, const char *path, mode_t mode)
|
||||
{
|
||||
return request_path_int (writer_socket, REVOKE_FS_MKDIR, path, mode);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_rmdir (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = get_valid_path (request->data, data_size);
|
||||
|
||||
if (unlinkat (basefd, path, AT_REMOVEDIR) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_rmdir (int writer_socket, const char *path)
|
||||
{
|
||||
return request_path (writer_socket, REVOKE_FS_RMDIR, path);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_unlink (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = get_valid_path (request->data, data_size);
|
||||
|
||||
if (unlinkat (basefd, path, 0) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_unlink (int writer_socket, const char *path)
|
||||
{
|
||||
return request_path (writer_socket, REVOKE_FS_UNLINK, path);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_symlink (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *to = NULL;
|
||||
|
||||
/* from doesn't have to be a valid path, it can be absolute or whatever */
|
||||
get_any_path_and_valid_path (request, data_size, &from, &to);
|
||||
|
||||
if (symlinkat (from, basefd, to) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_symlink (int writer_socket, const char *from, const char *to)
|
||||
{
|
||||
return request_path_path (writer_socket, REVOKE_FS_SYMLINK, from, to);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_link (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *to = NULL;
|
||||
|
||||
get_valid_2path (request, data_size, &from, &to);
|
||||
|
||||
if (linkat (basefd, from, basefd, to, 0) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_link (int writer_socket, const char *from, const char *to)
|
||||
{
|
||||
return request_path_path (writer_socket, REVOKE_FS_LINK, from, to);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_rename (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *to = NULL;
|
||||
|
||||
get_valid_2path (request, data_size, &from, &to);
|
||||
|
||||
if (renameat (basefd, from, basefd, to) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_rename (int writer_socket, const char *from, const char *to)
|
||||
{
|
||||
return request_path_path (writer_socket, REVOKE_FS_RENAME, from, to);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_chmod (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = get_valid_path (request->data, data_size);
|
||||
int mode = request->arg1;
|
||||
|
||||
/* Note we can't use AT_SYMLINK_NOFOLLOW yet;
|
||||
* https://marc.info/?l=linux-kernel&m=148830147803162&w=2
|
||||
* https://marc.info/?l=linux-fsdevel&m=149193779929561&w=2
|
||||
*/
|
||||
if (fchmodat (basefd, path, mask_mode (mode), 0) != 0)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_chmod(int writer_socket, const char *path, mode_t mode)
|
||||
{
|
||||
return request_path_int (writer_socket, REVOKE_FS_CHMOD, path, mode);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_chown (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = get_valid_path (request->data, data_size);
|
||||
uid_t uid = request->arg1;
|
||||
gid_t gid = request->arg2;
|
||||
|
||||
if (fchownat (basefd, path, uid, gid, AT_SYMLINK_NOFOLLOW) != 0)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_chown(int writer_socket, const char *path, uid_t uid, gid_t gid)
|
||||
{
|
||||
return request_path_int_int (writer_socket, REVOKE_FS_CHOWN, path, uid, gid);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_truncate (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = get_valid_path (request->data, data_size);
|
||||
off_t size = request->arg1;
|
||||
|
||||
glnx_autofd int fd = openat (basefd, path, O_NOFOLLOW|O_WRONLY);
|
||||
if (fd == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
{
|
||||
if (ftruncate (fd, size) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_truncate (int writer_socket, const char *path, off_t size)
|
||||
{
|
||||
return request_path_i64_i64 (writer_socket, REVOKE_FS_TRUNCATE, path, size, 0);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_utimens (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = NULL;
|
||||
struct timespec *tv;
|
||||
|
||||
if (request->arg1 + sizeof (struct timespec) * 2 != data_size)
|
||||
{
|
||||
g_printerr ("Invalid data size\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
path = get_valid_path (request->data, request->arg1);
|
||||
tv = (struct timespec *)(request->data + request->arg1);
|
||||
|
||||
if (utimensat (basefd, path, tv, AT_SYMLINK_NOFOLLOW) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_utimens (int writer_socket, const char *path, const struct timespec tv[2])
|
||||
{
|
||||
return request_path_data (writer_socket, REVOKE_FS_UTIMENS, path,
|
||||
(guchar *)tv, sizeof (struct timespec) * 2);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_open (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = get_valid_path (request->data, data_size);
|
||||
int mode = request->arg1;
|
||||
int flags = request->arg2;
|
||||
int fd;
|
||||
|
||||
|
||||
/* We need to specially handle O_TRUNC. Also, Fuse should have already
|
||||
* resolved symlinks, but use O_NOFOLLOW to be safe to avoid following
|
||||
* symlinks to some other filesystem. */
|
||||
fd = openat (basefd, path, (flags & ~O_TRUNC) | O_NOFOLLOW, mask_mode (mode));
|
||||
if (fd == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
{
|
||||
response->result = 0;
|
||||
if (flags & O_TRUNC)
|
||||
{
|
||||
if (ftruncate (fd, 0) == -1)
|
||||
response->result = -errno;
|
||||
}
|
||||
|
||||
if (response->result == 0)
|
||||
{
|
||||
g_hash_table_insert (outstanding_fds, GUINT_TO_POINTER(fd), GUINT_TO_POINTER(1));
|
||||
response->result = fd;
|
||||
}
|
||||
else
|
||||
(void) close (fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_open (int writer_socket, const char *path, mode_t mode, int flags)
|
||||
{
|
||||
return request_path_int_int (writer_socket, REVOKE_FS_OPEN, path, mode, flags);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_read (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
int r;
|
||||
int fd = request->arg1;
|
||||
size_t size = request->arg2;
|
||||
off_t offset = request->arg3;
|
||||
|
||||
if (size > MAX_DATA_SIZE)
|
||||
size = MAX_DATA_SIZE;
|
||||
|
||||
if (g_hash_table_lookup (outstanding_fds, GUINT_TO_POINTER(fd)) == NULL)
|
||||
{
|
||||
response->result = -EBADFD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = pread (fd, response->data, size, offset);
|
||||
if (r == -1)
|
||||
{
|
||||
response->result = -errno;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->result = r;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
request_read (int writer_socket, int fd, char *buf, size_t size, off_t offset)
|
||||
{
|
||||
RevokefsRequest request = { REVOKE_FS_READ };
|
||||
RevokefsResponse response;
|
||||
ssize_t response_data_len;
|
||||
|
||||
request.arg1 = fd;
|
||||
request.arg2 = size;
|
||||
request.arg3 = offset;
|
||||
|
||||
response_data_len = do_request (writer_socket, &request, NULL, 0, NULL, 0,
|
||||
&response, buf, size);
|
||||
if (response_data_len < 0)
|
||||
return -EIO;
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_write (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
int r;
|
||||
int fd = request->arg1;
|
||||
off_t offset = request->arg2;
|
||||
|
||||
if (g_hash_table_lookup (outstanding_fds, GUINT_TO_POINTER(fd)) == NULL)
|
||||
{
|
||||
response->result = -EBADFD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = pwrite (fd, request->data, data_size, offset);
|
||||
if (r == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_write (int writer_socket, int fd, const char *buf, size_t size, off_t offset)
|
||||
{
|
||||
RevokefsRequest request = { REVOKE_FS_WRITE };
|
||||
RevokefsResponse response;
|
||||
ssize_t response_data_len;
|
||||
|
||||
if (size > MAX_DATA_SIZE)
|
||||
size = MAX_DATA_SIZE;
|
||||
|
||||
request.arg1 = fd;
|
||||
request.arg2 = offset;
|
||||
|
||||
response_data_len = do_request (writer_socket, &request, buf, size, NULL, 0,
|
||||
&response, NULL, 0);
|
||||
if (response_data_len < 0)
|
||||
return -EIO;
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_fsync (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
int r;
|
||||
int fd = request->arg1;
|
||||
|
||||
if (g_hash_table_lookup (outstanding_fds, GUINT_TO_POINTER(fd)) == NULL)
|
||||
{
|
||||
response->result = -EBADFD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = fsync (fd);
|
||||
if (r == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_fsync (int writer_socket, int fd)
|
||||
{
|
||||
RevokefsRequest request = { REVOKE_FS_FSYNC };
|
||||
RevokefsResponse response;
|
||||
ssize_t response_data_len;
|
||||
|
||||
request.arg1 = fd;
|
||||
|
||||
response_data_len = do_request (writer_socket, &request, NULL, 0, NULL, 0,
|
||||
&response, NULL, 0);
|
||||
if (response_data_len < 0)
|
||||
return -EIO;
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_close (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
int fd = request->arg1;
|
||||
|
||||
if (!g_hash_table_remove (outstanding_fds, GUINT_TO_POINTER(fd)))
|
||||
{
|
||||
response->result = -EBADFD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
close (fd);
|
||||
response->result = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_close (int writer_socket, int fd)
|
||||
{
|
||||
RevokefsRequest request = { REVOKE_FS_CLOSE };
|
||||
RevokefsResponse response;
|
||||
ssize_t response_data_len;
|
||||
|
||||
request.arg1 = fd;
|
||||
response_data_len = do_request (writer_socket, &request, NULL, 0, NULL, 0,
|
||||
&response, NULL, 0);
|
||||
if (response_data_len < 0)
|
||||
return -EIO;
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
handle_access (RevokefsRequest *request,
|
||||
gsize data_size,
|
||||
RevokefsResponse *response)
|
||||
{
|
||||
g_autofree char *path = get_valid_path (request->data, data_size);
|
||||
int mode = request->arg1;
|
||||
|
||||
/* Apparently at least GNU coreutils rm calls `faccessat(W_OK)`
|
||||
* before trying to do an unlink. So...we'll just lie about
|
||||
* writable access here.
|
||||
*/
|
||||
if (faccessat (basefd, path, mode, AT_SYMLINK_NOFOLLOW) == -1)
|
||||
response->result = -errno;
|
||||
else
|
||||
response->result = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
request_access (int writer_socket, const char *path, int mode)
|
||||
{
|
||||
return request_path_int (writer_socket, REVOKE_FS_ACCESS, path, mode);
|
||||
}
|
||||
|
||||
void
|
||||
do_writer (int basefd_arg,
|
||||
int fuse_socket)
|
||||
{
|
||||
guchar request_buffer[MAX_REQUEST_SIZE];
|
||||
RevokefsRequest *request = (RevokefsRequest *)&request_buffer;
|
||||
guchar response_buffer[MAX_RESPONSE_SIZE];
|
||||
RevokefsResponse *response = (RevokefsResponse *)&response_buffer;
|
||||
|
||||
basefd = basefd_arg;
|
||||
outstanding_fds = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
|
||||
while (1)
|
||||
{
|
||||
ssize_t data_size, size;
|
||||
ssize_t response_data_size, response_size, written_size;
|
||||
|
||||
size = TEMP_FAILURE_RETRY (read (fuse_socket, request_buffer, sizeof (request_buffer)));
|
||||
if (size == -1)
|
||||
{
|
||||
perror ("Got error reading from fuse socket: ");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
/* Fuse filesystem finished */
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (size < sizeof (RevokefsRequest))
|
||||
{
|
||||
g_printerr ("Invalid request size %zd", size);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
data_size = size - sizeof (RevokefsRequest);
|
||||
memset (response_buffer, 0, sizeof(RevokefsResponse));
|
||||
|
||||
switch (request->op)
|
||||
{
|
||||
case REVOKE_FS_MKDIR:
|
||||
response_data_size = handle_mkdir (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_RMDIR:
|
||||
response_data_size = handle_rmdir (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_UNLINK:
|
||||
response_data_size = handle_unlink (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_SYMLINK:
|
||||
response_data_size = handle_symlink (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_LINK:
|
||||
response_data_size = handle_link (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_RENAME:
|
||||
response_data_size = handle_rename (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_CHMOD:
|
||||
response_data_size = handle_chmod (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_CHOWN:
|
||||
response_data_size = handle_chown (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_TRUNCATE:
|
||||
response_data_size = handle_truncate (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_UTIMENS:
|
||||
response_data_size = handle_utimens (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_OPEN:
|
||||
response_data_size = handle_open (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_READ:
|
||||
response_data_size = handle_read (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_WRITE:
|
||||
response_data_size = handle_write (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_FSYNC:
|
||||
response_data_size = handle_fsync (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_CLOSE:
|
||||
response_data_size = handle_close (request, data_size, response);
|
||||
break;
|
||||
case REVOKE_FS_ACCESS:
|
||||
response_data_size = handle_access (request, data_size, response);
|
||||
break;
|
||||
default:
|
||||
g_printerr ("Invalid request op %d", (guint) request->op);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (response_data_size < 0 || response_data_size > MAX_DATA_SIZE)
|
||||
{
|
||||
g_printerr ("Invalid response size %ld", response_size);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
response_size = RESPONSE_SIZE(response_data_size);
|
||||
|
||||
written_size = TEMP_FAILURE_RETRY (write (fuse_socket, response_buffer, response_size));
|
||||
if (written_size == -1)
|
||||
{
|
||||
perror ("Got error writing to fuse socket: ");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (written_size != response_size)
|
||||
{
|
||||
g_printerr ("Got partial write to fuse socket");
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
85
revokefs/writer.h
Normal file
85
revokefs/writer.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Alexander Larsson <alexl@redhat.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __REVOKEFS_WRITER_H__
|
||||
#define __REVOKEFS_WRITER_H__
|
||||
|
||||
int request_mkdir(int writer_socket, const char *path, mode_t mode);
|
||||
int request_rmdir (int writer_socket, const char *path);
|
||||
int request_unlink (int writer_socket, const char *path);
|
||||
int request_symlink (int writer_socket, const char *from, const char *to);
|
||||
int request_link (int writer_socket, const char *from, const char *to);
|
||||
int request_rename (int writer_socket, const char *from, const char *to);
|
||||
int request_chmod(int writer_socket, const char *path, mode_t mode);
|
||||
int request_chown(int writer_socket, const char *path, uid_t uid, gid_t gid);
|
||||
int request_truncate (int writer_socket, const char *path, off_t size);
|
||||
int request_utimens (int writer_socket, const char *path, const struct timespec tv[2]);
|
||||
int request_open (int writer_socket, const char *path, mode_t mode, int flags);
|
||||
int request_read (int writer_socket, int fd, char *buf, size_t size, off_t offset);
|
||||
int request_write (int writer_socket, int fd, const char *buf, size_t size, off_t offset);
|
||||
int request_fsync (int writer_socket, int fd);
|
||||
int request_close (int writer_socket, int fd);
|
||||
int request_access (int writer_socket, const char *path, int mode);
|
||||
|
||||
void do_writer (int basefd, int socket);
|
||||
|
||||
|
||||
typedef enum {
|
||||
REVOKE_FS_MKDIR,
|
||||
REVOKE_FS_RMDIR,
|
||||
REVOKE_FS_UNLINK,
|
||||
REVOKE_FS_SYMLINK,
|
||||
REVOKE_FS_LINK,
|
||||
REVOKE_FS_RENAME,
|
||||
REVOKE_FS_CHMOD,
|
||||
REVOKE_FS_CHOWN,
|
||||
REVOKE_FS_TRUNCATE,
|
||||
REVOKE_FS_UTIMENS,
|
||||
REVOKE_FS_OPEN,
|
||||
REVOKE_FS_READ,
|
||||
REVOKE_FS_WRITE,
|
||||
REVOKE_FS_FSYNC,
|
||||
REVOKE_FS_CLOSE,
|
||||
REVOKE_FS_ACCESS,
|
||||
} RevokefsOps;
|
||||
|
||||
typedef struct {
|
||||
guint32 op;
|
||||
guint64 arg1;
|
||||
guint64 arg2;
|
||||
guint64 arg3;
|
||||
guchar data[];
|
||||
} RevokefsRequest;
|
||||
|
||||
typedef struct {
|
||||
gint32 result;
|
||||
|
||||
guchar data[];
|
||||
} RevokefsResponse;
|
||||
|
||||
#define REQUEST_SIZE(__data_size) (sizeof(RevokefsRequest) + (__data_size))
|
||||
#define RESPONSE_SIZE(__data_size) (sizeof(RevokefsResponse) + (__data_size))
|
||||
|
||||
#define MAX_DATA_SIZE 16384
|
||||
#define MAX_REQUEST_SIZE REQUEST_SIZE(MAX_DATA_SIZE)
|
||||
#define MAX_RESPONSE_SIZE RESPONSE_SIZE(MAX_DATA_SIZE)
|
||||
|
||||
#endif /* __REVOKEFS_WRITER_H__ */
|
||||
Reference in New Issue
Block a user