fusefs: raise protocol level to 7.12
This commit raises the protocol level and adds backwards-compatibility code to handle structure size changes. It doesn't implement any new features. The new features added in protocol 7.12 are: * server-side umask processing (which FreeBSD won't do) * asynchronous inode and directory entry invalidation (which I'll do next) Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
e039bafa87
commit
a4856c96d0
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=348365
@ -331,12 +331,22 @@ int
|
||||
fuse_internal_mknod(struct vnode *dvp, struct vnode **vpp,
|
||||
struct componentname *cnp, struct vattr *vap)
|
||||
{
|
||||
struct fuse_data *data;
|
||||
struct fuse_mknod_in fmni;
|
||||
size_t insize;
|
||||
|
||||
data = fuse_get_mpdata(dvp->v_mount);
|
||||
|
||||
fmni.mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
||||
fmni.rdev = vap->va_rdev;
|
||||
if (fuse_libabi_geq(data, 7, 12)) {
|
||||
insize = sizeof(fmni);
|
||||
fmni.umask = curthread->td_proc->p_fd->fd_cmask;
|
||||
} else {
|
||||
insize = FUSE_COMPAT_MKNOD_IN_SIZE;
|
||||
}
|
||||
return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKNOD, &fmni,
|
||||
sizeof(fmni), vap->va_type));
|
||||
insize, vap->va_type));
|
||||
}
|
||||
|
||||
/* readdir */
|
||||
@ -824,6 +834,8 @@ fuse_internal_send_init(struct fuse_data *data, struct thread *td)
|
||||
* Unsupported features:
|
||||
* FUSE_FILE_OPS: No known FUSE server or client supports it
|
||||
* FUSE_ATOMIC_O_TRUNC: our VFS cannot support it
|
||||
* FUSE_DONT_MASK: unlike Linux, FreeBSD always applies the umask, even
|
||||
* when default ACLs are in use.
|
||||
*/
|
||||
fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT
|
||||
| FUSE_BIG_WRITES;
|
||||
|
@ -52,6 +52,11 @@
|
||||
* 7.11
|
||||
* - add IOCTL message
|
||||
* - add unsolicited notification support
|
||||
*
|
||||
* 7.12
|
||||
* - add umask flag to input argument of open, mknod and mkdir
|
||||
* - add notification messages for invalidation of inodes and
|
||||
* directory entries
|
||||
*/
|
||||
|
||||
#ifndef _FUSE_FUSE_KERNEL_H
|
||||
@ -60,6 +65,7 @@
|
||||
#ifndef linux
|
||||
#include <sys/types.h>
|
||||
#define __u64 uint64_t
|
||||
#define __s64 int64_t
|
||||
#define __u32 uint32_t
|
||||
#define __s32 int32_t
|
||||
#else
|
||||
@ -70,7 +76,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 11
|
||||
#define FUSE_KERNEL_MINOR_VERSION 12
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@ -146,6 +152,7 @@ struct fuse_file_lock {
|
||||
* INIT request/reply flags
|
||||
*
|
||||
* FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
|
||||
* FUSE_DONT_MASK: don't apply umask to file mode on create operations
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
@ -153,6 +160,7 @@ struct fuse_file_lock {
|
||||
#define FUSE_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_BIG_WRITES (1 << 5)
|
||||
#define FUSE_DONT_MASK (1 << 6)
|
||||
|
||||
#ifdef linux
|
||||
/**
|
||||
@ -262,6 +270,8 @@ enum fuse_opcode {
|
||||
|
||||
enum fuse_notify_code {
|
||||
FUSE_NOTIFY_POLL = 1,
|
||||
FUSE_NOTIFY_INVAL_INODE = 2,
|
||||
FUSE_NOTIFY_INVAL_ENTRY = 3,
|
||||
FUSE_NOTIFY_CODE_MAX,
|
||||
};
|
||||
|
||||
@ -300,14 +310,18 @@ struct fuse_attr_out {
|
||||
struct fuse_attr attr;
|
||||
};
|
||||
|
||||
#define FUSE_COMPAT_MKNOD_IN_SIZE 8
|
||||
|
||||
struct fuse_mknod_in {
|
||||
__u32 mode;
|
||||
__u32 rdev;
|
||||
__u32 umask;
|
||||
__u32 padding;
|
||||
};
|
||||
|
||||
struct fuse_mkdir_in {
|
||||
__u32 mode;
|
||||
__u32 padding;
|
||||
__u32 umask;
|
||||
};
|
||||
|
||||
struct fuse_rename_in {
|
||||
@ -338,8 +352,15 @@ struct fuse_setattr_in {
|
||||
};
|
||||
|
||||
struct fuse_open_in {
|
||||
__u32 flags;
|
||||
__u32 unused;
|
||||
};
|
||||
|
||||
struct fuse_create_in {
|
||||
__u32 flags;
|
||||
__u32 mode;
|
||||
__u32 umask;
|
||||
__u32 padding;
|
||||
};
|
||||
|
||||
struct fuse_open_out {
|
||||
@ -558,4 +579,16 @@ struct fuse_dirent {
|
||||
#define FUSE_DIRENT_SIZE(d) \
|
||||
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
|
||||
|
||||
struct fuse_notify_inval_inode_out {
|
||||
__u64 ino;
|
||||
__s64 off;
|
||||
__s64 len;
|
||||
};
|
||||
|
||||
struct fuse_notify_inval_entry_out {
|
||||
__u64 parent;
|
||||
__u32 namelen;
|
||||
__u32 padding;
|
||||
};
|
||||
|
||||
#endif /* _FUSE_FUSE_KERNEL_H */
|
||||
|
@ -540,7 +540,8 @@ fuse_vnop_create(struct vop_create_args *ap)
|
||||
struct thread *td = cnp->cn_thread;
|
||||
struct ucred *cred = cnp->cn_cred;
|
||||
|
||||
struct fuse_open_in *foi;
|
||||
struct fuse_data *data;
|
||||
struct fuse_create_in *fci;
|
||||
struct fuse_entry_out *feo;
|
||||
struct fuse_open_out *foo;
|
||||
struct fuse_dispatcher fdi, fdi2;
|
||||
@ -550,6 +551,7 @@ fuse_vnop_create(struct vop_create_args *ap)
|
||||
int err;
|
||||
|
||||
struct mount *mp = vnode_mount(dvp);
|
||||
data = fuse_get_mpdata(mp);
|
||||
uint64_t parentnid = VTOFUD(dvp)->nid;
|
||||
mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
||||
enum fuse_opcode op;
|
||||
@ -580,15 +582,24 @@ fuse_vnop_create(struct vop_create_args *ap)
|
||||
cred, mode, &op);
|
||||
} else {
|
||||
/* Use FUSE_CREATE */
|
||||
size_t insize;
|
||||
|
||||
op = FUSE_CREATE;
|
||||
fdisp_init(fdip, sizeof(*foi) + cnp->cn_namelen + 1);
|
||||
fdisp_init(fdip, sizeof(*fci) + cnp->cn_namelen + 1);
|
||||
fdisp_make(fdip, op, vnode_mount(dvp), parentnid, td, cred);
|
||||
foi = fdip->indata;
|
||||
foi->mode = mode;
|
||||
foi->flags = O_CREAT | flags;
|
||||
memcpy((char *)fdip->indata + sizeof(*foi), cnp->cn_nameptr,
|
||||
fci = fdip->indata;
|
||||
fci->mode = mode;
|
||||
fci->flags = O_CREAT | flags;
|
||||
if (fuse_libabi_geq(data, 7, 12)) {
|
||||
insize = sizeof(*fci);
|
||||
fci->umask = td->td_proc->p_fd->fd_cmask;
|
||||
} else {
|
||||
insize = sizeof(struct fuse_open_in);
|
||||
}
|
||||
|
||||
memcpy((char *)fdip->indata + insize, cnp->cn_nameptr,
|
||||
cnp->cn_namelen);
|
||||
((char *)fdip->indata)[sizeof(*foi) + cnp->cn_namelen] = '\0';
|
||||
((char *)fdip->indata)[insize + cnp->cn_namelen] = '\0';
|
||||
}
|
||||
|
||||
err = fdisp_wait_answ(fdip);
|
||||
@ -614,12 +625,13 @@ fuse_vnop_create(struct vop_create_args *ap)
|
||||
foo = (struct fuse_open_out*)(feo + 1);
|
||||
} else {
|
||||
/* Issue a separate FUSE_OPEN */
|
||||
struct fuse_open_in *foi;
|
||||
|
||||
fdip2 = &fdi2;
|
||||
fdisp_init(fdip2, sizeof(*foi));
|
||||
fdisp_make(fdip2, FUSE_OPEN, vnode_mount(dvp), feo->nodeid, td,
|
||||
cred);
|
||||
foi = fdip2->indata;
|
||||
foi->mode = mode;
|
||||
foi->flags = flags;
|
||||
err = fdisp_wait_answ(fdip2);
|
||||
if (err)
|
||||
@ -1162,6 +1174,7 @@ fuse_vnop_mkdir(struct vop_mkdir_args *ap)
|
||||
return ENXIO;
|
||||
}
|
||||
fmdi.mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
||||
fmdi.umask = curthread->td_proc->p_fd->fd_cmask;
|
||||
|
||||
return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKDIR, &fmdi,
|
||||
sizeof(fmdi), VDIR));
|
||||
|
@ -42,12 +42,16 @@ public:
|
||||
|
||||
void expect_create(const char *relpath, mode_t mode, ProcessMockerT r)
|
||||
{
|
||||
mode_t mask = umask(0);
|
||||
(void)umask(mask);
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
const char *name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_open_in);
|
||||
sizeof(fuse_create_in);
|
||||
return (in.header.opcode == FUSE_CREATE &&
|
||||
in.body.open.mode == mode &&
|
||||
in.body.create.mode == mode &&
|
||||
in.body.create.umask == mask &&
|
||||
(0 == strcmp(relpath, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
@ -63,6 +67,45 @@ virtual void SetUp() {
|
||||
m_kernel_minor_version = 8;
|
||||
Create::SetUp();
|
||||
}
|
||||
|
||||
void expect_create(const char *relpath, mode_t mode, ProcessMockerT r)
|
||||
{
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
const char *name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_open_in);
|
||||
return (in.header.opcode == FUSE_CREATE &&
|
||||
in.body.create.mode == mode &&
|
||||
(0 == strcmp(relpath, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(r));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* FUSE_CREATE operations for a server built at protocol <= 7.11 */
|
||||
class Create_7_11: public FuseTest {
|
||||
public:
|
||||
virtual void SetUp() {
|
||||
m_kernel_minor_version = 11;
|
||||
FuseTest::SetUp();
|
||||
}
|
||||
|
||||
void expect_create(const char *relpath, mode_t mode, ProcessMockerT r)
|
||||
{
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
const char *name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_open_in);
|
||||
return (in.header.opcode == FUSE_CREATE &&
|
||||
in.body.create.mode == mode &&
|
||||
(0 == strcmp(relpath, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(r));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -372,4 +415,25 @@ TEST_F(Create_7_8, ok)
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
||||
|
||||
TEST_F(Create_7_11, ok)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
mode_t mode = S_IFREG | 0755;
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_create(RELPATH, mode,
|
||||
ReturnImmediate([=](auto in __unused, auto& out) {
|
||||
SET_OUT_HEADER_LEN(out, create);
|
||||
out.body.create.entry.attr.mode = mode;
|
||||
out.body.create.entry.nodeid = ino;
|
||||
out.body.create.entry.entry_valid = UINT64_MAX;
|
||||
out.body.create.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
|
||||
EXPECT_LE(0, fd) << strerror(errno);
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ void expect_create(const char *relpath, uint64_t ino)
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
const char *name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_open_in);
|
||||
sizeof(fuse_create_in);
|
||||
return (in.header.opcode == FUSE_CREATE &&
|
||||
(0 == strcmp(relpath, name)));
|
||||
}, Eq(true)),
|
||||
|
@ -159,6 +159,10 @@ TEST_F(Mkdir, ok)
|
||||
const char RELPATH[] = "some_dir";
|
||||
mode_t mode = 0755;
|
||||
uint64_t ino = 42;
|
||||
mode_t mask;
|
||||
|
||||
mask = umask(0);
|
||||
(void)umask(mask);
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
|
||||
@ -168,6 +172,7 @@ TEST_F(Mkdir, ok)
|
||||
sizeof(fuse_mkdir_in);
|
||||
return (in.header.opcode == FUSE_MKDIR &&
|
||||
in.body.mkdir.mode == (S_IFDIR | mode) &&
|
||||
in.body.mkdir.umask == mask &&
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
|
@ -48,14 +48,23 @@ const char RELPATH[] = "some_file.txt";
|
||||
|
||||
class Mknod: public FuseTest {
|
||||
|
||||
mode_t m_oldmask;
|
||||
const static mode_t c_umask = 022;
|
||||
|
||||
public:
|
||||
|
||||
virtual void SetUp() {
|
||||
FuseTest::SetUp();
|
||||
|
||||
m_oldmask = umask(c_umask);
|
||||
printf("m_oldmask=%#o\n", m_oldmask);
|
||||
if (geteuid() != 0) {
|
||||
GTEST_SKIP() << "Only root may use most mknod(2) variations";
|
||||
}
|
||||
FuseTest::SetUp();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
FuseTest::TearDown();
|
||||
(void)umask(m_oldmask);
|
||||
}
|
||||
|
||||
/* Test an OK creation of a file with the given mode and device number */
|
||||
@ -68,6 +77,50 @@ void expect_mknod(mode_t mode, dev_t dev) {
|
||||
ResultOf([=](auto in) {
|
||||
const char *name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_mknod_in);
|
||||
return (in.header.opcode == FUSE_MKNOD &&
|
||||
in.body.mknod.mode == mode &&
|
||||
in.body.mknod.rdev == (uint32_t)dev &&
|
||||
in.body.mknod.umask == c_umask &&
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out.body.entry.attr.mode = mode;
|
||||
out.body.entry.nodeid = ino;
|
||||
out.body.entry.entry_valid = UINT64_MAX;
|
||||
out.body.entry.attr_valid = UINT64_MAX;
|
||||
out.body.entry.attr.rdev = dev;
|
||||
})));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class Mknod_7_11: public FuseTest {
|
||||
public:
|
||||
virtual void SetUp() {
|
||||
m_kernel_minor_version = 11;
|
||||
if (geteuid() != 0) {
|
||||
GTEST_SKIP() << "Only root may use most mknod(2) variations";
|
||||
}
|
||||
FuseTest::SetUp();
|
||||
}
|
||||
|
||||
void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
|
||||
{
|
||||
FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
|
||||
}
|
||||
|
||||
/* Test an OK creation of a file with the given mode and device number */
|
||||
void expect_mknod(mode_t mode, dev_t dev) {
|
||||
uint64_t ino = 42;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
const char *name = (const char*)in.body.bytes +
|
||||
FUSE_COMPAT_MKNOD_IN_SIZE;
|
||||
return (in.header.opcode == FUSE_MKNOD &&
|
||||
in.body.mknod.mode == mode &&
|
||||
in.body.mknod.rdev == (uint32_t)dev &&
|
||||
@ -172,3 +225,12 @@ TEST_F(Mknod, DISABLED_whiteout)
|
||||
expect_mknod(mode, rdev);
|
||||
EXPECT_EQ(0, mknod(FULLPATH, mode, 0)) << strerror(errno);
|
||||
}
|
||||
|
||||
/* A server built at protocol version 7.11 or earlier can still use mknod */
|
||||
TEST_F(Mknod_7_11, fifo)
|
||||
{
|
||||
mode_t mode = S_IFIFO | 0755;
|
||||
dev_t rdev = VNOVAL;
|
||||
expect_mknod(mode, rdev);
|
||||
EXPECT_EQ(0, mkfifo(FULLPATH, mode)) << strerror(errno);
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ void sigint_handler(int __unused sig) {
|
||||
// Don't do anything except interrupt the daemon's read(2) call
|
||||
}
|
||||
|
||||
void debug_fuseop(const mockfs_buf_in &in)
|
||||
void MockFS::debug_fuseop(const mockfs_buf_in &in)
|
||||
{
|
||||
printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
|
||||
in.header.nodeid);
|
||||
@ -169,6 +169,10 @@ void debug_fuseop(const mockfs_buf_in &in)
|
||||
printf(" mask=%#x", in.body.access.mask);
|
||||
break;
|
||||
case FUSE_CREATE:
|
||||
if (m_kernel_minor_version >= 12)
|
||||
name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_create_in);
|
||||
else
|
||||
name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_open_in);
|
||||
printf(" flags=%#x name=%s",
|
||||
@ -200,19 +204,25 @@ void debug_fuseop(const mockfs_buf_in &in)
|
||||
case FUSE_MKDIR:
|
||||
name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_mkdir_in);
|
||||
printf(" name=%s mode=%#o", name, in.body.mkdir.mode);
|
||||
printf(" name=%s mode=%#o umask=%#o", name,
|
||||
in.body.mkdir.mode, in.body.mkdir.umask);
|
||||
break;
|
||||
case FUSE_MKNOD:
|
||||
printf(" mode=%#o rdev=%x", in.body.mknod.mode,
|
||||
in.body.mknod.rdev);
|
||||
if (m_kernel_minor_version >= 12)
|
||||
name = (const char*)in.body.bytes +
|
||||
sizeof(fuse_mknod_in);
|
||||
else
|
||||
name = (const char*)in.body.bytes +
|
||||
FUSE_COMPAT_MKNOD_IN_SIZE;
|
||||
printf(" mode=%#o rdev=%x umask=%#o name=%s",
|
||||
in.body.mknod.mode, in.body.mknod.rdev,
|
||||
in.body.mknod.umask, name);
|
||||
break;
|
||||
case FUSE_OPEN:
|
||||
printf(" flags=%#x mode=%#o",
|
||||
in.body.open.flags, in.body.open.mode);
|
||||
printf(" flags=%#x", in.body.open.flags);
|
||||
break;
|
||||
case FUSE_OPENDIR:
|
||||
printf(" flags=%#x mode=%#o",
|
||||
in.body.opendir.flags, in.body.opendir.mode);
|
||||
printf(" flags=%#x", in.body.opendir.flags);
|
||||
break;
|
||||
case FUSE_READ:
|
||||
printf(" offset=%" PRIu64 " size=%u",
|
||||
|
@ -126,6 +126,7 @@ union fuse_payloads_in {
|
||||
fuse_access_in access;
|
||||
/* value is from fuse_kern_chan.c in fusefs-libs */
|
||||
uint8_t bytes[0x21000 - sizeof(struct fuse_in_header)];
|
||||
fuse_create_in create;
|
||||
fuse_flush_in flush;
|
||||
fuse_fsync_in fsync;
|
||||
fuse_fsync_in fsyncdir;
|
||||
@ -257,6 +258,8 @@ class MockFS {
|
||||
/* Method the daemon should use for I/O to and from /dev/fuse */
|
||||
enum poll_method m_pm;
|
||||
|
||||
void debug_fuseop(const mockfs_buf_in&);
|
||||
|
||||
/* Initialize a session after mounting */
|
||||
void init(uint32_t flags);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user