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