| OLD | NEW |
| (Empty) |
| 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/fileapi_worker.h" | |
| 6 | |
| 7 #include "base/files/file_path.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "base/task_runner_util.h" | |
| 10 #include "base/threading/sequenced_worker_pool.h" | |
| 11 #include "chrome/browser/chromeos/drive/drive.pb.h" | |
| 12 #include "chrome/browser/chromeos/drive/file_errors.h" | |
| 13 #include "chrome/browser/chromeos/drive/file_system_interface.h" | |
| 14 #include "chrome/browser/chromeos/drive/file_system_util.h" | |
| 15 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h" | |
| 16 #include "content/public/browser/browser_thread.h" | |
| 17 #include "webkit/common/fileapi/directory_entry.h" | |
| 18 | |
| 19 using content::BrowserThread; | |
| 20 | |
| 21 namespace drive { | |
| 22 namespace fileapi_internal { | |
| 23 namespace { | |
| 24 | |
| 25 // The summary of opening mode is: | |
| 26 // - PLATFORM_FILE_OPEN: Open the existing file. Fail if not exists. | |
| 27 // - PLATFORM_FILE_CREATE: Create the file if not exists. Fail if exists. | |
| 28 // - PLATFORM_FILE_OPEN_ALWAYS: Open the existing file. Create a new file | |
| 29 // if not exists. | |
| 30 // - PLATFORM_FILE_CREATE_ALWAYS: Create a new file if not exists. If exists | |
| 31 // open it with truncate. | |
| 32 // - PLATFORM_FILE_OPEN_TRUNCATE: Open the existing file with truncate. | |
| 33 // Fail if not exists. | |
| 34 OpenMode GetOpenMode(int file_flag) { | |
| 35 if (file_flag & (base::PLATFORM_FILE_OPEN | | |
| 36 base::PLATFORM_FILE_OPEN_TRUNCATED)) | |
| 37 return OPEN_FILE; | |
| 38 | |
| 39 if (file_flag & base::PLATFORM_FILE_CREATE) | |
| 40 return CREATE_FILE; | |
| 41 | |
| 42 DCHECK(file_flag & (base::PLATFORM_FILE_OPEN_ALWAYS | | |
| 43 base::PLATFORM_FILE_CREATE_ALWAYS)); | |
| 44 return OPEN_OR_CREATE_FILE; | |
| 45 } | |
| 46 | |
| 47 // Runs |callback| with the File::Error converted from |error|. | |
| 48 void RunStatusCallbackByFileError(const StatusCallback& callback, | |
| 49 FileError error) { | |
| 50 callback.Run(FileErrorToBaseFileError(error)); | |
| 51 } | |
| 52 | |
| 53 // Runs |callback| with arguments converted from |error| and |entry|. | |
| 54 void RunGetFileInfoCallback(const GetFileInfoCallback& callback, | |
| 55 FileError error, | |
| 56 scoped_ptr<ResourceEntry> entry) { | |
| 57 if (error != FILE_ERROR_OK) { | |
| 58 callback.Run(FileErrorToBaseFileError(error), base::File::Info()); | |
| 59 return; | |
| 60 } | |
| 61 | |
| 62 DCHECK(entry); | |
| 63 base::File::Info file_info; | |
| 64 ConvertResourceEntryToFileInfo(*entry, &file_info); | |
| 65 callback.Run(base::File::FILE_OK, file_info); | |
| 66 } | |
| 67 | |
| 68 // Runs |callback| with arguments converted from |error| and |resource_entries|. | |
| 69 void RunReadDirectoryCallback( | |
| 70 const ReadDirectoryCallback& callback, | |
| 71 FileError error, | |
| 72 scoped_ptr<ResourceEntryVector> resource_entries) { | |
| 73 if (error != FILE_ERROR_OK) { | |
| 74 callback.Run(FileErrorToBaseFileError(error), | |
| 75 std::vector<fileapi::DirectoryEntry>(), false); | |
| 76 return; | |
| 77 } | |
| 78 | |
| 79 DCHECK(resource_entries); | |
| 80 | |
| 81 std::vector<fileapi::DirectoryEntry> entries; | |
| 82 // Convert drive files to File API's directory entry. | |
| 83 entries.reserve(resource_entries->size()); | |
| 84 for (size_t i = 0; i < resource_entries->size(); ++i) { | |
| 85 const ResourceEntry& resource_entry = (*resource_entries)[i]; | |
| 86 fileapi::DirectoryEntry entry; | |
| 87 entry.name = resource_entry.base_name(); | |
| 88 | |
| 89 const PlatformFileInfoProto& file_info = resource_entry.file_info(); | |
| 90 entry.is_directory = file_info.is_directory(); | |
| 91 entry.size = file_info.size(); | |
| 92 entry.last_modified_time = | |
| 93 base::Time::FromInternalValue(file_info.last_modified()); | |
| 94 entries.push_back(entry); | |
| 95 } | |
| 96 | |
| 97 callback.Run(base::File::FILE_OK, entries, false); | |
| 98 } | |
| 99 | |
| 100 // Runs |callback| with arguments based on |error|, |local_path| and |entry|. | |
| 101 void RunCreateSnapshotFileCallback(const CreateSnapshotFileCallback& callback, | |
| 102 FileError error, | |
| 103 const base::FilePath& local_path, | |
| 104 scoped_ptr<ResourceEntry> entry) { | |
| 105 if (error != FILE_ERROR_OK) { | |
| 106 callback.Run( | |
| 107 FileErrorToBaseFileError(error), | |
| 108 base::File::Info(), base::FilePath(), | |
| 109 webkit_blob::ScopedFile::ScopeOutPolicy()); | |
| 110 return; | |
| 111 } | |
| 112 | |
| 113 DCHECK(entry); | |
| 114 | |
| 115 // When reading file, last modified time specified in file info will be | |
| 116 // compared to the last modified time of the local version of the drive file. | |
| 117 // Since those two values don't generally match (last modification time on the | |
| 118 // drive server vs. last modification time of the local, downloaded file), so | |
| 119 // we have to opt out from this check. We do this by unsetting last_modified | |
| 120 // value in the file info passed to the CreateSnapshot caller. | |
| 121 base::File::Info file_info; | |
| 122 ConvertResourceEntryToFileInfo(*entry, &file_info); | |
| 123 file_info.last_modified = base::Time(); | |
| 124 | |
| 125 // If the file is a hosted document, a temporary JSON file is created to | |
| 126 // represent the document. The JSON file is not cached and its lifetime | |
| 127 // is managed by ShareableFileReference. | |
| 128 webkit_blob::ScopedFile::ScopeOutPolicy scope_out_policy = | |
| 129 entry->file_specific_info().is_hosted_document() ? | |
| 130 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT : | |
| 131 webkit_blob::ScopedFile::DONT_DELETE_ON_SCOPE_OUT; | |
| 132 | |
| 133 callback.Run(base::File::FILE_OK, file_info, local_path, scope_out_policy); | |
| 134 } | |
| 135 | |
| 136 // Runs |callback| with arguments converted from |error| and |local_path|. | |
| 137 void RunCreateWritableSnapshotFileCallback( | |
| 138 const CreateWritableSnapshotFileCallback& callback, | |
| 139 FileError error, | |
| 140 const base::FilePath& local_path, | |
| 141 const base::Closure& close_callback) { | |
| 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 143 callback.Run(FileErrorToBaseFileError(error), local_path, close_callback); | |
| 144 } | |
| 145 | |
| 146 // Runs |callback| with |error| and |platform_file|. | |
| 147 void RunOpenFileCallback(const OpenFileCallback& callback, | |
| 148 const base::Closure& close_callback, | |
| 149 base::File::Error* error, | |
| 150 base::PlatformFile platform_file) { | |
| 151 callback.Run(*error, platform_file, close_callback); | |
| 152 } | |
| 153 | |
| 154 // Part of OpenFile(). Called after FileSystem::OpenFile(). | |
| 155 void OpenFileAfterFileSystemOpenFile(int file_flags, | |
| 156 const OpenFileCallback& callback, | |
| 157 FileError error, | |
| 158 const base::FilePath& local_path, | |
| 159 const base::Closure& close_callback) { | |
| 160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 161 | |
| 162 if (error != FILE_ERROR_OK) { | |
| 163 callback.Run(FileErrorToBaseFileError(error), | |
| 164 base::kInvalidPlatformFileValue, | |
| 165 base::Closure()); | |
| 166 return; | |
| 167 } | |
| 168 | |
| 169 // Here, the file should be at |local_path|, but there may be timing issue. | |
| 170 // Because the file is managed by Drive file system, so, in order to avoid | |
| 171 // unexpected file creation, CREATE, OPEN_ALWAYS and CREATE_ALWAYS are | |
| 172 // translated into OPEN or OPEN_TRUNCATED, here. Keep OPEN and OPEN_TRUNCATED | |
| 173 // as is. | |
| 174 if (file_flags & (base::PLATFORM_FILE_CREATE | | |
| 175 base::PLATFORM_FILE_OPEN_ALWAYS)) { | |
| 176 file_flags &= ~(base::PLATFORM_FILE_CREATE | | |
| 177 base::PLATFORM_FILE_OPEN_ALWAYS); | |
| 178 file_flags |= base::PLATFORM_FILE_OPEN; | |
| 179 } else if (file_flags & base::PLATFORM_FILE_CREATE_ALWAYS) { | |
| 180 file_flags &= ~base::PLATFORM_FILE_CREATE_ALWAYS; | |
| 181 file_flags |= base::PLATFORM_FILE_OPEN_TRUNCATED; | |
| 182 } | |
| 183 | |
| 184 // Cache file prepared for modification is available. Open it locally. | |
| 185 // TODO(rvargas): Convert this to base::File. | |
| 186 base::File::Error* result = | |
| 187 new base::File::Error(base::File::FILE_ERROR_FAILED); | |
| 188 bool posted = base::PostTaskAndReplyWithResult( | |
| 189 BrowserThread::GetBlockingPool(), FROM_HERE, | |
| 190 base::Bind(&base::CreatePlatformFile, | |
| 191 local_path, file_flags, static_cast<bool*>(NULL), | |
| 192 reinterpret_cast<base::PlatformFileError*>(result)), | |
| 193 base::Bind(&RunOpenFileCallback, | |
| 194 callback, close_callback, base::Owned(result))); | |
| 195 DCHECK(posted); | |
| 196 } | |
| 197 | |
| 198 } // namespace | |
| 199 | |
| 200 void RunFileSystemCallback( | |
| 201 const FileSystemGetter& file_system_getter, | |
| 202 const base::Callback<void(FileSystemInterface*)>& callback, | |
| 203 const base::Closure& on_error_callback) { | |
| 204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 205 FileSystemInterface* file_system = file_system_getter.Run(); | |
| 206 | |
| 207 if (!file_system) { | |
| 208 if (!on_error_callback.is_null()) | |
| 209 on_error_callback.Run(); | |
| 210 return; | |
| 211 } | |
| 212 | |
| 213 callback.Run(file_system); | |
| 214 } | |
| 215 | |
| 216 void GetFileInfo(const base::FilePath& file_path, | |
| 217 const GetFileInfoCallback& callback, | |
| 218 FileSystemInterface* file_system) { | |
| 219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 220 file_system->GetResourceEntry( | |
| 221 file_path, | |
| 222 base::Bind(&RunGetFileInfoCallback, callback)); | |
| 223 } | |
| 224 | |
| 225 void Copy(const base::FilePath& src_file_path, | |
| 226 const base::FilePath& dest_file_path, | |
| 227 bool preserve_last_modified, | |
| 228 const StatusCallback& callback, | |
| 229 FileSystemInterface* file_system) { | |
| 230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 231 file_system->Copy(src_file_path, dest_file_path, preserve_last_modified, | |
| 232 base::Bind(&RunStatusCallbackByFileError, callback)); | |
| 233 } | |
| 234 | |
| 235 void Move(const base::FilePath& src_file_path, | |
| 236 const base::FilePath& dest_file_path, | |
| 237 bool preserve_last_modified, | |
| 238 const StatusCallback& callback, | |
| 239 FileSystemInterface* file_system) { | |
| 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 241 file_system->Move(src_file_path, dest_file_path, preserve_last_modified, | |
| 242 base::Bind(&RunStatusCallbackByFileError, callback)); | |
| 243 } | |
| 244 | |
| 245 void CopyInForeignFile(const base::FilePath& src_foreign_file_path, | |
| 246 const base::FilePath& dest_file_path, | |
| 247 const StatusCallback& callback, | |
| 248 FileSystemInterface* file_system) { | |
| 249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 250 file_system->TransferFileFromLocalToRemote( | |
| 251 src_foreign_file_path, dest_file_path, | |
| 252 base::Bind(&RunStatusCallbackByFileError, callback)); | |
| 253 } | |
| 254 | |
| 255 void ReadDirectory(const base::FilePath& file_path, | |
| 256 const ReadDirectoryCallback& callback, | |
| 257 FileSystemInterface* file_system) { | |
| 258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 259 file_system->ReadDirectory(file_path, | |
| 260 base::Bind(&RunReadDirectoryCallback, callback)); | |
| 261 } | |
| 262 | |
| 263 void Remove(const base::FilePath& file_path, | |
| 264 bool is_recursive, | |
| 265 const StatusCallback& callback, | |
| 266 FileSystemInterface* file_system) { | |
| 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 268 file_system->Remove(file_path, is_recursive, | |
| 269 base::Bind(&RunStatusCallbackByFileError, callback)); | |
| 270 } | |
| 271 | |
| 272 void CreateDirectory(const base::FilePath& file_path, | |
| 273 bool is_exclusive, | |
| 274 bool is_recursive, | |
| 275 const StatusCallback& callback, | |
| 276 FileSystemInterface* file_system) { | |
| 277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 278 file_system->CreateDirectory( | |
| 279 file_path, is_exclusive, is_recursive, | |
| 280 base::Bind(&RunStatusCallbackByFileError, callback)); | |
| 281 } | |
| 282 | |
| 283 void CreateFile(const base::FilePath& file_path, | |
| 284 bool is_exclusive, | |
| 285 const StatusCallback& callback, | |
| 286 FileSystemInterface* file_system) { | |
| 287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 288 file_system->CreateFile(file_path, is_exclusive, | |
| 289 std::string(), // no mime type; guess from file_path | |
| 290 base::Bind(&RunStatusCallbackByFileError, callback)); | |
| 291 } | |
| 292 | |
| 293 void Truncate(const base::FilePath& file_path, | |
| 294 int64 length, | |
| 295 const StatusCallback& callback, | |
| 296 FileSystemInterface* file_system) { | |
| 297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 298 file_system->TruncateFile( | |
| 299 file_path, length, | |
| 300 base::Bind(&RunStatusCallbackByFileError, callback)); | |
| 301 } | |
| 302 | |
| 303 void CreateSnapshotFile(const base::FilePath& file_path, | |
| 304 const CreateSnapshotFileCallback& callback, | |
| 305 FileSystemInterface* file_system) { | |
| 306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 307 file_system->GetFile(file_path, | |
| 308 base::Bind(&RunCreateSnapshotFileCallback, callback)); | |
| 309 } | |
| 310 | |
| 311 void CreateWritableSnapshotFile( | |
| 312 const base::FilePath& file_path, | |
| 313 const CreateWritableSnapshotFileCallback& callback, | |
| 314 FileSystemInterface* file_system) { | |
| 315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 316 file_system->OpenFile( | |
| 317 file_path, | |
| 318 OPEN_FILE, | |
| 319 std::string(), // no mime type; we never create a new file here. | |
| 320 base::Bind(&RunCreateWritableSnapshotFileCallback, callback)); | |
| 321 } | |
| 322 | |
| 323 void OpenFile(const base::FilePath& file_path, | |
| 324 int file_flags, | |
| 325 const OpenFileCallback& callback, | |
| 326 FileSystemInterface* file_system) { | |
| 327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 328 | |
| 329 // Returns an error if any unsupported flag is found. | |
| 330 if (file_flags & ~(base::PLATFORM_FILE_OPEN | | |
| 331 base::PLATFORM_FILE_CREATE | | |
| 332 base::PLATFORM_FILE_OPEN_ALWAYS | | |
| 333 base::PLATFORM_FILE_CREATE_ALWAYS | | |
| 334 base::PLATFORM_FILE_OPEN_TRUNCATED | | |
| 335 base::PLATFORM_FILE_READ | | |
| 336 base::PLATFORM_FILE_WRITE | | |
| 337 base::PLATFORM_FILE_WRITE_ATTRIBUTES | | |
| 338 base::PLATFORM_FILE_APPEND)) { | |
| 339 base::MessageLoopProxy::current()->PostTask( | |
| 340 FROM_HERE, | |
| 341 base::Bind(callback, | |
| 342 base::File::FILE_ERROR_FAILED, | |
| 343 base::kInvalidPlatformFileValue, | |
| 344 base::Closure())); | |
| 345 return; | |
| 346 } | |
| 347 | |
| 348 file_system->OpenFile( | |
| 349 file_path, GetOpenMode(file_flags), | |
| 350 std::string(), // no mime type; guess from file_path | |
| 351 base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback)); | |
| 352 } | |
| 353 | |
| 354 void TouchFile(const base::FilePath& file_path, | |
| 355 const base::Time& last_access_time, | |
| 356 const base::Time& last_modified_time, | |
| 357 const StatusCallback& callback, | |
| 358 FileSystemInterface* file_system) { | |
| 359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 360 file_system->TouchFile(file_path, last_access_time, last_modified_time, | |
| 361 base::Bind(&RunStatusCallbackByFileError, callback)); | |
| 362 | |
| 363 } | |
| 364 | |
| 365 } // namespace fileapi_internal | |
| 366 } // namespace drive | |
| OLD | NEW |