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

Collapse All | Expand All

(-)b/sys/fs/fuse/fuse_internal.c (-23 / +51 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 806-812 fuse_internal_newentry_core(struct vnode *dvp, Link Here
806
	fuse_vnode_clear_attr_cache(dvp);
852
	fuse_vnode_clear_attr_cache(dvp);
807
853
808
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
854
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
809
		feo->attr_valid_nsec, NULL);
855
		feo->attr_valid_nsec, NULL, true);
810
856
811
	return err;
857
	return err;
812
}
858
}
Lines 912-937 fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap, Link Here
912
		fao->attr.mtime = old_mtime.tv_sec;
958
		fao->attr.mtime = old_mtime.tv_sec;
913
		fao->attr.mtimensec = old_mtime.tv_nsec;
959
		fao->attr.mtimensec = old_mtime.tv_nsec;
914
	}
960
	}
915
	if (vnode_isreg(vp) &&
916
	    fvdat->cached_attrs.va_size != VNOVAL &&
917
	    fao->attr.size != fvdat->cached_attrs.va_size) {
918
		/*
919
		 * The server changed the file's size even though we had it
920
		 * cached!  That's a server bug.
921
		 */
922
		struct mount *mp = vnode_mount(vp);
923
		struct fuse_data *data = fuse_get_mpdata(mp);
924
925
		fuse_warn(data, FSESS_WARN_CACHE_INCOHERENT,
926
		    "cache incoherent!  "
927
		    "To prevent data corruption, disable the data cache "
928
		    "by mounting with -o direct_io, or as directed "
929
		    "otherwise by your FUSE server's documentation.");
930
		int iosize = fuse_iosize(vp);
931
		v_inval_buf_range(vp, 0, INT64_MAX, iosize);
932
	}
933
	fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
961
	fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
934
		fao->attr_valid_nsec, vap);
962
		fao->attr_valid_nsec, vap, true);
935
	if (vtyp != vnode_vtype(vp)) {
963
	if (vtyp != vnode_vtype(vp)) {
936
		fuse_internal_vnode_disappear(vp);
964
		fuse_internal_vnode_disappear(vp);
937
		err = ENOENT;
965
		err = ENOENT;
Lines 1231-1237 int fuse_internal_setattr(struct vnode *vp, struct vattr *vap, Link Here
1231
		struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
1259
		struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
1232
		fuse_vnode_undirty_cached_timestamps(vp);
1260
		fuse_vnode_undirty_cached_timestamps(vp);
1233
		fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
1261
		fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
1234
			fao->attr_valid_nsec, NULL);
1262
			fao->attr_valid_nsec, NULL, false);
1235
	}
1263
	}
1236
1264
1237
out:
1265
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 (-9 / +14 lines)
Lines 571-577 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
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 1007-1019 fuse_io_strategy(struct vnode *vp, struct buf *bp) Link Here
1007
		/*
1007
		/*
1008
	         * Setup for actual write
1008
	         * Setup for actual write
1009
	         */
1009
	         */
1010
		error = fuse_vnode_size(vp, &filesize, cred, curthread);
1010
		/*
1011
		if (error) {
1011
		 * If the file's size is cached, use that value, even if the
1012
			bp->b_ioflags |= BIO_ERROR;
1012
		 * cache is expired.  At this point we're already committed to
1013
			bp->b_error = error;
1013
		 * writing something.  If the FUSE server has changed the
1014
			bufdone(bp);
1014
		 * file's size behind our back, it's too late for us to do
1015
			return (error);
1015
		 * anything about it.  In particular, we can't invalidate any
1016
		}
1016
		 * part of the file's buffers because VOP_STRATEGY is called
1017
		 * with them already locked.
1018
		 */
1019
		filesize = fvdat->cached_attrs.va_size;
1020
		/* filesize must've been cached by fuse_vnop_open.  */
1021
		KASSERT(filesize != VNOVAL, ("filesize should've been cached"));
1017
1022
1018
		if ((off_t)bp->b_lblkno * biosize + bp->b_dirtyend > filesize)
1023
		if ((off_t)bp->b_lblkno * biosize + bp->b_dirtyend > filesize)
1019
			bp->b_dirtyend = filesize - 
1024
			bp->b_dirtyend = filesize - 
(-)b/sys/fs/fuse/fuse_node.c (-3 / +16 lines)
Lines 389-399 fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid) Link Here
389
}
389
}
390
390
391
/*
391
/*
392
 * Adjust the vnode's size to a new value, such as that provided by
392
 * Adjust the vnode's size to a new value.
393
 * FUSE_GETATTR.
393
 *
394
 * If the new value came from the server, such as from a FUSE_GETATTR
395
 * operation, set `from_server` true.  But if it came from a local operation,
396
 * such as write(2) or truncate(2), set `from_server` false.
394
 */
