1 /*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "base/unix_file/fd_file.h"
18 #include "base/unix_file/random_access_file_test.h"
19 #include "common_runtime_test.h" // For ScratchFile
20 #include "gtest/gtest.h"
21
22 namespace unix_file {
23
24 class FdFileTest : public RandomAccessFileTest {
25 protected:
MakeTestFile()26 virtual RandomAccessFile* MakeTestFile() {
27 return new FdFile(fileno(tmpfile()), false);
28 }
29 };
30
TEST_F(FdFileTest,Read)31 TEST_F(FdFileTest, Read) {
32 TestRead();
33 }
34
TEST_F(FdFileTest,SetLength)35 TEST_F(FdFileTest, SetLength) {
36 TestSetLength();
37 }
38
TEST_F(FdFileTest,Write)39 TEST_F(FdFileTest, Write) {
40 TestWrite();
41 }
42
TEST_F(FdFileTest,UnopenedFile)43 TEST_F(FdFileTest, UnopenedFile) {
44 FdFile file;
45 EXPECT_EQ(-1, file.Fd());
46 EXPECT_FALSE(file.IsOpened());
47 EXPECT_TRUE(file.GetPath().empty());
48 }
49
TEST_F(FdFileTest,OpenClose)50 TEST_F(FdFileTest, OpenClose) {
51 std::string good_path(GetTmpPath("some-file.txt"));
52 FdFile file(good_path, O_CREAT | O_WRONLY, true);
53 ASSERT_TRUE(file.IsOpened());
54 EXPECT_GE(file.Fd(), 0);
55 EXPECT_TRUE(file.IsOpened());
56 EXPECT_FALSE(file.ReadOnlyMode());
57 EXPECT_EQ(0, file.Flush());
58 EXPECT_EQ(0, file.Close());
59 EXPECT_EQ(-1, file.Fd());
60 EXPECT_FALSE(file.IsOpened());
61 FdFile file2(good_path, O_RDONLY, true);
62 EXPECT_TRUE(file2.IsOpened());
63 EXPECT_TRUE(file2.ReadOnlyMode());
64 EXPECT_GE(file2.Fd(), 0);
65
66 ASSERT_EQ(file2.Close(), 0);
67 ASSERT_EQ(unlink(good_path.c_str()), 0);
68 }
69
TEST_F(FdFileTest,ReadFullyEmptyFile)70 TEST_F(FdFileTest, ReadFullyEmptyFile) {
71 // New scratch file, zero-length.
72 art::ScratchFile tmp;
73 FdFile file(tmp.GetFilename(), O_RDONLY, false);
74 ASSERT_TRUE(file.IsOpened());
75 EXPECT_TRUE(file.ReadOnlyMode());
76 EXPECT_GE(file.Fd(), 0);
77 uint8_t buffer[16];
78 EXPECT_FALSE(file.ReadFully(&buffer, 4));
79 }
80
81 template <size_t Size>
NullTerminateCharArray(char (& array)[Size])82 static void NullTerminateCharArray(char (&array)[Size]) {
83 array[Size - 1] = '\0';
84 }
85
TEST_F(FdFileTest,ReadFullyWithOffset)86 TEST_F(FdFileTest, ReadFullyWithOffset) {
87 // New scratch file, zero-length.
88 art::ScratchFile tmp;
89 FdFile file(tmp.GetFilename(), O_RDWR, false);
90 ASSERT_TRUE(file.IsOpened());
91 EXPECT_GE(file.Fd(), 0);
92 EXPECT_FALSE(file.ReadOnlyMode());
93
94 char ignore_prefix[20] = {'a', };
95 NullTerminateCharArray(ignore_prefix);
96 char read_suffix[10] = {'b', };
97 NullTerminateCharArray(read_suffix);
98
99 off_t offset = 0;
100 // Write scratch data to file that we can read back into.
101 EXPECT_TRUE(file.Write(ignore_prefix, sizeof(ignore_prefix), offset));
102 offset += sizeof(ignore_prefix);
103 EXPECT_TRUE(file.Write(read_suffix, sizeof(read_suffix), offset));
104
105 ASSERT_EQ(file.Flush(), 0);
106
107 // Reading at an offset should only produce 'bbbb...', since we ignore the 'aaa...' prefix.
108 char buffer[sizeof(read_suffix)];
109 EXPECT_TRUE(file.PreadFully(buffer, sizeof(read_suffix), offset));
110 EXPECT_STREQ(&read_suffix[0], &buffer[0]);
111
112 ASSERT_EQ(file.Close(), 0);
113 }
114
TEST_F(FdFileTest,ReadWriteFullyWithOffset)115 TEST_F(FdFileTest, ReadWriteFullyWithOffset) {
116 // New scratch file, zero-length.
117 art::ScratchFile tmp;
118 FdFile file(tmp.GetFilename(), O_RDWR, false);
119 ASSERT_GE(file.Fd(), 0);
120 EXPECT_TRUE(file.IsOpened());
121 EXPECT_FALSE(file.ReadOnlyMode());
122
123 const char* test_string = "This is a test string";
124 size_t length = strlen(test_string) + 1;
125 const size_t offset = 12;
126 std::unique_ptr<char[]> offset_read_string(new char[length]);
127 std::unique_ptr<char[]> read_string(new char[length]);
128
129 // Write scratch data to file that we can read back into.
130 EXPECT_TRUE(file.PwriteFully(test_string, length, offset));
131 ASSERT_EQ(file.Flush(), 0);
132
133 // Test reading both the offsets.
134 EXPECT_TRUE(file.PreadFully(&offset_read_string[0], length, offset));
135 EXPECT_STREQ(test_string, &offset_read_string[0]);
136
137 EXPECT_TRUE(file.PreadFully(&read_string[0], length, 0u));
138 EXPECT_NE(memcmp(&read_string[0], test_string, length), 0);
139
140 ASSERT_EQ(file.Close(), 0);
141 }
142
TEST_F(FdFileTest,Copy)143 TEST_F(FdFileTest, Copy) {
144 art::ScratchFile src_tmp;
145 FdFile src(src_tmp.GetFilename(), O_RDWR, false);
146 ASSERT_GE(src.Fd(), 0);
147 ASSERT_TRUE(src.IsOpened());
148
149 char src_data[] = "Some test data.";
150 ASSERT_TRUE(src.WriteFully(src_data, sizeof(src_data))); // Including the zero terminator.
151 ASSERT_EQ(0, src.Flush());
152 ASSERT_EQ(static_cast<int64_t>(sizeof(src_data)), src.GetLength());
153
154 art::ScratchFile dest_tmp;
155 FdFile dest(src_tmp.GetFilename(), O_RDWR, false);
156 ASSERT_GE(dest.Fd(), 0);
157 ASSERT_TRUE(dest.IsOpened());
158
159 ASSERT_TRUE(dest.Copy(&src, 0, sizeof(src_data)));
160 ASSERT_EQ(0, dest.Flush());
161 ASSERT_EQ(static_cast<int64_t>(sizeof(src_data)), dest.GetLength());
162
163 char check_data[sizeof(src_data)];
164 ASSERT_TRUE(dest.PreadFully(check_data, sizeof(src_data), 0u));
165 CHECK_EQ(0, memcmp(check_data, src_data, sizeof(src_data)));
166
167 ASSERT_EQ(0, dest.Close());
168 ASSERT_EQ(0, src.Close());
169 }
170
TEST_F(FdFileTest,MoveConstructor)171 TEST_F(FdFileTest, MoveConstructor) {
172 // New scratch file, zero-length.
173 art::ScratchFile tmp;
174 FdFile file(tmp.GetFilename(), O_RDWR, false);
175 ASSERT_TRUE(file.IsOpened());
176 EXPECT_GE(file.Fd(), 0);
177
178 int old_fd = file.Fd();
179
180 FdFile file2(std::move(file));
181 EXPECT_FALSE(file.IsOpened());
182 EXPECT_TRUE(file2.IsOpened());
183 EXPECT_EQ(old_fd, file2.Fd());
184
185 ASSERT_EQ(file2.Flush(), 0);
186 ASSERT_EQ(file2.Close(), 0);
187 }
188
TEST_F(FdFileTest,EraseWithPathUnlinks)189 TEST_F(FdFileTest, EraseWithPathUnlinks) {
190 // New scratch file, zero-length.
191 art::ScratchFile tmp;
192 std::string filename = tmp.GetFilename();
193 tmp.Close(); // This is required because of the unlink race between the scratch file and the
194 // FdFile, which leads to close-guard breakage.
195 FdFile file(filename, O_RDWR, false);
196 ASSERT_TRUE(file.IsOpened());
197 EXPECT_GE(file.Fd(), 0);
198 uint8_t buffer[16] = { 0 };
199 EXPECT_TRUE(file.WriteFully(&buffer, sizeof(buffer)));
200 EXPECT_EQ(file.Flush(), 0);
201
202 EXPECT_TRUE(file.Erase(true));
203
204 EXPECT_FALSE(file.IsOpened());
205
206 EXPECT_FALSE(art::OS::FileExists(filename.c_str())) << filename;
207 }
208
209 } // namespace unix_file
210