OLD | NEW |
| (Empty) |
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 storage { | |
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. | |
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(). | |
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: | |
78 NativeFileEnumerator(const base::FilePath& root_path, | |
79 bool recursive, | |
80 int file_type) | |
81 : file_enum_(root_path, recursive, file_type) { | |
82 } | |
83 | |
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 | |
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 | |
103 int64 NativeFileEnumerator::Size() { | |
104 return file_util_info_.GetSize(); | |
105 } | |
106 | |
107 base::Time NativeFileEnumerator::LastModifiedTime() { | |
108 return file_util_info_.GetLastModifiedTime(); | |
109 } | |
110 | |
111 bool NativeFileEnumerator::IsDirectory() { | |
112 return file_util_info_.IsDirectory(); | |
113 } | |
114 | |
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 | |
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 | |
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 | |
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 | |
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> | |
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 | |
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 | |
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 | |
235 bool NativeFileUtil::PathExists(const base::FilePath& path) { | |
236 return base::PathExists(path); | |
237 } | |
238 | |
239 bool NativeFileUtil::DirectoryExists(const base::FilePath& path) { | |
240 return base::DirectoryExists(path); | |
241 } | |
242 | |
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 | |
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 | |
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 storage | |
OLD | NEW |