| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <zircon/compiler.h> |
| #include <fbl/algorithm.h> |
| #include <fbl/unique_fd.h> |
| |
| #include "filesystems.h" |
| |
| namespace { |
| |
| bool TestLseekPosition(void) { |
| BEGIN_TEST; |
| |
| const char* const filename = "::lseek_position"; |
| fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR)); |
| ASSERT_TRUE(fd); |
| |
| // File offset initialized to zero. |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), 0); |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0); |
| |
| const char* const str = "hello"; |
| const size_t len = strlen(str); |
| ASSERT_EQ(write(fd.get(), str, len), len); |
| |
| // After writing, the offset has been updated. |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len); |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_END), len); |
| |
| // Reset the offset to the start of the file. |
| ASSERT_EQ(lseek(fd.get(), -len, SEEK_END), 0); |
| |
| // Read the entire file. |
| char buf[len + 1]; |
| ASSERT_EQ(read(fd.get(), buf, len), len); |
| ASSERT_EQ(memcmp(buf, str, len), 0); |
| |
| // Seek and read part of the file. |
| ASSERT_EQ(lseek(fd.get(), 1, SEEK_SET), 1); |
| ASSERT_EQ(read(fd.get(), buf, len - 1), len - 1); |
| ASSERT_EQ(memcmp(buf, &str[1], len - 1), 0); |
| |
| ASSERT_EQ(unlink(filename), 0); |
| END_TEST; |
| } |
| |
| bool TestLseekOutOfBounds(void) { |
| BEGIN_TEST; |
| |
| const char* const filename = "::lseek_out_of_bounds"; |
| fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR)); |
| ASSERT_TRUE(fd); |
| |
| const char* const str = "hello"; |
| const size_t len = strlen(str); |
| ASSERT_EQ(write(fd.get(), str, len), len); |
| |
| // After writing, the offset has been updated. |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len); |
| |
| // Seek beyond the end of the file. |
| ASSERT_EQ(lseek(fd.get(), 1, SEEK_CUR), len + 1); |
| ASSERT_EQ(lseek(fd.get(), 2, SEEK_END), len + 2); |
| ASSERT_EQ(lseek(fd.get(), len + 3, SEEK_SET), len + 3); |
| |
| // Seek before the start of the file. |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0); |
| |
| // Negative seek is not allowed on Fuchsia. |
| ASSERT_EQ(lseek(fd.get(), -2, SEEK_CUR), -1); |
| ASSERT_EQ(lseek(fd.get(), -2, SEEK_SET), -1); |
| ASSERT_EQ(lseek(fd.get(), -(len + 2), SEEK_END), -1); |
| |
| ASSERT_EQ(unlink(filename), 0); |
| END_TEST; |
| } |
| |
| bool TestLseekZeroFill(void) { |
| BEGIN_TEST; |
| |
| const char* const filename = "::lseek_zero_fill"; |
| fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR)); |
| ASSERT_TRUE(fd); |
| |
| const char* const str = "hello"; |
| const size_t len = strlen(str); |
| ASSERT_EQ(write(fd.get(), str, len), len); |
| |
| // After writing, the offset and length have been updated. |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len); |
| struct stat st; |
| ASSERT_EQ(fstat(fd.get(), &st), 0); |
| ASSERT_EQ(st.st_size, len); |
| |
| // Seek beyond the end of the file. |
| size_t zeros = 10; |
| ASSERT_EQ(lseek(fd.get(), len + zeros, SEEK_SET), static_cast<off_t>(len + zeros)); |
| |
| // This does not change the length of the file. |
| ASSERT_EQ(fstat(fd.get(), &st), 0); |
| ASSERT_EQ(st.st_size, len); |
| |
| // From the POSIX specification: |
| // |
| // "Before any action described below is taken, and if nbyte is zero and the |
| // file is a regular file, the write() function may detect and return |
| // errors as described below. In the absence of errors, or if error |
| // detection is not performed, the write() function shall return zero |
| // and have no other results." |
| ASSERT_EQ(write(fd.get(), str, 0), 0); |
| ASSERT_EQ(fstat(fd.get(), &st), 0); |
| ASSERT_EQ(st.st_size, len); |
| |
| // Zero-extend the file up to the sentinel value. |
| char sentinel = 'a'; |
| ASSERT_EQ(write(fd.get(), &sentinel, 1), 1); |
| ASSERT_EQ(fstat(fd.get(), &st), 0); |
| ASSERT_EQ(st.st_size, static_cast<off_t>(len + zeros + 1)); |
| |
| // Validate the file contents. |
| { |
| char expected[len + zeros + 1]; |
| memcpy(expected, str, len); |
| memset(&expected[len], 0, zeros); |
| expected[len + zeros] = 'a'; |
| |
| char buf[len + zeros + 1]; |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0); |
| ASSERT_EQ(read(fd.get(), buf, sizeof(buf)), static_cast<ssize_t>(sizeof(buf))); |
| ASSERT_EQ(memcmp(buf, expected, sizeof(expected)), 0); |
| } |
| |
| // Truncate and observe the (old) sentinel value has been |
| // overwritten with zeros. |
| ASSERT_EQ(ftruncate(fd.get(), len), 0); |
| zeros *= 2; |
| ASSERT_EQ(lseek(fd.get(), len + zeros, SEEK_SET), static_cast<off_t>(len + zeros)); |
| ASSERT_EQ(write(fd.get(), &sentinel, 1), 1); |
| ASSERT_EQ(fstat(fd.get(), &st), 0); |
| ASSERT_EQ(st.st_size, static_cast<off_t>(len + zeros + 1)); |
| |
| { |
| char expected[len + zeros + 1]; |
| memcpy(expected, str, len); |
| memset(&expected[len], 0, zeros); |
| expected[len + zeros] = 'a'; |
| |
| char buf[len + zeros + 1]; |
| ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0); |
| ASSERT_EQ(read(fd.get(), buf, sizeof(buf)), static_cast<ssize_t>(sizeof(buf))); |
| ASSERT_EQ(memcmp(buf, expected, sizeof(expected)), 0); |
| } |
| |
| ASSERT_EQ(unlink(filename), 0); |
| END_TEST; |
| } |
| |
| } // namespace |
| |
| RUN_FOR_ALL_FILESYSTEMS(lseek_tests, |
| RUN_TEST_MEDIUM(TestLseekPosition) RUN_TEST_MEDIUM(TestLseekOutOfBounds) |
| RUN_TEST_MEDIUM(TestLseekZeroFill)) |