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/chromeos/drive/fileapi/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/browser/fileapi/file_system_url.h" 18 #include "webkit/common/fileapi/directory_entry.h" 19 20 using content::BrowserThread; 21 22 namespace drive { 23 namespace fileapi_internal { 24 namespace { 25 26 // The summary of opening mode is: 27 // - File::FLAG_OPEN: Open the existing file. Fail if not exists. 28 // - File::FLAG_CREATE: Create the file if not exists. Fail if exists. 29 // - File::FLAG_OPEN_ALWAYS: Open the existing file. Create a new file 30 // if not exists. 31 // - File::FLAG_CREATE_ALWAYS: Create a new file if not exists. If exists 32 // open it with truncate. 33 // - File::FLAG_OPEN_TRUNCATE: Open the existing file with truncate. 34 // Fail if not exists. 35 OpenMode GetOpenMode(int file_flag) { 36 if (file_flag & (base::File::FLAG_OPEN | base::File::FLAG_OPEN_TRUNCATED)) 37 return OPEN_FILE; 38 39 if (file_flag & base::File::FLAG_CREATE) 40 return CREATE_FILE; 41 42 DCHECK(file_flag & 43 (base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_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 entries. 69 void RunReadDirectoryCallbackWithEntries( 70 const ReadDirectoryCallback& callback, 71 scoped_ptr<ResourceEntryVector> resource_entries) { 72 DCHECK(resource_entries); 73 74 std::vector<fileapi::DirectoryEntry> entries; 75 // Convert drive files to File API's directory entry. 76 entries.reserve(resource_entries->size()); 77 for (size_t i = 0; i < resource_entries->size(); ++i) { 78 const ResourceEntry& resource_entry = (*resource_entries)[i]; 79 fileapi::DirectoryEntry entry; 80 entry.name = resource_entry.base_name(); 81 82 const PlatformFileInfoProto& file_info = resource_entry.file_info(); 83 entry.is_directory = file_info.is_directory(); 84 entry.size = file_info.size(); 85 entry.last_modified_time = 86 base::Time::FromInternalValue(file_info.last_modified()); 87 entries.push_back(entry); 88 } 89 90 callback.Run(base::File::FILE_OK, entries, true /*has_more*/); 91 } 92 93 // Runs |callback| with |error|. 94 void RunReadDirectoryCallbackOnCompletion(const ReadDirectoryCallback& callback, 95 FileError error) { 96 callback.Run(FileErrorToBaseFileError(error), 97 std::vector<fileapi::DirectoryEntry>(), false /*has_more*/); 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 |file|. 147 void RunOpenFileCallback(const OpenFileCallback& callback, 148 const base::Closure& close_callback, 149 base::File file) { 150 callback.Run(file.Pass(), close_callback); 151 } 152 153 base::File OpenFile(const base::FilePath& path, int flags) { 154 return base::File(path, flags); 155 } 156 157 // Part of OpenFile(). Called after FileSystem::OpenFile(). 158 void OpenFileAfterFileSystemOpenFile(int file_flags, 159 const OpenFileCallback& callback, 160 FileError error, 161 const base::FilePath& local_path, 162 const base::Closure& close_callback) { 163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 164 165 if (error != FILE_ERROR_OK) { 166 callback.Run(base::File(FileErrorToBaseFileError(error)), base::Closure()); 167 return; 168 } 169 170 // Here, the file should be at |local_path|, but there may be timing issue. 171 // Because the file is managed by Drive file system, so, in order to avoid 172 // unexpected file creation, CREATE, OPEN_ALWAYS and CREATE_ALWAYS are 173 // translated into OPEN or OPEN_TRUNCATED, here. Keep OPEN and OPEN_TRUNCATED 174 // as is. 175 if (file_flags & (base::File::FLAG_CREATE | 176 base::File::FLAG_OPEN_ALWAYS)) { 177 file_flags &= ~(base::File::FLAG_CREATE | 178 base::File::FLAG_OPEN_ALWAYS); 179 file_flags |= base::File::FLAG_OPEN; 180 } else if (file_flags & base::File::FLAG_CREATE_ALWAYS) { 181 file_flags &= ~base::File::FLAG_CREATE_ALWAYS; 182 file_flags |= base::File::FLAG_OPEN_TRUNCATED; 183 } 184 185 // Cache file prepared for modification is available. Open it locally. 186 bool posted = base::PostTaskAndReplyWithResult( 187 BrowserThread::GetBlockingPool(), FROM_HERE, 188 base::Bind(&OpenFile, local_path, file_flags), 189 base::Bind(&RunOpenFileCallback, callback, close_callback)); 190 DCHECK(posted); 191 } 192 193 } // namespace 194 195 FileSystemInterface* GetFileSystemFromUrl(const fileapi::FileSystemURL& url) { 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 197 198 Profile* profile = util::ExtractProfileFromPath(url.path()); 199 return profile ? util::GetFileSystemByProfile(profile) : NULL; 200 } 201 202 void RunFileSystemCallback( 203 const FileSystemGetter& file_system_getter, 204 const base::Callback<void(FileSystemInterface*)>& callback, 205 const base::Closure& on_error_callback) { 206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 207 FileSystemInterface* file_system = file_system_getter.Run(); 208 209 if (!file_system) { 210 if (!on_error_callback.is_null()) 211 on_error_callback.Run(); 212 return; 213 } 214 215 callback.Run(file_system); 216 } 217 218 void GetFileInfo(const base::FilePath& file_path, 219 const GetFileInfoCallback& callback, 220 FileSystemInterface* file_system) { 221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 222 file_system->GetResourceEntry( 223 file_path, 224 base::Bind(&RunGetFileInfoCallback, callback)); 225 } 226 227 void Copy(const base::FilePath& src_file_path, 228 const base::FilePath& dest_file_path, 229 bool preserve_last_modified, 230 const StatusCallback& callback, 231 FileSystemInterface* file_system) { 232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 233 file_system->Copy(src_file_path, dest_file_path, preserve_last_modified, 234 base::Bind(&RunStatusCallbackByFileError, callback)); 235 } 236 237 void Move(const base::FilePath& src_file_path, 238 const base::FilePath& dest_file_path, 239 const StatusCallback& callback, 240 FileSystemInterface* file_system) { 241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 242 file_system->Move(src_file_path, dest_file_path, 243 base::Bind(&RunStatusCallbackByFileError, callback)); 244 } 245 246 void CopyInForeignFile(const base::FilePath& src_foreign_file_path, 247 const base::FilePath& dest_file_path, 248 const StatusCallback& callback, 249 FileSystemInterface* file_system) { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 251 file_system->TransferFileFromLocalToRemote( 252 src_foreign_file_path, dest_file_path, 253 base::Bind(&RunStatusCallbackByFileError, callback)); 254 } 255 256 void ReadDirectory(const base::FilePath& file_path, 257 const ReadDirectoryCallback& callback, 258 FileSystemInterface* file_system) { 259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 260 file_system->ReadDirectory( 261 file_path, 262 base::Bind(&RunReadDirectoryCallbackWithEntries, callback), 263 base::Bind(&RunReadDirectoryCallbackOnCompletion, callback)); 264 } 265 266 void Remove(const base::FilePath& file_path, 267 bool is_recursive, 268 const StatusCallback& callback, 269 FileSystemInterface* file_system) { 270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 271 file_system->Remove(file_path, is_recursive, 272 base::Bind(&RunStatusCallbackByFileError, callback)); 273 } 274 275 void CreateDirectory(const base::FilePath& file_path, 276 bool is_exclusive, 277 bool is_recursive, 278 const StatusCallback& callback, 279 FileSystemInterface* file_system) { 280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 281 file_system->CreateDirectory( 282 file_path, is_exclusive, is_recursive, 283 base::Bind(&RunStatusCallbackByFileError, callback)); 284 } 285 286 void CreateFile(const base::FilePath& file_path, 287 bool is_exclusive, 288 const StatusCallback& callback, 289 FileSystemInterface* file_system) { 290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 291 file_system->CreateFile(file_path, is_exclusive, 292 std::string(), // no mime type; guess from file_path 293 base::Bind(&RunStatusCallbackByFileError, callback)); 294 } 295 296 void Truncate(const base::FilePath& file_path, 297 int64 length, 298 const StatusCallback& callback, 299 FileSystemInterface* file_system) { 300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 301 file_system->TruncateFile( 302 file_path, length, 303 base::Bind(&RunStatusCallbackByFileError, callback)); 304 } 305 306 void CreateSnapshotFile(const base::FilePath& file_path, 307 const CreateSnapshotFileCallback& callback, 308 FileSystemInterface* file_system) { 309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 310 file_system->GetFile(file_path, 311 base::Bind(&RunCreateSnapshotFileCallback, callback)); 312 } 313 314 void CreateWritableSnapshotFile( 315 const base::FilePath& file_path, 316 const CreateWritableSnapshotFileCallback& callback, 317 FileSystemInterface* file_system) { 318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 319 file_system->OpenFile( 320 file_path, 321 OPEN_FILE, 322 std::string(), // no mime type; we never create a new file here. 323 base::Bind(&RunCreateWritableSnapshotFileCallback, callback)); 324 } 325 326 void OpenFile(const base::FilePath& file_path, 327 int file_flags, 328 const OpenFileCallback& callback, 329 FileSystemInterface* file_system) { 330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 331 332 // Returns an error if any unsupported flag is found. 333 if (file_flags & ~(base::File::FLAG_OPEN | 334 base::File::FLAG_CREATE | 335 base::File::FLAG_OPEN_ALWAYS | 336 base::File::FLAG_CREATE_ALWAYS | 337 base::File::FLAG_OPEN_TRUNCATED | 338 base::File::FLAG_READ | 339 base::File::FLAG_WRITE | 340 base::File::FLAG_WRITE_ATTRIBUTES | 341 base::File::FLAG_APPEND)) { 342 base::MessageLoopProxy::current()->PostTask( 343 FROM_HERE, 344 base::Bind(callback, 345 Passed(base::File(base::File::FILE_ERROR_FAILED)), 346 base::Closure())); 347 return; 348 } 349 350 file_system->OpenFile( 351 file_path, GetOpenMode(file_flags), 352 std::string(), // no mime type; guess from file_path 353 base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback)); 354 } 355 356 void TouchFile(const base::FilePath& file_path, 357 const base::Time& last_access_time, 358 const base::Time& last_modified_time, 359 const StatusCallback& callback, 360 FileSystemInterface* file_system) { 361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 362 file_system->TouchFile(file_path, last_access_time, last_modified_time, 363 base::Bind(&RunStatusCallbackByFileError, callback)); 364 365 } 366 367 } // namespace fileapi_internal 368 } // namespace drive 369