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