• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_enumerator.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "net/base/file_stream.h"
11 #include "webkit/browser/fileapi/file_system_operation_context.h"
12 #include "webkit/browser/fileapi/file_system_url.h"
13 
14 namespace fileapi {
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.
SetPlatformSpecificDirectoryPermissions(const base::FilePath & dir_path)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().
CopyFileAndSync(const base::FilePath & from,const base::FilePath & to)39 bool CopyFileAndSync(const base::FilePath& from, const base::FilePath& to) {
40   net::FileStream infile(NULL);
41   if (infile.OpenSync(from,
42           base::PLATFORM_FILE_OPEN | base:: PLATFORM_FILE_READ) < 0) {
43     return false;
44   }
45 
46   net::FileStream outfile(NULL);
47   if (outfile.OpenSync(to,
48           base::PLATFORM_FILE_CREATE_ALWAYS | base:: PLATFORM_FILE_WRITE) < 0) {
49     return false;
50   }
51 
52   const int kBufferSize = 32768;
53   std::vector<char> buffer(kBufferSize);
54 
55   for (;;) {
56     int bytes_read = infile.ReadSync(&buffer[0], kBufferSize);
57     if (bytes_read < 0)
58       return false;
59     if (bytes_read == 0)
60       break;
61     for (int bytes_written = 0; bytes_written < bytes_read; ) {
62       int bytes_written_partial = outfile.WriteSync(&buffer[bytes_written],
63                                                     bytes_read - bytes_written);
64       if (bytes_written_partial < 0)
65         return false;
66       bytes_written += bytes_written_partial;
67     }
68   }
69 
70   return outfile.FlushSync() >= 0;
71 }
72 
73 }  // namespace
74 
75 using base::PlatformFile;
76 using base::PlatformFileError;
77 
78 class NativeFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator {
79  public:
NativeFileEnumerator(const base::FilePath & root_path,bool recursive,int file_type)80   NativeFileEnumerator(const base::FilePath& root_path,
81                        bool recursive,
82                        int file_type)
83     : file_enum_(root_path, recursive, file_type) {
84   }
85 
~NativeFileEnumerator()86   virtual ~NativeFileEnumerator() {}
87 
88   virtual base::FilePath Next() OVERRIDE;
89   virtual int64 Size() OVERRIDE;
90   virtual base::Time LastModifiedTime() OVERRIDE;
91   virtual bool IsDirectory() OVERRIDE;
92 
93  private:
94   base::FileEnumerator file_enum_;
95   base::FileEnumerator::FileInfo file_util_info_;
96 };
97 
Next()98 base::FilePath NativeFileEnumerator::Next() {
99   base::FilePath rv = file_enum_.Next();
100   if (!rv.empty())
101     file_util_info_ = file_enum_.GetInfo();
102   return rv;
103 }
104 
Size()105 int64 NativeFileEnumerator::Size() {
106   return file_util_info_.GetSize();
107 }
108 
LastModifiedTime()109 base::Time NativeFileEnumerator::LastModifiedTime() {
110   return file_util_info_.GetLastModifiedTime();
111 }
112 
IsDirectory()113 bool NativeFileEnumerator::IsDirectory() {
114   return file_util_info_.IsDirectory();
115 }
116 
CopyOrMoveModeForDestination(const FileSystemURL & dest_url,bool copy)117 NativeFileUtil::CopyOrMoveMode NativeFileUtil::CopyOrMoveModeForDestination(
118     const FileSystemURL& dest_url, bool copy) {
119   if (copy) {
120     return dest_url.mount_option().copy_sync_option() == COPY_SYNC_OPTION_SYNC ?
121         COPY_SYNC : COPY_NOSYNC;
122   }
123   return MOVE;
124 }
125 
CreateOrOpen(const base::FilePath & path,int file_flags,PlatformFile * file_handle,bool * created)126 PlatformFileError NativeFileUtil::CreateOrOpen(
127     const base::FilePath& path, int file_flags,
128     PlatformFile* file_handle, bool* created) {
129   if (!base::DirectoryExists(path.DirName())) {
130     // If its parent does not exist, should return NOT_FOUND error.
131     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
132   }
133   if (base::DirectoryExists(path))
134     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
135   PlatformFileError error_code = base::PLATFORM_FILE_OK;
136   *file_handle = base::CreatePlatformFile(path, file_flags,
137                                           created, &error_code);
138   return error_code;
139 }
140 
Close(PlatformFile file_handle)141 PlatformFileError NativeFileUtil::Close(PlatformFile file_handle) {
142   if (!base::ClosePlatformFile(file_handle))
143     return base::PLATFORM_FILE_ERROR_FAILED;
144   return base::PLATFORM_FILE_OK;
145 }
146 
EnsureFileExists(const base::FilePath & path,bool * created)147 PlatformFileError NativeFileUtil::EnsureFileExists(
148     const base::FilePath& path,
149     bool* created) {
150   if (!base::DirectoryExists(path.DirName()))
151     // If its parent does not exist, should return NOT_FOUND error.
152     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
153   PlatformFileError error_code = base::PLATFORM_FILE_OK;
154   // Tries to create the |path| exclusively.  This should fail
155   // with base::PLATFORM_FILE_ERROR_EXISTS if the path already exists.
156   PlatformFile handle = base::CreatePlatformFile(
157       path,
158       base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
159       created, &error_code);
160   if (error_code == base::PLATFORM_FILE_ERROR_EXISTS) {
161     // Make sure created_ is false.
162     if (created)
163       *created = false;
164     error_code = base::PLATFORM_FILE_OK;
165   }
166   if (handle != base::kInvalidPlatformFileValue)
167     base::ClosePlatformFile(handle);
168   return error_code;
169 }
170 
CreateDirectory(const base::FilePath & path,bool exclusive,bool recursive)171 PlatformFileError NativeFileUtil::CreateDirectory(
172     const base::FilePath& path,
173     bool exclusive,
174     bool recursive) {
175   // If parent dir of file doesn't exist.
176   if (!recursive && !base::PathExists(path.DirName()))
177     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
178 
179   bool path_exists = base::PathExists(path);
180   if (exclusive && path_exists)
181     return base::PLATFORM_FILE_ERROR_EXISTS;
182 
183   // If file exists at the path.
184   if (path_exists && !base::DirectoryExists(path))
185     return base::PLATFORM_FILE_ERROR_EXISTS;
186 
187   if (!base::CreateDirectory(path))
188     return base::PLATFORM_FILE_ERROR_FAILED;
189 
190   if (!SetPlatformSpecificDirectoryPermissions(path)) {
191     // Since some file systems don't support permission setting, we do not treat
192     // an error from the function as the failure of copying. Just log it.
193     LOG(WARNING) << "Setting directory permission failed: "
194         << path.AsUTF8Unsafe();
195   }
196 
197   return base::PLATFORM_FILE_OK;
198 }
199 
GetFileInfo(const base::FilePath & path,base::PlatformFileInfo * file_info)200 PlatformFileError NativeFileUtil::GetFileInfo(
201     const base::FilePath& path,
202     base::PlatformFileInfo* file_info) {
203   if (!base::PathExists(path))
204     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
205   if (!base::GetFileInfo(path, file_info))
206     return base::PLATFORM_FILE_ERROR_FAILED;
207   return base::PLATFORM_FILE_OK;
208 }
209 
210 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
CreateFileEnumerator(const base::FilePath & root_path,bool recursive)211     NativeFileUtil::CreateFileEnumerator(const base::FilePath& root_path,
212                                          bool recursive) {
213   return make_scoped_ptr(new NativeFileEnumerator(
214       root_path, recursive,
215       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES))
216       .PassAs<FileSystemFileUtil::AbstractFileEnumerator>();
217 }
218 
Touch(const base::FilePath & path,const base::Time & last_access_time,const base::Time & last_modified_time)219 PlatformFileError NativeFileUtil::Touch(
220     const base::FilePath& path,
221     const base::Time& last_access_time,
222     const base::Time& last_modified_time) {
223   if (!base::TouchFile(path, last_access_time, last_modified_time))
224     return base::PLATFORM_FILE_ERROR_FAILED;
225   return base::PLATFORM_FILE_OK;
226 }
227 
Truncate(const base::FilePath & path,int64 length)228 PlatformFileError NativeFileUtil::Truncate(
229     const base::FilePath& path, int64 length) {
230   PlatformFileError error_code(base::PLATFORM_FILE_ERROR_FAILED);
231   PlatformFile file =
232       base::CreatePlatformFile(
233           path,
234           base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
235           NULL,
236           &error_code);
237   if (error_code != base::PLATFORM_FILE_OK) {
238     return error_code;
239   }
240   DCHECK_NE(base::kInvalidPlatformFileValue, file);
241   if (!base::TruncatePlatformFile(file, length))
242     error_code = base::PLATFORM_FILE_ERROR_FAILED;
243   base::ClosePlatformFile(file);
244   return error_code;
245 }
246 
PathExists(const base::FilePath & path)247 bool NativeFileUtil::PathExists(const base::FilePath& path) {
248   return base::PathExists(path);
249 }
250 
DirectoryExists(const base::FilePath & path)251 bool NativeFileUtil::DirectoryExists(const base::FilePath& path) {
252   return base::DirectoryExists(path);
253 }
254 
CopyOrMoveFile(const base::FilePath & src_path,const base::FilePath & dest_path,FileSystemOperation::CopyOrMoveOption option,CopyOrMoveMode mode)255 PlatformFileError NativeFileUtil::CopyOrMoveFile(
256     const base::FilePath& src_path,
257     const base::FilePath& dest_path,
258     FileSystemOperation::CopyOrMoveOption option,
259     CopyOrMoveMode mode) {
260   base::PlatformFileInfo info;
261   base::PlatformFileError error = NativeFileUtil::GetFileInfo(src_path, &info);
262   if (error != base::PLATFORM_FILE_OK)
263     return error;
264   if (info.is_directory)
265     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
266   base::Time last_modified = info.last_modified;
267 
268   error = NativeFileUtil::GetFileInfo(dest_path, &info);
269   if (error != base::PLATFORM_FILE_OK &&
270       error != base::PLATFORM_FILE_ERROR_NOT_FOUND)
271     return error;
272   if (info.is_directory)
273     return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
274   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
275     error = NativeFileUtil::GetFileInfo(dest_path.DirName(), &info);
276     if (error != base::PLATFORM_FILE_OK)
277       return error;
278     if (!info.is_directory)
279       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
280   }
281 
282   switch (mode) {
283     case COPY_NOSYNC:
284       if (!base::CopyFile(src_path, dest_path))
285         return base::PLATFORM_FILE_ERROR_FAILED;
286       break;
287     case COPY_SYNC:
288       if (!CopyFileAndSync(src_path, dest_path))
289         return base::PLATFORM_FILE_ERROR_FAILED;
290       break;
291     case MOVE:
292       if (!base::Move(src_path, dest_path))
293         return base::PLATFORM_FILE_ERROR_FAILED;
294       break;
295   }
296 
297   // Preserve the last modified time. Do not return error here even if
298   // the setting is failed, because the copy itself is successfully done.
299   if (option == FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED)
300     base::TouchFile(dest_path, last_modified, last_modified);
301 
302   return base::PLATFORM_FILE_OK;
303 }
304 
DeleteFile(const base::FilePath & path)305 PlatformFileError NativeFileUtil::DeleteFile(const base::FilePath& path) {
306   if (!base::PathExists(path))
307     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
308   if (base::DirectoryExists(path))
309     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
310   if (!base::DeleteFile(path, false))
311     return base::PLATFORM_FILE_ERROR_FAILED;
312   return base::PLATFORM_FILE_OK;
313 }
314 
DeleteDirectory(const base::FilePath & path)315 PlatformFileError NativeFileUtil::DeleteDirectory(const base::FilePath& path) {
316   if (!base::PathExists(path))
317     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
318   if (!base::DirectoryExists(path))
319     return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
320   if (!base::IsDirectoryEmpty(path))
321     return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
322   if (!base::DeleteFile(path, false))
323     return base::PLATFORM_FILE_ERROR_FAILED;
324   return base::PLATFORM_FILE_OK;
325 }
326 
327 }  // namespace fileapi
328