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 PlatformFileError converted from |error|. 48 void RunStatusCallbackByFileError(const StatusCallback& callback, 49 FileError error) { 50 callback.Run(FileErrorToPlatformError(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(FileErrorToPlatformError(error), base::PlatformFileInfo()); 59 return; 60 } 61 62 DCHECK(entry); 63 base::PlatformFileInfo file_info; 64 ConvertResourceEntryToPlatformFileInfo(*entry, &file_info); 65 callback.Run(base::PLATFORM_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(FileErrorToPlatformError(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::PLATFORM_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 FileErrorToPlatformError(error), 108 base::PlatformFileInfo(), 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::PlatformFileInfo file_info; 122 ConvertResourceEntryToPlatformFileInfo(*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::PLATFORM_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(FileErrorToPlatformError(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::PlatformFileError* 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(FileErrorToPlatformError(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 base::PlatformFileError* result = 186 new base::PlatformFileError(base::PLATFORM_FILE_ERROR_FAILED); 187 bool posted = base::PostTaskAndReplyWithResult( 188 BrowserThread::GetBlockingPool(), FROM_HERE, 189 base::Bind(&base::CreatePlatformFile, 190 local_path, file_flags, static_cast<bool*>(NULL), result), 191 base::Bind(&RunOpenFileCallback, 192 callback, close_callback, base::Owned(result))); 193 DCHECK(posted); 194 } 195 196 } // namespace 197 198 void RunFileSystemCallback( 199 const FileSystemGetter& file_system_getter, 200 const base::Callback<void(FileSystemInterface*)>& callback, 201 const base::Closure& on_error_callback) { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 203 FileSystemInterface* file_system = file_system_getter.Run(); 204 205 if (!file_system) { 206 if (!on_error_callback.is_null()) 207 on_error_callback.Run(); 208 return; 209 } 210 211 callback.Run(file_system); 212 } 213 214 void GetFileInfo(const base::FilePath& file_path, 215 const GetFileInfoCallback& callback, 216 FileSystemInterface* file_system) { 217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 218 file_system->GetResourceEntryByPath( 219 file_path, 220 base::Bind(&RunGetFileInfoCallback, callback)); 221 } 222 223 void Copy(const base::FilePath& src_file_path, 224 const base::FilePath& dest_file_path, 225 const StatusCallback& callback, 226 FileSystemInterface* file_system) { 227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 228 file_system->Copy(src_file_path, dest_file_path, 229 base::Bind(&RunStatusCallbackByFileError, callback)); 230 } 231 232 void Move(const base::FilePath& src_file_path, 233 const base::FilePath& dest_file_path, 234 const StatusCallback& callback, 235 FileSystemInterface* file_system) { 236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 237 file_system->Move(src_file_path, dest_file_path, 238 base::Bind(&RunStatusCallbackByFileError, callback)); 239 } 240 241 void CopyInForeignFile(const base::FilePath& src_foreign_file_path, 242 const base::FilePath& dest_file_path, 243 const StatusCallback& callback, 244 FileSystemInterface* file_system) { 245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 246 file_system->TransferFileFromLocalToRemote( 247 src_foreign_file_path, dest_file_path, 248 base::Bind(&RunStatusCallbackByFileError, callback)); 249 } 250 251 void ReadDirectory(const base::FilePath& file_path, 252 const ReadDirectoryCallback& callback, 253 FileSystemInterface* file_system) { 254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 255 file_system->ReadDirectoryByPath( 256 file_path, 257 base::Bind(&RunReadDirectoryCallback, callback)); 258 } 259 260 void Remove(const base::FilePath& file_path, 261 bool is_recursive, 262 const StatusCallback& callback, 263 FileSystemInterface* file_system) { 264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 265 file_system->Remove(file_path, is_recursive, 266 base::Bind(&RunStatusCallbackByFileError, callback)); 267 } 268 269 void CreateDirectory(const base::FilePath& file_path, 270 bool is_exclusive, 271 bool is_recursive, 272 const StatusCallback& callback, 273 FileSystemInterface* file_system) { 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 275 file_system->CreateDirectory( 276 file_path, is_exclusive, is_recursive, 277 base::Bind(&RunStatusCallbackByFileError, callback)); 278 } 279 280 void CreateFile(const base::FilePath& file_path, 281 bool is_exclusive, 282 const StatusCallback& callback, 283 FileSystemInterface* file_system) { 284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 285 file_system->CreateFile(file_path, is_exclusive, 286 base::Bind(&RunStatusCallbackByFileError, callback)); 287 } 288 289 void Truncate(const base::FilePath& file_path, 290 int64 length, 291 const StatusCallback& callback, 292 FileSystemInterface* file_system) { 293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 294 file_system->TruncateFile( 295 file_path, length, 296 base::Bind(&RunStatusCallbackByFileError, callback)); 297 } 298 299 void CreateSnapshotFile(const base::FilePath& file_path, 300 const CreateSnapshotFileCallback& callback, 301 FileSystemInterface* file_system) { 302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 303 file_system->GetFileByPath( 304 file_path, 305 base::Bind(&RunCreateSnapshotFileCallback, callback)); 306 } 307 308 void CreateWritableSnapshotFile( 309 const base::FilePath& file_path, 310 const CreateWritableSnapshotFileCallback& callback, 311 FileSystemInterface* file_system) { 312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 313 file_system->OpenFile( 314 file_path, 315 OPEN_FILE, 316 base::Bind(&RunCreateWritableSnapshotFileCallback, callback)); 317 } 318 319 void OpenFile(const base::FilePath& file_path, 320 int file_flags, 321 const OpenFileCallback& callback, 322 FileSystemInterface* file_system) { 323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 324 325 // Returns an error if any unsupported flag is found. 326 if (file_flags & ~(base::PLATFORM_FILE_OPEN | 327 base::PLATFORM_FILE_CREATE | 328 base::PLATFORM_FILE_OPEN_ALWAYS | 329 base::PLATFORM_FILE_CREATE_ALWAYS | 330 base::PLATFORM_FILE_OPEN_TRUNCATED | 331 base::PLATFORM_FILE_READ | 332 base::PLATFORM_FILE_WRITE | 333 base::PLATFORM_FILE_WRITE_ATTRIBUTES | 334 base::PLATFORM_FILE_APPEND)) { 335 base::MessageLoopProxy::current()->PostTask( 336 FROM_HERE, 337 base::Bind(callback, 338 base::PLATFORM_FILE_ERROR_FAILED, 339 base::kInvalidPlatformFileValue, 340 base::Closure())); 341 return; 342 } 343 344 file_system->OpenFile( 345 file_path, GetOpenMode(file_flags), 346 base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback)); 347 } 348 349 void TouchFile(const base::FilePath& file_path, 350 const base::Time& last_access_time, 351 const base::Time& last_modified_time, 352 const StatusCallback& callback, 353 FileSystemInterface* file_system) { 354 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 355 file_system->TouchFile(file_path, last_access_time, last_modified_time, 356 base::Bind(&RunStatusCallbackByFileError, callback)); 357 358 } 359 360 } // namespace fileapi_internal 361 } // namespace drive 362