fusefs: fallback to MKNOD/OPEN if a filesystem doesn't support CREATE
If a FUSE filesystem returns ENOSYS for FUSE_CREATE, then fallback to FUSE_MKNOD/FUSE_OPEN. Also, fix a memory leak in the error path of fuse_vnop_create. And do a little cleanup in fuse_vnop_open. PR: 199934 Reported by: samm@os2.kiev.ua Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
bf4d70841f
commit
19ef317d62
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=345429
@ -309,6 +309,29 @@ fuse_vnop_close(struct vop_close_args *ap)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fdisp_make_mknod_for_fallback(
|
||||
struct fuse_dispatcher *fdip,
|
||||
struct componentname *cnp,
|
||||
struct vnode *dvp,
|
||||
uint64_t parentnid,
|
||||
struct thread *td,
|
||||
struct ucred *cred,
|
||||
mode_t mode,
|
||||
enum fuse_opcode *op)
|
||||
{
|
||||
struct fuse_mknod_in *fmni;
|
||||
|
||||
fdisp_init(fdip, sizeof(*fmni) + cnp->cn_namelen + 1);
|
||||
*op = FUSE_MKNOD;
|
||||
fdisp_make(fdip, *op, vnode_mount(dvp), parentnid, td, cred);
|
||||
fmni = fdip->indata;
|
||||
fmni->mode = mode;
|
||||
fmni->rdev = 0;
|
||||
memcpy((char *)fdip->indata + sizeof(*fmni), cnp->cn_nameptr,
|
||||
cnp->cn_namelen);
|
||||
((char *)fdip->indata)[sizeof(*fmni) + cnp->cn_namelen] = '\0';
|
||||
}
|
||||
/*
|
||||
struct vnop_create_args {
|
||||
struct vnode *a_dvp;
|
||||
@ -329,51 +352,54 @@ fuse_vnop_create(struct vop_create_args *ap)
|
||||
|
||||
struct fuse_open_in *foi;
|
||||
struct fuse_entry_out *feo;
|
||||
struct fuse_dispatcher fdi;
|
||||
struct fuse_open_out *foo;
|
||||
struct fuse_dispatcher fdi, fdi2;
|
||||
struct fuse_dispatcher *fdip = &fdi;
|
||||
struct fuse_dispatcher *fdip2 = NULL;
|
||||
|
||||
int err;
|
||||
|
||||
struct mount *mp = vnode_mount(dvp);
|
||||
uint64_t parentnid = VTOFUD(dvp)->nid;
|
||||
mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
||||
uint64_t x_fh_id;
|
||||
uint32_t x_open_flags;
|
||||
enum fuse_opcode op;
|
||||
|
||||
if (fuse_isdeadfs(dvp)) {
|
||||
return ENXIO;
|
||||
}
|
||||
bzero(&fdi, sizeof(fdi));
|
||||
|
||||
/* XXX: Will we ever want devices ? */
|
||||
if ((vap->va_type != VREG)) {
|
||||
printf("fuse_vnop_create: unsupported va_type %d\n",
|
||||
vap->va_type);
|
||||
if ((vap->va_type != VREG))
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
fdisp_init(fdip, sizeof(*foi) + cnp->cn_namelen + 1);
|
||||
if (!fsess_isimpl(mp, FUSE_CREATE)) {
|
||||
SDT_PROBE2(fuse, , vnops, trace, 1,
|
||||
"eh, daemon doesn't implement create?");
|
||||
return (EINVAL);
|
||||
/* Fallback to FUSE_MKNOD/FUSE_OPEN */
|
||||
fdisp_make_mknod_for_fallback(fdip, cnp, dvp, parentnid, td,
|
||||
cred, mode, &op);
|
||||
} else {
|
||||
/* Use FUSE_CREATE */
|
||||
op = FUSE_CREATE;
|
||||
fdisp_init(fdip, sizeof(*foi) + cnp->cn_namelen + 1);
|
||||
fdisp_make(fdip, op, vnode_mount(dvp), parentnid, td, cred);
|
||||
foi = fdip->indata;
|
||||
foi->mode = mode;
|
||||
foi->flags = O_CREAT | O_RDWR;
|
||||
memcpy((char *)fdip->indata + sizeof(*foi), cnp->cn_nameptr,
|
||||
cnp->cn_namelen);
|
||||
((char *)fdip->indata)[sizeof(*foi) + cnp->cn_namelen] = '\0';
|
||||
}
|
||||
fdisp_make(fdip, FUSE_CREATE, vnode_mount(dvp), parentnid, td, cred);
|
||||
|
||||
foi = fdip->indata;
|
||||
foi->mode = mode;
|
||||
foi->flags = O_CREAT | O_RDWR;
|
||||
|
||||
memcpy((char *)fdip->indata + sizeof(*foi), cnp->cn_nameptr,
|
||||
cnp->cn_namelen);
|
||||
((char *)fdip->indata)[sizeof(*foi) + cnp->cn_namelen] = '\0';
|
||||
|
||||
err = fdisp_wait_answ(fdip);
|
||||
|
||||
if (err) {
|
||||
if (err == ENOSYS)
|
||||
if (err == ENOSYS && op == FUSE_CREATE) {
|
||||
fsess_set_notimpl(mp, FUSE_CREATE);
|
||||
goto out;
|
||||
fdisp_make_mknod_for_fallback(fdip, cnp, dvp,
|
||||
parentnid, td, cred, mode, &op);
|
||||
err = fdisp_wait_answ(fdip);
|
||||
}
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
feo = fdip->answ;
|
||||
@ -381,11 +407,28 @@ fuse_vnop_create(struct vop_create_args *ap)
|
||||
if ((err = fuse_internal_checkentry(feo, VREG))) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (op == FUSE_CREATE) {
|
||||
foo = (struct fuse_open_out*)(feo + 1);
|
||||
} else {
|
||||
/* Issue a separate FUSE_OPEN */
|
||||
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 = O_RDWR;
|
||||
err = fdisp_wait_answ(fdip2);
|
||||
if (err)
|
||||
goto out;
|
||||
foo = fdip2->answ;
|
||||
}
|
||||
err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, VREG);
|
||||
if (err) {
|
||||
struct fuse_release_in *fri;
|
||||
uint64_t nodeid = feo->nodeid;
|
||||
uint64_t fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
|
||||
uint64_t fh_id = foo->fh;
|
||||
|
||||
fdisp_init(fdip, sizeof(*fri));
|
||||
fdisp_make(fdip, FUSE_RELEASE, mp, nodeid, td, cred);
|
||||
@ -394,19 +437,17 @@ fuse_vnop_create(struct vop_create_args *ap)
|
||||
fri->flags = OFLAGS(mode);
|
||||
fuse_insert_callback(fdip->tick, fuse_internal_forget_callback);
|
||||
fuse_insert_message(fdip->tick);
|
||||
return err;
|
||||
goto out;
|
||||
}
|
||||
ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
|
||||
|
||||
fdip->answ = feo + 1;
|
||||
|
||||
x_fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
|
||||
x_open_flags = ((struct fuse_open_out *)(feo + 1))->open_flags;
|
||||
fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, x_fh_id);
|
||||
fuse_vnode_open(*vpp, x_open_flags, td);
|
||||
fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, foo->fh);
|
||||
fuse_vnode_open(*vpp, foo->open_flags, td);
|
||||
cache_purge_negative(dvp);
|
||||
|
||||
out:
|
||||
if (fdip2)
|
||||
fdisp_destroy(fdip2);
|
||||
fdisp_destroy(fdip);
|
||||
return err;
|
||||
}
|
||||
@ -1165,13 +1206,11 @@ fuse_vnop_open(struct vop_open_args *ap)
|
||||
int mode = ap->a_mode;
|
||||
struct thread *td = ap->a_td;
|
||||
struct ucred *cred = ap->a_cred;
|
||||
int32_t fuse_open_flags = 0;
|
||||
|
||||
fufh_type_t fufh_type;
|
||||
struct fuse_vnode_data *fvdat;
|
||||
|
||||
int error, isdir = 0;
|
||||
int32_t fuse_open_flags;
|
||||
|
||||
if (fuse_isdeadfs(vp))
|
||||
return ENXIO;
|
||||
if (vp->v_type == VCHR || vp->v_type == VBLK || vp->v_type == VFIFO)
|
||||
@ -1182,31 +1221,25 @@ fuse_vnop_open(struct vop_open_args *ap)
|
||||
fvdat = VTOFUD(vp);
|
||||
|
||||
if (vnode_isdir(vp)) {
|
||||
isdir = 1;
|
||||
}
|
||||
fuse_open_flags = 0;
|
||||
if (isdir) {
|
||||
fufh_type = FUFH_RDONLY;
|
||||
} else {
|
||||
fufh_type = fuse_filehandle_xlate_from_fflags(mode);
|
||||
/*
|
||||
* For WRONLY opens, force DIRECT_IO. This is necessary
|
||||
* since writing a partial block through the buffer cache
|
||||
* will result in a read of the block and that read won't
|
||||
* be allowed by the WRONLY open.
|
||||
*/
|
||||
if (fufh_type == FUFH_WRONLY ||
|
||||
(fvdat->flag & FN_DIRECTIO) != 0)
|
||||
fuse_open_flags = FOPEN_DIRECT_IO;
|
||||
}
|
||||
|
||||
/*
|
||||
* For WRONLY opens, force DIRECT_IO. This is necessary since writing
|
||||
* a partial block through the buffer cache will result in a read of
|
||||
* the block and that read won't be allowed by the WRONLY open.
|
||||
*/
|
||||
if (fufh_type == FUFH_WRONLY || (fvdat->flag & FN_DIRECTIO) != 0)
|
||||
fuse_open_flags = FOPEN_DIRECT_IO;
|
||||
|
||||
if (fuse_filehandle_validrw(vp, fufh_type) != FUFH_INVALID) {
|
||||
fuse_vnode_open(vp, fuse_open_flags, td);
|
||||
return 0;
|
||||
}
|
||||
error = fuse_filehandle_open(vp, fufh_type, NULL, td, cred);
|
||||
|
||||
return error;
|
||||
return fuse_filehandle_open(vp, fufh_type, NULL, td, cred);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -113,9 +113,7 @@ TEST_F(Create, eexist)
|
||||
* If the daemon doesn't implement FUSE_CREATE, then the kernel should fallback
|
||||
* to FUSE_MKNOD/FUSE_OPEN
|
||||
*/
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */
|
||||
TEST_F(Create, DISABLED_Enosys)
|
||||
TEST_F(Create, Enosys)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
@ -146,11 +144,11 @@ TEST_F(Create, DISABLED_Enosys)
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
|
||||
SET_OUT_HEADER_LEN(out, create);
|
||||
out->body.create.entry.attr.mode = S_IFREG | 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 = S_IFREG | mode;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.entry_valid = UINT64_MAX;
|
||||
out->body.entry.attr_valid = UINT64_MAX;
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
|
@ -160,6 +160,14 @@ void debug_fuseop(const mockfs_buf_in *in)
|
||||
in->header.unique, in->header.len);
|
||||
}
|
||||
switch (in->header.opcode) {
|
||||
const char *name, *value;
|
||||
|
||||
case FUSE_CREATE:
|
||||
name = (const char*)in->body.bytes +
|
||||
sizeof(fuse_open_in);
|
||||
printf(" flags=%#x name=%s",
|
||||
in->body.open.flags, name);
|
||||
break;
|
||||
case FUSE_FLUSH:
|
||||
printf(" lock_owner=%lu", in->body.flush.lock_owner);
|
||||
break;
|
||||
@ -229,12 +237,10 @@ void debug_fuseop(const mockfs_buf_in *in)
|
||||
* In theory neither the xattr name and value need be
|
||||
* ASCII, but in this test suite they always are.
|
||||
*/
|
||||
{
|
||||
const char *attr = (const char*)in->body.bytes +
|
||||
sizeof(fuse_setxattr_in);
|
||||
const char *v = attr + strlen(attr) + 1;
|
||||
printf(" %s=%s", attr, v);
|
||||
}
|
||||
name = (const char*)in->body.bytes +
|
||||
sizeof(fuse_setxattr_in);
|
||||
value = name + strlen(name) + 1;
|
||||
printf(" %s=%s", name, value);
|
||||
break;
|
||||
case FUSE_WRITE:
|
||||
printf(" offset=%lu size=%u flags=%u",
|
||||
|
Loading…
Reference in New Issue
Block a user