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/message_loop/message_loop.h" 10 #include "base/path_service.h" 11 #include "base/strings/stringprintf.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "base/synchronization/lock.h" 14 #include "chrome/browser/chromeos/fileapi/file_access_permissions.h" 15 #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h" 16 #include "chromeos/dbus/cros_disks_client.h" 17 #include "webkit/browser/blob/file_stream_reader.h" 18 #include "webkit/browser/fileapi/async_file_util_adapter.h" 19 #include "webkit/browser/fileapi/external_mount_points.h" 20 #include "webkit/browser/fileapi/file_system_context.h" 21 #include "webkit/browser/fileapi/file_system_file_stream_reader.h" 22 #include "webkit/browser/fileapi/file_system_operation_context.h" 23 #include "webkit/browser/fileapi/file_system_operation_impl.h" 24 #include "webkit/browser/fileapi/file_system_url.h" 25 #include "webkit/browser/fileapi/isolated_context.h" 26 #include "webkit/browser/fileapi/isolated_file_util.h" 27 #include "webkit/browser/fileapi/local_file_stream_writer.h" 28 29 namespace { 30 31 const char kChromeUIScheme[] = "chrome"; 32 33 } // namespace 34 35 namespace chromeos { 36 37 // static 38 bool FileSystemBackend::CanHandleURL(const fileapi::FileSystemURL& url) { 39 if (!url.is_valid()) 40 return false; 41 return url.type() == fileapi::kFileSystemTypeNativeLocal || 42 url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal || 43 url.type() == fileapi::kFileSystemTypeDrive; 44 } 45 46 FileSystemBackend::FileSystemBackend( 47 FileSystemBackendDelegate* drive_delegate, 48 scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy, 49 scoped_refptr<fileapi::ExternalMountPoints> mount_points, 50 fileapi::ExternalMountPoints* system_mount_points) 51 : special_storage_policy_(special_storage_policy), 52 file_access_permissions_(new FileAccessPermissions()), 53 local_file_util_(new fileapi::AsyncFileUtilAdapter( 54 new fileapi::IsolatedFileUtil())), 55 drive_delegate_(drive_delegate), 56 mount_points_(mount_points), 57 system_mount_points_(system_mount_points) { 58 } 59 60 FileSystemBackend::~FileSystemBackend() { 61 } 62 63 void FileSystemBackend::AddSystemMountPoints() { 64 // RegisterFileSystem() is no-op if the mount point with the same name 65 // already exists, hence it's safe to call without checking if a mount 66 // point already exists or not. 67 68 // TODO(satorux): "Downloads" directory should probably be per-profile. For 69 // this to be per-profile, a unique directory path should be chosen per 70 // profile, and the mount point should be added to 71 // mount_points_. crbug.com/247236 72 base::FilePath home_path; 73 if (PathService::Get(base::DIR_HOME, &home_path)) { 74 system_mount_points_->RegisterFileSystem( 75 "Downloads", 76 fileapi::kFileSystemTypeNativeLocal, 77 home_path.AppendASCII("Downloads")); 78 } 79 80 system_mount_points_->RegisterFileSystem( 81 "archive", 82 fileapi::kFileSystemTypeNativeLocal, 83 chromeos::CrosDisksClient::GetArchiveMountPoint()); 84 system_mount_points_->RegisterFileSystem( 85 "removable", 86 fileapi::kFileSystemTypeNativeLocal, 87 chromeos::CrosDisksClient::GetRemovableDiskMountPoint()); 88 system_mount_points_->RegisterFileSystem( 89 "oem", 90 fileapi::kFileSystemTypeRestrictedNativeLocal, 91 base::FilePath(FILE_PATH_LITERAL("/usr/share/oem"))); 92 } 93 94 bool FileSystemBackend::CanHandleType(fileapi::FileSystemType type) const { 95 switch (type) { 96 case fileapi::kFileSystemTypeExternal: 97 case fileapi::kFileSystemTypeDrive: 98 case fileapi::kFileSystemTypeRestrictedNativeLocal: 99 case fileapi::kFileSystemTypeNativeLocal: 100 case fileapi::kFileSystemTypeNativeForPlatformApp: 101 return true; 102 default: 103 return false; 104 } 105 } 106 107 void FileSystemBackend::Initialize(fileapi::FileSystemContext* context) { 108 } 109 110 void FileSystemBackend::OpenFileSystem( 111 const GURL& origin_url, 112 fileapi::FileSystemType type, 113 fileapi::OpenFileSystemMode mode, 114 const OpenFileSystemCallback& callback) { 115 DCHECK(fileapi::IsolatedContext::IsIsolatedType(type)); 116 // Nothing to validate for external filesystem. 117 callback.Run(GetFileSystemRootURI(origin_url, type), 118 GetFileSystemName(origin_url, type), 119 base::PLATFORM_FILE_OK); 120 } 121 122 fileapi::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() { 123 // No quota support. 124 return NULL; 125 } 126 127 bool FileSystemBackend::IsAccessAllowed( 128 const fileapi::FileSystemURL& url) const { 129 if (!url.is_valid()) 130 return false; 131 132 // Permit access to mount points from internal WebUI. 133 const GURL& origin_url = url.origin(); 134 if (origin_url.SchemeIs(kChromeUIScheme)) 135 return true; 136 137 // No extra check is needed for isolated file systems. 138 if (url.mount_type() == fileapi::kFileSystemTypeIsolated) 139 return true; 140 141 if (!CanHandleURL(url)) 142 return false; 143 144 std::string extension_id = origin_url.host(); 145 // TODO(mtomasz): Temporarily whitelist TimeScapes. Remove this in M-31. 146 // See: crbug.com/271946 147 if (extension_id == "mlbmkoenclnokonejhlfakkeabdlmpek" && 148 url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal) { 149 return true; 150 } 151 152 // Check first to make sure this extension has fileBrowserHander permissions. 153 if (!special_storage_policy_->IsFileHandler(extension_id)) 154 return false; 155 156 return file_access_permissions_->HasAccessPermission(extension_id, 157 url.virtual_path()); 158 } 159 160 void FileSystemBackend::GrantFullAccessToExtension( 161 const std::string& extension_id) { 162 DCHECK(special_storage_policy_->IsFileHandler(extension_id)); 163 if (!special_storage_policy_->IsFileHandler(extension_id)) 164 return; 165 166 std::vector<fileapi::MountPoints::MountPointInfo> files; 167 mount_points_->AddMountPointInfosTo(&files); 168 system_mount_points_->AddMountPointInfosTo(&files); 169 170 for (size_t i = 0; i < files.size(); ++i) { 171 file_access_permissions_->GrantAccessPermission( 172 extension_id, 173 base::FilePath::FromUTF8Unsafe(files[i].name)); 174 } 175 } 176 177 void FileSystemBackend::GrantFileAccessToExtension( 178 const std::string& extension_id, const base::FilePath& virtual_path) { 179 // All we care about here is access from extensions for now. 180 DCHECK(special_storage_policy_->IsFileHandler(extension_id)); 181 if (!special_storage_policy_->IsFileHandler(extension_id)) 182 return; 183 184 std::string id; 185 fileapi::FileSystemType type; 186 base::FilePath path; 187 if (!mount_points_->CrackVirtualPath(virtual_path, &id, &type, &path) && 188 !system_mount_points_->CrackVirtualPath(virtual_path, 189 &id, &type, &path)) { 190 return; 191 } 192 193 if (type == fileapi::kFileSystemTypeRestrictedNativeLocal) { 194 LOG(ERROR) << "Can't grant access for restricted mount point"; 195 return; 196 } 197 198 file_access_permissions_->GrantAccessPermission(extension_id, virtual_path); 199 } 200 201 void FileSystemBackend::RevokeAccessForExtension( 202 const std::string& extension_id) { 203 file_access_permissions_->RevokePermissions(extension_id); 204 } 205 206 std::vector<base::FilePath> FileSystemBackend::GetRootDirectories() const { 207 std::vector<fileapi::MountPoints::MountPointInfo> mount_points; 208 mount_points_->AddMountPointInfosTo(&mount_points); 209 system_mount_points_->AddMountPointInfosTo(&mount_points); 210 211 std::vector<base::FilePath> root_dirs; 212 for (size_t i = 0; i < mount_points.size(); ++i) 213 root_dirs.push_back(mount_points[i].path); 214 return root_dirs; 215 } 216 217 fileapi::FileSystemFileUtil* FileSystemBackend::GetFileUtil( 218 fileapi::FileSystemType type) { 219 DCHECK(type == fileapi::kFileSystemTypeNativeLocal || 220 type == fileapi::kFileSystemTypeRestrictedNativeLocal); 221 return local_file_util_->sync_file_util(); 222 } 223 224 fileapi::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil( 225 fileapi::FileSystemType type) { 226 if (type == fileapi::kFileSystemTypeDrive) 227 return drive_delegate_->GetAsyncFileUtil(type); 228 229 DCHECK(type == fileapi::kFileSystemTypeNativeLocal || 230 type == fileapi::kFileSystemTypeRestrictedNativeLocal); 231 return local_file_util_.get(); 232 } 233 234 fileapi::CopyOrMoveFileValidatorFactory* 235 FileSystemBackend::GetCopyOrMoveFileValidatorFactory( 236 fileapi::FileSystemType type, base::PlatformFileError* error_code) { 237 DCHECK(error_code); 238 *error_code = base::PLATFORM_FILE_OK; 239 return NULL; 240 } 241 242 fileapi::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation( 243 const fileapi::FileSystemURL& url, 244 fileapi::FileSystemContext* context, 245 base::PlatformFileError* error_code) const { 246 DCHECK(url.is_valid()); 247 248 if (!IsAccessAllowed(url)) { 249 *error_code = base::PLATFORM_FILE_ERROR_SECURITY; 250 return NULL; 251 } 252 253 DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal || 254 url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal || 255 url.type() == fileapi::kFileSystemTypeDrive); 256 scoped_ptr<fileapi::FileSystemOperationContext> operation_context( 257 new fileapi::FileSystemOperationContext(context)); 258 operation_context->set_root_path(GetFileSystemRootPath(url)); 259 return new fileapi::FileSystemOperationImpl(url, context, 260 operation_context.Pass()); 261 } 262 263 scoped_ptr<webkit_blob::FileStreamReader> 264 FileSystemBackend::CreateFileStreamReader( 265 const fileapi::FileSystemURL& url, 266 int64 offset, 267 const base::Time& expected_modification_time, 268 fileapi::FileSystemContext* context) const { 269 DCHECK(url.is_valid()); 270 271 if (!IsAccessAllowed(url)) 272 return scoped_ptr<webkit_blob::FileStreamReader>(); 273 274 if (url.type() == fileapi::kFileSystemTypeDrive) { 275 return drive_delegate_->CreateFileStreamReader( 276 url, offset, expected_modification_time, context); 277 } 278 279 return scoped_ptr<webkit_blob::FileStreamReader>( 280 new fileapi::FileSystemFileStreamReader( 281 context, url, offset, expected_modification_time)); 282 } 283 284 scoped_ptr<fileapi::FileStreamWriter> 285 FileSystemBackend::CreateFileStreamWriter( 286 const fileapi::FileSystemURL& url, 287 int64 offset, 288 fileapi::FileSystemContext* context) const { 289 DCHECK(url.is_valid()); 290 291 if (!IsAccessAllowed(url)) 292 return scoped_ptr<fileapi::FileStreamWriter>(); 293 294 if (url.type() == fileapi::kFileSystemTypeDrive) 295 return drive_delegate_->CreateFileStreamWriter(url, offset, context); 296 297 if (url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal) 298 return scoped_ptr<fileapi::FileStreamWriter>(); 299 300 DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal); 301 return scoped_ptr<fileapi::FileStreamWriter>( 302 new fileapi::LocalFileStreamWriter( 303 context->default_file_task_runner(), url.path(), offset)); 304 } 305 306 bool FileSystemBackend::GetVirtualPath( 307 const base::FilePath& filesystem_path, 308 base::FilePath* virtual_path) { 309 return mount_points_->GetVirtualPath(filesystem_path, virtual_path) || 310 system_mount_points_->GetVirtualPath(filesystem_path, virtual_path); 311 } 312 313 base::FilePath FileSystemBackend::GetFileSystemRootPath( 314 const fileapi::FileSystemURL& url) const { 315 DCHECK(fileapi::IsolatedContext::IsIsolatedType(url.mount_type())); 316 if (!url.is_valid()) 317 return base::FilePath(); 318 319 base::FilePath root_path; 320 std::string mount_name = url.filesystem_id(); 321 if (!mount_points_->GetRegisteredPath(mount_name, &root_path) && 322 !system_mount_points_->GetRegisteredPath(mount_name, &root_path)) { 323 return base::FilePath(); 324 } 325 326 return root_path.DirName(); 327 } 328 329 } // namespace chromeos 330