Home | History | Annotate | Download | only in fileapi
      1 // Copyright 2014 The Chromium 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 "chrome/browser/media_galleries/fileapi/mtp_file_stream_reader.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/numerics/safe_conversions.h"
     10 #include "chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h"
     11 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
     12 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h"
     13 #include "content/public/browser/browser_thread.h"
     14 #include "net/base/io_buffer.h"
     15 #include "net/base/mime_sniffer.h"
     16 #include "net/base/net_errors.h"
     17 #include "storage/browser/fileapi/file_system_context.h"
     18 
     19 using storage::FileStreamReader;
     20 
     21 namespace {
     22 
     23 // Called on the IO thread.
     24 MTPDeviceAsyncDelegate* GetMTPDeviceDelegate(
     25     const storage::FileSystemURL& url) {
     26   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
     27   return MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(
     28       url.filesystem_id());
     29 }
     30 
     31 void CallCompletionCallbackWithPlatformFileError(
     32     const net::CompletionCallback& callback,
     33     base::File::Error file_error) {
     34   callback.Run(net::FileErrorToNetError(file_error));
     35 }
     36 
     37 void CallInt64CompletionCallbackWithPlatformFileError(
     38     const net::Int64CompletionCallback& callback,
     39     base::File::Error file_error) {
     40   callback.Run(net::FileErrorToNetError(file_error));
     41 }
     42 
     43 void ReadBytes(
     44     const storage::FileSystemURL& url,
     45     const scoped_refptr<net::IOBuffer>& buf,
     46     int64 offset,
     47     int buf_len,
     48     const MTPDeviceAsyncDelegate::ReadBytesSuccessCallback& success_callback,
     49     const net::CompletionCallback& error_callback) {
     50   MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
     51   if (!delegate) {
     52     error_callback.Run(net::ERR_FAILED);
     53     return;
     54   }
     55 
     56   delegate->ReadBytes(
     57       url.path(),
     58       buf,
     59       offset,
     60       buf_len,
     61       success_callback,
     62       base::Bind(&CallCompletionCallbackWithPlatformFileError, error_callback));
     63 }
     64 
     65 }  // namespace
     66 
     67 MTPFileStreamReader::MTPFileStreamReader(
     68     storage::FileSystemContext* file_system_context,
     69     const storage::FileSystemURL& url,
     70     int64 initial_offset,
     71     const base::Time& expected_modification_time,
     72     bool do_media_header_validation)
     73     : file_system_context_(file_system_context),
     74       url_(url),
     75       current_offset_(initial_offset),
     76       expected_modification_time_(expected_modification_time),
     77       media_header_validated_(!do_media_header_validation),
     78       weak_factory_(this) {
     79 }
     80 
     81 MTPFileStreamReader::~MTPFileStreamReader() {
     82 }
     83 
     84 int MTPFileStreamReader::Read(net::IOBuffer* buf, int buf_len,
     85                               const net::CompletionCallback& callback) {
     86   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
     87 
     88   MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url_);
     89   if (!delegate)
     90     return net::ERR_FAILED;
     91 
     92   if (!media_header_validated_) {
     93     scoped_refptr<net::IOBuffer> header_buf;
     94     int header_buf_len = 0;
     95 
     96     if (current_offset_ == 0 && buf_len >= net::kMaxBytesToSniff) {
     97       // If requested read includes all the header bytes, we read directly to
     98       // the original buffer, and validate the header bytes within that.
     99       header_buf = buf;
    100       header_buf_len = buf_len;
    101     } else {
    102       // Otherwise, make a special request for the header.
    103       header_buf = new net::IOBuffer(net::kMaxBytesToSniff);
    104       header_buf_len = net::kMaxBytesToSniff;
    105     }
    106 
    107     ReadBytes(url_,
    108               header_buf.get(),
    109               0,
    110               header_buf_len,
    111               base::Bind(&MTPFileStreamReader::FinishValidateMediaHeader,
    112                          weak_factory_.GetWeakPtr(),
    113                          header_buf,
    114                          make_scoped_refptr(buf),
    115                          buf_len,
    116                          callback),
    117               callback);
    118     return net::ERR_IO_PENDING;
    119   }
    120 
    121   ReadBytes(url_, buf, current_offset_, buf_len,
    122             base::Bind(&MTPFileStreamReader::FinishRead,
    123                        weak_factory_.GetWeakPtr(), callback),
    124             callback);
    125 
    126   return net::ERR_IO_PENDING;
    127 }
    128 
    129 int64 MTPFileStreamReader::GetLength(
    130     const net::Int64CompletionCallback& callback) {
    131   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    132 
    133   MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url_);
    134   if (!delegate)
    135     return net::ERR_FAILED;
    136 
    137   delegate->GetFileInfo(
    138         url_.path(),
    139         base::Bind(&MTPFileStreamReader::FinishGetLength,
    140                    weak_factory_.GetWeakPtr(), callback),
    141         base::Bind(&CallInt64CompletionCallbackWithPlatformFileError,
    142                    callback));
    143 
    144   return net::ERR_IO_PENDING;
    145 }
    146 
    147 void MTPFileStreamReader::FinishValidateMediaHeader(
    148     net::IOBuffer* header_buf,
    149     net::IOBuffer* buf, int buf_len,
    150     const net::CompletionCallback& callback,
    151     const base::File::Info& file_info,
    152     int header_bytes_read) {
    153   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    154   DCHECK_GE(header_bytes_read, 0);
    155   base::File::Error error = NativeMediaFileUtil::BufferIsMediaHeader(
    156       header_buf, header_bytes_read);
    157   if (error != base::File::FILE_OK) {
    158     CallCompletionCallbackWithPlatformFileError(callback, error);
    159     return;
    160   }
    161 
    162   media_header_validated_ = true;
    163 
    164   // Finish the read immediately if we've already finished reading into the
    165   // originally requested buffer.
    166   if (header_buf == buf)
    167     return FinishRead(callback, file_info, header_bytes_read);
    168 
    169   // Header buffer isn't the same as the original read buffer. Make a separate
    170   // request for that.
    171   ReadBytes(url_, buf, current_offset_, buf_len,
    172             base::Bind(&MTPFileStreamReader::FinishRead,
    173                        weak_factory_.GetWeakPtr(), callback),
    174             callback);
    175 }
    176 
    177 void MTPFileStreamReader::FinishRead(const net::CompletionCallback& callback,
    178                                      const base::File::Info& file_info,
    179                                      int bytes_read) {
    180   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    181 
    182   if (!VerifySnapshotTime(expected_modification_time_, file_info)) {
    183     callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
    184     return;
    185   }
    186 
    187   DCHECK_GE(bytes_read, 0);
    188   current_offset_ += bytes_read;
    189   callback.Run(bytes_read);
    190 }
    191 
    192 void MTPFileStreamReader::FinishGetLength(
    193     const net::Int64CompletionCallback& callback,
    194     const base::File::Info& file_info) {
    195   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    196 
    197   if (!VerifySnapshotTime(expected_modification_time_, file_info)) {
    198     callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
    199     return;
    200   }
    201 
    202   callback.Run(file_info.size);
    203 }
    204