View | Details | Raw Unified | Return to bug 256937 | Differences between
and this patch

Collapse All | Expand All

(-)b/sys/fs/fuse/fuse_internal.c (-26 / +52 lines)
Lines 255-261 fuse_internal_access(struct vnode *vp, Link Here
255
 */
255
 */
256
void
256
void
257
fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
257
fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
258
	uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap)
258
	uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap,
259
	bool from_server)
259
{
260
{
260
	struct mount *mp;
261
	struct mount *mp;
261
	struct fuse_vnode_data *fvdat;
262
	struct fuse_vnode_data *fvdat;
Lines 271-279 fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr, Link Here
271
	fuse_validity_2_bintime(attr_valid, attr_valid_nsec,
272
	fuse_validity_2_bintime(attr_valid, attr_valid_nsec,
272
		&fvdat->attr_cache_timeout);
273
		&fvdat->attr_cache_timeout);
273
274
275
	if (vnode_isreg(vp) &&
276
	    fvdat->cached_attrs.va_size != VNOVAL &&
277
	    attr->size != fvdat->cached_attrs.va_size)
278
	{
279
		if ( data->cache_mode == FUSE_CACHE_WB &&
280
		    fvdat->flag & FN_SIZECHANGE)
281
		{
282
			const char *msg;
283
284
			/*
285
			 * The server changed the file's size even though we're
286
			 * using writeback cacheing and and we have outstanding
287
			 * dirty writes!  That's a server bug.
288
			 */
289
			if (fuse_libabi_geq(data, 7, 23)) {
290
				msg = "writeback cache incoherent!."
291
				    "To prevent data corruption, disable "
292
				    "the writeback cache according to your "
293
				    "FUSE server's documentation.";
294
			} else {
295
				msg = "writeback cache incoherent!."
296
				    "To prevent data corruption, disable "
297
				    "the writeback cache by setting "
298
				    "vfs.fusefs.data_cache_mode to 0 or 1.";
299
			}
300
			fuse_warn(data, FSESS_WARN_WB_CACHE_INCOHERENT, msg);
301
		}
302
		if (fuse_vnode_attr_cache_valid(vp) &&
303
		    data->cache_mode != FUSE_CACHE_UC)
304
		{
305
			/*
306
			 * The server changed the file's size even though we
307
			 * have it cached and our cache has not yet expired.
308
			 * That's a bug.
309
			 */
310
			fuse_warn(data, FSESS_WARN_CACHE_INCOHERENT,
311
			    "cache incoherent!  "
312
			    "To prevent "
313
			    "data corruption, disable the data cache "
314
			    "by mounting with -o direct_io, or as "
315
			    "directed otherwise by your FUSE server's "
316
			    "documentation.");
317
		}
318
	}
319
274
	/* Fix our buffers if the filesize changed without us knowing */
320
	/* Fix our buffers if the filesize changed without us knowing */
275
	if (vnode_isreg(vp) && attr->size != fvdat->cached_attrs.va_size) {
321
	if (vnode_isreg(vp) && attr->size != fvdat->cached_attrs.va_size) {
276
		(void)fuse_vnode_setsize(vp, attr->size);
322
		(void)fuse_vnode_setsize(vp, attr->size, from_server);
277
		fvdat->cached_attrs.va_size = attr->size;
323
		fvdat->cached_attrs.va_size = attr->size;
278
	}
324
	}
279
325
Lines 477-482 fuse_internal_invalidate_inode(struct mount *mp, struct uio *uio) Link Here
477
	 */
523
	 */
478
	if (fniio.off >= 0)
524
	if (fniio.off >= 0)
479
		fuse_io_invalbuf(vp, curthread);
525
		fuse_io_invalbuf(vp, curthread);
526
	/* TODO: suppress "cache invalid" warning when this happens */
480
	fuse_vnode_clear_attr_cache(vp);
527
	fuse_vnode_clear_attr_cache(vp);
481
	vput(vp);
528
	vput(vp);
482
	return (0);
529
	return (0);
Lines 805-811 fuse_internal_newentry_core(struct vnode *dvp, Link Here
805
	fuse_vnode_clear_attr_cache(dvp);
852
	fuse_vnode_clear_attr_cache(dvp);
806
853
807
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
854
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
808
		feo->attr_valid_nsec, NULL);
855
		feo->attr_valid_nsec, NULL, true);
809
856
810
	return err;
857
	return err;
811
}
858
}
Lines 869-877 fuse_internal_forget_send(struct mount *mp, Link Here
869
	fdisp_destroy(&fdi);
916
	fdisp_destroy(&fdi);
870
}
917
}
871
918
872
SDT_PROBE_DEFINE2(fusefs, , internal, getattr_cache_incoherent,
873
	"struct vnode*", "struct fuse_attr_out*");
