native_media_file_util.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright (c) 2012 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/media_galleries/fileapi/native_media_file_util.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/file_util.h"
12#include "base/files/file_enumerator.h"
13#include "base/strings/string_util.h"
14#include "base/task_runner_util.h"
15#include "chrome/browser/media_galleries/fileapi/media_file_system_mount_point_provider.h"
16#include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
17#include "content/public/browser/browser_thread.h"
18#include "net/base/mime_sniffer.h"
19#include "url/gurl.h"
20#include "webkit/browser/fileapi/file_system_context.h"
21#include "webkit/browser/fileapi/file_system_operation_context.h"
22#include "webkit/browser/fileapi/file_system_task_runners.h"
23#include "webkit/browser/fileapi/native_file_util.h"
24#include "webkit/common/blob/shareable_file_reference.h"
25
26namespace chrome {
27
28namespace {
29
30// Modelled after ScopedFILEClose.
31struct ScopedPlatformFileClose {
32  void operator()(base::PlatformFile* file) {
33    if (file && *file != base::kInvalidPlatformFileValue)
34      base::ClosePlatformFile(*file);
35  }
36};
37
38typedef scoped_ptr<base::PlatformFile, ScopedPlatformFileClose>
39    ScopedPlatformFile;
40
41// Used to skip the hidden folders and files. Returns true if the file specified
42// by |path| should be skipped.
43bool ShouldSkip(const base::FilePath& path) {
44  const base::FilePath::StringType base_name = path.BaseName().value();
45  if (base_name.empty())
46    return false;
47
48  // Dot files (aka hidden files)
49  if (base_name[0] == '.')
50    return true;
51
52  // Mac OS X file.
53  if (base_name == FILE_PATH_LITERAL("__MACOSX"))
54    return true;
55
56#if defined(OS_WIN)
57  DWORD file_attributes = ::GetFileAttributes(path.value().c_str());
58  if ((file_attributes != INVALID_FILE_ATTRIBUTES) &&
59      ((file_attributes & FILE_ATTRIBUTE_HIDDEN) != 0))
60    return true;
61#else
62  // Windows always creates a recycle bin folder in the attached device to store
63  // all the deleted contents. On non-windows operating systems, there is no way
64  // to get the hidden attribute of windows recycle bin folders that are present
65  // on the attached device. Therefore, compare the file path name to the
66  // recycle bin name and exclude those folders. For more details, please refer
67  // to http://support.microsoft.com/kb/171694.
68  const char win_98_recycle_bin_name[] = "RECYCLED";
69  const char win_xp_recycle_bin_name[] = "RECYCLER";
70  const char win_vista_recycle_bin_name[] = "$Recycle.bin";
71  if ((base::strncasecmp(base_name.c_str(),
72                         win_98_recycle_bin_name,
73                         strlen(win_98_recycle_bin_name)) == 0) ||
74      (base::strncasecmp(base_name.c_str(),
75                         win_xp_recycle_bin_name,
76                         strlen(win_xp_recycle_bin_name)) == 0) ||
77      (base::strncasecmp(base_name.c_str(),
78                         win_vista_recycle_bin_name,
79                         strlen(win_vista_recycle_bin_name)) == 0))
80    return true;
81#endif
82  return false;
83}
84
85// Returns true if the current thread is capable of doing IO.
86bool IsOnTaskRunnerThread(fileapi::FileSystemOperationContext* context) {
87  return context->task_runner()->RunsTasksOnCurrentThread();
88}
89
90MediaPathFilter* GetMediaPathFilter(
91    fileapi::FileSystemOperationContext* context) {
92  return context->GetUserValue<MediaPathFilter*>(
93          MediaFileSystemMountPointProvider::kMediaPathFilterKey);
94}
95
96}  // namespace
97
98NativeMediaFileUtil::NativeMediaFileUtil() : weak_factory_(this) {
99}
100
101NativeMediaFileUtil::~NativeMediaFileUtil() {
102}
103
104// static
105base::PlatformFileError NativeMediaFileUtil::IsMediaFile(
106    const base::FilePath& path) {
107  base::PlatformFile file_handle;
108  const int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
109  base::PlatformFileError error =
110      fileapi::NativeFileUtil::CreateOrOpen(path, flags, &file_handle, NULL);
111  if (error != base::PLATFORM_FILE_OK)
112    return error;
113
114  ScopedPlatformFile scoped_platform_file(&file_handle);
115  char buffer[net::kMaxBytesToSniff];
116
117  // Read as much as net::SniffMimeTypeFromLocalData() will bother looking at.
118  int64 len =
119      base::ReadPlatformFile(file_handle, 0, buffer, net::kMaxBytesToSniff);
120  if (len < 0)
121    return base::PLATFORM_FILE_ERROR_FAILED;
122  if (len == 0)
123    return base::PLATFORM_FILE_ERROR_SECURITY;
124
125  std::string mime_type;
126  if (!net::SniffMimeTypeFromLocalData(buffer, len, &mime_type))
127    return base::PLATFORM_FILE_ERROR_SECURITY;
128
129  if (StartsWithASCII(mime_type, "image/", true) ||
130      StartsWithASCII(mime_type, "audio/", true) ||
131      StartsWithASCII(mime_type, "video/", true) ||
132      mime_type == "application/x-shockwave-flash") {
133    return base::PLATFORM_FILE_OK;
134  }
135  return base::PLATFORM_FILE_ERROR_SECURITY;
136}
137
138bool NativeMediaFileUtil::CreateOrOpen(
139    scoped_ptr<fileapi::FileSystemOperationContext> context,
140    const fileapi::FileSystemURL& url,
141    int file_flags,
142    const CreateOrOpenCallback& callback) {
143  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
144  // Only called by NaCl, which should not have access to media file systems.
145  base::PlatformFile invalid_file(base::kInvalidPlatformFileValue);
146  if (!callback.is_null()) {
147    callback.Run(base::PLATFORM_FILE_ERROR_SECURITY,
148                 base::PassPlatformFile(&invalid_file));
149  }
150  return true;
151}
152
153bool NativeMediaFileUtil::EnsureFileExists(
154    scoped_ptr<fileapi::FileSystemOperationContext> context,
155    const fileapi::FileSystemURL& url,
156    const EnsureFileExistsCallback& callback) {
157  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
158  if (!callback.is_null())
159    callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, false);
160  return true;
161}
162
163bool NativeMediaFileUtil::CreateDirectory(
164    scoped_ptr<fileapi::FileSystemOperationContext> context,
165    const fileapi::FileSystemURL& url,
166    bool exclusive,
167    bool recursive,
168    const StatusCallback& callback) {
169  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
170  fileapi::FileSystemOperationContext* context_ptr = context.get();
171  return context_ptr->task_runner()->PostTask(
172      FROM_HERE,
173      base::Bind(&NativeMediaFileUtil::CreateDirectoryOnTaskRunnerThread,
174                 weak_factory_.GetWeakPtr(), base::Passed(&context),
175                 url, exclusive, recursive, callback));
176}
177
178bool NativeMediaFileUtil::GetFileInfo(
179    scoped_ptr<fileapi::FileSystemOperationContext> context,
180    const fileapi::FileSystemURL& url,
181    const GetFileInfoCallback& callback) {
182  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
183  fileapi::FileSystemOperationContext* context_ptr = context.get();
184  return context_ptr->task_runner()->PostTask(
185      FROM_HERE,
186      base::Bind(&NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread,
187                 weak_factory_.GetWeakPtr(), base::Passed(&context),
188                 url, callback));
189}
190
191bool NativeMediaFileUtil::ReadDirectory(
192    scoped_ptr<fileapi::FileSystemOperationContext> context,
193    const fileapi::FileSystemURL& url,
194    const ReadDirectoryCallback& callback) {
195  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
196  fileapi::FileSystemOperationContext* context_ptr = context.get();
197  return context_ptr->task_runner()->PostTask(
198      FROM_HERE,
199      base::Bind(&NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread,
200                 weak_factory_.GetWeakPtr(), base::Passed(&context),
201                 url, callback));
202}
203
204bool NativeMediaFileUtil::Touch(
205    scoped_ptr<fileapi::FileSystemOperationContext> context,
206    const fileapi::FileSystemURL& url,
207    const base::Time& last_access_time,
208    const base::Time& last_modified_time,
209    const StatusCallback& callback) {
210  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
211  if (!callback.is_null())
212    callback.Run(base::PLATFORM_FILE_ERROR_SECURITY);
213  return true;
214}
215
216bool NativeMediaFileUtil::Truncate(
217    scoped_ptr<fileapi::FileSystemOperationContext> context,
218    const fileapi::FileSystemURL& url,
219    int64 length,
220    const StatusCallback& callback) {
221  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
222  if (!callback.is_null())
223    callback.Run(base::PLATFORM_FILE_ERROR_SECURITY);
224  return true;
225}
226
227bool NativeMediaFileUtil::CopyFileLocal(
228    scoped_ptr<fileapi::FileSystemOperationContext> context,
229    const fileapi::FileSystemURL& src_url,
230    const fileapi::FileSystemURL& dest_url,
231    const StatusCallback& callback) {
232  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
233  fileapi::FileSystemOperationContext* context_ptr = context.get();
234  return context_ptr->task_runner()->PostTask(
235      FROM_HERE,
236      base::Bind(&NativeMediaFileUtil::CopyOrMoveFileLocalOnTaskRunnerThread,
237                 weak_factory_.GetWeakPtr(), base::Passed(&context),
238                 src_url, dest_url, true /* copy */, callback));
239}
240
241bool NativeMediaFileUtil::MoveFileLocal(
242    scoped_ptr<fileapi::FileSystemOperationContext> context,
243    const fileapi::FileSystemURL& src_url,
244    const fileapi::FileSystemURL& dest_url,
245    const StatusCallback& callback) {
246  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
247  fileapi::FileSystemOperationContext* context_ptr = context.get();
248  return context_ptr->task_runner()->PostTask(
249      FROM_HERE,
250      base::Bind(&NativeMediaFileUtil::CopyOrMoveFileLocalOnTaskRunnerThread,
251                 weak_factory_.GetWeakPtr(), base::Passed(&context),
252                 src_url, dest_url, false /* copy */, callback));
253}
254
255bool NativeMediaFileUtil::CopyInForeignFile(
256    scoped_ptr<fileapi::FileSystemOperationContext> context,
257    const base::FilePath& src_file_path,
258    const fileapi::FileSystemURL& dest_url,
259    const StatusCallback& callback) {
260  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
261  fileapi::FileSystemOperationContext* context_ptr = context.get();
262  return context_ptr->task_runner()->PostTask(
263      FROM_HERE,
264      base::Bind(&NativeMediaFileUtil::CopyInForeignFileOnTaskRunnerThread,
265                 weak_factory_.GetWeakPtr(), base::Passed(&context),
266                 src_file_path, dest_url, callback));
267}
268
269bool NativeMediaFileUtil::DeleteFile(
270    scoped_ptr<fileapi::FileSystemOperationContext> context,
271    const fileapi::FileSystemURL& url,
272    const StatusCallback& callback) {
273  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
274  if (!callback.is_null())
275    callback.Run(base::PLATFORM_FILE_ERROR_SECURITY);
276  return true;
277}
278
279// This is needed to support Copy and Move.
280bool NativeMediaFileUtil::DeleteDirectory(
281    scoped_ptr<fileapi::FileSystemOperationContext> context,
282    const fileapi::FileSystemURL& url,
283    const StatusCallback& callback) {
284  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
285  fileapi::FileSystemOperationContext* context_ptr = context.get();
286  return context_ptr->task_runner()->PostTask(
287      FROM_HERE,
288      base::Bind(&NativeMediaFileUtil::DeleteDirectoryOnTaskRunnerThread,
289                 weak_factory_.GetWeakPtr(), base::Passed(&context),
290                 url, callback));
291}
292
293bool NativeMediaFileUtil::DeleteRecursively(
294    scoped_ptr<fileapi::FileSystemOperationContext> context,
295    const fileapi::FileSystemURL& url,
296    const StatusCallback& callback) {
297  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
298  if (!callback.is_null())
299    callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
300  return true;
301}
302
303bool NativeMediaFileUtil::CreateSnapshotFile(
304    scoped_ptr<fileapi::FileSystemOperationContext> context,
305    const fileapi::FileSystemURL& url,
306    const CreateSnapshotFileCallback& callback) {
307  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
308  fileapi::FileSystemOperationContext* context_ptr = context.get();
309  return context_ptr->task_runner()->PostTask(
310      FROM_HERE,
311      base::Bind(&NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread,
312                 weak_factory_.GetWeakPtr(), base::Passed(&context),
313                 url, callback));
314}
315
316void NativeMediaFileUtil::CreateDirectoryOnTaskRunnerThread(
317    scoped_ptr<fileapi::FileSystemOperationContext> context,
318    const fileapi::FileSystemURL& url,
319    bool exclusive,
320    bool recursive,
321    const StatusCallback& callback) {
322  DCHECK(IsOnTaskRunnerThread(context.get()));
323  base::PlatformFileError error =
324      CreateDirectorySync(context.get(), url, exclusive, recursive);
325  if (callback.is_null())
326    return;
327  content::BrowserThread::PostTask(
328      content::BrowserThread::IO,
329      FROM_HERE,
330      base::Bind(callback, error));
331}
332
333void NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(
334    scoped_ptr<fileapi::FileSystemOperationContext> context,
335    const fileapi::FileSystemURL& url,
336    const GetFileInfoCallback& callback) {
337  DCHECK(IsOnTaskRunnerThread(context.get()));
338  base::PlatformFileInfo file_info;
339  // TODO(thestig): remove this.
340  base::FilePath platform_path;
341  base::PlatformFileError error =
342      GetFileInfoSync(context.get(), url, &file_info, &platform_path);
343  if (callback.is_null())
344    return;
345  content::BrowserThread::PostTask(
346      content::BrowserThread::IO,
347      FROM_HERE,
348      base::Bind(callback, error, file_info));
349}
350
351void NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(
352    scoped_ptr<fileapi::FileSystemOperationContext> context,
353    const fileapi::FileSystemURL& url,
354    const ReadDirectoryCallback& callback) {
355  DCHECK(IsOnTaskRunnerThread(context.get()));
356  EntryList entry_list;
357  base::PlatformFileError error =
358      ReadDirectorySync(context.get(), url, &entry_list);
359  if (callback.is_null())
360    return;
361  content::BrowserThread::PostTask(
362      content::BrowserThread::IO,
363      FROM_HERE,
364      base::Bind(callback, error, entry_list, false /* has_more */));
365}
366
367void NativeMediaFileUtil::CopyOrMoveFileLocalOnTaskRunnerThread(
368    scoped_ptr<fileapi::FileSystemOperationContext> context,
369    const fileapi::FileSystemURL& src_url,
370    const fileapi::FileSystemURL& dest_url,
371    bool copy,
372    const StatusCallback& callback) {
373  DCHECK(IsOnTaskRunnerThread(context.get()));
374  base::PlatformFileError error =
375      CopyOrMoveFileSync(context.get(), src_url, dest_url, copy);
376  if (callback.is_null())
377    return;
378  content::BrowserThread::PostTask(
379      content::BrowserThread::IO,
380      FROM_HERE,
381      base::Bind(callback, error));
382}
383
384void NativeMediaFileUtil::CopyInForeignFileOnTaskRunnerThread(
385    scoped_ptr<fileapi::FileSystemOperationContext> context,
386    const base::FilePath& src_file_path,
387    const fileapi::FileSystemURL& dest_url,
388    const StatusCallback& callback) {
389  DCHECK(IsOnTaskRunnerThread(context.get()));
390  base::PlatformFileError error =
391      CopyInForeignFileSync(context.get(), src_file_path, dest_url);
392  if (callback.is_null())
393    return;
394  content::BrowserThread::PostTask(
395      content::BrowserThread::IO,
396      FROM_HERE,
397      base::Bind(callback, error));
398}
399
400void NativeMediaFileUtil::DeleteDirectoryOnTaskRunnerThread(
401    scoped_ptr<fileapi::FileSystemOperationContext> context,
402    const fileapi::FileSystemURL& url,
403    const StatusCallback& callback) {
404  DCHECK(IsOnTaskRunnerThread(context.get()));
405  base::PlatformFileError error = DeleteDirectorySync(context.get(), url);
406  if (callback.is_null())
407    return;
408  content::BrowserThread::PostTask(
409      content::BrowserThread::IO,
410      FROM_HERE,
411      base::Bind(callback, error));
412}
413
414void NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(
415    scoped_ptr<fileapi::FileSystemOperationContext> context,
416    const fileapi::FileSystemURL& url,
417    const CreateSnapshotFileCallback& callback) {
418  DCHECK(IsOnTaskRunnerThread(context.get()));
419  base::PlatformFileInfo file_info;
420  base::FilePath platform_path;
421  scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
422  base::PlatformFileError error =
423      CreateSnapshotFileSync(context.get(), url, &file_info, &platform_path,
424                             &file_ref);
425  if (callback.is_null())
426    return;
427  content::BrowserThread::PostTask(
428      content::BrowserThread::IO,
429      FROM_HERE,
430      base::Bind(callback, error, file_info, platform_path, file_ref));
431}
432
433base::PlatformFileError NativeMediaFileUtil::CreateDirectorySync(
434    fileapi::FileSystemOperationContext* context,
435    const fileapi::FileSystemURL& url,
436    bool exclusive,
437    bool recursive) {
438  base::FilePath file_path;
439  base::PlatformFileError error = GetLocalFilePath(context, url, &file_path);
440  if (error != base::PLATFORM_FILE_OK)
441    return error;
442  return fileapi::NativeFileUtil::CreateDirectory(file_path, exclusive,
443                                                  recursive);
444}
445
446base::PlatformFileError NativeMediaFileUtil::CopyOrMoveFileSync(
447    fileapi::FileSystemOperationContext* context,
448    const fileapi::FileSystemURL& src_url,
449    const fileapi::FileSystemURL& dest_url,
450    bool copy) {
451  DCHECK(IsOnTaskRunnerThread(context));
452  base::FilePath src_file_path;
453  base::PlatformFileError error =
454      GetFilteredLocalFilePathForExistingFileOrDirectory(
455          context, src_url,
456          base::PLATFORM_FILE_ERROR_NOT_FOUND,
457          &src_file_path);
458  if (error != base::PLATFORM_FILE_OK)
459    return error;
460  if (fileapi::NativeFileUtil::DirectoryExists(src_file_path))
461    return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
462
463  base::FilePath dest_file_path;
464  error = GetLocalFilePath(context, dest_url, &dest_file_path);
465  if (error != base::PLATFORM_FILE_OK)
466    return error;
467  base::PlatformFileInfo file_info;
468  error = fileapi::NativeFileUtil::GetFileInfo(dest_file_path, &file_info);
469  if (error != base::PLATFORM_FILE_OK &&
470      error != base::PLATFORM_FILE_ERROR_NOT_FOUND)
471    return error;
472  if (error == base::PLATFORM_FILE_OK && file_info.is_directory)
473    return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
474  if (!GetMediaPathFilter(context)->Match(dest_file_path))
475    return base::PLATFORM_FILE_ERROR_SECURITY;
476
477  return fileapi::NativeFileUtil::CopyOrMoveFile(src_file_path, dest_file_path,
478                                                 copy);
479}
480
481base::PlatformFileError NativeMediaFileUtil::CopyInForeignFileSync(
482    fileapi::FileSystemOperationContext* context,
483    const base::FilePath& src_file_path,
484    const fileapi::FileSystemURL& dest_url) {
485  DCHECK(IsOnTaskRunnerThread(context));
486  if (src_file_path.empty())
487    return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
488
489  base::FilePath dest_file_path;
490  base::PlatformFileError error =
491      GetFilteredLocalFilePath(context, dest_url, &dest_file_path);
492  if (error != base::PLATFORM_FILE_OK)
493    return error;
494  return fileapi::NativeFileUtil::CopyOrMoveFile(src_file_path, dest_file_path,
495                                                 true);
496}
497
498base::PlatformFileError NativeMediaFileUtil::GetFileInfoSync(
499    fileapi::FileSystemOperationContext* context,
500    const fileapi::FileSystemURL& url,
501    base::PlatformFileInfo* file_info,
502    base::FilePath* platform_path) {
503  DCHECK(context);
504  DCHECK(IsOnTaskRunnerThread(context));
505  DCHECK(file_info);
506  DCHECK(GetMediaPathFilter(context));
507
508  base::FilePath file_path;
509  base::PlatformFileError error = GetLocalFilePath(context, url, &file_path);
510  if (error != base::PLATFORM_FILE_OK)
511    return error;
512  if (file_util::IsLink(file_path))
513    return base::PLATFORM_FILE_ERROR_NOT_FOUND;
514  error = fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
515  if (error != base::PLATFORM_FILE_OK)
516    return error;
517
518  if (platform_path)
519    *platform_path = file_path;
520  if (file_info->is_directory ||
521      GetMediaPathFilter(context)->Match(file_path)) {
522    return base::PLATFORM_FILE_OK;
523  }
524  return base::PLATFORM_FILE_ERROR_NOT_FOUND;
525}
526
527base::PlatformFileError NativeMediaFileUtil::GetLocalFilePath(
528    fileapi::FileSystemOperationContext* context,
529    const fileapi::FileSystemURL& url,
530    base::FilePath* local_file_path) {
531  DCHECK(local_file_path);
532  DCHECK(url.is_valid());
533  if (url.path().empty()) {
534    // Root direcory case, which should not be accessed.
535    return base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
536  }
537  *local_file_path = url.path();
538  return base::PLATFORM_FILE_OK;
539}
540
541base::PlatformFileError NativeMediaFileUtil::ReadDirectorySync(
542      fileapi::FileSystemOperationContext* context,
543      const fileapi::FileSystemURL& url,
544      EntryList* file_list) {
545  DCHECK(IsOnTaskRunnerThread(context));
546  DCHECK(file_list);
547  DCHECK(file_list->empty());
548  base::PlatformFileInfo file_info;
549  base::FilePath dir_path;
550  base::PlatformFileError error =
551      GetFileInfoSync(context, url, &file_info, &dir_path);
552
553  if (error != base::PLATFORM_FILE_OK)
554    return error;
555
556  if (!file_info.is_directory)
557    return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
558
559  base::FileEnumerator file_enum(
560      dir_path,
561      false /* recursive */,
562      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
563  for (base::FilePath enum_path = file_enum.Next();
564       !enum_path.empty();
565       enum_path = file_enum.Next()) {
566    // Skip symlinks.
567    if (file_util::IsLink(enum_path))
568      continue;
569
570    base::FileEnumerator::FileInfo info = file_enum.GetInfo();
571
572    // NativeMediaFileUtil skip criteria.
573    if (ShouldSkip(enum_path))
574      continue;
575    if (!info.IsDirectory() && !GetMediaPathFilter(context)->Match(enum_path))
576      continue;
577
578    fileapi::DirectoryEntry entry;
579    entry.is_directory = info.IsDirectory();
580    entry.name = enum_path.BaseName().value();
581    entry.size = info.GetSize();
582    entry.last_modified_time = info.GetLastModifiedTime();
583
584    file_list->push_back(entry);
585  }
586
587  return base::PLATFORM_FILE_OK;
588}
589
590base::PlatformFileError NativeMediaFileUtil::DeleteDirectorySync(
591    fileapi::FileSystemOperationContext* context,
592    const fileapi::FileSystemURL& url) {
593  DCHECK(IsOnTaskRunnerThread(context));
594  base::FilePath file_path;
595  base::PlatformFileError error = GetLocalFilePath(context, url, &file_path);
596  if (error != base::PLATFORM_FILE_OK)
597    return error;
598  return fileapi::NativeFileUtil::DeleteDirectory(file_path);
599}
600
601base::PlatformFileError NativeMediaFileUtil::CreateSnapshotFileSync(
602    fileapi::FileSystemOperationContext* context,
603    const fileapi::FileSystemURL& url,
604    base::PlatformFileInfo* file_info,
605    base::FilePath* platform_path,
606    scoped_refptr<webkit_blob::ShareableFileReference>* file_ref) {
607  DCHECK(IsOnTaskRunnerThread(context));
608  base::PlatformFileError error =
609      GetFileInfoSync(context, url, file_info, platform_path);
610  if (error == base::PLATFORM_FILE_OK && file_info->is_directory)
611    error = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
612  if (error == base::PLATFORM_FILE_OK)
613    error = NativeMediaFileUtil::IsMediaFile(*platform_path);
614
615  // We're just returning the local file information.
616  *file_ref = scoped_refptr<webkit_blob::ShareableFileReference>();
617
618  return error;
619}
620
621base::PlatformFileError NativeMediaFileUtil::GetFilteredLocalFilePath(
622    fileapi::FileSystemOperationContext* context,
623    const fileapi::FileSystemURL& file_system_url,
624    base::FilePath* local_file_path) {
625  DCHECK(IsOnTaskRunnerThread(context));
626  base::FilePath file_path;
627  base::PlatformFileError error =
628      GetLocalFilePath(context, file_system_url, &file_path);
629  if (error != base::PLATFORM_FILE_OK)
630    return error;
631  if (!GetMediaPathFilter(context)->Match(file_path))
632    return base::PLATFORM_FILE_ERROR_SECURITY;
633
634  *local_file_path = file_path;
635  return base::PLATFORM_FILE_OK;
636}
637
638base::PlatformFileError
639NativeMediaFileUtil::GetFilteredLocalFilePathForExistingFileOrDirectory(
640    fileapi::FileSystemOperationContext* context,
641    const fileapi::FileSystemURL& file_system_url,
642    base::PlatformFileError failure_error,
643    base::FilePath* local_file_path) {
644  DCHECK(IsOnTaskRunnerThread(context));
645  base::FilePath file_path;
646  base::PlatformFileError error =
647      GetLocalFilePath(context, file_system_url, &file_path);
648  if (error != base::PLATFORM_FILE_OK)
649    return error;
650
651  if (!file_util::PathExists(file_path))
652    return failure_error;
653  base::PlatformFileInfo file_info;
654  if (!file_util::GetFileInfo(file_path, &file_info))
655    return base::PLATFORM_FILE_ERROR_FAILED;
656
657  if (!file_info.is_directory &&
658      !GetMediaPathFilter(context)->Match(file_path)) {
659    return failure_error;
660  }
661
662  *local_file_path = file_path;
663  return base::PLATFORM_FILE_OK;
664}
665
666}  // namespace chrome
667