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 "webkit/browser/fileapi/native_file_util.h"
6
7 #include "base/file_util.h"
8 #include "base/files/file.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "webkit/browser/fileapi/file_system_operation_context.h"
12 #include "webkit/browser/fileapi/file_system_url.h"
13
14 namespace fileapi {
15
16 namespace {
17
18 // Sets permissions on directory at |dir_path| based on the target platform.
19 // Returns true on success, or false otherwise.
20 //
21 // TODO(benchan): Find a better place outside webkit to host this function.
SetPlatformSpecificDirectoryPermissions(const base::FilePath & dir_path)22 bool SetPlatformSpecificDirectoryPermissions(const base::FilePath& dir_path) {
23 #if defined(OS_CHROMEOS)
24 // System daemons on Chrome OS may run as a user different than the Chrome
25 // process but need to access files under the directories created here.
26 // Because of that, grant the execute permission on the created directory
27 // to group and other users.
28 if (HANDLE_EINTR(chmod(dir_path.value().c_str(),
29 S_IRWXU | S_IXGRP | S_IXOTH)) != 0) {
30 return false;
31 }
32 #endif
33 // Keep the directory permissions unchanged on non-Chrome OS platforms.
34 return true;
35 }
36
37 // Copies a file |from| to |to|, and ensure the written content is synced to
38 // the disk. This is essentially base::CopyFile followed by fsync().
CopyFileAndSync(const base::FilePath & from,const base::FilePath & to)39 bool CopyFileAndSync(const base::FilePath& from, const base::FilePath& to) {
40 base::File infile(from, base::File::FLAG_OPEN | base::File::FLAG_READ);
41 if (!infile.IsValid()) {
42 return false;
43 }
44
45 base::File outfile(to,
46 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
47 if (!outfile.IsValid()) {
48 return false;
49 }
50
51 const int kBufferSize = 32768;
52 std::vector<char> buffer(kBufferSize);
53
54 for (;;) {
55 int bytes_read = infile.ReadAtCurrentPos(&buffer[0], kBufferSize);
56 if (bytes_read < 0)
57 return false;
58 if (bytes_read == 0)
59 break;
60 for (int bytes_written = 0; bytes_written < bytes_read; ) {
61 int bytes_written_partial = outfile.WriteAtCurrentPos(
62 &buffer[bytes_written], bytes_read - bytes_written);
63 if (bytes_written_partial < 0)
64 return false;
65 bytes_written += bytes_written_partial;
66 }
67 }
68
69 return outfile.Flush();
70 }
71
72 } // namespace
73
74 using base::PlatformFile;
75
76 class NativeFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator {
77 public:
NativeFileEnumerator(const base::FilePath & root_path,bool recursive,int file_type)78 NativeFileEnumerator(const base::FilePath& root_path,
79 bool recursive,
80 int file_type)
81 : file_enum_(root_path, recursive, file_type) {
82 }
83
~NativeFileEnumerator()84 virtual ~NativeFileEnumerator() {}
85
86 virtual base::FilePath Next() OVERRIDE;
87 virtual int64 Size() OVERRIDE;
88 virtual base::Time LastModifiedTime() OVERRIDE;
89 virtual bool IsDirectory() OVERRIDE;
90
91 private:
92 base::FileEnumerator file_enum_;
93 base::FileEnumerator::FileInfo file_util_info_;
94 };
95
Next()96 base::FilePath NativeFileEnumerator::Next() {
97 base::FilePath rv = file_enum_.Next();
98 if (!rv.empty())
99 file_util_info_ = file_enum_.GetInfo();
100 return rv;
101 }
102
Size()103 int64 NativeFileEnumerator::Size() {
104 return file_util_info_.GetSize();
105 }
106
LastModifiedTime()107 base::Time NativeFileEnumerator::LastModifiedTime() {
108 return file_util_info_.GetLastModifiedTime();
109 }
110
IsDirectory()111 bool NativeFileEnumerator::IsDirectory() {
112 return file_util_info_.IsDirectory();
113 }
114
CopyOrMoveModeForDestination(const FileSystemURL & dest_url,bool copy)115 NativeFileUtil::CopyOrMoveMode NativeFileUtil::CopyOrMoveModeForDestination(
116 const FileSystemURL& dest_url, bool copy) {
117 if (copy) {
118 return dest_url.mount_option().copy_sync_option() == COPY_SYNC_OPTION_SYNC ?
119 COPY_SYNC : COPY_NOSYNC;
120 }
121 return MOVE;
122 }
123
CreateOrOpen(const base::FilePath & path,int file_flags)124 base::File NativeFileUtil::CreateOrOpen(const base::FilePath& path,
125 int file_flags) {
126 if (!base::DirectoryExists(path.DirName())) {
127 // If its parent does not exist, should return NOT_FOUND error.
128 return base::File(base::File::FILE_ERROR_NOT_FOUND);
129 }
130
131 // TODO(rvargas): Check |file_flags| instead. See bug 356358.
132 if (base::DirectoryExists(path))
133 return base::File(base::File::FILE_ERROR_NOT_A_FILE);
134
135 return base::File(path, file_flags);
136 }
137
EnsureFileExists(const base::FilePath & path,bool * created)138 base::File::Error NativeFileUtil::EnsureFileExists(
139 const base::FilePath& path,
140 bool* created) {
141 if (!base::DirectoryExists(path.DirName()))
142 // If its parent does not exist, should return NOT_FOUND error.
143 return base::File::FILE_ERROR_NOT_FOUND;
144
145 // Tries to create the |path| exclusively. This should fail
146 // with base::File::FILE_ERROR_EXISTS if the path already exists.
147 base::File file(path, base::File::FLAG_CREATE | base::File::FLAG_READ);
148
149 if (file.IsValid()) {
150 if (created)
151 *created = file.created();
152 return base::File::FILE_OK;
153 }
154
155 base::File::Error error_code = file.error_details();
156 if (error_code == base::File::FILE_ERROR_EXISTS) {
157 // Make sure created_ is false.
158 if (created)
159 *created = false;
160 error_code = base::File::FILE_OK;
161 }
162 return error_code;
163 }
164
CreateDirectory(const base::FilePath & path,bool exclusive,bool recursive)165 base::File::Error NativeFileUtil::CreateDirectory(
166 const base::FilePath& path,
167 bool exclusive,
168 bool recursive) {
169 // If parent dir of file doesn't exist.
170 if (!recursive && !base::PathExists(path.DirName()))
171 return base::File::FILE_ERROR_NOT_FOUND;
172
173 bool path_exists = base::PathExists(path);
174 if (exclusive && path_exists)
175 return base::File::FILE_ERROR_EXISTS;
176
177 // If file exists at the path.
178 if (path_exists && !base::DirectoryExists(path))
179 return base::File::FILE_ERROR_EXISTS;
180
181 if (!base::CreateDirectory(path))
182 return base::File::FILE_ERROR_FAILED;
183
184 if (!SetPlatformSpecificDirectoryPermissions(path)) {
185 // Since some file systems don't support permission setting, we do not treat
186 // an error from the function as the failure of copying. Just log it.
187 LOG(WARNING) << "Setting directory permission failed: "
188 << path.AsUTF8Unsafe();
189 }
190
191 return base::File::FILE_OK;
192 }
193
GetFileInfo(const base::FilePath & path,base::File::Info * file_info)194 base::File::Error NativeFileUtil::GetFileInfo(
195 const base::FilePath& path,
196 base::File::Info* file_info) {
197 if (!base::PathExists(path))
198 return base::File::FILE_ERROR_NOT_FOUND;
199
200 if (!base::GetFileInfo(path, file_info))
201 return base::File::FILE_ERROR_FAILED;
202 return base::File::FILE_OK;
203 }
204
205 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
CreateFileEnumerator(const base::FilePath & root_path,bool recursive)206 NativeFileUtil::CreateFileEnumerator(const base::FilePath& root_path,
207 bool recursive) {
208 return make_scoped_ptr(new NativeFileEnumerator(
209 root_path, recursive,
210 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES))
211 .PassAs<FileSystemFileUtil::AbstractFileEnumerator>();
212 }
213
Touch(const base::FilePath & path,const base::Time & last_access_time,const base::Time & last_modified_time)214 base::File::Error NativeFileUtil::Touch(
215 const base::FilePath& path,
216 const base::Time& last_access_time,
217 const base::Time& last_modified_time) {
218 if (!base::TouchFile(path, last_access_time, last_modified_time))
219 return base::File::FILE_ERROR_FAILED;
220 return base::File::FILE_OK;
221 }
222
Truncate(const base::FilePath & path,int64 length)223 base::File::Error NativeFileUtil::Truncate(const base::FilePath& path,
224 int64 length) {
225 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
226 if (!file.IsValid())
227 return file.error_details();
228
229 if (!file.SetLength(length))
230 return base::File::FILE_ERROR_FAILED;
231
232 return base::File::FILE_OK;
233 }
234
PathExists(const base::FilePath & path)235 bool NativeFileUtil::PathExists(const base::FilePath& path) {
236 return base::PathExists(path);
237 }
238
DirectoryExists(const base::FilePath & path)239 bool NativeFileUtil::DirectoryExists(const base::FilePath& path) {
240 return base::DirectoryExists(path);
241 }
242
CopyOrMoveFile(const base::FilePath & src_path,const base::FilePath & dest_path,FileSystemOperation::CopyOrMoveOption option,CopyOrMoveMode mode)243 base::File::Error NativeFileUtil::CopyOrMoveFile(
244 const base::FilePath& src_path,
245 const base::FilePath& dest_path,
246 FileSystemOperation::CopyOrMoveOption option,
247 CopyOrMoveMode mode) {
248 base::File::Info info;
249 base::File::Error error = NativeFileUtil::GetFileInfo(src_path, &info);
250 if (error != base::File::FILE_OK)
251 return error;
252 if (info.is_directory)
253 return base::File::FILE_ERROR_NOT_A_FILE;
254 base::Time last_modified = info.last_modified;
255
256 error = NativeFileUtil::GetFileInfo(dest_path, &info);
257 if (error != base::File::FILE_OK &&
258 error != base::File::FILE_ERROR_NOT_FOUND)
259 return error;
260 if (info.is_directory)
261 return base::File::FILE_ERROR_INVALID_OPERATION;
262 if (error == base::File::FILE_ERROR_NOT_FOUND) {
263 error = NativeFileUtil::GetFileInfo(dest_path.DirName(), &info);
264 if (error != base::File::FILE_OK)
265 return error;
266 if (!info.is_directory)
267 return base::File::FILE_ERROR_NOT_FOUND;
268 }
269
270 switch (mode) {
271 case COPY_NOSYNC:
272 if (!base::CopyFile(src_path, dest_path))
273 return base::File::FILE_ERROR_FAILED;
274 break;
275 case COPY_SYNC:
276 if (!CopyFileAndSync(src_path, dest_path))
277 return base::File::FILE_ERROR_FAILED;
278 break;
279 case MOVE:
280 if (!base::Move(src_path, dest_path))
281 return base::File::FILE_ERROR_FAILED;
282 break;
283 }
284
285 // Preserve the last modified time. Do not return error here even if
286 // the setting is failed, because the copy itself is successfully done.
287 if (option == FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED)
288 base::TouchFile(dest_path, last_modified, last_modified);
289
290 return base::File::FILE_OK;
291 }
292
DeleteFile(const base::FilePath & path)293 base::File::Error NativeFileUtil::DeleteFile(const base::FilePath& path) {
294 if (!base::PathExists(path))
295 return base::File::FILE_ERROR_NOT_FOUND;
296 if (base::DirectoryExists(path))
297 return base::File::FILE_ERROR_NOT_A_FILE;
298 if (!base::DeleteFile(path, false))
299 return base::File::FILE_ERROR_FAILED;
300 return base::File::FILE_OK;
301 }
302
DeleteDirectory(const base::FilePath & path)303 base::File::Error NativeFileUtil::DeleteDirectory(const base::FilePath& path) {
304 if (!base::PathExists(path))
305 return base::File::FILE_ERROR_NOT_FOUND;
306 if (!base::DirectoryExists(path))
307 return base::File::FILE_ERROR_NOT_A_DIRECTORY;
308 if (!base::IsDirectoryEmpty(path))
309 return base::File::FILE_ERROR_NOT_EMPTY;
310 if (!base::DeleteFile(path, false))
311 return base::File::FILE_ERROR_FAILED;
312 return base::File::FILE_OK;
313 }
314
315 } // namespace fileapi
316