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 "storage/browser/fileapi/native_file_util.h"
7 #include "base/files/file_enumerator.h"
8 #include "base/files/file_util.h"
9 #include "storage/browser/fileapi/file_system_operation_context.h"
10 #include "storage/browser/fileapi/file_system_url.h"
11 #include "storage/common/fileapi/file_system_mount_option.h"
17 // Sets permissions on directory at |dir_path| based on the target platform.
18 // Returns true on success, or false otherwise.
20 // TODO(benchan): Find a better place outside webkit to host this function.
21 bool SetPlatformSpecificDirectoryPermissions(const base::FilePath
& dir_path
) {
22 #if defined(OS_CHROMEOS)
23 // System daemons on Chrome OS may run as a user different than the Chrome
24 // process but need to access files under the directories created here.
25 // Because of that, grant the execute permission on the created directory
26 // to group and other users.
27 if (HANDLE_EINTR(chmod(dir_path
.value().c_str(),
28 S_IRWXU
| S_IXGRP
| S_IXOTH
)) != 0) {
32 // Keep the directory permissions unchanged on non-Chrome OS platforms.
36 // Copies a file |from| to |to|, and ensure the written content is synced to
37 // the disk. This is essentially base::CopyFile followed by fsync().
38 bool CopyFileAndSync(const base::FilePath
& from
, const base::FilePath
& to
) {
39 base::File
infile(from
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
40 if (!infile
.IsValid()) {
44 base::File
outfile(to
,
45 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
46 if (!outfile
.IsValid()) {
50 const int kBufferSize
= 32768;
51 std::vector
<char> buffer(kBufferSize
);
54 int bytes_read
= infile
.ReadAtCurrentPos(&buffer
[0], kBufferSize
);
59 for (int bytes_written
= 0; bytes_written
< bytes_read
; ) {
60 int bytes_written_partial
= outfile
.WriteAtCurrentPos(
61 &buffer
[bytes_written
], bytes_read
- bytes_written
);
62 if (bytes_written_partial
< 0)
64 bytes_written
+= bytes_written_partial
;
68 return outfile
.Flush();
73 using base::PlatformFile
;
75 class NativeFileEnumerator
: public FileSystemFileUtil::AbstractFileEnumerator
{
77 NativeFileEnumerator(const base::FilePath
& root_path
,
80 : file_enum_(root_path
, recursive
, file_type
) {
83 ~NativeFileEnumerator() override
{}
85 base::FilePath
Next() override
;
86 int64
Size() override
;
87 base::Time
LastModifiedTime() override
;
88 bool IsDirectory() override
;
91 base::FileEnumerator file_enum_
;
92 base::FileEnumerator::FileInfo file_util_info_
;
95 base::FilePath
NativeFileEnumerator::Next() {
96 base::FilePath rv
= file_enum_
.Next();
98 file_util_info_
= file_enum_
.GetInfo();
102 int64
NativeFileEnumerator::Size() {
103 return file_util_info_
.GetSize();
106 base::Time
NativeFileEnumerator::LastModifiedTime() {
107 return file_util_info_
.GetLastModifiedTime();
110 bool NativeFileEnumerator::IsDirectory() {
111 return file_util_info_
.IsDirectory();
114 NativeFileUtil::CopyOrMoveMode
NativeFileUtil::CopyOrMoveModeForDestination(
115 const FileSystemURL
& dest_url
, bool copy
) {
117 return dest_url
.mount_option().flush_policy() ==
118 FlushPolicy::FLUSH_ON_COMPLETION
125 base::File
NativeFileUtil::CreateOrOpen(const base::FilePath
& path
,
127 if (!base::DirectoryExists(path
.DirName())) {
128 // If its parent does not exist, should return NOT_FOUND error.
129 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
132 // TODO(rvargas): Check |file_flags| instead. See bug 356358.
133 if (base::DirectoryExists(path
))
134 return base::File(base::File::FILE_ERROR_NOT_A_FILE
);
136 return base::File(path
, file_flags
);
139 base::File::Error
NativeFileUtil::EnsureFileExists(
140 const base::FilePath
& path
,
142 if (!base::DirectoryExists(path
.DirName()))
143 // If its parent does not exist, should return NOT_FOUND error.
144 return base::File::FILE_ERROR_NOT_FOUND
;
146 // Tries to create the |path| exclusively. This should fail
147 // with base::File::FILE_ERROR_EXISTS if the path already exists.
148 base::File
file(path
, base::File::FLAG_CREATE
| base::File::FLAG_READ
);
150 if (file
.IsValid()) {
152 *created
= file
.created();
153 return base::File::FILE_OK
;
156 base::File::Error error_code
= file
.error_details();
157 if (error_code
== base::File::FILE_ERROR_EXISTS
) {
158 // Make sure created_ is false.
161 error_code
= base::File::FILE_OK
;
166 base::File::Error
NativeFileUtil::CreateDirectory(
167 const base::FilePath
& path
,
170 // If parent dir of file doesn't exist.
171 if (!recursive
&& !base::PathExists(path
.DirName()))
172 return base::File::FILE_ERROR_NOT_FOUND
;
174 bool path_exists
= base::PathExists(path
);
175 if (exclusive
&& path_exists
)
176 return base::File::FILE_ERROR_EXISTS
;
178 // If file exists at the path.
179 if (path_exists
&& !base::DirectoryExists(path
))
180 return base::File::FILE_ERROR_EXISTS
;
182 if (!base::CreateDirectory(path
))
183 return base::File::FILE_ERROR_FAILED
;
185 if (!SetPlatformSpecificDirectoryPermissions(path
)) {
186 // Since some file systems don't support permission setting, we do not treat
187 // an error from the function as the failure of copying. Just log it.
188 LOG(WARNING
) << "Setting directory permission failed: "
189 << path
.AsUTF8Unsafe();
192 return base::File::FILE_OK
;
195 base::File::Error
NativeFileUtil::GetFileInfo(
196 const base::FilePath
& path
,
197 base::File::Info
* file_info
) {
198 if (!base::PathExists(path
))
199 return base::File::FILE_ERROR_NOT_FOUND
;
201 if (!base::GetFileInfo(path
, file_info
))
202 return base::File::FILE_ERROR_FAILED
;
203 return base::File::FILE_OK
;
206 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
207 NativeFileUtil::CreateFileEnumerator(const base::FilePath
& root_path
,
209 return make_scoped_ptr(new NativeFileEnumerator(
212 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
));
215 base::File::Error
NativeFileUtil::Touch(
216 const base::FilePath
& path
,
217 const base::Time
& last_access_time
,
218 const base::Time
& last_modified_time
) {
219 if (!base::TouchFile(path
, last_access_time
, last_modified_time
))
220 return base::File::FILE_ERROR_FAILED
;
221 return base::File::FILE_OK
;
224 base::File::Error
NativeFileUtil::Truncate(const base::FilePath
& path
,
226 base::File
file(path
, base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
228 return file
.error_details();
230 if (!file
.SetLength(length
))
231 return base::File::FILE_ERROR_FAILED
;
233 return base::File::FILE_OK
;
236 bool NativeFileUtil::PathExists(const base::FilePath
& path
) {
237 return base::PathExists(path
);
240 bool NativeFileUtil::DirectoryExists(const base::FilePath
& path
) {
241 return base::DirectoryExists(path
);
244 base::File::Error
NativeFileUtil::CopyOrMoveFile(
245 const base::FilePath
& src_path
,
246 const base::FilePath
& dest_path
,
247 FileSystemOperation::CopyOrMoveOption option
,
248 CopyOrMoveMode mode
) {
249 base::File::Info info
;
250 base::File::Error error
= NativeFileUtil::GetFileInfo(src_path
, &info
);
251 if (error
!= base::File::FILE_OK
)
253 if (info
.is_directory
)
254 return base::File::FILE_ERROR_NOT_A_FILE
;
255 base::Time last_modified
= info
.last_modified
;
257 error
= NativeFileUtil::GetFileInfo(dest_path
, &info
);
258 if (error
!= base::File::FILE_OK
&&
259 error
!= base::File::FILE_ERROR_NOT_FOUND
)
261 if (info
.is_directory
)
262 return base::File::FILE_ERROR_INVALID_OPERATION
;
263 if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
264 error
= NativeFileUtil::GetFileInfo(dest_path
.DirName(), &info
);
265 if (error
!= base::File::FILE_OK
)
267 if (!info
.is_directory
)
268 return base::File::FILE_ERROR_NOT_FOUND
;
273 if (!base::CopyFile(src_path
, dest_path
))
274 return base::File::FILE_ERROR_FAILED
;
277 if (!CopyFileAndSync(src_path
, dest_path
))
278 return base::File::FILE_ERROR_FAILED
;
281 if (!base::Move(src_path
, dest_path
))
282 return base::File::FILE_ERROR_FAILED
;
286 // Preserve the last modified time. Do not return error here even if
287 // the setting is failed, because the copy itself is successfully done.
288 if (option
== FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED
)
289 base::TouchFile(dest_path
, last_modified
, last_modified
);
291 return base::File::FILE_OK
;
294 base::File::Error
NativeFileUtil::DeleteFile(const base::FilePath
& path
) {
295 if (!base::PathExists(path
))
296 return base::File::FILE_ERROR_NOT_FOUND
;
297 if (base::DirectoryExists(path
))
298 return base::File::FILE_ERROR_NOT_A_FILE
;
299 if (!base::DeleteFile(path
, false))
300 return base::File::FILE_ERROR_FAILED
;
301 return base::File::FILE_OK
;
304 base::File::Error
NativeFileUtil::DeleteDirectory(const base::FilePath
& path
) {
305 if (!base::PathExists(path
))
306 return base::File::FILE_ERROR_NOT_FOUND
;
307 if (!base::DirectoryExists(path
))
308 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
309 if (!base::IsDirectoryEmpty(path
))
310 return base::File::FILE_ERROR_NOT_EMPTY
;
311 if (!base::DeleteFile(path
, false))
312 return base::File::FILE_ERROR_FAILED
;
313 return base::File::FILE_OK
;
316 } // namespace storage