874
875
/* Fetch the vnode's attributes from the daemon*/
919
/* Fetch the vnode's attributes from the daemon*/
876
int
920
int
877
fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap,
921
fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap,
Lines 914-939 fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap, Link Here
914
		fao->attr.mtime = old_mtime.tv_sec;
958
		fao->attr.mtime = old_mtime.tv_sec;
915
		fao->attr.mtimensec = old_mtime.tv_nsec;
959
		fao->attr.mtimensec = old_mtime.tv_nsec;
916
	}
960
	}
917
	if (vnode_isreg(vp) &&
918
	    fvdat->cached_attrs.va_size != VNOVAL &&
919
	    fao->attr.size != fvdat->cached_attrs.va_size) {
920
		/*
921
		 * The server changed the file's size even though we had it
922
		 * cached!  That's a server bug.
923
		 */
924
		SDT_PROBE2(fusefs, , internal, getattr_cache_incoherent, vp,
925
		    fao);
926
		printf("%s: cache incoherent on %s!  "
927
		    "Buggy FUSE server detected.  To prevent data corruption, "
928
		    "disable the data cache by mounting with -o direct_io, or "
929
		    "as directed otherwise by your FUSE server's "
930
		    "documentation\n", __func__,
931
		    vnode_mount(vp)->mnt_stat.f_mntonname);
932
		int iosize = fuse_iosize(vp);
933
		v_inval_buf_range(vp, 0, INT64_MAX, iosize);
934
	}
935
	fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
961
	fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
936
		fao->attr_valid_nsec, vap);
962
		fao->attr_valid_nsec, vap, true);
937
	if (vtyp != vnode_vtype(vp)) {
963
	if (vtyp != vnode_vtype(vp)) {
938
		fuse_internal_vnode_disappear(vp);
964
		fuse_internal_vnode_disappear(vp);
939
		err = ENOENT;
965
		err = ENOENT;
Lines 1229-1235 int fuse_internal_setattr(struct vnode *vp, struct vattr *vap, Link Here
1229
		struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
1255
		struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
1230
		fuse_vnode_undirty_cached_timestamps(vp);
1256
		fuse_vnode_undirty_cached_timestamps(vp);
1231
		fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
1257
		fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
1232
			fao->attr_valid_nsec, NULL);
1258
			fao->attr_valid_nsec, NULL, false);
1233
	}
1259
	}
1234
1260
1235
out:
1261
out:
(-)b/sys/fs/fuse/fuse_internal.h (-1 / +2 lines)
Lines 223-229 int fuse_internal_access(struct vnode *vp, accmode_t mode, Link Here
223
223
224
/* attributes */
224
/* attributes */
225
void fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
225
void fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
226
	uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap);
226
	uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap,
227
	bool from_server);
