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
CanHandleURL(const storage::FileSystemURL & url)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
FileSystemBackend(FileSystemBackendDelegate * drive_delegate,FileSystemBackendDelegate * file_system_provider_delegate,FileSystemBackendDelegate * mtp_delegate,scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,scoped_refptr<storage::ExternalMountPoints> mount_points,storage::ExternalMountPoints * system_mount_points)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
~FileSystemBackend()54 FileSystemBackend::~FileSystemBackend() {
55 }
56
AddSystemMountPoints()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
CanHandleType(storage::FileSystemType type) const78 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
Initialize(storage::FileSystemContext * context)93 void FileSystemBackend::Initialize(storage::FileSystemContext* context) {
94 }
95
ResolveURL(const storage::FileSystemURL & url,storage::OpenFileSystemMode mode,const OpenFileSystemCallback & callback)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
GetQuotaUtil()144 storage::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() {
145 // No quota support.
146 return NULL;
147 }
148
GetUpdateObservers(storage::FileSystemType type) const149 const storage::UpdateObserverList* FileSystemBackend::GetUpdateObservers(
150 storage::FileSystemType type) const {
151 return NULL;
152 }
153
GetChangeObservers(storage::FileSystemType type) const154 const storage::ChangeObserverList* FileSystemBackend::GetChangeObservers(
155 storage::FileSystemType type) const {
156 return NULL;
157 }
158
GetAccessObservers(storage::FileSystemType type) const159 const storage::AccessObserverList* FileSystemBackend::GetAccessObservers(
160 storage::FileSystemType type) const {
161 return NULL;
162 }
163
IsAccessAllowed(const storage::FileSystemURL & url) const164 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
GrantFullAccessToExtension(const std::string & extension_id)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
GrantFileAccessToExtension(const std::string & extension_id,const base::FilePath & virtual_path)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
RevokeAccessForExtension(const std::string & extension_id)240 void FileSystemBackend::RevokeAccessForExtension(
241 const std::string& extension_id) {
242 file_access_permissions_->RevokePermissions(extension_id);
243 }
244
GetRootDirectories() const245 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
GetAsyncFileUtil(storage::FileSystemType type)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
GetWatcherManager(storage::FileSystemType type)274 storage::WatcherManager* FileSystemBackend::GetWatcherManager(
275 storage::FileSystemType type) {
276 return NULL;
277 }
278
279 storage::CopyOrMoveFileValidatorFactory*
GetCopyOrMoveFileValidatorFactory(storage::FileSystemType type,base::File::Error * error_code)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
CreateFileSystemOperation(const storage::FileSystemURL & url,storage::FileSystemContext * context,base::File::Error * error_code) const288 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
SupportsStreaming(const storage::FileSystemURL & url) const318 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
HasInplaceCopyImplementation(storage::FileSystemType type) const325 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
CreateFileStreamReader(const storage::FileSystemURL & url,int64 offset,int64 max_bytes_to_read,const base::Time & expected_modification_time,storage::FileSystemContext * context) const341 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
CreateFileStreamWriter(const storage::FileSystemURL & url,int64 offset,storage::FileSystemContext * context) const373 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
GetVirtualPath(const base::FilePath & filesystem_path,base::FilePath * virtual_path)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
GetRedirectURLForContents(const storage::FileSystemURL & url,const storage::URLCallback & callback)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