Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 27 May 2019 21:36:28 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r348313 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs
Message-ID:  <201905272136.x4RLaSKY035361@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: asomers
Date: Mon May 27 21:36:28 2019
New Revision: 348313
URL: https://svnweb.freebsd.org/changeset/base/348313

Log:
  fusefs: set FUSE_WRITE_CACHE when writing from cache
  
  This bit tells the server that we're not sure which uid, gid, and/or pid
  originated the write.  I don't know of a single file system that cares, but
  it's part of the protocol.
  
  Sponsored by:	The FreeBSD Foundation

Modified:
  projects/fuse2/sys/fs/fuse/fuse_io.c
  projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc
  projects/fuse2/tests/sys/fs/fusefs/fsync.cc
  projects/fuse2/tests/sys/fs/fusefs/utils.cc
  projects/fuse2/tests/sys/fs/fusefs/utils.hh
  projects/fuse2/tests/sys/fs/fusefs/write.cc

Modified: projects/fuse2/sys/fs/fuse/fuse_io.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_io.c	Mon May 27 20:22:54 2019	(r348312)
+++ projects/fuse2/sys/fs/fuse/fuse_io.c	Mon May 27 21:36:28 2019	(r348313)
@@ -119,7 +119,7 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio
 static int 
 fuse_write_directbackend(struct vnode *vp, struct uio *uio,
     struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
-    int ioflag);
+    int ioflag, bool pages);
 static int 
 fuse_write_biobackend(struct vnode *vp, struct uio *uio,
     struct ucred *cred, struct fuse_filehandle *fufh, int ioflag, pid_t pid);
@@ -245,7 +245,7 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, in
 			if (!pages )
 				v_inval_buf_range(vp, start, end, iosize);
 			err = fuse_write_directbackend(vp, uio, cred, fufh,
-				filesize, ioflag);
+				filesize, ioflag, pages);
 		} else {
 			SDT_PROBE2(fusefs, , io, trace, 1,
 				"buffered write of vnode");
@@ -405,7 +405,7 @@ out:
 static int
 fuse_write_directbackend(struct vnode *vp, struct uio *uio,
     struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
-    int ioflag)
+    int ioflag, bool pages)
 {
 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
 	struct fuse_data *data;
@@ -418,9 +418,29 @@ fuse_write_directbackend(struct vnode *vp, struct uio 
 	int diff;
 	int err = 0;
 	bool direct_io = fufh->fuse_open_flags & FOPEN_DIRECT_IO;
+	uint32_t write_flags;
 
 	data = fuse_get_mpdata(vp->v_mount);
 
+	/* 
+	 * Don't set FUSE_WRITE_LOCKOWNER in write_flags.  It can't be set
+	 * accurately when using POSIX AIO, libfuse doesn't use it, and I'm not
+	 * aware of any file systems that do.  It was an attempt to add
+	 * Linux-style mandatory locking to the FUSE protocol, but mandatory
+	 * locking is deprecated even on Linux.  See Linux commit
+	 * f33321141b273d60cbb3a8f56a5489baad82ba5e .
+	 */
+	/*
+	 * Set FUSE_WRITE_CACHE whenever we don't know the uid, gid, and/or pid
+	 * that originated a write.  For example when writing from the
+	 * writeback cache.  I don't know of a single file system that cares,
+	 * but the protocol says we're supposed to do this.
+	 */
+	write_flags = !pages && (
+		(ioflag & IO_DIRECT) ||
+		!fsess_opt_datacache(vnode_mount(vp)) ||
+		fuse_data_cache_mode != FUSE_CACHE_WB) ? 0 : FUSE_WRITE_CACHE;
+
 	if (uio->uio_resid == 0)
 		return (0);
 
@@ -439,17 +459,8 @@ fuse_write_directbackend(struct vnode *vp, struct uio 
 		fwi->fh = fufh->fh_id;
 		fwi->offset = uio->uio_offset;
 		fwi->size = chunksize;
+		fwi->write_flags = write_flags;
 		if (fuse_libabi_geq(data, 7, 9)) {
-			/* 
-			 * Don't set FUSE_WRITE_LOCKOWNER.  It can't be set
-			 * accurately when using POSIX AIO, libfuse doesn't use
-			 * it, and I'm not aware of any file systems that do.
-			 * It was an attempt to add Linux-style mandatory
-			 * locking to the FUSE protocol, but mandatory locking
-			 * is deprecated even on Linux.  See Linux commit
-			 * f33321141b273d60cbb3a8f56a5489baad82ba5e .
-			 */
-			fwi->write_flags = 0;
 			fwi->flags = 0;		/* TODO */
 			fwi_data = (char *)fdi.indata + sizeof(*fwi);
 		} else {
@@ -525,6 +536,7 @@ retry:
 				fwi->fh = fufh->fh_id;
 				fwi->offset = as_written_offset;
 				fwi->size = diff;
+				fwi->write_flags = write_flags;
 				goto retry;
 			}
 		}
@@ -884,7 +896,7 @@ fuse_io_strategy(struct vnode *vp, struct buf *bp)
 			uiop->uio_rw = UIO_WRITE;
 
 			error = fuse_write_directbackend(vp, uiop, cred, fufh,
-				filesize, 0);
+				filesize, 0, false);
 
 			if (error == EINTR || error == ETIMEDOUT
 			    || (!error && (bp->b_flags & B_NEEDCOMMIT))) {

Modified: projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc	Mon May 27 20:22:54 2019	(r348312)
+++ projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc	Mon May 27 21:36:28 2019	(r348313)
@@ -1229,7 +1229,7 @@ TEST_F(Write, clear_suid)
 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
 	expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, wbuf);
+	expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
 	expect_chmod(ino, newmode, sizeof(wbuf));
 
 	fd = open(FULLPATH, O_WRONLY);
@@ -1255,7 +1255,7 @@ TEST_F(Write, clear_sgid)
 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
 	expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, wbuf);
