1 /** @file flint_lock.cc
2 * @brief Flint-compatible database locking.
4 /* Copyright (C) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015 Olly Betts
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include "flint_lock.h"
27 #include "safeerrno.h"
29 #include "safefcntl.h"
32 #include <sys/types.h>
33 #include "safesyssocket.h"
39 #include "closefrom.h"
40 #include "errno_to_string.h"
44 # include <cygwin/version.h>
45 # include <sys/cygwin.h>
48 #ifdef FLINTLOCK_USE_FLOCK
49 # include <sys/file.h>
52 #include "xapian/error.h"
58 // Apparently defining _GNU_SOURCE should get us F_OFD_SETLK, etc, but that
59 // doesn't actually seem to work, so hard-code the known values.
60 # define F_OFD_GETLK 36
61 # define F_OFD_SETLK 37
62 # define F_OFD_SETLKW 38
67 FlintLock::lock(bool exclusive
, bool wait
, string
& explanation
) {
68 // Currently we only support exclusive locks.
71 #if defined __CYGWIN__ || defined __WIN32__
72 Assert(hFile
== INVALID_HANDLE_VALUE
);
75 #if CYGWIN_VERSION_API_MAJOR == 0 && CYGWIN_VERSION_API_MINOR < 181
76 cygwin_conv_to_win32_path(filename
.c_str(), fnm
);
78 if (cygwin_conv_path(CCP_POSIX_TO_WIN_A
|CCP_RELATIVE
, filename
.c_str(),
80 explanation
.assign("cygwin_conv_path failed: ");
81 errno_to_string(errno
, explanation
);
86 const char *fnm
= filename
.c_str();
89 // FIXME: Use LockFileEx() for locking, which would allow proper blocking
90 // and also byte-range locking for when we implement MVCC. But is there a
91 // way to interwork with the CreateFile()-based locking while doing so?
92 hFile
= CreateFile(fnm
, GENERIC_WRITE
, FILE_SHARE_READ
,
93 NULL
, OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
94 if (hFile
!= INVALID_HANDLE_VALUE
) return SUCCESS
;
95 if (GetLastError() == ERROR_ALREADY_EXISTS
) {
102 explanation
= string();
104 #elif defined FLINTLOCK_USE_FLOCK
105 // This is much simpler than using fcntl() due to saner semantics around
106 // releasing locks when closing other descriptors on the same file (at
107 // least on platforms where flock() isn't just a compatibility wrapper
108 // around fcntl()). We can't simply switch to this without breaking
109 // locking compatibility with previous releases, though it might be useful
110 // for porting to platforms without fcntl() locking. Also, flock()
111 // apparently doesn't work over NFS - perhaps that's OK, but we should at
112 // least check the failure mode.
114 int lockfd
= open(filename
.c_str(), O_WRONLY
| O_CREAT
| O_TRUNC
| O_CLOEXEC
, 0666);
116 // Couldn't open lockfile.
117 explanation
.assign("Couldn't open lockfile: ");
118 errno_to_string(errno
, explanation
);
119 return ((errno
== EMFILE
|| errno
== ENFILE
) ? FDLIMIT
: UNKNOWN
);
123 if (!wait
) op
|= LOCK_NB
;
124 while (flock(lockfd
, op
) == -1) {
125 if (errno
!= EINTR
) {
126 // Lock failed - translate known errno values into a reason code.
132 return UNSUPPORTED
; // FIXME: what do we get for NFS?
143 #if defined F_SETFD && defined FD_CLOEXEC
144 int lockfd
= open(filename
.c_str(), O_WRONLY
| O_CREAT
| O_TRUNC
| O_CLOEXEC
, 0666);
146 int lockfd
= open(filename
.c_str(), O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
149 // Couldn't open lockfile.
150 explanation
.assign("Couldn't open lockfile: ");
151 errno_to_string(errno
, explanation
);
152 return ((errno
== EMFILE
|| errno
== ENFILE
) ? FDLIMIT
: UNKNOWN
);
156 // F_OFD_SETLK has exactly the semantics we want, so use it if it's
157 // available. Support was added in Linux 3.15, and there's work on
158 // getting it standardised via POSIX:
159 // http://austingroupbugs.net/view.php?id=768
161 // Use a static flag so we don't repeatedly try F_OFD_SETLK when
162 // the kernel in use doesn't support it. This should be safe in a
163 // threaded context - at worst multiple threads might end up trying
164 // F_OFD_SETLK and then setting f_ofd_setlk_fails to true.
165 static bool f_ofd_setlk_fails
= false;
166 if (!f_ofd_setlk_fails
) {
169 fl
.l_whence
= SEEK_SET
;
173 while (fcntl(lockfd
, wait
? F_OFD_SETLKW
: F_OFD_SETLK
, &fl
) == -1) {
174 if (errno
!= EINTR
) {
175 if (errno
== EINVAL
) {
176 // F_OFD_SETLK not supported by this kernel.
179 // Lock failed - translate known errno values into a reason
184 case EACCES
: case EAGAIN
:
197 f_ofd_setlk_fails
= true;
201 // If stdin and/or stdout have been closed, it is possible that lockfd could
202 // be 0 or 1. We need fds 0 and 1 to be available in the child process to
203 // be stdin and stdout, and we can't use dup() on lockfd after locking it,
204 // as the lock won't be transferred, so we handle this corner case here by
205 // using F_DUPFD or by calling dup() once or twice so that lockfd >= 2.
206 if (rare(lockfd
< 2)) {
207 // Note this temporarily requires one or two spare fds to work, but
208 // then we need two spare for socketpair() to succeed below anyway.
210 // Where available, F_DUPFD allows us to directly get the first unused
211 // fd which is at least 2.
212 int lockfd_dup
= fcntl(lockfd
, F_DUPFD
, 2);
215 if (lockfd_dup
< 0) {
216 return ((eno
== EMFILE
|| eno
== ENFILE
) ? FDLIMIT
: UNKNOWN
);
220 // Otherwise we have to call dup() until we get one, though at least
221 // that's only at most twice.
222 int lockfd_dup
= dup(lockfd
);
223 if (rare(lockfd_dup
< 2)) {
225 if (lockfd_dup
< 0) {
229 int lockfd_dup2
= dup(lockfd
);
230 if (lockfd_dup2
< 0) {
235 lockfd
= lockfd_dup2
;
238 return ((eno
== EMFILE
|| eno
== ENFILE
) ? FDLIMIT
: UNKNOWN
);
248 if (socketpair(AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, PF_UNSPEC
, fds
) < 0) {
249 // Couldn't create socketpair.
250 explanation
.assign("Couldn't create socketpair: ");
251 errno_to_string(errno
, explanation
);
252 reason why
= ((errno
== EMFILE
|| errno
== ENFILE
) ? FDLIMIT
: UNKNOWN
);
257 pid_t child
= fork();
263 #if defined F_SETFD && defined FD_CLOEXEC
264 // Clear close-on-exec flag, if we set it when we called socketpair().
265 // Clearing it here means there's no window where another thread in the
266 // parent process could fork()+exec() and end up with this fd still
267 // open (assuming close-on-exec is supported).
269 // We can't use a preprocessor check on the *value* of SOCK_CLOEXEC as
270 // on Linux SOCK_CLOEXEC is an enum, with '#define SOCK_CLOEXEC
271 // SOCK_CLOEXEC' to allow '#ifdef SOCK_CLOEXEC' to work.
272 if (SOCK_CLOEXEC
!= 0)
273 (void)fcntl(fds
[1], F_SETFD
, 0);
275 (void)fcntl(lockfd
, F_SETFD
, 0);
277 // Connect pipe to stdin and stdout.
281 // Make sure we don't hang on to open files which may get deleted but
282 // not have their disk space released until we exit. Close these
283 // before we try to get the lock because if one of them is open on
284 // the lock file then closing it after obtaining the lock would release
285 // the lock, which would be really bad.
286 for (int i
= 2; i
< lockfd
; ++i
) {
287 // Retry on EINTR; just ignore other errors (we'll get
288 // EBADF if the fd isn't open so that's OK).
289 while (close(i
) < 0 && errno
== EINTR
) { }
291 closefrom(lockfd
+ 1);
293 reason why
= SUCCESS
;
297 fl
.l_whence
= SEEK_SET
;
300 while (fcntl(lockfd
, wait
? F_SETLKW
: F_SETLK
, &fl
) == -1) {
301 if (errno
!= EINTR
) {
302 // Lock failed - translate known errno values into a reason
304 if (errno
== EACCES
|| errno
== EAGAIN
) {
306 } else if (errno
== ENOLCK
) {
317 // Tell the parent if we got the lock, and if not, why not.
318 char ch
= static_cast<char>(why
);
319 while (write(1, &ch
, 1) < 0) {
320 // EINTR means a signal interrupted us, so retry.
321 // Otherwise we're DOOMED! The best we can do is just exit
322 // and the parent process should get EOF and know the lock
324 if (errno
!= EINTR
) _exit(1);
326 if (why
!= SUCCESS
) _exit(0);
329 // Make sure we don't block unmount of partition holding the current
331 if (chdir("/") < 0) {
332 // We can't usefully do anything in response to an error, so just
333 // ignore it - the worst harm it can do is make it impossible to
334 // unmount a partition.
336 // We need the if statement because glibc's _FORTIFY_SOURCE mode
337 // gives a warning even if we cast the result to void.
340 // FIXME: use special statically linked helper instead of cat.
341 execl("/bin/cat", "/bin/cat", static_cast<void*>(NULL
));
342 // Emulate cat ourselves (we try to avoid this to reduce VM overhead).
344 while (read(0, &ch
, 1) != 0) { /* Do nothing */ }
353 explanation
.assign("Couldn't fork: ");
354 errno_to_string(errno
, explanation
);
359 reason why
= UNKNOWN
;
364 ssize_t n
= read(fds
[0], &ch
, 1);
366 why
= static_cast<reason
>(ch
);
367 if (why
!= SUCCESS
) break;
374 // EOF means the lock failed.
375 explanation
.assign("Got EOF reading from child process");
378 if (errno
!= EINTR
) {
379 // Treat unexpected errors from read() as failure to get the lock.
380 explanation
.assign("Error reading from child process: ");
381 errno_to_string(errno
, explanation
);
389 while (waitpid(child
, &status
, 0) < 0) {
390 if (errno
!= EINTR
) break;
398 FlintLock::release() {
399 #if defined __CYGWIN__ || defined __WIN32__
400 if (hFile
== INVALID_HANDLE_VALUE
) return;
402 hFile
= INVALID_HANDLE_VALUE
;
403 #elif defined FLINTLOCK_USE_FLOCK
412 if (pid
== 0) return;
414 // Kill the child process which is holding the lock. Use SIGKILL since
415 // that can't be caught or ignored (we used to use SIGHUP, but if the
416 // application has set that to SIG_IGN, the child process inherits that
417 // setting, which sometimes results in the child process not exiting -
420 // The only likely error from kill is ESRCH (pid doesn't exist). The other
421 // possibilities (according to the Linux man page) are EINVAL (invalid
422 // signal) and EPERM (don't have permission to SIGKILL the process) but in
423 // none of the cases does calling waitpid do us any good!
424 if (kill(pid
, SIGKILL
) == 0) {
426 while (waitpid(pid
, &status
, 0) < 0) {
427 if (errno
!= EINTR
) break;
434 FlintLock::throw_databaselockerror(FlintLock::reason why
,
435 const string
& db_dir
,
436 const string
& explanation
)
438 string
msg("Unable to get write lock on ");
440 if (why
== FlintLock::INUSE
) {
441 msg
+= ": already locked";
442 } else if (why
== FlintLock::UNSUPPORTED
) {
443 msg
+= ": locking probably not supported by this FS";
444 } else if (why
== FlintLock::FDLIMIT
) {
445 msg
+= ": too many open files";
446 } else if (why
== FlintLock::UNKNOWN
) {
447 if (!explanation
.empty())
448 msg
+= ": " + explanation
;
450 throw Xapian::DatabaseLockError(msg
);