Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "nsDumpUtils.h" |
michael@0 | 8 | #include "nsDirectoryServiceDefs.h" |
michael@0 | 9 | #include "nsDirectoryServiceUtils.h" |
michael@0 | 10 | #include "prenv.h" |
michael@0 | 11 | #include <errno.h> |
michael@0 | 12 | #include "mozilla/Services.h" |
michael@0 | 13 | #include "nsIObserverService.h" |
michael@0 | 14 | #include "mozilla/ClearOnShutdown.h" |
michael@0 | 15 | |
michael@0 | 16 | #ifdef XP_UNIX // { |
michael@0 | 17 | #include "mozilla/Preferences.h" |
michael@0 | 18 | #include <fcntl.h> |
michael@0 | 19 | #include <unistd.h> |
michael@0 | 20 | #include <sys/types.h> |
michael@0 | 21 | #include <sys/stat.h> |
michael@0 | 22 | |
michael@0 | 23 | using namespace mozilla; |
michael@0 | 24 | |
michael@0 | 25 | /* |
michael@0 | 26 | * The following code supports triggering a registered callback upon |
michael@0 | 27 | * receiving a specific signal. |
michael@0 | 28 | * |
michael@0 | 29 | * Take about:memory for example, we register |
michael@0 | 30 | * 1. doGCCCDump for doMemoryReport |
michael@0 | 31 | * 2. doMemoryReport for sDumpAboutMemorySignum(SIGRTMIN) |
michael@0 | 32 | * and sDumpAboutMemoryAfterMMUSignum(SIGRTMIN+1). |
michael@0 | 33 | * |
michael@0 | 34 | * When we receive one of these signals, we write the signal number to a pipe. |
michael@0 | 35 | * The IO thread then notices that the pipe has been written to, and kicks off |
michael@0 | 36 | * the appropriate task on the main thread. |
michael@0 | 37 | * |
michael@0 | 38 | * This scheme is similar to using signalfd(), except it's portable and it |
michael@0 | 39 | * doesn't require the use of sigprocmask, which is problematic because it |
michael@0 | 40 | * masks signals received by child processes. |
michael@0 | 41 | * |
michael@0 | 42 | * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this. |
michael@0 | 43 | * But that uses libevent, which does not handle the realtime signals (bug |
michael@0 | 44 | * 794074). |
michael@0 | 45 | */ |
michael@0 | 46 | |
michael@0 | 47 | // This is the write-end of a pipe that we use to notice when a |
michael@0 | 48 | // specific signal occurs. |
michael@0 | 49 | static Atomic<int> sDumpPipeWriteFd(-1); |
michael@0 | 50 | |
michael@0 | 51 | const char* const FifoWatcher::kPrefName = |
michael@0 | 52 | "memory_info_dumper.watch_fifo.enabled"; |
michael@0 | 53 | |
michael@0 | 54 | static void |
michael@0 | 55 | DumpSignalHandler(int aSignum) |
michael@0 | 56 | { |
michael@0 | 57 | // This is a signal handler, so everything in here needs to be |
michael@0 | 58 | // async-signal-safe. Be careful! |
michael@0 | 59 | |
michael@0 | 60 | if (sDumpPipeWriteFd != -1) { |
michael@0 | 61 | uint8_t signum = static_cast<int>(aSignum); |
michael@0 | 62 | write(sDumpPipeWriteFd, &signum, sizeof(signum)); |
michael@0 | 63 | } |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | NS_IMPL_ISUPPORTS(FdWatcher, nsIObserver); |
michael@0 | 67 | |
michael@0 | 68 | void FdWatcher::Init() |
michael@0 | 69 | { |
michael@0 | 70 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 71 | |
michael@0 | 72 | nsCOMPtr<nsIObserverService> os = services::GetObserverService(); |
michael@0 | 73 | os->AddObserver(this, "xpcom-shutdown", /* ownsWeak = */ false); |
michael@0 | 74 | |
michael@0 | 75 | XRE_GetIOMessageLoop()->PostTask( |
michael@0 | 76 | FROM_HERE, |
michael@0 | 77 | NewRunnableMethod(this, &FdWatcher::StartWatching)); |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | // Implementations may call this function multiple times if they ensure that |
michael@0 | 81 | // it's safe to call OpenFd() multiple times and they call StopWatching() |
michael@0 | 82 | // first. |
michael@0 | 83 | void FdWatcher::StartWatching() |
michael@0 | 84 | { |
michael@0 | 85 | MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current()); |
michael@0 | 86 | MOZ_ASSERT(mFd == -1); |
michael@0 | 87 | |
michael@0 | 88 | mFd = OpenFd(); |
michael@0 | 89 | if (mFd == -1) { |
michael@0 | 90 | LOG("FdWatcher: OpenFd failed."); |
michael@0 | 91 | return; |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | MessageLoopForIO::current()->WatchFileDescriptor( |
michael@0 | 95 | mFd, /* persistent = */ true, |
michael@0 | 96 | MessageLoopForIO::WATCH_READ, |
michael@0 | 97 | &mReadWatcher, this); |
michael@0 | 98 | } |
michael@0 | 99 | |
michael@0 | 100 | // Since implementations can call StartWatching() multiple times, they can of |
michael@0 | 101 | // course call StopWatching() multiple times. |
michael@0 | 102 | void FdWatcher::StopWatching() |
michael@0 | 103 | { |
michael@0 | 104 | MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current()); |
michael@0 | 105 | |
michael@0 | 106 | mReadWatcher.StopWatchingFileDescriptor(); |
michael@0 | 107 | if (mFd != -1) { |
michael@0 | 108 | close(mFd); |
michael@0 | 109 | mFd = -1; |
michael@0 | 110 | } |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | StaticRefPtr<SignalPipeWatcher> SignalPipeWatcher::sSingleton; |
michael@0 | 114 | |
michael@0 | 115 | /* static */ SignalPipeWatcher* |
michael@0 | 116 | SignalPipeWatcher::GetSingleton() |
michael@0 | 117 | { |
michael@0 | 118 | if (!sSingleton) { |
michael@0 | 119 | sSingleton = new SignalPipeWatcher(); |
michael@0 | 120 | sSingleton->Init(); |
michael@0 | 121 | ClearOnShutdown(&sSingleton); |
michael@0 | 122 | } |
michael@0 | 123 | return sSingleton; |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | void |
michael@0 | 127 | SignalPipeWatcher::RegisterCallback(uint8_t aSignal, |
michael@0 | 128 | PipeCallback aCallback) |
michael@0 | 129 | { |
michael@0 | 130 | MutexAutoLock lock(mSignalInfoLock); |
michael@0 | 131 | |
michael@0 | 132 | for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) |
michael@0 | 133 | { |
michael@0 | 134 | if (mSignalInfo[i].mSignal == aSignal) { |
michael@0 | 135 | LOG("Register Signal(%d) callback failed! (DUPLICATE)", aSignal); |
michael@0 | 136 | return; |
michael@0 | 137 | } |
michael@0 | 138 | } |
michael@0 | 139 | SignalInfo signalInfo = { aSignal, aCallback }; |
michael@0 | 140 | mSignalInfo.AppendElement(signalInfo); |
michael@0 | 141 | RegisterSignalHandler(signalInfo.mSignal); |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | void |
michael@0 | 145 | SignalPipeWatcher::RegisterSignalHandler(uint8_t aSignal) |
michael@0 | 146 | { |
michael@0 | 147 | struct sigaction action; |
michael@0 | 148 | memset(&action, 0, sizeof(action)); |
michael@0 | 149 | sigemptyset(&action.sa_mask); |
michael@0 | 150 | action.sa_handler = DumpSignalHandler; |
michael@0 | 151 | |
michael@0 | 152 | if (aSignal) { |
michael@0 | 153 | if (sigaction(aSignal, &action, nullptr)) { |
michael@0 | 154 | LOG("SignalPipeWatcher failed to register sig %d.", aSignal); |
michael@0 | 155 | } |
michael@0 | 156 | } else { |
michael@0 | 157 | MutexAutoLock lock(mSignalInfoLock); |
michael@0 | 158 | for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) { |
michael@0 | 159 | if (sigaction(mSignalInfo[i].mSignal, &action, nullptr)) { |
michael@0 | 160 | LOG("SignalPipeWatcher failed to register signal(%d) " |
michael@0 | 161 | "dump signal handler.", mSignalInfo[i].mSignal); |
michael@0 | 162 | } |
michael@0 | 163 | } |
michael@0 | 164 | } |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | SignalPipeWatcher::~SignalPipeWatcher() |
michael@0 | 168 | { |
michael@0 | 169 | if (sDumpPipeWriteFd != -1) { |
michael@0 | 170 | StopWatching(); |
michael@0 | 171 | } |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | int SignalPipeWatcher::OpenFd() |
michael@0 | 175 | { |
michael@0 | 176 | MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current()); |
michael@0 | 177 | |
michael@0 | 178 | // Create a pipe. When we receive a signal in our signal handler, we'll |
michael@0 | 179 | // write the signum to the write-end of this pipe. |
michael@0 | 180 | int pipeFds[2]; |
michael@0 | 181 | if (pipe(pipeFds)) { |
michael@0 | 182 | LOG("SignalPipeWatcher failed to create pipe."); |
michael@0 | 183 | return -1; |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | // Close this pipe on calls to exec(). |
michael@0 | 187 | fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC); |
michael@0 | 188 | fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC); |
michael@0 | 189 | |
michael@0 | 190 | int readFd = pipeFds[0]; |
michael@0 | 191 | sDumpPipeWriteFd = pipeFds[1]; |
michael@0 | 192 | |
michael@0 | 193 | RegisterSignalHandler(); |
michael@0 | 194 | return readFd; |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | void SignalPipeWatcher::StopWatching() |
michael@0 | 198 | { |
michael@0 | 199 | MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current()); |
michael@0 | 200 | |
michael@0 | 201 | // Close sDumpPipeWriteFd /after/ setting the fd to -1. |
michael@0 | 202 | // Otherwise we have the (admittedly far-fetched) race where we |
michael@0 | 203 | // |
michael@0 | 204 | // 1) close sDumpPipeWriteFd |
michael@0 | 205 | // 2) open a new fd with the same number as sDumpPipeWriteFd |
michael@0 | 206 | // had. |
michael@0 | 207 | // 3) receive a signal, then write to the fd. |
michael@0 | 208 | int pipeWriteFd = sDumpPipeWriteFd.exchange(-1); |
michael@0 | 209 | close(pipeWriteFd); |
michael@0 | 210 | |
michael@0 | 211 | FdWatcher::StopWatching(); |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | void SignalPipeWatcher::OnFileCanReadWithoutBlocking(int aFd) |
michael@0 | 215 | { |
michael@0 | 216 | MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current()); |
michael@0 | 217 | |
michael@0 | 218 | uint8_t signum; |
michael@0 | 219 | ssize_t numReceived = read(aFd, &signum, sizeof(signum)); |
michael@0 | 220 | if (numReceived != sizeof(signum)) { |
michael@0 | 221 | LOG("Error reading from buffer in " |
michael@0 | 222 | "SignalPipeWatcher::OnFileCanReadWithoutBlocking."); |
michael@0 | 223 | return; |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | { |
michael@0 | 227 | MutexAutoLock lock(mSignalInfoLock); |
michael@0 | 228 | for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) { |
michael@0 | 229 | if(signum == mSignalInfo[i].mSignal) { |
michael@0 | 230 | mSignalInfo[i].mCallback(signum); |
michael@0 | 231 | return; |
michael@0 | 232 | } |
michael@0 | 233 | } |
michael@0 | 234 | } |
michael@0 | 235 | LOG("SignalPipeWatcher got unexpected signum."); |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | StaticRefPtr<FifoWatcher> FifoWatcher::sSingleton; |
michael@0 | 239 | |
michael@0 | 240 | /* static */ FifoWatcher* |
michael@0 | 241 | FifoWatcher::GetSingleton() |
michael@0 | 242 | { |
michael@0 | 243 | if (!sSingleton) { |
michael@0 | 244 | nsAutoCString dirPath; |
michael@0 | 245 | Preferences::GetCString( |
michael@0 | 246 | "memory_info_dumper.watch_fifo.directory", &dirPath); |
michael@0 | 247 | sSingleton = new FifoWatcher(dirPath); |
michael@0 | 248 | sSingleton->Init(); |
michael@0 | 249 | ClearOnShutdown(&sSingleton); |
michael@0 | 250 | } |
michael@0 | 251 | return sSingleton; |
michael@0 | 252 | } |
michael@0 | 253 | |
michael@0 | 254 | /* static */ bool |
michael@0 | 255 | FifoWatcher::MaybeCreate() |
michael@0 | 256 | { |
michael@0 | 257 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 258 | |
michael@0 | 259 | if (XRE_GetProcessType() != GeckoProcessType_Default) { |
michael@0 | 260 | // We want this to be main-process only, since two processes can't listen |
michael@0 | 261 | // to the same fifo. |
michael@0 | 262 | return false; |
michael@0 | 263 | } |
michael@0 | 264 | |
michael@0 | 265 | if (!Preferences::GetBool(kPrefName, false)) { |
michael@0 | 266 | LOG("Fifo watcher disabled via pref."); |
michael@0 | 267 | return false; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | // The FifoWatcher is held alive by the observer service. |
michael@0 | 271 | if (!sSingleton) { |
michael@0 | 272 | GetSingleton(); |
michael@0 | 273 | } |
michael@0 | 274 | return true; |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | void |
michael@0 | 278 | FifoWatcher::RegisterCallback(const nsCString& aCommand, FifoCallback aCallback) |
michael@0 | 279 | { |
michael@0 | 280 | MutexAutoLock lock(mFifoInfoLock); |
michael@0 | 281 | |
michael@0 | 282 | for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); i++) |
michael@0 | 283 | { |
michael@0 | 284 | if (mFifoInfo[i].mCommand.Equals(aCommand)) { |
michael@0 | 285 | LOG("Register command(%s) callback failed! (DUPLICATE)", aCommand.get()); |
michael@0 | 286 | return; |
michael@0 | 287 | } |
michael@0 | 288 | } |
michael@0 | 289 | FifoInfo aFifoInfo = { aCommand, aCallback }; |
michael@0 | 290 | mFifoInfo.AppendElement(aFifoInfo); |
michael@0 | 291 | } |
michael@0 | 292 | |
michael@0 | 293 | FifoWatcher::~FifoWatcher() |
michael@0 | 294 | { |
michael@0 | 295 | } |
michael@0 | 296 | |
michael@0 | 297 | int FifoWatcher::OpenFd() |
michael@0 | 298 | { |
michael@0 | 299 | // If the memory_info_dumper.directory pref is specified, put the fifo |
michael@0 | 300 | // there. Otherwise, put it into the system's tmp directory. |
michael@0 | 301 | |
michael@0 | 302 | nsCOMPtr<nsIFile> file; |
michael@0 | 303 | |
michael@0 | 304 | nsresult rv; |
michael@0 | 305 | if (mDirPath.Length() > 0) { |
michael@0 | 306 | rv = XRE_GetFileFromPath(mDirPath.get(), getter_AddRefs(file)); |
michael@0 | 307 | if (NS_FAILED(rv)) { |
michael@0 | 308 | LOG("FifoWatcher failed to open file \"%s\"", mDirPath.get()); |
michael@0 | 309 | return -1; |
michael@0 | 310 | } |
michael@0 | 311 | } else { |
michael@0 | 312 | rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file)); |
michael@0 | 313 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 314 | return -1; |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | rv = file->AppendNative(NS_LITERAL_CSTRING("debug_info_trigger")); |
michael@0 | 318 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 319 | return -1; |
michael@0 | 320 | |
michael@0 | 321 | nsAutoCString path; |
michael@0 | 322 | rv = file->GetNativePath(path); |
michael@0 | 323 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 324 | return -1; |
michael@0 | 325 | |
michael@0 | 326 | // unlink might fail because the file doesn't exist, or for other reasons. |
michael@0 | 327 | // But we don't care it fails; any problems will be detected later, when we |
michael@0 | 328 | // try to mkfifo or open the file. |
michael@0 | 329 | if (unlink(path.get())) { |
michael@0 | 330 | LOG("FifoWatcher::OpenFifo unlink failed; errno=%d. " |
michael@0 | 331 | "Continuing despite error.", errno); |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | if (mkfifo(path.get(), 0766)) { |
michael@0 | 335 | LOG("FifoWatcher::OpenFifo mkfifo failed; errno=%d", errno); |
michael@0 | 336 | return -1; |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | #ifdef ANDROID |
michael@0 | 340 | // Android runs with a umask, so we need to chmod our fifo to make it |
michael@0 | 341 | // world-writable. |
michael@0 | 342 | chmod(path.get(), 0666); |
michael@0 | 343 | #endif |
michael@0 | 344 | |
michael@0 | 345 | int fd; |
michael@0 | 346 | do { |
michael@0 | 347 | // The fifo will block until someone else has written to it. In |
michael@0 | 348 | // particular, open() will block until someone else has opened it for |
michael@0 | 349 | // writing! We want open() to succeed and read() to block, so we open |
michael@0 | 350 | // with NONBLOCK and then fcntl that away. |
michael@0 | 351 | fd = open(path.get(), O_RDONLY | O_NONBLOCK); |
michael@0 | 352 | } while (fd == -1 && errno == EINTR); |
michael@0 | 353 | |
michael@0 | 354 | if (fd == -1) { |
michael@0 | 355 | LOG("FifoWatcher::OpenFifo open failed; errno=%d", errno); |
michael@0 | 356 | return -1; |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | // Make fd blocking now that we've opened it. |
michael@0 | 360 | if (fcntl(fd, F_SETFL, 0)) { |
michael@0 | 361 | close(fd); |
michael@0 | 362 | return -1; |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | return fd; |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | void FifoWatcher::OnFileCanReadWithoutBlocking(int aFd) |
michael@0 | 369 | { |
michael@0 | 370 | MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current()); |
michael@0 | 371 | |
michael@0 | 372 | char buf[1024]; |
michael@0 | 373 | int nread; |
michael@0 | 374 | do { |
michael@0 | 375 | // sizeof(buf) - 1 to leave space for the null-terminator. |
michael@0 | 376 | nread = read(aFd, buf, sizeof(buf)); |
michael@0 | 377 | } while(nread == -1 && errno == EINTR); |
michael@0 | 378 | |
michael@0 | 379 | if (nread == -1) { |
michael@0 | 380 | // We want to avoid getting into a situation where |
michael@0 | 381 | // OnFileCanReadWithoutBlocking is called in an infinite loop, so when |
michael@0 | 382 | // something goes wrong, stop watching the fifo altogether. |
michael@0 | 383 | LOG("FifoWatcher hit an error (%d) and is quitting.", errno); |
michael@0 | 384 | StopWatching(); |
michael@0 | 385 | return; |
michael@0 | 386 | } |
michael@0 | 387 | |
michael@0 | 388 | if (nread == 0) { |
michael@0 | 389 | // If we get EOF, that means that the other side closed the fifo. We need |
michael@0 | 390 | // to close and re-open the fifo; if we don't, |
michael@0 | 391 | // OnFileCanWriteWithoutBlocking will be called in an infinite loop. |
michael@0 | 392 | |
michael@0 | 393 | LOG("FifoWatcher closing and re-opening fifo."); |
michael@0 | 394 | StopWatching(); |
michael@0 | 395 | StartWatching(); |
michael@0 | 396 | return; |
michael@0 | 397 | } |
michael@0 | 398 | |
michael@0 | 399 | nsAutoCString inputStr; |
michael@0 | 400 | inputStr.Append(buf, nread); |
michael@0 | 401 | |
michael@0 | 402 | // Trimming whitespace is important because if you do |
michael@0 | 403 | // |echo "foo" >> debug_info_trigger|, |
michael@0 | 404 | // it'll actually write "foo\n" to the fifo. |
michael@0 | 405 | inputStr.Trim("\b\t\r\n"); |
michael@0 | 406 | |
michael@0 | 407 | { |
michael@0 | 408 | MutexAutoLock lock(mFifoInfoLock); |
michael@0 | 409 | |
michael@0 | 410 | for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); i++) { |
michael@0 | 411 | const nsCString commandStr = mFifoInfo[i].mCommand; |
michael@0 | 412 | if(inputStr == commandStr.get()) { |
michael@0 | 413 | mFifoInfo[i].mCallback(inputStr); |
michael@0 | 414 | return; |
michael@0 | 415 | } |
michael@0 | 416 | } |
michael@0 | 417 | } |
michael@0 | 418 | LOG("Got unexpected value from fifo; ignoring it."); |
michael@0 | 419 | } |
michael@0 | 420 | |
michael@0 | 421 | #endif // XP_UNIX } |
michael@0 | 422 | |
michael@0 | 423 | // In Android case, this function will open a file named aFilename under |
michael@0 | 424 | // /data/local/tmp/"aFoldername". |
michael@0 | 425 | // Otherwise, it will open a file named aFilename under "NS_OS_TEMP_DIR". |
michael@0 | 426 | /* static */ nsresult |
michael@0 | 427 | nsDumpUtils::OpenTempFile(const nsACString& aFilename, nsIFile** aFile, |
michael@0 | 428 | const nsACString& aFoldername) |
michael@0 | 429 | { |
michael@0 | 430 | #ifdef ANDROID |
michael@0 | 431 | // For Android, first try the downloads directory which is world-readable |
michael@0 | 432 | // rather than the temp directory which is not. |
michael@0 | 433 | if (!*aFile) { |
michael@0 | 434 | char *env = PR_GetEnv("DOWNLOADS_DIRECTORY"); |
michael@0 | 435 | if (env) { |
michael@0 | 436 | NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile); |
michael@0 | 437 | } |
michael@0 | 438 | } |
michael@0 | 439 | #endif |
michael@0 | 440 | nsresult rv; |
michael@0 | 441 | if (!*aFile) { |
michael@0 | 442 | rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, aFile); |
michael@0 | 443 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 444 | return rv; |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | #ifdef ANDROID |
michael@0 | 448 | // /data/local/tmp is a true tmp directory; anyone can create a file there, |
michael@0 | 449 | // but only the user which created the file can remove it. We want non-root |
michael@0 | 450 | // users to be able to remove these files, so we write them into a |
michael@0 | 451 | // subdirectory of the temp directory and chmod 777 that directory. |
michael@0 | 452 | if (aFoldername != EmptyCString()) { |
michael@0 | 453 | rv = (*aFile)->AppendNative(aFoldername); |
michael@0 | 454 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 455 | return rv; |
michael@0 | 456 | |
michael@0 | 457 | // It's OK if this fails; that probably just means that the directory already |
michael@0 | 458 | // exists. |
michael@0 | 459 | (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777); |
michael@0 | 460 | |
michael@0 | 461 | nsAutoCString dirPath; |
michael@0 | 462 | rv = (*aFile)->GetNativePath(dirPath); |
michael@0 | 463 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 464 | return rv; |
michael@0 | 465 | |
michael@0 | 466 | while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) {} |
michael@0 | 467 | } |
michael@0 | 468 | #endif |
michael@0 | 469 | |
michael@0 | 470 | nsCOMPtr<nsIFile> file(*aFile); |
michael@0 | 471 | |
michael@0 | 472 | rv = file->AppendNative(aFilename); |
michael@0 | 473 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 474 | return rv; |
michael@0 | 475 | |
michael@0 | 476 | rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666); |
michael@0 | 477 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 478 | return rv; |
michael@0 | 479 | |
michael@0 | 480 | #ifdef ANDROID |
michael@0 | 481 | // Make this file world-read/writable; the permissions passed to the |
michael@0 | 482 | // CreateUnique call above are not sufficient on Android, which runs with a |
michael@0 | 483 | // umask. |
michael@0 | 484 | nsAutoCString path; |
michael@0 | 485 | rv = file->GetNativePath(path); |
michael@0 | 486 | if (NS_WARN_IF(NS_FAILED(rv))) |
michael@0 | 487 | return rv; |
michael@0 | 488 | |
michael@0 | 489 | while (chmod(path.get(), 0666) == -1 && errno == EINTR) {} |
michael@0 | 490 | #endif |
michael@0 | 491 | |
michael@0 | 492 | return NS_OK; |
michael@0 | 493 | } |