From c3064c9442283c46cf43808595c710d3350eb093 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Sun, 15 Nov 2009 19:26:36 +0000 Subject: [PATCH] Add temporary debug code to allow better debugging of races with various udev-event tools which wrongly scan and open internal temporary cryptsetup devices. If cryptsetup run in debug mode (--debug) and remove of device fails, code scan /proc directory and tries to find process name which locked that device. git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@141 36d66b0a-2a48-0410-832c-cd162a569da5 --- ChangeLog | 1 + lib/Makefile.am | 1 + lib/internal.h | 3 ++ lib/libdevmapper.c | 2 + lib/setup.c | 5 +++ lib/utils_debug.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 142 insertions(+) create mode 100644 lib/utils_debug.c diff --git a/ChangeLog b/ChangeLog index 387a9ed..413c49a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ * Add CRYPT_ prefix to enum defined in libcryptsetup.h. * Fix status call to fail when running as non-root user. * Check in configure if selinux libraries are required in static version. + * Add temporary debug code to find processes locking internal device. 2009-09-30 Milan Broz * Fix exported symbols and versions in libcryptsetup. diff --git a/lib/Makefile.am b/lib/Makefile.am index 2597cf3..ab75137 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -42,6 +42,7 @@ libcryptsetup_la_SOURCES = \ blockdev.h \ libcryptsetup.h \ utils.c \ + utils_debug.c \ backends.c \ libdevmapper.c \ gcrypt.c diff --git a/lib/internal.h b/lib/internal.h index a49e4d1..fe9dfa0 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -116,6 +116,9 @@ void logger(struct crypt_device *cd, int class, const char *file, int line, cons logger(c, CRYPT_LOG_ERROR, __FILE__, __LINE__, x); \ set_error(x); } while(0) +int crypt_get_debug_level(void); +void debug_processes_using_device(const char *name); + int crypt_memlock_inc(struct crypt_device *ctx); int crypt_memlock_dec(struct crypt_device *ctx); diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index beaeb34..f4b0bdd 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -249,6 +249,8 @@ int dm_remove_device(const char *name, int force, uint64_t size) if (--retries && r) { log_dbg("WARNING: other process locked internal device %s, %s.", name, retries ? "retrying remove" : "giving up"); + if (force && (crypt_get_debug_level() == CRYPT_LOG_DEBUG)) + debug_processes_using_device(name); sleep(1); if (force && !error_target) { /* If force flag is set, replace device with error, read-only target. diff --git a/lib/setup.c b/lib/setup.c index bd9bf25..43c2acb 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -47,6 +47,11 @@ void crypt_set_debug_level(int level) _debug_level = level; } +int crypt_get_debug_level() +{ + return _debug_level; +} + void crypt_log(struct crypt_device *cd, int class, const char *msg) { if (cd && cd->log) diff --git a/lib/utils_debug.c b/lib/utils_debug.c new file mode 100644 index 0000000..6616b5c --- /dev/null +++ b/lib/utils_debug.c @@ -0,0 +1,130 @@ +/* + * Temporary debug code to find processes locking internal cryptsetup devices. + * This code is intended to run only in debug mode. + * + * inspired by psmisc/fuser proc scanning code + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libcryptsetup.h" +#include "internal.h" + +#define MAX_PATHNAME 1024 +#define MAX_SHORTNAME 64 + +static int numeric_name(const char *name) +{ + return (name[0] < '0' || name[0] > '9') ? 0 : 1; +} + +static int check_pid(const pid_t pid, const char *dev_name, const char *short_dev_name) +{ + char dirpath[MAX_SHORTNAME], fdpath[MAX_SHORTNAME], linkpath[MAX_PATHNAME]; + DIR *dirp; + struct dirent *direntry; + size_t len; + int r = 0; + + snprintf(dirpath, sizeof(dirpath), "/proc/%d/fd", pid); + + if (!(dirp = opendir(dirpath))) + return r; + + while ((direntry = readdir(dirp))) { + if (!numeric_name(direntry->d_name)) + continue; + + snprintf(fdpath, sizeof(fdpath), "/proc/%d/fd/%s", pid, direntry->d_name); + + if ((len = readlink(fdpath, linkpath, MAX_PATHNAME-1)) < 0) + break; + linkpath[len] = '\0'; + + if (!strcmp(dev_name, linkpath)) { + r = 1; + break; + } + + if (!strcmp(short_dev_name, linkpath)) { + r = 2; + break; + } + } + closedir(dirp); + return r; +} + +static int read_proc_info(const pid_t pid, pid_t *ppid, char *name, int max_size) +{ + char path[MAX_SHORTNAME], info[max_size], c; + int fd, xpid, r = 0; + + snprintf(path, sizeof(path), "/proc/%u/stat", pid); + if ((fd = open(path, O_RDONLY)) < 0) + return 0; + + if (read(fd, info, max_size) > 0 && + sscanf(info, "%d %s %c %d", &xpid, name, &c, ppid) == 4) + r = 1; + + if (!r) { + *ppid = 0; + name[0] = '\0'; + } + close(fd); + return r; +} + +static void report_proc(const pid_t pid, const char *dev_name) +{ + char name[MAX_PATHNAME], name2[MAX_PATHNAME]; + pid_t ppid, ppid2; + + if (read_proc_info(pid, &ppid, name, MAX_PATHNAME) && + read_proc_info(ppid, &ppid2, name2, MAX_PATHNAME)) + log_dbg("WARNING: Process PID %u %s [PPID %u %s] spying on internal device %s.", + pid, name, ppid, name2, dev_name); +} + +void debug_processes_using_device(const char *dm_name) +{ + char short_dev_name[MAX_SHORTNAME], dev_name[MAX_PATHNAME]; + DIR *proc_dir; + struct dirent *proc_dentry; + struct stat st; + pid_t pid; + + if (crypt_get_debug_level() != CRYPT_LOG_DEBUG) + return; + + snprintf(dev_name, sizeof(dev_name), "/dev/mapper/%s", dm_name); + if (stat(dev_name, &st) || !S_ISBLK(st.st_mode)) + return; + snprintf(short_dev_name, sizeof(short_dev_name), "/dev/dm-%u", minor(st.st_rdev)); + + if (!(proc_dir = opendir("/proc"))) + return; + + while ((proc_dentry = readdir(proc_dir))) { + if (!numeric_name(proc_dentry->d_name)) + continue; + + pid = atoi(proc_dentry->d_name); + switch(check_pid(pid, dev_name, short_dev_name)) { + case 1: report_proc(pid, dev_name); + break; + case 2: report_proc(pid, short_dev_name); + default: + break; + } + } + closedir(proc_dir); +} -- 2.7.4