+	expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
 	expect_chmod(ino, newmode, sizeof(wbuf));
 
 	fd = open(FULLPATH, O_WRONLY);
@@ -1285,7 +1285,7 @@ TEST_F(Write, recursion_panic_while_clearing_suid)
 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
 	expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, wbuf);
+	expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
 	/* XXX Return a smaller file size than what we just wrote! */
 	expect_chmod(ino, newmode, 0);
 

Modified: projects/fuse2/tests/sys/fs/fusefs/fsync.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/fsync.cc	Mon May 27 20:22:54 2019	(r348312)
+++ projects/fuse2/tests/sys/fs/fusefs/fsync.cc	Mon May 27 21:36:28 2019	(r348313)
@@ -75,7 +75,7 @@ void expect_lookup(const char *relpath, uint64_t ino)
 
 void expect_write(uint64_t ino, uint64_t size, const void *contents)
 {
-	FuseTest::expect_write(ino, 0, size, size, 0, contents);
+	FuseTest::expect_write(ino, 0, size, size, 0, 0, contents);
 }
 
 };

Modified: projects/fuse2/tests/sys/fs/fusefs/utils.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/utils.cc	Mon May 27 20:22:54 2019	(r348312)
+++ projects/fuse2/tests/sys/fs/fusefs/utils.cc	Mon May 27 21:36:28 2019	(r348313)
@@ -377,15 +377,17 @@ void FuseTest::expect_unlink(uint64_t parent, const ch
 }
 
 void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
