1e5b50fe7SAlan Somers /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3e5b50fe7SAlan Somers * 4e5b50fe7SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 5e5b50fe7SAlan Somers * 6e5b50fe7SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 7e5b50fe7SAlan Somers * from the FreeBSD Foundation. 8e5b50fe7SAlan Somers * 9e5b50fe7SAlan Somers * Redistribution and use in source and binary forms, with or without 10e5b50fe7SAlan Somers * modification, are permitted provided that the following conditions 11e5b50fe7SAlan Somers * are met: 12e5b50fe7SAlan Somers * 1. Redistributions of source code must retain the above copyright 13e5b50fe7SAlan Somers * notice, this list of conditions and the following disclaimer. 14e5b50fe7SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 15e5b50fe7SAlan Somers * notice, this list of conditions and the following disclaimer in the 16e5b50fe7SAlan Somers * documentation and/or other materials provided with the distribution. 17e5b50fe7SAlan Somers * 18e5b50fe7SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19e5b50fe7SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20e5b50fe7SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21e5b50fe7SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22e5b50fe7SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23e5b50fe7SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24e5b50fe7SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25e5b50fe7SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26e5b50fe7SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27e5b50fe7SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28e5b50fe7SAlan Somers * SUCH DAMAGE. 291fa8ebfbSAlan Somers * 301fa8ebfbSAlan Somers * $FreeBSD$ 31e5b50fe7SAlan Somers */ 32e5b50fe7SAlan Somers 33e5b50fe7SAlan Somers /* This file tests functionality needed by NFS servers */ 34e5b50fe7SAlan Somers extern "C" { 35e5b50fe7SAlan Somers #include <sys/param.h> 36e5b50fe7SAlan Somers #include <sys/mount.h> 37e5b50fe7SAlan Somers 38e5b50fe7SAlan Somers #include <dirent.h> 39e5b50fe7SAlan Somers #include <fcntl.h> 40e5b50fe7SAlan Somers #include <unistd.h> 41e5b50fe7SAlan Somers } 42e5b50fe7SAlan Somers 43e5b50fe7SAlan Somers #include "mockfs.hh" 44e5b50fe7SAlan Somers #include "utils.hh" 45e5b50fe7SAlan Somers 46e5b50fe7SAlan Somers using namespace std; 47e5b50fe7SAlan Somers using namespace testing; 48e5b50fe7SAlan Somers 49e5b50fe7SAlan Somers 50e5b50fe7SAlan Somers class Nfs: public FuseTest { 51e5b50fe7SAlan Somers public: 52e5b50fe7SAlan Somers virtual void SetUp() { 53e5b50fe7SAlan Somers if (geteuid() != 0) 54e5b50fe7SAlan Somers GTEST_SKIP() << "This test requires a privileged user"; 55e5b50fe7SAlan Somers FuseTest::SetUp(); 56e5b50fe7SAlan Somers } 57e5b50fe7SAlan Somers }; 58e5b50fe7SAlan Somers 59e5b50fe7SAlan Somers class Exportable: public Nfs { 60e5b50fe7SAlan Somers public: 61e5b50fe7SAlan Somers virtual void SetUp() { 62e5b50fe7SAlan Somers m_init_flags = FUSE_EXPORT_SUPPORT; 63e5b50fe7SAlan Somers Nfs::SetUp(); 64e5b50fe7SAlan Somers } 65e5b50fe7SAlan Somers }; 66e5b50fe7SAlan Somers 67e5b50fe7SAlan Somers class Fhstat: public Exportable {}; 68e5b50fe7SAlan Somers class FhstatNotExportable: public Nfs {}; 69e5b50fe7SAlan Somers class Getfh: public Exportable {}; 70e5b50fe7SAlan Somers class Readdir: public Exportable {}; 71e5b50fe7SAlan Somers 72e5b50fe7SAlan Somers /* If the server returns a different generation number, then file is stale */ 73e5b50fe7SAlan Somers TEST_F(Fhstat, estale) 74e5b50fe7SAlan Somers { 75e5b50fe7SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/."; 76e5b50fe7SAlan Somers const char RELDIRPATH[] = "some_dir"; 77e5b50fe7SAlan Somers fhandle_t fhp; 78e5b50fe7SAlan Somers struct stat sb; 79e5b50fe7SAlan Somers const uint64_t ino = 42; 80e5b50fe7SAlan Somers const mode_t mode = S_IFDIR | 0755; 81e5b50fe7SAlan Somers Sequence seq; 82e5b50fe7SAlan Somers 83a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) 84e5b50fe7SAlan Somers .InSequence(seq) 8529edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 86e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 8729edc611SAlan Somers out.body.entry.attr.mode = mode; 8829edc611SAlan Somers out.body.entry.nodeid = ino; 8929edc611SAlan Somers out.body.entry.generation = 1; 9029edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 9129edc611SAlan Somers out.body.entry.entry_valid = 0; 92e5b50fe7SAlan Somers }))); 93e5b50fe7SAlan Somers 94e5b50fe7SAlan Somers EXPECT_LOOKUP(ino, ".") 95e5b50fe7SAlan Somers .InSequence(seq) 9629edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 97e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 9829edc611SAlan Somers out.body.entry.attr.mode = mode; 9929edc611SAlan Somers out.body.entry.nodeid = ino; 10029edc611SAlan Somers out.body.entry.generation = 2; 10129edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 10229edc611SAlan Somers out.body.entry.entry_valid = 0; 103e5b50fe7SAlan Somers }))); 104e5b50fe7SAlan Somers 105e5b50fe7SAlan Somers ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno); 106e5b50fe7SAlan Somers ASSERT_EQ(-1, fhstat(&fhp, &sb)); 107e5b50fe7SAlan Somers EXPECT_EQ(ESTALE, errno); 108e5b50fe7SAlan Somers } 109e5b50fe7SAlan Somers 110e5b50fe7SAlan Somers /* If we must lookup an entry from the server, send a LOOKUP request for "." */ 111e5b50fe7SAlan Somers TEST_F(Fhstat, lookup_dot) 112e5b50fe7SAlan Somers { 113e5b50fe7SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/."; 114e5b50fe7SAlan Somers const char RELDIRPATH[] = "some_dir"; 115e5b50fe7SAlan Somers fhandle_t fhp; 116e5b50fe7SAlan Somers struct stat sb; 117e5b50fe7SAlan Somers const uint64_t ino = 42; 118e5b50fe7SAlan Somers const mode_t mode = S_IFDIR | 0755; 119e5b50fe7SAlan Somers const uid_t uid = 12345; 120e5b50fe7SAlan Somers 121a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) 12229edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 123e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 12429edc611SAlan Somers out.body.entry.attr.mode = mode; 12529edc611SAlan Somers out.body.entry.nodeid = ino; 12629edc611SAlan Somers out.body.entry.generation = 1; 12729edc611SAlan Somers out.body.entry.attr.uid = uid; 12829edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 12929edc611SAlan Somers out.body.entry.entry_valid = 0; 130e5b50fe7SAlan Somers }))); 131e5b50fe7SAlan Somers 132e5b50fe7SAlan Somers EXPECT_LOOKUP(ino, ".") 13329edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 134e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 13529edc611SAlan Somers out.body.entry.attr.mode = mode; 13629edc611SAlan Somers out.body.entry.nodeid = ino; 13729edc611SAlan Somers out.body.entry.generation = 1; 13829edc611SAlan Somers out.body.entry.attr.uid = uid; 13929edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 14029edc611SAlan Somers out.body.entry.entry_valid = 0; 141e5b50fe7SAlan Somers }))); 142e5b50fe7SAlan Somers 143e5b50fe7SAlan Somers ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno); 144e5b50fe7SAlan Somers ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno); 145e5b50fe7SAlan Somers EXPECT_EQ(uid, sb.st_uid); 146e5b50fe7SAlan Somers EXPECT_EQ(mode, sb.st_mode); 147e5b50fe7SAlan Somers } 148e5b50fe7SAlan Somers 149e5b50fe7SAlan Somers /* Use a file handle whose entry is still cached */ 1500d2bf489SAlan Somers TEST_F(Fhstat, cached) 151e5b50fe7SAlan Somers { 152e5b50fe7SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/."; 153e5b50fe7SAlan Somers const char RELDIRPATH[] = "some_dir"; 154e5b50fe7SAlan Somers fhandle_t fhp; 155e5b50fe7SAlan Somers struct stat sb; 156e5b50fe7SAlan Somers const uint64_t ino = 42; 157e5b50fe7SAlan Somers const mode_t mode = S_IFDIR | 0755; 158e5b50fe7SAlan Somers 159a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) 16029edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 161e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 16229edc611SAlan Somers out.body.entry.attr.mode = mode; 16329edc611SAlan Somers out.body.entry.nodeid = ino; 16429edc611SAlan Somers out.body.entry.generation = 1; 1650d2bf489SAlan Somers out.body.entry.attr.ino = ino; 16629edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 16729edc611SAlan Somers out.body.entry.entry_valid = UINT64_MAX; 168e5b50fe7SAlan Somers }))); 169e5b50fe7SAlan Somers 170e5b50fe7SAlan Somers ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno); 171e5b50fe7SAlan Somers ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno); 1720d2bf489SAlan Somers EXPECT_EQ(ino, sb.st_ino); 1730d2bf489SAlan Somers } 1740d2bf489SAlan Somers 1750d2bf489SAlan Somers /* File handle entries should expire from the cache, too */ 1760d2bf489SAlan Somers TEST_F(Fhstat, cache_expired) 1770d2bf489SAlan Somers { 1780d2bf489SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/."; 1790d2bf489SAlan Somers const char RELDIRPATH[] = "some_dir"; 1800d2bf489SAlan Somers fhandle_t fhp; 1810d2bf489SAlan Somers struct stat sb; 1820d2bf489SAlan Somers const uint64_t ino = 42; 1830d2bf489SAlan Somers const mode_t mode = S_IFDIR | 0755; 1840d2bf489SAlan Somers 1850d2bf489SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) 1860d2bf489SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1870d2bf489SAlan Somers SET_OUT_HEADER_LEN(out, entry); 1880d2bf489SAlan Somers out.body.entry.attr.mode = mode; 1890d2bf489SAlan Somers out.body.entry.nodeid = ino; 1900d2bf489SAlan Somers out.body.entry.generation = 1; 1910d2bf489SAlan Somers out.body.entry.attr.ino = ino; 1920d2bf489SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 1930d2bf489SAlan Somers out.body.entry.entry_valid_nsec = NAP_NS / 2; 1940d2bf489SAlan Somers }))); 1950d2bf489SAlan Somers 1960d2bf489SAlan Somers EXPECT_LOOKUP(ino, ".") 1970d2bf489SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1980d2bf489SAlan Somers SET_OUT_HEADER_LEN(out, entry); 1990d2bf489SAlan Somers out.body.entry.attr.mode = mode; 2000d2bf489SAlan Somers out.body.entry.nodeid = ino; 2010d2bf489SAlan Somers out.body.entry.generation = 1; 2020d2bf489SAlan Somers out.body.entry.attr.ino = ino; 2030d2bf489SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 2040d2bf489SAlan Somers out.body.entry.entry_valid = 0; 2050d2bf489SAlan Somers }))); 2060d2bf489SAlan Somers 2070d2bf489SAlan Somers ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno); 2080d2bf489SAlan Somers ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno); 2090d2bf489SAlan Somers EXPECT_EQ(ino, sb.st_ino); 2100d2bf489SAlan Somers 2110d2bf489SAlan Somers nap(); 2120d2bf489SAlan Somers 2130d2bf489SAlan Somers /* Cache should be expired; fuse should issue a FUSE_LOOKUP */ 2140d2bf489SAlan Somers ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno); 2150d2bf489SAlan Somers EXPECT_EQ(ino, sb.st_ino); 216e5b50fe7SAlan Somers } 217e5b50fe7SAlan Somers 218e5b50fe7SAlan Somers /* 219e5b50fe7SAlan Somers * If the server doesn't set FUSE_EXPORT_SUPPORT, then we can't do NFS-style 220e5b50fe7SAlan Somers * lookups 221e5b50fe7SAlan Somers */ 222e5b50fe7SAlan Somers TEST_F(FhstatNotExportable, lookup_dot) 223e5b50fe7SAlan Somers { 224e5b50fe7SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/."; 225e5b50fe7SAlan Somers const char RELDIRPATH[] = "some_dir"; 226e5b50fe7SAlan Somers fhandle_t fhp; 227e5b50fe7SAlan Somers const uint64_t ino = 42; 228e5b50fe7SAlan Somers const mode_t mode = S_IFDIR | 0755; 229e5b50fe7SAlan Somers 230a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) 23129edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 232e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 23329edc611SAlan Somers out.body.entry.attr.mode = mode; 23429edc611SAlan Somers out.body.entry.nodeid = ino; 23529edc611SAlan Somers out.body.entry.generation = 1; 23629edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 23729edc611SAlan Somers out.body.entry.entry_valid = 0; 238e5b50fe7SAlan Somers }))); 239e5b50fe7SAlan Somers 240e5b50fe7SAlan Somers ASSERT_EQ(-1, getfh(FULLPATH, &fhp)); 241e5b50fe7SAlan Somers ASSERT_EQ(EOPNOTSUPP, errno); 242e5b50fe7SAlan Somers } 243e5b50fe7SAlan Somers 244e5b50fe7SAlan Somers /* FreeBSD's fid struct doesn't have enough space for 64-bit generations */ 245e5b50fe7SAlan Somers TEST_F(Getfh, eoverflow) 246e5b50fe7SAlan Somers { 247e5b50fe7SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/."; 248e5b50fe7SAlan Somers const char RELDIRPATH[] = "some_dir"; 249e5b50fe7SAlan Somers fhandle_t fhp; 250e5b50fe7SAlan Somers uint64_t ino = 42; 251e5b50fe7SAlan Somers 252a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) 25329edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 254e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 25529edc611SAlan Somers out.body.entry.attr.mode = S_IFDIR | 0755; 25629edc611SAlan Somers out.body.entry.nodeid = ino; 25729edc611SAlan Somers out.body.entry.generation = (uint64_t)UINT32_MAX + 1; 25829edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 25929edc611SAlan Somers out.body.entry.entry_valid = UINT64_MAX; 260e5b50fe7SAlan Somers }))); 261e5b50fe7SAlan Somers 262e5b50fe7SAlan Somers ASSERT_NE(0, getfh(FULLPATH, &fhp)); 263e5b50fe7SAlan Somers EXPECT_EQ(EOVERFLOW, errno); 264e5b50fe7SAlan Somers } 265e5b50fe7SAlan Somers 266e5b50fe7SAlan Somers /* Get an NFS file handle */ 267e5b50fe7SAlan Somers TEST_F(Getfh, ok) 268e5b50fe7SAlan Somers { 269e5b50fe7SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/."; 270e5b50fe7SAlan Somers const char RELDIRPATH[] = "some_dir"; 271e5b50fe7SAlan Somers fhandle_t fhp; 272e5b50fe7SAlan Somers uint64_t ino = 42; 273e5b50fe7SAlan Somers 274a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH) 27529edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 276e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 27729edc611SAlan Somers out.body.entry.attr.mode = S_IFDIR | 0755; 27829edc611SAlan Somers out.body.entry.nodeid = ino; 27929edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 28029edc611SAlan Somers out.body.entry.entry_valid = UINT64_MAX; 281e5b50fe7SAlan Somers }))); 282e5b50fe7SAlan Somers 283e5b50fe7SAlan Somers ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno); 284e5b50fe7SAlan Somers } 285e5b50fe7SAlan Somers 286e5b50fe7SAlan Somers /* 287e5b50fe7SAlan Somers * Call readdir via a file handle. 288e5b50fe7SAlan Somers * 289e5b50fe7SAlan Somers * This is how a userspace nfs server like nfs-ganesha or unfs3 would call 290e5b50fe7SAlan Somers * readdir. The in-kernel NFS server never does any equivalent of open. I 291e5b50fe7SAlan Somers * haven't discovered a way to mimic nfsd's behavior short of actually running 292e5b50fe7SAlan Somers * nfsd. 293e5b50fe7SAlan Somers */ 294e5b50fe7SAlan Somers TEST_F(Readdir, getdirentries) 295e5b50fe7SAlan Somers { 296e5b50fe7SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 297e5b50fe7SAlan Somers const char RELPATH[] = "some_dir"; 298e5b50fe7SAlan Somers uint64_t ino = 42; 299e5b50fe7SAlan Somers mode_t mode = S_IFDIR | 0755; 300e5b50fe7SAlan Somers fhandle_t fhp; 301e5b50fe7SAlan Somers int fd; 302e5b50fe7SAlan Somers char buf[8192]; 303e5b50fe7SAlan Somers ssize_t r; 304e5b50fe7SAlan Somers 305a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 30629edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 307e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 30829edc611SAlan Somers out.body.entry.attr.mode = mode; 30929edc611SAlan Somers out.body.entry.nodeid = ino; 31029edc611SAlan Somers out.body.entry.generation = 1; 31129edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 31229edc611SAlan Somers out.body.entry.entry_valid = 0; 313e5b50fe7SAlan Somers }))); 314e5b50fe7SAlan Somers 315e5b50fe7SAlan Somers EXPECT_LOOKUP(ino, ".") 31629edc611SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 317e5b50fe7SAlan Somers SET_OUT_HEADER_LEN(out, entry); 31829edc611SAlan Somers out.body.entry.attr.mode = mode; 31929edc611SAlan Somers out.body.entry.nodeid = ino; 32029edc611SAlan Somers out.body.entry.generation = 1; 32129edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 32229edc611SAlan Somers out.body.entry.entry_valid = 0; 323e5b50fe7SAlan Somers }))); 324e5b50fe7SAlan Somers 325e5b50fe7SAlan Somers expect_opendir(ino); 326e5b50fe7SAlan Somers 327e5b50fe7SAlan Somers EXPECT_CALL(*m_mock, process( 328e5b50fe7SAlan Somers ResultOf([=](auto in) { 32929edc611SAlan Somers return (in.header.opcode == FUSE_READDIR && 33029edc611SAlan Somers in.header.nodeid == ino && 33129edc611SAlan Somers in.body.readdir.size == sizeof(buf)); 332e5b50fe7SAlan Somers }, Eq(true)), 333e5b50fe7SAlan Somers _) 33429edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 33529edc611SAlan Somers out.header.error = 0; 33629edc611SAlan Somers out.header.len = sizeof(out.header); 337e5b50fe7SAlan Somers }))); 338e5b50fe7SAlan Somers 339e5b50fe7SAlan Somers ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno); 340e5b50fe7SAlan Somers fd = fhopen(&fhp, O_DIRECTORY); 341e5b50fe7SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 342e5b50fe7SAlan Somers r = getdirentries(fd, buf, sizeof(buf), 0); 343e5b50fe7SAlan Somers ASSERT_EQ(0, r) << strerror(errno); 344e5b50fe7SAlan Somers 3457fc0921dSAlan Somers leak(fd); 346e5b50fe7SAlan Somers } 347