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_enumerator.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "net/base/file_stream.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 net::FileStream infile(NULL);
41 if (infile.OpenSync(from,
42 base::PLATFORM_FILE_OPEN | base:: PLATFORM_FILE_READ) < 0) {
43 return false;
44 }
45
46 net::FileStream outfile(NULL);
47 if (outfile.OpenSync(to,
48 base::PLATFORM_FILE_CREATE_ALWAYS | base:: PLATFORM_FILE_WRITE) < 0) {
49 return false;
50 }
51
52 const int kBufferSize = 32768;
53 std::vector<char> buffer(kBufferSize);
54
55 for (;;) {
56 int bytes_read = infile.ReadSync(&buffer[0], kBufferSize);
57 if (bytes_read < 0)
58 return false;
59 if (bytes_read == 0)
60 break;
61 for (int bytes_written = 0; bytes_written < bytes_read; ) {
62 int bytes_written_partial = outfile.WriteSync(&buffer[bytes_written],
63 bytes_read - bytes_written);
64 if (bytes_written_partial < 0)
65 return false;
66 bytes_written += bytes_written_partial;
67 }
68 }
69
70 return outfile.FlushSync() >= 0;
71 }
72
73 } // namespace
74
75 using base::PlatformFile;
76 using base::PlatformFileError;
77
78 class NativeFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator {
79 public:
NativeFileEnumerator(const base::FilePath & root_path,bool recursive,int file_type)80 NativeFileEnumerator(const base::FilePath& root_path,
81 bool recursive,
82 int file_type)
83 : file_enum_(root_path, recursive, file_type) {
84 }
85
~NativeFileEnumerator()86 virtual ~NativeFileEnumerator() {}
87
88 virtual base::FilePath Next() OVERRIDE;
89 virtual int64 Size() OVERRIDE;
90 virtual base::Time LastModifiedTime() OVERRIDE;
91 virtual bool IsDirectory() OVERRIDE;
92
93 private:
94 base::FileEnumerator file_enum_;
95 base::FileEnumerator::FileInfo file_util_info_;
96 };
97
Next()98 base::FilePath NativeFileEnumerator::Next() {
99 base::FilePath rv = file_enum_.Next();
100 if (!rv.empty())
101 file_util_info_ = file_enum_.GetInfo();
102 return rv;
103 }
104
Size()105 int64 NativeFileEnumerator::Size() {
106 return file_util_info_.GetSize();
107 }
108
LastModifiedTime()109 base::Time NativeFileEnumerator::LastModifiedTime() {
110 return file_util_info_.GetLastModifiedTime();
111 }
112
IsDirectory()113 bool NativeFileEnumerator::IsDirectory() {
114 return file_util_info_.IsDirectory();
115 }
116
CopyOrMoveModeForDestination(const FileSystemURL & dest_url,bool copy)117 NativeFileUtil::CopyOrMoveMode NativeFileUtil::CopyOrMoveModeForDestination(
118 const FileSystemURL& dest_url, bool copy) {
119 if (copy) {
120 return dest_url.mount_option().copy_sync_option() == COPY_SYNC_OPTION_SYNC ?
121 COPY_SYNC : COPY_NOSYNC;
122 }
123 return MOVE;
124 }
125
CreateOrOpen(const base::FilePath & path,int file_flags,PlatformFile * file_handle,bool * created)126 PlatformFileError NativeFileUtil::CreateOrOpen(
127 const base::FilePath& path, int file_flags,
128 PlatformFile* file_handle, bool* created) {
129 if (!base::DirectoryExists(path.DirName())) {
130 // If its parent does not exist, should return NOT_FOUND error.
131 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
132 }
133 if (base::DirectoryExists(path))
134 return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
135 PlatformFileError error_code = base::PLATFORM_FILE_OK;
136 *file_handle = base::CreatePlatformFile(path, file_flags,
137 created, &error_code);
138 return error_code;
139 }
140
Close(PlatformFile file_handle)141 PlatformFileError NativeFileUtil::Close(PlatformFile file_handle) {
142 if (!base::ClosePlatformFile(file_handle))
143 return base::PLATFORM_FILE_ERROR_FAILED;
144 return base::PLATFORM_FILE_OK;
145 }
146
EnsureFileExists(const base::FilePath & path,bool * created)147 PlatformFileError NativeFileUtil::EnsureFileExists(
148 const base::FilePath& path,
149 bool* created) {
150 if (!base::DirectoryExists(path.DirName()))
151 // If its parent does not exist, should return NOT_FOUND error.
152 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
153 PlatformFileError error_code = base::PLATFORM_FILE_OK;
154 // Tries to create the |path| exclusively. This should fail
155 // with base::PLATFORM_FILE_ERROR_EXISTS if the path already exists.
156 PlatformFile handle = base::CreatePlatformFile(
157 path,
158 base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
159 created, &error_code);
160 if (error_code == base::PLATFORM_FILE_ERROR_EXISTS) {
161 // Make sure created_ is false.
162 if (created)
163 *created = false;
164 error_code = base::PLATFORM_FILE_OK;
165 }
166 if (handle != base::kInvalidPlatformFileValue)
167 base::ClosePlatformFile(handle);
168 return error_code;
169 }
170
CreateDirectory(const base::FilePath & path,bool exclusive,bool recursive)171 PlatformFileError NativeFileUtil::CreateDirectory(
172 const base::FilePath& path,
173 bool exclusive,
174 bool recursive) {
175 // If parent dir of file doesn't exist.
176 if (!recursive && !base::PathExists(path.DirName()))
177 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
178
179 bool path_exists = base::PathExists(path);
180 if (exclusive && path_exists)
181 return base::PLATFORM_FILE_ERROR_EXISTS;
182
183 // If file exists at the path.
184 if (path_exists && !base::DirectoryExists(path))
185 return base::PLATFORM_FILE_ERROR_EXISTS;
186
187 if (!base::CreateDirectory(path))
188 return base::PLATFORM_FILE_ERROR_FAILED;
189
190 if (!SetPlatformSpecificDirectoryPermissions(path)) {
191 // Since some file systems don't support permission setting, we do not treat
192 // an error from the function as the failure of copying. Just log it.
193 LOG(WARNING) << "Setting directory permission failed: "
194 << path.AsUTF8Unsafe();
195 }
196
197 return base::PLATFORM_FILE_OK;
198 }
199
GetFileInfo(const base::FilePath & path,base::PlatformFileInfo * file_info)200 PlatformFileError NativeFileUtil::GetFileInfo(
201 const base::FilePath& path,
202 base::PlatformFileInfo* file_info) {
203 if (!base::PathExists(path))
204 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
205 if (!base::GetFileInfo(path, file_info))
206 return base::PLATFORM_FILE_ERROR_FAILED;
207 return base::PLATFORM_FILE_OK;
208 }
209
210 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
CreateFileEnumerator(const base::FilePath & root_path,bool recursive)211 NativeFileUtil::CreateFileEnumerator(const base::FilePath& root_path,
212 bool recursive) {
213 return make_scoped_ptr(new NativeFileEnumerator(
214 root_path, recursive,
215 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES))
216 .PassAs<FileSystemFileUtil::AbstractFileEnumerator>();
217 }
218
Touch(const base::FilePath & path,const base::Time & last_access_time,const base::Time & last_modified_time)219 PlatformFileError NativeFileUtil::Touch(
220 const base::FilePath& path,
221 const base::Time& last_access_time,
222 const base::Time& last_modified_time) {
223 if (!base::TouchFile(path, last_access_time, last_modified_time))
224 return base::PLATFORM_FILE_ERROR_FAILED;
225 return base::PLATFORM_FILE_OK;
226 }
227
Truncate(const base::FilePath & path,int64 length)228 PlatformFileError NativeFileUtil::Truncate(
229 const base::FilePath& path, int64 length) {
230 PlatformFileError error_code(base::PLATFORM_FILE_ERROR_FAILED);
231 PlatformFile file =
232 base::CreatePlatformFile(
233 path,
234 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
235 NULL,
236 &error_code);
237 if (error_code != base::PLATFORM_FILE_OK) {
238 return error_code;
239 }
240 DCHECK_NE(base::kInvalidPlatformFileValue, file);
241 if (!base::TruncatePlatformFile(file, length))
242 error_code = base::PLATFORM_FILE_ERROR_FAILED;
243 base::ClosePlatformFile(file);
244 return error_code;
245 }
246
PathExists(const base::FilePath & path)247 bool NativeFileUtil::PathExists(const base::FilePath& path) {
248 return base::PathExists(path);
249 }
250
DirectoryExists(const base::FilePath & path)251 bool NativeFileUtil::DirectoryExists(const base::FilePath& path) {
252 return base::DirectoryExists(path);
253 }
254
CopyOrMoveFile(const base::FilePath & src_path,const base::FilePath & dest_path,FileSystemOperation::CopyOrMoveOption option,CopyOrMoveMode mode)255 PlatformFileError NativeFileUtil::CopyOrMoveFile(
256 const base::FilePath& src_path,
257 const base::FilePath& dest_path,
258 FileSystemOperation::CopyOrMoveOption option,
259 CopyOrMoveMode mode) {
260 base::PlatformFileInfo info;
261 base::PlatformFileError error = NativeFileUtil::GetFileInfo(src_path, &info);
262 if (error != base::PLATFORM_FILE_OK)
263 return error;
264 if (info.is_directory)
265 return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
266 base::Time last_modified = info.last_modified;
267
268 error = NativeFileUtil::GetFileInfo(dest_path, &info);
269 if (error != base::PLATFORM_FILE_OK &&
270 error != base::PLATFORM_FILE_ERROR_NOT_FOUND)
271 return error;
272 if (info.is_directory)
273 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
274 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
275 error = NativeFileUtil::GetFileInfo(dest_path.DirName(), &info);
276 if (error != base::PLATFORM_FILE_OK)
277 return error;
278 if (!info.is_directory)
279 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
280 }
281
282 switch (mode) {
283 case COPY_NOSYNC:
284 if (!base::CopyFile(src_path, dest_path))
285 return base::PLATFORM_FILE_ERROR_FAILED;
286 break;
287 case COPY_SYNC:
288 if (!CopyFileAndSync(src_path, dest_path))
289 return base::PLATFORM_FILE_ERROR_FAILED;
290 break;
291 case MOVE:
292 if (!base::Move(src_path, dest_path))
293 return base::PLATFORM_FILE_ERROR_FAILED;
294 break;
295 }
296
297 // Preserve the last modified time. Do not return error here even if
298 // the setting is failed, because the copy itself is successfully done.
299 if (option == FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED)
300 base::TouchFile(dest_path, last_modified, last_modified);
301
302 return base::PLATFORM_FILE_OK;
303 }
304
DeleteFile(const base::FilePath & path)305 PlatformFileError NativeFileUtil::DeleteFile(const base::FilePath& path) {
306 if (!base::PathExists(path))
307 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
308 if (base::DirectoryExists(path))
309 return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
310 if (!base::DeleteFile(path, false))
311 return base::PLATFORM_FILE_ERROR_FAILED;
312 return base::PLATFORM_FILE_OK;
313 }
314
DeleteDirectory(const base::FilePath & path)315 PlatformFileError NativeFileUtil::DeleteDirectory(const base::FilePath& path) {
316 if (!base::PathExists(path))
317 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
318 if (!base::DirectoryExists(path))
319 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
320 if (!base::IsDirectoryEmpty(path))
321 return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
322 if (!base::DeleteFile(path, false))
323 return base::PLATFORM_FILE_ERROR_FAILED;
324 return base::PLATFORM_FILE_OK;
325 }
326
327 } // namespace fileapi
328