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 "ppapi/native_client/src/trusted/plugin/local_temp_file.h" 6 7 #include "native_client/src/include/portability_io.h" 8 #include "native_client/src/shared/platform/nacl_check.h" 9 10 #include "ppapi/c/ppb_file_io.h" 11 #include "ppapi/cpp/file_io.h" 12 #include "ppapi/cpp/file_ref.h" 13 #include "ppapi/cpp/file_system.h" 14 15 #include "ppapi/native_client/src/trusted/plugin/plugin.h" 16 #include "ppapi/native_client/src/trusted/plugin/utility.h" 17 18 ////////////////////////////////////////////////////////////////////// 19 // Local temporary file access. 20 ////////////////////////////////////////////////////////////////////// 21 namespace plugin { 22 23 namespace { 24 nacl::string Random32CharHexString(struct NaClDescRng* rng) { 25 struct NaClDesc* desc = reinterpret_cast<struct NaClDesc*>(rng); 26 const struct NaClDescVtbl* vtbl = 27 reinterpret_cast<const struct NaClDescVtbl*>(desc->base.vtbl); 28 29 nacl::string hex_string; 30 const int32_t kTempFileNameWords = 4; 31 for (int32_t i = 0; i < kTempFileNameWords; ++i) { 32 int32_t num; 33 CHECK(sizeof num == vtbl->Read(desc, 34 reinterpret_cast<char*>(&num), 35 sizeof num)); 36 char frag[16]; 37 SNPRINTF(frag, sizeof frag, "%08x", num); 38 hex_string += nacl::string(frag); 39 } 40 return hex_string; 41 } 42 43 // Some constants for LocalTempFile::GetFD readability. 44 const bool kReadOnly = false; 45 const bool kWriteable = true; 46 } // namespace 47 48 uint32_t LocalTempFile::next_identifier = 0; 49 50 LocalTempFile::LocalTempFile(Plugin* plugin, 51 pp::FileSystem* file_system, 52 const nacl::string &base_dir) 53 : plugin_(plugin), 54 file_system_(file_system), 55 base_dir_(base_dir) { 56 PLUGIN_PRINTF(("LocalTempFile::LocalTempFile (plugin=%p, " 57 "file_system=%p)\n", 58 static_cast<void*>(plugin), static_cast<void*>(file_system))); 59 Initialize(); 60 } 61 62 LocalTempFile::LocalTempFile(Plugin* plugin, 63 pp::FileSystem* file_system, 64 const nacl::string &base_dir, 65 const nacl::string &filename) 66 : plugin_(plugin), 67 file_system_(file_system), 68 base_dir_(base_dir), 69 filename_(base_dir + "/" + filename) { 70 PLUGIN_PRINTF(("LocalTempFile::LocalTempFile (plugin=%p, " 71 "file_system=%p, filename=%s)\n", 72 static_cast<void*>(plugin), static_cast<void*>(file_system), 73 filename.c_str())); 74 file_ref_.reset(new pp::FileRef(*file_system_, filename_.c_str())); 75 Initialize(); 76 } 77 78 void LocalTempFile::Initialize() { 79 callback_factory_.Initialize(this); 80 rng_desc_ = (struct NaClDescRng *) malloc(sizeof *rng_desc_); 81 CHECK(rng_desc_ != NULL); 82 CHECK(NaClDescRngCtor(rng_desc_)); 83 file_io_trusted_ = static_cast<const PPB_FileIOTrusted*>( 84 pp::Module::Get()->GetBrowserInterface(PPB_FILEIOTRUSTED_INTERFACE)); 85 ++next_identifier; 86 SNPRINTF(reinterpret_cast<char *>(identifier_), sizeof identifier_, 87 "%" NACL_PRIu32, next_identifier); 88 } 89 90 LocalTempFile::~LocalTempFile() { 91 PLUGIN_PRINTF(("LocalTempFile::~LocalTempFile\n")); 92 NaClDescUnref(reinterpret_cast<NaClDesc*>(rng_desc_)); 93 } 94 95 void LocalTempFile::OpenWrite(const pp::CompletionCallback& cb) { 96 done_callback_ = cb; 97 // If we don't already have a filename, generate one. 98 if (filename_ == "") { 99 // Get a random temp file name. 100 filename_ = base_dir_ + "/" + Random32CharHexString(rng_desc_); 101 // Remember the ref used to open for writing and reading. 102 file_ref_.reset(new pp::FileRef(*file_system_, filename_.c_str())); 103 } 104 PLUGIN_PRINTF(("LocalTempFile::OpenWrite: %s\n", filename_.c_str())); 105 // Open the writeable file. 106 write_io_.reset(new pp::FileIO(plugin_)); 107 pp::CompletionCallback open_write_cb = 108 callback_factory_.NewCallback(&LocalTempFile::WriteFileDidOpen); 109 write_io_->Open(*file_ref_, 110 PP_FILEOPENFLAG_WRITE | 111 PP_FILEOPENFLAG_CREATE | 112 PP_FILEOPENFLAG_EXCLUSIVE, 113 open_write_cb); 114 } 115 116 int32_t LocalTempFile::GetFD(int32_t pp_error, 117 const pp::Resource& resource, 118 bool is_writable) { 119 PLUGIN_PRINTF(("LocalTempFile::GetFD (pp_error=%" NACL_PRId32 120 ", is_writable=%d)\n", pp_error, is_writable)); 121 if (pp_error != PP_OK) { 122 PLUGIN_PRINTF(("LocalTempFile::GetFD pp_error != PP_OK\n")); 123 return -1; 124 } 125 int32_t file_desc = 126 file_io_trusted_->GetOSFileDescriptor(resource.pp_resource()); 127 #if NACL_WINDOWS 128 // Convert the Windows HANDLE from Pepper to a POSIX file descriptor. 129 int32_t open_flags = ((is_writable ? _O_RDWR : _O_RDONLY) | _O_BINARY); 130 int32_t posix_desc = _open_osfhandle(file_desc, open_flags); 131 if (posix_desc == -1) { 132 // Close the Windows HANDLE if it can't be converted. 133 CloseHandle(reinterpret_cast<HANDLE>(file_desc)); 134 PLUGIN_PRINTF(("LocalTempFile::GetFD _open_osfhandle failed.\n")); 135 return NACL_NO_FILE_DESC; 136 } 137 file_desc = posix_desc; 138 #endif 139 int32_t file_desc_ok_to_close = DUP(file_desc); 140 if (file_desc_ok_to_close == NACL_NO_FILE_DESC) { 141 PLUGIN_PRINTF(("LocalTempFile::GetFD dup failed.\n")); 142 return -1; 143 } 144 return file_desc_ok_to_close; 145 } 146 147 void LocalTempFile::WriteFileDidOpen(int32_t pp_error) { 148 PLUGIN_PRINTF(("LocalTempFile::WriteFileDidOpen (pp_error=%" 149 NACL_PRId32")\n", pp_error)); 150 if (pp_error == PP_ERROR_FILEEXISTS) { 151 // Filenames clashed, retry. 152 filename_ = ""; 153 OpenWrite(done_callback_); 154 } 155 // Run the client's completion callback. 156 pp::Core* core = pp::Module::Get()->core(); 157 if (pp_error != PP_OK) { 158 core->CallOnMainThread(0, done_callback_, pp_error); 159 return; 160 } 161 // Remember the object temporary file descriptor. 162 int32_t fd = GetFD(pp_error, *write_io_, kWriteable); 163 if (fd < 0) { 164 core->CallOnMainThread(0, done_callback_, pp_error); 165 return; 166 } 167 // The descriptor for a writeable file needs to have quota management. 168 write_wrapper_.reset( 169 plugin_->wrapper_factory()->MakeFileDescQuota(fd, O_RDWR, identifier_)); 170 core->CallOnMainThread(0, done_callback_, PP_OK); 171 } 172 173 void LocalTempFile::OpenRead(const pp::CompletionCallback& cb) { 174 PLUGIN_PRINTF(("LocalTempFile::OpenRead: %s\n", filename_.c_str())); 175 done_callback_ = cb; 176 // Open the read only file. 177 read_io_.reset(new pp::FileIO(plugin_)); 178 pp::CompletionCallback open_read_cb = 179 callback_factory_.NewCallback(&LocalTempFile::ReadFileDidOpen); 180 read_io_->Open(*file_ref_, PP_FILEOPENFLAG_READ, open_read_cb); 181 } 182 183 void LocalTempFile::ReadFileDidOpen(int32_t pp_error) { 184 PLUGIN_PRINTF(("LocalTempFile::ReadFileDidOpen (pp_error=%" 185 NACL_PRId32")\n", pp_error)); 186 // Run the client's completion callback. 187 pp::Core* core = pp::Module::Get()->core(); 188 if (pp_error != PP_OK) { 189 core->CallOnMainThread(0, done_callback_, pp_error); 190 return; 191 } 192 // Remember the object temporary file descriptor. 193 int32_t fd = GetFD(pp_error, *read_io_, kReadOnly); 194 if (fd < 0) { 195 core->CallOnMainThread(0, done_callback_, PP_ERROR_FAILED); 196 return; 197 } 198 read_wrapper_.reset(plugin_->wrapper_factory()->MakeFileDesc(fd, O_RDONLY)); 199 core->CallOnMainThread(0, done_callback_, PP_OK); 200 } 201 202 void LocalTempFile::Close(const pp::CompletionCallback& cb) { 203 PLUGIN_PRINTF(("LocalTempFile::Close: %s\n", filename_.c_str())); 204 // Close the open DescWrappers and FileIOs. 205 if (write_io_.get() != NULL) { 206 write_io_->Close(); 207 } 208 write_wrapper_.reset(NULL); 209 write_io_.reset(NULL); 210 if (read_io_.get() != NULL) { 211 read_io_->Close(); 212 } 213 read_wrapper_.reset(NULL); 214 read_io_.reset(NULL); 215 // Run the client's completion callback. 216 pp::Core* core = pp::Module::Get()->core(); 217 core->CallOnMainThread(0, cb, PP_OK); 218 } 219 220 void LocalTempFile::Delete(const pp::CompletionCallback& cb) { 221 PLUGIN_PRINTF(("LocalTempFile::Delete: %s\n", filename_.c_str())); 222 file_ref_->Delete(cb); 223 } 224 225 void LocalTempFile::Rename(const nacl::string& new_name, 226 const pp::CompletionCallback& cb) { 227 // Rename the temporary file. 228 filename_ = base_dir_ + "/" + new_name; 229 PLUGIN_PRINTF(("LocalTempFile::Rename %s to %s\n", 230 file_ref_->GetName().AsString().c_str(), 231 filename_.c_str())); 232 // Remember the old ref until the rename is complete. 233 old_ref_.reset(file_ref_.release()); 234 file_ref_.reset(new pp::FileRef(*file_system_, filename_.c_str())); 235 old_ref_->Rename(*file_ref_, cb); 236 } 237 238 void LocalTempFile::FinishRename() { 239 // Now we can release the old ref. 240 old_ref_.reset(NULL); 241 } 242 243 } // namespace plugin 244