Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 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 | |
David Sehr | d5f8de8 | 2018-04-27 14:12:03 -0700 | [diff] [blame] | 17 | #include "base/common_art_test.h" // For ScratchFile |
Andreas Gampe | dfcd82c | 2018-10-16 20:22:37 -0700 | [diff] [blame] | 18 | #include "base/file_utils.h" |
Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 19 | #include "gtest/gtest.h" |
David Sehr | 1979c64 | 2018-04-26 14:41:18 -0700 | [diff] [blame] | 20 | #include "fd_file.h" |
| 21 | #include "random_access_file_test.h" |
Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 22 | |
| 23 | namespace unix_file { |
| 24 | |
| 25 | class FdFileTest : public RandomAccessFileTest { |
| 26 | protected: |
Andreas Gampe | fa6a1b0 | 2018-09-07 08:11:55 -0700 | [diff] [blame] | 27 | RandomAccessFile* MakeTestFile() override { |
Josh Gao | 7f00f5a | 2018-08-30 17:26:49 -0700 | [diff] [blame] | 28 | FILE* tmp = tmpfile(); |
Andreas Gampe | dfcd82c | 2018-10-16 20:22:37 -0700 | [diff] [blame] | 29 | int fd = art::DupCloexec(fileno(tmp)); |
Josh Gao | 7f00f5a | 2018-08-30 17:26:49 -0700 | [diff] [blame] | 30 | fclose(tmp); |
| 31 | return new FdFile(fd, false); |
Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 32 | } |
| 33 | }; |
| 34 | |
| 35 | TEST_F(FdFileTest, Read) { |
| 36 | TestRead(); |
| 37 | } |
| 38 | |
| 39 | TEST_F(FdFileTest, SetLength) { |
| 40 | TestSetLength(); |
| 41 | } |
| 42 | |
| 43 | TEST_F(FdFileTest, Write) { |
| 44 | TestWrite(); |
| 45 | } |
| 46 | |
| 47 | TEST_F(FdFileTest, UnopenedFile) { |
| 48 | FdFile file; |
| 49 | EXPECT_EQ(-1, file.Fd()); |
| 50 | EXPECT_FALSE(file.IsOpened()); |
| 51 | EXPECT_TRUE(file.GetPath().empty()); |
| 52 | } |
| 53 | |
| 54 | TEST_F(FdFileTest, OpenClose) { |
| 55 | std::string good_path(GetTmpPath("some-file.txt")); |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 56 | FdFile file(good_path, O_CREAT | O_WRONLY, true); |
| 57 | ASSERT_TRUE(file.IsOpened()); |
Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 58 | EXPECT_GE(file.Fd(), 0); |
| 59 | EXPECT_TRUE(file.IsOpened()); |
David Brazdil | b64decd | 2016-08-09 12:10:56 +0100 | [diff] [blame] | 60 | EXPECT_FALSE(file.ReadOnlyMode()); |
Andreas Gampe | 4303ba9 | 2014-11-06 01:00:46 -0800 | [diff] [blame] | 61 | EXPECT_EQ(0, file.Flush()); |
Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 62 | EXPECT_EQ(0, file.Close()); |
| 63 | EXPECT_EQ(-1, file.Fd()); |
| 64 | EXPECT_FALSE(file.IsOpened()); |
David Brazdil | b64decd | 2016-08-09 12:10:56 +0100 | [diff] [blame] | 65 | FdFile file2(good_path, O_RDONLY, true); |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 66 | EXPECT_TRUE(file2.IsOpened()); |
David Brazdil | b64decd | 2016-08-09 12:10:56 +0100 | [diff] [blame] | 67 | EXPECT_TRUE(file2.ReadOnlyMode()); |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 68 | EXPECT_GE(file2.Fd(), 0); |
Andreas Gampe | 7747c8d | 2014-08-06 14:53:03 -0700 | [diff] [blame] | 69 | |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 70 | ASSERT_EQ(file2.Close(), 0); |
Andreas Gampe | 7747c8d | 2014-08-06 14:53:03 -0700 | [diff] [blame] | 71 | ASSERT_EQ(unlink(good_path.c_str()), 0); |
Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 72 | } |
| 73 | |
Andreas Gampe | 825201e | 2014-06-20 10:45:30 -0700 | [diff] [blame] | 74 | TEST_F(FdFileTest, ReadFullyEmptyFile) { |
| 75 | // New scratch file, zero-length. |
| 76 | art::ScratchFile tmp; |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 77 | FdFile file(tmp.GetFilename(), O_RDONLY, false); |
| 78 | ASSERT_TRUE(file.IsOpened()); |
David Brazdil | b64decd | 2016-08-09 12:10:56 +0100 | [diff] [blame] | 79 | EXPECT_TRUE(file.ReadOnlyMode()); |
Andreas Gampe | 825201e | 2014-06-20 10:45:30 -0700 | [diff] [blame] | 80 | EXPECT_GE(file.Fd(), 0); |
Andreas Gampe | 825201e | 2014-06-20 10:45:30 -0700 | [diff] [blame] | 81 | uint8_t buffer[16]; |
| 82 | EXPECT_FALSE(file.ReadFully(&buffer, 4)); |
| 83 | } |
| 84 | |
Igor Murashkin | 3774335 | 2014-11-13 14:38:00 -0800 | [diff] [blame] | 85 | template <size_t Size> |
| 86 | static void NullTerminateCharArray(char (&array)[Size]) { |
| 87 | array[Size - 1] = '\0'; |
| 88 | } |
| 89 | |
| 90 | TEST_F(FdFileTest, ReadFullyWithOffset) { |
| 91 | // New scratch file, zero-length. |
| 92 | art::ScratchFile tmp; |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 93 | FdFile file(tmp.GetFilename(), O_RDWR, false); |
| 94 | ASSERT_TRUE(file.IsOpened()); |
Igor Murashkin | 3774335 | 2014-11-13 14:38:00 -0800 | [diff] [blame] | 95 | EXPECT_GE(file.Fd(), 0); |
David Brazdil | b64decd | 2016-08-09 12:10:56 +0100 | [diff] [blame] | 96 | EXPECT_FALSE(file.ReadOnlyMode()); |
Igor Murashkin | 3774335 | 2014-11-13 14:38:00 -0800 | [diff] [blame] | 97 | |
| 98 | char ignore_prefix[20] = {'a', }; |
| 99 | NullTerminateCharArray(ignore_prefix); |
| 100 | char read_suffix[10] = {'b', }; |
| 101 | NullTerminateCharArray(read_suffix); |
| 102 | |
| 103 | off_t offset = 0; |
| 104 | // Write scratch data to file that we can read back into. |
| 105 | EXPECT_TRUE(file.Write(ignore_prefix, sizeof(ignore_prefix), offset)); |
| 106 | offset += sizeof(ignore_prefix); |
| 107 | EXPECT_TRUE(file.Write(read_suffix, sizeof(read_suffix), offset)); |
| 108 | |
| 109 | ASSERT_EQ(file.Flush(), 0); |
| 110 | |
| 111 | // Reading at an offset should only produce 'bbbb...', since we ignore the 'aaa...' prefix. |
| 112 | char buffer[sizeof(read_suffix)]; |
| 113 | EXPECT_TRUE(file.PreadFully(buffer, sizeof(read_suffix), offset)); |
| 114 | EXPECT_STREQ(&read_suffix[0], &buffer[0]); |
| 115 | |
| 116 | ASSERT_EQ(file.Close(), 0); |
| 117 | } |
| 118 | |
Mathieu Chartier | 6f6b134 | 2016-03-09 11:14:50 -0800 | [diff] [blame] | 119 | TEST_F(FdFileTest, ReadWriteFullyWithOffset) { |
| 120 | // New scratch file, zero-length. |
| 121 | art::ScratchFile tmp; |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 122 | FdFile file(tmp.GetFilename(), O_RDWR, false); |
| 123 | ASSERT_GE(file.Fd(), 0); |
Mathieu Chartier | 6f6b134 | 2016-03-09 11:14:50 -0800 | [diff] [blame] | 124 | EXPECT_TRUE(file.IsOpened()); |
David Brazdil | b64decd | 2016-08-09 12:10:56 +0100 | [diff] [blame] | 125 | EXPECT_FALSE(file.ReadOnlyMode()); |
Mathieu Chartier | 6f6b134 | 2016-03-09 11:14:50 -0800 | [diff] [blame] | 126 | |
| 127 | const char* test_string = "This is a test string"; |
| 128 | size_t length = strlen(test_string) + 1; |
| 129 | const size_t offset = 12; |
| 130 | std::unique_ptr<char[]> offset_read_string(new char[length]); |
| 131 | std::unique_ptr<char[]> read_string(new char[length]); |
| 132 | |
| 133 | // Write scratch data to file that we can read back into. |
| 134 | EXPECT_TRUE(file.PwriteFully(test_string, length, offset)); |
| 135 | ASSERT_EQ(file.Flush(), 0); |
| 136 | |
| 137 | // Test reading both the offsets. |
| 138 | EXPECT_TRUE(file.PreadFully(&offset_read_string[0], length, offset)); |
| 139 | EXPECT_STREQ(test_string, &offset_read_string[0]); |
| 140 | |
| 141 | EXPECT_TRUE(file.PreadFully(&read_string[0], length, 0u)); |
| 142 | EXPECT_NE(memcmp(&read_string[0], test_string, length), 0); |
| 143 | |
| 144 | ASSERT_EQ(file.Close(), 0); |
| 145 | } |
| 146 | |
Vladimir Marko | 5096e66 | 2015-12-08 19:25:49 +0000 | [diff] [blame] | 147 | TEST_F(FdFileTest, Copy) { |
| 148 | art::ScratchFile src_tmp; |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 149 | FdFile src(src_tmp.GetFilename(), O_RDWR, false); |
Vladimir Marko | 5096e66 | 2015-12-08 19:25:49 +0000 | [diff] [blame] | 150 | ASSERT_GE(src.Fd(), 0); |
| 151 | ASSERT_TRUE(src.IsOpened()); |
| 152 | |
| 153 | char src_data[] = "Some test data."; |
| 154 | ASSERT_TRUE(src.WriteFully(src_data, sizeof(src_data))); // Including the zero terminator. |
| 155 | ASSERT_EQ(0, src.Flush()); |
| 156 | ASSERT_EQ(static_cast<int64_t>(sizeof(src_data)), src.GetLength()); |
| 157 | |
| 158 | art::ScratchFile dest_tmp; |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 159 | FdFile dest(src_tmp.GetFilename(), O_RDWR, false); |
Vladimir Marko | 5096e66 | 2015-12-08 19:25:49 +0000 | [diff] [blame] | 160 | ASSERT_GE(dest.Fd(), 0); |
| 161 | ASSERT_TRUE(dest.IsOpened()); |
| 162 | |
| 163 | ASSERT_TRUE(dest.Copy(&src, 0, sizeof(src_data))); |
| 164 | ASSERT_EQ(0, dest.Flush()); |
| 165 | ASSERT_EQ(static_cast<int64_t>(sizeof(src_data)), dest.GetLength()); |
| 166 | |
| 167 | char check_data[sizeof(src_data)]; |
| 168 | ASSERT_TRUE(dest.PreadFully(check_data, sizeof(src_data), 0u)); |
| 169 | CHECK_EQ(0, memcmp(check_data, src_data, sizeof(src_data))); |
| 170 | |
| 171 | ASSERT_EQ(0, dest.Close()); |
| 172 | ASSERT_EQ(0, src.Close()); |
| 173 | } |
| 174 | |
Andreas Gampe | df87892 | 2015-08-13 16:44:54 -0700 | [diff] [blame] | 175 | TEST_F(FdFileTest, MoveConstructor) { |
| 176 | // New scratch file, zero-length. |
| 177 | art::ScratchFile tmp; |
| 178 | FdFile file(tmp.GetFilename(), O_RDWR, false); |
| 179 | ASSERT_TRUE(file.IsOpened()); |
| 180 | EXPECT_GE(file.Fd(), 0); |
| 181 | |
| 182 | int old_fd = file.Fd(); |
| 183 | |
| 184 | FdFile file2(std::move(file)); |
| 185 | EXPECT_FALSE(file.IsOpened()); |
| 186 | EXPECT_TRUE(file2.IsOpened()); |
| 187 | EXPECT_EQ(old_fd, file2.Fd()); |
| 188 | |
| 189 | ASSERT_EQ(file2.Flush(), 0); |
| 190 | ASSERT_EQ(file2.Close(), 0); |
| 191 | } |
| 192 | |
Narayan Kamath | eda7d3d | 2017-05-22 13:23:49 +0100 | [diff] [blame] | 193 | TEST_F(FdFileTest, OperatorMoveEquals) { |
| 194 | // Make sure the read_only_ flag is correctly copied |
| 195 | // over. |
| 196 | art::ScratchFile tmp; |
| 197 | FdFile file(tmp.GetFilename(), O_RDONLY, false); |
| 198 | ASSERT_TRUE(file.ReadOnlyMode()); |
| 199 | |
| 200 | FdFile file2(tmp.GetFilename(), O_RDWR, false); |
| 201 | ASSERT_FALSE(file2.ReadOnlyMode()); |
| 202 | |
| 203 | file2 = std::move(file); |
| 204 | ASSERT_TRUE(file2.ReadOnlyMode()); |
| 205 | } |
| 206 | |
Andreas Gampe | 550c589 | 2016-06-24 10:49:32 -0700 | [diff] [blame] | 207 | TEST_F(FdFileTest, EraseWithPathUnlinks) { |
| 208 | // New scratch file, zero-length. |
| 209 | art::ScratchFile tmp; |
| 210 | std::string filename = tmp.GetFilename(); |
| 211 | tmp.Close(); // This is required because of the unlink race between the scratch file and the |
| 212 | // FdFile, which leads to close-guard breakage. |
| 213 | FdFile file(filename, O_RDWR, false); |
| 214 | ASSERT_TRUE(file.IsOpened()); |
| 215 | EXPECT_GE(file.Fd(), 0); |
| 216 | uint8_t buffer[16] = { 0 }; |
| 217 | EXPECT_TRUE(file.WriteFully(&buffer, sizeof(buffer))); |
| 218 | EXPECT_EQ(file.Flush(), 0); |
| 219 | |
| 220 | EXPECT_TRUE(file.Erase(true)); |
| 221 | |
| 222 | EXPECT_FALSE(file.IsOpened()); |
| 223 | |
| 224 | EXPECT_FALSE(art::OS::FileExists(filename.c_str())) << filename; |
| 225 | } |
| 226 | |
Mathieu Chartier | 140c5d1 | 2017-08-04 14:25:22 -0700 | [diff] [blame] | 227 | TEST_F(FdFileTest, Compare) { |
| 228 | std::vector<uint8_t> buffer; |
| 229 | constexpr int64_t length = 17 * art::KB; |
| 230 | for (size_t i = 0; i < length; ++i) { |
| 231 | buffer.push_back(static_cast<uint8_t>(i)); |
| 232 | } |
| 233 | |
| 234 | auto reset_compare = [&](art::ScratchFile& a, art::ScratchFile& b) { |
| 235 | a.GetFile()->ResetOffset(); |
| 236 | b.GetFile()->ResetOffset(); |
| 237 | return a.GetFile()->Compare(b.GetFile()); |
| 238 | }; |
| 239 | |
| 240 | art::ScratchFile tmp; |
| 241 | EXPECT_TRUE(tmp.GetFile()->WriteFully(&buffer[0], length)); |
| 242 | EXPECT_EQ(tmp.GetFile()->GetLength(), length); |
| 243 | |
| 244 | art::ScratchFile tmp2; |
| 245 | EXPECT_TRUE(tmp2.GetFile()->WriteFully(&buffer[0], length)); |
| 246 | EXPECT_EQ(tmp2.GetFile()->GetLength(), length); |
| 247 | |
| 248 | // Basic equality check. |
| 249 | tmp.GetFile()->ResetOffset(); |
| 250 | tmp2.GetFile()->ResetOffset(); |
| 251 | // Files should be the same. |
| 252 | EXPECT_EQ(reset_compare(tmp, tmp2), 0); |
| 253 | |
| 254 | // Change a byte near the start. |
| 255 | ++buffer[2]; |
| 256 | art::ScratchFile tmp3; |
| 257 | EXPECT_TRUE(tmp3.GetFile()->WriteFully(&buffer[0], length)); |
| 258 | --buffer[2]; |
| 259 | EXPECT_NE(reset_compare(tmp, tmp3), 0); |
| 260 | |
| 261 | // Change a byte near the middle. |
| 262 | ++buffer[length / 2]; |
| 263 | art::ScratchFile tmp4; |
| 264 | EXPECT_TRUE(tmp4.GetFile()->WriteFully(&buffer[0], length)); |
| 265 | --buffer[length / 2]; |
| 266 | EXPECT_NE(reset_compare(tmp, tmp4), 0); |
| 267 | |
| 268 | // Change a byte near the end. |
| 269 | ++buffer[length - 5]; |
| 270 | art::ScratchFile tmp5; |
| 271 | EXPECT_TRUE(tmp5.GetFile()->WriteFully(&buffer[0], length)); |
| 272 | --buffer[length - 5]; |
| 273 | EXPECT_NE(reset_compare(tmp, tmp5), 0); |
| 274 | |
| 275 | // Reference check |
| 276 | art::ScratchFile tmp6; |
| 277 | EXPECT_TRUE(tmp6.GetFile()->WriteFully(&buffer[0], length)); |
| 278 | EXPECT_EQ(reset_compare(tmp, tmp6), 0); |
| 279 | } |
| 280 | |
Josh Gao | 494ec69 | 2017-10-03 12:51:54 -0700 | [diff] [blame] | 281 | TEST_F(FdFileTest, PipeFlush) { |
| 282 | int pipefd[2]; |
| 283 | ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC)); |
| 284 | |
| 285 | FdFile file(pipefd[1], true); |
| 286 | ASSERT_TRUE(file.WriteFully("foo", 3)); |
| 287 | ASSERT_EQ(0, file.Flush()); |
| 288 | ASSERT_EQ(0, file.FlushCloseOrErase()); |
| 289 | close(pipefd[0]); |
| 290 | } |
| 291 | |
Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 292 | } // namespace unix_file |