1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

locking: Separate out flock and signal code.

This commit is contained in:
Alasdair G Kergon 2014-05-01 17:37:14 +01:00
parent 239ba5bb04
commit 29a3fbf067
7 changed files with 333 additions and 227 deletions

View File

@ -42,17 +42,19 @@
@top_builddir@/lib/misc/configure.h
@top_srcdir@/lib/misc/crc.h
@top_srcdir@/lib/misc/intl.h
@top_srcdir@/lib/misc/util.h
@top_srcdir@/lib/misc/last-path-component.h
@top_srcdir@/lib/misc/lib.h
@top_srcdir@/lib/misc/lvm-exec.h
@top_srcdir@/lib/misc/lvm-file.h
@top_srcdir@/lib/misc/lvm-flock.h
@top_srcdir@/lib/misc/lvm-globals.h
@top_srcdir@/lib/misc/lvm-signal.h
@top_srcdir@/lib/misc/lvm-string.h
@top_builddir@/lib/misc/lvm-version.h
@top_srcdir@/lib/misc/lvm-wrappers.h
@top_srcdir@/lib/misc/lvm-percent.h
@top_srcdir@/lib/misc/lvm-wrappers.h
@top_srcdir@/lib/misc/sharedlib.h
@top_srcdir@/lib/misc/util.h
@top_srcdir@/lib/properties/prop_common.h
@top_srcdir@/lib/report/properties.h
@top_srcdir@/lib/report/report.h

View File

@ -106,7 +106,9 @@ SOURCES =\
misc/crc.c \
misc/lvm-exec.c \
misc/lvm-file.c \
misc/lvm-flock.c \
misc/lvm-globals.c \
misc/lvm-signal.c \
misc/lvm-string.c \
misc/lvm-wrappers.c \
misc/lvm-percent.c \

View File

