fusefs: create sockets with FUSE_MKNOD, not FUSE_CREATE

libfuse expects sockets to be created with FUSE_MKNOD, not FUSE_CREATE,
because that's how Linux does it.  My first attempt at creating sockets
(r346894) used FUSE_CREATE because FreeBSD uses VOP_CREATE for this purpose.
There are no backwards-compatibility concerns with this change, because
socket support hasn't yet been merged to head.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-05-09 16:25:01 +00:00
parent 002e54b0aa
commit d5ff268834
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=347395
6 changed files with 59 additions and 46 deletions

View File

@ -312,6 +312,19 @@ fuse_internal_fsync(struct vnode *vp,
return err;
}
/* mknod */
int
fuse_internal_mknod(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vattr *vap)
{
struct fuse_mknod_in fmni;
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));
}
/* readdir */
int

View File

@ -228,6 +228,11 @@ struct pseudo_dirent {
uint32_t d_namlen;
};
/* mknod */
int fuse_internal_mknod(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vattr *vap);
/* readdir */
int fuse_internal_readdir(struct vnode *vp, struct uio *uio,
struct fuse_filehandle *fufh, struct fuse_iov *cookediov);
int fuse_internal_readdir_processdata(struct uio *uio, size_t reqsize,

View File

@ -537,6 +537,13 @@ fuse_vnop_create(struct vop_create_args *ap)
enum fuse_opcode op;
int flags;
if (fuse_isdeadfs(dvp))
return ENXIO;
/* FUSE expects sockets to be created with FUSE_MKNOD */
if (vap->va_type == VSOCK)
return fuse_internal_mknod(dvp, vpp, cnp, vap);
/*
* VOP_CREATE doesn't tell us the open(2) flags, so we guess. Only a
* writable mode makes sense, and we might as well include readability
@ -544,15 +551,12 @@ fuse_vnop_create(struct vop_create_args *ap)
*/
flags = O_RDWR;
if (fuse_isdeadfs(dvp)) {
return ENXIO;
}
bzero(&fdi, sizeof(fdi));
if ((vap->va_type != VREG && vap->va_type != VSOCK))
if (vap->va_type != VREG)
return (EINVAL);
if (!fsess_isimpl(mp, FUSE_CREATE)) {
if (!fsess_isimpl(mp, FUSE_CREATE) || vap->va_type == VSOCK) {
/* Fallback to FUSE_MKNOD/FUSE_OPEN */
fdisp_make_mknod_for_fallback(fdip, cnp, dvp, parentnid, td,
cred, mode, &op);
@ -1163,15 +1167,11 @@ fuse_vnop_mknod(struct vop_mknod_args *ap)
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));
return fuse_internal_mknod(dvp, vpp, cnp, vap);
}
/*

View File

@ -30,8 +30,6 @@
extern "C" {
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
}
#include "mockfs.hh"
@ -310,34 +308,6 @@ TEST_F(Create, ok)
/* Deliberately leak fd. close(2) will be tested in release.cc */
}
/* Create a unix-domain socket */
TEST_F(Create, socket)
{
const char FULLPATH[] = "mountpoint/some_sock";
const char RELPATH[] = "some_sock";
mode_t mode = S_IFSOCK | 0755;
struct sockaddr_un sa;
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 = socket(AF_UNIX, SOCK_STREAM, 0);
ASSERT_LE(0, fd) << strerror(errno);
sa.sun_family = AF_UNIX;
strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path));
ASSERT_EQ(0, bind(fd, (struct sockaddr*)&sa, sizeof(sa)))
<< strerror(errno);
}
/*
* A regression test for a bug that affected old FUSE implementations:
* open(..., O_WRONLY | O_CREAT, 0444) should work despite the seeming

View File

@ -159,17 +159,18 @@ TEST_F(Socket, read_write)
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_CREATE);
return (in->header.opcode == FUSE_MKNOD);
}, Eq(true)),
_)
).InSequence(seq)
.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;
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;
})));
EXPECT_LOOKUP(1, RELPATH)
.InSequence(seq)
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {

View File

@ -30,6 +30,8 @@
extern "C" {
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
}
#include "mockfs.hh"
@ -137,6 +139,28 @@ TEST_F(Mknod, fifo)
EXPECT_EQ(0, mkfifo(FULLPATH, mode)) << strerror(errno);
}
/*
* Create a unix-domain socket.
*
* This test case doesn't actually need root privileges.
*/
TEST_F(Mknod, socket)
{
mode_t mode = S_IFSOCK | 0755;
struct sockaddr_un sa;
int fd;
dev_t rdev = -1; /* Really it's a don't care */
expect_mknod(mode, rdev);
fd = socket(AF_UNIX, SOCK_STREAM, 0);
ASSERT_LE(0, fd) << strerror(errno);
sa.sun_family = AF_UNIX;
strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path));
ASSERT_EQ(0, bind(fd, (struct sockaddr*)&sa, sizeof(sa)))
<< strerror(errno);
}
/*
* fusefs(5) lacks VOP_WHITEOUT support. No bugzilla entry, because that's a
* feature, not a bug