FreeBSD Bugzilla – Attachment 228446 Details for
Bug 256937
[fusefs] Kernel panic in FUSE - lockmgr_xlock_hard: recursing on non recursive lockmgr
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch formatted for releng/13.0
pr256937.releng13.patch (text/plain), 23.59 KB, created by
Alan Somers
on 2021-10-04 20:13:06 UTC
(
hide
)
Description:
Patch formatted for releng/13.0
Filename:
MIME Type:
Creator:
Alan Somers
Created:
2021-10-04 20:13:06 UTC
Size:
23.59 KB
patch
obsolete
>diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c >index 60f9a7319e00..ade790ec672b 100644 >--- a/sys/fs/fuse/fuse_internal.c >+++ b/sys/fs/fuse/fuse_internal.c >@@ -255,7 +255,8 @@ fuse_internal_access(struct vnode *vp, > */ > void > fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr, >- uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap) >+ uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap, >+ bool from_server) > { > struct mount *mp; > struct fuse_vnode_data *fvdat; >@@ -271,9 +272,54 @@ fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr, > fuse_validity_2_bintime(attr_valid, attr_valid_nsec, > &fvdat->attr_cache_timeout); > >+ if (vnode_isreg(vp) && >+ fvdat->cached_attrs.va_size != VNOVAL && >+ attr->size != fvdat->cached_attrs.va_size) >+ { >+ if ( data->cache_mode == FUSE_CACHE_WB && >+ fvdat->flag & FN_SIZECHANGE) >+ { >+ const char *msg; >+ >+ /* >+ * The server changed the file's size even though we're >+ * using writeback cacheing and and we have outstanding >+ * dirty writes! That's a server bug. >+ */ >+ if (fuse_libabi_geq(data, 7, 23)) { >+ msg = "writeback cache incoherent!." >+ "To prevent data corruption, disable " >+ "the writeback cache according to your " >+ "FUSE server's documentation."; >+ } else { >+ msg = "writeback cache incoherent!." >+ "To prevent data corruption, disable " >+ "the writeback cache by setting " >+ "vfs.fusefs.data_cache_mode to 0 or 1."; >+ } >+ fuse_warn(data, FSESS_WARN_WB_CACHE_INCOHERENT, msg); >+ } >+ if (fuse_vnode_attr_cache_valid(vp) && >+ data->cache_mode != FUSE_CACHE_UC) >+ { >+ /* >+ * The server changed the file's size even though we >+ * have it cached and our cache has not yet expired. >+ * That's a bug. >+ */ >+ fuse_warn(data, FSESS_WARN_CACHE_INCOHERENT, >+ "cache incoherent! " >+ "To prevent " >+ "data corruption, disable the data cache " >+ "by mounting with -o direct_io, or as " >+ "directed otherwise by your FUSE server's " >+ "documentation."); >+ } >+ } >+ > /* Fix our buffers if the filesize changed without us knowing */ > if (vnode_isreg(vp) && attr->size != fvdat->cached_attrs.va_size) { >- (void)fuse_vnode_setsize(vp, attr->size); >+ (void)fuse_vnode_setsize(vp, attr->size, from_server); > fvdat->cached_attrs.va_size = attr->size; > } > >@@ -477,6 +523,7 @@ fuse_internal_invalidate_inode(struct mount *mp, struct uio *uio) > */ > if (fniio.off >= 0) > fuse_io_invalbuf(vp, curthread); >+ /* TODO: suppress "cache invalid" warning when this happens */ > fuse_vnode_clear_attr_cache(vp); > vput(vp); > return (0); >@@ -805,7 +852,7 @@ fuse_internal_newentry_core(struct vnode *dvp, > fuse_vnode_clear_attr_cache(dvp); > > fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid, >- feo->attr_valid_nsec, NULL); >+ feo->attr_valid_nsec, NULL, true); > > return err; > } >@@ -869,9 +916,6 @@ fuse_internal_forget_send(struct mount *mp, > fdisp_destroy(&fdi); > } > >-SDT_PROBE_DEFINE2(fusefs, , internal, getattr_cache_incoherent, >- "struct vnode*", "struct fuse_attr_out*"); >- > /* Fetch the vnode's attributes from the daemon*/ > int > fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap, >@@ -914,26 +958,8 @@ fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap, > fao->attr.mtime = old_mtime.tv_sec; > fao->attr.mtimensec = old_mtime.tv_nsec; > } >- if (vnode_isreg(vp) && >- fvdat->cached_attrs.va_size != VNOVAL && >- fao->attr.size != fvdat->cached_attrs.va_size) { >- /* >- * The server changed the file's size even though we had it >- * cached! That's a server bug. >- */ >- SDT_PROBE2(fusefs, , internal, getattr_cache_incoherent, vp, >- fao); >- printf("%s: cache incoherent on %s! " >- "Buggy FUSE server detected. To prevent data corruption, " >- "disable the data cache by mounting with -o direct_io, or " >- "as directed otherwise by your FUSE server's " >- "documentation\n", __func__, >- vnode_mount(vp)->mnt_stat.f_mntonname); >- int iosize = fuse_iosize(vp); >- v_inval_buf_range(vp, 0, INT64_MAX, iosize); >- } > fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid, >- fao->attr_valid_nsec, vap); >+ fao->attr_valid_nsec, vap, true); > if (vtyp != vnode_vtype(vp)) { > fuse_internal_vnode_disappear(vp); > err = ENOENT; >@@ -1229,7 +1255,7 @@ int fuse_internal_setattr(struct vnode *vp, struct vattr *vap, > struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ; > fuse_vnode_undirty_cached_timestamps(vp); > fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid, >- fao->attr_valid_nsec, NULL); >+ fao->attr_valid_nsec, NULL, false); > } > > out: >diff --git a/sys/fs/fuse/fuse_internal.h b/sys/fs/fuse/fuse_internal.h >index 20a10d7dfda0..e9fa3857227a 100644 >--- a/sys/fs/fuse/fuse_internal.h >+++ b/sys/fs/fuse/fuse_internal.h >@@ -223,7 +223,8 @@ int fuse_internal_access(struct vnode *vp, accmode_t mode, > > /* attributes */ > void fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr, >- uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap); >+ uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap, >+ bool from_server); > > /* fsync */ > >diff --git a/sys/fs/fuse/fuse_io.c b/sys/fs/fuse/fuse_io.c >index 3f23a35a8626..a240217fcc08 100644 >--- a/sys/fs/fuse/fuse_io.c >+++ b/sys/fs/fuse/fuse_io.c >@@ -571,21 +571,21 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio, > as_written_offset = uio->uio_offset - diff; > > if (as_written_offset - diff > filesize) >- fuse_vnode_setsize(vp, as_written_offset); >+ fuse_vnode_setsize(vp, as_written_offset, false); > if (as_written_offset - diff >= filesize) > fvdat->flag &= ~FN_SIZECHANGE; > > if (diff < 0) { >- printf("WARNING: misbehaving FUSE filesystem " >- "wrote more data than we provided it\n"); >+ fuse_warn(data, FSESS_WARN_WROTE_LONG, >+ "wrote more data than we provided it."); > err = EINVAL; > break; > } else if (diff > 0) { > /* Short write */ > if (!direct_io) { >- printf("WARNING: misbehaving FUSE filesystem: " >+ fuse_warn(data, FSESS_WARN_SHORT_WRITE, > "short writes are only allowed with " >- "direct_io\n"); >+ "direct_io."); > } > if (ioflag & IO_DIRECT) { > /* Return early */ >@@ -715,7 +715,7 @@ fuse_write_biobackend(struct vnode *vp, struct uio *uio, > * Extend file _after_ locking buffer so we won't race > * with other readers > */ >- err = fuse_vnode_setsize(vp, uio->uio_offset + n); >+ err = fuse_vnode_setsize(vp, uio->uio_offset + n, false); > filesize = uio->uio_offset + n; > fvdat->flag |= FN_SIZECHANGE; > if (err) { >@@ -1006,13 +1006,18 @@ fuse_io_strategy(struct vnode *vp, struct buf *bp) > /* > * Setup for actual write > */ >- error = fuse_vnode_size(vp, &filesize, cred, curthread); >- if (error) { >- bp->b_ioflags |= BIO_ERROR; >- bp->b_error = error; >- bufdone(bp); >- return (error); >- } >+ /* >+ * If the file's size is cached, use that value, even if the >+ * cache is expired. At this point we're already committed to >+ * writing something. If the FUSE server has changed the >+ * file's size behind our back, it's too late for us to do >+ * anything about it. In particular, we can't invalidate any >+ * part of the file's buffers because VOP_STRATEGY is called >+ * with them already locked. >+ */ >+ filesize = fvdat->cached_attrs.va_size; >+ /* filesize must've been cached by fuse_vnop_open. */ >+ KASSERT(filesize != VNOVAL, ("filesize should've been cached")); > > if ((off_t)bp->b_lblkno * biosize + bp->b_dirtyend > filesize) > bp->b_dirtyend = filesize - >diff --git a/sys/fs/fuse/fuse_ipc.c b/sys/fs/fuse/fuse_ipc.c >index 791ee9f38444..6b4a29214a96 100644 >--- a/sys/fs/fuse/fuse_ipc.c >+++ b/sys/fs/fuse/fuse_ipc.c >@@ -1073,3 +1073,17 @@ fuse_ipc_destroy(void) > counter_u64_free(fuse_ticket_count); > uma_zdestroy(ticket_zone); > } >+ >+SDT_PROBE_DEFINE3(fusefs,, ipc, warn, "struct fuse_data*", "unsigned", "char*"); >+void >+fuse_warn(struct fuse_data *data, unsigned flag, const char *msg) >+{ >+ SDT_PROBE3(fusefs, , ipc, warn, data, flag, msg); >+ if (!(data->dataflags & flag)) { >+ printf("WARNING: FUSE protocol violation for server mounted at " >+ "%s: %s " >+ "This warning will not be repeated.\n", >+ data->mp->mnt_stat.f_mntonname, msg); >+ data->dataflags |= flag; >+ } >+} >diff --git a/sys/fs/fuse/fuse_ipc.h b/sys/fs/fuse/fuse_ipc.h >index 2ed75b3319e3..fcc6ea9a62fa 100644 >--- a/sys/fs/fuse/fuse_ipc.h >+++ b/sys/fs/fuse/fuse_ipc.h >@@ -235,6 +235,11 @@ struct fuse_data { > #define FSESS_POSIX_LOCKS 0x2000 /* daemon supports POSIX locks */ > #define FSESS_EXPORT_SUPPORT 0x10000 /* daemon supports NFS-style lookups */ > #define FSESS_INTR 0x20000 /* interruptible mounts */ >+#define FSESS_WARN_SHORT_WRITE 0x40000 /* Short write without direct_io */ >+#define FSESS_WARN_WROTE_LONG 0x80000 /* Wrote more data than provided */ >+#define FSESS_WARN_LSEXTATTR_LONG 0x100000 /* Returned too many extattrs */ >+#define FSESS_WARN_CACHE_INCOHERENT 0x200000 /* Read cache incoherent */ >+#define FSESS_WARN_WB_CACHE_INCOHERENT 0x400000 /* WB cache incoherent */ > #define FSESS_MNTOPTS_MASK ( \ > FSESS_DAEMON_CAN_SPY | FSESS_PUSH_SYMLINKS_IN | \ > FSESS_DEFAULT_PERMISSIONS | FSESS_INTR) >@@ -396,6 +401,9 @@ fuse_libabi_geq(struct fuse_data *data, uint32_t abi_maj, uint32_t abi_min) > data->fuse_libabi_minor >= abi_min)); > } > >+/* Print msg as a warning to the console, but no more than once per session */ >+void fuse_warn(struct fuse_data *data, unsigned flag, const char *msg); >+ > struct fuse_data *fdata_alloc(struct cdev *dev, struct ucred *cred); > void fdata_trydestroy(struct fuse_data *data); > void fdata_set_dead(struct fuse_data *data); >diff --git a/sys/fs/fuse/fuse_node.c b/sys/fs/fuse/fuse_node.c >index d19e49358a22..c8982c5a48a1 100644 >--- a/sys/fs/fuse/fuse_node.c >+++ b/sys/fs/fuse/fuse_node.c >@@ -388,11 +388,14 @@ fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid) > } > > /* >- * Adjust the vnode's size to a new value, such as that provided by >- * FUSE_GETATTR. >+ * Adjust the vnode's size to a new value. >+ * >+ * If the new value came from the server, such as from a FUSE_GETATTR >+ * operation, set `from_server` true. But if it came from a local operation, >+ * such as write(2) or truncate(2), set `from_server` false. > */ > int >-fuse_vnode_setsize(struct vnode *vp, off_t newsize) >+fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server) > { > struct fuse_vnode_data *fvdat = VTOFUD(vp); > struct vattr *attrs; >@@ -433,6 +436,16 @@ fuse_vnode_setsize(struct vnode *vp, off_t newsize) > MPASS(bp->b_flags & B_VMIO); > vfs_bio_clrbuf(bp); > bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize); >+ } else if (from_server && newsize > oldsize && oldsize != VNOVAL) { >+ /* >+ * The FUSE server changed the file size behind our back. We >+ * should invalidate the entire cache. >+ */ >+ daddr_t left_lbn, end_lbn; >+ >+ left_lbn = oldsize / iosize; >+ end_lbn = howmany(newsize, iosize); >+ v_inval_buf_range(vp, 0, end_lbn, iosize); > } > out: > if (bp) >diff --git a/sys/fs/fuse/fuse_node.h b/sys/fs/fuse/fuse_node.h >index c92874334aa1..8f69ef9a08f3 100644 >--- a/sys/fs/fuse/fuse_node.h >+++ b/sys/fs/fuse/fuse_node.h >@@ -199,7 +199,7 @@ void fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, > > int fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid); > >-int fuse_vnode_setsize(struct vnode *vp, off_t newsize); >+int fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server); > > void fuse_vnode_undirty_cached_timestamps(struct vnode *vp); > >diff --git a/sys/fs/fuse/fuse_vfsops.c b/sys/fs/fuse/fuse_vfsops.c >index 7f47f8800994..3d5e168bcde2 100644 >--- a/sys/fs/fuse/fuse_vfsops.c >+++ b/sys/fs/fuse/fuse_vfsops.c >@@ -539,7 +539,6 @@ fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) > struct fuse_entry_out *feo; > struct fuse_vnode_data *fvdat; > const char dot[] = "."; >- off_t filesize; > enum vtype vtyp; > int error; > >@@ -576,30 +575,10 @@ fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) > error = fuse_vnode_get(mp, feo, nodeid, NULL, vpp, NULL, vtyp); > if (error) > goto out; >- filesize = feo->attr.size; >- >- /* >- * In the case where we are looking up a FUSE node represented by an >- * existing cached vnode, and the true size reported by FUSE_LOOKUP >- * doesn't match the vnode's cached size, then any cached writes beyond >- * the file's current size are lost. >- * >- * We can get here: >- * * following attribute cache expiration, or >- * * due a bug in the daemon, or >- */ > fvdat = VTOFUD(*vpp); >- if (vnode_isreg(*vpp) && >- filesize != fvdat->cached_attrs.va_size && >- fvdat->flag & FN_SIZECHANGE) { >- printf("%s: WB cache incoherent on %s!\n", __func__, >- vnode_mount(*vpp)->mnt_stat.f_mntonname); >- >- fvdat->flag &= ~FN_SIZECHANGE; >- } > > fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid, >- feo->attr_valid_nsec, NULL); >+ feo->attr_valid_nsec, NULL, true); > fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec, > &fvdat->entry_cache_timeout); > out: >diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c >index 5bbde1e278c9..2ef1ca809a85 100644 >--- a/sys/fs/fuse/fuse_vnops.c >+++ b/sys/fs/fuse/fuse_vnops.c >@@ -513,8 +513,9 @@ fuse_vnop_bmap(struct vop_bmap_args *ap) > struct fuse_bmap_in *fbi; > struct fuse_bmap_out *fbo; > struct fuse_data *data; >+ struct fuse_vnode_data *fvdat = VTOFUD(vp); > uint64_t biosize; >- off_t filesize; >+ off_t fsize; > daddr_t lbn = ap->a_bn; > daddr_t *pbn = ap->a_bnp; > int *runp = ap->a_runp; >@@ -547,10 +548,21 @@ fuse_vnop_bmap(struct vop_bmap_args *ap) > */ > if (runb != NULL) > *runb = MIN(lbn, maxrun); >- if (runp != NULL) { >- error = fuse_vnode_size(vp, &filesize, td->td_ucred, td); >+ if (runp != NULL && maxrun == 0) >+ *runp = 0; >+ else if (runp != NULL) { >+ /* >+ * If the file's size is cached, use that value to calculate >+ * runp, even if the cache is expired. runp is only advisory, >+ * and the risk of getting it wrong is not worth the cost of >+ * another upcall. >+ */ >+ if (fvdat->cached_attrs.va_size != VNOVAL) >+ fsize = fvdat->cached_attrs.va_size; >+ else >+ error = fuse_vnode_size(vp, &fsize, td->td_ucred, td); > if (error == 0) >- *runp = MIN(MAX(0, filesize / (off_t)biosize - lbn - 1), >+ *runp = MIN(MAX(0, fsize / (off_t)biosize - lbn - 1), > maxrun); > else > *runp = 0; >@@ -892,7 +904,7 @@ fuse_vnop_create(struct vop_create_args *ap) > } > ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create"); > fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid, >- feo->attr_valid_nsec, NULL); >+ feo->attr_valid_nsec, NULL, true); > > fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo); > fuse_vnode_open(*vpp, foo->open_flags, td); >@@ -1148,7 +1160,7 @@ fuse_vnop_link(struct vop_link_args *ap) > */ > fuse_vnode_clear_attr_cache(tdvp); > fuse_internal_cache_attrs(vp, &feo->attr, feo->attr_valid, >- feo->attr_valid_nsec, NULL); >+ feo->attr_valid_nsec, NULL, true); > } > out: > fdisp_destroy(&fdi); >@@ -1174,8 +1186,6 @@ fuse_lookup_alloc(struct mount *mp, void *arg, int lkflags, struct vnode **vpp) > > SDT_PROBE_DEFINE3(fusefs, , vnops, cache_lookup, > "int", "struct timespec*", "struct timespec*"); >-SDT_PROBE_DEFINE2(fusefs, , vnops, lookup_cache_incoherent, >- "struct vnode*", "struct fuse_entry_out*"); > /* > struct vnop_lookup_args { > struct vnodeop_desc *a_desc; >@@ -1359,53 +1369,17 @@ fuse_vnop_lookup(struct vop_lookup_args *ap) > *vpp = dvp; > } else { > struct fuse_vnode_data *fvdat; >- struct vattr *vap; > > err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp, > &vp, cnp, vtyp); > if (err) > goto out; > *vpp = vp; >- >- /* >- * In the case where we are looking up a FUSE node >- * represented by an existing cached vnode, and the >- * true size reported by FUSE_LOOKUP doesn't match >- * the vnode's cached size, then any cached writes >- * beyond the file's current size are lost. >- * >- * We can get here: >- * * following attribute cache expiration, or >- * * due a bug in the daemon, or >- */ > fvdat = VTOFUD(vp); >- if (vnode_isreg(vp) && >- ((filesize != fvdat->cached_attrs.va_size && >- fvdat->flag & FN_SIZECHANGE) || >- ((vap = VTOVA(vp)) && >- filesize != vap->va_size))) >- { >- SDT_PROBE2(fusefs, , vnops, lookup_cache_incoherent, vp, feo); >- fvdat->flag &= ~FN_SIZECHANGE; >- /* >- * The server changed the file's size even >- * though we had it cached, or had dirty writes >- * in the WB cache! >- */ >- printf("%s: cache incoherent on %s! " >- "Buggy FUSE server detected. To prevent " >- "data corruption, disable the data cache " >- "by mounting with -o direct_io, or as " >- "directed otherwise by your FUSE server's " >- "documentation\n", __func__, >- vnode_mount(vp)->mnt_stat.f_mntonname); >- int iosize = fuse_iosize(vp); >- v_inval_buf_range(vp, 0, INT64_MAX, iosize); >- } > > MPASS(feo != NULL); > fuse_internal_cache_attrs(*vpp, &feo->attr, >- feo->attr_valid, feo->attr_valid_nsec, NULL); >+ feo->attr_valid, feo->attr_valid_nsec, NULL, true); > fuse_validity_2_bintime(feo->entry_valid, > feo->entry_valid_nsec, > &fvdat->entry_cache_timeout); >@@ -2592,9 +2566,12 @@ fuse_vnop_listextattr(struct vop_listextattr_args *ap) > linux_list = fdi.answ; > /* FUSE doesn't allow the server to return more data than requested */ > if (fdi.iosize > linux_list_len) { >- printf("WARNING: FUSE protocol violation. Server returned " >+ struct fuse_data *data = fuse_get_mpdata(mp); >+ >+ fuse_warn(data, FSESS_WARN_LSEXTATTR_LONG, >+ "server returned " > "more extended attribute data than requested; " >- "should've returned ERANGE instead"); >+ "should've returned ERANGE instead."); > } else { > /* But returning less data is fine */ > linux_list_len = fdi.iosize; >diff --git a/tests/sys/fs/fusefs/bmap.cc b/tests/sys/fs/fusefs/bmap.cc >index 3f392a4447f2..c635f4d7e46f 100644 >--- a/tests/sys/fs/fusefs/bmap.cc >+++ b/tests/sys/fs/fusefs/bmap.cc >@@ -75,6 +75,8 @@ void expect_lookup(const char *relpath, uint64_t ino, off_t size) > } > }; > >+class BmapEof: public Bmap, public WithParamInterface<int> {}; >+ > /* > * Test FUSE_BMAP > * XXX The FUSE protocol does not include the runp and runb variables, so those >@@ -165,3 +167,87 @@ TEST_F(Bmap, default_) > > leak(fd); > } >+ >+/* >+ * VOP_BMAP should not query the server for the file's size, even if its cached >+ * attributes have expired. >+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937 >+ */ >+TEST_P(BmapEof, eof) >+{ >+ /* >+ * Outline: >+ * 1) lookup the file, setting attr_valid=0 >+ * 2) Read more than one block, causing the kernel to issue VOP_BMAP to >+ * plan readahead. >+ * 3) Nothing should panic >+ * 4) Repeat the tests, truncating the file after different numbers of >+ * GETATTR operations. >+ */ >+ Sequence seq; >+ const off_t filesize = 2 * m_maxbcachebuf; >+ const ino_t ino = 42; >+ mode_t mode = S_IFREG | 0644; >+ void *contents, *buf; >+ int fd; >+ int ngetattrs; >+ >+ ngetattrs = GetParam(); >+ contents = calloc(1, filesize); >+ FuseTest::expect_lookup(RELPATH, ino, mode, filesize, 1, 0); >+ expect_open(ino, 0, 1); >+ // Depending on ngetattrs, FUSE_READ could be called with either >+ // filesize or filesize / 2 . >+ EXPECT_CALL(*m_mock, process( >+ ResultOf([=](auto in) { >+ return (in.header.opcode == FUSE_READ && >+ in.header.nodeid == ino && >+ in.body.read.offset == 0 && >+ ( in.body.read.size == filesize || >+ in.body.read.size == filesize / 2)); >+ }, Eq(true)), >+ _) >+ ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) { >+ size_t osize = in.body.read.size; >+ out.header.len = sizeof(struct fuse_out_header) + osize; >+ bzero(out.body.bytes, osize); >+ }))); >+ EXPECT_CALL(*m_mock, process( >+ ResultOf([](auto in) { >+ return (in.header.opcode == FUSE_GETATTR && >+ in.header.nodeid == ino); >+ }, Eq(true)), >+ _) >+ ).Times(Between(ngetattrs - 1, ngetattrs)) >+ .InSequence(seq) >+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { >+ SET_OUT_HEADER_LEN(out, attr); >+ out.body.attr.attr_valid = 0; >+ out.body.attr.attr.ino = ino; >+ out.body.attr.attr.mode = S_IFREG | 0644; >+ out.body.attr.attr.size = filesize; >+ }))); >+ EXPECT_CALL(*m_mock, process( >+ ResultOf([](auto in) { >+ return (in.header.opcode == FUSE_GETATTR && >+ in.header.nodeid == ino); >+ }, Eq(true)), >+ _) >+ ).InSequence(seq) >+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { >+ SET_OUT_HEADER_LEN(out, attr); >+ out.body.attr.attr_valid = 0; >+ out.body.attr.attr.ino = ino; >+ out.body.attr.attr.mode = S_IFREG | 0644; >+ out.body.attr.attr.size = filesize / 2; >+ }))); >+ >+ buf = calloc(1, filesize); >+ fd = open(FULLPATH, O_RDWR); >+ ASSERT_LE(0, fd) << strerror(errno); >+ read(fd, buf, filesize); >+} >+ >+INSTANTIATE_TEST_CASE_P(BE, BmapEof, >+ Values(1, 2, 3) >+); >diff --git a/tests/sys/fs/fusefs/write.cc b/tests/sys/fs/fusefs/write.cc >index f0f9685000fb..0521524f230f 100644 >--- a/tests/sys/fs/fusefs/write.cc >+++ b/tests/sys/fs/fusefs/write.cc >@@ -208,6 +208,9 @@ virtual void SetUp() { > } > }; > >+class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int> >+{}; >+ > void sigxfsz_handler(int __unused sig) { > Write::s_sigxfsz = 1; > } >@@ -526,6 +529,84 @@ TEST_F(Write, eof_during_rmw) > leak(fd); > } > >+/* >+ * VOP_STRATEGY should not query the server for the file's size, even if its >+ * cached attributes have expired. >+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937 >+ */ >+TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy) >+{ >+ const char FULLPATH[] = "mountpoint/some_file.txt"; >+ const char RELPATH[] = "some_file.txt"; >+ Sequence seq; >+ const off_t filesize = 2 * m_maxbcachebuf; >+ void *contents; >+ uint64_t ino = 42; >+ uint64_t attr_valid = 0; >+ uint64_t attr_valid_nsec = 0; >+ mode_t mode = S_IFREG | 0644; >+ int fd; >+ int ngetattrs; >+ >+ ngetattrs = GetParam(); >+ contents = calloc(1, filesize); >+ >+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) >+ .WillRepeatedly(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.attr.nlink = 1; >+ out.body.entry.attr.size = filesize; >+ out.body.entry.attr_valid = attr_valid; >+ out.body.entry.attr_valid_nsec = attr_valid_nsec; >+ }))); >+ expect_open(ino, 0, 1); >+ EXPECT_CALL(*m_mock, process( >+ ResultOf([=](auto in) { >+ return (in.header.opcode == FUSE_GETATTR && >+ in.header.nodeid == ino); >+ }, Eq(true)), >+ _) >+ ).Times(Between(ngetattrs - 1, ngetattrs)) >+ .InSequence(seq) >+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { >+ SET_OUT_HEADER_LEN(out, attr); >+ out.body.attr.attr.ino = ino; >+ out.body.attr.attr.mode = mode; >+ out.body.attr.attr_valid = attr_valid; >+ out.body.attr.attr_valid_nsec = attr_valid_nsec; >+ out.body.attr.attr.size = filesize; >+ }))); >+ EXPECT_CALL(*m_mock, process( >+ ResultOf([=](auto in) { >+ return (in.header.opcode == FUSE_GETATTR && >+ in.header.nodeid == ino); >+ }, Eq(true)), >+ _) >+ ).InSequence(seq) >+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { >+ SET_OUT_HEADER_LEN(out, attr); >+ out.body.attr.attr.ino = ino; >+ out.body.attr.attr.mode = mode; >+ out.body.attr.attr_valid = attr_valid; >+ out.body.attr.attr_valid_nsec = attr_valid_nsec; >+ out.body.attr.attr.size = filesize / 2; >+ }))); >+ expect_write(ino, 0, filesize / 2, filesize / 2, contents); >+ >+ fd = open(FULLPATH, O_RDWR); >+ ASSERT_LE(0, fd) << strerror(errno); >+ ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2)) >+ << strerror(errno); >+ >+} >+ >+INSTANTIATE_TEST_CASE_P(W, WriteEofDuringVnopStrategy, >+ Values(1, 2, 3) >+); >+ > /* > * If the kernel cannot be sure which uid, gid, or pid was responsible for a > * write, then it must set the FUSE_WRITE_CACHE bit
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 256937
:
226169
|
226183
|
228319
|
228320
|
228412
|
228438
| 228446