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.h"
8 #include "base/files/file_enumerator.h"
9 #include "base/files/file_util.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "storage/browser/fileapi/file_system_operation_context.h"
12 #include "storage/browser/fileapi/file_system_url.h"
13 #include "storage/common/fileapi/file_system_mount_option.h"
19 // Sets permissions on directory at |dir_path| based on the target platform.
20 // Returns true on success, or false otherwise.
22 // TODO(benchan): Find a better place outside webkit to host this function.
23 bool SetPlatformSpecificDirectoryPermissions(const base::FilePath
& dir_path
) {
24 #if defined(OS_CHROMEOS)
25 // System daemons on Chrome OS may run as a user different than the Chrome
26 // process but need to access files under the directories created here.
27 // Because of that, grant the execute permission on the created directory
28 // to group and other users.
29 if (HANDLE_EINTR(chmod(dir_path
.value().c_str(),
30 S_IRWXU
| S_IXGRP
| S_IXOTH
)) != 0) {
34 // Keep the directory permissions unchanged on non-Chrome OS platforms.
38 // Copies a file |from| to |to|, and ensure the written content is synced to
39 // the disk. This is essentially base::CopyFile followed by fsync().
40 bool CopyFileAndSync(const base::FilePath
& from
, const base::FilePath
& to
) {
41 base::File
infile(from
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
42 if (!infile
.IsValid()) {
46 base::File
outfile(to
,
47 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
48 if (!outfile
.IsValid()) {
52 const int kBufferSize
= 32768;
53 std::vector
<char> buffer(kBufferSize
);
56 int bytes_read
= infile
.ReadAtCurrentPos(&buffer
[0], kBufferSize
);
61 for (int bytes_written
= 0; bytes_written
< bytes_read
; ) {
62 int bytes_written_partial
= outfile
.WriteAtCurrentPos(
63 &buffer
[bytes_written
], bytes_read
- bytes_written
);
64 if (bytes_written_partial
< 0)
66 bytes_written
+= bytes_written_partial
;
70 return outfile
.Flush();
75 using base::PlatformFile
;
77 class NativeFileEnumerator
: public FileSystemFileUtil::AbstractFileEnumerator
{
79 NativeFileEnumerator(const base::FilePath
& root_path
,
82 : file_enum_(root_path
, recursive
, file_type
) {
85 ~NativeFileEnumerator() override
{}
87 base::FilePath
Next() override
;
88 int64
Size() override
;
89 base::Time
LastModifiedTime() override
;
90 bool IsDirectory() override
;
93 base::FileEnumerator file_enum_
;
94 base::FileEnumerator::FileInfo file_util_info_
;
97 base::FilePath
NativeFileEnumerator::Next() {
98 base::FilePath rv
= file_enum_
.Next();
100 file_util_info_
= file_enum_
.GetInfo();
104 int64
NativeFileEnumerator::Size() {
105 return file_util_info_
.GetSize();
108 base::Time
NativeFileEnumerator::LastModifiedTime() {
109 return file_util_info_
.GetLastModifiedTime();
112 bool NativeFileEnumerator::IsDirectory() {
113 return file_util_info_
.IsDirectory();
116 NativeFileUtil::CopyOrMoveMode
NativeFileUtil::CopyOrMoveModeForDestination(
117 const FileSystemURL
& dest_url
, bool copy
) {
119 return dest_url
.mount_option().flush_policy() ==
120 FlushPolicy::FLUSH_ON_COMPLETION
127 base::File
NativeFileUtil::CreateOrOpen(const base::FilePath
& path
,
129 if (!base::DirectoryExists(path
.DirName())) {
130 // If its parent does not exist, should return NOT_FOUND error.
131 return base::File(base::File::FILE_ERROR_NOT_FOUND
);
134 // TODO(rvargas): Check |file_flags| instead. See bug 356358.
135 if (base::DirectoryExists(path
))
136 return base::File(base::File::FILE_ERROR_NOT_A_FILE
);
138 return base::File(path
, file_flags
);
141 base::File::Error
NativeFileUtil::EnsureFileExists(
142 const base::FilePath
& path
,
144 if (!base::DirectoryExists(path
.DirName()))
145 // If its parent does not exist, should return NOT_FOUND error.
146 return base::File::FILE_ERROR_NOT_FOUND
;
148 // Tries to create the |path| exclusively. This should fail
149 // with base::File::FILE_ERROR_EXISTS if the path already exists.
150 base::File
file(path
, base::File::FLAG_CREATE
| base::File::FLAG_READ
);
152 if (file
.IsValid()) {
154 *created
= file
.created();
155 return base::File::FILE_OK
;
158 base::File::Error error_code
= file
.error_details();
159 if (error_code
== base::File::FILE_ERROR_EXISTS
) {
160 // Make sure created_ is false.
163 error_code
= base::File::FILE_OK
;
168 base::File::Error
NativeFileUtil::CreateDirectory(
169 const base::FilePath
& path
,
172 // If parent dir of file doesn't exist.
173 if (!recursive
&& !base::PathExists(path
.DirName()))
174 return base::File::FILE_ERROR_NOT_FOUND
;
176 bool path_exists
= base::PathExists(path
);
177 if (exclusive
&& path_exists
)
178 return base::File::FILE_ERROR_EXISTS
;
180 // If file exists at the path.
181 if (path_exists
&& !base::DirectoryExists(path
))
182 return base::File::FILE_ERROR_EXISTS
;
184 if (!base::CreateDirectory(path
))
185 return base::File::FILE_ERROR_FAILED
;
187 if (!SetPlatformSpecificDirectoryPermissions(path
)) {
188 // Since some file systems don't support permission setting, we do not treat
189 // an error from the function as the failure of copying. Just log it.
190 LOG(WARNING
) << "Setting directory permission failed: "
191 << path
.AsUTF8Unsafe();
194 return base::File::FILE_OK
;
197 base::File::Error
NativeFileUtil::GetFileInfo(
198 const base::FilePath
& path
,
199 base::File::Info
* file_info
) {
200 if (!base::PathExists(path
))
201 return base::File::FILE_ERROR_NOT_FOUND
;
203 if (!base::GetFileInfo(path
, file_info
))
204 return base::File::FILE_ERROR_FAILED
;
205 return base::File::FILE_OK
;
208 scoped_ptr
<FileSystemFileUtil::AbstractFileEnumerator
>
209 NativeFileUtil::CreateFileEnumerator(const base::FilePath
& root_path
,
211 return make_scoped_ptr(new NativeFileEnumerator(
214 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
));
217 base::File::Error
NativeFileUtil::Touch(
218 const base::FilePath
& path
,
219 const base::Time
& last_access_time
,
220 const base::Time
& last_modified_time
) {
221 if (!base::TouchFile(path
, last_access_time
, last_modified_time
))
222 return base::File::FILE_ERROR_FAILED
;
223 return base::File::FILE_OK
;
226 base::File::Error
NativeFileUtil::Truncate(const base::FilePath
& path
,
228 base::File
file(path
, base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
230 return file
.error_details();
232 if (!file
.SetLength(length
))
233 return base::File::FILE_ERROR_FAILED
;
235 return base::File::FILE_OK
;
238 bool NativeFileUtil::PathExists(const base::FilePath
& path
) {
239 return base::PathExists(path
);
242 bool NativeFileUtil::DirectoryExists(const base::FilePath
& path
) {
243 return base::DirectoryExists(path
);
246 base::File::Error
NativeFileUtil::CopyOrMoveFile(
247 const base::FilePath
& src_path
,
248 const base::FilePath
& dest_path
,
249 FileSystemOperation::CopyOrMoveOption option
,
250 CopyOrMoveMode mode
) {
251 base::File::Info info
;
252 base::File::Error error
= NativeFileUtil::GetFileInfo(src_path
, &info
);
253 if (error
!= base::File::FILE_OK
)
255 if (info
.is_directory
)
256 return base::File::FILE_ERROR_NOT_A_FILE
;
257 base::Time last_modified
= info
.last_modified
;
259 error
= NativeFileUtil::GetFileInfo(dest_path
, &info
);
260 if (error
!= base::File::FILE_OK
&&
261 error
!= base::File::FILE_ERROR_NOT_FOUND
)
263 if (info
.is_directory
)
264 return base::File::FILE_ERROR_INVALID_OPERATION
;
265 if (error
== base::File::FILE_ERROR_NOT_FOUND
) {
266 error
= NativeFileUtil::GetFileInfo(dest_path
.DirName(), &info
);
267 if (error
!= base::File::FILE_OK
)
269 if (!info
.is_directory
)
270 return base::File::FILE_ERROR_NOT_FOUND
;
275 if (!base::CopyFile(src_path
, dest_path
))
276 return base::File::FILE_ERROR_FAILED
;
279 if (!CopyFileAndSync(src_path
, dest_path
))
280 return base::File::FILE_ERROR_FAILED
;
283 if (!base::Move(src_path
, dest_path
))
284 return base::File::FILE_ERROR_FAILED
;
288 // Preserve the last modified time. Do not return error here even if
289 // the setting is failed, because the copy itself is successfully done.
290 if (option
== FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED
)
291 base::TouchFile(dest_path
, last_modified
, last_modified
);
293 return base::File::FILE_OK
;
296 base::File::Error
NativeFileUtil::DeleteFile(const base::FilePath
& path
) {
297 if (!base::PathExists(path
))
298 return base::File::FILE_ERROR_NOT_FOUND
;
299 if (base::DirectoryExists(path
))
300 return base::File::FILE_ERROR_NOT_A_FILE
;
301 if (!base::DeleteFile(path
, false))
302 return base::File::FILE_ERROR_FAILED
;
303 return base::File::FILE_OK
;
306 base::File::Error
NativeFileUtil::DeleteDirectory(const base::FilePath
& path
) {
307 if (!base::PathExists(path
))
308 return base::File::FILE_ERROR_NOT_FOUND
;
309 if (!base::DirectoryExists(path
))
310 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
311 if (!base::IsDirectoryEmpty(path
))
312 return base::File::FILE_ERROR_NOT_EMPTY
;
313 if (!base::DeleteFile(path
, false))
314 return base::File::FILE_ERROR_FAILED
;
315 return base::File::FILE_OK
;
318 } // namespace storage