github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/lseek.cc (about) 1 // Copyright 2018 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 <stdlib.h> 18 #include <sys/stat.h> 19 #include <sys/types.h> 20 #include <unistd.h> 21 22 #include "gtest/gtest.h" 23 #include "test/util/file_descriptor.h" 24 #include "test/util/temp_path.h" 25 #include "test/util/test_util.h" 26 27 namespace gvisor { 28 namespace testing { 29 30 namespace { 31 32 TEST(LseekTest, InvalidWhence) { 33 const std::string kFileData = "hello world\n"; 34 const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 35 GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); 36 const FileDescriptor fd = 37 ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); 38 39 ASSERT_THAT(lseek(fd.get(), 0, -1), SyscallFailsWithErrno(EINVAL)); 40 } 41 42 TEST(LseekTest, NegativeOffset) { 43 const std::string kFileData = "hello world\n"; 44 const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 45 GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); 46 const FileDescriptor fd = 47 ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); 48 49 EXPECT_THAT(lseek(fd.get(), -(kFileData.length() + 1), SEEK_CUR), 50 SyscallFailsWithErrno(EINVAL)); 51 } 52 53 // A 32-bit off_t is not large enough to represent an offset larger than 54 // maximum file size on standard file systems, so it isn't possible to cause 55 // overflow. 56 #if defined(__x86_64__) || defined(__aarch64__) 57 TEST(LseekTest, Overflow) { 58 // HA! Classic Linux. We really should have an EOVERFLOW 59 // here, since we're seeking to something that cannot be 60 // represented.. but instead we are given an EINVAL. 61 const std::string kFileData = "hello world\n"; 62 const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 63 GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); 64 const FileDescriptor fd = 65 ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); 66 EXPECT_THAT(lseek(fd.get(), 0x7fffffffffffffff, SEEK_END), 67 SyscallFailsWithErrno(EINVAL)); 68 } 69 #endif 70 71 TEST(LseekTest, Set) { 72 const std::string kFileData = "hello world\n"; 73 const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 74 GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); 75 const FileDescriptor fd = 76 ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); 77 78 char buf = '\0'; 79 EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); 80 ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); 81 EXPECT_EQ(buf, kFileData.c_str()[0]); 82 EXPECT_THAT(lseek(fd.get(), 6, SEEK_SET), SyscallSucceedsWithValue(6)); 83 ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); 84 EXPECT_EQ(buf, kFileData.c_str()[6]); 85 } 86 87 TEST(LseekTest, Cur) { 88 const std::string kFileData = "hello world\n"; 89 const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 90 GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); 91 const FileDescriptor fd = 92 ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); 93 94 char buf = '\0'; 95 EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); 96 ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); 97 EXPECT_EQ(buf, kFileData.c_str()[0]); 98 EXPECT_THAT(lseek(fd.get(), 3, SEEK_CUR), SyscallSucceedsWithValue(4)); 99 ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); 100 EXPECT_EQ(buf, kFileData.c_str()[4]); 101 } 102 103 TEST(LseekTest, End) { 104 const std::string kFileData = "hello world\n"; 105 const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 106 GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); 107 const FileDescriptor fd = 108 ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); 109 110 char buf = '\0'; 111 EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); 112 ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); 113 EXPECT_EQ(buf, kFileData.c_str()[0]); 114 EXPECT_THAT(lseek(fd.get(), -2, SEEK_END), SyscallSucceedsWithValue(10)); 115 ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); 116 EXPECT_EQ(buf, kFileData.c_str()[kFileData.length() - 2]); 117 } 118 119 TEST(LseekTest, InvalidFD) { 120 EXPECT_THAT(lseek(-1, 0, SEEK_SET), SyscallFailsWithErrno(EBADF)); 121 } 122 123 TEST(LseekTest, DirCurEnd) { 124 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/tmp", O_RDONLY)); 125 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 126 } 127 128 TEST(LseekTest, ProcDir) { 129 const FileDescriptor fd = 130 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self", O_RDONLY)); 131 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); 132 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); 133 } 134 135 TEST(LseekTest, ProcFile) { 136 const FileDescriptor fd = 137 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/meminfo", O_RDONLY)); 138 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); 139 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); 140 } 141 142 TEST(LseekTest, SysDir) { 143 const FileDescriptor fd = 144 ASSERT_NO_ERRNO_AND_VALUE(Open("/sys/devices", O_RDONLY)); 145 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); 146 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); 147 } 148 149 TEST(LseekTest, SeekCurrentDir) { 150 // From include/linux/fs.h. 151 constexpr loff_t MAX_LFS_FILESIZE = 0x7fffffffffffffff; 152 153 char* dir = getcwd(NULL, 0); 154 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir, O_RDONLY)); 155 156 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); 157 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), 158 // Some filesystems (like ext4) allow lseek(SEEK_END) on a 159 // directory and return MAX_LFS_FILESIZE, others return EINVAL. 160 AnyOf(SyscallSucceedsWithValue(MAX_LFS_FILESIZE), 161 SyscallFailsWithErrno(EINVAL))); 162 free(dir); 163 } 164 165 TEST(LseekTest, ProcStatTwice) { 166 const FileDescriptor fd1 = 167 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); 168 const FileDescriptor fd2 = 169 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); 170 171 ASSERT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 172 ASSERT_THAT(lseek(fd1.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); 173 ASSERT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceeds()); 174 // Check that just because we moved fd1, fd2 doesn't move. 175 ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 176 177 const FileDescriptor fd3 = 178 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); 179 ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 180 } 181 182 TEST(LseekTest, EtcPasswdDup) { 183 const FileDescriptor fd1 = 184 ASSERT_NO_ERRNO_AND_VALUE(Open("/etc/passwd", O_RDONLY)); 185 const FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(fd1.Dup()); 186 187 ASSERT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 188 ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 189 ASSERT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceeds()); 190 // Check that just because we moved fd1, fd2 doesn't move. 191 ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); 192 193 const FileDescriptor fd3 = ASSERT_NO_ERRNO_AND_VALUE(fd1.Dup()); 194 ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); 195 } 196 197 // TODO(magi): Add tests where we have donated in sockets. 198 199 } // namespace 200 201 } // namespace testing 202 } // namespace gvisor