227
228
228
/* fsync */
229
/* fsync */
229
230
(-)b/sys/fs/fuse/fuse_io.c (-13 / +18 lines)
Lines 571-591 fuse_write_directbackend(struct vnode *vp, struct uio *uio, Link Here
571
		as_written_offset = uio->uio_offset - diff;
571
		as_written_offset = uio->uio_offset - diff;
572
572
573
		if (as_written_offset - diff > filesize)
573
		if (as_written_offset - diff > filesize)
574
			fuse_vnode_setsize(vp, as_written_offset);
574
			fuse_vnode_setsize(vp, as_written_offset, false);
575
		if (as_written_offset - diff >= filesize)
575
		if (as_written_offset - diff >= filesize)
576
			fvdat->flag &= ~FN_SIZECHANGE;
576
			fvdat->flag &= ~FN_SIZECHANGE;
577
577
578
		if (diff < 0) {
578
		if (diff < 0) {
579
			printf("WARNING: misbehaving FUSE filesystem "
579
			fuse_warn(data, FSESS_WARN_WROTE_LONG,
580
				"wrote more data than we provided it\n");
580
				"wrote more data than we provided it.");
581
			err = EINVAL;
581
			err = EINVAL;
582
			break;
582
			break;
583
		} else if (diff > 0) {
583
		} else if (diff > 0) {
584
			/* Short write */
584
			/* Short write */
585
			if (!direct_io) {
585
			if (!direct_io) {
586
				printf("WARNING: misbehaving FUSE filesystem: "
586
				fuse_warn(data, FSESS_WARN_SHORT_WRITE,
587
					"short writes are only allowed with "
587
					"short writes are only allowed with "
588
					"direct_io\n");
588
					"direct_io.");
589
			}
589
			}
590
			if (ioflag & IO_DIRECT) {
590
			if (ioflag & IO_DIRECT) {
591
				/* Return early */
591
				/* Return early */
Lines 715-721 fuse_write_biobackend(struct vnode *vp, struct uio *uio, Link Here
715
			 * Extend file _after_ locking buffer so we won't race
715
			 * Extend file _after_ locking buffer so we won't race
716
			 * with other readers
716
			 * with other readers
717
			 */
717
			 */
718
			err = fuse_vnode_setsize(vp, uio->uio_offset + n);
718
			err = fuse_vnode_setsize(vp, uio->uio_offset + n, false);
719
			filesize = uio->uio_offset + n;
719
			filesize = uio->uio_offset + n;
720
			fvdat->flag |= FN_SIZECHANGE;
720
			fvdat->flag |= FN_SIZECHANGE;
721
			if (err) {
721
			if (err) {
Lines 1006-1018 fuse_io_strategy(struct vnode *vp, struct buf *bp) Link Here
1006
		/*
1006
		/*
1007
	         * Setup for actual write
1007
	         * Setup for actual write
1008
	         */
1008
	         */
1009
		error = fuse_vnode_size(vp, &filesize, cred, curthread);
1009
		/*
1010
		if (error) {
1010
		 * If the file's size is cached, use that value, even if the
1011
			bp->b_ioflags |= BIO_ERROR;
1011
		 * cache is expired.  At this point we're already committed to
1012
			bp->b_error = error;
1012
		 * writing something.  If the FUSE server has changed the
1013
			bufdone(bp);
1013
		 * file's size behind our back, it's too late for us to do
1014
			return (error);
1014
		 * anything about it.  In particular, we can't invalidate any
1015
		}
1015
		 * part of the file's buffers because VOP_STRATEGY is called
1016
		 * with them already locked.
1017
		 */
1018
		filesize = fvdat->cached_attrs.va_size;
1019
		/* filesize must've been cached by fuse_vnop_open.  */
1020
		KASSERT(filesize != VNOVAL, ("filesize should've been cached"));
1016
1021
1017
		if ((off_t)bp->b_lblkno * biosize + bp->b_dirtyend > filesize)
1022
		if ((off_t)bp->b_lblkno * biosize + bp->b_dirtyend > filesize)
1018
			bp->b_dirtyend = filesize - 
1023
			bp->b_dirtyend = filesize - 
(-)b/sys/fs/fuse/fuse_ipc.c (+14 lines)
Lines 1073-1075 fuse_ipc_destroy(void) Link Here
1073
	counter_u64_free(fuse_ticket_count);
1073
	counter_u64_free(fuse_ticket_count);
1074
	uma_zdestroy(ticket_zone);
1074
	uma_zdestroy(ticket_zone);
1075
}
1075
}
1076
1077
SDT_PROBE_DEFINE3(fusefs,, ipc, warn, "struct fuse_data*", "unsigned", "char*");
1078
void
1079
fuse_warn(struct fuse_data *data, unsigned flag, const char *msg)
1080
{
1081
	SDT_PROBE3(fusefs, , ipc, warn, data, flag, msg);
1082
	if (!(data->dataflags & flag)) {
1083
		printf("WARNING: FUSE protocol violation for server mounted at "
1084
		    "%s: %s  "
1085
		    "This warning will not be repeated.\n",
1086
		    data->mp->mnt_stat.f_mntonname, msg);
1087
		data->dataflags |= flag;
1088
	}
1089
}
(-)b/sys/fs/fuse/fuse_ipc.h (+8 lines)
Lines 235-240 struct fuse_data { Link Here
235
#define FSESS_POSIX_LOCKS         0x2000 /* daemon supports POSIX locks */
235
#define FSESS_POSIX_LOCKS         0x2000 /* daemon supports POSIX locks */
236
#define FSESS_EXPORT_SUPPORT      0x10000 /* daemon supports NFS-style lookups */
236
#define FSESS_EXPORT_SUPPORT      0x10000 /* daemon supports NFS-style lookups */
237
#define FSESS_INTR                0x20000 /* interruptible mounts */
237
#define FSESS_INTR                0x20000 /* interruptible mounts */
238
#define FSESS_WARN_SHORT_WRITE    0x40000 /* Short write without direct_io */
239
#define FSESS_WARN_WROTE_LONG     0x80000 /* Wrote more data than provided */
240
#define FSESS_WARN_LSEXTATTR_LONG 0x100000 /* Returned too many extattrs */
241
#define FSESS_WARN_CACHE_INCOHERENT 0x200000	/* Read cache incoherent */
242
#define FSESS_WARN_WB_CACHE_INCOHERENT 0x400000	/* WB cache incoherent */
238
#define FSESS_MNTOPTS_MASK	( \
243
#define FSESS_MNTOPTS_MASK	( \
239
	FSESS_DAEMON_CAN_SPY | FSESS_PUSH_SYMLINKS_IN | \
244
	FSESS_DAEMON_CAN_SPY | FSESS_PUSH_SYMLINKS_IN | \
240
	FSESS_DEFAULT_PERMISSIONS | FSESS_INTR)
245
	FSESS_DEFAULT_PERMISSIONS | FSESS_INTR)
Lines 396-401 fuse_libabi_geq(struct fuse_data *data, uint32_t abi_maj, uint32_t abi_min) Link Here
396
	     data->fuse_libabi_minor >= abi_min));
401
	     data->fuse_libabi_minor >= abi_min));
397
}
402
}
398
403
404
/* Print msg as a warning to the console, but no more than once per session */
405
void fuse_warn(struct fuse_data *data, unsigned flag, const char *msg);
406
399
struct fuse_data *fdata_alloc(struct cdev *dev, struct ucred *cred);
407
struct fuse_data *fdata_alloc(struct cdev *dev, struct ucred *cred);
400
void fdata_trydestroy(struct fuse_data *data);
408
void fdata_trydestroy(struct fuse_data *data);
401
void fdata_set_dead(struct fuse_data *data);
409
void fdata_set_dead(struct fuse_data *data);
(-)b/sys/fs/fuse/fuse_node.c (-3 / +16 lines)
Lines 388-398 fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid) Link Here
388
}
388
}
389
389
390
/*
390
/*
391
 * Adjust the vnode's size to a new value, such as that provided by
391
 * Adjust the vnode's size to a new value.
392
 * FUSE_GETATTR.
392
 *
393
 * If the new value came from the server, such as from a FUSE_GETATTR
394
 * operation, set `from_server` true.  But if it came from a local operation,
395
 * such as write(2) or truncate(2), set `from_server` false.
393
 */
