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. 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(). 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: 80 NativeFileEnumerator(const base::FilePath& root_path, 81 bool recursive, 82 int file_type) 83 : file_enum_(root_path, recursive, file_type) { 84 } 85 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 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 105 int64 NativeFileEnumerator::Size() { 106 return file_util_info_.GetSize(); 107 } 108 109 base::Time NativeFileEnumerator::LastModifiedTime() { 110 return file_util_info_.GetLastModifiedTime(); 111 } 112 113 bool NativeFileEnumerator::IsDirectory() { 114 return file_util_info_.IsDirectory(); 115 } 116 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 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 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 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 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 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> 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 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 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 247 bool NativeFileUtil::PathExists(const base::FilePath& path) { 248 return base::PathExists(path); 249 } 250 251 bool NativeFileUtil::DirectoryExists(const base::FilePath& path) { 252 return base::DirectoryExists(path); 253 } 254 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 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 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