1 // Copyright 2014 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 "base/files/file_proxy.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/file_util.h" 10 #include "base/files/file.h" 11 #include "base/location.h" 12 #include "base/message_loop/message_loop_proxy.h" 13 #include "base/task_runner.h" 14 #include "base/task_runner_util.h" 15 16 namespace { 17 18 void FileDeleter(base::File file) { 19 } 20 21 } // namespace 22 23 namespace base { 24 25 class FileHelper { 26 public: 27 FileHelper(FileProxy* proxy, File file) 28 : file_(file.Pass()), 29 error_(File::FILE_ERROR_FAILED), 30 task_runner_(proxy->task_runner()), 31 proxy_(AsWeakPtr(proxy)) { 32 } 33 34 void PassFile() { 35 if (proxy_) 36 proxy_->SetFile(file_.Pass()); 37 else if (file_.IsValid()) 38 task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_))); 39 } 40 41 protected: 42 File file_; 43 File::Error error_; 44 45 private: 46 scoped_refptr<TaskRunner> task_runner_; 47 WeakPtr<FileProxy> proxy_; 48 DISALLOW_COPY_AND_ASSIGN(FileHelper); 49 }; 50 51 namespace { 52 53 class GenericFileHelper : public FileHelper { 54 public: 55 GenericFileHelper(FileProxy* proxy, File file) 56 : FileHelper(proxy, file.Pass()) { 57 } 58 59 void Close() { 60 file_.Close(); 61 error_ = File::FILE_OK; 62 } 63 64 void SetTimes(Time last_access_time, Time last_modified_time) { 65 bool rv = file_.SetTimes(last_access_time, last_modified_time); 66 error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED; 67 } 68 69 void SetLength(int64 length) { 70 if (file_.SetLength(length)) 71 error_ = File::FILE_OK; 72 } 73 74 void Flush() { 75 if (file_.Flush()) 76 error_ = File::FILE_OK; 77 } 78 79 void Reply(const FileProxy::StatusCallback& callback) { 80 PassFile(); 81 if (!callback.is_null()) 82 callback.Run(error_); 83 } 84 85 private: 86 DISALLOW_COPY_AND_ASSIGN(GenericFileHelper); 87 }; 88 89 class CreateOrOpenHelper : public FileHelper { 90 public: 91 CreateOrOpenHelper(FileProxy* proxy, File file) 92 : FileHelper(proxy, file.Pass()) { 93 } 94 95 void RunWork(const FilePath& file_path, int file_flags) { 96 file_.Initialize(file_path, file_flags); 97 error_ = file_.IsValid() ? File::FILE_OK : file_.error_details(); 98 } 99 100 void Reply(const FileProxy::StatusCallback& callback) { 101 DCHECK(!callback.is_null()); 102 PassFile(); 103 callback.Run(error_); 104 } 105 106 private: 107 DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper); 108 }; 109 110 class CreateTemporaryHelper : public FileHelper { 111 public: 112 CreateTemporaryHelper(FileProxy* proxy, File file) 113 : FileHelper(proxy, file.Pass()) { 114 } 115 116 void RunWork(uint32 additional_file_flags) { 117 // TODO(darin): file_util should have a variant of CreateTemporaryFile 118 // that returns a FilePath and a File. 119 if (!CreateTemporaryFile(&file_path_)) { 120 // TODO(davidben): base::CreateTemporaryFile should preserve the error 121 // code. 122 error_ = File::FILE_ERROR_FAILED; 123 return; 124 } 125 126 uint32 file_flags = File::FLAG_WRITE | 127 File::FLAG_TEMPORARY | 128 File::FLAG_CREATE_ALWAYS | 129 additional_file_flags; 130 131 file_.Initialize(file_path_, file_flags); 132 if (file_.IsValid()) { 133 error_ = File::FILE_OK; 134 } else { 135 error_ = file_.error_details(); 136 DeleteFile(file_path_, false); 137 file_path_.clear(); 138 } 139 } 140 141 void Reply(const FileProxy::CreateTemporaryCallback& callback) { 142 DCHECK(!callback.is_null()); 143 PassFile(); 144 callback.Run(error_, file_path_); 145 } 146 147 private: 148 FilePath file_path_; 149 DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper); 150 }; 151 152 class GetInfoHelper : public FileHelper { 153 public: 154 GetInfoHelper(FileProxy* proxy, File file) 155 : FileHelper(proxy, file.Pass()) { 156 } 157 158 void RunWork() { 159 if (file_.GetInfo(&file_info_)) 160 error_ = File::FILE_OK; 161 } 162 163 void Reply(const FileProxy::GetFileInfoCallback& callback) { 164 PassFile(); 165 DCHECK(!callback.is_null()); 166 callback.Run(error_, file_info_); 167 } 168 169 private: 170 File::Info file_info_; 171 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper); 172 }; 173 174 class ReadHelper : public FileHelper { 175 public: 176 ReadHelper(FileProxy* proxy, File file, int bytes_to_read) 177 : FileHelper(proxy, file.Pass()), 178 buffer_(new char[bytes_to_read]), 179 bytes_to_read_(bytes_to_read), 180 bytes_read_(0) { 181 } 182 183 void RunWork(int64 offset) { 184 bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_); 185 error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK; 186 } 187 188 void Reply(const FileProxy::ReadCallback& callback) { 189 PassFile(); 190 DCHECK(!callback.is_null()); 191 callback.Run(error_, buffer_.get(), bytes_read_); 192 } 193 194 private: 195 scoped_ptr<char[]> buffer_; 196 int bytes_to_read_; 197 int bytes_read_; 198 DISALLOW_COPY_AND_ASSIGN(ReadHelper); 199 }; 200 201 class WriteHelper : public FileHelper { 202 public: 203 WriteHelper(FileProxy* proxy, 204 File file, 205 const char* buffer, int bytes_to_write) 206 : FileHelper(proxy, file.Pass()), 207 buffer_(new char[bytes_to_write]), 208 bytes_to_write_(bytes_to_write), 209 bytes_written_(0) { 210 memcpy(buffer_.get(), buffer, bytes_to_write); 211 } 212 213 void RunWork(int64 offset) { 214 bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_); 215 error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK; 216 } 217 218 void Reply(const FileProxy::WriteCallback& callback) { 219 PassFile(); 220 if (!callback.is_null()) 221 callback.Run(error_, bytes_written_); 222 } 223 224 private: 225 scoped_ptr<char[]> buffer_; 226 int bytes_to_write_; 227 int bytes_written_; 228 DISALLOW_COPY_AND_ASSIGN(WriteHelper); 229 }; 230 231 } // namespace 232 233 FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) { 234 } 235 236 FileProxy::~FileProxy() { 237 if (file_.IsValid()) 238 task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_))); 239 } 240 241 bool FileProxy::CreateOrOpen(const FilePath& file_path, 242 uint32 file_flags, 243 const StatusCallback& callback) { 244 DCHECK(!file_.IsValid()); 245 CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File()); 246 return task_runner_->PostTaskAndReply( 247 FROM_HERE, 248 Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path, 249 file_flags), 250 Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback)); 251 } 252 253 bool FileProxy::CreateTemporary(uint32 additional_file_flags, 254 const CreateTemporaryCallback& callback) { 255 DCHECK(!file_.IsValid()); 256 CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File()); 257 return task_runner_->PostTaskAndReply( 258 FROM_HERE, 259 Bind(&CreateTemporaryHelper::RunWork, Unretained(helper), 260 additional_file_flags), 261 Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback)); 262 } 263 264 bool FileProxy::IsValid() const { 265 return file_.IsValid(); 266 } 267 268 void FileProxy::SetFile(File file) { 269 DCHECK(!file_.IsValid()); 270 file_ = file.Pass(); 271 } 272 273 File FileProxy::TakeFile() { 274 return file_.Pass(); 275 } 276 277 PlatformFile FileProxy::GetPlatformFile() const { 278 return file_.GetPlatformFile(); 279 } 280 281 bool FileProxy::Close(const StatusCallback& callback) { 282 DCHECK(file_.IsValid()); 283 GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass()); 284 return task_runner_->PostTaskAndReply( 285 FROM_HERE, 286 Bind(&GenericFileHelper::Close, Unretained(helper)), 287 Bind(&GenericFileHelper::Reply, Owned(helper), callback)); 288 } 289 290 bool FileProxy::GetInfo(const GetFileInfoCallback& callback) { 291 DCHECK(file_.IsValid()); 292 GetInfoHelper* helper = new GetInfoHelper(this, file_.Pass()); 293 return task_runner_->PostTaskAndReply( 294 FROM_HERE, 295 Bind(&GetInfoHelper::RunWork, Unretained(helper)), 296 Bind(&GetInfoHelper::Reply, Owned(helper), callback)); 297 } 298 299 bool FileProxy::Read(int64 offset, 300 int bytes_to_read, 301 const ReadCallback& callback) { 302 DCHECK(file_.IsValid()); 303 if (bytes_to_read < 0) 304 return false; 305 306 ReadHelper* helper = new ReadHelper(this, file_.Pass(), bytes_to_read); 307 return task_runner_->PostTaskAndReply( 308 FROM_HERE, 309 Bind(&ReadHelper::RunWork, Unretained(helper), offset), 310 Bind(&ReadHelper::Reply, Owned(helper), callback)); 311 } 312 313 bool FileProxy::Write(int64 offset, 314 const char* buffer, 315 int bytes_to_write, 316 const WriteCallback& callback) { 317 DCHECK(file_.IsValid()); 318 if (bytes_to_write <= 0 || buffer == NULL) 319 return false; 320 321 WriteHelper* helper = 322 new WriteHelper(this, file_.Pass(), buffer, bytes_to_write); 323 return task_runner_->PostTaskAndReply( 324 FROM_HERE, 325 Bind(&WriteHelper::RunWork, Unretained(helper), offset), 326 Bind(&WriteHelper::Reply, Owned(helper), callback)); 327 } 328 329 bool FileProxy::SetTimes(Time last_access_time, 330 Time last_modified_time, 331 const StatusCallback& callback) { 332 DCHECK(file_.IsValid()); 333 GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass()); 334 return task_runner_->PostTaskAndReply( 335 FROM_HERE, 336 Bind(&GenericFileHelper::SetTimes, Unretained(helper), last_access_time, 337 last_modified_time), 338 Bind(&GenericFileHelper::Reply, Owned(helper), callback)); 339 } 340 341 bool FileProxy::SetLength(int64 length, const StatusCallback& callback) { 342 DCHECK(file_.IsValid()); 343 GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass()); 344 return task_runner_->PostTaskAndReply( 345 FROM_HERE, 346 Bind(&GenericFileHelper::SetLength, Unretained(helper), length), 347 Bind(&GenericFileHelper::Reply, Owned(helper), callback)); 348 } 349 350 bool FileProxy::Flush(const StatusCallback& callback) { 351 DCHECK(file_.IsValid()); 352 GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass()); 353 return task_runner_->PostTaskAndReply( 354 FROM_HERE, 355 Bind(&GenericFileHelper::Flush, Unretained(helper)), 356 Bind(&GenericFileHelper::Reply, Owned(helper), callback)); 357 } 358 359 } // namespace base 360