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