397
 */
395
int
398
int
396
fuse_vnode_setsize(struct vnode *vp, off_t newsize)
399
fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server)
397
{
400
{
398
	struct fuse_vnode_data *fvdat = VTOFUD(vp);
401
	struct fuse_vnode_data *fvdat = VTOFUD(vp);
399
	struct vattr *attrs;
402
	struct vattr *attrs;
Lines 434-439 fuse_vnode_setsize(struct vnode *vp, off_t newsize) Link Here
434
		MPASS(bp->b_flags & B_VMIO);
437
		MPASS(bp->b_flags & B_VMIO);
435
		vfs_bio_clrbuf(bp);
438
		vfs_bio_clrbuf(bp);
436
		bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
439
		bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
440
	} else if (from_server && newsize > oldsize && oldsize != VNOVAL) {
441
		/*
442
		 * The FUSE server changed the file size behind our back.  We
443
		 * should invalidate the entire cache.
444
		 */
445
		daddr_t left_lbn, end_lbn;
446
447
		left_lbn = oldsize / iosize;
448
		end_lbn = howmany(newsize, iosize);
449
		v_inval_buf_range(vp, 0, end_lbn, iosize);
437
	}
450
	}
438
out:
451
out:
439
	if (bp)
452
	if (bp)
(-)b/sys/fs/fuse/fuse_node.h (-1 / +1 lines)
Lines 201-207 void fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, Link Here
201
201
202
int fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid);
202
int fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid);
203
203
204
int fuse_vnode_setsize(struct vnode *vp, off_t newsize);
204
int fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server);
205
205
206
void fuse_vnode_undirty_cached_timestamps(struct vnode *vp);
206
void fuse_vnode_undirty_cached_timestamps(struct vnode *vp);
207
207
(-)b/sys/fs/fuse/fuse_vfsops.c (-39 / +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-622 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
		if (data->cache_mode == fuse_data_cache_mode) {
596
			const char *msg;
597
598
			if (fuse_libabi_geq(data, 7, 23)) {
599
				msg = "writeback cache incoherent!."
600
				    "To prevent data corruption, disable "
601
				    "the writeback cache according to your "
602
				    "FUSE server's documentation.";
603
			} else {
604
				msg = "writeback cache incoherent!."
605
				    "To prevent data corruption, disable "
606
				    "the writeback cache by setting "
607
				    "vfs.fusefs.data_cache_mode to 0 or 1.";
608
			}
609
			fuse_warn(data, FSESS_WARN_WB_CACHE_INCOHERENT, msg);
610
		} else {
611
			/* If we get here, it's likely a fusefs kernel bug */
612
			printf("%s: WB cache incoherent on %s!\n", __func__,
613
			    vnode_mount(*vpp)->mnt_stat.f_mntonname);
614
		}
615
		fvdat->flag &= ~FN_SIZECHANGE;
616
	}
617
579
618
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
580
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
619
		feo->attr_valid_nsec, NULL);
581
		feo->attr_valid_nsec, NULL, true);
620
	fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec,
582
	fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec,
621
		&fvdat->entry_cache_timeout);
583
		&fvdat->entry_cache_timeout);
622
out:
584
out:
(-)b/sys/fs/fuse/fuse_vnops.c (-42 / +19 lines)
Lines 516-523 fuse_vnop_bmap(struct vop_bmap_args *ap) Link Here
516
	struct fuse_bmap_in *fbi;
516
	struct fuse_bmap_in *fbi;
517
	struct fuse_bmap_out *fbo;
517
	struct fuse_bmap_out *fbo;
518
	struct fuse_data *data;
518
	struct fuse_data *data;
519
	struct fuse_vnode_data *fvdat = VTOFUD(vp);
519
	uint64_t biosize;
520
	uint64_t biosize;
520
	off_t filesize;
521
	off_t fsize;
521
	daddr_t lbn = ap->a_bn;
522
	daddr_t lbn = ap->a_bn;
522
	daddr_t *pbn = ap->a_bnp;
523
	daddr_t *pbn = ap->a_bnp;
523
	int *runp = ap->a_runp;
524
	int *runp = ap->a_runp;
Lines 550-559 fuse_vnop_bmap(struct vop_bmap_args *ap) Link Here
550
	 */
551
	 */
551
	if (runb != NULL)
552
	if (runb != NULL)
552
		*runb = MIN(lbn, maxrun);
553
		*runb = MIN(lbn, maxrun);
