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/fileapi/file_system_backend.h" 6 7 #include "base/logging.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/strings/stringprintf.h" 10 #include "chrome/browser/chromeos/fileapi/file_access_permissions.h" 11 #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h" 12 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" 13 #include "chrome/common/url_constants.h" 14 #include "chromeos/dbus/cros_disks_client.h" 15 #include "storage/browser/blob/file_stream_reader.h" 16 #include "storage/browser/fileapi/async_file_util.h" 17 #include "storage/browser/fileapi/external_mount_points.h" 18 #include "storage/browser/fileapi/file_stream_writer.h" 19 #include "storage/browser/fileapi/file_system_context.h" 20 #include "storage/browser/fileapi/file_system_operation.h" 21 #include "storage/browser/fileapi/file_system_operation_context.h" 22 #include "storage/browser/fileapi/file_system_url.h" 23 24 namespace chromeos { 25 26 // static 27 bool FileSystemBackend::CanHandleURL(const storage::FileSystemURL& url) { 28 if (!url.is_valid()) 29 return false; 30 return url.type() == storage::kFileSystemTypeNativeLocal || 31 url.type() == storage::kFileSystemTypeRestrictedNativeLocal || 32 url.type() == storage::kFileSystemTypeDrive || 33 url.type() == storage::kFileSystemTypeProvided || 34 url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage; 35 } 36 37 FileSystemBackend::FileSystemBackend( 38 FileSystemBackendDelegate* drive_delegate, 39 FileSystemBackendDelegate* file_system_provider_delegate, 40 FileSystemBackendDelegate* mtp_delegate, 41 scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy, 42 scoped_refptr<storage::ExternalMountPoints> mount_points, 43 storage::ExternalMountPoints* system_mount_points) 44 : special_storage_policy_(special_storage_policy), 45 file_access_permissions_(new FileAccessPermissions()), 46 local_file_util_(storage::AsyncFileUtil::CreateForLocalFileSystem()), 47 drive_delegate_(drive_delegate), 48 file_system_provider_delegate_(file_system_provider_delegate), 49 mtp_delegate_(mtp_delegate), 50 mount_points_(mount_points), 51 system_mount_points_(system_mount_points) { 52 } 53 54 FileSystemBackend::~FileSystemBackend() { 55 } 56 57 void FileSystemBackend::AddSystemMountPoints() { 58 // RegisterFileSystem() is no-op if the mount point with the same name 59 // already exists, hence it's safe to call without checking if a mount 60 // point already exists or not. 61 system_mount_points_->RegisterFileSystem( 62 "archive", 63 storage::kFileSystemTypeNativeLocal, 64 storage::FileSystemMountOption(), 65 chromeos::CrosDisksClient::GetArchiveMountPoint()); 66 system_mount_points_->RegisterFileSystem( 67 "removable", 68 storage::kFileSystemTypeNativeLocal, 69 storage::FileSystemMountOption(storage::COPY_SYNC_OPTION_SYNC), 70 chromeos::CrosDisksClient::GetRemovableDiskMountPoint()); 71 system_mount_points_->RegisterFileSystem( 72 "oem", 73 storage::kFileSystemTypeRestrictedNativeLocal, 74 storage::FileSystemMountOption(), 75 base::FilePath(FILE_PATH_LITERAL("/usr/share/oem"))); 76 } 77 78 bool FileSystemBackend::CanHandleType(storage::FileSystemType type) const { 79 switch (type) { 80 case storage::kFileSystemTypeExternal: 81 case storage::kFileSystemTypeDrive: 82 case storage::kFileSystemTypeRestrictedNativeLocal: 83 case storage::kFileSystemTypeNativeLocal: 84 case storage::kFileSystemTypeNativeForPlatformApp: 85 case storage::kFileSystemTypeDeviceMediaAsFileStorage: 86 case storage::kFileSystemTypeProvided: 87 return true; 88 default: 89 return false; 90 } 91 } 92 93 void FileSystemBackend::Initialize(storage::FileSystemContext* context) { 94 } 95 96 void FileSystemBackend::ResolveURL(const storage::FileSystemURL& url, 97 storage::OpenFileSystemMode mode, 98 const OpenFileSystemCallback& callback) { 99 std::string id; 100 storage::FileSystemType type; 101 std::string cracked_id; 102 base::FilePath path; 103 storage::FileSystemMountOption option; 104 if (!mount_points_->CrackVirtualPath( 105 url.virtual_path(), &id, &type, &cracked_id, &path, &option) && 106 !system_mount_points_->CrackVirtualPath( 107 url.virtual_path(), &id, &type, &cracked_id, &path, &option)) { 108 // Not under a mount point, so return an error, since the root is not 109 // accessible. 110 GURL root_url = GURL(storage::GetExternalFileSystemRootURIString( 111 url.origin(), std::string())); 112 callback.Run(root_url, std::string(), base::File::FILE_ERROR_SECURITY); 113 return; 114 } 115 116 std::string name; 117 // Construct a URL restricted to the found mount point. 118 std::string root_url = 119 storage::GetExternalFileSystemRootURIString(url.origin(), id); 120 121 // For removable and archives, the file system root is the external mount 122 // point plus the inner mount point. 123 if (id == "archive" || id == "removable") { 124 std::vector<std::string> components; 125 url.virtual_path().GetComponents(&components); 126 DCHECK_EQ(id, components.at(0)); 127 if (components.size() < 2) { 128 // Unable to access /archive and /removable directories directly. The 129 // inner mount name must be specified. 130 callback.Run( 131 GURL(root_url), std::string(), base::File::FILE_ERROR_SECURITY); 132 return; 133 } 134 std::string inner_mount_name = components[1]; 135 root_url += inner_mount_name + "/"; 136 name = inner_mount_name; 137 } else { 138 name = id; 139 } 140 141 callback.Run(GURL(root_url), name, base::File::FILE_OK); 142 } 143 144 storage::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() { 145 // No quota support. 146 return NULL; 147 } 148 149 const storage::UpdateObserverList* FileSystemBackend::GetUpdateObservers( 150 storage::FileSystemType type) const { 151 return NULL; 152 } 153 154 const storage::ChangeObserverList* FileSystemBackend::GetChangeObservers( 155 storage::FileSystemType type) const { 156 return NULL; 157 } 158 159 const storage::AccessObserverList* FileSystemBackend::GetAccessObservers( 160 storage::FileSystemType type) const { 161 return NULL; 162 } 163 164 bool FileSystemBackend::IsAccessAllowed( 165 const storage::FileSystemURL& url) const { 166 if (!url.is_valid()) 167 return false; 168 169 // No extra check is needed for isolated file systems. 170 if (url.mount_type() == storage::kFileSystemTypeIsolated) 171 return true; 172 173 if (!CanHandleURL(url)) 174 return false; 175 176 std::string extension_id = url.origin().host(); 177 // TODO(mtomasz): Temporarily whitelist TimeScapes. Remove this in M-31. 178 // See: crbug.com/271946 179 if (extension_id == "mlbmkoenclnokonejhlfakkeabdlmpek" && 180 url.type() == storage::kFileSystemTypeRestrictedNativeLocal) { 181 return true; 182 } 183 184 // Grant access for URL having "externalfile:" scheme. The URL 185 // filesystem:externalfile:/xxx cannot be parsed directly. The URL is created 186 // only by DriveURLRequestJob. 187 if (url.origin().scheme() == chrome::kExternalFileScheme) 188 return true; 189 190 // Check first to make sure this extension has fileBrowserHander permissions. 191 if (!special_storage_policy_.get() || 192 !special_storage_policy_->IsFileHandler(extension_id)) 193 return false; 194 195 return file_access_permissions_->HasAccessPermission(extension_id, 196 url.virtual_path()); 197 } 198 199 void FileSystemBackend::GrantFullAccessToExtension( 200 const std::string& extension_id) { 201 if (!special_storage_policy_.get()) 202 return; 203 if (!special_storage_policy_->IsFileHandler(extension_id)) { 204 NOTREACHED(); 205 return; 206 } 207 file_access_permissions_->GrantFullAccessPermission(extension_id); 208 } 209 210 void FileSystemBackend::GrantFileAccessToExtension( 211 const std::string& extension_id, const base::FilePath& virtual_path) { 212 if (!special_storage_policy_.get()) 213 return; 214 // All we care about here is access from extensions for now. 215 if (!special_storage_policy_->IsFileHandler(extension_id)) { 216 NOTREACHED(); 217 return; 218 } 219 220 std::string id; 221 storage::FileSystemType type; 222 std::string cracked_id; 223 base::FilePath path; 224 storage::FileSystemMountOption option; 225 if (!mount_points_->CrackVirtualPath(virtual_path, &id, &type, &cracked_id, 226 &path, &option) && 227 !system_mount_points_->CrackVirtualPath(virtual_path, &id, &type, 228 &cracked_id, &path, &option)) { 229 return; 230 } 231 232 if (type == storage::kFileSystemTypeRestrictedNativeLocal) { 233 LOG(ERROR) << "Can't grant access for restricted mount point"; 234 return; 235 } 236 237 file_access_permissions_->GrantAccessPermission(extension_id, virtual_path); 238 } 239 240 void FileSystemBackend::RevokeAccessForExtension( 241 const std::string& extension_id) { 242 file_access_permissions_->RevokePermissions(extension_id); 243 } 244 245 std::vector<base::FilePath> FileSystemBackend::GetRootDirectories() const { 246 std::vector<storage::MountPoints::MountPointInfo> mount_points; 247 mount_points_->AddMountPointInfosTo(&mount_points); 248 system_mount_points_->AddMountPointInfosTo(&mount_points); 249 250 std::vector<base::FilePath> root_dirs; 251 for (size_t i = 0; i < mount_points.size(); ++i) 252 root_dirs.push_back(mount_points[i].path); 253 return root_dirs; 254 } 255 256 storage::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil( 257 storage::FileSystemType type) { 258 switch (type) { 259 case storage::kFileSystemTypeDrive: 260 return drive_delegate_->GetAsyncFileUtil(type); 261 case storage::kFileSystemTypeProvided: 262 return file_system_provider_delegate_->GetAsyncFileUtil(type); 263 case storage::kFileSystemTypeNativeLocal: 264 case storage::kFileSystemTypeRestrictedNativeLocal: 265 return local_file_util_.get(); 266 case storage::kFileSystemTypeDeviceMediaAsFileStorage: 267 return mtp_delegate_->GetAsyncFileUtil(type); 268 default: 269 NOTREACHED(); 270 } 271 return NULL; 272 } 273 274 storage::WatcherManager* FileSystemBackend::GetWatcherManager( 275 storage::FileSystemType type) { 276 return NULL; 277 } 278 279 storage::CopyOrMoveFileValidatorFactory* 280 FileSystemBackend::GetCopyOrMoveFileValidatorFactory( 281 storage::FileSystemType type, 282 base::File::Error* error_code) { 283 DCHECK(error_code); 284 *error_code = base::File::FILE_OK; 285 return NULL; 286 } 287 288 storage::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation( 289 const storage::FileSystemURL& url, 290 storage::FileSystemContext* context, 291 base::File::Error* error_code) const { 292 DCHECK(url.is_valid()); 293 294 if (!IsAccessAllowed(url)) { 295 *error_code = base::File::FILE_ERROR_SECURITY; 296 return NULL; 297 } 298 299 if (url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage) { 300 // MTP file operations run on MediaTaskRunner. 301 return storage::FileSystemOperation::Create( 302 url, 303 context, 304 make_scoped_ptr(new storage::FileSystemOperationContext( 305 context, MediaFileSystemBackend::MediaTaskRunner().get()))); 306 } 307 308 DCHECK(url.type() == storage::kFileSystemTypeNativeLocal || 309 url.type() == storage::kFileSystemTypeRestrictedNativeLocal || 310 url.type() == storage::kFileSystemTypeDrive || 311 url.type() == storage::kFileSystemTypeProvided); 312 return storage::FileSystemOperation::Create( 313 url, 314 context, 315 make_scoped_ptr(new storage::FileSystemOperationContext(context))); 316 } 317 318 bool FileSystemBackend::SupportsStreaming( 319 const storage::FileSystemURL& url) const { 320 return url.type() == storage::kFileSystemTypeDrive || 321 url.type() == storage::kFileSystemTypeProvided || 322 url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage; 323 } 324 325 bool FileSystemBackend::HasInplaceCopyImplementation( 326 storage::FileSystemType type) const { 327 switch (type) { 328 case storage::kFileSystemTypeDrive: 329 case storage::kFileSystemTypeProvided: 330 case storage::kFileSystemTypeDeviceMediaAsFileStorage: 331 return true; 332 case storage::kFileSystemTypeNativeLocal: 333 case storage::kFileSystemTypeRestrictedNativeLocal: 334 return false; 335 default: 336 NOTREACHED(); 337 } 338 return true; 339 } 340 341 scoped_ptr<storage::FileStreamReader> FileSystemBackend::CreateFileStreamReader( 342 const storage::FileSystemURL& url, 343 int64 offset, 344 int64 max_bytes_to_read, 345 const base::Time& expected_modification_time, 346 storage::FileSystemContext* context) const { 347 DCHECK(url.is_valid()); 348 349 if (!IsAccessAllowed(url)) 350 return scoped_ptr<storage::FileStreamReader>(); 351 352 switch (url.type()) { 353 case storage::kFileSystemTypeDrive: 354 return drive_delegate_->CreateFileStreamReader( 355 url, offset, max_bytes_to_read, expected_modification_time, context); 356 case storage::kFileSystemTypeProvided: 357 return file_system_provider_delegate_->CreateFileStreamReader( 358 url, offset, max_bytes_to_read, expected_modification_time, context); 359 case storage::kFileSystemTypeNativeLocal: 360 case storage::kFileSystemTypeRestrictedNativeLocal: 361 return scoped_ptr<storage::FileStreamReader>( 362 storage::FileStreamReader::CreateForFileSystemFile( 363 context, url, offset, expected_modification_time)); 364 case storage::kFileSystemTypeDeviceMediaAsFileStorage: 365 return mtp_delegate_->CreateFileStreamReader( 366 url, offset, max_bytes_to_read, expected_modification_time, context); 367 default: 368 NOTREACHED(); 369 } 370 return scoped_ptr<storage::FileStreamReader>(); 371 } 372 373 scoped_ptr<storage::FileStreamWriter> FileSystemBackend::CreateFileStreamWriter( 374 const storage::FileSystemURL& url, 375 int64 offset, 376 storage::FileSystemContext* context) const { 377 DCHECK(url.is_valid()); 378 379 if (!IsAccessAllowed(url)) 380 return scoped_ptr<storage::FileStreamWriter>(); 381 382 switch (url.type()) { 383 case storage::kFileSystemTypeDrive: 384 return drive_delegate_->CreateFileStreamWriter(url, offset, context); 385 case storage::kFileSystemTypeProvided: 386 return file_system_provider_delegate_->CreateFileStreamWriter( 387 url, offset, context); 388 case storage::kFileSystemTypeNativeLocal: 389 return scoped_ptr<storage::FileStreamWriter>( 390 storage::FileStreamWriter::CreateForLocalFile( 391 context->default_file_task_runner(), 392 url.path(), 393 offset, 394 storage::FileStreamWriter::OPEN_EXISTING_FILE)); 395 case storage::kFileSystemTypeRestrictedNativeLocal: 396 // Restricted native local file system is read only. 397 return scoped_ptr<storage::FileStreamWriter>(); 398 case storage::kFileSystemTypeDeviceMediaAsFileStorage: 399 return mtp_delegate_->CreateFileStreamWriter(url, offset, context); 400 default: 401 NOTREACHED(); 402 } 403 return scoped_ptr<storage::FileStreamWriter>(); 404 } 405 406 bool FileSystemBackend::GetVirtualPath( 407 const base::FilePath& filesystem_path, 408 base::FilePath* virtual_path) { 409 return mount_points_->GetVirtualPath(filesystem_path, virtual_path) || 410 system_mount_points_->GetVirtualPath(filesystem_path, virtual_path); 411 } 412 413 void FileSystemBackend::GetRedirectURLForContents( 414 const storage::FileSystemURL& url, 415 const storage::URLCallback& callback) { 416 DCHECK(url.is_valid()); 417 418 if (!IsAccessAllowed(url)) 419 return callback.Run(GURL()); 420 421 switch (url.type()) { 422 case storage::kFileSystemTypeDrive: 423 drive_delegate_->GetRedirectURLForContents(url, callback); 424 return; 425 case storage::kFileSystemTypeProvided: 426 file_system_provider_delegate_->GetRedirectURLForContents(url, 427 callback); 428 return; 429 case storage::kFileSystemTypeDeviceMediaAsFileStorage: 430 mtp_delegate_->GetRedirectURLForContents(url, callback); 431 return; 432 case storage::kFileSystemTypeNativeLocal: 433 case storage::kFileSystemTypeRestrictedNativeLocal: 434 callback.Run(GURL()); 435 return; 436 default: 437 NOTREACHED(); 438 } 439 callback.Run(GURL()); 440 } 441 442 } // namespace chromeos 443