|
|
4b6aa8 |
From d4b89774005c569d7fd576d0d0efa6b3dc877a4f Mon Sep 17 00:00:00 2001
|
|
|
4b6aa8 |
From: Jakub Filak <jfilak@redhat.com>
|
|
|
4b6aa8 |
Date: Mon, 18 Mar 2019 12:26:37 +0100
|
|
|
4b6aa8 |
Subject: [PATCH] dump_dir: allow (semi)recursive locking
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
This patch only tries to mitigate the consequences of a bug in code
|
|
|
4b6aa8 |
where someone tries to lock a dump directory while it is already locked
|
|
|
4b6aa8 |
by the same process. This usually happens when a callee accepts a path
|
|
|
4b6aa8 |
to directory and opens it on its own or when someone forgets to call
|
|
|
4b6aa8 |
dd_unlock() or in all the unpredictable circumstance we usually have to
|
|
|
4b6aa8 |
face in ABRT.
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
It is not possible to implement the lock counter using only a symbolic
|
|
|
4b6aa8 |
link and file system functions, thus I've decided to put the
|
|
|
4b6aa8 |
responsibility of unlocking to the first dd_lock() caller and disallow
|
|
|
4b6aa8 |
the consecutive callers to unlock the dump directory.
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
Related to abrt/abrt#898
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
Signed-off-by: Jakub Filak <jfilak@redhat.com>
|
|
|
4b6aa8 |
---
|
|
|
4b6aa8 |
src/include/dump_dir.h | 6 ++++++
|
|
|
4b6aa8 |
src/lib/dump_dir.c | 21 +++++++++++++-----
|
|
|
4b6aa8 |
tests/dump_dir.at | 49 ++++++++++++++++++++++++++++++++++++++++++
|
|
|
4b6aa8 |
3 files changed, 71 insertions(+), 5 deletions(-)
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
diff --git a/src/include/dump_dir.h b/src/include/dump_dir.h
|
|
|
4b6aa8 |
index badef17..b617c6c 100644
|
|
|
4b6aa8 |
--- a/src/include/dump_dir.h
|
|
|
4b6aa8 |
+++ b/src/include/dump_dir.h
|
|
|
4b6aa8 |
@@ -71,6 +71,12 @@ struct dump_dir {
|
|
|
4b6aa8 |
time_t dd_time;
|
|
|
4b6aa8 |
char *dd_type;
|
|
|
4b6aa8 |
int dd_fd;
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ /* In case of recursive locking the first caller owns the lock and is
|
|
|
4b6aa8 |
+ * responsible for unlocking. The consecutive dd_lock() callers acquire the
|
|
|
4b6aa8 |
+ * lock but are not able to unlock the dump directory.
|
|
|
4b6aa8 |
+ */
|
|
|
4b6aa8 |
+ int owns_lock;
|
|
|
4b6aa8 |
};
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
void dd_close(struct dump_dir *dd);
|
|
|
4b6aa8 |
diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c
|
|
|
4b6aa8 |
index eb0c176..5e32c08 100644
|
|
|
4b6aa8 |
--- a/src/lib/dump_dir.c
|
|
|
4b6aa8 |
+++ b/src/lib/dump_dir.c
|
|
|
4b6aa8 |
@@ -277,6 +277,7 @@ int create_symlink_lockfile_at(int dir_fd, const char* lock_file,
|
|
|
4b6aa8 |
if (strcmp(pid_buf, pid) == 0)
|
|
|
4b6aa8 |
{
|
|
|
4b6aa8 |
log("Lock file '%s' is already locked by us", lock_file);
|
|
|
4b6aa8 |
+ errno = EALREADY;
|
|
|
4b6aa8 |
return 0;
|
|
|
4b6aa8 |
}
|
|
|
4b6aa8 |
if (isdigit_str(pid_buf))
|
|
|
4b6aa8 |
@@ -359,7 +360,7 @@ static int dd_lock(struct dump_dir *dd, unsigned sleep_usec, int flags)
|
|
|
4b6aa8 |
int r = create_symlink_lockfile_at(dd->dd_fd, ".lock", pid_buf, log_all_warnings);
|
|
|
4b6aa8 |
if (r < 0)
|
|
|
4b6aa8 |
return r; /* error */
|
|
|
4b6aa8 |
- if (r > 0)
|
|
|
4b6aa8 |
+ if (r > 0 || EALREADY == errno)
|
|
|
4b6aa8 |
break; /* locked successfully */
|
|
|
4b6aa8 |
if (flags & DD_DONT_WAIT_FOR_LOCK)
|
|
|
4b6aa8 |
{
|
|
|
4b6aa8 |
@@ -371,6 +372,12 @@ static int dd_lock(struct dump_dir *dd, unsigned sleep_usec, int flags)
|
|
|
4b6aa8 |
log_all_warnings = false;
|
|
|
4b6aa8 |
}
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
+ /* Reset errno to 0 only if errno is EALREADY (used by
|
|
|
4b6aa8 |
+ * create_symlink_lockfile() to signal that the dump directory is already
|
|
|
4b6aa8 |
+ * locked by us) */
|
|
|
4b6aa8 |
+ if (!(dd->owns_lock = (errno != EALREADY)))
|
|
|
4b6aa8 |
+ errno = 0;
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
/* Are we called by dd_opendir (as opposed to dd_create)? */
|
|
|
4b6aa8 |
if (sleep_usec == WAIT_FOR_OTHER_PROCESS_USLEEP) /* yes */
|
|
|
4b6aa8 |
{
|
|
|
4b6aa8 |
@@ -382,8 +389,10 @@ static int dd_lock(struct dump_dir *dd, unsigned sleep_usec, int flags)
|
|
|
4b6aa8 |
*/
|
|
|
4b6aa8 |
if (missing_file)
|
|
|
4b6aa8 |
{
|
|
|
4b6aa8 |
- xunlinkat(dd->dd_fd, ".lock", /*only files*/0);
|
|
|
4b6aa8 |
- log_notice("Unlocked '%s' (no or corrupted '%s' file)", dd->dd_dirname, missing_file);
|
|
|
4b6aa8 |
+ if (dd->owns_lock)
|
|
|
4b6aa8 |
+ xunlinkat(dd->dd_fd, ".lock", /*only files*/0);
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ log_warning("Unlocked '%s/.lock' (no or corrupted '%s' file)", dd->dd_dirname, missing_file);
|
|
|
4b6aa8 |
if (--count == 0 || flags & DD_DONT_WAIT_FOR_LOCK)
|
|
|
4b6aa8 |
{
|
|
|
4b6aa8 |
errno = EISDIR; /* "this is an ordinary dir, not dump dir" */
|
|
|
4b6aa8 |
@@ -402,9 +411,11 @@ static void dd_unlock(struct dump_dir *dd)
|
|
|
4b6aa8 |
{
|
|
|
4b6aa8 |
if (dd->locked)
|
|
|
4b6aa8 |
{
|
|
|
4b6aa8 |
- dd->locked = 0;
|
|
|
4b6aa8 |
+ if (dd->owns_lock)
|
|
|
4b6aa8 |
+ xunlinkat(dd->dd_fd, ".lock", /*only files*/0);
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
- xunlinkat(dd->dd_fd, ".lock", /*only files*/0);
|
|
|
4b6aa8 |
+ dd->owns_lock = 0;
|
|
|
4b6aa8 |
+ dd->locked = 0;
|
|
|
4b6aa8 |
|
|
|
4b6aa8 |
log_info("Unlocked '%s/.lock'", dd->dd_dirname);
|
|
|
4b6aa8 |
}
|
|
|
4b6aa8 |
diff --git a/tests/dump_dir.at b/tests/dump_dir.at
|
|
|
4b6aa8 |
index dc95e5b..98d564c 100644
|
|
|
4b6aa8 |
--- a/tests/dump_dir.at
|
|
|
4b6aa8 |
+++ b/tests/dump_dir.at
|
|
|
4b6aa8 |
@@ -566,3 +566,52 @@ TS_MAIN
|
|
|
4b6aa8 |
}
|
|
|
4b6aa8 |
TS_RETURN_MAIN
|
|
|
4b6aa8 |
]])
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+## -------------- ##
|
|
|
4b6aa8 |
+## recursive_lock ##
|
|
|
4b6aa8 |
+## -------------- ##
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+AT_TESTFUN([recursive_lock],
|
|
|
4b6aa8 |
+[[
|
|
|
4b6aa8 |
+#include "internal_libreport.h"
|
|
|
4b6aa8 |
+#include <errno.h>
|
|
|
4b6aa8 |
+#include <assert.h>
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+int main(int argc, char **argv)
|
|
|
4b6aa8 |
+{
|
|
|
4b6aa8 |
+ g_verbose = 3;
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ char *path = tmpnam(NULL);
|
|
|
4b6aa8 |
+ struct dump_dir *dd = dd_create(path, -1L, DEFAULT_DUMP_DIR_MODE);
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ char *lock_path = concat_path_file(path, ".lock");
|
|
|
4b6aa8 |
+ struct stat buf;
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ assert(dd);
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ assert(lstat(lock_path, &buf) == 0 && S_ISLNK(buf.st_mode));
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ dd_create_basic_files(dd, -1L, "/");
|
|
|
4b6aa8 |
+ dd_save_text(dd, "type", "custom");
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ struct dump_dir *dd2 = dd_opendir(path, DD_OPEN_READONLY);
|
|
|
4b6aa8 |
+ assert(dd2->owns_lock == 0);
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ struct dump_dir *dd3 = dd_opendir(path, 0);
|
|
|
4b6aa8 |
+ assert(dd3->owns_lock == 0);
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ dd_close(dd2);
|
|
|
4b6aa8 |
+ assert(lstat(lock_path, &buf) == 0 && S_ISLNK(buf.st_mode));
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ dd_close(dd3);
|
|
|
4b6aa8 |
+ assert(lstat(lock_path, &buf) == 0 && S_ISLNK(buf.st_mode));
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ dd_close(dd);
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ assert(stat(lock_path, &buf) != 0 && errno == ENOENT);
|
|
|
4b6aa8 |
+ free(lock_path);
|
|
|
4b6aa8 |
+
|
|
|
4b6aa8 |
+ return 0;
|
|
|
4b6aa8 |
+}
|
|
|
4b6aa8 |
+]])
|
|
|
4b6aa8 |
--
|
|
|
4b6aa8 |
2.21.0
|
|
|
4b6aa8 |
|