1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/lib/locking/file_locking.c

311 lines
6.6 KiB
C
Raw Normal View History

/*
2004-03-30 23:35:44 +04:00
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2.
*
2004-03-30 23:35:44 +04:00
* 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.
2004-03-30 23:35:44 +04:00
*
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2002-11-18 17:01:16 +03:00
#include "lib.h"
#include "locking.h"
#include "locking_types.h"
#include "activate.h"
#include "config.h"
#include "defaults.h"
#include "lvm-file.h"
#include "lvm-string.h"
#include "lvmcache.h"
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#include <signal.h>
struct lock_list {
struct list list;
int lf;
char *res;
};
static struct list _lock_list;
static char _lock_dir[NAME_LEN];
static sig_t _oldhandler;
static sigset_t _fullsigset, _intsigset;
2005-03-21 17:16:16 +03:00
static volatile sig_atomic_t _handler_installed;
2003-05-06 16:03:13 +04:00
static int _release_lock(const char *file, int unlock)
{
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);
if (!file || !strcmp(ll->res, file)) {
list_del(llh);
2003-05-06 16:03:13 +04:00
if (unlock) {
log_very_verbose("Unlocking %s", ll->res);
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) &&
is_same_inode(buf1, buf2))
if (unlink(ll->res))
log_sys_error("unlink", ll->res);
if (close(ll->lf) < 0)
log_sys_error("close", ll->res);
dm_free(ll->res);
dm_free(llh);
if (file)
return 1;
}
}
return 0;
}
static void _fin_file_locking(void)
{
2003-05-06 16:03:13 +04:00
_release_lock(NULL, 1);
}
static void _reset_file_locking(void)
{
_release_lock(NULL, 0);
}
static void _remove_ctrl_c_handler()
{
siginterrupt(SIGINT, 0);
2005-03-21 17:16:16 +03:00
if (!_handler_installed)
return;
2005-03-21 17:16:16 +03:00
_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()
{
2005-03-21 17:16:16 +03:00
_handler_installed = 1;
if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) {
_handler_installed = 0;
return;
2005-03-21 17:16:16 +03:00
}
sigprocmask(SIG_SETMASK, &_intsigset, NULL);
siginterrupt(SIGINT, 1);
}
static int _lock_file(const char *file, uint32_t flags)
{
int operation;
int r = 1;
2005-03-21 17:16:16 +03:00
int old_errno;
struct lock_list *ll;
struct stat buf1, buf2;
2002-11-18 17:01:16 +03:00
char state;
switch (flags & LCK_TYPE_MASK) {
case LCK_READ:
operation = LOCK_SH;
2002-11-18 17:01:16 +03:00
state = 'R';
break;
case LCK_WRITE:
operation = LOCK_EX;
2002-11-18 17:01:16 +03:00
state = 'W';
break;
2002-04-04 15:18:45 +04:00
case LCK_UNLOCK:
2003-05-06 16:03:13 +04:00
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;
2002-11-18 17:01:16 +03:00
log_very_verbose("Locking %s %c%c", ll->res, state,
flags & LCK_NONBLOCK ? ' ' : 'B');
do {
if ((ll->lf > -1) && close(ll->lf))
log_sys_error("close", file);
if ((ll->lf = open(file, O_CREAT | O_APPEND | O_RDWR, 0777))
< 0) {
log_sys_error("open", file);
goto err;
}
if ((flags & LCK_NONBLOCK))
operation |= LOCK_NB;
else
_install_ctrl_c_handler();
r = flock(ll->lf, operation);
2005-03-21 17:16:16 +03:00
old_errno = errno;
if (!(flags & LCK_NONBLOCK))
_remove_ctrl_c_handler();
if (r) {
2005-03-21 17:16:16 +03:00
errno = old_errno;
log_sys_error("flock", ll->res);
close(ll->lf);
goto err;
}
if (!stat(ll->res, &buf1) && !fstat(ll->lf, &buf2) &&
is_same_inode(buf1, buf2))
break;
} while (!(flags & LCK_NONBLOCK));
list_add(&_lock_list, &ll->list);
return 1;
err:
dm_free(ll->res);
dm_free(ll);
return 0;
}
static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
uint32_t flags)
{
char lockfile[PATH_MAX];
2006-05-10 21:49:25 +04:00
assert(resource);
switch (flags & LCK_SCOPE_MASK) {
case LCK_VG:
if (!*resource) /* FIXME Deprecated */
2006-08-21 16:54:53 +04:00
dm_snprintf(lockfile, sizeof(lockfile),
"%s/P_orphans", _lock_dir);
else if (*resource == '#')
dm_snprintf(lockfile, sizeof(lockfile),
"%s/P_%s", _lock_dir, resource + 1);
else
2006-08-21 16:54:53 +04:00
dm_snprintf(lockfile, sizeof(lockfile),
"%s/V_%s", _lock_dir, resource);
if (!_lock_file(lockfile, flags))
return 0;
switch (flags & LCK_TYPE_MASK) {
case LCK_UNLOCK:
lvmcache_unlock_vgname(resource);
break;
default:
lvmcache_lock_vgname(resource,
(flags & LCK_TYPE_MASK) ==
LCK_READ);
}
break;
case LCK_LV:
switch (flags & LCK_TYPE_MASK) {
2002-04-04 15:18:45 +04:00
case LCK_UNLOCK:
log_debug("Unlocking LV %s", resource);
if (!lv_resume_if_active(cmd, resource))
return 0;
break;
2004-05-05 16:03:07 +04:00
case LCK_NULL:
log_debug("Locking LV %s (NL)", resource);
if (!lv_deactivate(cmd, resource))
return 0;
break;
case LCK_READ:
log_debug("Locking LV %s (R)", resource);
if (!lv_activate_with_filter(cmd, resource, 0))
return 0;
break;
case LCK_WRITE:
log_debug("Locking LV %s (W)", resource);
if (!lv_suspend_if_active(cmd, resource))
return 0;
break;
case LCK_EXCL:
log_debug("Locking LV %s (EX)", resource);
if (!lv_activate_with_filter(cmd, resource, 1))
return 0;
break;
default:
break;
}
break;
default:
log_error("Unrecognised lock scope: %d",
flags & LCK_SCOPE_MASK);
return 0;
}
return 1;
}
int init_file_locking(struct locking_type *locking, struct cmd_context *cmd)
{
locking->lock_resource = _file_lock_resource;
2003-05-06 16:03:13 +04:00
locking->reset_locking = _reset_file_locking;
locking->fin_locking = _fin_file_locking;
2004-03-26 23:17:11 +03:00
locking->flags = 0;
/* Get lockfile directory from config file */
strncpy(_lock_dir, find_config_tree_str(cmd, "global/locking_dir",
DEFAULT_LOCK_DIR),
sizeof(_lock_dir));
if (!dm_create_dir(_lock_dir))
return 0;
2002-07-11 00:43:32 +04:00
/* Trap a read-only file system */
if ((access(_lock_dir, R_OK | W_OK | X_OK) == -1) && (errno == EROFS))
return 0;
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;
}