1// Copyright 2013 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/chromeos/drive/file_system/truncate_operation.h"
6
7#include "base/bind.h"
8#include "base/files/file_path.h"
9#include "base/files/scoped_platform_file_closer.h"
10#include "base/logging.h"
11#include "base/message_loop/message_loop_proxy.h"
12#include "base/platform_file.h"
13#include "base/sequenced_task_runner.h"
14#include "base/task_runner_util.h"
15#include "chrome/browser/chromeos/drive/drive.pb.h"
16#include "chrome/browser/chromeos/drive/file_cache.h"
17#include "chrome/browser/chromeos/drive/file_errors.h"
18#include "chrome/browser/chromeos/drive/file_system/download_operation.h"
19#include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
20#include "content/public/browser/browser_thread.h"
21
22using content::BrowserThread;
23
24namespace drive {
25namespace file_system {
26namespace {
27
28// Truncates the local file at |local_cache_path| to the |length| bytes,
29// then marks the resource is dirty on |cache|.
30FileError TruncateOnBlockingPool(internal::ResourceMetadata* metadata,
31                                 internal::FileCache* cache,
32                                 const std::string& local_id,
33                                 const base::FilePath& local_cache_path,
34                                 int64 length) {
35  DCHECK(metadata);
36  DCHECK(cache);
37
38  FileError error = cache->MarkDirty(local_id);
39  if (error != FILE_ERROR_OK)
40    return error;
41
42  base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED;
43  base::PlatformFile file = base::CreatePlatformFile(
44      local_cache_path,
45      base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
46      NULL,
47      &result);
48  if (result != base::PLATFORM_FILE_OK)
49    return FILE_ERROR_FAILED;
50
51  DCHECK_NE(base::kInvalidPlatformFileValue, file);
52  base::ScopedPlatformFileCloser file_closer(&file);
53
54  if (!base::TruncatePlatformFile(file, length))
55    return FILE_ERROR_FAILED;
56
57  return FILE_ERROR_OK;
58}
59
60}  // namespace
61
62TruncateOperation::TruncateOperation(
63    base::SequencedTaskRunner* blocking_task_runner,
64    OperationObserver* observer,
65    JobScheduler* scheduler,
66    internal::ResourceMetadata* metadata,
67    internal::FileCache* cache,
68    const base::FilePath& temporary_file_directory)
69    : blocking_task_runner_(blocking_task_runner),
70      observer_(observer),
71      metadata_(metadata),
72      cache_(cache),
73      download_operation_(new DownloadOperation(blocking_task_runner,
74                                                observer,
75                                                scheduler,
76                                                metadata,
77                                                cache,
78                                                temporary_file_directory)),
79      weak_ptr_factory_(this) {
80}
81
82TruncateOperation::~TruncateOperation() {
83}
84
85void TruncateOperation::Truncate(const base::FilePath& file_path,
86                                 int64 length,
87                                 const FileOperationCallback& callback) {
88  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
89  DCHECK(!callback.is_null());
90
91  if (length < 0) {
92    base::MessageLoopProxy::current()->PostTask(
93        FROM_HERE,
94        base::Bind(callback, FILE_ERROR_INVALID_OPERATION));
95    return;
96  }
97
98  // TODO(kinaba): http://crbug.com/132780.
99  // Optimize the cases for small |length|, at least for |length| == 0.
100  download_operation_->EnsureFileDownloadedByPath(
101      file_path,
102      ClientContext(USER_INITIATED),
103      GetFileContentInitializedCallback(),
104      google_apis::GetContentCallback(),
105      base::Bind(&TruncateOperation::TruncateAfterEnsureFileDownloadedByPath,
106                 weak_ptr_factory_.GetWeakPtr(), length, callback));
107}
108
109void TruncateOperation::TruncateAfterEnsureFileDownloadedByPath(
110    int64 length,
111    const FileOperationCallback& callback,
112    FileError error,
113    const base::FilePath& local_file_path,
114    scoped_ptr<ResourceEntry> entry) {
115  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
116  DCHECK(!callback.is_null());
117
118  if (error != FILE_ERROR_OK) {
119    callback.Run(error);
120    return;
121  }
122  DCHECK(entry);
123  DCHECK(entry->has_file_specific_info());
124
125  if (entry->file_specific_info().is_hosted_document()) {
126    callback.Run(FILE_ERROR_INVALID_OPERATION);
127    return;
128  }
129
130  base::PostTaskAndReplyWithResult(
131      blocking_task_runner_.get(),
132      FROM_HERE,
133      base::Bind(&TruncateOnBlockingPool,
134                 metadata_, cache_, entry->local_id(), local_file_path, length),
135      base::Bind(
136          &TruncateOperation::TruncateAfterTruncateOnBlockingPool,
137          weak_ptr_factory_.GetWeakPtr(), entry->local_id(), callback));
138}
139
140void TruncateOperation::TruncateAfterTruncateOnBlockingPool(
141    const std::string& local_id,
142    const FileOperationCallback& callback,
143    FileError error) {
144  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
145  DCHECK(!callback.is_null());
146
147  observer_->OnCacheFileUploadNeededByOperation(local_id);
148
149  callback.Run(error);
150}
151
152}  // namespace file_system
153}  // namespace drive
154