diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c index 60cbfe4c7f4e..25a40abfb2ee 100644 --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -1134,9 +1134,20 @@ static int fuse_vnop_mknod(struct vop_mknod_args *ap) { - return (EINVAL); -} + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct vattr *vap = ap->a_vap; + struct fuse_mknod_in fmni; + if (fuse_isdeadfs(dvp)) + return ENXIO; + + fmni.mode = MAKEIMODE(vap->va_type, vap->va_mode); + fmni.rdev = vap->va_rdev; + return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKNOD, &fmni, + sizeof(fmni), vap->va_type)); +} /* struct vnop_open_args { @@ -1161,9 +1172,10 @@ fuse_vnop_open(struct vop_open_args *ap) int error, isdir = 0; int32_t fuse_open_flags; - if (fuse_isdeadfs(vp)) { + if (fuse_isdeadfs(vp)) return ENXIO; - } + if (vp->v_type == VCHR || vp->v_type == VBLK || vp->v_type == VFIFO) + return (EOPNOTSUPP); if ((mode & (FREAD | FWRITE)) == 0) return EINVAL; diff --git a/tests/sys/fs/fusefs/mknod.cc b/tests/sys/fs/fusefs/mknod.cc index f77a91bfe3cc..a61ce44ae60e 100644 --- a/tests/sys/fs/fusefs/mknod.cc +++ b/tests/sys/fs/fusefs/mknod.cc @@ -37,6 +37,13 @@ extern "C" { using namespace testing; +#ifndef VNOVAL +#define VNOVAL (-1) /* Defined in sys/vnode.h */ +#endif + +const char FULLPATH[] = "mountpoint/some_file.txt"; +const char RELPATH[] = "some_file.txt"; + class Mknod: public FuseTest { public: @@ -50,9 +57,7 @@ virtual void SetUp() { } /* Test an OK creation of a file with the given mode and device number */ -void test_ok(mode_t mode, dev_t dev) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; +void expect_mknod(mode_t mode, dev_t dev) { uint64_t ino = 42; EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); @@ -63,19 +68,18 @@ void test_ok(mode_t mode, dev_t dev) { sizeof(fuse_mknod_in); return (in->header.opcode == FUSE_MKNOD && in->body.mknod.mode == mode && - in->body.mknod.rdev == dev && + in->body.mknod.rdev == (uint32_t)dev && (0 == strcmp(RELPATH, name))); }, Eq(true)), _) ).WillOnce(Invoke(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; - out->body.create.entry.attr.rdev = dev; + 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; }))); - EXPECT_EQ(0, mknod(FULLPATH, mode, dev)) << strerror(errno); } }; @@ -85,27 +89,28 @@ void test_ok(mode_t mode, dev_t dev) { * though FreeBSD doesn't use block devices, this is useful when copying media * from or preparing media for other operating systems. */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_blk) +TEST_F(Mknod, blk) { - test_ok(S_IFBLK | 0755, 0xfe00); /* /dev/vda's device number on Linux */ + mode_t mode = S_IFBLK | 0755; + dev_t rdev = 0xfe00; /* /dev/vda's device number on Linux */ + expect_mknod(mode, rdev); + EXPECT_EQ(0, mknod(FULLPATH, mode, rdev)) << strerror(errno); } -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_chr) +TEST_F(Mknod, chr) { - test_ok(S_IFCHR | 0755, 0x64); /* /dev/fuse's device number */ + mode_t mode = S_IFCHR | 0755; + dev_t rdev = 54; /* /dev/fuse's device number */ + expect_mknod(mode, rdev); + EXPECT_EQ(0, mknod(FULLPATH, mode, rdev)) << strerror(errno); } /* * The daemon is responsible for checking file permissions (unless the * default_permissions mount option was used) */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_eperm) +TEST_F(Mknod, eperm) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; mode_t mode = S_IFIFO | 0755; EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); @@ -120,19 +125,26 @@ TEST_F(Mknod, DISABLED_eperm) }, Eq(true)), _) ).WillOnce(Invoke(ReturnErrno(EPERM))); - EXPECT_NE(0, mknod(FULLPATH, mode, 0)); + EXPECT_NE(0, mkfifo(FULLPATH, mode)); EXPECT_EQ(EPERM, errno); } - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_fifo) +TEST_F(Mknod, fifo) { - test_ok(S_IFIFO | 0755, 0); + mode_t mode = S_IFIFO | 0755; + dev_t rdev = VNOVAL; /* Fifos don't have device numbers */ + expect_mknod(mode, rdev); + EXPECT_EQ(0, mkfifo(FULLPATH, mode)) << strerror(errno); } -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ +/* + * fusefs(5) lacks VOP_WHITEOUT support. No bugzilla entry, because that's a + * feature, not a bug + */ TEST_F(Mknod, DISABLED_whiteout) { - test_ok(S_IFWHT | 0755, 0); + mode_t mode = S_IFWHT | 0755; + dev_t rdev = VNOVAL; /* whiteouts don't have device numbers */ + expect_mknod(mode, rdev); + EXPECT_EQ(0, mknod(FULLPATH, mode, 0)) << strerror(errno); } diff --git a/tests/sys/fs/fusefs/mockfs.cc b/tests/sys/fs/fusefs/mockfs.cc index d010f5a870b6..41a0ae97f76e 100644 --- a/tests/sys/fs/fusefs/mockfs.cc +++ b/tests/sys/fs/fusefs/mockfs.cc @@ -175,6 +175,10 @@ void debug_fuseop(const mockfs_buf_in *in) case FUSE_LOOKUP: printf(" %s", in->body.lookup); break; + case FUSE_MKNOD: + printf(" mode=%#o rdev=%x", in->body.mknod.mode, + in->body.mknod.rdev); + break; case FUSE_OPEN: printf(" flags=%#x mode=%#o", in->body.open.flags, in->body.open.mode); diff --git a/tests/sys/fs/fusefs/open.cc b/tests/sys/fs/fusefs/open.cc index 220f2a79818d..f54280a18d98 100644 --- a/tests/sys/fs/fusefs/open.cc +++ b/tests/sys/fs/fusefs/open.cc @@ -81,6 +81,30 @@ void test_ok(int os_flags, int fuse_flags) { }; +/* + * fusefs(5) does not support I/O on device nodes (neither does UFS). But it + * shouldn't crash + */ +TEST_F(Open, chr) +{ + const char FULLPATH[] = "mountpoint/zero"; + const char RELPATH[] = "zero"; + uint64_t ino = 42; + + EXPECT_LOOKUP(1, RELPATH) + .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFCHR | 0644; + out->body.entry.nodeid = ino; + out->body.entry.attr.nlink = 1; + out->body.entry.attr_valid = UINT64_MAX; + out->body.entry.attr.rdev = 44; /* /dev/zero's rdev */ + }))); + + ASSERT_EQ(-1, open(FULLPATH, O_RDONLY)); + EXPECT_EQ(EOPNOTSUPP, errno); +} + /* * The fuse daemon fails the request with enoent. This usually indicates a * race condition: some other FUSE client removed the file in between when the @@ -126,6 +150,26 @@ TEST_F(Open, eperm) EXPECT_EQ(EPERM, errno); } +/* fusefs(5) does not yet support I/O on fifos. But it shouldn't crash. */ +TEST_F(Open, fifo) +{ + const char FULLPATH[] = "mountpoint/zero"; + const char RELPATH[] = "zero"; + uint64_t ino = 42; + + EXPECT_LOOKUP(1, RELPATH) + .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFIFO | 0644; + out->body.entry.nodeid = ino; + out->body.entry.attr.nlink = 1; + out->body.entry.attr_valid = UINT64_MAX; + }))); + + ASSERT_EQ(-1, open(FULLPATH, O_RDONLY)); + EXPECT_EQ(EOPNOTSUPP, errno); +} + /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ TEST_F(Open, DISABLED_o_append) {