mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
Review locking: block signals instead of ignoring them and restore state
afterwards; avoid race condition with unlink; add LCK_HOLD to process_each_vg.
This commit is contained in:
parent
14a9cda63b
commit
568d7229bf
@ -32,11 +32,17 @@ struct lock_list {
|
||||
static struct list _lock_list;
|
||||
static char _lock_dir[NAME_LEN];
|
||||
|
||||
static sig_t _oldhandler;
|
||||
static sigset_t _fullsigset, _intsigset;
|
||||
static int _handler_installed;
|
||||
|
||||
static int _release_lock(const char *file)
|
||||
{
|
||||
struct lock_list *ll;
|
||||
struct list *llh, *llt;
|
||||
|
||||
struct stat buf1, buf2;
|
||||
|
||||
list_iterate_safe(llh, llt, &_lock_list) {
|
||||
ll = list_item(llh, struct lock_list);
|
||||
|
||||
@ -44,10 +50,13 @@ static int _release_lock(const char *file)
|
||||
list_del(llh);
|
||||
log_very_verbose("Unlocking %s", ll->res);
|
||||
|
||||
/*
|
||||
* If this is the last pid using the file, remove it
|
||||
*/
|
||||
if (!flock(ll->lf, LOCK_NB | LOCK_EX))
|
||||
if (flock(ll->lf, LOCK_NB | LOCK_UN))
|
||||
log_sys_error("flock", ll->res);
|
||||
|
||||
if (!flock(ll->lf, LOCK_NB | LOCK_EX) &&
|
||||
!stat(ll->res, &buf1) &&
|
||||
!fstat(ll->lf, &buf2) &&
|
||||
!memcmp(&buf1.st_ino, &buf2.st_ino, sizeof(ino_t)))
|
||||
if (unlink(ll->res))
|
||||
log_sys_error("unlink", ll->res);
|
||||
|
||||
@ -70,22 +79,35 @@ void fin_file_locking(void)
|
||||
_release_lock(NULL);
|
||||
}
|
||||
|
||||
static void _remove_ctrl_c_handler()
|
||||
{
|
||||
siginterrupt(SIGINT, 0);
|
||||
if (!_handler_installed || _oldhandler == SIG_ERR)
|
||||
return;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &_fullsigset, NULL);
|
||||
if (signal(SIGINT, _oldhandler) == SIG_ERR)
|
||||
log_sys_error("signal", "_remove_ctrl_c_handler");
|
||||
|
||||
_handler_installed = 0;
|
||||
}
|
||||
|
||||
void _trap_ctrl_c(int signal)
|
||||
{
|
||||
_remove_ctrl_c_handler();
|
||||
log_error("CTRL-c detected: giving up waiting for lock");
|
||||
return;
|
||||
}
|
||||
|
||||
static void _install_ctrl_c_handler()
|
||||
{
|
||||
siginterrupt(SIGINT, 1);
|
||||
signal(SIGINT, _trap_ctrl_c);
|
||||
}
|
||||
if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR)
|
||||
return;
|
||||
|
||||
static void _remove_ctrl_c_handler()
|
||||
{
|
||||
signal(SIGINT, SIG_IGN);
|
||||
siginterrupt(SIGINT, 0);
|
||||
sigprocmask(SIG_SETMASK, &_intsigset, NULL);
|
||||
siginterrupt(SIGINT, 1);
|
||||
|
||||
_handler_installed = 1;
|
||||
}
|
||||
|
||||
static int _lock_file(const char *file, int flags)
|
||||
@ -94,6 +116,7 @@ static int _lock_file(const char *file, int flags)
|
||||
int r = 1;
|
||||
|
||||
struct lock_list *ll;
|
||||
struct stat buf1, buf2;
|
||||
|
||||
switch (flags & LCK_TYPE_MASK) {
|
||||
case LCK_READ:
|
||||
@ -117,32 +140,45 @@ static int _lock_file(const char *file, int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ll->lf = -1;
|
||||
|
||||
log_very_verbose("Locking %s", ll->res);
|
||||
if ((ll->lf = open(file, O_CREAT | O_APPEND | O_RDWR, 0777)) < 0) {
|
||||
log_sys_error("open", file);
|
||||
dbg_free(ll->res);
|
||||
dbg_free(ll);
|
||||
return 0;
|
||||
}
|
||||
do {
|
||||
if (ll->lf > -1)
|
||||
close(ll->lf);
|
||||
|
||||
if ((flags & LCK_NONBLOCK))
|
||||
operation |= LOCK_NB;
|
||||
else
|
||||
_install_ctrl_c_handler();
|
||||
if ((ll->lf = open(file, O_CREAT | O_APPEND | O_RDWR, 0777))
|
||||
< 0) {
|
||||
log_sys_error("open", file);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (flock(ll->lf, operation)) {
|
||||
log_sys_error("flock", ll->res);
|
||||
dbg_free(ll->res);
|
||||
dbg_free(ll);
|
||||
r = 0;
|
||||
} else
|
||||
list_add(&_lock_list, &ll->list);
|
||||
if ((flags & LCK_NONBLOCK))
|
||||
operation |= LOCK_NB;
|
||||
else
|
||||
_install_ctrl_c_handler();
|
||||
|
||||
r = flock(ll->lf, operation);
|
||||
if (!(flags & LCK_NONBLOCK))
|
||||
_remove_ctrl_c_handler();
|
||||
|
||||
if (!(flags & LCK_NONBLOCK))
|
||||
_remove_ctrl_c_handler();
|
||||
if (r) {
|
||||
log_sys_error("flock", ll->res);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return r;
|
||||
if (!stat(ll->res, &buf1) && !fstat(ll->lf, &buf2) &&
|
||||
!memcmp(&buf1.st_ino, &buf2.st_ino, sizeof(ino_t)))
|
||||
break;
|
||||
} while (!(flags & LCK_NONBLOCK));
|
||||
|
||||
list_add(&_lock_list, &ll->list);
|
||||
return 1;
|
||||
|
||||
err:
|
||||
dbg_free(ll->res);
|
||||
dbg_free(ll);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lock_resource(struct cmd_context *cmd, const char *resource, int flags)
|
||||
@ -191,7 +227,6 @@ int lock_resource(struct cmd_context *cmd, const char *resource, int flags)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int init_file_locking(struct locking_type *locking, struct config_file *cf)
|
||||
{
|
||||
locking->lock_resource = lock_resource;
|
||||
@ -207,5 +242,15 @@ int init_file_locking(struct locking_type *locking, struct config_file *cf)
|
||||
|
||||
list_init(&_lock_list);
|
||||
|
||||
if (sigfillset(&_intsigset) || sigfillset(&_fullsigset)) {
|
||||
log_sys_error("sigfillset", "init_file_locking");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sigdelset(&_intsigset, SIGINT)) {
|
||||
log_sys_error("sigdelset", "init_file_locking");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -14,37 +14,45 @@
|
||||
#include <signal.h>
|
||||
|
||||
static struct locking_type _locking;
|
||||
static sigset_t _oldset;
|
||||
|
||||
static int _lock_count = 0; /* Number of locks held */
|
||||
static int _signals_ignored = 0;
|
||||
static int _lock_count = 0; /* Number of locks held */
|
||||
static int _signals_blocked = 0;
|
||||
|
||||
static void _ignore_signals(void)
|
||||
static void _block_signals(void)
|
||||
{
|
||||
int s;
|
||||
sigset_t set;
|
||||
|
||||
if (_signals_ignored)
|
||||
if (_signals_blocked)
|
||||
return;
|
||||
|
||||
for (s = 0; s < NSIG; s++)
|
||||
signal(s, SIG_IGN);
|
||||
if (sigfillset(&set)) {
|
||||
log_sys_error("sigfillset", "_block_signals");
|
||||
return;
|
||||
}
|
||||
|
||||
_signals_ignored = 1;
|
||||
if (sigprocmask(SIG_SETMASK, &set, &_oldset)) {
|
||||
log_sys_error("sigprocmask", "_block_signals");
|
||||
return;
|
||||
}
|
||||
|
||||
_signals_blocked = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void _enable_signals(void)
|
||||
static void _unblock_signals(void)
|
||||
{
|
||||
int s;
|
||||
|
||||
/* Don't enable signals while any locks are held */
|
||||
if (!_signals_ignored || _lock_count)
|
||||
/* Don't unblock signals while any locks are held */
|
||||
if (!_signals_blocked || _lock_count)
|
||||
return;
|
||||
|
||||
for (s = 0; s < NSIG; s++)
|
||||
signal(s, SIG_DFL);
|
||||
if (sigprocmask(SIG_SETMASK, &_oldset, NULL)) {
|
||||
log_sys_error("sigprocmask", "_block_signals");
|
||||
return;
|
||||
}
|
||||
|
||||
_signals_ignored = 0;
|
||||
_signals_blocked = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
@ -70,7 +78,7 @@ void no_fin_locking(void)
|
||||
return;
|
||||
}
|
||||
|
||||
static void _init_no_locking(struct locking_type *locking,
|
||||
static void _init_no_locking(struct locking_type *locking,
|
||||
struct config_file *cf)
|
||||
{
|
||||
locking->lock_resource = no_lock_resource;
|
||||
@ -114,21 +122,20 @@ void fin_locking(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* VG locking is by name
|
||||
* LV locking is by VG_name/LV_uuid
|
||||
* FIXME This should take a VG_uuid instead of VG_name
|
||||
* VG locking is by VG name.
|
||||
* FIXME This should become VG uuid.
|
||||
*/
|
||||
int _lock_vol(struct cmd_context *cmd, const char *resource, int flags)
|
||||
{
|
||||
_ignore_signals();
|
||||
_block_signals();
|
||||
|
||||
if (!(_locking.lock_resource(cmd, resource, flags))) {
|
||||
_enable_signals();
|
||||
_unblock_signals();
|
||||
return 0;
|
||||
}
|
||||
|
||||
_update_lock_count(flags);
|
||||
_enable_signals();
|
||||
_unblock_signals();
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -138,8 +145,8 @@ int lock_vol(struct cmd_context *cmd, const char *vol, int flags)
|
||||
char resource[258];
|
||||
|
||||
switch (flags & LCK_SCOPE_MASK) {
|
||||
case LCK_VG: /* Lock volume group before changing on-disk metadata. */
|
||||
case LCK_LV: /* Suspends LV if it's active. */
|
||||
case LCK_VG: /* Lock VG to change on-disk metadata. */
|
||||
case LCK_LV: /* Suspends LV if it's active. */
|
||||
strncpy(resource, (char *) vol, sizeof(resource));
|
||||
break;
|
||||
default:
|
||||
@ -153,11 +160,10 @@ int lock_vol(struct cmd_context *cmd, const char *vol, int flags)
|
||||
|
||||
/* Perform immediate unlock unless LCK_HOLD set */
|
||||
if (!(flags & LCK_HOLD) && ((flags & LCK_TYPE_MASK) != LCK_NONE)) {
|
||||
if (!_lock_vol(cmd, resource,
|
||||
if (!_lock_vol(cmd, resource,
|
||||
(flags & ~LCK_TYPE_MASK) | LCK_NONE))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
|
||||
log_verbose("Using volume group(s) on command line");
|
||||
for (; opt < argc; opt++) {
|
||||
vg_name = argv[opt];
|
||||
if (!lock_vol(cmd, vg_name, LCK_VG | lock_type)) {
|
||||
if (!lock_vol(cmd, vg_name, lock_type)) {
|
||||
log_error("Can't lock %s: skipping", vg_name);
|
||||
continue;
|
||||
}
|
||||
@ -150,7 +150,7 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
|
||||
}
|
||||
list_iterate(vgh, vgs) {
|
||||
vg_name = list_item(vgh, struct name_list)->name;
|
||||
if (!lock_vol(cmd, vg_name, LCK_VG | lock_type)) {
|
||||
if (!lock_vol(cmd, vg_name, lock_type)) {
|
||||
log_error("Can't lock %s: skipping", vg_name);
|
||||
continue;
|
||||
}
|
||||
|
@ -54,5 +54,5 @@ static int vg_backup_single(struct cmd_context *cmd, const char *vg_name)
|
||||
|
||||
int vgcfgbackup(struct cmd_context *cmd, int argc, char **argv)
|
||||
{
|
||||
return process_each_vg(cmd, argc, argv, LCK_READ, &vg_backup_single);
|
||||
return process_each_vg(cmd, argc, argv, LCK_VG_READ, &vg_backup_single);
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
return process_each_vg(cmd, argc, argv,
|
||||
(arg_count(cmd, available_ARG)) ?
|
||||
LCK_READ : LCK_WRITE, &vgchange_single);
|
||||
LCK_VG_READ : LCK_VG_WRITE, &vgchange_single);
|
||||
}
|
||||
|
||||
static int vgchange_single(struct cmd_context *cmd, const char *vg_name)
|
||||
|
@ -24,7 +24,7 @@ static int vgck_single(struct cmd_context *cmd, const char *vg_name);
|
||||
|
||||
int vgck(struct cmd_context *cmd, int argc, char **argv)
|
||||
{
|
||||
return process_each_vg(cmd, argc, argv, LCK_READ, &vgck_single);
|
||||
return process_each_vg(cmd, argc, argv, LCK_VG_READ, &vgck_single);
|
||||
}
|
||||
|
||||
static int vgck_single(struct cmd_context *cmd, const char *vg_name)
|
||||
|
@ -45,7 +45,7 @@ int vgdisplay(struct cmd_context *cmd, int argc, char **argv)
|
||||
}
|
||||
**********/
|
||||
|
||||
process_each_vg(cmd, argc, argv, LCK_READ, &vgdisplay_single);
|
||||
process_each_vg(cmd, argc, argv, LCK_VG_READ, &vgdisplay_single);
|
||||
|
||||
/******** FIXME Need to count number processed
|
||||
Add this to process_each_vg if arg_count(cmd,activevolumegroups_ARG) ?
|
||||
|
@ -34,7 +34,7 @@ int vgexport(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
return process_each_vg(cmd, argc, argv, LCK_READ, &vgexport_single);
|
||||
return process_each_vg(cmd, argc, argv, LCK_VG_READ, &vgexport_single);
|
||||
}
|
||||
|
||||
static int vgexport_single(struct cmd_context *cmd, const char *vg_name)
|
||||
|
@ -34,7 +34,7 @@ int vgimport(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
return process_each_vg(cmd, argc, argv, LCK_WRITE, &vgimport_single);
|
||||
return process_each_vg(cmd, argc, argv, LCK_VG_WRITE, &vgimport_single);
|
||||
}
|
||||
|
||||
static int vgimport_single(struct cmd_context *cmd, const char *vg_name)
|
||||
|
@ -31,7 +31,8 @@ int vgremove(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
ret = process_each_vg(cmd, argc, argv, LCK_WRITE | LCK_NONBLOCK,
|
||||
ret = process_each_vg(cmd, argc, argv,
|
||||
LCK_VG | LCK_WRITE | LCK_NONBLOCK,
|
||||
&vgremove_single);
|
||||
|
||||
unlock_vg(cmd, "");
|
||||
|
@ -37,7 +37,7 @@ int vgscan(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
log_print("Reading all physical volumes. This may take a while...");
|
||||
|
||||
return process_each_vg(cmd, argc, argv, LCK_READ, &vgscan_single);
|
||||
return process_each_vg(cmd, argc, argv, LCK_VG_READ, &vgscan_single);
|
||||
}
|
||||
|
||||
static int vgscan_single(struct cmd_context *cmd, const char *vg_name)
|
||||
|
Loading…
Reference in New Issue
Block a user