profile/dirserviceprovider/src/nsProfileLock.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/profile/dirserviceprovider/src/nsProfileLock.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,649 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsProfileStringTypes.h"
    1.10 +#include "nsProfileLock.h"
    1.11 +#include "nsCOMPtr.h"
    1.12 +
    1.13 +#if defined(XP_MACOSX)
    1.14 +#include <Carbon/Carbon.h>
    1.15 +#include <CoreFoundation/CoreFoundation.h>
    1.16 +#endif
    1.17 +
    1.18 +#ifdef XP_UNIX
    1.19 +#include <unistd.h>
    1.20 +#include <fcntl.h>
    1.21 +#include <errno.h>
    1.22 +#include <signal.h>
    1.23 +#include <stdlib.h>
    1.24 +#include "prnetdb.h"
    1.25 +#include "prsystem.h"
    1.26 +#include "prprf.h"
    1.27 +#include "prenv.h"
    1.28 +#endif
    1.29 +
    1.30 +#ifdef VMS
    1.31 +#include <rmsdef.h>
    1.32 +#endif
    1.33 +
    1.34 +// **********************************************************************
    1.35 +// class nsProfileLock
    1.36 +//
    1.37 +// This code was moved from profile/src/nsProfileAccess.
    1.38 +// **********************************************************************
    1.39 +
    1.40 +#if defined (XP_UNIX)
    1.41 +static bool sDisableSignalHandling = false;
    1.42 +#endif
    1.43 +
    1.44 +nsProfileLock::nsProfileLock() :
    1.45 +    mHaveLock(false),
    1.46 +    mReplacedLockTime(0)
    1.47 +#if defined (XP_WIN)
    1.48 +    ,mLockFileHandle(INVALID_HANDLE_VALUE)
    1.49 +#elif defined (XP_UNIX)
    1.50 +    ,mPidLockFileName(nullptr)
    1.51 +    ,mLockFileDesc(-1)
    1.52 +#endif
    1.53 +{
    1.54 +#if defined (XP_UNIX)
    1.55 +    next = prev = this;
    1.56 +    sDisableSignalHandling = PR_GetEnv("MOZ_DISABLE_SIG_HANDLER") ? true : false;
    1.57 +#endif
    1.58 +}
    1.59 +
    1.60 +
    1.61 +nsProfileLock::nsProfileLock(nsProfileLock& src)
    1.62 +{
    1.63 +    *this = src;
    1.64 +}
    1.65 +
    1.66 +
    1.67 +nsProfileLock& nsProfileLock::operator=(nsProfileLock& rhs)
    1.68 +{
    1.69 +    Unlock();
    1.70 +
    1.71 +    mHaveLock = rhs.mHaveLock;
    1.72 +    rhs.mHaveLock = false;
    1.73 +
    1.74 +#if defined (XP_WIN)
    1.75 +    mLockFileHandle = rhs.mLockFileHandle;
    1.76 +    rhs.mLockFileHandle = INVALID_HANDLE_VALUE;
    1.77 +#elif defined (XP_UNIX)
    1.78 +    mLockFileDesc = rhs.mLockFileDesc;
    1.79 +    rhs.mLockFileDesc = -1;
    1.80 +    mPidLockFileName = rhs.mPidLockFileName;
    1.81 +    rhs.mPidLockFileName = nullptr;
    1.82 +    if (mPidLockFileName)
    1.83 +    {
    1.84 +        // rhs had a symlink lock, therefore it was on the list.
    1.85 +        PR_REMOVE_LINK(&rhs);
    1.86 +        PR_APPEND_LINK(this, &mPidLockList);
    1.87 +    }
    1.88 +#endif
    1.89 +
    1.90 +    return *this;
    1.91 +}
    1.92 +
    1.93 +
    1.94 +nsProfileLock::~nsProfileLock()
    1.95 +{
    1.96 +    Unlock();
    1.97 +}
    1.98 +
    1.99 +
   1.100 +#if defined (XP_UNIX)
   1.101 +
   1.102 +static int setupPidLockCleanup;
   1.103 +
   1.104 +PRCList nsProfileLock::mPidLockList =
   1.105 +    PR_INIT_STATIC_CLIST(&nsProfileLock::mPidLockList);
   1.106 +
   1.107 +void nsProfileLock::RemovePidLockFiles(bool aFatalSignal)
   1.108 +{
   1.109 +    while (!PR_CLIST_IS_EMPTY(&mPidLockList))
   1.110 +    {
   1.111 +        nsProfileLock *lock = static_cast<nsProfileLock*>(mPidLockList.next);
   1.112 +        lock->Unlock(aFatalSignal);
   1.113 +    }
   1.114 +}
   1.115 +
   1.116 +static struct sigaction SIGHUP_oldact;
   1.117 +static struct sigaction SIGINT_oldact;
   1.118 +static struct sigaction SIGQUIT_oldact;
   1.119 +static struct sigaction SIGILL_oldact;
   1.120 +static struct sigaction SIGABRT_oldact;
   1.121 +static struct sigaction SIGSEGV_oldact;
   1.122 +static struct sigaction SIGTERM_oldact;
   1.123 +
   1.124 +void nsProfileLock::FatalSignalHandler(int signo
   1.125 +#ifdef SA_SIGINFO
   1.126 +                                       , siginfo_t *info, void *context
   1.127 +#endif
   1.128 +                                       )
   1.129 +{
   1.130 +    // Remove any locks still held.
   1.131 +    RemovePidLockFiles(true);
   1.132 +
   1.133 +    // Chain to the old handler, which may exit.
   1.134 +    struct sigaction *oldact = nullptr;
   1.135 +
   1.136 +    switch (signo) {
   1.137 +      case SIGHUP:
   1.138 +        oldact = &SIGHUP_oldact;
   1.139 +        break;
   1.140 +      case SIGINT:
   1.141 +        oldact = &SIGINT_oldact;
   1.142 +        break;
   1.143 +      case SIGQUIT:
   1.144 +        oldact = &SIGQUIT_oldact;
   1.145 +        break;
   1.146 +      case SIGILL:
   1.147 +        oldact = &SIGILL_oldact;
   1.148 +        break;
   1.149 +      case SIGABRT:
   1.150 +        oldact = &SIGABRT_oldact;
   1.151 +        break;
   1.152 +      case SIGSEGV:
   1.153 +        oldact = &SIGSEGV_oldact;
   1.154 +        break;
   1.155 +      case SIGTERM:
   1.156 +        oldact = &SIGTERM_oldact;
   1.157 +        break;
   1.158 +      default:
   1.159 +        NS_NOTREACHED("bad signo");
   1.160 +        break;
   1.161 +    }
   1.162 +
   1.163 +    if (oldact) {
   1.164 +        if (oldact->sa_handler == SIG_DFL) {
   1.165 +            // Make sure the default sig handler is executed
   1.166 +            // We need it to get Mozilla to dump core.
   1.167 +            sigaction(signo,oldact, nullptr);
   1.168 +
   1.169 +            // Now that we've restored the default handler, unmask the
   1.170 +            // signal and invoke it.
   1.171 +
   1.172 +            sigset_t unblock_sigs;
   1.173 +            sigemptyset(&unblock_sigs);
   1.174 +            sigaddset(&unblock_sigs, signo);
   1.175 +
   1.176 +            sigprocmask(SIG_UNBLOCK, &unblock_sigs, nullptr);
   1.177 +
   1.178 +            raise(signo);
   1.179 +        }
   1.180 +#ifdef SA_SIGINFO
   1.181 +        else if (oldact->sa_sigaction &&
   1.182 +                 (oldact->sa_flags & SA_SIGINFO) == SA_SIGINFO) {
   1.183 +            oldact->sa_sigaction(signo, info, context);
   1.184 +        }
   1.185 +#endif
   1.186 +        else if (oldact->sa_handler && oldact->sa_handler != SIG_IGN)
   1.187 +        {
   1.188 +            oldact->sa_handler(signo);
   1.189 +        }
   1.190 +    }
   1.191 +
   1.192 +    // Backstop exit call, just in case.
   1.193 +    _exit(signo);
   1.194 +}
   1.195 +
   1.196 +nsresult nsProfileLock::LockWithFcntl(nsIFile *aLockFile)
   1.197 +{
   1.198 +    nsresult rv = NS_OK;
   1.199 +
   1.200 +    nsAutoCString lockFilePath;
   1.201 +    rv = aLockFile->GetNativePath(lockFilePath);
   1.202 +    if (NS_FAILED(rv)) {
   1.203 +        NS_ERROR("Could not get native path");
   1.204 +        return rv;
   1.205 +    }
   1.206 +
   1.207 +    aLockFile->GetLastModifiedTime(&mReplacedLockTime);
   1.208 +
   1.209 +    mLockFileDesc = open(lockFilePath.get(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
   1.210 +    if (mLockFileDesc != -1)
   1.211 +    {
   1.212 +        struct flock lock;
   1.213 +        lock.l_start = 0;
   1.214 +        lock.l_len = 0; // len = 0 means entire file
   1.215 +        lock.l_type = F_WRLCK;
   1.216 +        lock.l_whence = SEEK_SET;
   1.217 +
   1.218 +        // If fcntl(F_GETLK) fails then the server does not support/allow fcntl(),
   1.219 +        // return failure rather than access denied in this case so we fallback
   1.220 +        // to using a symlink lock, bug 303633.
   1.221 +        struct flock testlock = lock;
   1.222 +        if (fcntl(mLockFileDesc, F_GETLK, &testlock) == -1)
   1.223 +        {
   1.224 +            close(mLockFileDesc);
   1.225 +            mLockFileDesc = -1;
   1.226 +            rv = NS_ERROR_FAILURE;
   1.227 +        }
   1.228 +        else if (fcntl(mLockFileDesc, F_SETLK, &lock) == -1)
   1.229 +        {
   1.230 +            close(mLockFileDesc);
   1.231 +            mLockFileDesc = -1;
   1.232 +
   1.233 +            // With OS X, on NFS, errno == ENOTSUP
   1.234 +            // XXX Check for that and return specific rv for it?
   1.235 +#ifdef DEBUG
   1.236 +            printf("fcntl(F_SETLK) failed. errno = %d\n", errno);
   1.237 +#endif
   1.238 +            if (errno == EAGAIN || errno == EACCES)
   1.239 +                rv = NS_ERROR_FILE_ACCESS_DENIED;
   1.240 +            else
   1.241 +                rv = NS_ERROR_FAILURE;
   1.242 +        }
   1.243 +        else
   1.244 +            mHaveLock = true;
   1.245 +    }
   1.246 +    else
   1.247 +    {
   1.248 +        NS_ERROR("Failed to open lock file.");
   1.249 +        rv = NS_ERROR_FAILURE;
   1.250 +    }
   1.251 +    return rv;
   1.252 +}
   1.253 +
   1.254 +static bool IsSymlinkStaleLock(struct in_addr* aAddr, const char* aFileName,
   1.255 +                                 bool aHaveFcntlLock)
   1.256 +{
   1.257 +    // the link exists; see if it's from this machine, and if
   1.258 +    // so if the process is still active
   1.259 +    char buf[1024];
   1.260 +    int len = readlink(aFileName, buf, sizeof buf - 1);
   1.261 +    if (len > 0)
   1.262 +    {
   1.263 +        buf[len] = '\0';
   1.264 +        char *colon = strchr(buf, ':');
   1.265 +        if (colon)
   1.266 +        {
   1.267 +            *colon++ = '\0';
   1.268 +            unsigned long addr = inet_addr(buf);
   1.269 +            if (addr != (unsigned long) -1)
   1.270 +            {
   1.271 +                if (colon[0] == '+' && aHaveFcntlLock) {
   1.272 +                    // This lock was placed by a Firefox build which would have
   1.273 +                    // taken the fnctl lock, and we've already taken the fcntl lock,
   1.274 +                    // so the process that created this obsolete lock must be gone
   1.275 +                    return true;
   1.276 +                }
   1.277 +                    
   1.278 +                char *after = nullptr;
   1.279 +                pid_t pid = strtol(colon, &after, 0);
   1.280 +                if (pid != 0 && *after == '\0')
   1.281 +                {
   1.282 +                    if (addr != aAddr->s_addr)
   1.283 +                    {
   1.284 +                        // Remote lock: give up even if stuck.
   1.285 +                        return false;
   1.286 +                    }
   1.287 +    
   1.288 +                    // kill(pid,0) is a neat trick to check if a
   1.289 +                    // process exists
   1.290 +                    if (kill(pid, 0) == 0 || errno != ESRCH)
   1.291 +                    {
   1.292 +                        // Local process appears to be alive, ass-u-me it
   1.293 +                        // is another Mozilla instance, or a compatible
   1.294 +                        // derivative, that's currently using the profile.
   1.295 +                        // XXX need an "are you Mozilla?" protocol
   1.296 +                        return false;
   1.297 +                    }
   1.298 +                }
   1.299 +            }
   1.300 +        }
   1.301 +    }
   1.302 +    return true;
   1.303 +}
   1.304 +
   1.305 +nsresult nsProfileLock::LockWithSymlink(nsIFile *aLockFile, bool aHaveFcntlLock)
   1.306 +{
   1.307 +    nsresult rv;
   1.308 +    nsAutoCString lockFilePath;
   1.309 +    rv = aLockFile->GetNativePath(lockFilePath);
   1.310 +    if (NS_FAILED(rv)) {
   1.311 +        NS_ERROR("Could not get native path");
   1.312 +        return rv;
   1.313 +    }
   1.314 +
   1.315 +    // don't replace an existing lock time if fcntl already got one
   1.316 +    if (!mReplacedLockTime)
   1.317 +        aLockFile->GetLastModifiedTimeOfLink(&mReplacedLockTime);
   1.318 +
   1.319 +    struct in_addr inaddr;
   1.320 +    inaddr.s_addr = htonl(INADDR_LOOPBACK);
   1.321 +
   1.322 +    char hostname[256];
   1.323 +    PRStatus status = PR_GetSystemInfo(PR_SI_HOSTNAME, hostname, sizeof hostname);
   1.324 +    if (status == PR_SUCCESS)
   1.325 +    {
   1.326 +        char netdbbuf[PR_NETDB_BUF_SIZE];
   1.327 +        PRHostEnt hostent;
   1.328 +        status = PR_GetHostByName(hostname, netdbbuf, sizeof netdbbuf, &hostent);
   1.329 +        if (status == PR_SUCCESS)
   1.330 +            memcpy(&inaddr, hostent.h_addr, sizeof inaddr);
   1.331 +    }
   1.332 +
   1.333 +    char *signature =
   1.334 +        PR_smprintf("%s:%s%lu", inet_ntoa(inaddr), aHaveFcntlLock ? "+" : "",
   1.335 +                    (unsigned long)getpid());
   1.336 +    const char *fileName = lockFilePath.get();
   1.337 +    int symlink_rv, symlink_errno = 0, tries = 0;
   1.338 +
   1.339 +    // use ns4.x-compatible symlinks if the FS supports them
   1.340 +    while ((symlink_rv = symlink(signature, fileName)) < 0)
   1.341 +    {
   1.342 +        symlink_errno = errno;
   1.343 +        if (symlink_errno != EEXIST)
   1.344 +            break;
   1.345 +
   1.346 +        if (!IsSymlinkStaleLock(&inaddr, fileName, aHaveFcntlLock))
   1.347 +            break;
   1.348 +
   1.349 +        // Lock seems to be bogus: try to claim it.  Give up after a large
   1.350 +        // number of attempts (100 comes from the 4.x codebase).
   1.351 +        (void) unlink(fileName);
   1.352 +        if (++tries > 100)
   1.353 +            break;
   1.354 +    }
   1.355 +
   1.356 +    PR_smprintf_free(signature);
   1.357 +    signature = nullptr;
   1.358 +
   1.359 +    if (symlink_rv == 0)
   1.360 +    {
   1.361 +        // We exclusively created the symlink: record its name for eventual
   1.362 +        // unlock-via-unlink.
   1.363 +        rv = NS_OK;
   1.364 +        mHaveLock = true;
   1.365 +        mPidLockFileName = strdup(fileName);
   1.366 +        if (mPidLockFileName)
   1.367 +        {
   1.368 +            PR_APPEND_LINK(this, &mPidLockList);
   1.369 +            if (!setupPidLockCleanup++)
   1.370 +            {
   1.371 +                // Clean up on normal termination.
   1.372 +                // This instanciates a dummy class, and will trigger the class
   1.373 +                // destructor when libxul is unloaded. This is equivalent to atexit(),
   1.374 +                // but gracefully handles dlclose().
   1.375 +                static RemovePidLockFilesExiting r;
   1.376 +
   1.377 +                // Clean up on abnormal termination, using POSIX sigaction.
   1.378 +                // Don't arm a handler if the signal is being ignored, e.g.,
   1.379 +                // because mozilla is run via nohup.
   1.380 +                if (!sDisableSignalHandling) {
   1.381 +                    struct sigaction act, oldact;
   1.382 +#ifdef SA_SIGINFO
   1.383 +                    act.sa_sigaction = FatalSignalHandler;
   1.384 +                    act.sa_flags = SA_SIGINFO;
   1.385 +#else
   1.386 +                    act.sa_handler = FatalSignalHandler;
   1.387 +#endif
   1.388 +                    sigfillset(&act.sa_mask);
   1.389 +
   1.390 +#define CATCH_SIGNAL(signame)                                           \
   1.391 +PR_BEGIN_MACRO                                                          \
   1.392 +  if (sigaction(signame, nullptr, &oldact) == 0 &&                      \
   1.393 +      oldact.sa_handler != SIG_IGN)                                     \
   1.394 +  {                                                                     \
   1.395 +      sigaction(signame, &act, &signame##_oldact);                      \
   1.396 +  }                                                                     \
   1.397 +  PR_END_MACRO
   1.398 +
   1.399 +                    CATCH_SIGNAL(SIGHUP);
   1.400 +                    CATCH_SIGNAL(SIGINT);
   1.401 +                    CATCH_SIGNAL(SIGQUIT);
   1.402 +                    CATCH_SIGNAL(SIGILL);
   1.403 +                    CATCH_SIGNAL(SIGABRT);
   1.404 +                    CATCH_SIGNAL(SIGSEGV);
   1.405 +                    CATCH_SIGNAL(SIGTERM);
   1.406 +
   1.407 +#undef CATCH_SIGNAL
   1.408 +                }
   1.409 +            }
   1.410 +        }
   1.411 +    }
   1.412 +    else if (symlink_errno == EEXIST)
   1.413 +        rv = NS_ERROR_FILE_ACCESS_DENIED;
   1.414 +    else
   1.415 +    {
   1.416 +#ifdef DEBUG
   1.417 +        printf("symlink() failed. errno = %d\n", errno);
   1.418 +#endif
   1.419 +        rv = NS_ERROR_FAILURE;
   1.420 +    }
   1.421 +    return rv;
   1.422 +}
   1.423 +#endif /* XP_UNIX */
   1.424 +
   1.425 +nsresult nsProfileLock::GetReplacedLockTime(PRTime *aResult) {
   1.426 +    *aResult = mReplacedLockTime;
   1.427 +    return NS_OK;
   1.428 +}
   1.429 +
   1.430 +nsresult nsProfileLock::Lock(nsIFile* aProfileDir,
   1.431 +                             nsIProfileUnlocker* *aUnlocker)
   1.432 +{
   1.433 +#if defined (XP_MACOSX)
   1.434 +    NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock");
   1.435 +    NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "parent.lock");
   1.436 +#elif defined (XP_UNIX)
   1.437 +    NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "lock");
   1.438 +    NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock");
   1.439 +#else
   1.440 +    NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, "parent.lock");
   1.441 +#endif
   1.442 +
   1.443 +    nsresult rv;
   1.444 +    if (aUnlocker)
   1.445 +        *aUnlocker = nullptr;
   1.446 +
   1.447 +    NS_ENSURE_STATE(!mHaveLock);
   1.448 +
   1.449 +    bool isDir;
   1.450 +    rv = aProfileDir->IsDirectory(&isDir);
   1.451 +    if (NS_FAILED(rv))
   1.452 +        return rv;
   1.453 +    if (!isDir)
   1.454 +        return NS_ERROR_FILE_NOT_DIRECTORY;
   1.455 +
   1.456 +    nsCOMPtr<nsIFile> lockFile;
   1.457 +    rv = aProfileDir->Clone(getter_AddRefs(lockFile));
   1.458 +    if (NS_FAILED(rv))
   1.459 +        return rv;
   1.460 +
   1.461 +    rv = lockFile->Append(LOCKFILE_NAME);
   1.462 +    if (NS_FAILED(rv))
   1.463 +        return rv;
   1.464 +        
   1.465 +#if defined(XP_MACOSX)
   1.466 +    // First, try locking using fcntl. It is more reliable on
   1.467 +    // a local machine, but may not be supported by an NFS server.
   1.468 +
   1.469 +    rv = LockWithFcntl(lockFile);
   1.470 +    if (NS_FAILED(rv) && (rv != NS_ERROR_FILE_ACCESS_DENIED))
   1.471 +    {
   1.472 +        // If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
   1.473 +        // assume we tried an NFS that does not support it. Now, try with symlink.
   1.474 +        rv = LockWithSymlink(lockFile, false);
   1.475 +    }
   1.476 +    
   1.477 +    if (NS_SUCCEEDED(rv))
   1.478 +    {
   1.479 +        // Check for the old-style lock used by pre-mozilla 1.3 builds.
   1.480 +        // Those builds used an earlier check to prevent the application
   1.481 +        // from launching if another instance was already running. Because
   1.482 +        // of that, we don't need to create an old-style lock as well.
   1.483 +        struct LockProcessInfo
   1.484 +        {
   1.485 +            ProcessSerialNumber psn;
   1.486 +            unsigned long launchDate;
   1.487 +        };
   1.488 +
   1.489 +        PRFileDesc *fd = nullptr;
   1.490 +        int32_t ioBytes;
   1.491 +        ProcessInfoRec processInfo;
   1.492 +        LockProcessInfo lockProcessInfo;
   1.493 +
   1.494 +        rv = lockFile->SetLeafName(OLD_LOCKFILE_NAME);
   1.495 +        if (NS_FAILED(rv))
   1.496 +            return rv;
   1.497 +        rv = lockFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
   1.498 +        if (NS_SUCCEEDED(rv))
   1.499 +        {
   1.500 +            ioBytes = PR_Read(fd, &lockProcessInfo, sizeof(LockProcessInfo));
   1.501 +            PR_Close(fd);
   1.502 +
   1.503 +            if (ioBytes == sizeof(LockProcessInfo))
   1.504 +            {
   1.505 +#ifdef __LP64__
   1.506 +                processInfo.processAppRef = nullptr;
   1.507 +#else
   1.508 +                processInfo.processAppSpec = nullptr;
   1.509 +#endif
   1.510 +                processInfo.processName = nullptr;
   1.511 +                processInfo.processInfoLength = sizeof(ProcessInfoRec);
   1.512 +                if (::GetProcessInformation(&lockProcessInfo.psn, &processInfo) == noErr &&
   1.513 +                    processInfo.processLaunchDate == lockProcessInfo.launchDate)
   1.514 +                {
   1.515 +                    return NS_ERROR_FILE_ACCESS_DENIED;
   1.516 +                }
   1.517 +            }
   1.518 +            else
   1.519 +            {
   1.520 +                NS_WARNING("Could not read lock file - ignoring lock");
   1.521 +            }
   1.522 +        }
   1.523 +        rv = NS_OK; // Don't propagate error from OpenNSPRFileDesc.
   1.524 +    }
   1.525 +#elif defined(XP_UNIX)
   1.526 +    // Get the old lockfile name
   1.527 +    nsCOMPtr<nsIFile> oldLockFile;
   1.528 +    rv = aProfileDir->Clone(getter_AddRefs(oldLockFile));
   1.529 +    if (NS_FAILED(rv))
   1.530 +        return rv;
   1.531 +    rv = oldLockFile->Append(OLD_LOCKFILE_NAME);
   1.532 +    if (NS_FAILED(rv))
   1.533 +        return rv;
   1.534 +
   1.535 +    // First, try locking using fcntl. It is more reliable on
   1.536 +    // a local machine, but may not be supported by an NFS server.
   1.537 +    rv = LockWithFcntl(lockFile);
   1.538 +    if (NS_SUCCEEDED(rv)) {
   1.539 +        // Check to see whether there is a symlink lock held by an older
   1.540 +        // Firefox build, and also place our own symlink lock --- but
   1.541 +        // mark it "obsolete" so that other newer builds can break the lock
   1.542 +        // if they obtain the fcntl lock
   1.543 +        rv = LockWithSymlink(oldLockFile, true);
   1.544 +
   1.545 +        // If the symlink failed for some reason other than it already
   1.546 +        // exists, then something went wrong e.g. the file system
   1.547 +        // doesn't support symlinks, or we don't have permission to
   1.548 +        // create a symlink there.  In such cases we should just
   1.549 +        // continue because it's unlikely there is an old build
   1.550 +        // running with a symlink there and we've already successfully
   1.551 +        // placed a fcntl lock.
   1.552 +        if (rv != NS_ERROR_FILE_ACCESS_DENIED)
   1.553 +            rv = NS_OK;
   1.554 +    }
   1.555 +    else if (rv != NS_ERROR_FILE_ACCESS_DENIED)
   1.556 +    {
   1.557 +        // If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
   1.558 +        // assume we tried an NFS that does not support it. Now, try with symlink
   1.559 +        // using the old symlink path
   1.560 +        rv = LockWithSymlink(oldLockFile, false);
   1.561 +    }
   1.562 +
   1.563 +#elif defined(XP_WIN)
   1.564 +    nsAutoString filePath;
   1.565 +    rv = lockFile->GetPath(filePath);
   1.566 +    if (NS_FAILED(rv))
   1.567 +        return rv;
   1.568 +
   1.569 +    lockFile->GetLastModifiedTime(&mReplacedLockTime);
   1.570 +
   1.571 +    // always create the profile lock and never delete it so we can use its
   1.572 +    // modification timestamp to detect startup crashes
   1.573 +    mLockFileHandle = CreateFileW(filePath.get(),
   1.574 +                                  GENERIC_READ | GENERIC_WRITE,
   1.575 +                                  0, // no sharing - of course
   1.576 +                                  nullptr,
   1.577 +                                  CREATE_ALWAYS,
   1.578 +                                  0,
   1.579 +                                  nullptr);
   1.580 +    if (mLockFileHandle == INVALID_HANDLE_VALUE) {
   1.581 +        // XXXbsmedberg: provide a profile-unlocker here!
   1.582 +        return NS_ERROR_FILE_ACCESS_DENIED;
   1.583 +    }
   1.584 +#elif defined(VMS)
   1.585 +    nsAutoCString filePath;
   1.586 +    rv = lockFile->GetNativePath(filePath);
   1.587 +    if (NS_FAILED(rv))
   1.588 +        return rv;
   1.589 +
   1.590 +    lockFile->GetLastModifiedTime(&mReplacedLockTime);
   1.591 +
   1.592 +    mLockFileDesc = open_noshr(filePath.get(), O_CREAT, 0666);
   1.593 +    if (mLockFileDesc == -1)
   1.594 +    {
   1.595 +        if ((errno == EVMSERR) && (vaxc$errno == RMS$_FLK))
   1.596 +        {
   1.597 +            return NS_ERROR_FILE_ACCESS_DENIED;
   1.598 +        }
   1.599 +        else
   1.600 +        {
   1.601 +            NS_ERROR("Failed to open lock file.");
   1.602 +            return NS_ERROR_FAILURE;
   1.603 +        }
   1.604 +    }
   1.605 +#endif
   1.606 +
   1.607 +    mHaveLock = true;
   1.608 +
   1.609 +    return rv;
   1.610 +}
   1.611 +
   1.612 +
   1.613 +nsresult nsProfileLock::Unlock(bool aFatalSignal)
   1.614 +{
   1.615 +    nsresult rv = NS_OK;
   1.616 +
   1.617 +    if (mHaveLock)
   1.618 +    {
   1.619 +#if defined (XP_WIN)
   1.620 +        if (mLockFileHandle != INVALID_HANDLE_VALUE)
   1.621 +        {
   1.622 +            CloseHandle(mLockFileHandle);
   1.623 +            mLockFileHandle = INVALID_HANDLE_VALUE;
   1.624 +        }
   1.625 +#elif defined (XP_UNIX)
   1.626 +        if (mPidLockFileName)
   1.627 +        {
   1.628 +            PR_REMOVE_LINK(this);
   1.629 +            (void) unlink(mPidLockFileName);
   1.630 +
   1.631 +            // Only free mPidLockFileName if we're not in the fatal signal
   1.632 +            // handler.  The problem is that a call to free() might be the
   1.633 +            // cause of this fatal signal.  If so, calling free() might cause
   1.634 +            // us to wait on the malloc implementation's lock.  We're already
   1.635 +            // holding this lock, so we'll deadlock. See bug 522332.
   1.636 +            if (!aFatalSignal)
   1.637 +                free(mPidLockFileName);
   1.638 +            mPidLockFileName = nullptr;
   1.639 +        }
   1.640 +        if (mLockFileDesc != -1)
   1.641 +        {
   1.642 +            close(mLockFileDesc);
   1.643 +            mLockFileDesc = -1;
   1.644 +            // Don't remove it
   1.645 +        }
   1.646 +#endif
   1.647 +
   1.648 +        mHaveLock = false;
   1.649 +    }
   1.650 +
   1.651 +    return rv;
   1.652 +}

mercurial