gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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( 125 Open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY)); 126 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 127 } 128 129 TEST(LseekTest, ProcDir) { 130 const FileDescriptor fd = 131 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self", O_RDONLY)); 132 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); 133 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); 134 } 135 136 TEST(LseekTest, ProcFile) { 137 const FileDescriptor fd = 138 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/meminfo", O_RDONLY)); 139 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); 140 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); 141 } 142 143 TEST(LseekTest, SysDir) { 144 const FileDescriptor fd = 145 ASSERT_NO_ERRNO_AND_VALUE(Open("/sys/devices", O_RDONLY)); 146 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); 147 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); 148 } 149 150 TEST(LseekTest, SeekCurrentDir) { 151 // From include/linux/fs.h. 152 constexpr loff_t MAX_LFS_FILESIZE = 0x7fffffffffffffff; 153 154 char* dir = getcwd(NULL, 0); 155 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir, O_RDONLY)); 156 157 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); 158 ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), 159 // Some filesystems (like ext4) allow lseek(SEEK_END) on a 160 // directory and return MAX_LFS_FILESIZE, others return EINVAL. 161 AnyOf(SyscallSucceedsWithValue(MAX_LFS_FILESIZE), 162 SyscallFailsWithErrno(EINVAL))); 163 free(dir); 164 } 165 166 TEST(LseekTest, ProcStatTwice) { 167 const FileDescriptor fd1 = 168 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); 169 const FileDescriptor fd2 = 170 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); 171 172 ASSERT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 173 ASSERT_THAT(lseek(fd1.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); 174 ASSERT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceeds()); 175 // Check that just because we moved fd1, fd2 doesn't move. 176 ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 177 178 const FileDescriptor fd3 = 179 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); 180 ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 181 } 182 183 TEST(LseekTest, EtcPasswdDup) { 184 const FileDescriptor fd1 = 185 ASSERT_NO_ERRNO_AND_VALUE(Open("/etc/passwd", O_RDONLY)); 186 const FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(fd1.Dup()); 187 188 ASSERT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 189 ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 190 ASSERT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceeds()); 191 // Check that just because we moved fd1, fd2 doesn't move. 192 ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); 193 194 const FileDescriptor fd3 = ASSERT_NO_ERRNO_AND_VALUE(fd1.Dup()); 195 ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); 196 } 197 198 TEST(LseekTest, SeekDataAndSeekHole) { 199 auto file_name = NewTempAbsPath(); 200 std::string contents("DEADBEEF"); 201 ASSERT_NO_ERRNO(CreateWithContents(file_name, contents, 0666)); 202 auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_RDWR)); 203 204 // Not all filesystems support SEEK_DATA and SEEK_HOLE yet. 205 SKIP_IF(lseek(fd.get(), 0, SEEK_DATA) == -1 && errno == EINVAL); 206 207 int mid = contents.size() / 2, end = contents.size(); 208 ASSERT_THAT(lseek(fd.get(), mid, SEEK_DATA), SyscallSucceedsWithValue(mid)); 209 ASSERT_THAT(lseek(fd.get(), mid, SEEK_HOLE), SyscallSucceedsWithValue(end)); 210 211 // "ENXIO whence is SEEK_DATA or SEEK_HOLE, and offset is beyond the end of 212 // the file" - lseek(2) 213 ASSERT_THAT(lseek(fd.get(), end, SEEK_DATA), SyscallFailsWithErrno(ENXIO)); 214 ASSERT_THAT(lseek(fd.get(), end, SEEK_HOLE), SyscallFailsWithErrno(ENXIO)); 215 } 216 217 // TODO(magi): Add tests where we have donated in sockets. 218 219 } // namespace 220 221 } // namespace testing 222 } // namespace gvisor