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.
5 #include "webkit/browser/fileapi/native_file_util.h"
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"
18 // Sets permissions on directory at |dir_path| based on the target platform.
19 // Returns true on success, or false otherwise.
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) {
33 // Keep the directory permissions unchanged on non-Chrome OS platforms.
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()) {
45 base::File
outfile(to
,
46 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
47 if (!outfile
.IsValid()) {
51 const int kBufferSize
= 32768;
52 std::vector
<char> buffer(kBufferSize
);
55 int bytes_read
= infile
.ReadAtCurrentPos(&buffer
[0], kBufferSize
);
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)
65 bytes_written
+= bytes_written_partial
;
69 return outfile
.Flush();
74 using base::PlatformFile
;
76 class NativeFileEnumerator
: public FileSystemFileUtil::AbstractFileEnumerator
{
78 NativeFileEnumerator(const base::FilePath
& root_path
,
81 : file_enum_(root_path
, recursive
, file_type
) {
84 virtual ~NativeFileEnumerator() {}
86 virtual base::FilePath
Next() OVERRIDE
;
87 virtual int64
Size() OVERRIDE
;
88 virtual base::Time
LastModifiedTime() OVERRIDE
;
89 virtual bool IsDirectory() OVERRIDE
;
92 base::FileEnumerator file_enum_
;
93 base::FileEnumerator::FileInfo file_util_info_
;
96 base::FilePath
NativeFileEnumerator::Next() {
97 base::FilePath rv
= file_enum_
.Next();
99 file_util_info_
= file_enum_
.GetInfo();
103 int64
NativeFileEnumerator::Size() {
104 return file_util_info_
.GetSize();
107 base::Time
NativeFileEnumerator::LastModifiedTime() {
108 return file_util_info_
.GetLastModifiedTime();
111 bool NativeFileEnumerator::IsDirectory() {
112 return file_util_info_
.IsDirectory();
115 NativeFileUtil::CopyOrMoveMode
NativeFileUtil::CopyOrMoveModeForDestination(
116 const FileSystemURL
& dest_url
, bool copy
) {
118 return dest_url
.mount_option().copy_sync_option() == COPY_SYNC_OPTION_SYNC
?
119 COPY_SYNC
: COPY_NOSYNC
;
124 base::File
NativeFileUtil::CreateOrOpen(const base::FilePath
& path
,
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
);
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
);
135 return base::File(path
, file_flags
);
138 base::File::Error
NativeFileUtil::EnsureFileExists(
139 const base::FilePath
& path
,
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
;
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
);
149 if (file
.IsValid()) {
151 *created
= file
.created();
152 return base::File::FILE_OK
;
155 base::File::Error error_code
= file
.error_details();
156 if (error_code
== base::File::FILE_ERROR_EXISTS
) {
157 // Make sure created_ is false.
160 error_code
= base::File::FILE_OK
;
165 base::File::Error
NativeFileUtil::CreateDirectory(
166 const base::FilePath
& path
,
169 // If parent dir of file doesn't exist.
170 if (!recursive
&& !base::PathExists(path
.DirName()))
171 return base::File::FILE_ERROR_NOT_FOUND
;
173 bool path_exists
= base::PathExists(path
);
174 if (exclusive
&& path_exists
)
175 return base::File::FILE_ERROR_EXISTS
;
177 // If file exists at the path.
178 if (path_exists
&& !base::DirectoryExists(path
))
179 return base::File::FILE_ERROR_EXISTS
;
181 if (!base::CreateDirectory(path
))
182 return base::File::FILE_ERROR_FAILED
;
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();
191 return base::File::FILE_OK
;
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
;
200 if (!base::GetFileInfo(path
, file_info
))
201 return base::File::FILE_ERROR_FAILED
;
202 return base::File::FILE_OK
;
205 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
206 NativeFileUtil::CreateFileEnumerator(const base::FilePath
& root_path
,
208 return make_scoped_ptr(new NativeFileEnumerator(
209 root_path
, recursive
,
210 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
))
211 .PassAs
<FileSystemFileUtil::AbstractFileEnumerator
>();
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
;
223 base::File::Error
NativeFileUtil::Truncate(const base::FilePath
& path
,
225 base::File
file(path
, base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
227 return file
.error_details();
229 if (!file
.SetLength(length
))
230 return base::File::FILE_ERROR_FAILED
;
232 return base::File::FILE_OK
;
235 bool NativeFileUtil::PathExists(const base::FilePath
& path
) {
236 return base::PathExists(path
);
239 bool NativeFileUtil::DirectoryExists(const base::FilePath
& path
) {
240 return base::DirectoryExists(path
);
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
)
252 if (info
.is_directory
)
253 return base::File::FILE_ERROR_NOT_A_FILE
;
254 base::Time last_modified
= info
.last_modified
;
256 error
= NativeFileUtil::GetFileInfo(dest_path
, &info
);
257 if (error
!= base::File::FILE_OK
&&
258 error
!= base::File::FILE_ERROR_NOT_FOUND
)
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
)
266 if (!info
.is_directory
)
267 return base::File::FILE_ERROR_NOT_FOUND
;
272 if (!base::CopyFile(src_path
, dest_path
))
273 return base::File::FILE_ERROR_FAILED
;
276 if (!CopyFileAndSync(src_path
, dest_path
))
277 return base::File::FILE_ERROR_FAILED
;
280 if (!base::Move(src_path
, dest_path
))
281 return base::File::FILE_ERROR_FAILED
;
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
);
290 return base::File::FILE_OK
;
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
;
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
;
315 } // namespace fileapi