-	uint64_t osize, uint32_t flags, const void *contents)
+	uint64_t osize, uint32_t flags_set, uint32_t flags_unset,
+	const void *contents)
 {
 	EXPECT_CALL(*m_mock, process(
 		ResultOf([=](auto in) {
 			const char *buf = (const char*)in.body.bytes +
 				sizeof(struct fuse_write_in);
 			bool pid_ok;
+			uint32_t wf = in.body.write.write_flags;
 
-			if (in.body.write.write_flags & FUSE_WRITE_CACHE)
+			if (wf & FUSE_WRITE_CACHE)
 				pid_ok = true;
 			else
 				pid_ok = (pid_t)in.header.pid == getpid();
@@ -396,7 +398,8 @@ void FuseTest::expect_write(uint64_t ino, uint64_t off
 				in.body.write.offset == offset  &&
 				in.body.write.size == isize &&
 				pid_ok &&
-				in.body.write.write_flags == flags &&
+				(wf & flags_set) == flags_set &&
+				(wf & flags_unset) == 0 &&
 				0 == bcmp(buf, contents, isize));
 		}, Eq(true)),
 		_)
@@ -407,7 +410,7 @@ void FuseTest::expect_write(uint64_t ino, uint64_t off
 }
 
 void FuseTest::expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
-	uint64_t osize, uint32_t flags, const void *contents)
+	uint64_t osize, const void *contents)
 {
 	EXPECT_CALL(*m_mock, process(
 		ResultOf([=](auto in) {
@@ -420,7 +423,6 @@ void FuseTest::expect_write_7_8(uint64_t ino, uint64_t
 				in.body.write.offset == offset  &&
 				in.body.write.size == isize &&
 				pid_ok &&
-				in.body.write.write_flags == flags &&
 				0 == bcmp(buf, contents, isize));
 		}, Eq(true)),
 		_)

Modified: projects/fuse2/tests/sys/fs/fusefs/utils.hh
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/utils.hh	Mon May 27 20:22:54 2019	(r348312)
+++ projects/fuse2/tests/sys/fs/fusefs/utils.hh	Mon May 27 21:36:28 2019	(r348313)
@@ -169,15 +169,18 @@ class FuseTest : public ::testing::Test {
 
 	/*
 	 * Create an expectation that FUSE_WRITE will be called exactly once
-	 * for the given inode, at offset offset, with write_flags flags, 
-	 * size isize and buffer contents.  It will return osize
+	 * for the given inode, at offset offset, with  size isize and buffer
+	 * contents.  Any flags present in flags_set must be set, and any
+	 * present in flags_unset must not be set.  Other flags are don't care.
+	 * It will return osize.
 	 */
 	void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
-		uint64_t osize, uint32_t flags, const void *contents);
+		uint64_t osize, uint32_t flags_set, uint32_t flags_unset,
+		const void *contents);
 
 	/* Protocol 7.8 version of expect_write */
 	void expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
-		uint64_t osize, uint32_t flags, const void *contents);
+		uint64_t osize, const void *contents);
 
 	/*
 	 * Helper that runs code in a child process.

Modified: projects/fuse2/tests/sys/fs/fusefs/write.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/write.cc	Mon May 27 20:22:54 2019	(r348312)
+++ projects/fuse2/tests/sys/fs/fusefs/write.cc	Mon May 27 21:36:28 2019	(r348313)
@@ -65,6 +65,12 @@ void expect_release(uint64_t ino, ProcessMockerT r)
 	).WillRepeatedly(Invoke(r));
 }
 
+void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
+	uint64_t osize, const void *contents)
+{
+	FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
+}
+
 };
 
 class Write_7_8: public FuseTest {
@@ -100,7 +106,7 @@ virtual void SetUp() {
 
 /* Tests for the write-through cache mode */
 class WriteThrough: public Write {
-
+public:
 virtual void SetUp() {
 	const char *cache_mode_node = "vfs.fusefs.data_cache_mode";
 	int val = 0;
@@ -117,11 +123,17 @@ virtual void SetUp() {
 			"(writethrough) for this test";
 }
 
+void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
+	uint64_t osize, const void *contents)
+{
+	FuseTest::expect_write(ino, offset, isize, osize, 0, FUSE_WRITE_CACHE,
+		contents);
+}
 };
 
 /* Tests for the writeback cache mode */
 class WriteBack: public Write {
-
+public:
 virtual void SetUp() {
 	const char *node = "vfs.fusefs.data_cache_mode";
 	int val = 0;
@@ -138,6 +150,12 @@ virtual void SetUp() {
 			"(writeback) for this test";
 }
 
+void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
+	uint64_t osize, const void *contents)
+{
+	FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
+		contents);
+}
 };
 
 /* AIO writes need to set the header's pid field correctly */
@@ -155,7 +173,7 @@ TEST_F(AioWrite, DISABLED_aio_write)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
+	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_WRONLY);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -196,7 +214,7 @@ TEST_F(Write, append)
 
 	expect_lookup(RELPATH, ino, initial_offset);
 	expect_open(ino, 0, 1);
-	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
+	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
 
 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
 	fd = open(FULLPATH, O_RDWR | O_APPEND);
@@ -218,7 +236,7 @@ TEST_F(Write, append_direct_io)
 
 	expect_lookup(RELPATH, ino, initial_offset);
 	expect_open(ino, FOPEN_DIRECT_IO, 1);
