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 "fd_file.h"
     18 
     19 #include <errno.h>
     20 #include <sys/stat.h>
     21 #include <sys/types.h>
     22 #include <unistd.h>
     23 
     24 #if defined(__BIONIC__)
     25 #include <android/fdsan.h>
     26 #endif
     27 
     28 #if defined(_WIN32)
     29 #include <windows.h>
     30 #endif
     31 
     32 #include <limits>
     33 
     34 #include <android-base/file.h>
     35 #include <android-base/logging.h>
     36 
     37 // Includes needed for FdFile::Copy().
     38 #ifdef __linux__
     39 #include <sys/sendfile.h>
     40 #else
     41 #include <algorithm>
     42 #include "base/globals.h"
     43 #include "base/stl_util.h"
     44 #endif
     45 
     46 namespace unix_file {
     47 
     48 #if defined(_WIN32)
     49 // RAII wrapper for an event object to allow asynchronous I/O to correctly signal completion.
     50 class ScopedEvent {
     51  public:
     52   ScopedEvent() {
     53     handle_ = CreateEventA(/*lpEventAttributes*/ nullptr,
     54                            /*bManualReset*/ true,
     55                            /*bInitialState*/ false,
     56                            /*lpName*/ nullptr);
     57   }
     58 
     59   ~ScopedEvent() { CloseHandle(handle_); }
     60 
     61   HANDLE handle() { return handle_; }
     62 
     63  private:
     64   HANDLE handle_;
     65   DISALLOW_COPY_AND_ASSIGN(ScopedEvent);
     66 };
     67 
     68 // Windows implementation of pread/pwrite. Note that these DO move the file descriptor's read/write
     69 // position, but do so atomically.
     70 static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
     71   ScopedEvent event;
     72   if (event.handle() == INVALID_HANDLE_VALUE) {
     73     PLOG(ERROR) << "Could not create event handle.";
     74     errno = EIO;
     75     return static_cast<ssize_t>(-1);
     76   }
     77 
     78   auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
     79   DWORD bytes_read = 0;
     80   OVERLAPPED overlapped = {};
     81   overlapped.Offset = static_cast<DWORD>(offset);
     82   overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
     83   overlapped.hEvent = event.handle();
     84   if (!ReadFile(handle, data, static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
     85     // If the read failed with other than ERROR_IO_PENDING, return an error.
     86     // ERROR_IO_PENDING signals the write was begun asynchronously.
     87     // Block until the asynchronous operation has finished or fails, and return
     88     // result accordingly.
     89     if (::GetLastError() != ERROR_IO_PENDING ||
     90         !::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE)) {
     91       // In case someone tries to read errno (since this is masquerading as a POSIX call).
     92       errno = EIO;
     93       return static_cast<ssize_t>(-1);
     94     }
     95   }
     96   return static_cast<ssize_t>(bytes_read);
     97 }
     98 
     99 static ssize_t pwrite(int fd, const void* buf, size_t count, off64_t offset) {
    100   ScopedEvent event;
    101   if (event.handle() == INVALID_HANDLE_VALUE) {
    102     PLOG(ERROR) << "Could not create event handle.";
    103     errno = EIO;
    104     return static_cast<ssize_t>(-1);
    105   }
    106 
    107   auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
    108   DWORD bytes_written = 0;
    109   OVERLAPPED overlapped = {};
    110   overlapped.Offset = static_cast<DWORD>(offset);
    111   overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
    112   overlapped.hEvent = event.handle();
    113   if (!::WriteFile(handle, buf, count, &bytes_written, &overlapped)) {
    114     // If the write failed with other than ERROR_IO_PENDING, return an error.
    115     // ERROR_IO_PENDING signals the write was begun asynchronously.
    116     // Block until the asynchronous operation has finished or fails, and return
    117     // result accordingly.
    118     if (::GetLastError() != ERROR_IO_PENDING ||
    119         !::GetOverlappedResult(handle, &overlapped, &bytes_written, TRUE)) {
    120       // In case someone tries to read errno (since this is masquerading as a POSIX call).
    121       errno = EIO;
    122       return static_cast<ssize_t>(-1);
    123     }
    124   }
    125   return static_cast<ssize_t>(bytes_written);
    126 }
    127 
    128 static int fsync(int fd) {
    129   auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
    130   if (handle != INVALID_HANDLE_VALUE && ::FlushFileBuffers(handle)) {
    131     return 0;
    132   }
    133   errno = EINVAL;
    134   return -1;
    135 }
    136 #endif
    137 
    138 #if defined(__BIONIC__)
    139 static uint64_t GetFdFileOwnerTag(FdFile* fd_file) {
    140   return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ART_FDFILE,
    141                                         reinterpret_cast<uint64_t>(fd_file));
    142 }
    143 #endif
    144 
    145 FdFile::FdFile(int fd, bool check_usage)
    146     : FdFile(fd, std::string(), check_usage) {}
    147 
    148 FdFile::FdFile(int fd, const std::string& path, bool check_usage)
    149     : FdFile(fd, path, check_usage, false) {}
    150 
    151 FdFile::FdFile(int fd, const std::string& path, bool check_usage,
    152                bool read_only_mode)
    153     : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
    154       fd_(fd),
    155       file_path_(path),
    156       read_only_mode_(read_only_mode) {
    157 #if defined(__BIONIC__)
    158   if (fd >= 0) {
    159     android_fdsan_exchange_owner_tag(fd, 0, GetFdFileOwnerTag(this));
    160   }
    161 #endif
    162 }
    163 
    164 FdFile::FdFile(const std::string& path, int flags, mode_t mode,
    165                bool check_usage) {
    166   Open(path, flags, mode);
    167   if (!check_usage || !IsOpened()) {
    168     guard_state_ = GuardState::kNoCheck;
    169   }
    170 }
    171 
    172 void FdFile::Destroy() {
    173   if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) {
    174     if (guard_state_ < GuardState::kFlushed) {
    175       LOG(ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction.";
    176     }
    177     if (guard_state_ < GuardState::kClosed) {
    178       LOG(ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
    179     }
    180     DCHECK_GE(guard_state_, GuardState::kClosed);
    181   }
    182   if (fd_ != -1) {
    183     if (Close() != 0) {
    184       PLOG(WARNING) << "Failed to close file with fd=" << fd_ << " path=" << file_path_;
    185     }
    186   }
    187 }
    188 
    189 FdFile::FdFile(FdFile&& other) noexcept
    190     : guard_state_(other.guard_state_),
    191       fd_(other.fd_),
    192       file_path_(std::move(other.file_path_)),
    193       read_only_mode_(other.read_only_mode_) {
    194 #if defined(__BIONIC__)
    195   if (fd_ >= 0) {
    196     android_fdsan_exchange_owner_tag(fd_, GetFdFileOwnerTag(&other), GetFdFileOwnerTag(this));
    197   }
    198 #endif
    199   other.guard_state_ = GuardState::kClosed;
    200   other.fd_ = -1;
    201 }
    202 
    203 FdFile& FdFile::operator=(FdFile&& other) noexcept {
    204   if (this == &other) {
    205     return *this;
    206   }
    207 
    208   if (this->fd_ != other.fd_) {
    209     Destroy();  // Free old state.
    210   }
    211 
    212   guard_state_ = other.guard_state_;
    213   fd_ = other.fd_;
    214   file_path_ = std::move(other.file_path_);
    215   read_only_mode_ = other.read_only_mode_;
    216 
    217 #if defined(__BIONIC__)
    218   if (fd_ >= 0) {
    219     android_fdsan_exchange_owner_tag(fd_, GetFdFileOwnerTag(&other), GetFdFileOwnerTag(this));
    220   }
    221 #endif
    222   other.guard_state_ = GuardState::kClosed;
    223   other.fd_ = -1;
    224   return *this;
    225 }
    226 
    227 FdFile::~FdFile() {
    228   Destroy();
    229 }
    230 
    231 int FdFile::Release() {
    232   int tmp_fd = fd_;
    233   fd_ = -1;
    234   guard_state_ = GuardState::kNoCheck;
    235 #if defined(__BIONIC__)
    236   if (tmp_fd >= 0) {
    237     android_fdsan_exchange_owner_tag(tmp_fd, GetFdFileOwnerTag(this), 0);
    238   }
    239 #endif
    240   return tmp_fd;
    241 }
    242 
    243 void FdFile::Reset(int fd, bool check_usage) {
    244   CHECK_NE(fd, fd_);
    245 
    246   if (fd_ != -1) {
    247     Destroy();
    248   }
    249   fd_ = fd;
    250 
    251 #if defined(__BIONIC__)
    252   if (fd_ >= 0) {
    253     android_fdsan_exchange_owner_tag(fd_, 0, GetFdFileOwnerTag(this));
    254   }
    255 #endif
    256 
    257   if (check_usage) {
    258     guard_state_ = fd == -1 ? GuardState::kNoCheck : GuardState::kBase;
    259   } else {
    260     guard_state_ = GuardState::kNoCheck;
    261   }
    262 }
    263 
    264 void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) {
    265   if (kCheckSafeUsage) {
    266     if (guard_state_ < GuardState::kNoCheck) {
    267       if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) {
    268         LOG(ERROR) << warning;
    269       }
    270       guard_state_ = target;
    271     }
    272   }
    273 }
    274 
    275 void FdFile::moveUp(GuardState target, const char* warning) {
    276   if (kCheckSafeUsage) {
    277     if (guard_state_ < GuardState::kNoCheck) {
    278       if (guard_state_ < target) {
    279         guard_state_ = target;
    280       } else if (target < guard_state_) {
    281         LOG(ERROR) << warning;
    282       }
    283     }
    284   }
    285 }
    286 
    287 bool FdFile::Open(const std::string& path, int flags) {
    288   return Open(path, flags, 0640);
    289 }
    290 
    291 bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
    292   static_assert(O_RDONLY == 0, "Readonly flag has unexpected value.");
    293   DCHECK_EQ(fd_, -1) << path;
    294   read_only_mode_ = ((flags & O_ACCMODE) == O_RDONLY);
    295   fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
    296   if (fd_ == -1) {
    297     return false;
    298   }
    299 
    300 #if defined(__BIONIC__)
    301   android_fdsan_exchange_owner_tag(fd_, 0, GetFdFileOwnerTag(this));
    302 #endif
    303 
    304   file_path_ = path;
    305   if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) {
    306     // Start in the base state (not flushed, not closed).
    307     guard_state_ = GuardState::kBase;
    308   } else {
    309     // We are not concerned with read-only files. In that case, proper flushing and closing is
    310     // not important.
    311     guard_state_ = GuardState::kNoCheck;
    312   }
    313   return true;
    314 }
    315 
    316 int FdFile::Close() {
    317 #if defined(__BIONIC__)
    318   int result = android_fdsan_close_with_tag(fd_, GetFdFileOwnerTag(this));
    319 #else
    320   int result = close(fd_);
    321 #endif
    322 
    323   // Test here, so the file is closed and not leaked.
    324   if (kCheckSafeUsage) {
    325     DCHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
    326         << " has not been flushed before closing.";
    327     moveUp(GuardState::kClosed, nullptr);
    328   }
    329 
    330 #if defined(__linux__)
    331   // close always succeeds on linux, even if failure is reported.
    332   UNUSED(result);
    333 #else
    334   if (result == -1) {
    335     return -errno;
    336   }
    337 #endif
    338 
    339   fd_ = -1;
    340   file_path_ = "";
    341   return 0;
    342 }
    343 
    344 int FdFile::Flush() {
    345   DCHECK(!read_only_mode_);
    346 
    347 #ifdef __linux__
    348   int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
    349 #else
    350   int rc = TEMP_FAILURE_RETRY(fsync(fd_));
    351 #endif
    352 
    353   moveUp(GuardState::kFlushed, "Flushing closed file.");
    354   if (rc == 0) {
    355     return 0;
    356   }
    357 
    358   // Don't report failure if we just tried to flush a pipe or socket.
    359   return errno == EINVAL ? 0 : -errno;
    360 }
    361 
    362 int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
    363 #ifdef __linux__
    364   int rc = TEMP_FAILURE_RETRY(pread64(fd_, buf, byte_count, offset));
    365 #else
    366   int rc = TEMP_FAILURE_RETRY(pread(fd_, buf, byte_count, offset));
    367 #endif
    368   return (rc == -1) ? -errno : rc;
    369 }
    370 
    371 int FdFile::SetLength(int64_t new_length) {
    372   DCHECK(!read_only_mode_);
    373 #ifdef __linux__
    374   int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length));
    375 #else
    376   int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length));
    377 #endif
    378   moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file.");
    379   return (rc == -1) ? -errno : rc;
    380 }
    381 
    382 int64_t FdFile::GetLength() const {
    383   struct stat s;
    384   int rc = TEMP_FAILURE_RETRY(fstat(fd_, &s));
    385   return (rc == -1) ? -errno : s.st_size;
    386 }
    387 
    388 int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
    389   DCHECK(!read_only_mode_);
    390 #ifdef __linux__
    391   int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset));
    392 #else
    393   int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset));
    394 #endif
    395   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
    396   return (rc == -1) ? -errno : rc;
    397 }
    398 
    399 int FdFile::Fd() const {
    400   return fd_;
    401 }
    402 
    403 bool FdFile::ReadOnlyMode() const {
    404   return read_only_mode_;
    405 }
    406 
    407 bool FdFile::CheckUsage() const {
    408   return guard_state_ != GuardState::kNoCheck;
    409 }
    410 
    411 bool FdFile::IsOpened() const {
    412   return fd_ >= 0;
    413 }
    414 
    415 static ssize_t ReadIgnoreOffset(int fd, void *buf, size_t count, off_t offset) {
    416   DCHECK_EQ(offset, 0);
    417   return read(fd, buf, count);
    418 }
    419 
    420 template <ssize_t (*read_func)(int, void*, size_t, off_t)>
    421 static bool ReadFullyGeneric(int fd, void* buffer, size_t byte_count, size_t offset) {
    422   char* ptr = static_cast<char*>(buffer);
    423   while (byte_count > 0) {
    424     ssize_t bytes_read = TEMP_FAILURE_RETRY(read_func(fd, ptr, byte_count, offset));
    425     if (bytes_read <= 0) {
    426       // 0: end of file
    427       // -1: error
    428       return false;
    429     }
    430     byte_count -= bytes_read;  // Reduce the number of remaining bytes.
    431     ptr += bytes_read;  // Move the buffer forward.
    432     offset += static_cast<size_t>(bytes_read);  // Move the offset forward.
    433   }
    434   return true;
    435 }
    436 
    437 bool FdFile::ReadFully(void* buffer, size_t byte_count) {
    438   return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0);
    439 }
    440 
    441 bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
    442   return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
    443 }
    444 
    445 template <bool kUseOffset>
    446 bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) {
    447   DCHECK(!read_only_mode_);
    448   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
    449   DCHECK(kUseOffset || offset == 0u);
    450   const char* ptr = static_cast<const char*>(buffer);
    451   while (byte_count > 0) {
    452     ssize_t bytes_written = kUseOffset
    453         ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset))
    454         : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
    455     if (bytes_written == -1) {
    456       return false;
    457     }
    458     byte_count -= bytes_written;  // Reduce the number of remaining bytes.
    459     ptr += bytes_written;  // Move the buffer forward.
    460     offset += static_cast<size_t>(bytes_written);
    461   }
    462   return true;
    463 }
    464 
    465 bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) {
    466   return WriteFullyGeneric<true>(buffer, byte_count, offset);
    467 }
    468 
    469 bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
    470   return WriteFullyGeneric<false>(buffer, byte_count, 0u);
    471 }
    472 
    473 bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
    474   DCHECK(!read_only_mode_);
    475   off_t off = static_cast<off_t>(offset);
    476   off_t sz = static_cast<off_t>(size);
    477   if (offset < 0 || static_cast<int64_t>(off) != offset ||
    478       size < 0 || static_cast<int64_t>(sz) != size ||
    479       sz > std::numeric_limits<off_t>::max() - off) {
    480     errno = EINVAL;
    481     return false;
    482   }
    483   if (size == 0) {
    484     return true;
    485   }
    486 #ifdef __linux__
    487   // Use sendfile(), available for files since linux kernel 2.6.33.
    488   off_t end = off + sz;
    489   while (off != end) {
    490     int result = TEMP_FAILURE_RETRY(
    491         sendfile(Fd(), input_file->Fd(), &off, end - off));
    492     if (result == -1) {
    493       return false;
    494     }
    495     // Ignore the number of bytes in `result`, sendfile() already updated `off`.
    496   }
    497 #else
    498   if (lseek(input_file->Fd(), off, SEEK_SET) != off) {
    499     return false;
    500   }
    501   constexpr size_t kMaxBufferSize = 4 * ::art::kPageSize;
    502   const size_t buffer_size = std::min<uint64_t>(size, kMaxBufferSize);
    503   art::UniqueCPtr<void> buffer(malloc(buffer_size));
    504   if (buffer == nullptr) {
    505     errno = ENOMEM;
    506     return false;
    507   }
    508   while (size != 0) {
    509     size_t chunk_size = std::min<uint64_t>(buffer_size, size);
    510     if (!input_file->ReadFully(buffer.get(), chunk_size) ||
    511         !WriteFully(buffer.get(), chunk_size)) {
    512       return false;
    513     }
    514     size -= chunk_size;
    515   }
    516 #endif
    517   return true;
    518 }
    519 
    520 bool FdFile::Unlink() {
    521   if (file_path_.empty()) {
    522     return false;
    523   }
    524 
    525   // Try to figure out whether this file is still referring to the one on disk.
    526   bool is_current = false;
    527   {
    528     struct stat this_stat, current_stat;
    529     int cur_fd = TEMP_FAILURE_RETRY(open(file_path_.c_str(), O_RDONLY | O_CLOEXEC));
    530     if (cur_fd > 0) {
    531       // File still exists.
    532       if (fstat(fd_, &this_stat) == 0 && fstat(cur_fd, &current_stat) == 0) {
    533         is_current = (this_stat.st_dev == current_stat.st_dev) &&
    534                      (this_stat.st_ino == current_stat.st_ino);
    535       }
    536       close(cur_fd);
    537     }
    538   }
    539 
    540   if (is_current) {
    541     unlink(file_path_.c_str());
    542   }
    543 
    544   return is_current;
    545 }
    546 
    547 bool FdFile::Erase(bool unlink) {
    548   DCHECK(!read_only_mode_);
    549 
    550   bool ret_result = true;
    551   if (unlink) {
    552     ret_result = Unlink();
    553   }
    554 
    555   int result;
    556   result = SetLength(0);
    557   result = Flush();
    558   result = Close();
    559   // Ignore the errors.
    560 
    561   return ret_result;
    562 }
    563 
    564 int FdFile::FlushCloseOrErase() {
    565   DCHECK(!read_only_mode_);
    566   int flush_result = Flush();
    567   if (flush_result != 0) {
    568     LOG(ERROR) << "CloseOrErase failed while flushing a file.";
    569     Erase();
    570     return flush_result;
    571   }
    572   int close_result = Close();
    573   if (close_result != 0) {
    574     LOG(ERROR) << "CloseOrErase failed while closing a file.";
    575     Erase();
    576     return close_result;
    577   }
    578   return 0;
    579 }
    580 
    581 int FdFile::FlushClose() {
    582   DCHECK(!read_only_mode_);
    583   int flush_result = Flush();
    584   if (flush_result != 0) {
    585     LOG(ERROR) << "FlushClose failed while flushing a file.";
    586   }
    587   int close_result = Close();
    588   if (close_result != 0) {
    589     LOG(ERROR) << "FlushClose failed while closing a file.";
    590   }
    591   return (flush_result != 0) ? flush_result : close_result;
    592 }
    593 
    594 void FdFile::MarkUnchecked() {
    595   guard_state_ = GuardState::kNoCheck;
    596 }
    597 
    598 bool FdFile::ClearContent() {
    599   DCHECK(!read_only_mode_);
    600   if (SetLength(0) < 0) {
    601     PLOG(ERROR) << "Failed to reset the length";
    602     return false;
    603   }
    604   return ResetOffset();
    605 }
    606 
    607 bool FdFile::ResetOffset() {
    608   DCHECK(!read_only_mode_);
    609   off_t rc =  TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET));
    610   if (rc == static_cast<off_t>(-1)) {
    611     PLOG(ERROR) << "Failed to reset the offset";
    612     return false;
    613   }
    614   return true;
    615 }
    616 
    617 int FdFile::Compare(FdFile* other) {
    618   int64_t length = GetLength();
    619   int64_t length2 = other->GetLength();
    620   if (length != length2) {
    621     return length < length2 ? -1 : 1;
    622   }
    623   static const size_t kBufferSize = 4096;
    624   std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]);
    625   std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]);
    626   size_t offset = 0;
    627   while (length > 0) {
    628     size_t len = std::min(kBufferSize, static_cast<size_t>(length));
    629     if (!PreadFully(&buffer1[0], len, offset)) {
    630       return -1;
    631     }
    632     if (!other->PreadFully(&buffer2[0], len, offset)) {
    633       return 1;
    634     }
    635     int result = memcmp(&buffer1[0], &buffer2[0], len);
    636     if (result != 0) {
    637       return result;
    638     }
    639     length -= len;
    640     offset += len;
    641   }
    642   return 0;
    643 }
    644 
    645 }  // namespace unix_file
    646