Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F151004373
D27818.id81324.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D27818.id81324.diff
View Options
Index: sys/fs/fuse/fuse_internal.h
===================================================================
--- sys/fs/fuse/fuse_internal.h
+++ sys/fs/fuse/fuse_internal.h
@@ -274,6 +274,10 @@
int fuse_internal_setattr(struct vnode *vp, struct vattr *va,
struct thread *td, struct ucred *cred);
+/* write */
+void fuse_internal_clear_suid_on_write(struct vnode *vp, struct ucred *cred,
+ struct thread *td);
+
/* strategy */
/* entity creation */
Index: sys/fs/fuse/fuse_internal.c
===================================================================
--- sys/fs/fuse/fuse_internal.c
+++ sys/fs/fuse/fuse_internal.c
@@ -1054,6 +1054,9 @@
if (!fuse_libabi_geq(data, 7, 24))
fsess_set_notimpl(data->mp, FUSE_LSEEK);
+ if (!fuse_libabi_geq(data, 7, 28))
+ fsess_set_notimpl(data->mp, FUSE_COPY_FILE_RANGE);
+
out:
if (err) {
fdata_set_dead(data);
@@ -1098,6 +1101,12 @@
* FUSE_READDIRPLUS_AUTO: not yet implemented
* FUSE_ASYNC_DIO: not yet implemented
* FUSE_NO_OPEN_SUPPORT: not yet implemented
+ * FUSE_PARALLEL_DIROPS: not yet implemented
+ * FUSE_HANDLE_KILLPRIV: not yet implemented
+ * FUSE_POSIX_ACL: not yet implemented
+ * FUSE_ABORT_ERROR: not yet implemented
+ * FUSE_CACHE_SYMLINKS: not yet implemented
+ * FUSE_MAX_PAGES: not yet implemented
*/
fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT
| FUSE_BIG_WRITES | FUSE_WRITEBACK_CACHE;
@@ -1228,6 +1237,43 @@
return err;
}
+/*
+ * FreeBSD clears the SUID and SGID bits on any write by a non-root user.
+ */
+void
+fuse_internal_clear_suid_on_write(struct vnode *vp, struct ucred *cred,
+ struct thread *td)
+{
+ struct fuse_data *data;
+ struct mount *mp;
+ struct vattr va;
+ int dataflags;
+
+ mp = vnode_mount(vp);
+ data = fuse_get_mpdata(mp);
+ dataflags = data->dataflags;
+
+ if (dataflags & FSESS_DEFAULT_PERMISSIONS) {
+ if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID)) {
+ fuse_internal_getattr(vp, &va, cred, td);
+ if (va.va_mode & (S_ISUID | S_ISGID)) {
+ mode_t mode = va.va_mode & ~(S_ISUID | S_ISGID);
+ /* Clear all vattr fields except mode */
+ vattr_null(&va);
+ va.va_mode = mode;
+
+ /*
+ * Ignore fuse_internal_setattr's return value,
+ * because at this point the write operation has
+ * already succeeded and we don't want to return
+ * failing status for that.
+ */
+ (void)fuse_internal_setattr(vp, &va, td, NULL);
+ }
+ }
+ }
+}
+
#ifdef ZERO_PAD_INCOMPLETE_BUFS
static int
isbzero(void *buf, size_t len)
Index: sys/fs/fuse/fuse_io.c
===================================================================
--- sys/fs/fuse/fuse_io.c
+++ sys/fs/fuse/fuse_io.c
@@ -121,9 +121,6 @@
static int
fuse_inval_buf_range(struct vnode *vp, off_t filesize, off_t start, off_t end);
-static void
-fuse_io_clear_suid_on_write(struct vnode *vp, struct ucred *cred,
- struct thread *td);
static int
fuse_read_directbackend(struct vnode *vp, struct uio *uio,
struct ucred *cred, struct fuse_filehandle *fufh);
@@ -190,43 +187,6 @@
return (0);
}
-/*
- * FreeBSD clears the SUID and SGID bits on any write by a non-root user.
- */
-static void
-fuse_io_clear_suid_on_write(struct vnode *vp, struct ucred *cred,
- struct thread *td)
-{
- struct fuse_data *data;
- struct mount *mp;
- struct vattr va;
- int dataflags;
-
- mp = vnode_mount(vp);
- data = fuse_get_mpdata(mp);
- dataflags = data->dataflags;
-
- if (dataflags & FSESS_DEFAULT_PERMISSIONS) {
- if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID)) {
- fuse_internal_getattr(vp, &va, cred, td);
- if (va.va_mode & (S_ISUID | S_ISGID)) {
- mode_t mode = va.va_mode & ~(S_ISUID | S_ISGID);
- /* Clear all vattr fields except mode */
- vattr_null(&va);
- va.va_mode = mode;
-
- /*
- * Ignore fuse_internal_setattr's return value,
- * because at this point the write operation has
- * already succeeded and we don't want to return
- * failing status for that.
- */
- (void)fuse_internal_setattr(vp, &va, td, NULL);
- }
- }
- }
-}
-
SDT_PROBE_DEFINE5(fusefs, , io, io_dispatch, "struct vnode*", "struct uio*",
"int", "struct ucred*", "struct fuse_filehandle*");
SDT_PROBE_DEFINE4(fusefs, , io, io_dispatch_filehandles_closed, "struct vnode*",
@@ -318,7 +278,7 @@
err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag,
pid);
}
- fuse_io_clear_suid_on_write(vp, cred, uio->uio_td);
+ fuse_internal_clear_suid_on_write(vp, cred, uio->uio_td);
break;
default:
panic("uninterpreted mode passed to fuse_io_dispatch");
Index: sys/fs/fuse/fuse_ipc.c
===================================================================
--- sys/fs/fuse/fuse_ipc.c
+++ sys/fs/fuse/fuse_ipc.c
@@ -855,6 +855,10 @@
err = (blen == sizeof(struct fuse_lseek_out)) ? 0 : EINVAL;
break;
+ case FUSE_COPY_FILE_RANGE:
+ err = (blen == sizeof(struct fuse_write_out)) ? 0 : EINVAL;
+ break;
+
default:
panic("FUSE: opcodes out of sync (%d)\n", opcode);
}
Index: sys/fs/fuse/fuse_kernel.h
===================================================================
--- sys/fs/fuse/fuse_kernel.h
+++ sys/fs/fuse/fuse_kernel.h
@@ -1,4 +1,6 @@
-/*--
+/*-
+ * SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause)
+ *
* This file defines the kernel interface of FUSE
* Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
*
@@ -105,6 +107,22 @@
*
* 7.24
* - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support
+ *
+ * 7.25
+ * - add FUSE_PARALLEL_DIROPS
+ *
+ * 7.26
+ * - add FUSE_HANDLE_KILLPRIV
+ * - add FUSE_POSIX_ACL
+ *
+ * 7.27
+ * - add FUSE_ABORT_ERROR
+ *
+ * 7.28
+ * - add FUSE_COPY_FILE_RANGE
+ * - add FOPEN_CACHE_DIR
+ * - add FUSE_MAX_PAGES, add max_pages to init_out
+ * - add FUSE_CACHE_SYMLINKS
*/
#ifndef _FUSE_FUSE_KERNEL_H
@@ -120,7 +138,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 24
+#define FUSE_KERNEL_MINOR_VERSION 28
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -188,10 +206,12 @@
* FOPEN_DIRECT_IO: bypass page cache for this open file
* FOPEN_KEEP_CACHE: don't invalidate the data cache on open
* FOPEN_NONSEEKABLE: the file is not seekable
+ * FOPEN_CACHE_DIR: allow caching this directory
*/
#define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1)
#define FOPEN_NONSEEKABLE (1 << 2)
+#define FOPEN_CACHE_DIR (1 << 3)
/**
* INIT request/reply flags
@@ -214,6 +234,12 @@
* FUSE_ASYNC_DIO: asynchronous direct I/O submission
* FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes
* FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens
+ * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir
+ * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc
+ * FUSE_POSIX_ACL: filesystem supports posix acls
+ * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED
+ * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
+ * FUSE_CACHE_SYMLINKS: cache READLINK responses
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -233,6 +259,12 @@
#define FUSE_ASYNC_DIO (1 << 15)
#define FUSE_WRITEBACK_CACHE (1 << 16)
#define FUSE_NO_OPEN_SUPPORT (1 << 17)
+#define FUSE_PARALLEL_DIROPS (1 << 18)
+#define FUSE_HANDLE_KILLPRIV (1 << 19)
+#define FUSE_POSIX_ACL (1 << 20)
+#define FUSE_ABORT_ERROR (1 << 21)
+#define FUSE_MAX_PAGES (1 << 22)
+#define FUSE_CACHE_SYMLINKS (1 << 23)
#ifdef linux
/**
@@ -300,54 +332,55 @@
#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
enum fuse_opcode {
- FUSE_LOOKUP = 1,
- FUSE_FORGET = 2, /* no reply */
- FUSE_GETATTR = 3,
- FUSE_SETATTR = 4,
- FUSE_READLINK = 5,
- FUSE_SYMLINK = 6,
- FUSE_MKNOD = 8,
- FUSE_MKDIR = 9,
- FUSE_UNLINK = 10,
- FUSE_RMDIR = 11,
- FUSE_RENAME = 12,
- FUSE_LINK = 13,
- FUSE_OPEN = 14,
- FUSE_READ = 15,
- FUSE_WRITE = 16,
- FUSE_STATFS = 17,
- FUSE_RELEASE = 18,
- FUSE_FSYNC = 20,
- FUSE_SETXATTR = 21,
- FUSE_GETXATTR = 22,
- FUSE_LISTXATTR = 23,
- FUSE_REMOVEXATTR = 24,
- FUSE_FLUSH = 25,
- FUSE_INIT = 26,
- FUSE_OPENDIR = 27,
- FUSE_READDIR = 28,
- FUSE_RELEASEDIR = 29,
- FUSE_FSYNCDIR = 30,
- FUSE_GETLK = 31,
- FUSE_SETLK = 32,
- FUSE_SETLKW = 33,
- FUSE_ACCESS = 34,
- FUSE_CREATE = 35,
- FUSE_INTERRUPT = 36,
- FUSE_BMAP = 37,
- FUSE_DESTROY = 38,
- FUSE_IOCTL = 39,
- FUSE_POLL = 40,
- FUSE_NOTIFY_REPLY = 41,
- FUSE_BATCH_FORGET = 42,
- FUSE_FALLOCATE = 43,
- FUSE_READDIRPLUS = 44,
- FUSE_RENAME2 = 45,
- FUSE_LSEEK = 46,
+ FUSE_LOOKUP = 1,
+ FUSE_FORGET = 2, /* no reply */
+ FUSE_GETATTR = 3,
+ FUSE_SETATTR = 4,
+ FUSE_READLINK = 5,
+ FUSE_SYMLINK = 6,
+ FUSE_MKNOD = 8,
+ FUSE_MKDIR = 9,
+ FUSE_UNLINK = 10,
+ FUSE_RMDIR = 11,
+ FUSE_RENAME = 12,
+ FUSE_LINK = 13,
+ FUSE_OPEN = 14,
+ FUSE_READ = 15,
+ FUSE_WRITE = 16,
+ FUSE_STATFS = 17,
+ FUSE_RELEASE = 18,
+ FUSE_FSYNC = 20,
+ FUSE_SETXATTR = 21,
+ FUSE_GETXATTR = 22,
+ FUSE_LISTXATTR = 23,
+ FUSE_REMOVEXATTR = 24,
+ FUSE_FLUSH = 25,
+ FUSE_INIT = 26,
+ FUSE_OPENDIR = 27,
+ FUSE_READDIR = 28,
+ FUSE_RELEASEDIR = 29,
+ FUSE_FSYNCDIR = 30,
+ FUSE_GETLK = 31,
+ FUSE_SETLK = 32,
+ FUSE_SETLKW = 33,
+ FUSE_ACCESS = 34,
+ FUSE_CREATE = 35,
+ FUSE_INTERRUPT = 36,
+ FUSE_BMAP = 37,
+ FUSE_DESTROY = 38,
+ FUSE_IOCTL = 39,
+ FUSE_POLL = 40,
+ FUSE_NOTIFY_REPLY = 41,
+ FUSE_BATCH_FORGET = 42,
+ FUSE_FALLOCATE = 43,
+ FUSE_READDIRPLUS = 44,
+ FUSE_RENAME2 = 45,
+ FUSE_LSEEK = 46,
+ FUSE_COPY_FILE_RANGE = 47,
#ifdef linux
/* CUSE specific operations */
- CUSE_INIT = 4096,
+ CUSE_INIT = 4096,
#endif /* linux */
};
@@ -585,7 +618,9 @@
uint16_t congestion_threshold;
uint32_t max_write;
uint32_t time_gran;
- uint32_t unused[9];
+ uint16_t max_pages;
+ uint16_t padding;
+ uint32_t unused[8];
};
#ifdef linux
@@ -766,4 +801,14 @@
uint64_t offset;
};
+struct fuse_copy_file_range_in {
+ uint64_t fh_in;
+ uint64_t off_in;
+ uint64_t nodeid_out;
+ uint64_t fh_out;
+ uint64_t off_out;
+ uint64_t len;
+ uint64_t flags;
+};
+
#endif /* _FUSE_FUSE_KERNEL_H */
Index: sys/fs/fuse/fuse_vnops.c
===================================================================
--- sys/fs/fuse/fuse_vnops.c
+++ sys/fs/fuse/fuse_vnops.c
@@ -130,6 +130,7 @@
static vop_bmap_t fuse_vnop_bmap;
static vop_close_t fuse_fifo_close;
static vop_close_t fuse_vnop_close;
+static vop_copy_file_range_t fuse_vnop_copy_file_range;
static vop_create_t fuse_vnop_create;
static vop_deleteextattr_t fuse_vnop_deleteextattr;
static vop_fdatasync_t fuse_vnop_fdatasync;
@@ -185,6 +186,7 @@
.vop_advlock = fuse_vnop_advlock,
.vop_bmap = fuse_vnop_bmap,
.vop_close = fuse_vnop_close,
+ .vop_copy_file_range = fuse_vnop_copy_file_range,
.vop_create = fuse_vnop_create,
.vop_deleteextattr = fuse_vnop_deleteextattr,
.vop_fsync = fuse_vnop_fsync,
@@ -608,6 +610,126 @@
return err;
}
+/*
+ struct vop_copy_file_range_args {
+ struct vop_generic_args a_gen;
+ struct vnode *a_invp;
+ off_t *a_inoffp;
+ struct vnode *a_outvp;
+ off_t *a_outoffp;
+ size_t *a_lenp;
+ unsigned int a_flags;
+ struct ucred *a_incred;
+ struct ucred *a_outcred;
+ struct thread *a_fsizetd;
+}
+ */
+static int
+fuse_vnop_copy_file_range(struct vop_copy_file_range_args *ap)
+{
+ struct vnode *invp = ap->a_invp;
+ struct vnode *outvp = ap->a_outvp;
+ struct mount *mp = vnode_mount(invp);
+ struct fuse_dispatcher fdi;
+ struct fuse_filehandle *infufh, *outfufh;
+ struct fuse_copy_file_range_in *fcfri;
+ struct ucred *incred = ap->a_incred;
+ struct ucred *outcred = ap->a_outcred;
+ struct fuse_write_out *fwo;
+ struct thread *td;
+ struct uio io;
+ pid_t pid;
+ int err;
+
+ if (mp != vnode_mount(outvp))
+ goto fallback;
+
+ if (incred->cr_uid != outcred->cr_uid)
+ goto fallback;
+
+ if (incred->cr_groups[0] != outcred->cr_groups[0])
+ goto fallback;
+
+ if (!fsess_maybe_impl(mp, FUSE_COPY_FILE_RANGE))
+ goto fallback;
+
+ if (ap->a_fsizetd == NULL)
+ td = curthread;
+ else
+ td = ap->a_fsizetd;
+ pid = td->td_proc->p_pid;
+
+ err = fuse_filehandle_getrw(invp, FREAD, &infufh, incred, pid);
+ if (err)
+ return (err);
+
+ err = fuse_filehandle_getrw(outvp, FWRITE, &outfufh, outcred, pid);
+ if (err)
+ return (err);
+
+ /* Lock both vnodes, avoiding risk of deadlock. */
+ do {
+ err = vn_lock(outvp, LK_EXCLUSIVE);
+ if (invp == outvp)
+ break;
+ if (err == 0) {
+ err = vn_lock(invp, LK_SHARED | LK_NOWAIT);
+ if (err == 0)
+ break;
+ VOP_UNLOCK(outvp);
+ err = vn_lock(invp, LK_SHARED);
+ if (err == 0)
+ VOP_UNLOCK(invp);
+ }
+ } while (err == 0);
+ if (err != 0)
+ return (err);
+
+ io.uio_offset = *ap->a_outoffp;
+ io.uio_resid = *ap->a_lenp;
+ if (ap->a_fsizetd) {
+ err = vn_rlimit_fsize(outvp, &io, ap->a_fsizetd);
+ if (err)
+ goto unlock;
+ }
+
+ fdisp_init(&fdi, sizeof(*fcfri));
+ fdisp_make_vp(&fdi, FUSE_COPY_FILE_RANGE, invp, td, incred);
+ fcfri = fdi.indata;
+ fcfri->fh_in = infufh->fh_id;
+ fcfri->off_in = *ap->a_inoffp;
+ fcfri->nodeid_out = VTOI(outvp);
+ fcfri->fh_out = outfufh->fh_id;
+ fcfri->off_out = *ap->a_outoffp;
+ fcfri->len = *ap->a_lenp;
+ fcfri->flags = 0;
+
+ err = fdisp_wait_answ(&fdi);
+ if (err == 0) {
+ fwo = fdi.answ;
+ *ap->a_lenp = fwo->size;
+ *ap->a_inoffp += fwo->size;
+ *ap->a_outoffp += fwo->size;
+ fuse_internal_clear_suid_on_write(outvp, outcred, td);
+ }
+ fdisp_destroy(&fdi);
+
+unlock:
+ if (invp != outvp)
+ VOP_UNLOCK(invp);
+ VOP_UNLOCK(outvp);
+
+ if (err == ENOSYS) {
+ fsess_set_notimpl(mp, FUSE_COPY_FILE_RANGE);
+fallback:
+ err = vn_generic_copy_file_range(ap->a_invp, ap->a_inoffp,
+ ap->a_outvp, ap->a_outoffp, ap->a_lenp, ap->a_flags,
+ ap->a_incred, ap->a_outcred, ap->a_fsizetd);
+ }
+
+ return (err);
+}
+
static void
fdisp_make_mknod_for_fallback(
struct fuse_dispatcher *fdip,
Index: tests/sys/fs/fusefs/Makefile
===================================================================
--- tests/sys/fs/fusefs/Makefile
+++ tests/sys/fs/fusefs/Makefile
@@ -13,6 +13,7 @@
GTESTS+= allow_other
GTESTS+= bmap
GTESTS+= cache
+GTESTS+= copy_file_range
GTESTS+= create
GTESTS+= default_permissions
GTESTS+= default_permissions_privileged
Index: tests/sys/fs/fusefs/default_permissions.cc
===================================================================
--- tests/sys/fs/fusefs/default_permissions.cc
+++ tests/sys/fs/fusefs/default_permissions.cc
@@ -109,6 +109,25 @@
})));
}
+void expect_copy_file_range(uint64_t ino_in, uint64_t off_in, uint64_t ino_out,
+ uint64_t off_out, uint64_t len)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino_in &&
+ in.body.copy_file_range.off_in == off_in &&
+ in.body.copy_file_range.nodeid_out == ino_out &&
+ in.body.copy_file_range.off_out == off_out &&
+ in.body.copy_file_range.len == len);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len;
+ })));
+}
+
void expect_getattr(uint64_t ino, mode_t mode, uint64_t attr_valid, int times,
uid_t uid = 0, gid_t gid = 0)
{
@@ -141,6 +160,7 @@
class Access: public DefaultPermissions {};
class Chown: public DefaultPermissions {};
class Chgrp: public DefaultPermissions {};
+class CopyFileRange: public DefaultPermissions {};
class Lookup: public DefaultPermissions {};
class Open: public DefaultPermissions {};
class Setattr: public DefaultPermissions {};
@@ -477,6 +497,94 @@
EXPECT_EQ(0, chown(FULLPATH, -1, newgid)) << strerror(errno);
}
+/* A write by a non-owner should clear a file's SGID bit */
+TEST_F(CopyFileRange, clear_guid)
+{
+ const char FULLPATH_IN[] = "mountpoint/in.txt";
+ const char RELPATH_IN[] = "in.txt";
+ const char FULLPATH_OUT[] = "mountpoint/out.txt";
+ const char RELPATH_OUT[] = "out.txt";
+ struct stat sb;
+ uint64_t ino_in = 42;
+ uint64_t ino_out = 43;
+ mode_t oldmode = 02777;
+ mode_t newmode = 0777;
+ off_t fsize = 16;
+ off_t off_in = 0;
+ off_t off_out = 8;
+ off_t len = 8;
+ int fd_in, fd_out;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH_IN, ino_in, S_IFREG | oldmode, fsize, 1,
+ UINT64_MAX, 0, 0);
+ expect_open(ino_in, 0, 1);
+ FuseTest::expect_lookup(RELPATH_OUT, ino_out, S_IFREG | oldmode, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino_out, 0, 1);
+ expect_copy_file_range(ino_in, off_in, ino_out, off_out, len);
+ expect_chmod(ino_out, newmode, fsize);
+
+ fd_in = open(FULLPATH_IN, O_RDONLY);
+ ASSERT_LE(0, fd_in) << strerror(errno);
+ fd_out = open(FULLPATH_OUT, O_WRONLY);
+ ASSERT_LE(0, fd_out) << strerror(errno);
+ ASSERT_EQ(len,
+ copy_file_range(fd_in, &off_in, fd_out, &off_out, len, 0))
+ << strerror(errno);
+ ASSERT_EQ(0, fstat(fd_out, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+ ASSERT_EQ(0, fstat(fd_in, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | oldmode, sb.st_mode);
+
+ leak(fd_in);
+ leak(fd_out);
+}
+
+/* A write by a non-owner should clear a file's SUID bit */
+TEST_F(CopyFileRange, clear_suid)
+{
+ const char FULLPATH_IN[] = "mountpoint/in.txt";
+ const char RELPATH_IN[] = "in.txt";
+ const char FULLPATH_OUT[] = "mountpoint/out.txt";
+ const char RELPATH_OUT[] = "out.txt";
+ struct stat sb;
+ uint64_t ino_in = 42;
+ uint64_t ino_out = 43;
+ mode_t oldmode = 04777;
+ mode_t newmode = 0777;
+ off_t fsize = 16;
+ off_t off_in = 0;
+ off_t off_out = 8;
+ off_t len = 8;
+ int fd_in, fd_out;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH_IN, ino_in, S_IFREG | oldmode, fsize, 1,
+ UINT64_MAX, 0, 0);
+ expect_open(ino_in, 0, 1);
+ FuseTest::expect_lookup(RELPATH_OUT, ino_out, S_IFREG | oldmode, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino_out, 0, 1);
+ expect_copy_file_range(ino_in, off_in, ino_out, off_out, len);
+ expect_chmod(ino_out, newmode, fsize);
+
+ fd_in = open(FULLPATH_IN, O_RDONLY);
+ ASSERT_LE(0, fd_in) << strerror(errno);
+ fd_out = open(FULLPATH_OUT, O_WRONLY);
+ ASSERT_LE(0, fd_out) << strerror(errno);
+ ASSERT_EQ(len,
+ copy_file_range(fd_in, &off_in, fd_out, &off_out, len, 0))
+ << strerror(errno);
+ ASSERT_EQ(0, fstat(fd_out, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+ ASSERT_EQ(0, fstat(fd_in, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | oldmode, sb.st_mode);
+
+ leak(fd_in);
+ leak(fd_out);
+}
+
TEST_F(Create, ok)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
@@ -1311,5 +1419,3 @@
ASSERT_EQ(1, write(fd, wbuf, sizeof(wbuf))) << strerror(errno);
leak(fd);
}
-
-
Index: tests/sys/fs/fusefs/mockfs.hh
===================================================================
--- tests/sys/fs/fusefs/mockfs.hh
+++ tests/sys/fs/fusefs/mockfs.hh
@@ -157,6 +157,7 @@
uint8_t bytes[
max_max_write + 0x1000 - sizeof(struct fuse_in_header)
];
+ fuse_copy_file_range_in copy_file_range;
fuse_create_in create;
fuse_flush_in flush;
fuse_fsync_in fsync;
Index: tests/sys/fs/fusefs/mockfs.cc
===================================================================
--- tests/sys/fs/fusefs/mockfs.cc
+++ tests/sys/fs/fusefs/mockfs.cc
@@ -62,7 +62,7 @@
const char* opcode2opname(uint32_t opcode)
{
- const int NUM_OPS = 47;
+ const int NUM_OPS = 48;
const char* table[NUM_OPS] = {
"Unknown (opcode 0)",
"LOOKUP",
@@ -111,6 +111,7 @@
"READDIRPLUS",
"RENAME2",
"LSEEK",
+ "COPY_FILE_RANGE",
};
if (opcode >= NUM_OPS)
return ("Unknown (opcode > max)");
@@ -183,6 +184,20 @@
printf(" block=%" PRIx64 " blocksize=%#x",
in.body.bmap.block, in.body.bmap.blocksize);
break;
+ case FUSE_COPY_FILE_RANGE:
+ printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
+ " off_out=%" PRIu64 " size=%" PRIu64,
+ in.body.copy_file_range.off_in,
+ in.body.copy_file_range.nodeid_out,
+ in.body.copy_file_range.off_out,
+ in.body.copy_file_range.len);
+ if (verbosity > 1)
+ printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
+ " flags=%" PRIx64,
+ in.body.copy_file_range.fh_in,
+ in.body.copy_file_range.fh_out,
+ in.body.copy_file_range.flags);
+ break;
case FUSE_CREATE:
if (m_kernel_minor_version >= 12)
name = (const char*)in.body.bytes +
@@ -664,6 +679,11 @@
EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
EXPECT_EQ((size_t)buflen, inlen);
break;
+ case FUSE_COPY_FILE_RANGE:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
+ EXPECT_EQ(0ul, in.body.copy_file_range.flags);
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
case FUSE_NOTIFY_REPLY:
case FUSE_BATCH_FORGET:
case FUSE_FALLOCATE:
Index: tests/sys/fs/fusefs/utils.cc
===================================================================
--- tests/sys/fs/fusefs/utils.cc
+++ tests/sys/fs/fusefs/utils.cc
@@ -376,10 +376,10 @@
in.body.read.fh == FH &&
in.body.read.offset == offset &&
in.body.read.size == isize &&
- flags == -1 ?
+ (flags == -1 ?
(in.body.read.flags == O_RDONLY ||
in.body.read.flags == O_RDWR)
- : in.body.read.flags == (uint32_t)flags);
+ : in.body.read.flags == (uint32_t)flags));
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
Index: tests/sys/fs/fusefs/write.cc
===================================================================
--- tests/sys/fs/fusefs/write.cc
+++ tests/sys/fs/fusefs/write.cc
@@ -508,17 +508,14 @@
const char *INITIAL = "XXXXXXXXXX";
uint64_t ino = 42;
uint64_t offset = 1;
- ssize_t bufsize = strlen(CONTENTS);
+ ssize_t bufsize = strlen(CONTENTS) + 1;
off_t orig_fsize = 10;
off_t truncated_fsize = 5;
- off_t final_fsize = bufsize;
int fd;
FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
expect_open(ino, 0, 1);
expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
- expect_getattr(ino, truncated_fsize);
- expect_read(ino, 0, final_fsize, final_fsize, INITIAL, O_RDWR);
maybe_expect_write(ino, offset, bufsize, CONTENTS);
fd = open(FULLPATH, O_RDWR);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Apr 6, 10:15 AM (5 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30958619
Default Alt Text
D27818.id81324.diff (21 KB)
Attached To
Mode
D27818: fusefs: implement FUSE_COPY_FILE_RANGE.
Attached
Detach File
Event Timeline
Log In to Comment