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