github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/fuse/linux/readdir_test.cc (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <dirent.h> 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <linux/fuse.h> 19 #include <linux/unistd.h> 20 #include <sys/stat.h> 21 #include <sys/statfs.h> 22 #include <sys/types.h> 23 #include <unistd.h> 24 25 #include <string> 26 27 #include "gtest/gtest.h" 28 #include "test/fuse/linux/fuse_base.h" 29 #include "test/util/fuse_util.h" 30 #include "test/util/test_util.h" 31 32 #define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) 33 #define FUSE_DIRENT_ALIGN(x) \ 34 (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) 35 #define FUSE_DIRENT_SIZE(d) FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) 36 37 namespace gvisor { 38 namespace testing { 39 40 namespace { 41 42 class ReaddirTest : public FuseTest { 43 public: 44 void fill_fuse_dirent(char *buf, const char *name, uint64_t ino) { 45 size_t namelen = strlen(name); 46 size_t entlen = FUSE_NAME_OFFSET + namelen; 47 size_t entlen_padded = FUSE_DIRENT_ALIGN(entlen); 48 struct fuse_dirent *dirent; 49 50 dirent = reinterpret_cast<struct fuse_dirent *>(buf); 51 dirent->ino = ino; 52 dirent->namelen = namelen; 53 memcpy(dirent->name, name, namelen); 54 memset(dirent->name + namelen, 0, entlen_padded - entlen); 55 } 56 57 protected: 58 const std::string test_dir_name_ = "test_dir"; 59 }; 60 61 TEST_F(ReaddirTest, SingleEntry) { 62 const std::string test_dir_path = 63 JoinPath(mount_point_.path().c_str(), test_dir_name_); 64 65 const uint64_t ino_dir = 1024; 66 // We need to make sure the test dir is a directory that can be found. 67 mode_t expected_mode = 68 S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 69 struct fuse_attr dir_attr = { 70 .ino = ino_dir, 71 .size = 512, 72 .blocks = 4, 73 .mode = expected_mode, 74 .blksize = 4096, 75 }; 76 77 // We need to make sure the test dir is a directory that can be found. 78 struct fuse_out_header lookup_header = { 79 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), 80 }; 81 struct fuse_entry_out lookup_payload = { 82 .nodeid = 1, 83 .entry_valid = true, 84 .attr_valid = true, 85 .attr = dir_attr, 86 }; 87 88 struct fuse_out_header open_header = { 89 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), 90 }; 91 struct fuse_open_out open_payload = { 92 .fh = 1, 93 }; 94 auto iov_out = FuseGenerateIovecs(lookup_header, lookup_payload); 95 SetServerResponse(FUSE_LOOKUP, iov_out); 96 97 iov_out = FuseGenerateIovecs(open_header, open_payload); 98 SetServerResponse(FUSE_OPENDIR, iov_out); 99 100 FileDescriptor fd = 101 ASSERT_NO_ERRNO_AND_VALUE(Open(test_dir_path.c_str(), O_RDONLY)); 102 103 // The open command makes two syscalls. Lookup the dir file and open. 104 // We don't need to inspect those headers in this test. 105 SkipServerActualRequest(); // LOOKUP. 106 SkipServerActualRequest(); // OPENDIR. 107 108 // Readdir test code. 109 std::string dot = "."; 110 std::string dot_dot = ".."; 111 std::string test_file = "testFile"; 112 113 // Figure out how many dirents to send over and allocate them appropriately. 114 // Each dirent has a dynamic name and a static metadata part. The dirent size 115 // is aligned to being a multiple of 8. 116 size_t dot_file_dirent_size = 117 FUSE_DIRENT_ALIGN(dot.length() + FUSE_NAME_OFFSET); 118 size_t dot_dot_file_dirent_size = 119 FUSE_DIRENT_ALIGN(dot_dot.length() + FUSE_NAME_OFFSET); 120 size_t test_file_dirent_size = 121 FUSE_DIRENT_ALIGN(test_file.length() + FUSE_NAME_OFFSET); 122 123 // Create an appropriately sized payload. 124 size_t readdir_payload_size = 125 test_file_dirent_size + dot_file_dirent_size + dot_dot_file_dirent_size; 126 std::vector<char> readdir_payload_vec(readdir_payload_size); 127 char *readdir_payload = readdir_payload_vec.data(); 128 129 // Use fake ino for other directories. 130 fill_fuse_dirent(readdir_payload, dot.c_str(), ino_dir - 2); 131 fill_fuse_dirent(readdir_payload + dot_file_dirent_size, dot_dot.c_str(), 132 ino_dir - 1); 133 fill_fuse_dirent( 134 readdir_payload + dot_file_dirent_size + dot_dot_file_dirent_size, 135 test_file.c_str(), ino_dir); 136 137 struct fuse_out_header readdir_header = { 138 .len = uint32_t(sizeof(struct fuse_out_header) + readdir_payload_size), 139 }; 140 struct fuse_out_header readdir_header_break = { 141 .len = uint32_t(sizeof(struct fuse_out_header)), 142 }; 143 144 iov_out = FuseGenerateIovecs(readdir_header, readdir_payload_vec); 145 SetServerResponse(FUSE_READDIR, iov_out); 146 147 iov_out = FuseGenerateIovecs(readdir_header_break); 148 SetServerResponse(FUSE_READDIR, iov_out); 149 150 std::vector<char> buf(4090, 0); 151 int nread, off = 0, i = 0; 152 EXPECT_THAT( 153 nread = syscall(__NR_getdents64, fd.get(), buf.data(), buf.size()), 154 SyscallSucceeds()); 155 for (; off < nread;) { 156 struct dirent64 *ent = (struct dirent64 *)(buf.data() + off); 157 off += ent->d_reclen; 158 switch (i++) { 159 case 0: 160 EXPECT_EQ(std::string(ent->d_name), dot); 161 break; 162 case 1: 163 EXPECT_EQ(std::string(ent->d_name), dot_dot); 164 break; 165 case 2: 166 EXPECT_EQ(std::string(ent->d_name), test_file); 167 break; 168 } 169 } 170 171 EXPECT_THAT( 172 nread = syscall(__NR_getdents64, fd.get(), buf.data(), buf.size()), 173 SyscallSucceedsWithValue(0)); 174 175 SkipServerActualRequest(); // READDIR. 176 SkipServerActualRequest(); // READDIR with no data. 177 178 // Clean up. 179 fd.reset(-1); 180 181 struct fuse_in_header in_header; 182 struct fuse_release_in in_payload; 183 184 auto iov_in = FuseGenerateIovecs(in_header, in_payload); 185 GetServerActualRequest(iov_in); 186 EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); 187 EXPECT_EQ(in_header.opcode, FUSE_RELEASEDIR); 188 } 189 190 } // namespace 191 192 } // namespace testing 193 } // namespace gvisor