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 "storage/browser/fileapi/local_file_util.h"
6
7#include "base/files/file_enumerator.h"
8#include "base/files/file_util.h"
9#include "base/files/file_util_proxy.h"
10#include "storage/browser/fileapi/async_file_util_adapter.h"
11#include "storage/browser/fileapi/file_system_context.h"
12#include "storage/browser/fileapi/file_system_operation_context.h"
13#include "storage/browser/fileapi/file_system_url.h"
14#include "storage/browser/fileapi/native_file_util.h"
15#include "storage/common/fileapi/file_system_types.h"
16#include "storage/common/fileapi/file_system_util.h"
17#include "url/gurl.h"
18
19namespace storage {
20
21AsyncFileUtil* AsyncFileUtil::CreateForLocalFileSystem() {
22  return new AsyncFileUtilAdapter(new LocalFileUtil());
23}
24
25class LocalFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator {
26 public:
27  LocalFileEnumerator(const base::FilePath& platform_root_path,
28                      const base::FilePath& virtual_root_path,
29                      int file_type)
30      : file_enum_(platform_root_path, false /* recursive */, file_type),
31        platform_root_path_(platform_root_path),
32        virtual_root_path_(virtual_root_path) {
33  }
34
35  virtual ~LocalFileEnumerator() {}
36
37  virtual base::FilePath Next() OVERRIDE;
38  virtual int64 Size() OVERRIDE;
39  virtual base::Time LastModifiedTime() OVERRIDE;
40  virtual bool IsDirectory() OVERRIDE;
41
42 private:
43  base::FileEnumerator file_enum_;
44  base::FileEnumerator::FileInfo file_util_info_;
45  base::FilePath platform_root_path_;
46  base::FilePath virtual_root_path_;
47};
48
49base::FilePath LocalFileEnumerator::Next() {
50  base::FilePath next = file_enum_.Next();
51  // Don't return symlinks.
52  while (!next.empty() && base::IsLink(next))
53    next = file_enum_.Next();
54  if (next.empty())
55    return next;
56  file_util_info_ = file_enum_.GetInfo();
57
58  base::FilePath path;
59  platform_root_path_.AppendRelativePath(next, &path);
60  return virtual_root_path_.Append(path);
61}
62
63int64 LocalFileEnumerator::Size() {
64  return file_util_info_.GetSize();
65}
66
67base::Time LocalFileEnumerator::LastModifiedTime() {
68  return file_util_info_.GetLastModifiedTime();
69}
70
71bool LocalFileEnumerator::IsDirectory() {
72  return file_util_info_.IsDirectory();
73}
74
75LocalFileUtil::LocalFileUtil() {}
76
77LocalFileUtil::~LocalFileUtil() {}
78
79base::File LocalFileUtil::CreateOrOpen(
80    FileSystemOperationContext* context,
81    const FileSystemURL& url, int file_flags) {
82  base::FilePath file_path;
83  base::File::Error error = GetLocalFilePath(context, url, &file_path);
84  if (error != base::File::FILE_OK)
85    return base::File(error);
86  // Disallow opening files in symlinked paths.
87  if (base::IsLink(file_path))
88    return base::File(base::File::FILE_ERROR_NOT_FOUND);
89
90  return NativeFileUtil::CreateOrOpen(file_path, file_flags);
91}
92
93base::File::Error LocalFileUtil::EnsureFileExists(
94    FileSystemOperationContext* context,
95    const FileSystemURL& url,
96    bool* created) {
97  base::FilePath file_path;
98  base::File::Error error = GetLocalFilePath(context, url, &file_path);
99  if (error != base::File::FILE_OK)
100    return error;
101  return NativeFileUtil::EnsureFileExists(file_path, created);
102}
103
104base::File::Error LocalFileUtil::CreateDirectory(
105    FileSystemOperationContext* context,
106    const FileSystemURL& url,
107    bool exclusive,
108    bool recursive) {
109  base::FilePath file_path;
110  base::File::Error error = GetLocalFilePath(context, url, &file_path);
111  if (error != base::File::FILE_OK)
112    return error;
113  return NativeFileUtil::CreateDirectory(file_path, exclusive, recursive);
114}
115
116base::File::Error LocalFileUtil::GetFileInfo(
117    FileSystemOperationContext* context,
118    const FileSystemURL& url,
119    base::File::Info* file_info,
120    base::FilePath* platform_file_path) {
121  base::FilePath file_path;
122  base::File::Error error = GetLocalFilePath(context, url, &file_path);
123  if (error != base::File::FILE_OK)
124    return error;
125  // We should not follow symbolic links in sandboxed file system.
126  if (base::IsLink(file_path))
127    return base::File::FILE_ERROR_NOT_FOUND;
128
129  error = NativeFileUtil::GetFileInfo(file_path, file_info);
130  if (error == base::File::FILE_OK)
131    *platform_file_path = file_path;
132  return error;
133}
134
135scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> LocalFileUtil::
136    CreateFileEnumerator(
137        FileSystemOperationContext* context,
138        const FileSystemURL& root_url) {
139  base::FilePath file_path;
140  if (GetLocalFilePath(context, root_url, &file_path) !=
141      base::File::FILE_OK) {
142    return make_scoped_ptr(new EmptyFileEnumerator)
143        .PassAs<FileSystemFileUtil::AbstractFileEnumerator>();
144  }
145  return make_scoped_ptr(new LocalFileEnumerator(
146      file_path, root_url.path(),
147      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES))
148      .PassAs<FileSystemFileUtil::AbstractFileEnumerator>();
149}
150
151base::File::Error LocalFileUtil::GetLocalFilePath(
152    FileSystemOperationContext* context,
153    const FileSystemURL& url,
154    base::FilePath* local_file_path) {
155  DCHECK(local_file_path);
156  DCHECK(url.is_valid());
157  if (url.path().empty()) {
158    // Root direcory case, which should not be accessed.
159    return base::File::FILE_ERROR_ACCESS_DENIED;
160  }
161  *local_file_path = url.path();
162  return base::File::FILE_OK;
163}
164
165base::File::Error LocalFileUtil::Touch(
166    FileSystemOperationContext* context,
167    const FileSystemURL& url,
168    const base::Time& last_access_time,
169    const base::Time& last_modified_time) {
170  base::FilePath file_path;
171  base::File::Error error = GetLocalFilePath(context, url, &file_path);
172  if (error != base::File::FILE_OK)
173    return error;
174  return NativeFileUtil::Touch(file_path, last_access_time, last_modified_time);
175}
176
177base::File::Error LocalFileUtil::Truncate(
178    FileSystemOperationContext* context,
179    const FileSystemURL& url,
180    int64 length) {
181  base::FilePath file_path;
182  base::File::Error error = GetLocalFilePath(context, url, &file_path);
183  if (error != base::File::FILE_OK)
184    return error;
185  return NativeFileUtil::Truncate(file_path, length);
186}
187
188base::File::Error LocalFileUtil::CopyOrMoveFile(
189    FileSystemOperationContext* context,
190    const FileSystemURL& src_url,
191    const FileSystemURL& dest_url,
192    CopyOrMoveOption option,
193    bool copy) {
194  base::FilePath src_file_path;
195  base::File::Error error = GetLocalFilePath(context, src_url, &src_file_path);
196  if (error != base::File::FILE_OK)
197    return error;
198
199  base::FilePath dest_file_path;
200  error = GetLocalFilePath(context, dest_url, &dest_file_path);
201  if (error != base::File::FILE_OK)
202    return error;
203
204  return NativeFileUtil::CopyOrMoveFile(
205      src_file_path,
206      dest_file_path,
207      option,
208      storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url, copy));
209}
210
211base::File::Error LocalFileUtil::CopyInForeignFile(
212    FileSystemOperationContext* context,
213    const base::FilePath& src_file_path,
214    const FileSystemURL& dest_url) {
215  if (src_file_path.empty())
216    return base::File::FILE_ERROR_INVALID_OPERATION;
217
218  base::FilePath dest_file_path;
219  base::File::Error error =
220      GetLocalFilePath(context, dest_url, &dest_file_path);
221  if (error != base::File::FILE_OK)
222    return error;
223  return NativeFileUtil::CopyOrMoveFile(
224      src_file_path,
225      dest_file_path,
226      FileSystemOperation::OPTION_NONE,
227      storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url,
228                                                            true /* copy */));
229}
230
231base::File::Error LocalFileUtil::DeleteFile(
232    FileSystemOperationContext* context,
233    const FileSystemURL& url) {
234  base::FilePath file_path;
235  base::File::Error error = GetLocalFilePath(context, url, &file_path);
236  if (error != base::File::FILE_OK)
237    return error;
238  return NativeFileUtil::DeleteFile(file_path);
239}
240
241base::File::Error LocalFileUtil::DeleteDirectory(
242    FileSystemOperationContext* context,
243    const FileSystemURL& url) {
244  base::FilePath file_path;
245  base::File::Error error = GetLocalFilePath(context, url, &file_path);
246  if (error != base::File::FILE_OK)
247    return error;
248  return NativeFileUtil::DeleteDirectory(file_path);
249}
250
251storage::ScopedFile LocalFileUtil::CreateSnapshotFile(
252    FileSystemOperationContext* context,
253    const FileSystemURL& url,
254    base::File::Error* error,
255    base::File::Info* file_info,
256    base::FilePath* platform_path) {
257  DCHECK(file_info);
258  // We're just returning the local file information.
259  *error = GetFileInfo(context, url, file_info, platform_path);
260  if (*error == base::File::FILE_OK && file_info->is_directory)
261    *error = base::File::FILE_ERROR_NOT_A_FILE;
262  return storage::ScopedFile();
263}
264
265}  // namespace storage
266