396
 */
394
int
397
int
395
fuse_vnode_setsize(struct vnode *vp, off_t newsize)
398
fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server)
396
{
399
{
397
	struct fuse_vnode_data *fvdat = VTOFUD(vp);
400
	struct fuse_vnode_data *fvdat = VTOFUD(vp);
398
	struct vattr *attrs;
401
	struct vattr *attrs;
Lines 433-438 fuse_vnode_setsize(struct vnode *vp, off_t newsize) Link Here
433
		MPASS(bp->b_flags & B_VMIO);
436
		MPASS(bp->b_flags & B_VMIO);
434
		vfs_bio_clrbuf(bp);
437
		vfs_bio_clrbuf(bp);
435
		bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
438
		bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
439
	} else if (from_server && newsize > oldsize && oldsize != VNOVAL) {
440
		/*
441
		 * The FUSE server changed the file size behind our back.  We
442
		 * should invalidate the entire cache.
443
		 */
444
		daddr_t left_lbn, end_lbn;
445
446
		left_lbn = oldsize / iosize;
447
		end_lbn = howmany(newsize, iosize);
448
		v_inval_buf_range(vp, 0, end_lbn, iosize);
436
	}
449
	}
437
out:
450
out:
438
	if (bp)
451
	if (bp)
(-)b/sys/fs/fuse/fuse_node.h (-1 / +1 lines)
Lines 199-205 void fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, Link Here
199
199
200
int fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid);
200
int fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid);
201
201
202
int fuse_vnode_setsize(struct vnode *vp, off_t newsize);
202
int fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server);
203
203
204
void fuse_vnode_undirty_cached_timestamps(struct vnode *vp);
204
void fuse_vnode_undirty_cached_timestamps(struct vnode *vp);
205
205
(-)b/sys/fs/fuse/fuse_vfsops.c (-22 / +1 lines)
Lines 539-545 fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) Link Here
539
	struct fuse_entry_out *feo;
539
	struct fuse_entry_out *feo;
540
	struct fuse_vnode_data *fvdat;
540
	struct fuse_vnode_data *fvdat;
541
	const char dot[] = ".";
541
	const char dot[] = ".";
542
	off_t filesize;
543
	enum vtype vtyp;
542
	enum vtype vtyp;
544
	int error;
543
	int error;
545
544
Lines 576-605 fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) Link Here
576
	error = fuse_vnode_get(mp, feo, nodeid, NULL, vpp, NULL, vtyp);
