Home | History | Annotate | Download | only in unix_file
      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