Home | History | Annotate | Download | only in parser
      1 // Copyright 2017 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "core/fpdfapi/parser/cpdf_read_validator.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "core/fpdfapi/cpdf_modulemgr.h"
     10 #include "third_party/base/logging.h"
     11 
     12 namespace {
     13 
     14 constexpr FX_FILESIZE kAlignBlockValue = CPDF_ModuleMgr::kFileBufSize;
     15 
     16 FX_FILESIZE AlignDown(FX_FILESIZE offset) {
     17   return offset > 0 ? (offset - offset % kAlignBlockValue) : 0;
     18 }
     19 
     20 FX_FILESIZE AlignUp(FX_FILESIZE offset) {
     21   FX_SAFE_FILESIZE safe_result = AlignDown(offset);
     22   safe_result += kAlignBlockValue;
     23   if (safe_result.IsValid())
     24     return safe_result.ValueOrDie();
     25   return offset;
     26 }
     27 
     28 }  // namespace
     29 
     30 CPDF_ReadValidator::Session::Session(CPDF_ReadValidator* validator)
     31     : validator_(validator) {
     32   ASSERT(validator_);
     33   saved_read_error_ = validator_->read_error_;
     34   saved_has_unavailable_data_ = validator_->has_unavailable_data_;
     35   validator_->ResetErrors();
     36 }
     37 
     38 CPDF_ReadValidator::Session::~Session() {
     39   validator_->read_error_ |= saved_read_error_;
     40   validator_->has_unavailable_data_ |= saved_has_unavailable_data_;
     41 }
     42 
     43 CPDF_ReadValidator::CPDF_ReadValidator(
     44     const RetainPtr<IFX_SeekableReadStream>& file_read,
     45     CPDF_DataAvail::FileAvail* file_avail)
     46     : file_read_(file_read),
     47       file_avail_(file_avail),
     48       read_error_(false),
     49       has_unavailable_data_(false),
     50       whole_file_already_available_(false),
     51       file_size_(file_read->GetSize()) {}
     52 
     53 CPDF_ReadValidator::~CPDF_ReadValidator() {}
     54 
     55 void CPDF_ReadValidator::ResetErrors() {
     56   read_error_ = false;
     57   has_unavailable_data_ = false;
     58 }
     59 
     60 bool CPDF_ReadValidator::ReadBlock(void* buffer,
     61                                    FX_FILESIZE offset,
     62                                    size_t size) {
     63   FX_SAFE_FILESIZE end_offset = offset;
     64   end_offset += size;
     65   if (!end_offset.IsValid() || end_offset.ValueOrDie() > file_size_)
     66     return false;
     67 
     68   if (!IsDataRangeAvailable(offset, size)) {
     69     ScheduleDownload(offset, size);
     70     return false;
     71   }
     72 
     73   if (file_read_->ReadBlock(buffer, offset, size))
     74     return true;
     75 
     76   read_error_ = true;
     77   ScheduleDownload(offset, size);
     78   return false;
     79 }
     80 
     81 FX_FILESIZE CPDF_ReadValidator::GetSize() {
     82   return file_size_;
     83 }
     84 
     85 void CPDF_ReadValidator::ScheduleDownload(FX_FILESIZE offset, size_t size) {
     86   has_unavailable_data_ = true;
     87   if (!hints_ || size == 0)
     88     return;
     89 
     90   const FX_FILESIZE start_segment_offset = AlignDown(offset);
     91   FX_SAFE_FILESIZE end_segment_offset = offset;
     92   end_segment_offset += size;
     93   if (!end_segment_offset.IsValid()) {
     94     NOTREACHED();
     95     return;
     96   }
     97   end_segment_offset =
     98       std::min(file_size_, AlignUp(end_segment_offset.ValueOrDie()));
     99 
    100   FX_SAFE_SIZE_T segment_size = end_segment_offset;
    101   segment_size -= start_segment_offset;
    102   if (!segment_size.IsValid()) {
    103     NOTREACHED();
    104     return;
    105   }
    106   hints_->AddSegment(start_segment_offset, segment_size.ValueOrDie());
    107 }
    108 
    109 bool CPDF_ReadValidator::IsDataRangeAvailable(FX_FILESIZE offset,
    110                                               size_t size) const {
    111   return whole_file_already_available_ || !file_avail_ ||
    112          file_avail_->IsDataAvail(offset, size);
    113 }
    114 
    115 bool CPDF_ReadValidator::IsWholeFileAvailable() {
    116   const FX_SAFE_SIZE_T safe_size = file_size_;
    117   whole_file_already_available_ =
    118       whole_file_already_available_ ||
    119       (safe_size.IsValid() ? IsDataRangeAvailable(0, safe_size.ValueOrDie())
    120                            : false);
    121 
    122   return whole_file_already_available_;
    123 }
    124 
    125 bool CPDF_ReadValidator::CheckDataRangeAndRequestIfUnavailable(
    126     FX_FILESIZE offset,
    127     size_t size) {
    128   if (offset > file_size_)
    129     return true;
    130 
    131   FX_SAFE_FILESIZE end_segment_offset = offset;
    132   end_segment_offset += size;
    133   // Increase checked range to allow CPDF_SyntaxParser read whole buffer.
    134   end_segment_offset += CPDF_ModuleMgr::kFileBufSize;
    135   if (!end_segment_offset.IsValid()) {
    136     NOTREACHED();
    137     return false;
    138   }
    139   end_segment_offset = std::min(
    140       file_size_, static_cast<FX_FILESIZE>(end_segment_offset.ValueOrDie()));
    141   FX_SAFE_SIZE_T segment_size = end_segment_offset;
    142   segment_size -= offset;
    143   if (!segment_size.IsValid()) {
    144     NOTREACHED();
    145     return false;
    146   }
    147 
    148   if (IsDataRangeAvailable(offset, segment_size.ValueOrDie()))
    149     return true;
    150 
    151   ScheduleDownload(offset, segment_size.ValueOrDie());
    152   return false;
    153 }
    154 
    155 bool CPDF_ReadValidator::CheckWholeFileAndRequestIfUnavailable() {
    156   if (IsWholeFileAvailable())
    157     return true;
    158 
    159   const FX_SAFE_SIZE_T safe_size = file_size_;
    160   if (safe_size.IsValid())
    161     ScheduleDownload(0, safe_size.ValueOrDie());
    162 
    163   return false;
    164 }
    165