575
	error = fuse_vnode_get(mp, feo, nodeid, NULL, vpp, NULL, vtyp);
577
	if (error)
576
	if (error)
578
		goto out;
577
		goto out;
579
	filesize = feo->attr.size;
580
581
	/*
582
	 * In the case where we are looking up a FUSE node represented by an
583
	 * existing cached vnode, and the true size reported by FUSE_LOOKUP
584
	 * doesn't match the vnode's cached size, then any cached writes beyond
585
	 * the file's current size are lost.
586
	 *
587
	 * We can get here:
588
	 * * following attribute cache expiration, or
589
	 * * due a bug in the daemon, or
590
	 */
591
	fvdat = VTOFUD(*vpp);
578
	fvdat = VTOFUD(*vpp);
592
	if (vnode_isreg(*vpp) &&
593
	    filesize != fvdat->cached_attrs.va_size &&
594
	    fvdat->flag & FN_SIZECHANGE) {
595
		printf("%s: WB cache incoherent on %s!\n", __func__,
596
		    vnode_mount(*vpp)->mnt_stat.f_mntonname);
597
598
		fvdat->flag &= ~FN_SIZECHANGE;
599
	}
600
579
601
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
580
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
602
		feo->attr_valid_nsec, NULL);
581
		feo->attr_valid_nsec, NULL, true);
603
	fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec,
582
	fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec,
604
		&fvdat->entry_cache_timeout);
583
		&fvdat->entry_cache_timeout);
605
out:
584
out:
(-)b/sys/fs/fuse/fuse_vnops.c (-47 / +24 lines)
Lines 513-520 fuse_vnop_bmap(struct vop_bmap_args *ap) Link Here
513
	struct fuse_bmap_in *fbi;
513
	struct fuse_bmap_in *fbi;
514
	struct fuse_bmap_out *fbo;
514
	struct fuse_bmap_out *fbo;
515
	struct fuse_data *data;
515
	struct fuse_data *data;
516
	struct fuse_vnode_data *fvdat = VTOFUD(vp);
516
	uint64_t biosize;
517
	uint64_t biosize;
517
	off_t filesize;
518
	off_t fsize;
518
	daddr_t lbn = ap->a_bn;
519
	daddr_t lbn = ap->a_bn;
519
	daddr_t *pbn = ap->a_bnp;
520
	daddr_t *pbn = ap->a_bnp;
520
	int *runp = ap->a_runp;
521
	int *runp = ap->a_runp;
Lines 547-556 fuse_vnop_bmap(struct vop_bmap_args *ap) Link Here
547
	 */
548
	 */
548
	if (runb != NULL)
549
	if (runb != NULL)
549
		*runb = MIN(lbn, maxrun);
550
		*runb = MIN(lbn, maxrun);
550
	if (runp != NULL) {
551
	if (runp != NULL && maxrun == 0)
551
		error = fuse_vnode_size(vp, &filesize, td->td_ucred, td);
552
		*runp = 0;
553
	else if (runp != NULL) {
554
		/*
555
		 * If the file's size is cached, use that value to calculate
556
		 * runp, even if the cache is expired.  runp is only advisory,
557
		 * and the risk of getting it wrong is not worth the cost of
558
		 * another upcall.
559
		 */
560
		if (fvdat->cached_attrs.va_size != VNOVAL)
561
			fsize = fvdat->cached_attrs.va_size;
562
		else
563
			error = fuse_vnode_size(vp, &fsize, td->td_ucred, td);
552
		if (error == 0)
564
		if (error == 0)
553
			*runp = MIN(MAX(0, filesize / (off_t)biosize - lbn - 1),
565
			*runp = MIN(MAX(0, fsize / (off_t)biosize - lbn - 1),
554
				    maxrun);
566
				    maxrun);
555
		else
567
		else
556
			*runp = 0;
568
			*runp = 0;
Lines 892-898 fuse_vnop_create(struct vop_create_args *ap) Link Here
892
	}
904
	}
893
	ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
905
	ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
894
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
906
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
895
		feo->attr_valid_nsec, NULL);
907
		feo->attr_valid_nsec, NULL, true);
896
908
897
	fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo);
909
	fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo);
898
	fuse_vnode_open(*vpp, foo->open_flags, td);
910
	fuse_vnode_open(*vpp, foo->open_flags, td);
Lines 1148-1154 fuse_vnop_link(struct vop_link_args *ap) Link Here
1148
		 */
1160
		 */
1149
		fuse_vnode_clear_attr_cache(tdvp);
1161
		fuse_vnode_clear_attr_cache(tdvp);
