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: 26 virtual RandomAccessFile* MakeTestFile() { 27 return new FdFile(fileno(tmpfile()), false); 28 } 29 }; 30 31 TEST_F(FdFileTest, Read) { 32 TestRead(); 33 } 34 35 TEST_F(FdFileTest, SetLength) { 36 TestSetLength(); 37 } 38 39 TEST_F(FdFileTest, Write) { 40 TestWrite(); 41 } 42 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 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 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> 82 static void NullTerminateCharArray(char (&array)[Size]) { 83 array[Size - 1] = '\0'; 84 } 85 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 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 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 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 189 TEST_F(FdFileTest, OperatorMoveEquals) { 190 // Make sure the read_only_ flag is correctly copied 191 // over. 192 art::ScratchFile tmp; 193 FdFile file(tmp.GetFilename(), O_RDONLY, false); 194 ASSERT_TRUE(file.ReadOnlyMode()); 195 196 FdFile file2(tmp.GetFilename(), O_RDWR, false); 197 ASSERT_FALSE(file2.ReadOnlyMode()); 198 199 file2 = std::move(file); 200 ASSERT_TRUE(file2.ReadOnlyMode()); 201 } 202 203 TEST_F(FdFileTest, EraseWithPathUnlinks) { 204 // New scratch file, zero-length. 205 art::ScratchFile tmp; 206 std::string filename = tmp.GetFilename(); 207 tmp.Close(); // This is required because of the unlink race between the scratch file and the 208 // FdFile, which leads to close-guard breakage. 209 FdFile file(filename, O_RDWR, false); 210 ASSERT_TRUE(file.IsOpened()); 211 EXPECT_GE(file.Fd(), 0); 212 uint8_t buffer[16] = { 0 }; 213 EXPECT_TRUE(file.WriteFully(&buffer, sizeof(buffer))); 214 EXPECT_EQ(file.Flush(), 0); 215 216 EXPECT_TRUE(file.Erase(true)); 217 218 EXPECT_FALSE(file.IsOpened()); 219 220 EXPECT_FALSE(art::OS::FileExists(filename.c_str())) << filename; 221 } 222 223 TEST_F(FdFileTest, Compare) { 224 std::vector<uint8_t> buffer; 225 constexpr int64_t length = 17 * art::KB; 226 for (size_t i = 0; i < length; ++i) { 227 buffer.push_back(static_cast<uint8_t>(i)); 228 } 229 230 auto reset_compare = [&](art::ScratchFile& a, art::ScratchFile& b) { 231 a.GetFile()->ResetOffset(); 232 b.GetFile()->ResetOffset(); 233 return a.GetFile()->Compare(b.GetFile()); 234 }; 235 236 art::ScratchFile tmp; 237 EXPECT_TRUE(tmp.GetFile()->WriteFully(&buffer[0], length)); 238 EXPECT_EQ(tmp.GetFile()->GetLength(), length); 239 240 art::ScratchFile tmp2; 241 EXPECT_TRUE(tmp2.GetFile()->WriteFully(&buffer[0], length)); 242 EXPECT_EQ(tmp2.GetFile()->GetLength(), length); 243 244 // Basic equality check. 245 tmp.GetFile()->ResetOffset(); 246 tmp2.GetFile()->ResetOffset(); 247 // Files should be the same. 248 EXPECT_EQ(reset_compare(tmp, tmp2), 0); 249 250 // Change a byte near the start. 251 ++buffer[2]; 252 art::ScratchFile tmp3; 253 EXPECT_TRUE(tmp3.GetFile()->WriteFully(&buffer[0], length)); 254 --buffer[2]; 255 EXPECT_NE(reset_compare(tmp, tmp3), 0); 256 257 // Change a byte near the middle. 258 ++buffer[length / 2]; 259 art::ScratchFile tmp4; 260 EXPECT_TRUE(tmp4.GetFile()->WriteFully(&buffer[0], length)); 261 --buffer[length / 2]; 262 EXPECT_NE(reset_compare(tmp, tmp4), 0); 263 264 // Change a byte near the end. 265 ++buffer[length - 5]; 266 art::ScratchFile tmp5; 267 EXPECT_TRUE(tmp5.GetFile()->WriteFully(&buffer[0], length)); 268 --buffer[length - 5]; 269 EXPECT_NE(reset_compare(tmp, tmp5), 0); 270 271 // Reference check 272 art::ScratchFile tmp6; 273 EXPECT_TRUE(tmp6.GetFile()->WriteFully(&buffer[0], length)); 274 EXPECT_EQ(reset_compare(tmp, tmp6), 0); 275 } 276 277 TEST_F(FdFileTest, PipeFlush) { 278 int pipefd[2]; 279 ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC)); 280 281 FdFile file(pipefd[1], true); 282 ASSERT_TRUE(file.WriteFully("foo", 3)); 283 ASSERT_EQ(0, file.Flush()); 284 ASSERT_EQ(0, file.FlushCloseOrErase()); 285 close(pipefd[0]); 286 } 287 288 } // namespace unix_file 289