aboutsummaryrefslogtreecommitdiff
path: root/src/lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lock.c')
-rw-r--r--src/lock.c218
1 files changed, 142 insertions, 76 deletions
diff --git a/src/lock.c b/src/lock.c
index 23711aa..e6762f4 100644
--- a/src/lock.c
+++ b/src/lock.c
@@ -1,7 +1,7 @@
/* lock.c - Implement basic file locking for GDBM. */
/* This file is part of GDBM, the GNU data base manager.
- Copyright (C) 2008-2021 Free Software Foundation, Inc.
+ Copyright (C) 2008-2023 Free Software Foundation, Inc.
GDBM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -47,108 +47,174 @@
# define HAVE_FCNTL_LOCK 0
#endif
-#if 0
-int
-gdbm_locked (GDBM_FILE dbf)
+/* Return values for try_lock_ functions: */
+enum
+ {
+ TRY_LOCK_OK, /* Locking succeeded */
+ TRY_LOCK_FAIL, /* File already locked by another process. */
+ TRY_LOCK_NEXT /* Another error (including locking mechanism not
+ available). The caller should try next locking
+ mechanism. */
+
+ };
+
+/*
+ * Locking using flock().
+ */
+static int
+try_lock_flock (GDBM_FILE dbf)
{
- return (dbf->lock_type != LOCKING_NONE);
-}
+#if HAVE_FLOCK
+ if (flock (dbf->desc,
+ ((dbf->read_write == GDBM_READER) ? LOCK_SH : LOCK_EX)
+ | LOCK_NB) == 0)
+ {
+ return TRY_LOCK_OK;
+ }
+ else if (errno == EWOULDBLOCK)
+ {
+ return TRY_LOCK_FAIL;
+ }
#endif
+ return TRY_LOCK_NEXT;
+}
-void
-_gdbm_unlock_file (GDBM_FILE dbf)
+static void
+unlock_flock (GDBM_FILE dbf)
{
-#if HAVE_FCNTL_LOCK
- struct flock fl;
-#endif
-
- switch (dbf->lock_type)
- {
- case LOCKING_FLOCK:
#if HAVE_FLOCK
- flock (dbf->desc, LOCK_UN);
+ flock (dbf->desc, LOCK_UN);
#endif
- break;
+}
+
+/*
+ * Locking via lockf.
+ */
- case LOCKING_LOCKF:
+static int
+try_lock_lockf (GDBM_FILE dbf)
+{
#if HAVE_LOCKF
- lockf (dbf->desc, F_ULOCK, (off_t)0L);
+ /*
+ * NOTE: lockf will fail with EINVAL unless the database file was opened
+ * with write-only permission (O_WRONLY) or with read/write permission
+ * (O_RDWR). This means that this locking mechanism will always fail for
+ * databases opened with GDBM_READER,
+ */
+ if (dbf->read_write != GDBM_READER)
+ {
+ if (lockf (dbf->desc, F_TLOCK, (off_t)0L) == 0)
+ return TRY_LOCK_OK;
+
+ switch (errno)
+ {
+ case EACCES:
+ case EAGAIN:
+ case EDEADLK:
+ return TRY_LOCK_FAIL;
+
+ default:
+ /* try next locking method */
+ break;
+ }
+ }
#endif
- break;
+ return TRY_LOCK_NEXT;
+}
- case LOCKING_FCNTL:
-#if HAVE_FCNTL_LOCK
- fl.l_type = F_UNLCK;
- fl.l_whence = SEEK_SET;
- fl.l_start = fl.l_len = (off_t)0L;
- fcntl (dbf->desc, F_SETLK, &fl);
+static void
+unlock_lockf (GDBM_FILE dbf)
+{
+#if HAVE_LOCKF
+ lockf (dbf->desc, F_ULOCK, (off_t)0L);
#endif
- break;
-
- case LOCKING_NONE:
- break;
- }
-
- dbf->lock_type = LOCKING_NONE;
}
+
+/*
+ * Locking via fcntl().
+ */
-/* Try each supported locking mechanism. */
-int
-_gdbm_lock_file (GDBM_FILE dbf)
+static int
+try_lock_fcntl (GDBM_FILE dbf)
{
#if HAVE_FCNTL_LOCK
struct flock fl;
-#endif
- int lock_val = -1;
-#if HAVE_FLOCK
+ /* If we're still here, try fcntl. */
if (dbf->read_write == GDBM_READER)
- lock_val = flock (dbf->desc, LOCK_SH + LOCK_NB);
+ fl.l_type = F_RDLCK;
else
- lock_val = flock (dbf->desc, LOCK_EX + LOCK_NB);
-
- if ((lock_val == -1) && (errno == EWOULDBLOCK))
- {
- dbf->lock_type = LOCKING_NONE;
- return lock_val;
- }
- else if (lock_val != -1)
- {
- dbf->lock_type = LOCKING_FLOCK;
- return lock_val;
- }
-#endif
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = fl.l_len = (off_t)0L;
+ if (fcntl (dbf->desc, F_SETLK, &fl) == 0)
+ return TRY_LOCK_OK;
-#if HAVE_LOCKF
- /* Mask doesn't matter for lockf. */
- lock_val = lockf (dbf->desc, F_LOCK, (off_t)0L);
- if ((lock_val == -1) && (errno == EDEADLK))
+ switch (errno)
{
- dbf->lock_type = LOCKING_NONE;
- return lock_val;
- }
- else if (lock_val != -1)
- {
- dbf->lock_type = LOCKING_LOCKF;
- return lock_val;
+ case EACCES:
+ case EAGAIN:
+ case EDEADLK:
+ return TRY_LOCK_FAIL;
+
+ default:
+ /* try next locking method */
+ break;
}
+
#endif
+ return TRY_LOCK_NEXT;
+}
+static void
+unlock_fcntl (GDBM_FILE dbf)
+{
#if HAVE_FCNTL_LOCK
- /* If we're still here, try fcntl. */
- if (dbf->read_write == GDBM_READER)
- fl.l_type = F_RDLCK;
- else
- fl.l_type = F_WRLCK;
+ struct flock fl;
+
+ fl.l_type = F_UNLCK;
fl.l_whence = SEEK_SET;
fl.l_start = fl.l_len = (off_t)0L;
- lock_val = fcntl (dbf->desc, F_SETLK, &fl);
-
- if (lock_val != -1)
- dbf->lock_type = LOCKING_FCNTL;
+ fcntl (dbf->desc, F_SETLK, &fl);
#endif
+}
+
+/* Try each supported locking mechanism. */
+int
+_gdbm_lock_file (GDBM_FILE dbf)
+{
+ int res;
- if (lock_val == -1)
- dbf->lock_type = LOCKING_NONE;
- return lock_val;
+ dbf->lock_type = LOCKING_NONE;
+ if ((res = try_lock_flock (dbf)) == TRY_LOCK_OK)
+ dbf->lock_type = LOCKING_FLOCK;
+ else if (res == TRY_LOCK_NEXT)
+ {
+ if ((res = try_lock_lockf (dbf)) == TRY_LOCK_OK)
+ dbf->lock_type = LOCKING_LOCKF;
+ else if (res == TRY_LOCK_NEXT)
+ {
+ if (try_lock_fcntl (dbf) == TRY_LOCK_OK)
+ dbf->lock_type = LOCKING_FCNTL;
+ }
+ }
+
+ return dbf->lock_type == LOCKING_NONE ? -1 : 0;
}
+
+void
+_gdbm_unlock_file (GDBM_FILE dbf)
+{
+ void (*unlock_fn[]) (GDBM_FILE) = {
+ [LOCKING_FLOCK] = unlock_flock,
+ [LOCKING_LOCKF] = unlock_lockf,
+ [LOCKING_FCNTL] = unlock_fcntl
+ };
+
+ if (dbf->lock_type != LOCKING_NONE)
+ {
+ unlock_fn[dbf->lock_type] (dbf);
+ dbf->lock_type = LOCKING_NONE;
+ }
+}
+

Return to:

Send suggestions and report system problems to the System administrator.