1150
		fuse_internal_cache_attrs(vp, &feo->attr, feo->attr_valid,
1162
		fuse_internal_cache_attrs(vp, &feo->attr, feo->attr_valid,
1151
			feo->attr_valid_nsec, NULL);
1163
			feo->attr_valid_nsec, NULL, true);
1152
	}
1164
	}
1153
out:
1165
out:
1154
	fdisp_destroy(&fdi);
1166
	fdisp_destroy(&fdi);
Lines 1174-1181 fuse_lookup_alloc(struct mount *mp, void *arg, int lkflags, struct vnode **vpp) Link Here
1174
1186
1175
SDT_PROBE_DEFINE3(fusefs, , vnops, cache_lookup,
1187
SDT_PROBE_DEFINE3(fusefs, , vnops, cache_lookup,
1176
	"int", "struct timespec*", "struct timespec*");
1188
	"int", "struct timespec*", "struct timespec*");
1177
SDT_PROBE_DEFINE2(fusefs, , vnops, lookup_cache_incoherent,
1178
	"struct vnode*", "struct fuse_entry_out*");
1179
/*
1189
/*
1180
    struct vnop_lookup_args {
1190
    struct vnop_lookup_args {
1181
	struct vnodeop_desc *a_desc;
1191
	struct vnodeop_desc *a_desc;
Lines 1359-1411 fuse_vnop_lookup(struct vop_lookup_args *ap) Link Here
1359
			*vpp = dvp;
1369
			*vpp = dvp;
1360
		} else {
1370
		} else {
1361
			struct fuse_vnode_data *fvdat;
1371
			struct fuse_vnode_data *fvdat;
1362
			struct vattr *vap;
1363
1372
1364
			err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
1373
			err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
1365
			    &vp, cnp, vtyp);
1374
			    &vp, cnp, vtyp);
1366
			if (err)
1375
			if (err)
1367
				goto out;
1376
				goto out;
1368
			*vpp = vp;
1377
			*vpp = vp;
1369
1370
			/*
1371
			 * In the case where we are looking up a FUSE node
1372
			 * represented by an existing cached vnode, and the
1373
			 * true size reported by FUSE_LOOKUP doesn't match
1374
			 * the vnode's cached size, then any cached writes
1375
			 * beyond the file's current size are lost.
1376
			 *
1377
			 * We can get here:
1378
			 * * following attribute cache expiration, or
1379
			 * * due a bug in the daemon, or
1380
			 */
1381
			fvdat = VTOFUD(vp);
1378
			fvdat = VTOFUD(vp);
1382
			if (vnode_isreg(vp) &&
1383
			    ((filesize != fvdat->cached_attrs.va_size &&
1384
			      fvdat->flag & FN_SIZECHANGE) ||
1385
			     ((vap = VTOVA(vp)) &&
1386
			      filesize != vap->va_size)))
1387
			{
1388
				SDT_PROBE2(fusefs, , vnops, lookup_cache_incoherent, vp, feo);
1389
				fvdat->flag &= ~FN_SIZECHANGE;
1390
				/*
1391
				 * The server changed the file's size even
1392
				 * though we had it cached, or had dirty writes
1393
				 * in the WB cache!
1394
				 */
1395
				printf("%s: cache incoherent on %s!  "
1396
		    		    "Buggy FUSE server detected.  To prevent "
1397
				    "data corruption, disable the data cache "
1398
				    "by mounting with -o direct_io, or as "
1399
				    "directed otherwise by your FUSE server's "
1400
		    		    "documentation\n", __func__,
1401
				    vnode_mount(vp)->mnt_stat.f_mntonname);
1402
				int iosize = fuse_iosize(vp);
1403
				v_inval_buf_range(vp, 0, INT64_MAX, iosize);
1404
			}
1405
1379
1406
			MPASS(feo != NULL);
1380
			MPASS(feo != NULL);
1407
			fuse_internal_cache_attrs(*vpp, &feo->attr,
1381
			fuse_internal_cache_attrs(*vpp, &feo->attr,
1408
				feo->attr_valid, feo->attr_valid_nsec, NULL);
1382
				feo->attr_valid, feo->attr_valid_nsec, NULL, true);
1409
			fuse_validity_2_bintime(feo->entry_valid,
1383
			fuse_validity_2_bintime(feo->entry_valid,
1410
				feo->entry_valid_nsec,
1384
				feo->entry_valid_nsec,
1411
				&fvdat->entry_cache_timeout);
1385
				&fvdat->entry_cache_timeout);
Lines 2592-2600 fuse_vnop_listextattr(struct vop_listextattr_args *ap) Link Here
2592
	linux_list = fdi.answ;
