xref: /freebsd/tests/sys/fs/fusefs/fsync.cc (revision 9821f1d3231d4dea3e1319358f416425be2266a6)
1*9821f1d3SAlan Somers /*-
2*9821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*9821f1d3SAlan Somers  *
4*9821f1d3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
5*9821f1d3SAlan Somers  *
6*9821f1d3SAlan Somers  * This software was developed by BFF Storage Systems, LLC under sponsorship
7*9821f1d3SAlan Somers  * from the FreeBSD Foundation.
8*9821f1d3SAlan Somers  *
9*9821f1d3SAlan Somers  * Redistribution and use in source and binary forms, with or without
10*9821f1d3SAlan Somers  * modification, are permitted provided that the following conditions
11*9821f1d3SAlan Somers  * are met:
12*9821f1d3SAlan Somers  * 1. Redistributions of source code must retain the above copyright
13*9821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer.
14*9821f1d3SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
15*9821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
16*9821f1d3SAlan Somers  *    documentation and/or other materials provided with the distribution.
17*9821f1d3SAlan Somers  *
18*9821f1d3SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*9821f1d3SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*9821f1d3SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*9821f1d3SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*9821f1d3SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*9821f1d3SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*9821f1d3SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*9821f1d3SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*9821f1d3SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*9821f1d3SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*9821f1d3SAlan Somers  * SUCH DAMAGE.
29*9821f1d3SAlan Somers  */
30*9821f1d3SAlan Somers 
31*9821f1d3SAlan Somers extern "C" {
32*9821f1d3SAlan Somers #include <aio.h>
33*9821f1d3SAlan Somers #include <fcntl.h>
34*9821f1d3SAlan Somers #include <unistd.h>
35*9821f1d3SAlan Somers }
36*9821f1d3SAlan Somers 
37*9821f1d3SAlan Somers #include "mockfs.hh"
38*9821f1d3SAlan Somers #include "utils.hh"
39*9821f1d3SAlan Somers 
40*9821f1d3SAlan Somers using namespace testing;
41*9821f1d3SAlan Somers 
42*9821f1d3SAlan Somers /*
43*9821f1d3SAlan Somers  * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28.
44*9821f1d3SAlan Somers  * This bit was actually part of kernel protocol version 5.2, but never
45*9821f1d3SAlan Somers  * documented until after 7.28
46*9821f1d3SAlan Somers  */
47*9821f1d3SAlan Somers #ifndef FUSE_FSYNC_FDATASYNC
48*9821f1d3SAlan Somers #define FUSE_FSYNC_FDATASYNC 1
49*9821f1d3SAlan Somers #endif
50*9821f1d3SAlan Somers 
51*9821f1d3SAlan Somers class Fsync: public FuseTest {
52*9821f1d3SAlan Somers public:
53*9821f1d3SAlan Somers void expect_fsync(uint64_t ino, uint32_t flags, int error)
54*9821f1d3SAlan Somers {
55*9821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
56*9821f1d3SAlan Somers 		ResultOf([=](auto in) {
57*9821f1d3SAlan Somers 			return (in->header.opcode == FUSE_FSYNC &&
58*9821f1d3SAlan Somers 				in->header.nodeid == ino &&
59*9821f1d3SAlan Somers 				in->body.fsync.fh == FH &&
60*9821f1d3SAlan Somers 				in->body.fsync.fsync_flags == flags);
61*9821f1d3SAlan Somers 		}, Eq(true)),
62*9821f1d3SAlan Somers 		_)
63*9821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnErrno(error)));
64*9821f1d3SAlan Somers }
65*9821f1d3SAlan Somers 
66*9821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino)
67*9821f1d3SAlan Somers {
68*9821f1d3SAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
69*9821f1d3SAlan Somers }
70*9821f1d3SAlan Somers 
71*9821f1d3SAlan Somers void expect_write(uint64_t ino, uint64_t size, const void *contents)
72*9821f1d3SAlan Somers {
73*9821f1d3SAlan Somers 	FuseTest::expect_write(ino, 0, size, size, 0, contents);
74*9821f1d3SAlan Somers }
75*9821f1d3SAlan Somers 
76*9821f1d3SAlan Somers };
77*9821f1d3SAlan Somers 
78*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
79*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */
80*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */
81*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_aio_fsync)
82*9821f1d3SAlan Somers {
83*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
84*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
85*9821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
86*9821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
87*9821f1d3SAlan Somers 	uint64_t ino = 42;
88*9821f1d3SAlan Somers 	struct aiocb iocb, *piocb;
89*9821f1d3SAlan Somers 	int fd;
90*9821f1d3SAlan Somers 
91*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
92*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
93*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
94*9821f1d3SAlan Somers 	expect_write(ino, bufsize, CONTENTS);
95*9821f1d3SAlan Somers 	expect_fsync(ino, 0, 0);
96*9821f1d3SAlan Somers 
97*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
98*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
99*9821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
100*9821f1d3SAlan Somers 
101*9821f1d3SAlan Somers 	bzero(&iocb, sizeof(iocb));
102*9821f1d3SAlan Somers 	iocb.aio_fildes = fd;
103*9821f1d3SAlan Somers 
104*9821f1d3SAlan Somers 	ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno);
105*9821f1d3SAlan Somers 	ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
106*9821f1d3SAlan Somers 
107*9821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
108*9821f1d3SAlan Somers }
109*9821f1d3SAlan Somers 
110*9821f1d3SAlan Somers /*
111*9821f1d3SAlan Somers  * fuse(4) should NOT fsync during VOP_RELEASE or VOP_INACTIVE
112*9821f1d3SAlan Somers  *
113*9821f1d3SAlan Somers  * This test only really make sense in writeback caching mode, but it should
114*9821f1d3SAlan Somers  * still pass in any cache mode.
115*9821f1d3SAlan Somers  */
116*9821f1d3SAlan Somers TEST_F(Fsync, close)
117*9821f1d3SAlan Somers {
118*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
119*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
120*9821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
121*9821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
122*9821f1d3SAlan Somers 	uint64_t ino = 42;
123*9821f1d3SAlan Somers 	int fd;
124*9821f1d3SAlan Somers 
125*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
126*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
127*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
128*9821f1d3SAlan Somers 	expect_write(ino, bufsize, CONTENTS);
129*9821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
130*9821f1d3SAlan Somers 		ResultOf([=](auto in) {
131*9821f1d3SAlan Somers 			return (in->header.opcode == FUSE_SETATTR);
132*9821f1d3SAlan Somers 		}, Eq(true)),
133*9821f1d3SAlan Somers 		_)
134*9821f1d3SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
135*9821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
136*9821f1d3SAlan Somers 		out->body.attr.attr.ino = ino;	// Must match nodeid
137*9821f1d3SAlan Somers 	})));
138*9821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
139*9821f1d3SAlan Somers 		ResultOf([=](auto in) {
140*9821f1d3SAlan Somers 			return (in->header.opcode == FUSE_FSYNC);
141*9821f1d3SAlan Somers 		}, Eq(true)),
142*9821f1d3SAlan Somers 		_)
143*9821f1d3SAlan Somers 	).Times(0);
144*9821f1d3SAlan Somers 	expect_release(ino, 1, 0, 0);
145*9821f1d3SAlan Somers 
146*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
147*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
148*9821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
149*9821f1d3SAlan Somers 	close(fd);
150*9821f1d3SAlan Somers }
151*9821f1d3SAlan Somers 
152*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */
153*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_eio)
154*9821f1d3SAlan Somers {
155*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
156*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
157*9821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
158*9821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
159*9821f1d3SAlan Somers 	uint64_t ino = 42;
160*9821f1d3SAlan Somers 	int fd;
161*9821f1d3SAlan Somers 
162*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
163*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
164*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
165*9821f1d3SAlan Somers 	expect_write(ino, bufsize, CONTENTS);
166*9821f1d3SAlan Somers 	expect_fsync(ino, FUSE_FSYNC_FDATASYNC, EIO);
167*9821f1d3SAlan Somers 
168*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
169*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
170*9821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
171*9821f1d3SAlan Somers 	ASSERT_NE(0, fdatasync(fd));
172*9821f1d3SAlan Somers 	ASSERT_EQ(EIO, errno);
173*9821f1d3SAlan Somers 
174*9821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
175*9821f1d3SAlan Somers }
176*9821f1d3SAlan Somers 
177*9821f1d3SAlan Somers /*
178*9821f1d3SAlan Somers  * If the filesystem returns ENOSYS, it will be treated as success and
179*9821f1d3SAlan Somers  * subsequent calls to VOP_FSYNC will succeed automatically without being sent
180*9821f1d3SAlan Somers  * to the filesystem daemon
181*9821f1d3SAlan Somers  */
182*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */
183*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */
184*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_enosys)
185*9821f1d3SAlan Somers {
186*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
187*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
188*9821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
189*9821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
190*9821f1d3SAlan Somers 	uint64_t ino = 42;
191*9821f1d3SAlan Somers 	int fd;
192*9821f1d3SAlan Somers 
193*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
194*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
195*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
196*9821f1d3SAlan Somers 	expect_write(ino, bufsize, CONTENTS);
197*9821f1d3SAlan Somers 	expect_fsync(ino, FUSE_FSYNC_FDATASYNC, ENOSYS);
198*9821f1d3SAlan Somers 
199*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
200*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
201*9821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
202*9821f1d3SAlan Somers 	EXPECT_EQ(0, fdatasync(fd));
203*9821f1d3SAlan Somers 
204*9821f1d3SAlan Somers 	/* Subsequent calls shouldn't query the daemon*/
205*9821f1d3SAlan Somers 	EXPECT_EQ(0, fdatasync(fd));
206*9821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
207*9821f1d3SAlan Somers }
208*9821f1d3SAlan Somers 
209*9821f1d3SAlan Somers 
210*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */
211*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_fdatasync)
212*9821f1d3SAlan Somers {
213*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
214*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
215*9821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
216*9821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
217*9821f1d3SAlan Somers 	uint64_t ino = 42;
218*9821f1d3SAlan Somers 	int fd;
219*9821f1d3SAlan Somers 
220*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
221*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
222*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
223*9821f1d3SAlan Somers 	expect_write(ino, bufsize, CONTENTS);
224*9821f1d3SAlan Somers 	expect_fsync(ino, FUSE_FSYNC_FDATASYNC, 0);
225*9821f1d3SAlan Somers 
226*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
227*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
228*9821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
229*9821f1d3SAlan Somers 	ASSERT_EQ(0, fdatasync(fd)) << strerror(errno);
230*9821f1d3SAlan Somers 
231*9821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
232*9821f1d3SAlan Somers }
233*9821f1d3SAlan Somers 
234*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */
235*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */
236*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_fsync)
237*9821f1d3SAlan Somers {
238*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
239*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
240*9821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
241*9821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
242*9821f1d3SAlan Somers 	uint64_t ino = 42;
243*9821f1d3SAlan Somers 	int fd;
244*9821f1d3SAlan Somers 
245*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
246*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
247*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
248*9821f1d3SAlan Somers 	expect_write(ino, bufsize, CONTENTS);
249*9821f1d3SAlan Somers 	expect_fsync(ino, 0, 0);
250*9821f1d3SAlan Somers 
251*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
252*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
253*9821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
254*9821f1d3SAlan Somers 	ASSERT_EQ(0, fsync(fd)) << strerror(errno);
255*9821f1d3SAlan Somers 
256*9821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
257*9821f1d3SAlan Somers }
258*9821f1d3SAlan Somers 
259*9821f1d3SAlan Somers /* Fsync should sync a file with dirty metadata but clean data */
260*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */
261*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */
262*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_fsync_metadata_only)
263*9821f1d3SAlan Somers {
264*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
265*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
266*9821f1d3SAlan Somers 	uint64_t ino = 42;
267*9821f1d3SAlan Somers 	int fd;
268*9821f1d3SAlan Somers 	mode_t mode = 0755;
269*9821f1d3SAlan Somers 
270*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
271*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
272*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
273*9821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
274*9821f1d3SAlan Somers 		ResultOf([=](auto in) {
275*9821f1d3SAlan Somers 			return (in->header.opcode == FUSE_SETATTR);
276*9821f1d3SAlan Somers 		}, Eq(true)),
277*9821f1d3SAlan Somers 		_)
278*9821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
279*9821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
280*9821f1d3SAlan Somers 		out->body.attr.attr.ino = ino;	// Must match nodeid
281*9821f1d3SAlan Somers 		out->body.attr.attr.mode = S_IFREG | mode;
282*9821f1d3SAlan Somers 	})));
283*9821f1d3SAlan Somers 
284*9821f1d3SAlan Somers 	expect_fsync(ino, 0, 0);
285*9821f1d3SAlan Somers 
286*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
287*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
288*9821f1d3SAlan Somers 	ASSERT_EQ(0, fchmod(fd, mode)) << strerror(errno);
289*9821f1d3SAlan Somers 	ASSERT_EQ(0, fsync(fd)) << strerror(errno);
290*9821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
291*9821f1d3SAlan Somers }
292*9821f1d3SAlan Somers 
293*9821f1d3SAlan Somers // fsync()ing a file that isn't dirty should be a no-op
294*9821f1d3SAlan Somers TEST_F(Fsync, nop)
295*9821f1d3SAlan Somers {
296*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
297*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
298*9821f1d3SAlan Somers 	uint64_t ino = 42;
299*9821f1d3SAlan Somers 	int fd;
300*9821f1d3SAlan Somers 
301*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
302*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
303*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
304*9821f1d3SAlan Somers 
305*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
306*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
307*9821f1d3SAlan Somers 
308*9821f1d3SAlan Somers 	ASSERT_EQ(0, fsync(fd)) << strerror(errno);
309*9821f1d3SAlan Somers 
310*9821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
311*9821f1d3SAlan Somers }
312*9821f1d3SAlan Somers 
313*9821f1d3SAlan Somers // TODO: ENOSYS test
314