@ -21,6 +21,7 @@
#include "defaults.h"
#include "lvm-file.h"
#include "lvm-string.h"
#include "lvm-flock.h"
#include "lvmcache.h"
#include <limits.h>
@ -30,237 +31,20 @@
#include <fcntl.h>
#include <signal.h>
struct lock_list {
struct dm_list list;
int lf;
char *res;
};
static struct dm_list _lock_list;
static char _lock_dir[PATH_MAX];
static int _prioritise_write_locks;
static sig_t _oldhandler;
static sigset_t _fullsigset, _intsigset;
static volatile sig_atomic_t _handler_installed;
/* Drop lock known to be shared with another file descriptor. */
static void _drop_shared_flock(const char *file, int fd)
{
log_debug_locking("_drop_shared_flock %s.", file);
if (close(fd) < 0)
log_sys_debug("close", file);
}
static void _undo_flock(const char *file, int fd)
{
struct stat buf1, buf2;
log_debug_locking("_undo_flock %s", file);
if (!flock(fd, LOCK_NB | LOCK_EX) &&
!stat(file, &buf1) &&
!fstat(fd, &buf2) &&
is_same_inode(buf1, buf2))
if (unlink(file))
log_sys_debug("unlink", file);
if (close(fd) < 0)
log_sys_debug("close", file);
}
static int _release_lock(const char *file, int unlock)
{
struct lock_list *ll;
struct dm_list *llh, *llt;
dm_list_iterate_safe(llh, llt, &_lock_list) {
ll = dm_list_item(llh, struct lock_list);
if (!file || !strcmp(ll->res, file)) {
dm_list_del(llh);
if (unlock) {
log_very_verbose("Unlocking %s", ll->res);
if (flock(ll->lf, LOCK_NB | LOCK_UN))
log_sys_debug("flock", ll->res);
_undo_flock(ll->res, ll->lf);
} else
_drop_shared_flock(ll->res, ll->lf);
dm_free(ll->res);
dm_free(llh);
if (file)
return 1;
}
}
return 0;
}
static void _fin_file_locking(void)
{
_release_lock(NULL, 1);
release_flocks(1);
}
static void _reset_file_locking(void)
{
_release_lock(NULL, 0);
}
static void _remove_ctrl_c_handler(void)
{
siginterrupt(SIGINT, 0);
if (!_handler_installed)
return;
_handler_installed = 0;
sigprocmask(SIG_SETMASK, &_fullsigset, NULL);
if (signal(SIGINT, _oldhandler) == SIG_ERR)
log_sys_error("signal", "_remove_ctrl_c_handler");
}
static void _trap_ctrl_c(int sig __attribute__((unused)))
{
_remove_ctrl_c_handler();
log_error("CTRL-c detected: giving up waiting for lock");
}
static void _install_ctrl_c_handler(void)
{
_handler_installed = 1;
if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) {
_handler_installed = 0;
return;
}
sigprocmask(SIG_SETMASK, &_intsigset, NULL);
siginterrupt(SIGINT, 1);
}
static int _do_flock(const char *file, int *fd, int operation, uint32_t nonblock)
{
int r = 1;
int old_errno;
struct stat buf1, buf2;
log_debug_locking("_do_flock %s %c%c", file,
operation == LOCK_EX ? 'W' : 'R', nonblock ? ' ' : 'B');
do {
if ((*fd > -1) && close(*fd))
log_sys_debug("close", file);
if ((*fd = open(file, O_CREAT | O_APPEND | O_RDWR, 0777)) < 0) {
log_sys_error("open", file);
return 0;
}
if (nonblock)
operation |= LOCK_NB;
else
_install_ctrl_c_handler();
r = flock(*fd, operation);
old_errno = errno;
if (!nonblock)
_remove_ctrl_c_handler();
if (r) {
errno = old_errno;
log_sys_error("flock", file);
if (close(*fd))
log_sys_debug("close", file);
*fd = -1;
return 0;
}
if (!stat(file, &buf1) && !fstat(*fd, &buf2) &&
is_same_inode(buf1, buf2))
return 1;
} while (!nonblock);
return_0;
}
#define AUX_LOCK_SUFFIX ":aux"
static int _do_write_priority_flock(const char *file, int *fd, int operation, uint32_t nonblock)
{
int r, fd_aux = -1;
char *file_aux = alloca(strlen(file) + sizeof(AUX_LOCK_SUFFIX));
strcpy(file_aux, file);
strcat(file_aux, AUX_LOCK_SUFFIX);
if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, 0))) {
if (operation == LOCK_EX) {
r = _do_flock(file, fd, operation, nonblock);
_undo_flock(file_aux, fd_aux);
} else {
_undo_flock(file_aux, fd_aux);
r = _do_flock(file, fd, operation, nonblock);
}
}
return r;
}
static int _lock_file(const char *file, uint32_t flags)
{
int operation;
uint32_t nonblock = flags & LCK_NONBLOCK;
int r;
struct lock_list *ll;
char state;
switch (flags & LCK_TYPE_MASK) {
case LCK_READ:
operation = LOCK_SH;
state = 'R';
break;
case LCK_WRITE:
operation = LOCK_EX;
state = 'W';
break;
case LCK_UNLOCK:
return _release_lock(file, 1);
default:
log_error("Unrecognised lock type: %d", flags & LCK_TYPE_MASK);
return 0;
}
if (!(ll = dm_malloc(sizeof(struct lock_list))))
return_0;
if (!(ll->res = dm_strdup(file))) {
dm_free(ll);
return_0;
}
ll->lf = -1;
log_very_verbose("Locking %s %c%c", ll->res, state,
nonblock ? ' ' : 'B');
(void) dm_prepare_selinux_context(file, S_IFREG);
if (_prioritise_write_locks)
r = _do_write_priority_flock(file, &ll->lf, operation, nonblock);
else
r = _do_flock(file, &ll->lf, operation, nonblock);
(void) dm_prepare_selinux_context(NULL, 0);
if (r)
dm_list_add(&_lock_list, &ll->list);
else {
dm_free(ll->res);
dm_free(ll);
stack;
}
return r;
release_flocks(0);
}
static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
@ -298,7 +82,7 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
return 0;
}
if (!_lock_file(lockfile, flags))
if (!lock_file(lockfile, flags))
return_0;
break;
case LCK_LV:
@ -352,6 +136,8 @@ int init_file_locking(struct locking_type *locking, struct cmd_context *cmd,
int r;
const char *locking_dir;
init_flock(cmd);
locking->lock_resource = _file_lock_resource;
locking->reset_locking = _reset_file_locking;
locking->fin_locking = _fin_file_locking;
@ -366,9 +152,6 @@ int init_file_locking(struct locking_type *locking, struct cmd_context *cmd,
strcpy(_lock_dir, locking_dir);
_prioritise_write_locks =
find_config_tree_bool(cmd, global_prioritise_write_locks_CFG, NULL);
(void) dm_prepare_selinux_context(_lock_dir, S_IFDIR);
r = dm_create_dir(_lock_dir);
(void) dm_prepare_selinux_context(NULL, 0);
@ -380,8 +163,6 @@ int init_file_locking(struct locking_type *locking, struct cmd_context *cmd,
if ((access(_lock_dir, R_OK | W_OK | X_OK) == -1) && (errno == EROFS))
return 0;
dm_list_init(&_lock_list);
if (sigfillset(&_intsigset) || sigfillset(&_fullsigset)) {
log_sys_error_suppress(suppress_messages, "sigfillset",
"init_file_locking");

223
lib/misc/lvm-flock.c Normal file
View File

@ -0,0 +1,223 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lib.h"
#include "config.h"
#include "lvm-file.h"
#include "lvm-flock.h"
#include "lvm-signal.h"
#include "locking.h"
#include <sys/file.h>
#include <fcntl.h>
struct lock_list {
struct dm_list list;
int lf;
char *res;
};
static struct dm_list _lock_list;
static int _prioritise_write_locks;
/* Drop lock known to be shared with another file descriptor. */
static void _drop_shared_flock(const char *file, int fd)
{
log_debug_locking("_drop_shared_flock %s.", file);
if (close(fd) < 0)
log_sys_debug("close", file);
}
static void _undo_flock(const char *file, int fd)
{
struct stat buf1, buf2;
log_debug_locking("_undo_flock %s", file);
if (!flock(fd, LOCK_NB | LOCK_EX) &&
!stat(file, &buf1) &&
!fstat(fd, &buf2) &&
is_same_inode(buf1, buf2))
if (unlink(file))
log_sys_debug("unlink", file);
if (close(fd) < 0)
log_sys_debug("close", file);
}
static int _release_lock(const char *file, int unlock)
{
struct lock_list *ll;
struct dm_list *llh, *llt;
dm_list_iterate_safe(llh, llt, &_lock_list) {
ll = dm_list_item(llh, struct lock_list);
if (!file || !strcmp(ll->res, file)) {
dm_list_del(llh);
if (unlock) {
log_very_verbose("Unlocking %s", ll->res);
if (flock(ll->lf, LOCK_NB | LOCK_UN))
log_sys_debug("flock", ll->res);
_undo_flock(ll->res, ll->lf);
} else
_drop_shared_flock(ll->res, ll->lf);
dm_free(ll->res);
dm_free(llh);
if (file)
return 1;
}
}
return 0;
}
void release_flocks(int unlock)
{
_release_lock(NULL, unlock);
}
static int _do_flock(const char *file, int *fd, int operation, uint32_t nonblock)
{
int r = 1;
int old_errno;
struct stat buf1, buf2;
log_debug_locking("_do_flock %s %c%c", file,
operation == LOCK_EX ? 'W' : 'R', nonblock ? ' ' : 'B');
do {
if ((*fd > -1) && close(*fd))
log_sys_debug("close", file);
if ((*fd = open(file, O_CREAT | O_APPEND | O_RDWR, 0777)) < 0) {
log_sys_error("open", file);
return 0;
}
if (nonblock)
operation |= LOCK_NB;
else
install_ctrl_c_handler();
r = flock(*fd, operation);
old_errno = errno;
if (!nonblock)
remove_ctrl_c_handler();
if (r) {
errno = old_errno;
log_sys_error("flock", file);
if (close(*fd))
log_sys_debug("close", file);
*fd = -1;
return 0;
}
if (!stat(file, &buf1) && !fstat(*fd, &buf2) &&
is_same_inode(buf1, buf2))
return 1;
} while (!nonblock);
return_0;
}
#define AUX_LOCK_SUFFIX ":aux"
static int _do_write_priority_flock(const char *file, int *fd, int operation, uint32_t nonblock)
{
int r, fd_aux = -1;
char *file_aux = alloca(strlen(file) + sizeof(AUX_LOCK_SUFFIX));
strcpy(file_aux, file);
strcat(file_aux, AUX_LOCK_SUFFIX);
if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, 0))) {
if (operation == LOCK_EX) {
r = _do_flock(file, fd, operation, nonblock);
_undo_flock(file_aux, fd_aux);
} else {
_undo_flock(file_aux, fd_aux);
r = _do_flock(file, fd, operation, nonblock);
}
}
return r;
}
int lock_file(const char *file, uint32_t flags)
{
int operation;
uint32_t nonblock = flags & LCK_NONBLOCK;
int r;
struct lock_list *ll;
char state;
switch (flags & LCK_TYPE_MASK) {
case LCK_READ:
operation = LOCK_SH;
state = 'R';
break;
case LCK_WRITE:
operation = LOCK_EX;
state = 'W';
break;
case LCK_UNLOCK:
return _release_lock(file, 1);
default:
log_error("Unrecognised lock type: %d", flags & LCK_TYPE_MASK);
return 0;
}
if (!(ll = dm_malloc(sizeof(struct lock_list))))
return_0;
if (!(ll->res = dm_strdup(file))) {
dm_free(ll);
return_0;
}
ll->lf = -1;
log_very_verbose("Locking %s %c%c", ll->res, state,
nonblock ? ' ' : 'B');
(void) dm_prepare_selinux_context(file, S_IFREG);
if (_prioritise_write_locks)
r = _do_write_priority_flock(file, &ll->lf, operation, nonblock);
else
r = _do_flock(file, &ll->lf, operation, nonblock);
(void) dm_prepare_selinux_context(NULL, 0);
if (r)
dm_list_add(&_lock_list, &ll->list);
else {
dm_free(ll->res);
dm_free(ll);
stack;
}
return r;
}
void init_flock(struct cmd_context *cmd)
{
dm_list_init(&_lock_list);
_prioritise_write_locks =
find_config_tree_bool(cmd, global_prioritise_write_locks_CFG, NULL);
}

22
lib/misc/lvm-flock.h Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2014 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _LVM_SIGNAL_H
# define _LVM_SIGNAL_H
void init_flock(struct cmd_context *cmd);
int lock_file(const char *file, uint32_t flags);
void release_flocks(int unlock);
#endif /* _LVM_SIGNAL_H */

56
lib/misc/lvm-signal.c Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lib.h"
#include "lvm-signal.h"
#include <signal.h>
static sig_t _oldhandler;
static sigset_t _fullsigset, _intsigset;
static volatile sig_atomic_t _handler_installed;
void remove_ctrl_c_handler(void)
{
siginterrupt(SIGINT, 0);
if (!_handler_installed)
return;
_handler_installed = 0;
sigprocmask(SIG_SETMASK, &_fullsigset, NULL);
if (signal(SIGINT, _oldhandler) == SIG_ERR)
log_sys_error("signal", "_remove_ctrl_c_handler");
}
static void _trap_ctrl_c(int sig __attribute__((unused)))
{
remove_ctrl_c_handler();
log_error("CTRL-c detected: giving up waiting for lock");
}
void install_ctrl_c_handler(void)
{
_handler_installed = 1;
if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) {
_handler_installed = 0;
return;
}
sigprocmask(SIG_SETMASK, &_intsigset, NULL);
siginterrupt(SIGINT, 1);
}

20
lib/misc/lvm-signal.h Normal file
View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _LVM_SIGNAL_H
# define _LVM_SIGNAL_H
void remove_ctrl_c_handler(void);
void install_ctrl_c_handler(void);
#endif