553
	if (runp != NULL) {
554
	if (runp != NULL && maxrun == 0)
554
		error = fuse_vnode_size(vp, &filesize, td->td_ucred, td);
555
		*runp = 0;
556
	else if (runp != NULL) {
557
		/*
558
		 * If the file's size is cached, use that value to calculate
559
		 * runp, even if the cache is expired.  runp is only advisory,
560
		 * and the risk of getting it wrong is not worth the cost of
561
		 * another upcall.
562
		 */
563
		if (fvdat->cached_attrs.va_size != VNOVAL)
564
			fsize = fvdat->cached_attrs.va_size;
565
		else
566
			error = fuse_vnode_size(vp, &fsize, td->td_ucred, td);
555
		if (error == 0)
567
		if (error == 0)
556
			*runp = MIN(MAX(0, filesize / (off_t)biosize - lbn - 1),
568
			*runp = MIN(MAX(0, fsize / (off_t)biosize - lbn - 1),
557
				    maxrun);
569
				    maxrun);
558
		else
570
		else
559
			*runp = 0;
571
			*runp = 0;
Lines 895-901 fuse_vnop_create(struct vop_create_args *ap) Link Here
895
	}
907
	}
896
	ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
908
	ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
897
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
909
	fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
898
		feo->attr_valid_nsec, NULL);
910
		feo->attr_valid_nsec, NULL, true);
899
911
900
	fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo);
912
	fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo);
901
	fuse_vnode_open(*vpp, foo->open_flags, td);
913
	fuse_vnode_open(*vpp, foo->open_flags, td);
Lines 1151-1157 fuse_vnop_link(struct vop_link_args *ap) Link Here
1151
		 */
1163
		 */
1152
		fuse_vnode_clear_attr_cache(tdvp);
1164
		fuse_vnode_clear_attr_cache(tdvp);
1153
		fuse_internal_cache_attrs(vp, &feo->attr, feo->attr_valid,
1165
		fuse_internal_cache_attrs(vp, &feo->attr, feo->attr_valid,
1154
			feo->attr_valid_nsec, NULL);
1166
			feo->attr_valid_nsec, NULL, true);
1155
	}
1167
	}
1156
out:
1168
out:
1157
	fdisp_destroy(&fdi);
1169
	fdisp_destroy(&fdi);
Lines 1360-1411 fuse_vnop_lookup(struct vop_lookup_args *ap) Link Here
1360
			*vpp = dvp;
1372
			*vpp = dvp;
1361
		} else {
1373
		} else {
1362
			struct fuse_vnode_data *fvdat;
1374
			struct fuse_vnode_data *fvdat;
1363
			struct vattr *vap;
1364
1375
1365
			err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
1376
			err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
1366
			    &vp, cnp, vtyp);
1377
			    &vp, cnp, vtyp);
1367
			if (err)
1378
			if (err)
1368
				goto out;
1379
				goto out;
1369
			*vpp = vp;
1380
			*vpp = vp;
1370
1371
			/*
1372
			 * In the case where we are looking up a FUSE node
1373
			 * represented by an existing cached vnode, and the
1374
			 * true size reported by FUSE_LOOKUP doesn't match
1375
			 * the vnode's cached size, then any cached writes
1376
			 * beyond the file's current size are lost.
1377
			 *
1378
			 * We can get here:
1379
			 * * following attribute cache expiration, or
1380
			 * * due a bug in the daemon, or
1381
			 */
1382
			fvdat = VTOFUD(vp);
1381
			fvdat = VTOFUD(vp);
1383
			if (vnode_isreg(vp) &&
1384
			    ((filesize != fvdat->cached_attrs.va_size &&
1385
			      fvdat->flag & FN_SIZECHANGE) ||
1386
			     ((vap = VTOVA(vp)) &&
1387
			      filesize != vap->va_size)))
1388
			{
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
				fuse_warn(data, FSESS_WARN_CACHE_INCOHERENT,
1396
				    "cache incoherent!  "
1397
				    "To prevent "
1398
				    "data corruption, disable the data cache "
1399
				    "by mounting with -o direct_io, or as "
1400
				    "directed otherwise by your FUSE server's "
1401
				    "documentation.");
1402
				int iosize = fuse_iosize(vp);
1403
				v_inval_buf_range(vp, 0, INT64_MAX, iosize);
1404
			}
1405
1382
1406
			MPASS(feo != NULL);
1383
			MPASS(feo != NULL);
1407
			fuse_internal_cache_attrs(*vpp, &feo->attr,
1384
			fuse_internal_cache_attrs(*vpp, &feo->attr,
1408
				feo->attr_valid, feo->attr_valid_nsec, NULL);
1385
				feo->attr_valid, feo->attr_valid_nsec, NULL, true);
1409
			fuse_validity_2_bintime(feo->entry_valid,
1386
			fuse_validity_2_bintime(feo->entry_valid,
1410
				feo->entry_valid_nsec,
1387
				feo->entry_valid_nsec,
1411
				&fvdat->entry_cache_timeout);
1388
				&fvdat->entry_cache_timeout);
(-)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