Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / storage / browser / fileapi / native_file_util.cc
blob692fc87a2be7de1ca58ec79975393b10f7fc6f53
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"
13 namespace storage {
15 namespace {
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) {
29 return false;
31 #endif
32 // Keep the directory permissions unchanged on non-Chrome OS platforms.
33 return true;
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()) {
41 return false;
44 base::File outfile(to,
45 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
46 if (!outfile.IsValid()) {
47 return false;
50 const int kBufferSize = 32768;
51 std::vector<char> buffer(kBufferSize);
53 for (;;) {
54 int bytes_read = infile.ReadAtCurrentPos(&buffer[0], kBufferSize);
55 if (bytes_read < 0)
56 return false;
57 if (bytes_read == 0)
58 break;
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)
63 return false;
64 bytes_written += bytes_written_partial;
68 return outfile.Flush();
71 } // namespace
73 using base::PlatformFile;
75 class NativeFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator {
76 public:
77 NativeFileEnumerator(const base::FilePath& root_path,
78 bool recursive,
79 int file_type)
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;
90 private:
91 base::FileEnumerator file_enum_;
92 base::FileEnumerator::FileInfo file_util_info_;
95 base::FilePath NativeFileEnumerator::Next() {
96 base::FilePath rv = file_enum_.Next();
97 if (!rv.empty())
98 file_util_info_ = file_enum_.GetInfo();
99 return rv;
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) {
116 if (copy) {
117 return dest_url.mount_option().flush_policy() ==
118 FlushPolicy::FLUSH_ON_COMPLETION
119 ? COPY_SYNC
120 : COPY_NOSYNC;
122 return MOVE;
125 base::File NativeFileUtil::CreateOrOpen(const base::FilePath& path,
126 int file_flags) {
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,
141 bool* created) {
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()) {
151 if (created)
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.
159 if (created)
160 *created = false;
161 error_code = base::File::FILE_OK;
163 return error_code;
166 base::File::Error NativeFileUtil::CreateDirectory(
167 const base::FilePath& path,
168 bool exclusive,
169 bool recursive) {
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,
208 bool recursive) {
209 return make_scoped_ptr(new NativeFileEnumerator(
210 root_path,
211 recursive,
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,
225 int64 length) {
226 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
227 if (!file.IsValid())
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)
252 return error;
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)
260 return error;
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)
266 return error;
267 if (!info.is_directory)
268 return base::File::FILE_ERROR_NOT_FOUND;
271 switch (mode) {
272 case COPY_NOSYNC:
273 if (!base::CopyFile(src_path, dest_path))
274 return base::File::FILE_ERROR_FAILED;
275 break;
276 case COPY_SYNC:
277 if (!CopyFileAndSync(src_path, dest_path))
278 return base::File::FILE_ERROR_FAILED;
279 break;
280 case MOVE:
281 if (!base::Move(src_path, dest_path))
282 return base::File::FILE_ERROR_FAILED;
283 break;
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