1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <assert.h> 6#include <errno.h> 7#include <fcntl.h> 8#include <limits.h> 9#include <stdint.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <sys/stat.h> 14#include <unistd.h> 15 16#include <zircon/compiler.h> 17#include <fbl/algorithm.h> 18#include <fbl/unique_fd.h> 19 20#include "filesystems.h" 21 22namespace { 23 24bool test_lseek_position(void) { 25 BEGIN_TEST; 26 27 const char* const filename = "::lseek_position"; 28 fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR)); 29 ASSERT_TRUE(fd); 30 31 // File offset initialized to zero. 32 ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), 0); 33 ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0); 34 35 const char* const str = "hello"; 36 const size_t len = strlen(str); 37 ASSERT_EQ(write(fd.get(), str, len), len); 38 39 // After writing, the offset has been updated. 40 ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len); 41 ASSERT_EQ(lseek(fd.get(), 0, SEEK_END), len); 42 43 // Reset the offset to the start of the file. 44 ASSERT_EQ(lseek(fd.get(), -len, SEEK_END), 0); 45 46 // Read the entire file. 47 char buf[len + 1]; 48 ASSERT_EQ(read(fd.get(), buf, len), len); 49 ASSERT_EQ(memcmp(buf, str, len), 0); 50 51 // Seek and read part of the file. 52 ASSERT_EQ(lseek(fd.get(), 1, SEEK_SET), 1); 53 ASSERT_EQ(read(fd.get(), buf, len - 1), len - 1); 54 ASSERT_EQ(memcmp(buf, &str[1], len - 1), 0); 55 56 ASSERT_EQ(unlink(filename), 0); 57 END_TEST; 58} 59 60bool test_lseek_out_of_bounds(void) { 61 BEGIN_TEST; 62 63 const char* const filename = "::lseek_out_of_bounds"; 64 fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR)); 65 ASSERT_TRUE(fd); 66 67 const char* const str = "hello"; 68 const size_t len = strlen(str); 69 ASSERT_EQ(write(fd.get(), str, len), len); 70 71 // After writing, the offset has been updated. 72 ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len); 73 74 // Seek beyond the end of the file. 75 ASSERT_EQ(lseek(fd.get(), 1, SEEK_CUR), len + 1); 76 ASSERT_EQ(lseek(fd.get(), 2, SEEK_END), len + 2); 77 ASSERT_EQ(lseek(fd.get(), len + 3, SEEK_SET), len + 3); 78 79 // Seek before the start of the file. 80 ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0); 81 82 // Negative seek is not allowed on Fuchsia. 83 ASSERT_EQ(lseek(fd.get(), -2, SEEK_CUR), -1); 84 ASSERT_EQ(lseek(fd.get(), -2, SEEK_SET), -1); 85 ASSERT_EQ(lseek(fd.get(), -(len + 2), SEEK_END), -1); 86 87 ASSERT_EQ(unlink(filename), 0); 88 END_TEST; 89} 90 91bool test_lseek_zero_fill(void) { 92 BEGIN_TEST; 93 94 const char* const filename = "::lseek_zero_fill"; 95 fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR)); 96 ASSERT_TRUE(fd); 97 98 const char* const str = "hello"; 99 const size_t len = strlen(str); 100 ASSERT_EQ(write(fd.get(), str, len), len); 101 102 // After writing, the offset and length have been updated. 103 ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len); 104 struct stat st; 105 ASSERT_EQ(fstat(fd.get(), &st), 0); 106 ASSERT_EQ(st.st_size, len); 107 108 // Seek beyond the end of the file. 109 size_t zeros = 10; 110 ASSERT_EQ(lseek(fd.get(), len + zeros, SEEK_SET), static_cast<off_t>(len + zeros)); 111 112 // This does not change the length of the file. 113 ASSERT_EQ(fstat(fd.get(), &st), 0); 114 ASSERT_EQ(st.st_size, len); 115 116 // From the POSIX specification: 117 // 118 // "Before any action described below is taken, and if nbyte is zero and the 119 // file is a regular file, the write() function may detect and return 120 // errors as described below. In the absence of errors, or if error 121 // detection is not performed, the write() function shall return zero 122 // and have no other results." 123 ASSERT_EQ(write(fd.get(), str, 0), 0); 124 ASSERT_EQ(fstat(fd.get(), &st), 0); 125 ASSERT_EQ(st.st_size, len); 126 127 // Zero-extend the file up to the sentinel value. 128 char sentinel = 'a'; 129 ASSERT_EQ(write(fd.get(), &sentinel, 1), 1); 130 ASSERT_EQ(fstat(fd.get(), &st), 0); 131 ASSERT_EQ(st.st_size, static_cast<off_t>(len + zeros + 1)); 132 133 // Validate the file contents. 134 { 135 char expected[len + zeros + 1]; 136 memcpy(expected, str, len); 137 memset(&expected[len], 0, zeros); 138 expected[len + zeros] = 'a'; 139 140 char buf[len + zeros + 1]; 141 ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0); 142 ASSERT_EQ(read(fd.get(), buf, sizeof(buf)), static_cast<ssize_t>(sizeof(buf))); 143 ASSERT_EQ(memcmp(buf, expected, sizeof(expected)), 0); 144 } 145 146 // Truncate and observe the (old) sentinel value has been 147 // overwritten with zeros. 148 ASSERT_EQ(ftruncate(fd.get(), len), 0); 149 zeros *= 2; 150 ASSERT_EQ(lseek(fd.get(), len + zeros, SEEK_SET), static_cast<off_t>(len + zeros)); 151 ASSERT_EQ(write(fd.get(), &sentinel, 1), 1); 152 ASSERT_EQ(fstat(fd.get(), &st), 0); 153 ASSERT_EQ(st.st_size, static_cast<off_t>(len + zeros + 1)); 154 155 { 156 char expected[len + zeros + 1]; 157 memcpy(expected, str, len); 158 memset(&expected[len], 0, zeros); 159 expected[len + zeros] = 'a'; 160 161 char buf[len + zeros + 1]; 162 ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0); 163 ASSERT_EQ(read(fd.get(), buf, sizeof(buf)), static_cast<ssize_t>(sizeof(buf))); 164 ASSERT_EQ(memcmp(buf, expected, sizeof(expected)), 0); 165 } 166 167 ASSERT_EQ(unlink(filename), 0); 168 END_TEST; 169} 170 171} // namespace 172 173RUN_FOR_ALL_FILESYSTEMS(lseek_tests, 174 RUN_TEST_MEDIUM(test_lseek_position) 175 RUN_TEST_MEDIUM(test_lseek_out_of_bounds) 176 RUN_TEST_MEDIUM(test_lseek_zero_fill) 177) 178