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
22 namespace {
23
TestLseekPosition(void)24 bool TestLseekPosition(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
TestLseekOutOfBounds(void)60 bool TestLseekOutOfBounds(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
TestLseekZeroFill(void)91 bool TestLseekZeroFill(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
173 RUN_FOR_ALL_FILESYSTEMS(lseek_tests,
174 RUN_TEST_MEDIUM(TestLseekPosition)
175 RUN_TEST_MEDIUM(TestLseekOutOfBounds)
176 RUN_TEST_MEDIUM(TestLseekZeroFill)
177 )
178