2566
	linux_list = fdi.answ;
2593
	/* FUSE doesn't allow the server to return more data than requested */
2567
	/* FUSE doesn't allow the server to return more data than requested */
2594
	if (fdi.iosize > linux_list_len) {
2568
	if (fdi.iosize > linux_list_len) {
2595
		printf("WARNING: FUSE protocol violation.  Server returned "
2569
		struct fuse_data *data = fuse_get_mpdata(mp);
2570
2571
		fuse_warn(data, FSESS_WARN_LSEXTATTR_LONG,
2572
			"server returned "
2596
			"more extended attribute data than requested; "
2573
			"more extended attribute data than requested; "
2597
			"should've returned ERANGE instead");
2574
			"should've returned ERANGE instead.");
2598
	} else {
2575
	} else {
2599
		/* But returning less data is fine */
2576
		/* But returning less data is fine */
2600
		linux_list_len = fdi.iosize;
2577
		linux_list_len = fdi.iosize;
(-)b/tests/sys/fs/fusefs/bmap.cc (+86 lines)
Lines 75-80 void expect_lookup(const char *relpath, uint64_t ino, off_t size) Link Here
75
}
75
}
76
};
76
};
77
77
78
class BmapEof: public Bmap, public WithParamInterface<int> {};
79
78
/*
80
/*
79
 * Test FUSE_BMAP
81
 * Test FUSE_BMAP
80
 * XXX The FUSE protocol does not include the runp and runb variables, so those
82
 * XXX The FUSE protocol does not include the runp and runb variables, so those
Lines 165-167 TEST_F(Bmap, default_) Link Here
165
167
166
	leak(fd);
168
	leak(fd);
167
}
169
}
170
171
/*
172
 * VOP_BMAP should not query the server for the file's size, even if its cached
173
 * attributes have expired.
174
 * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
175
 */
176
TEST_P(BmapEof, eof)
177
{
178
	/*
179
	 * Outline:
180
	 * 1) lookup the file, setting attr_valid=0
181
	 * 2) Read more than one block, causing the kernel to issue VOP_BMAP to
182
	 *    plan readahead.
183
	 * 3) Nothing should panic
184
	 * 4) Repeat the tests, truncating the file after different numbers of
185
	 *    GETATTR operations.
186
	 */
187
	Sequence seq;
188
	const off_t filesize = 2 * m_maxbcachebuf;
189
	const ino_t ino = 42;
190
	mode_t mode = S_IFREG | 0644;
191
	void *contents, *buf;
192
	int fd;
193
	int ngetattrs;
194
195
	ngetattrs = GetParam();
196
	contents = calloc(1, filesize);
197
	FuseTest::expect_lookup(RELPATH, ino, mode, filesize, 1, 0);
198
	expect_open(ino, 0, 1);
199
	// Depending on ngetattrs, FUSE_READ could be called with either
200
	// filesize or filesize / 2 .
201
	EXPECT_CALL(*m_mock, process(
202
	ResultOf([=](auto in) {
203
		return (in.header.opcode == FUSE_READ &&
204
			in.header.nodeid == ino &&
205
			in.body.read.offset == 0 &&
206
			( in.body.read.size == filesize ||
207
			  in.body.read.size == filesize / 2));
208
		}, Eq(true)),
209
		_)
210
	).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
211
		size_t osize = in.body.read.size;
212
		out.header.len = sizeof(struct fuse_out_header) + osize;
213
		bzero(out.body.bytes, osize);
214
	})));
215
	EXPECT_CALL(*m_mock, process(
216
		ResultOf([](auto in) {
217
			return (in.header.opcode == FUSE_GETATTR &&
218
				in.header.nodeid == ino);
219
		}, Eq(true)),
220
		_)
221
	).Times(Between(ngetattrs - 1, ngetattrs))
222
	.InSequence(seq)
223
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
224
		SET_OUT_HEADER_LEN(out, attr);
225
		out.body.attr.attr_valid = 0;
226
		out.body.attr.attr.ino = ino;
227
		out.body.attr.attr.mode = S_IFREG | 0644;
228
		out.body.attr.attr.size = filesize;
229
	})));
230
	EXPECT_CALL(*m_mock, process(
231
		ResultOf([](auto in) {
232
			return (in.header.opcode == FUSE_GETATTR &&
233
				in.header.nodeid == ino);
234
		}, Eq(true)),
235
		_)
236
	).InSequence(seq)
237
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
238
		SET_OUT_HEADER_LEN(out, attr);
239
		out.body.attr.attr_valid = 0;
240
		out.body.attr.attr.ino = ino;
