fusefs: eliminate a superfluous FUSE_GETATTR from VOP_LOOKUP
fuse_vnop_lookup was using a FUSE_GETATTR operation when looking up "." and "..", even though the only information it needed was the file type and file size. "." and ".." are obviously always going to be directories; there's no need to double check. Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
73825da397
commit
438b8a6fa2
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=346117
@ -734,10 +734,10 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
struct vnode *vp = NULL;
|
struct vnode *vp = NULL;
|
||||||
|
|
||||||
struct fuse_dispatcher fdi;
|
struct fuse_dispatcher fdi;
|
||||||
enum fuse_opcode op;
|
bool did_lookup = false;
|
||||||
struct fuse_entry_out *feo = NULL;
|
struct fuse_entry_out *feo = NULL;
|
||||||
struct fuse_attr_out *fao = NULL;
|
enum vtype vtyp; /* vnode type of target */
|
||||||
struct fuse_attr *fattr = NULL;
|
off_t filesize; /* filesize of target */
|
||||||
|
|
||||||
uint64_t nid;
|
uint64_t nid;
|
||||||
|
|
||||||
@ -756,17 +756,16 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
|
|
||||||
if (flags & ISDOTDOT) {
|
if (flags & ISDOTDOT) {
|
||||||
nid = VTOFUD(dvp)->parent_nid;
|
nid = VTOFUD(dvp)->parent_nid;
|
||||||
if (nid == 0) {
|
if (nid == 0)
|
||||||
return ENOENT;
|
return ENOENT;
|
||||||
}
|
/* .. is obviously a directory */
|
||||||
fdisp_init(&fdi, 0);
|
vtyp = VDIR;
|
||||||
op = FUSE_GETATTR;
|
filesize = 0;
|
||||||
goto calldaemon;
|
|
||||||
} else if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') {
|
} else if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') {
|
||||||
nid = VTOI(dvp);
|
nid = VTOI(dvp);
|
||||||
fdisp_init(&fdi, 0);
|
/* . is obviously a directory */
|
||||||
op = FUSE_GETATTR;
|
vtyp = VDIR;
|
||||||
goto calldaemon;
|
filesize = 0;
|
||||||
} else {
|
} else {
|
||||||
struct timespec now, timeout;
|
struct timespec now, timeout;
|
||||||
|
|
||||||
@ -806,43 +805,41 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
default:
|
default:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
nid = VTOI(dvp);
|
|
||||||
fdisp_init(&fdi, cnp->cn_namelen + 1);
|
|
||||||
op = FUSE_LOOKUP;
|
|
||||||
|
|
||||||
calldaemon:
|
nid = VTOI(dvp);
|
||||||
fdisp_make(&fdi, op, mp, nid, td, cred);
|
fdisp_init(&fdi, cnp->cn_namelen + 1);
|
||||||
|
fdisp_make(&fdi, FUSE_LOOKUP, mp, nid, td, cred);
|
||||||
|
|
||||||
if (op == FUSE_LOOKUP) {
|
|
||||||
memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
|
memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
|
||||||
((char *)fdi.indata)[cnp->cn_namelen] = '\0';
|
((char *)fdi.indata)[cnp->cn_namelen] = '\0';
|
||||||
}
|
lookup_err = fdisp_wait_answ(&fdi);
|
||||||
lookup_err = fdisp_wait_answ(&fdi);
|
did_lookup = true;
|
||||||
|
|
||||||
if ((op == FUSE_LOOKUP) && !lookup_err) {
|
if (!lookup_err) {
|
||||||
/* lookup call succeeded */
|
/* lookup call succeeded */
|
||||||
nid = ((struct fuse_entry_out *)fdi.answ)->nodeid;
|
nid = ((struct fuse_entry_out *)fdi.answ)->nodeid;
|
||||||
if (nid == 0) {
|
feo = (struct fuse_entry_out *)fdi.answ;
|
||||||
/* zero nodeid means ENOENT and cache it */
|
if (nid == 0) {
|
||||||
struct timespec timeout;
|
/* zero nodeid means ENOENT and cache it */
|
||||||
|
struct timespec timeout;
|
||||||
|
|
||||||
fdi.answ_stat = ENOENT;
|
fdi.answ_stat = ENOENT;
|
||||||
lookup_err = ENOENT;
|
lookup_err = ENOENT;
|
||||||
if (cnp->cn_flags & MAKEENTRY) {
|
if (cnp->cn_flags & MAKEENTRY) {
|
||||||
feo = (struct fuse_entry_out *)fdi.answ;
|
fuse_validity_2_timespec(feo, &timeout);
|
||||||
fuse_validity_2_timespec(feo, &timeout);
|
cache_enter_time(dvp, *vpp, cnp,
|
||||||
cache_enter_time(dvp, *vpp, cnp, &timeout,
|
&timeout, NULL);
|
||||||
NULL);
|
}
|
||||||
|
} else if (nid == FUSE_ROOT_ID) {
|
||||||
|
lookup_err = EINVAL;
|
||||||
}
|
}
|
||||||
} else if (nid == FUSE_ROOT_ID) {
|
vtyp = IFTOVT(feo->attr.mode);
|
||||||
lookup_err = EINVAL;
|
filesize = feo->attr.size;
|
||||||
|
}
|
||||||
|
if (lookup_err && (!fdi.answ_stat || lookup_err != ENOENT)) {
|
||||||
|
fdisp_destroy(&fdi);
|
||||||
|
return lookup_err;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (lookup_err &&
|
|
||||||
(!fdi.answ_stat || lookup_err != ENOENT || op != FUSE_LOOKUP)) {
|
|
||||||
fdisp_destroy(&fdi);
|
|
||||||
return lookup_err;
|
|
||||||
}
|
}
|
||||||
/* lookup_err, if non-zero, must be ENOENT at this point */
|
/* lookup_err, if non-zero, must be ENOENT at this point */
|
||||||
|
|
||||||
@ -871,13 +868,6 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Entry was found */
|
/* Entry was found */
|
||||||
if (op == FUSE_GETATTR) {
|
|
||||||
fattr = &((struct fuse_attr_out *)fdi.answ)->attr;
|
|
||||||
} else {
|
|
||||||
feo = (struct fuse_entry_out *)fdi.answ;
|
|
||||||
fattr = &(feo->attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((nameiop == DELETE || nameiop == RENAME) && islastcn) {
|
if ((nameiop == DELETE || nameiop == RENAME) && islastcn) {
|
||||||
err = fuse_internal_access(dvp, VWRITE, td, cred);
|
err = fuse_internal_access(dvp, VWRITE, td, cred);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
@ -912,7 +902,7 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
*vpp = dvp;
|
*vpp = dvp;
|
||||||
} else {
|
} else {
|
||||||
err = fuse_vnode_get(dvp->v_mount, feo, nid,
|
err = fuse_vnode_get(dvp->v_mount, feo, nid,
|
||||||
dvp, &vp, cnp, IFTOVT(fattr->mode));
|
dvp, &vp, cnp, vtyp);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
*vpp = vp;
|
*vpp = vp;
|
||||||
@ -941,7 +931,7 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
|
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
|
||||||
&vp, cnp, IFTOVT(fattr->mode));
|
&vp, cnp, vtyp);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -980,7 +970,7 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
}
|
}
|
||||||
VOP_UNLOCK(dvp, 0);
|
VOP_UNLOCK(dvp, 0);
|
||||||
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, NULL,
|
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, NULL,
|
||||||
&vp, cnp, IFTOVT(fattr->mode));
|
&vp, cnp, vtyp);
|
||||||
vfs_unbusy(mp);
|
vfs_unbusy(mp);
|
||||||
vn_lock(dvp, ltype | LK_RETRY);
|
vn_lock(dvp, ltype | LK_RETRY);
|
||||||
if ((dvp->v_iflag & VI_DOOMED) != 0) {
|
if ((dvp->v_iflag & VI_DOOMED) != 0) {
|
||||||
@ -998,7 +988,7 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
struct fuse_vnode_data *fvdat;
|
struct fuse_vnode_data *fvdat;
|
||||||
|
|
||||||
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
|
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
|
||||||
&vp, cnp, IFTOVT(fattr->mode));
|
&vp, cnp, vtyp);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1016,7 +1006,7 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
*/
|
*/
|
||||||
fvdat = VTOFUD(vp);
|
fvdat = VTOFUD(vp);
|
||||||
if (vnode_isreg(vp) &&
|
if (vnode_isreg(vp) &&
|
||||||
fattr->size != fvdat->filesize) {
|
filesize != fvdat->filesize) {
|
||||||
/*
|
/*
|
||||||
* The FN_SIZECHANGE flag reflects a dirty
|
* The FN_SIZECHANGE flag reflects a dirty
|
||||||
* append. If userspace lets us know our cache
|
* append. If userspace lets us know our cache
|
||||||
@ -1031,21 +1021,15 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
"%s!\n", __func__,
|
"%s!\n", __func__,
|
||||||
vnode_mount(vp)->mnt_stat.f_mntonname);
|
vnode_mount(vp)->mnt_stat.f_mntonname);
|
||||||
|
|
||||||
(void)fuse_vnode_setsize(vp, cred, fattr->size);
|
(void)fuse_vnode_setsize(vp, cred, filesize);
|
||||||
fvdat->flag &= ~FN_SIZECHANGE;
|
fvdat->flag &= ~FN_SIZECHANGE;
|
||||||
}
|
}
|
||||||
*vpp = vp;
|
*vpp = vp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op == FUSE_GETATTR) {
|
if (feo != NULL) {
|
||||||
fao = (struct fuse_attr_out*)fdi.answ;
|
fuse_internal_cache_attrs(*vpp, &feo->attr,
|
||||||
fuse_internal_cache_attrs(*vpp,
|
feo->attr_valid, feo->attr_valid_nsec, NULL);
|
||||||
&fao->attr, fao->attr_valid,
|
|
||||||
fao->attr_valid_nsec, NULL);
|
|
||||||
} else {
|
|
||||||
fuse_internal_cache_attrs(*vpp,
|
|
||||||
&feo->attr, feo->attr_valid,
|
|
||||||
feo->attr_valid_nsec, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
@ -1054,16 +1038,17 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|||||||
/* No lookup error; need to clean up. */
|
/* No lookup error; need to clean up. */
|
||||||
|
|
||||||
if (err) { /* Found inode; exit with no vnode. */
|
if (err) { /* Found inode; exit with no vnode. */
|
||||||
if (op == FUSE_LOOKUP) {
|
if (feo != NULL) {
|
||||||
fuse_internal_forget_send(vnode_mount(dvp), td, cred,
|
fuse_internal_forget_send(vnode_mount(dvp), td,
|
||||||
nid, 1);
|
cred, nid, 1);
|
||||||
}
|
}
|
||||||
fdisp_destroy(&fdi);
|
if (did_lookup)
|
||||||
|
fdisp_destroy(&fdi);
|
||||||
return err;
|
return err;
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fdisp_destroy(&fdi);
|
if (did_lookup)
|
||||||
|
fdisp_destroy(&fdi);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,49 @@ TEST_F(Lookup, attr_cache_timeout)
|
|||||||
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
|
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(Lookup, dot)
|
||||||
|
{
|
||||||
|
const char FULLPATH[] = "mountpoint/some_dir/.";
|
||||||
|
const char RELDIRPATH[] = "some_dir";
|
||||||
|
uint64_t ino = 42;
|
||||||
|
|
||||||
|
EXPECT_LOOKUP(1, RELDIRPATH)
|
||||||
|
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
|
||||||
|
SET_OUT_HEADER_LEN(out, entry);
|
||||||
|
out->body.entry.attr.mode = S_IFDIR | 0755;
|
||||||
|
out->body.entry.nodeid = ino;
|
||||||
|
out->body.entry.attr_valid = UINT64_MAX;
|
||||||
|
out->body.entry.entry_valid = UINT64_MAX;
|
||||||
|
})));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* access(2) is one of the few syscalls that will not (always) follow
|
||||||
|
* up a successful VOP_LOOKUP with another VOP.
|
||||||
|
*/
|
||||||
|
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Lookup, dotdot)
|
||||||
|
{
|
||||||
|
const char FULLPATH[] = "mountpoint/some_dir/..";
|
||||||
|
const char RELDIRPATH[] = "some_dir";
|
||||||
|
|
||||||
|
EXPECT_LOOKUP(1, RELDIRPATH)
|
||||||
|
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
|
||||||
|
SET_OUT_HEADER_LEN(out, entry);
|
||||||
|
out->body.entry.attr.mode = S_IFDIR | 0755;
|
||||||
|
out->body.entry.nodeid = 14;
|
||||||
|
out->body.entry.attr_valid = UINT64_MAX;
|
||||||
|
out->body.entry.entry_valid = UINT64_MAX;
|
||||||
|
})));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* access(2) is one of the few syscalls that will not (always) follow
|
||||||
|
* up a successful VOP_LOOKUP with another VOP.
|
||||||
|
*/
|
||||||
|
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(Lookup, enoent)
|
TEST_F(Lookup, enoent)
|
||||||
{
|
{
|
||||||
const char FULLPATH[] = "mountpoint/does_not_exist";
|
const char FULLPATH[] = "mountpoint/does_not_exist";
|
||||||
|
@ -636,5 +636,3 @@ TEST_F(RofsXattr, setextattr_erofs)
|
|||||||
ASSERT_EQ(-1, r);
|
ASSERT_EQ(-1, r);
|
||||||
EXPECT_EQ(EROFS, errno);
|
EXPECT_EQ(EROFS, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: EROFS tests
|
|
||||||
|
Loading…
Reference in New Issue
Block a user