Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nsProfileStringTypes.h" |
michael@0 | 7 | #include "nsProfileLock.h" |
michael@0 | 8 | #include "nsCOMPtr.h" |
michael@0 | 9 | |
michael@0 | 10 | #if defined(XP_MACOSX) |
michael@0 | 11 | #include <Carbon/Carbon.h> |
michael@0 | 12 | #include <CoreFoundation/CoreFoundation.h> |
michael@0 | 13 | #endif |
michael@0 | 14 | |
michael@0 | 15 | #ifdef XP_UNIX |
michael@0 | 16 | #include <unistd.h> |
michael@0 | 17 | #include <fcntl.h> |
michael@0 | 18 | #include <errno.h> |
michael@0 | 19 | #include <signal.h> |
michael@0 | 20 | #include <stdlib.h> |
michael@0 | 21 | #include "prnetdb.h" |
michael@0 | 22 | #include "prsystem.h" |
michael@0 | 23 | #include "prprf.h" |
michael@0 | 24 | #include "prenv.h" |
michael@0 | 25 | #endif |
michael@0 | 26 | |
michael@0 | 27 | #ifdef VMS |
michael@0 | 28 | #include <rmsdef.h> |
michael@0 | 29 | #endif |
michael@0 | 30 | |
michael@0 | 31 | // ********************************************************************** |
michael@0 | 32 | // class nsProfileLock |
michael@0 | 33 | // |
michael@0 | 34 | // This code was moved from profile/src/nsProfileAccess. |
michael@0 | 35 | // ********************************************************************** |
michael@0 | 36 | |
michael@0 | 37 | #if defined (XP_UNIX) |
michael@0 | 38 | static bool sDisableSignalHandling = false; |
michael@0 | 39 | #endif |
michael@0 | 40 | |
michael@0 | 41 | nsProfileLock::nsProfileLock() : |
michael@0 | 42 | mHaveLock(false), |
michael@0 | 43 | mReplacedLockTime(0) |
michael@0 | 44 | #if defined (XP_WIN) |
michael@0 | 45 | ,mLockFileHandle(INVALID_HANDLE_VALUE) |
michael@0 | 46 | #elif defined (XP_UNIX) |
michael@0 | 47 | ,mPidLockFileName(nullptr) |
michael@0 | 48 | ,mLockFileDesc(-1) |
michael@0 | 49 | #endif |
michael@0 | 50 | { |
michael@0 | 51 | #if defined (XP_UNIX) |
michael@0 | 52 | next = prev = this; |
michael@0 | 53 | sDisableSignalHandling = PR_GetEnv("MOZ_DISABLE_SIG_HANDLER") ? true : false; |
michael@0 | 54 | #endif |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | |
michael@0 | 58 | nsProfileLock::nsProfileLock(nsProfileLock& src) |
michael@0 | 59 | { |
michael@0 | 60 | *this = src; |
michael@0 | 61 | } |
michael@0 | 62 | |
michael@0 | 63 | |
michael@0 | 64 | nsProfileLock& nsProfileLock::operator=(nsProfileLock& rhs) |
michael@0 | 65 | { |
michael@0 | 66 | Unlock(); |
michael@0 | 67 | |
michael@0 | 68 | mHaveLock = rhs.mHaveLock; |
michael@0 | 69 | rhs.mHaveLock = false; |
michael@0 | 70 | |
michael@0 | 71 | #if defined (XP_WIN) |
michael@0 | 72 | mLockFileHandle = rhs.mLockFileHandle; |
michael@0 | 73 | rhs.mLockFileHandle = INVALID_HANDLE_VALUE; |
michael@0 | 74 | #elif defined (XP_UNIX) |
michael@0 | 75 | mLockFileDesc = rhs.mLockFileDesc; |
michael@0 | 76 | rhs.mLockFileDesc = -1; |
michael@0 | 77 | mPidLockFileName = rhs.mPidLockFileName; |
michael@0 | 78 | rhs.mPidLockFileName = nullptr; |
michael@0 | 79 | if (mPidLockFileName) |
michael@0 | 80 | { |
michael@0 | 81 | // rhs had a symlink lock, therefore it was on the list. |
michael@0 | 82 | PR_REMOVE_LINK(&rhs); |
michael@0 | 83 | PR_APPEND_LINK(this, &mPidLockList); |
michael@0 | 84 | } |
michael@0 | 85 | #endif |
michael@0 | 86 | |
michael@0 | 87 | return *this; |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | |
michael@0 | 91 | nsProfileLock::~nsProfileLock() |
michael@0 | 92 | { |
michael@0 | 93 | Unlock(); |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | |
michael@0 | 97 | #if defined (XP_UNIX) |
michael@0 | 98 | |
michael@0 | 99 | static int setupPidLockCleanup; |
michael@0 | 100 | |
michael@0 | 101 | PRCList nsProfileLock::mPidLockList = |
michael@0 | 102 | PR_INIT_STATIC_CLIST(&nsProfileLock::mPidLockList); |
michael@0 | 103 | |
michael@0 | 104 | void nsProfileLock::RemovePidLockFiles(bool aFatalSignal) |
michael@0 | 105 | { |
michael@0 | 106 | while (!PR_CLIST_IS_EMPTY(&mPidLockList)) |
michael@0 | 107 | { |
michael@0 | 108 | nsProfileLock *lock = static_cast<nsProfileLock*>(mPidLockList.next); |
michael@0 | 109 | lock->Unlock(aFatalSignal); |
michael@0 | 110 | } |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | static struct sigaction SIGHUP_oldact; |
michael@0 | 114 | static struct sigaction SIGINT_oldact; |
michael@0 | 115 | static struct sigaction SIGQUIT_oldact; |
michael@0 | 116 | static struct sigaction SIGILL_oldact; |
michael@0 | 117 | static struct sigaction SIGABRT_oldact; |
michael@0 | 118 | static struct sigaction SIGSEGV_oldact; |
michael@0 | 119 | static struct sigaction SIGTERM_oldact; |
michael@0 | 120 | |
michael@0 | 121 | void nsProfileLock::FatalSignalHandler(int signo |
michael@0 | 122 | #ifdef SA_SIGINFO |
michael@0 | 123 | , siginfo_t *info, void *context |
michael@0 | 124 | #endif |
michael@0 | 125 | ) |
michael@0 | 126 | { |
michael@0 | 127 | // Remove any locks still held. |
michael@0 | 128 | RemovePidLockFiles(true); |
michael@0 | 129 | |
michael@0 | 130 | // Chain to the old handler, which may exit. |
michael@0 | 131 | struct sigaction *oldact = nullptr; |
michael@0 | 132 | |
michael@0 | 133 | switch (signo) { |
michael@0 | 134 | case SIGHUP: |
michael@0 | 135 | oldact = &SIGHUP_oldact; |
michael@0 | 136 | break; |
michael@0 | 137 | case SIGINT: |
michael@0 | 138 | oldact = &SIGINT_oldact; |
michael@0 | 139 | break; |
michael@0 | 140 | case SIGQUIT: |
michael@0 | 141 | oldact = &SIGQUIT_oldact; |
michael@0 | 142 | break; |
michael@0 | 143 | case SIGILL: |
michael@0 | 144 | oldact = &SIGILL_oldact; |
michael@0 | 145 | break; |
michael@0 | 146 | case SIGABRT: |
michael@0 | 147 | oldact = &SIGABRT_oldact; |
michael@0 | 148 | break; |
michael@0 | 149 | case SIGSEGV: |
michael@0 | 150 | oldact = &SIGSEGV_oldact; |
michael@0 | 151 | break; |
michael@0 | 152 | case SIGTERM: |
michael@0 | 153 | oldact = &SIGTERM_oldact; |
michael@0 | 154 | break; |
michael@0 | 155 | default: |
michael@0 | 156 | NS_NOTREACHED("bad signo"); |
michael@0 | 157 | break; |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | if (oldact) { |
michael@0 | 161 | if (oldact->sa_handler == SIG_DFL) { |
michael@0 | 162 | // Make sure the default sig handler is executed |
michael@0 | 163 | // We need it to get Mozilla to dump core. |
michael@0 | 164 | sigaction(signo,oldact, nullptr); |
michael@0 | 165 | |
michael@0 | 166 | // Now that we've restored the default handler, unmask the |
michael@0 | 167 | // signal and invoke it. |
michael@0 | 168 | |
michael@0 | 169 | sigset_t unblock_sigs; |
michael@0 | 170 | sigemptyset(&unblock_sigs); |
michael@0 | 171 | sigaddset(&unblock_sigs, signo); |
michael@0 | 172 | |
michael@0 | 173 | sigprocmask(SIG_UNBLOCK, &unblock_sigs, nullptr); |
michael@0 | 174 | |
michael@0 | 175 | raise(signo); |
michael@0 | 176 | } |
michael@0 | 177 | #ifdef SA_SIGINFO |
michael@0 | 178 | else if (oldact->sa_sigaction && |
michael@0 | 179 | (oldact->sa_flags & SA_SIGINFO) == SA_SIGINFO) { |
michael@0 | 180 | oldact->sa_sigaction(signo, info, context); |
michael@0 | 181 | } |
michael@0 | 182 | #endif |
michael@0 | 183 | else if (oldact->sa_handler && oldact->sa_handler != SIG_IGN) |
michael@0 | 184 | { |
michael@0 | 185 | oldact->sa_handler(signo); |
michael@0 | 186 | } |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | // Backstop exit call, just in case. |
michael@0 | 190 | _exit(signo); |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | nsresult nsProfileLock::LockWithFcntl(nsIFile *aLockFile) |
michael@0 | 194 | { |
michael@0 | 195 | nsresult rv = NS_OK; |
michael@0 | 196 | |
michael@0 | 197 | nsAutoCString lockFilePath; |
michael@0 | 198 | rv = aLockFile->GetNativePath(lockFilePath); |
michael@0 | 199 | if (NS_FAILED(rv)) { |
michael@0 | 200 | NS_ERROR("Could not get native path"); |
michael@0 | 201 | return rv; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | aLockFile->GetLastModifiedTime(&mReplacedLockTime); |
michael@0 | 205 | |
michael@0 | 206 | mLockFileDesc = open(lockFilePath.get(), O_WRONLY | O_CREAT | O_TRUNC, 0666); |
michael@0 | 207 | if (mLockFileDesc != -1) |
michael@0 | 208 | { |
michael@0 | 209 | struct flock lock; |
michael@0 | 210 | lock.l_start = 0; |
michael@0 | 211 | lock.l_len = 0; // len = 0 means entire file |
michael@0 | 212 | lock.l_type = F_WRLCK; |
michael@0 | 213 | lock.l_whence = SEEK_SET; |
michael@0 | 214 | |
michael@0 | 215 | // If fcntl(F_GETLK) fails then the server does not support/allow fcntl(), |
michael@0 | 216 | // return failure rather than access denied in this case so we fallback |
michael@0 | 217 | // to using a symlink lock, bug 303633. |
michael@0 | 218 | struct flock testlock = lock; |
michael@0 | 219 | if (fcntl(mLockFileDesc, F_GETLK, &testlock) == -1) |
michael@0 | 220 | { |
michael@0 | 221 | close(mLockFileDesc); |
michael@0 | 222 | mLockFileDesc = -1; |
michael@0 | 223 | rv = NS_ERROR_FAILURE; |
michael@0 | 224 | } |
michael@0 | 225 | else if (fcntl(mLockFileDesc, F_SETLK, &lock) == -1) |
michael@0 | 226 | { |
michael@0 | 227 | close(mLockFileDesc); |
michael@0 | 228 | mLockFileDesc = -1; |
michael@0 | 229 | |
michael@0 | 230 | // With OS X, on NFS, errno == ENOTSUP |
michael@0 | 231 | // XXX Check for that and return specific rv for it? |
michael@0 | 232 | #ifdef DEBUG |
michael@0 | 233 | printf("fcntl(F_SETLK) failed. errno = %d\n", errno); |
michael@0 | 234 | #endif |
michael@0 | 235 | if (errno == EAGAIN || errno == EACCES) |
michael@0 | 236 | rv = NS_ERROR_FILE_ACCESS_DENIED; |
michael@0 | 237 | else |
michael@0 | 238 | rv = NS_ERROR_FAILURE; |
michael@0 | 239 | } |
michael@0 | 240 | else |
michael@0 | 241 | mHaveLock = true; |
michael@0 | 242 | } |
michael@0 | 243 | else |
michael@0 | 244 | { |
michael@0 | 245 | NS_ERROR("Failed to open lock file."); |
michael@0 | 246 | rv = NS_ERROR_FAILURE; |
michael@0 | 247 | } |
michael@0 | 248 | return rv; |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | static bool IsSymlinkStaleLock(struct in_addr* aAddr, const char* aFileName, |
michael@0 | 252 | bool aHaveFcntlLock) |
michael@0 | 253 | { |
michael@0 | 254 | // the link exists; see if it's from this machine, and if |
michael@0 | 255 | // so if the process is still active |
michael@0 | 256 | char buf[1024]; |
michael@0 | 257 | int len = readlink(aFileName, buf, sizeof buf - 1); |
michael@0 | 258 | if (len > 0) |
michael@0 | 259 | { |
michael@0 | 260 | buf[len] = '\0'; |
michael@0 | 261 | char *colon = strchr(buf, ':'); |
michael@0 | 262 | if (colon) |
michael@0 | 263 | { |
michael@0 | 264 | *colon++ = '\0'; |
michael@0 | 265 | unsigned long addr = inet_addr(buf); |
michael@0 | 266 | if (addr != (unsigned long) -1) |
michael@0 | 267 | { |
michael@0 | 268 | if (colon[0] == '+' && aHaveFcntlLock) { |
michael@0 | 269 | // This lock was placed by a Firefox build which would have |
michael@0 | 270 | // taken the fnctl lock, and we've already taken the fcntl lock, |
michael@0 | 271 | // so the process that created this obsolete lock must be gone |
michael@0 | 272 | return true; |
michael@0 | 273 | } |
michael@0 | 274 | |
michael@0 | 275 | char *after = nullptr; |
michael@0 | 276 | pid_t pid = strtol(colon, &after, 0); |
michael@0 | 277 | if (pid != 0 && *after == '\0') |
michael@0 | 278 | { |
michael@0 | 279 | if (addr != aAddr->s_addr) |
michael@0 | 280 | { |
michael@0 | 281 | // Remote lock: give up even if stuck. |
michael@0 | 282 | return false; |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | // kill(pid,0) is a neat trick to check if a |
michael@0 | 286 | // process exists |
michael@0 | 287 | if (kill(pid, 0) == 0 || errno != ESRCH) |
michael@0 | 288 | { |
michael@0 | 289 | // Local process appears to be alive, ass-u-me it |
michael@0 | 290 | // is another Mozilla instance, or a compatible |
michael@0 | 291 | // derivative, that's currently using the profile. |
michael@0 | 292 | // XXX need an "are you Mozilla?" protocol |
michael@0 | 293 | return false; |
michael@0 | 294 | } |
michael@0 | 295 | } |
michael@0 | 296 | } |
michael@0 | 297 | } |
michael@0 | 298 | } |
michael@0 | 299 | return true; |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | nsresult nsProfileLock::LockWithSymlink(nsIFile *aLockFile, bool aHaveFcntlLock) |
michael@0 | 303 | { |
michael@0 | 304 | nsresult rv; |
michael@0 | 305 | nsAutoCString lockFilePath; |
michael@0 | 306 | rv = aLockFile->GetNativePath(lockFilePath); |
michael@0 | 307 | if (NS_FAILED(rv)) { |
michael@0 | 308 | NS_ERROR("Could not get native path"); |
michael@0 | 309 | return rv; |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | // don't replace an existing lock time if fcntl already got one |
michael@0 | 313 | if (!mReplacedLockTime) |
michael@0 | 314 | aLockFile->GetLastModifiedTimeOfLink(&mReplacedLockTime); |
michael@0 | 315 | |
michael@0 | 316 | struct in_addr inaddr; |
michael@0 | 317 | inaddr.s_addr = htonl(INADDR_LOOPBACK); |
michael@0 | 318 | |
michael@0 | 319 | char hostname[256]; |
michael@0 | 320 | PRStatus status = PR_GetSystemInfo(PR_SI_HOSTNAME, hostname, sizeof hostname); |
michael@0 | 321 | if (status == PR_SUCCESS) |
michael@0 | 322 | { |
michael@0 | 323 | char netdbbuf[PR_NETDB_BUF_SIZE]; |
michael@0 | 324 | PRHostEnt hostent; |
michael@0 | 325 | status = PR_GetHostByName(hostname, netdbbuf, sizeof netdbbuf, &hostent); |
michael@0 | 326 | if (status == PR_SUCCESS) |
michael@0 | 327 | memcpy(&inaddr, hostent.h_addr, sizeof inaddr); |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | char *signature = |
michael@0 | 331 | PR_smprintf("%s:%s%lu", inet_ntoa(inaddr), aHaveFcntlLock ? "+" : "", |
michael@0 | 332 | (unsigned long)getpid()); |
michael@0 | 333 | const char *fileName = lockFilePath.get(); |
michael@0 | 334 | int symlink_rv, symlink_errno = 0, tries = 0; |
michael@0 | 335 | |
michael@0 | 336 | // use ns4.x-compatible symlinks if the FS supports them |
michael@0 | 337 | while ((symlink_rv = symlink(signature, fileName)) < 0) |
michael@0 | 338 | { |
michael@0 | 339 | symlink_errno = errno; |
michael@0 | 340 | if (symlink_errno != EEXIST) |
michael@0 | 341 | break; |
michael@0 | 342 | |
michael@0 | 343 | if (!IsSymlinkStaleLock(&inaddr, fileName, aHaveFcntlLock)) |
michael@0 | 344 | break; |
michael@0 | 345 | |
michael@0 | 346 | // Lock seems to be bogus: try to claim it. Give up after a large |
michael@0 | 347 | // number of attempts (100 comes from the 4.x codebase). |
michael@0 | 348 | (void) unlink(fileName); |
michael@0 | 349 | if (++tries > 100) |
michael@0 | 350 | break; |
michael@0 | 351 | } |
michael@0 | 352 | |
michael@0 | 353 | PR_smprintf_free(signature); |
michael@0 | 354 | signature = nullptr; |
michael@0 | 355 | |
michael@0 | 356 | if (symlink_rv == 0) |
michael@0 | 357 | { |
michael@0 | 358 | // We exclusively created the symlink: record its name for eventual |
michael@0 | 359 | // unlock-via-unlink. |
michael@0 | 360 | rv = NS_OK; |
michael@0 | 361 | mHaveLock = true; |
michael@0 | 362 | mPidLockFileName = strdup(fileName); |
michael@0 | 363 | if (mPidLockFileName) |
michael@0 | 364 | { |
michael@0 | 365 | PR_APPEND_LINK(this, &mPidLockList); |
michael@0 | 366 | if (!setupPidLockCleanup++) |
michael@0 | 367 | { |
michael@0 | 368 | // Clean up on normal termination. |
michael@0 | 369 | // This instanciates a dummy class, and will trigger the class |
michael@0 | 370 | // destructor when libxul is unloaded. This is equivalent to atexit(), |
michael@0 | 371 | // but gracefully handles dlclose(). |
michael@0 | 372 | static RemovePidLockFilesExiting r; |
michael@0 | 373 | |
michael@0 | 374 | // Clean up on abnormal termination, using POSIX sigaction. |
michael@0 | 375 | // Don't arm a handler if the signal is being ignored, e.g., |
michael@0 | 376 | // because mozilla is run via nohup. |
michael@0 | 377 | if (!sDisableSignalHandling) { |
michael@0 | 378 | struct sigaction act, oldact; |
michael@0 | 379 | #ifdef SA_SIGINFO |
michael@0 | 380 | act.sa_sigaction = FatalSignalHandler; |
michael@0 | 381 | act.sa_flags = SA_SIGINFO; |
michael@0 | 382 | #else |
michael@0 | 383 | act.sa_handler = FatalSignalHandler; |
michael@0 | 384 | #endif |
michael@0 | 385 | sigfillset(&act.sa_mask); |
michael@0 | 386 | |
michael@0 | 387 | #define CATCH_SIGNAL(signame) \ |
michael@0 | 388 | PR_BEGIN_MACRO \ |
michael@0 | 389 | if (sigaction(signame, nullptr, &oldact) == 0 && \ |
michael@0 | 390 | oldact.sa_handler != SIG_IGN) \ |
michael@0 | 391 | { \ |
michael@0 | 392 | sigaction(signame, &act, &signame##_oldact); \ |
michael@0 | 393 | } \ |
michael@0 | 394 | PR_END_MACRO |
michael@0 | 395 | |
michael@0 | 396 | CATCH_SIGNAL(SIGHUP); |
michael@0 | 397 | CATCH_SIGNAL(SIGINT); |
michael@0 | 398 | CATCH_SIGNAL(SIGQUIT); |
michael@0 | 399 | CATCH_SIGNAL(SIGILL); |
michael@0 | 400 | CATCH_SIGNAL(SIGABRT); |
michael@0 | 401 | CATCH_SIGNAL(SIGSEGV); |
michael@0 | 402 | CATCH_SIGNAL(SIGTERM); |
michael@0 | 403 | |
michael@0 | 404 | #undef CATCH_SIGNAL |
michael@0 | 405 | } |
michael@0 | 406 | } |
michael@0 | 407 | } |
michael@0 | 408 | } |
michael@0 | 409 | else if (symlink_errno == EEXIST) |
michael@0 | 410 | rv = NS_ERROR_FILE_ACCESS_DENIED; |
michael@0 | 411 | else |
michael@0 | 412 | { |
michael@0 | 413 | #ifdef DEBUG |
michael@0 | 414 | printf("symlink() failed. errno = %d\n", errno); |
michael@0 | 415 | #endif |
michael@0 | 416 | rv = NS_ERROR_FAILURE; |
michael@0 | 417 | } |
michael@0 | 418 | return rv; |
michael@0 | 419 | } |
michael@0 | 420 | #endif /* XP_UNIX */ |
michael@0 | 421 | |
michael@0 | 422 | nsresult nsProfileLock::GetReplacedLockTime(PRTime *aResult) { |
michael@0 | 423 | *aResult = mReplacedLockTime; |
michael@0 | 424 | return NS_OK; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | nsresult nsProfileLock::Lock(nsIFile* aProfileDir, |
michael@0 | 428 | nsIProfileUnlocker* *aUnlocker) |
michael@0 | 429 | { |
michael@0 | 430 | #if defined (XP_MACOSX) |
michael@0 | 431 | NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock"); |
michael@0 | 432 | NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "parent.lock"); |
michael@0 | 433 | #elif defined (XP_UNIX) |
michael@0 | 434 | NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "lock"); |
michael@0 | 435 | NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock"); |
michael@0 | 436 | #else |
michael@0 | 437 | NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, "parent.lock"); |
michael@0 | 438 | #endif |
michael@0 | 439 | |
michael@0 | 440 | nsresult rv; |
michael@0 | 441 | if (aUnlocker) |
michael@0 | 442 | *aUnlocker = nullptr; |
michael@0 | 443 | |
michael@0 | 444 | NS_ENSURE_STATE(!mHaveLock); |
michael@0 | 445 | |
michael@0 | 446 | bool isDir; |
michael@0 | 447 | rv = aProfileDir->IsDirectory(&isDir); |
michael@0 | 448 | if (NS_FAILED(rv)) |
michael@0 | 449 | return rv; |
michael@0 | 450 | if (!isDir) |
michael@0 | 451 | return NS_ERROR_FILE_NOT_DIRECTORY; |
michael@0 | 452 | |
michael@0 | 453 | nsCOMPtr<nsIFile> lockFile; |
michael@0 | 454 | rv = aProfileDir->Clone(getter_AddRefs(lockFile)); |
michael@0 | 455 | if (NS_FAILED(rv)) |
michael@0 | 456 | return rv; |
michael@0 | 457 | |
michael@0 | 458 | rv = lockFile->Append(LOCKFILE_NAME); |
michael@0 | 459 | if (NS_FAILED(rv)) |
michael@0 | 460 | return rv; |
michael@0 | 461 | |
michael@0 | 462 | #if defined(XP_MACOSX) |
michael@0 | 463 | // First, try locking using fcntl. It is more reliable on |
michael@0 | 464 | // a local machine, but may not be supported by an NFS server. |
michael@0 | 465 | |
michael@0 | 466 | rv = LockWithFcntl(lockFile); |
michael@0 | 467 | if (NS_FAILED(rv) && (rv != NS_ERROR_FILE_ACCESS_DENIED)) |
michael@0 | 468 | { |
michael@0 | 469 | // If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED, |
michael@0 | 470 | // assume we tried an NFS that does not support it. Now, try with symlink. |
michael@0 | 471 | rv = LockWithSymlink(lockFile, false); |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | if (NS_SUCCEEDED(rv)) |
michael@0 | 475 | { |
michael@0 | 476 | // Check for the old-style lock used by pre-mozilla 1.3 builds. |
michael@0 | 477 | // Those builds used an earlier check to prevent the application |
michael@0 | 478 | // from launching if another instance was already running. Because |
michael@0 | 479 | // of that, we don't need to create an old-style lock as well. |
michael@0 | 480 | struct LockProcessInfo |
michael@0 | 481 | { |
michael@0 | 482 | ProcessSerialNumber psn; |
michael@0 | 483 | unsigned long launchDate; |
michael@0 | 484 | }; |
michael@0 | 485 | |
michael@0 | 486 | PRFileDesc *fd = nullptr; |
michael@0 | 487 | int32_t ioBytes; |
michael@0 | 488 | ProcessInfoRec processInfo; |
michael@0 | 489 | LockProcessInfo lockProcessInfo; |
michael@0 | 490 | |
michael@0 | 491 | rv = lockFile->SetLeafName(OLD_LOCKFILE_NAME); |
michael@0 | 492 | if (NS_FAILED(rv)) |
michael@0 | 493 | return rv; |
michael@0 | 494 | rv = lockFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); |
michael@0 | 495 | if (NS_SUCCEEDED(rv)) |
michael@0 | 496 | { |
michael@0 | 497 | ioBytes = PR_Read(fd, &lockProcessInfo, sizeof(LockProcessInfo)); |
michael@0 | 498 | PR_Close(fd); |
michael@0 | 499 | |
michael@0 | 500 | if (ioBytes == sizeof(LockProcessInfo)) |
michael@0 | 501 | { |
michael@0 | 502 | #ifdef __LP64__ |
michael@0 | 503 | processInfo.processAppRef = nullptr; |
michael@0 | 504 | #else |
michael@0 | 505 | processInfo.processAppSpec = nullptr; |
michael@0 | 506 | #endif |
michael@0 | 507 | processInfo.processName = nullptr; |
michael@0 | 508 | processInfo.processInfoLength = sizeof(ProcessInfoRec); |
michael@0 | 509 | if (::GetProcessInformation(&lockProcessInfo.psn, &processInfo) == noErr && |
michael@0 | 510 | processInfo.processLaunchDate == lockProcessInfo.launchDate) |
michael@0 | 511 | { |
michael@0 | 512 | return NS_ERROR_FILE_ACCESS_DENIED; |
michael@0 | 513 | } |
michael@0 | 514 | } |
michael@0 | 515 | else |
michael@0 | 516 | { |
michael@0 | 517 | NS_WARNING("Could not read lock file - ignoring lock"); |
michael@0 | 518 | } |
michael@0 | 519 | } |
michael@0 | 520 | rv = NS_OK; // Don't propagate error from OpenNSPRFileDesc. |
michael@0 | 521 | } |
michael@0 | 522 | #elif defined(XP_UNIX) |
michael@0 | 523 | // Get the old lockfile name |
michael@0 | 524 | nsCOMPtr<nsIFile> oldLockFile; |
michael@0 | 525 | rv = aProfileDir->Clone(getter_AddRefs(oldLockFile)); |
michael@0 | 526 | if (NS_FAILED(rv)) |
michael@0 | 527 | return rv; |
michael@0 | 528 | rv = oldLockFile->Append(OLD_LOCKFILE_NAME); |
michael@0 | 529 | if (NS_FAILED(rv)) |
michael@0 | 530 | return rv; |
michael@0 | 531 | |
michael@0 | 532 | // First, try locking using fcntl. It is more reliable on |
michael@0 | 533 | // a local machine, but may not be supported by an NFS server. |
michael@0 | 534 | rv = LockWithFcntl(lockFile); |
michael@0 | 535 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 536 | // Check to see whether there is a symlink lock held by an older |
michael@0 | 537 | // Firefox build, and also place our own symlink lock --- but |
michael@0 | 538 | // mark it "obsolete" so that other newer builds can break the lock |
michael@0 | 539 | // if they obtain the fcntl lock |
michael@0 | 540 | rv = LockWithSymlink(oldLockFile, true); |
michael@0 | 541 | |
michael@0 | 542 | // If the symlink failed for some reason other than it already |
michael@0 | 543 | // exists, then something went wrong e.g. the file system |
michael@0 | 544 | // doesn't support symlinks, or we don't have permission to |
michael@0 | 545 | // create a symlink there. In such cases we should just |
michael@0 | 546 | // continue because it's unlikely there is an old build |
michael@0 | 547 | // running with a symlink there and we've already successfully |
michael@0 | 548 | // placed a fcntl lock. |
michael@0 | 549 | if (rv != NS_ERROR_FILE_ACCESS_DENIED) |
michael@0 | 550 | rv = NS_OK; |
michael@0 | 551 | } |
michael@0 | 552 | else if (rv != NS_ERROR_FILE_ACCESS_DENIED) |
michael@0 | 553 | { |
michael@0 | 554 | // If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED, |
michael@0 | 555 | // assume we tried an NFS that does not support it. Now, try with symlink |
michael@0 | 556 | // using the old symlink path |
michael@0 | 557 | rv = LockWithSymlink(oldLockFile, false); |
michael@0 | 558 | } |
michael@0 | 559 | |
michael@0 | 560 | #elif defined(XP_WIN) |
michael@0 | 561 | nsAutoString filePath; |
michael@0 | 562 | rv = lockFile->GetPath(filePath); |
michael@0 | 563 | if (NS_FAILED(rv)) |
michael@0 | 564 | return rv; |
michael@0 | 565 | |
michael@0 | 566 | lockFile->GetLastModifiedTime(&mReplacedLockTime); |
michael@0 | 567 | |
michael@0 | 568 | // always create the profile lock and never delete it so we can use its |
michael@0 | 569 | // modification timestamp to detect startup crashes |
michael@0 | 570 | mLockFileHandle = CreateFileW(filePath.get(), |
michael@0 | 571 | GENERIC_READ | GENERIC_WRITE, |
michael@0 | 572 | 0, // no sharing - of course |
michael@0 | 573 | nullptr, |
michael@0 | 574 | CREATE_ALWAYS, |
michael@0 | 575 | 0, |
michael@0 | 576 | nullptr); |
michael@0 | 577 | if (mLockFileHandle == INVALID_HANDLE_VALUE) { |
michael@0 | 578 | // XXXbsmedberg: provide a profile-unlocker here! |
michael@0 | 579 | return NS_ERROR_FILE_ACCESS_DENIED; |
michael@0 | 580 | } |
michael@0 | 581 | #elif defined(VMS) |
michael@0 | 582 | nsAutoCString filePath; |
michael@0 | 583 | rv = lockFile->GetNativePath(filePath); |
michael@0 | 584 | if (NS_FAILED(rv)) |
michael@0 | 585 | return rv; |
michael@0 | 586 | |
michael@0 | 587 | lockFile->GetLastModifiedTime(&mReplacedLockTime); |
michael@0 | 588 | |
michael@0 | 589 | mLockFileDesc = open_noshr(filePath.get(), O_CREAT, 0666); |
michael@0 | 590 | if (mLockFileDesc == -1) |
michael@0 | 591 | { |
michael@0 | 592 | if ((errno == EVMSERR) && (vaxc$errno == RMS$_FLK)) |
michael@0 | 593 | { |
michael@0 | 594 | return NS_ERROR_FILE_ACCESS_DENIED; |
michael@0 | 595 | } |
michael@0 | 596 | else |
michael@0 | 597 | { |
michael@0 | 598 | NS_ERROR("Failed to open lock file."); |
michael@0 | 599 | return NS_ERROR_FAILURE; |
michael@0 | 600 | } |
michael@0 | 601 | } |
michael@0 | 602 | #endif |
michael@0 | 603 | |
michael@0 | 604 | mHaveLock = true; |
michael@0 | 605 | |
michael@0 | 606 | return rv; |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | |
michael@0 | 610 | nsresult nsProfileLock::Unlock(bool aFatalSignal) |
michael@0 | 611 | { |
michael@0 | 612 | nsresult rv = NS_OK; |
michael@0 | 613 | |
michael@0 | 614 | if (mHaveLock) |
michael@0 | 615 | { |
michael@0 | 616 | #if defined (XP_WIN) |
michael@0 | 617 | if (mLockFileHandle != INVALID_HANDLE_VALUE) |
michael@0 | 618 | { |
michael@0 | 619 | CloseHandle(mLockFileHandle); |
michael@0 | 620 | mLockFileHandle = INVALID_HANDLE_VALUE; |
michael@0 | 621 | } |
michael@0 | 622 | #elif defined (XP_UNIX) |
michael@0 | 623 | if (mPidLockFileName) |
michael@0 | 624 | { |
michael@0 | 625 | PR_REMOVE_LINK(this); |
michael@0 | 626 | (void) unlink(mPidLockFileName); |
michael@0 | 627 | |
michael@0 | 628 | // Only free mPidLockFileName if we're not in the fatal signal |
michael@0 | 629 | // handler. The problem is that a call to free() might be the |
michael@0 | 630 | // cause of this fatal signal. If so, calling free() might cause |
michael@0 | 631 | // us to wait on the malloc implementation's lock. We're already |
michael@0 | 632 | // holding this lock, so we'll deadlock. See bug 522332. |
michael@0 | 633 | if (!aFatalSignal) |
michael@0 | 634 | free(mPidLockFileName); |
michael@0 | 635 | mPidLockFileName = nullptr; |
michael@0 | 636 | } |
michael@0 | 637 | if (mLockFileDesc != -1) |
michael@0 | 638 | { |
michael@0 | 639 | close(mLockFileDesc); |
michael@0 | 640 | mLockFileDesc = -1; |
michael@0 | 641 | // Don't remove it |
michael@0 | 642 | } |
michael@0 | 643 | #endif |
michael@0 | 644 | |
michael@0 | 645 | mHaveLock = false; |
michael@0 | 646 | } |
michael@0 | 647 | |
michael@0 | 648 | return rv; |
michael@0 | 649 | } |