[honey] Improve spelling table key encoding
[xapian.git] / xapian-core / backends / flint_lock.cc
blob87fac7d0fca6198e3f75f2a64c1b349c290801a6
1 /** @file flint_lock.cc
2 * @brief Flint-compatible database locking.
3 */
4 /* Copyright (C) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017 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
19 * USA
22 #include <config.h>
24 #include "flint_lock.h"
26 #ifndef __WIN32__
27 #include "safeerrno.h"
29 #include "safefcntl.h"
30 #include <unistd.h>
31 #include <cstdlib>
32 #include <sys/types.h>
33 #include "safesyssocket.h"
34 #include <sys/wait.h>
35 #include <signal.h>
36 #include <cstring>
37 #endif
39 #include "closefrom.h"
40 #include "errno_to_string.h"
41 #include "omassert.h"
43 #ifdef __CYGWIN__
44 # include <cygwin/version.h>
45 # include <sys/cygwin.h>
46 #endif
48 #ifdef FLINTLOCK_USE_FLOCK
49 # include <sys/file.h>
50 #endif
52 #include "xapian/error.h"
54 using namespace std;
56 #ifndef F_OFD_SETLK
57 # ifdef __linux__
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
63 # endif
64 #endif
66 [[noreturn]]
67 static void
68 throw_cannot_test_lock()
70 throw Xapian::FeatureUnavailableError("Can't test lock without trying to "
71 "take it");
74 bool
75 FlintLock::test() const
77 // A database which doesn't support update can't be locked for update.
78 if (filename.empty()) return false;
80 #if defined __CYGWIN__ || defined __WIN32__
81 if (hFile != INVALID_HANDLE_VALUE) return true;
82 // Doesn't seem to be possible to check if the lock is held without briefly
83 // taking the lock.
84 throw_cannot_test_lock();
85 #elif defined FLINTLOCK_USE_FLOCK
86 if (fd != -1) return true;
87 // Doesn't seem to be possible to check if the lock is held without briefly
88 // taking the lock.
89 throw_cannot_test_lock();
90 #else
91 if (fd != -1) return true;
92 int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666);
93 if (lockfd < 0) {
94 // Couldn't open lockfile.
95 reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
96 throw_databaselockerror(why, filename, "Testing lock");
99 struct flock fl;
100 fl.l_type = F_WRLCK;
101 fl.l_whence = SEEK_SET;
102 fl.l_start = 0;
103 fl.l_len = 1;
104 fl.l_pid = 0;
105 while (fcntl(lockfd, F_GETLK, &fl) == -1) {
106 if (errno != EINTR) {
107 // Translate known errno values into a reason code.
108 int e = errno;
109 close(lockfd);
110 if (e == ENOSYS) {
111 // F_GETLK isn't implemented by GNU Hurd, and always fails with
112 // ENOSYS: https://bugs.debian.org/190367
113 throw_cannot_test_lock();
115 reason why = (e == ENOLCK ? UNSUPPORTED : UNKNOWN);
116 throw_databaselockerror(why, filename, "Testing lock");
119 close(lockfd);
120 return fl.l_type != F_UNLCK;
121 #endif
124 FlintLock::reason
125 FlintLock::lock(bool exclusive, bool wait, string & explanation) {
126 // Currently we only support exclusive locks.
127 (void)exclusive;
128 Assert(exclusive);
129 #if defined __CYGWIN__ || defined __WIN32__
130 Assert(hFile == INVALID_HANDLE_VALUE);
131 #ifdef __CYGWIN__
132 char fnm[MAX_PATH];
133 #if CYGWIN_VERSION_API_MAJOR == 0 && CYGWIN_VERSION_API_MINOR < 181
134 cygwin_conv_to_win32_path(filename.c_str(), fnm);
135 #else
136 if (cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, filename.c_str(),
137 fnm, MAX_PATH) < 0) {
138 explanation.assign("cygwin_conv_path failed: ");
139 errno_to_string(errno, explanation);
140 return UNKNOWN;
142 #endif
143 #else
144 const char *fnm = filename.c_str();
145 #endif
146 retry:
147 // FIXME: Use LockFileEx() for locking, which would allow proper blocking
148 // and also byte-range locking for when we implement MVCC. But is there a
149 // way to interwork with the CreateFile()-based locking while doing so?
150 hFile = CreateFile(fnm, GENERIC_WRITE, FILE_SHARE_READ,
151 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
152 if (hFile != INVALID_HANDLE_VALUE) return SUCCESS;
153 if (GetLastError() == ERROR_ALREADY_EXISTS) {
154 if (wait) {
155 Sleep(1000);
156 goto retry;
158 return INUSE;
160 explanation = string();
161 return UNKNOWN;
162 #elif defined FLINTLOCK_USE_FLOCK
163 // This is much simpler than using fcntl() due to saner semantics around
164 // releasing locks when closing other descriptors on the same file (at
165 // least on platforms where flock() isn't just a compatibility wrapper
166 // around fcntl()). We can't simply switch to this without breaking
167 // locking compatibility with previous releases, though it might be useful
168 // for porting to platforms without fcntl() locking. Also, flock()
169 // apparently doesn't work over NFS - perhaps that's OK, but we should at
170 // least check the failure mode.
171 Assert(fd == -1);
172 int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666);
173 if (lockfd < 0) {
174 // Couldn't open lockfile.
175 explanation.assign("Couldn't open lockfile: ");
176 errno_to_string(errno, explanation);
177 return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
180 int op = LOCK_EX;
181 if (!wait) op |= LOCK_NB;
182 while (flock(lockfd, op) == -1) {
183 if (errno != EINTR) {
184 // Lock failed - translate known errno values into a reason code.
185 close(lockfd);
186 switch (errno) {
187 case EWOULDBLOCK:
188 return INUSE;
189 case ENOLCK:
190 return UNSUPPORTED; // FIXME: what do we get for NFS?
191 default:
192 return UNKNOWN;
197 fd = lockfd;
198 return SUCCESS;
199 #else
200 Assert(fd == -1);
201 #if defined F_SETFD && defined FD_CLOEXEC
202 int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666);
203 #else
204 int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
205 #endif
206 if (lockfd < 0) {
207 // Couldn't open lockfile.
208 explanation.assign("Couldn't open lockfile: ");
209 errno_to_string(errno, explanation);
210 return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
213 #ifdef F_OFD_SETLK
214 // F_OFD_SETLK has exactly the semantics we want, so use it if it's
215 // available. Support was added in Linux 3.15, and there's work on
216 // getting it standardised via POSIX:
217 // http://austingroupbugs.net/view.php?id=768
219 // Use a static flag so we don't repeatedly try F_OFD_SETLK when
220 // the kernel in use doesn't support it. This should be safe in a
221 // threaded context - at worst multiple threads might end up trying
222 // F_OFD_SETLK and then setting f_ofd_setlk_fails to true.
223 static bool f_ofd_setlk_fails = false;
224 if (!f_ofd_setlk_fails) {
225 struct flock fl;
226 fl.l_type = F_WRLCK;
227 fl.l_whence = SEEK_SET;
228 fl.l_start = 0;
229 fl.l_len = 1;
230 fl.l_pid = 0;
231 while (fcntl(lockfd, wait ? F_OFD_SETLKW : F_OFD_SETLK, &fl) == -1) {
232 if (errno != EINTR) {
233 if (errno == EINVAL) {
234 // F_OFD_SETLK not supported by this kernel.
235 goto no_ofd_support;
237 // Lock failed - translate known errno values into a reason
238 // code.
239 int e = errno;
240 close(lockfd);
241 switch (e) {
242 case EACCES: case EAGAIN:
243 return INUSE;
244 case ENOLCK:
245 return UNSUPPORTED;
246 default:
247 return UNKNOWN;
251 fd = lockfd;
252 pid = 0;
253 return SUCCESS;
254 no_ofd_support:
255 f_ofd_setlk_fails = true;
257 #endif
259 // If stdin and/or stdout have been closed, it is possible that lockfd could
260 // be 0 or 1. We need fds 0 and 1 to be available in the child process to
261 // be stdin and stdout, and we can't use dup() on lockfd after locking it,
262 // as the lock won't be transferred, so we handle this corner case here by
263 // using F_DUPFD or by calling dup() once or twice so that lockfd >= 2.
264 if (rare(lockfd < 2)) {
265 // Note this temporarily requires one or two spare fds to work, but
266 // then we need two spare for socketpair() to succeed below anyway.
267 #ifdef F_DUPFD
268 // Where available, F_DUPFD allows us to directly get the first unused
269 // fd which is at least 2.
270 int lockfd_dup = fcntl(lockfd, F_DUPFD, 2);
271 int eno = errno;
272 close(lockfd);
273 if (lockfd_dup < 0) {
274 return ((eno == EMFILE || eno == ENFILE) ? FDLIMIT : UNKNOWN);
276 lockfd = lockfd_dup;
277 #else
278 // Otherwise we have to call dup() until we get one, though at least
279 // that's only at most twice.
280 int lockfd_dup = dup(lockfd);
281 if (rare(lockfd_dup < 2)) {
282 int eno = 0;
283 if (lockfd_dup < 0) {
284 eno = errno;
285 close(lockfd);
286 } else {
287 int lockfd_dup2 = dup(lockfd);
288 if (lockfd_dup2 < 0) {
289 eno = errno;
291 close(lockfd);
292 close(lockfd_dup);
293 lockfd = lockfd_dup2;
295 if (eno) {
296 return ((eno == EMFILE || eno == ENFILE) ? FDLIMIT : UNKNOWN);
298 } else {
299 close(lockfd);
300 lockfd = lockfd_dup;
302 #endif
305 int fds[2];
306 if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, PF_UNSPEC, fds) < 0) {
307 // Couldn't create socketpair.
308 explanation.assign("Couldn't create socketpair: ");
309 errno_to_string(errno, explanation);
310 reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
311 (void)close(lockfd);
312 return why;
315 pid_t child = fork();
317 if (child == 0) {
318 // Child process.
319 close(fds[0]);
321 #if defined F_SETFD && defined FD_CLOEXEC
322 // Clear close-on-exec flag, if we set it when we called socketpair().
323 // Clearing it here means there's no window where another thread in the
324 // parent process could fork()+exec() and end up with this fd still
325 // open (assuming close-on-exec is supported).
327 // We can't use a preprocessor check on the *value* of SOCK_CLOEXEC as
328 // on Linux SOCK_CLOEXEC is an enum, with '#define SOCK_CLOEXEC
329 // SOCK_CLOEXEC' to allow '#ifdef SOCK_CLOEXEC' to work.
330 if (SOCK_CLOEXEC != 0)
331 (void)fcntl(fds[1], F_SETFD, 0);
332 if (O_CLOEXEC != 0)
333 (void)fcntl(lockfd, F_SETFD, 0);
334 #endif
335 // Connect pipe to stdin and stdout.
336 dup2(fds[1], 0);
337 dup2(fds[1], 1);
339 // Make sure we don't hang on to open files which may get deleted but
340 // not have their disk space released until we exit. Close these
341 // before we try to get the lock because if one of them is open on
342 // the lock file then closing it after obtaining the lock would release
343 // the lock, which would be really bad.
344 for (int i = 2; i < lockfd; ++i) {
345 // Retry on EINTR; just ignore other errors (we'll get
346 // EBADF if the fd isn't open so that's OK).
347 while (close(i) < 0 && errno == EINTR) { }
349 closefrom(lockfd + 1);
351 reason why = SUCCESS;
353 struct flock fl;
354 fl.l_type = F_WRLCK;
355 fl.l_whence = SEEK_SET;
356 fl.l_start = 0;
357 fl.l_len = 1;
358 while (fcntl(lockfd, wait ? F_SETLKW : F_SETLK, &fl) == -1) {
359 if (errno != EINTR) {
360 // Lock failed - translate known errno values into a reason
361 // code.
362 if (errno == EACCES || errno == EAGAIN) {
363 why = INUSE;
364 } else if (errno == ENOLCK) {
365 why = UNSUPPORTED;
366 } else {
367 _exit(0);
369 break;
375 // Tell the parent if we got the lock, and if not, why not.
376 char ch = static_cast<char>(why);
377 while (write(1, &ch, 1) < 0) {
378 // EINTR means a signal interrupted us, so retry.
379 // Otherwise we're DOOMED! The best we can do is just exit
380 // and the parent process should get EOF and know the lock
381 // failed.
382 if (errno != EINTR) _exit(1);
384 if (why != SUCCESS) _exit(0);
387 // Make sure we don't block unmount of partition holding the current
388 // directory.
389 if (chdir("/") < 0) {
390 // We can't usefully do anything in response to an error, so just
391 // ignore it - the worst harm it can do is make it impossible to
392 // unmount a partition.
394 // We need the if statement because glibc's _FORTIFY_SOURCE mode
395 // gives a warning even if we cast the result to void.
398 // FIXME: use special statically linked helper instead of cat.
399 execl("/bin/cat", "/bin/cat", static_cast<void*>(NULL));
400 // Emulate cat ourselves (we try to avoid this to reduce VM overhead).
401 char ch;
402 while (read(0, &ch, 1) != 0) {
403 /* Do nothing */
405 _exit(0);
408 close(lockfd);
409 close(fds[1]);
411 if (child == -1) {
412 // Couldn't fork.
413 explanation.assign("Couldn't fork: ");
414 errno_to_string(errno, explanation);
415 close(fds[0]);
416 return UNKNOWN;
419 reason why = UNKNOWN;
421 // Parent process.
422 while (true) {
423 char ch;
424 ssize_t n = read(fds[0], &ch, 1);
425 if (n == 1) {
426 why = static_cast<reason>(ch);
427 if (why != SUCCESS) break;
428 // Got the lock.
429 fd = fds[0];
430 pid = child;
431 return SUCCESS;
433 if (n == 0) {
434 // EOF means the lock failed.
435 explanation.assign("Got EOF reading from child process");
436 break;
438 if (errno != EINTR) {
439 // Treat unexpected errors from read() as failure to get the lock.
440 explanation.assign("Error reading from child process: ");
441 errno_to_string(errno, explanation);
442 break;
446 close(fds[0]);
448 int status;
449 while (waitpid(child, &status, 0) < 0) {
450 if (errno != EINTR) break;
453 return why;
454 #endif
457 void
458 FlintLock::release() {
459 #if defined __CYGWIN__ || defined __WIN32__
460 if (hFile == INVALID_HANDLE_VALUE) return;
461 CloseHandle(hFile);
462 hFile = INVALID_HANDLE_VALUE;
463 #elif defined FLINTLOCK_USE_FLOCK
464 if (fd < 0) return;
465 close(fd);
466 fd = -1;
467 #else
468 if (fd < 0) return;
469 close(fd);
470 fd = -1;
471 #ifdef F_OFD_SETLK
472 if (pid == 0) return;
473 #endif
474 // Kill the child process which is holding the lock. Use SIGKILL since
475 // that can't be caught or ignored (we used to use SIGHUP, but if the
476 // application has set that to SIG_IGN, the child process inherits that
477 // setting, which sometimes results in the child process not exiting -
478 // noted on Linux).
480 // The only likely error from kill is ESRCH (pid doesn't exist). The other
481 // possibilities (according to the Linux man page) are EINVAL (invalid
482 // signal) and EPERM (don't have permission to SIGKILL the process) but in
483 // none of the cases does calling waitpid do us any good!
484 if (kill(pid, SIGKILL) == 0) {
485 int status;
486 while (waitpid(pid, &status, 0) < 0) {
487 if (errno != EINTR) break;
490 #endif
493 void
494 FlintLock::throw_databaselockerror(FlintLock::reason why,
495 const string & db_dir,
496 const string & explanation) const
498 string msg("Unable to get write lock on ");
499 msg += db_dir;
500 if (why == FlintLock::INUSE) {
501 msg += ": already locked";
502 } else if (why == FlintLock::UNSUPPORTED) {
503 msg += ": locking probably not supported by this FS";
504 } else if (why == FlintLock::FDLIMIT) {
505 msg += ": too many open files";
506 } else if (why == FlintLock::UNKNOWN) {
507 if (!explanation.empty())
508 msg += ": " + explanation;
510 throw Xapian::DatabaseLockError(msg);