-	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
+	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
 
 	fd = open(FULLPATH, O_WRONLY | O_APPEND);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -242,7 +260,7 @@ TEST_F(Write, direct_io_evicts_cache)
 	expect_lookup(RELPATH, ino, bufsize);
 	expect_open(ino, 0, 1);
 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
-	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1);
+	expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
 
 	fd = open(FULLPATH, O_RDWR);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -285,9 +303,8 @@ TEST_F(Write, indirect_io_short_write)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, bufsize, bufsize0, 0, CONTENTS);
-	expect_write(ino, bufsize0, bufsize1, bufsize1, 0,
-		contents1);
+	expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
+	expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
 
 	fd = open(FULLPATH, O_WRONLY);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -312,7 +329,7 @@ TEST_F(Write, direct_io_short_write)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, FOPEN_DIRECT_IO, 1);
-	expect_write(ino, 0, bufsize, halfbufsize, 0, CONTENTS);
+	expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_WRONLY);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -342,7 +359,7 @@ TEST_F(Write, direct_io_short_write_iov)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, FOPEN_DIRECT_IO, 1);
-	expect_write(ino, 0, totalsize, size0, 0, EXPECTED0);
+	expect_write(ino, 0, totalsize, size0, EXPECTED0);
 
 	fd = open(FULLPATH, O_WRONLY);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -388,9 +405,8 @@ TEST_F(Write, mmap)
 	/* 
 	 * Writes from the pager may or may not be associated with the correct
 	 * pid, so they must set FUSE_WRITE_CACHE.
-	 * TODO: expect FUSE_WRITE_CACHE after upgrading to protocol 7.9
 	 */
-	expect_write(ino, 0, len, len, 0, expected);
+	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
 	expect_flush(ino, 1, ReturnErrno(0));
 	expect_release(ino, ReturnErrno(0));
 
@@ -435,7 +451,7 @@ TEST_F(WriteThrough, evicts_read_cache)
 	expect_lookup(RELPATH, ino, bufsize);
 	expect_open(ino, 0, 1);
 	expect_read(ino, 0, bufsize, bufsize, contents0);
-	expect_write(ino, 0, wrsize, wrsize, 0, contents1);
+	expect_write(ino, 0, wrsize, wrsize, contents1);
 
 	fd = open(FULLPATH, O_RDWR);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -468,7 +484,7 @@ TEST_F(WriteThrough, pwrite)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
+	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_WRONLY);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -489,7 +505,7 @@ TEST_F(Write, write)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
+	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_WRONLY);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -518,8 +534,8 @@ TEST_F(Write, write_large)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, halfbufsize, halfbufsize, 0, contents);
-	expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0,
+	expect_write(ino, 0, halfbufsize, halfbufsize, contents);
+	expect_write(ino, halfbufsize, halfbufsize, halfbufsize,
 		&contents[halfbufsize / sizeof(int)]);
 
 	fd = open(FULLPATH, O_WRONLY);
@@ -561,7 +577,7 @@ TEST_F(Write_7_8, write)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write_7_8(ino, 0, bufsize, bufsize, 0, CONTENTS);
+	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_WRONLY);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -582,7 +598,7 @@ TEST_F(WriteBack, close)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
+	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
 	EXPECT_CALL(*m_mock, process(
 		ResultOf([=](auto in) {
 			return (in.header.opcode == FUSE_SETATTR);
@@ -621,7 +637,7 @@ TEST_F(WriteBack, rmw)
 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
 	expect_open(ino, 0, 1);
 	expect_read(ino, 0, fsize, fsize, INITIAL);
-	expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
+	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_WRONLY);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -646,7 +662,7 @@ TEST_F(WriteBack, writeback)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
+	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_RDWR);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -678,7 +694,8 @@ TEST_F(WriteBack, o_direct)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
+	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
+		CONTENTS);
 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
@@ -711,7 +728,7 @@ TEST_F(WriteThrough, DISABLED_writethrough)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
+	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_RDWR);
 	EXPECT_LE(0, fd) << strerror(errno);
@@ -738,7 +755,7 @@ TEST_F(WriteThrough, update_file_size)
 
 	expect_lookup(RELPATH, ino, 0);
 	expect_open(ino, 0, 1);
-	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
+	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
 
 	fd = open(FULLPATH, O_RDWR);
 	EXPECT_LE(0, fd) << strerror(errno);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201905272136.x4RLaSKY035361>