241
		out.body.attr.attr.mode = S_IFREG | 0644;
242
		out.body.attr.attr.size = filesize / 2;
243
	})));
244
245
	buf = calloc(1, filesize);
246
	fd = open(FULLPATH, O_RDWR);
247
	ASSERT_LE(0, fd) << strerror(errno);
248
	read(fd, buf, filesize);
249
}
250
251
INSTANTIATE_TEST_CASE_P(BE, BmapEof,
252
	Values(1, 2, 3)
253
);
(-)b/tests/sys/fs/fusefs/write.cc (+81 lines)
Lines 208-213 virtual void SetUp() { Link Here
208
}
208
}
209
};
209
};
210
210
211
class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int>
212
{};
213
211
void sigxfsz_handler(int __unused sig) {
214
void sigxfsz_handler(int __unused sig) {
212
	Write::s_sigxfsz = 1;
215
	Write::s_sigxfsz = 1;
213
}
216
}
Lines 526-531 TEST_F(Write, eof_during_rmw) Link Here
526
	leak(fd);
529
	leak(fd);
527
}
530
}
528
531
532
/*
533
 * VOP_STRATEGY should not query the server for the file's size, even if its
534
 * cached attributes have expired.
535
 * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
536
 */
537
TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy)
538
{
539
	const char FULLPATH[] = "mountpoint/some_file.txt";
540
	const char RELPATH[] = "some_file.txt";
541
	Sequence seq;
542
	const off_t filesize = 2 * m_maxbcachebuf;
543
	void *contents;
544
	uint64_t ino = 42;
545
	uint64_t attr_valid = 0;
546
	uint64_t attr_valid_nsec = 0;
547
	mode_t mode = S_IFREG | 0644;
548
	int fd;
549
	int ngetattrs;
550
551
	ngetattrs = GetParam();
552
	contents = calloc(1, filesize);
553
554
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
555
	.WillRepeatedly(Invoke(
556
		ReturnImmediate([=](auto in __unused, auto& out) {
557
		SET_OUT_HEADER_LEN(out, entry);
558
		out.body.entry.attr.mode = mode;
559
		out.body.entry.nodeid = ino;
560
		out.body.entry.attr.nlink = 1;
561
		out.body.entry.attr.size = filesize;
562
		out.body.entry.attr_valid = attr_valid;
563
		out.body.entry.attr_valid_nsec = attr_valid_nsec;
564
	})));
565
	expect_open(ino, 0, 1);
566
	EXPECT_CALL(*m_mock, process(
567
		ResultOf([=](auto in) {
568
			return (in.header.opcode == FUSE_GETATTR &&
569
				in.header.nodeid == ino);
570
		}, Eq(true)),
571
		_)
572
	).Times(Between(ngetattrs - 1, ngetattrs))
573
	.InSequence(seq)
574
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
575
		SET_OUT_HEADER_LEN(out, attr);
576
		out.body.attr.attr.ino = ino;
577
		out.body.attr.attr.mode = mode;
578
		out.body.attr.attr_valid = attr_valid;
579
		out.body.attr.attr_valid_nsec = attr_valid_nsec;
580
		out.body.attr.attr.size = filesize;
581
	})));
582
	EXPECT_CALL(*m_mock, process(
583
		ResultOf([=](auto in) {
584
			return (in.header.opcode == FUSE_GETATTR &&
585
				in.header.nodeid == ino);
586
		}, Eq(true)),
587
		_)
588
	).InSequence(seq)
589
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
590
		SET_OUT_HEADER_LEN(out, attr);
591
		out.body.attr.attr.ino = ino;
592
		out.body.attr.attr.mode = mode;
593
		out.body.attr.attr_valid = attr_valid;
594
		out.body.attr.attr_valid_nsec = attr_valid_nsec;
595
		out.body.attr.attr.size = filesize / 2;
596
	})));
597
	expect_write(ino, 0, filesize / 2, filesize / 2, contents);
598
599
	fd = open(FULLPATH, O_RDWR);
600
	ASSERT_LE(0, fd) << strerror(errno);
601
	ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2))
602
		<< strerror(errno);
603
604
}
605
606
INSTANTIATE_TEST_CASE_P(W, WriteEofDuringVnopStrategy,
607
	Values(1, 2, 3)
608
);
609
529
/*
610
/*
530
 * If the kernel cannot be sure which uid, gid, or pid was responsible for a
611
 * If the kernel cannot be sure which uid, gid, or pid was responsible for a
531
 * write, then it must set the FUSE_WRITE_CACHE bit
612
 * write, then it must set the FUSE_WRITE_CACHE bit

Return to bug 256937