• 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.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"
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   base::File infile(from, base::File::FLAG_OPEN | base::File::FLAG_READ);
41   if (!infile.IsValid()) {
42     return false;
43   }
44 
45   base::File outfile(to,
46                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
47   if (!outfile.IsValid()) {
48     return false;
49   }
50 
51   const int kBufferSize = 32768;
52   std::vector<char> buffer(kBufferSize);
53 
54   for (;;) {
55     int bytes_read = infile.ReadAtCurrentPos(&buffer[0], kBufferSize);
56     if (bytes_read < 0)
57       return false;
58     if (bytes_read == 0)
59       break;
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)
64         return false;
65       bytes_written += bytes_written_partial;
66     }
67   }
68 
69   return outfile.Flush();
70 }
71 
72 }  // namespace
73 
74 using base::PlatformFile;
75 
76 class NativeFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator {
77  public:
NativeFileEnumerator(const base::FilePath & root_path,bool recursive,int file_type)78   NativeFileEnumerator(const base::FilePath& root_path,
79                        bool recursive,
80                        int file_type)
81     : file_enum_(root_path, recursive, file_type) {
82   }
83 
~NativeFileEnumerator()84   virtual ~NativeFileEnumerator() {}
85 
86   virtual base::FilePath Next() OVERRIDE;
87   virtual int64 Size() OVERRIDE;
88   virtual base::Time LastModifiedTime() OVERRIDE;
89   virtual bool IsDirectory() OVERRIDE;
90 
91  private:
92   base::FileEnumerator file_enum_;
93   base::FileEnumerator::FileInfo file_util_info_;
94 };
95 
Next()96 base::FilePath NativeFileEnumerator::Next() {
97   base::FilePath rv = file_enum_.Next();
98   if (!rv.empty())
99     file_util_info_ = file_enum_.GetInfo();
100   return rv;
101 }
102 
Size()103 int64 NativeFileEnumerator::Size() {
104   return file_util_info_.GetSize();
105 }
106 
LastModifiedTime()107 base::Time NativeFileEnumerator::LastModifiedTime() {
108   return file_util_info_.GetLastModifiedTime();
109 }
110 
IsDirectory()111 bool NativeFileEnumerator::IsDirectory() {
112   return file_util_info_.IsDirectory();
113 }
114 
CopyOrMoveModeForDestination(const FileSystemURL & dest_url,bool copy)115 NativeFileUtil::CopyOrMoveMode NativeFileUtil::CopyOrMoveModeForDestination(
116     const FileSystemURL& dest_url, bool copy) {
117   if (copy) {
118     return dest_url.mount_option().copy_sync_option() == COPY_SYNC_OPTION_SYNC ?
119         COPY_SYNC : COPY_NOSYNC;
120   }
121   return MOVE;
122 }
123 
CreateOrOpen(const base::FilePath & path,int file_flags)124 base::File NativeFileUtil::CreateOrOpen(const base::FilePath& path,
125                                         int file_flags) {
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);
129   }
130 
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);
134 
135   return base::File(path, file_flags);
136 }
137 
EnsureFileExists(const base::FilePath & path,bool * created)138 base::File::Error NativeFileUtil::EnsureFileExists(
139     const base::FilePath& path,
140     bool* created) {
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;
144 
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);
148 
149   if (file.IsValid()) {
150     if (created)
151       *created = file.created();
152     return base::File::FILE_OK;
153   }
154 
155   base::File::Error error_code = file.error_details();
156   if (error_code == base::File::FILE_ERROR_EXISTS) {
157     // Make sure created_ is false.
158     if (created)
159       *created = false;
160     error_code = base::File::FILE_OK;
161   }
162   return error_code;
163 }
164 
CreateDirectory(const base::FilePath & path,bool exclusive,bool recursive)165 base::File::Error NativeFileUtil::CreateDirectory(
166     const base::FilePath& path,
167     bool exclusive,
168     bool recursive) {
169   // If parent dir of file doesn't exist.
170   if (!recursive && !base::PathExists(path.DirName()))
171     return base::File::FILE_ERROR_NOT_FOUND;
172 
173   bool path_exists = base::PathExists(path);
174   if (exclusive && path_exists)
175     return base::File::FILE_ERROR_EXISTS;
176 
177   // If file exists at the path.
178   if (path_exists && !base::DirectoryExists(path))
179     return base::File::FILE_ERROR_EXISTS;
180 
181   if (!base::CreateDirectory(path))
182     return base::File::FILE_ERROR_FAILED;
183 
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();
189   }
190 
191   return base::File::FILE_OK;
192 }
193 
GetFileInfo(const base::FilePath & path,base::File::Info * file_info)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;
199 
200   if (!base::GetFileInfo(path, file_info))
201     return base::File::FILE_ERROR_FAILED;
202   return base::File::FILE_OK;
203 }
204 
205 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
CreateFileEnumerator(const base::FilePath & root_path,bool recursive)206     NativeFileUtil::CreateFileEnumerator(const base::FilePath& root_path,
207                                          bool recursive) {
208   return make_scoped_ptr(new NativeFileEnumerator(
209       root_path, recursive,
210       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES))
211       .PassAs<FileSystemFileUtil::AbstractFileEnumerator>();
212 }
213 
Touch(const base::FilePath & path,const base::Time & last_access_time,const base::Time & last_modified_time)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;
221 }
222 
Truncate(const base::FilePath & path,int64 length)223 base::File::Error NativeFileUtil::Truncate(const base::FilePath& path,
224                                            int64 length) {
225   base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
226   if (!file.IsValid())
227     return file.error_details();
228 
229   if (!file.SetLength(length))
230     return base::File::FILE_ERROR_FAILED;
231 
232   return base::File::FILE_OK;
233 }
234 
PathExists(const base::FilePath & path)235 bool NativeFileUtil::PathExists(const base::FilePath& path) {
236   return base::PathExists(path);
237 }
238 
DirectoryExists(const base::FilePath & path)239 bool NativeFileUtil::DirectoryExists(const base::FilePath& path) {
240   return base::DirectoryExists(path);
241 }
242 
CopyOrMoveFile(const base::FilePath & src_path,const base::FilePath & dest_path,FileSystemOperation::CopyOrMoveOption option,CopyOrMoveMode mode)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)
251     return error;
252   if (info.is_directory)
253     return base::File::FILE_ERROR_NOT_A_FILE;
254   base::Time last_modified = info.last_modified;
255 
256   error = NativeFileUtil::GetFileInfo(dest_path, &info);
257   if (error != base::File::FILE_OK &&
258       error != base::File::FILE_ERROR_NOT_FOUND)
259     return error;
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)
265       return error;
266     if (!info.is_directory)
267       return base::File::FILE_ERROR_NOT_FOUND;
268   }
269 
270   switch (mode) {
271     case COPY_NOSYNC:
272       if (!base::CopyFile(src_path, dest_path))
273         return base::File::FILE_ERROR_FAILED;
274       break;
275     case COPY_SYNC:
276       if (!CopyFileAndSync(src_path, dest_path))
277         return base::File::FILE_ERROR_FAILED;
278       break;
279     case MOVE:
280       if (!base::Move(src_path, dest_path))
281         return base::File::FILE_ERROR_FAILED;
282       break;
283   }
284 
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);
289 
290   return base::File::FILE_OK;
291 }
292 
DeleteFile(const base::FilePath & path)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;
301 }
302 
DeleteDirectory(const base::FilePath & path)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;
313 }
314 
315 }  // namespace fileapi
316