github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/fuse/linux/stat_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 <errno.h> 16 #include <fcntl.h> 17 #include <linux/fuse.h> 18 #include <sys/stat.h> 19 #include <sys/statfs.h> 20 #include <sys/types.h> 21 #include <sys/uio.h> 22 #include <unistd.h> 23 24 #include <vector> 25 26 #include "gtest/gtest.h" 27 #include "test/fuse/linux/fuse_fd_util.h" 28 #include "test/util/cleanup.h" 29 #include "test/util/fs_util.h" 30 #include "test/util/fuse_util.h" 31 #include "test/util/test_util.h" 32 33 namespace gvisor { 34 namespace testing { 35 36 namespace { 37 38 class StatTest : public FuseFdTest { 39 public: 40 void SetUp() override { 41 FuseFdTest::SetUp(); 42 test_file_path_ = JoinPath(mount_point_.path(), test_file_); 43 } 44 45 protected: 46 bool StatsAreEqual(struct stat expected, struct stat actual) { 47 // Device number will be dynamically allocated by kernel, we cannot know in 48 // advance. 49 actual.st_dev = expected.st_dev; 50 return memcmp(&expected, &actual, sizeof(struct stat)) == 0; 51 } 52 53 const std::string test_file_ = "testfile"; 54 const mode_t expected_mode = S_IFREG | S_IRUSR | S_IWUSR; 55 const uint64_t fh = 23; 56 57 std::string test_file_path_; 58 }; 59 60 TEST_F(StatTest, StatNormal) { 61 // Set up fixture. 62 struct fuse_attr attr = DefaultFuseAttr(expected_mode, 1); 63 struct fuse_out_header out_header = { 64 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), 65 }; 66 struct fuse_attr_out out_payload = { 67 .attr = attr, 68 }; 69 auto iov_out = FuseGenerateIovecs(out_header, out_payload); 70 SetServerResponse(FUSE_GETATTR, iov_out); 71 72 // Make syscall. 73 struct stat stat_buf; 74 EXPECT_THAT(stat(mount_point_.path().c_str(), &stat_buf), SyscallSucceeds()); 75 76 // Check filesystem operation result. 77 struct stat expected_stat = { 78 .st_ino = attr.ino, 79 #ifdef __aarch64__ 80 .st_mode = expected_mode, 81 .st_nlink = attr.nlink, 82 #else 83 .st_nlink = attr.nlink, 84 .st_mode = expected_mode, 85 #endif 86 .st_uid = attr.uid, 87 .st_gid = attr.gid, 88 .st_rdev = attr.rdev, 89 .st_size = static_cast<off_t>(attr.size), 90 .st_blksize = attr.blksize, 91 .st_blocks = static_cast<blkcnt_t>(attr.blocks), 92 .st_atim = (struct timespec){.tv_sec = static_cast<int>(attr.atime), 93 .tv_nsec = attr.atimensec}, 94 .st_mtim = (struct timespec){.tv_sec = static_cast<int>(attr.mtime), 95 .tv_nsec = attr.mtimensec}, 96 .st_ctim = (struct timespec){.tv_sec = static_cast<int>(attr.ctime), 97 .tv_nsec = attr.ctimensec}, 98 }; 99 EXPECT_TRUE(StatsAreEqual(stat_buf, expected_stat)); 100 101 // Check FUSE request. 102 struct fuse_in_header in_header; 103 struct fuse_getattr_in in_payload; 104 auto iov_in = FuseGenerateIovecs(in_header, in_payload); 105 106 GetServerActualRequest(iov_in); 107 EXPECT_EQ(in_header.opcode, FUSE_GETATTR); 108 EXPECT_EQ(in_payload.getattr_flags, 0); 109 EXPECT_EQ(in_payload.fh, 0); 110 } 111 112 TEST_F(StatTest, StatNotFound) { 113 // Set up fixture. 114 struct fuse_out_header out_header = { 115 .len = sizeof(struct fuse_out_header), 116 .error = -ENOENT, 117 }; 118 auto iov_out = FuseGenerateIovecs(out_header); 119 SetServerResponse(FUSE_GETATTR, iov_out); 120 121 // Make syscall. 122 struct stat stat_buf; 123 EXPECT_THAT(stat(mount_point_.path().c_str(), &stat_buf), 124 SyscallFailsWithErrno(ENOENT)); 125 126 // Check FUSE request. 127 struct fuse_in_header in_header; 128 struct fuse_getattr_in in_payload; 129 auto iov_in = FuseGenerateIovecs(in_header, in_payload); 130 131 GetServerActualRequest(iov_in); 132 EXPECT_EQ(in_header.opcode, FUSE_GETATTR); 133 EXPECT_EQ(in_payload.getattr_flags, 0); 134 EXPECT_EQ(in_payload.fh, 0); 135 } 136 137 TEST_F(StatTest, FstatNormal) { 138 // Set up fixture. 139 SetServerInodeLookup(test_file_); 140 auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDONLY, fh)); 141 auto close_fd = CloseFD(fd); 142 143 struct fuse_attr attr = DefaultFuseAttr(expected_mode, 2); 144 struct fuse_out_header out_header = { 145 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), 146 }; 147 struct fuse_attr_out out_payload = { 148 .attr = attr, 149 }; 150 auto iov_out = FuseGenerateIovecs(out_header, out_payload); 151 SetServerResponse(FUSE_GETATTR, iov_out); 152 153 // Make syscall. 154 struct stat stat_buf; 155 EXPECT_THAT(fstat(fd.get(), &stat_buf), SyscallSucceeds()); 156 157 // Check filesystem operation result. 158 struct stat expected_stat = { 159 .st_ino = attr.ino, 160 #ifdef __aarch64__ 161 .st_mode = expected_mode, 162 .st_nlink = attr.nlink, 163 #else 164 .st_nlink = attr.nlink, 165 .st_mode = expected_mode, 166 #endif 167 .st_uid = attr.uid, 168 .st_gid = attr.gid, 169 .st_rdev = attr.rdev, 170 .st_size = static_cast<off_t>(attr.size), 171 .st_blksize = attr.blksize, 172 .st_blocks = static_cast<blkcnt_t>(attr.blocks), 173 .st_atim = (struct timespec){.tv_sec = static_cast<int>(attr.atime), 174 .tv_nsec = attr.atimensec}, 175 .st_mtim = (struct timespec){.tv_sec = static_cast<int>(attr.mtime), 176 .tv_nsec = attr.mtimensec}, 177 .st_ctim = (struct timespec){.tv_sec = static_cast<int>(attr.ctime), 178 .tv_nsec = attr.ctimensec}, 179 }; 180 EXPECT_TRUE(StatsAreEqual(stat_buf, expected_stat)); 181 182 // Check FUSE request. 183 struct fuse_in_header in_header; 184 struct fuse_getattr_in in_payload; 185 auto iov_in = FuseGenerateIovecs(in_header, in_payload); 186 187 GetServerActualRequest(iov_in); 188 EXPECT_EQ(in_header.opcode, FUSE_GETATTR); 189 EXPECT_EQ(in_payload.getattr_flags, 0); 190 EXPECT_EQ(in_payload.fh, 0); 191 } 192 193 TEST_F(StatTest, StatByFileHandle) { 194 // Set up fixture. 195 SetServerInodeLookup(test_file_, expected_mode, 0); 196 auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDONLY, fh)); 197 auto close_fd = CloseFD(fd); 198 199 struct fuse_attr attr = DefaultFuseAttr(expected_mode, 2, 0); 200 struct fuse_out_header out_header = { 201 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), 202 }; 203 struct fuse_attr_out out_payload = { 204 .attr = attr, 205 }; 206 auto iov_out = FuseGenerateIovecs(out_header, out_payload); 207 SetServerResponse(FUSE_GETATTR, iov_out); 208 209 // Make syscall. 210 std::vector<char> buf(1); 211 // Since this is an empty file, it won't issue FUSE_READ. But a FUSE_GETATTR 212 // will be issued before read completes. 213 EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), SyscallSucceeds()); 214 215 // Check FUSE request. 216 struct fuse_in_header in_header; 217 struct fuse_getattr_in in_payload; 218 auto iov_in = FuseGenerateIovecs(in_header, in_payload); 219 220 GetServerActualRequest(iov_in); 221 EXPECT_EQ(in_header.opcode, FUSE_GETATTR); 222 EXPECT_EQ(in_payload.getattr_flags, FUSE_GETATTR_FH); 223 EXPECT_EQ(in_payload.fh, fh); 224 } 225 226 } // namespace 227 228 } // namespace testing 229 } // namespace gvisor