profile/dirserviceprovider/src/nsProfileLock.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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 }

mercurial