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 "net/base/file_stream_context.h" 6 7 #include "base/files/file_path.h" 8 #include "base/location.h" 9 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/task_runner.h" 11 #include "base/task_runner_util.h" 12 #include "base/threading/thread_restrictions.h" 13 #include "base/values.h" 14 #include "net/base/net_errors.h" 15 16 #if defined(OS_ANDROID) 17 #include "base/android/content_uri_utils.h" 18 #endif 19 20 namespace net { 21 22 namespace { 23 24 void CallInt64ToInt(const CompletionCallback& callback, int64 result) { 25 callback.Run(static_cast<int>(result)); 26 } 27 28 } // namespace 29 30 FileStream::Context::IOResult::IOResult() 31 : result(OK), 32 os_error(0) { 33 } 34 35 FileStream::Context::IOResult::IOResult(int64 result, int os_error) 36 : result(result), 37 os_error(os_error) { 38 } 39 40 // static 41 FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError( 42 int64 os_error) { 43 return IOResult(MapSystemError(os_error), os_error); 44 } 45 46 // --------------------------------------------------------------------- 47 48 FileStream::Context::OpenResult::OpenResult() { 49 } 50 51 FileStream::Context::OpenResult::OpenResult(base::File file, 52 IOResult error_code) 53 : file(file.Pass()), 54 error_code(error_code) { 55 } 56 57 FileStream::Context::OpenResult::OpenResult(RValue other) 58 : file(other.object->file.Pass()), 59 error_code(other.object->error_code) { 60 } 61 62 FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=( 63 RValue other) { 64 if (this != other.object) { 65 file = other.object->file.Pass(); 66 error_code = other.object->error_code; 67 } 68 return *this; 69 } 70 71 // --------------------------------------------------------------------- 72 73 void FileStream::Context::Orphan() { 74 DCHECK(!orphaned_); 75 76 orphaned_ = true; 77 78 if (!async_in_progress_) { 79 CloseAndDelete(); 80 } else if (file_.IsValid()) { 81 CancelIo(file_.GetPlatformFile()); 82 } 83 } 84 85 void FileStream::Context::OpenAsync(const base::FilePath& path, 86 int open_flags, 87 const CompletionCallback& callback) { 88 DCHECK(!async_in_progress_); 89 90 bool posted = base::PostTaskAndReplyWithResult( 91 task_runner_.get(), 92 FROM_HERE, 93 base::Bind( 94 &Context::OpenFileImpl, base::Unretained(this), path, open_flags), 95 base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback)); 96 DCHECK(posted); 97 98 async_in_progress_ = true; 99 } 100 101 void FileStream::Context::CloseAsync(const CompletionCallback& callback) { 102 DCHECK(!async_in_progress_); 103 bool posted = base::PostTaskAndReplyWithResult( 104 task_runner_.get(), 105 FROM_HERE, 106 base::Bind(&Context::CloseFileImpl, base::Unretained(this)), 107 base::Bind(&Context::OnAsyncCompleted, 108 base::Unretained(this), 109 IntToInt64(callback))); 110 DCHECK(posted); 111 112 async_in_progress_ = true; 113 } 114 115 void FileStream::Context::SeekAsync(Whence whence, 116 int64 offset, 117 const Int64CompletionCallback& callback) { 118 DCHECK(!async_in_progress_); 119 120 bool posted = base::PostTaskAndReplyWithResult( 121 task_runner_.get(), 122 FROM_HERE, 123 base::Bind( 124 &Context::SeekFileImpl, base::Unretained(this), whence, offset), 125 base::Bind(&Context::OnAsyncCompleted, 126 base::Unretained(this), 127 callback)); 128 DCHECK(posted); 129 130 async_in_progress_ = true; 131 } 132 133 void FileStream::Context::FlushAsync(const CompletionCallback& callback) { 134 DCHECK(!async_in_progress_); 135 136 bool posted = base::PostTaskAndReplyWithResult( 137 task_runner_.get(), 138 FROM_HERE, 139 base::Bind(&Context::FlushFileImpl, base::Unretained(this)), 140 base::Bind(&Context::OnAsyncCompleted, 141 base::Unretained(this), 142 IntToInt64(callback))); 143 DCHECK(posted); 144 145 async_in_progress_ = true; 146 } 147 148 FileStream::Context::OpenResult FileStream::Context::OpenFileImpl( 149 const base::FilePath& path, int open_flags) { 150 #if defined(OS_POSIX) 151 // Always use blocking IO. 152 open_flags &= ~base::File::FLAG_ASYNC; 153 #endif 154 base::File file; 155 #if defined(OS_ANDROID) 156 if (path.IsContentUri()) { 157 // Check that only Read flags are set. 158 DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC, 159 base::File::FLAG_OPEN | base::File::FLAG_READ); 160 file = base::OpenContentUriForRead(path); 161 } else { 162 #endif // defined(OS_ANDROID) 163 // FileStream::Context actually closes the file asynchronously, 164 // independently from FileStream's destructor. It can cause problems for 165 // users wanting to delete the file right after FileStream deletion. Thus 166 // we are always adding SHARE_DELETE flag to accommodate such use case. 167 // TODO(rvargas): This sounds like a bug, as deleting the file would 168 // presumably happen on the wrong thread. There should be an async delete. 169 open_flags |= base::File::FLAG_SHARE_DELETE; 170 file.Initialize(path, open_flags); 171 #if defined(OS_ANDROID) 172 } 173 #endif // defined(OS_ANDROID) 174 if (!file.IsValid()) 175 return OpenResult(base::File(), IOResult::FromOSError(GetLastErrno())); 176 177 return OpenResult(file.Pass(), IOResult(OK, 0)); 178 } 179 180 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() { 181 file_.Close(); 182 return IOResult(OK, 0); 183 } 184 185 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback, 186 OpenResult open_result) { 187 file_ = open_result.file.Pass(); 188 if (file_.IsValid() && !orphaned_) 189 OnAsyncFileOpened(); 190 191 OnAsyncCompleted(IntToInt64(callback), open_result.error_code); 192 } 193 194 void FileStream::Context::CloseAndDelete() { 195 DCHECK(!async_in_progress_); 196 197 if (file_.IsValid()) { 198 bool posted = task_runner_.get()->PostTask( 199 FROM_HERE, 200 base::Bind(base::IgnoreResult(&Context::CloseFileImpl), 201 base::Owned(this))); 202 DCHECK(posted); 203 } else { 204 delete this; 205 } 206 } 207 208 Int64CompletionCallback FileStream::Context::IntToInt64( 209 const CompletionCallback& callback) { 210 return base::Bind(&CallInt64ToInt, callback); 211 } 212 213 void FileStream::Context::OnAsyncCompleted( 214 const Int64CompletionCallback& callback, 215 const IOResult& result) { 216 // Reset this before Run() as Run() may issue a new async operation. Also it 217 // should be reset before CloseAsync() because it shouldn't run if any async 218 // operation is in progress. 219 async_in_progress_ = false; 220 if (orphaned_) 221 CloseAndDelete(); 222 else 223 callback.Run(result.result); 224 } 225 226 } // namespace net 227