1*912a040bShaad /* $NetBSD: file_locking.c,v 1.2 2011/01/05 14:57:28 haad Exp $ */
256a34939Shaad
356a34939Shaad /*
456a34939Shaad * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
556a34939Shaad * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
656a34939Shaad *
756a34939Shaad * This file is part of LVM2.
856a34939Shaad *
956a34939Shaad * This copyrighted material is made available to anyone wishing to use,
1056a34939Shaad * modify, copy, or redistribute it subject to the terms and conditions
1156a34939Shaad * of the GNU Lesser General Public License v.2.1.
1256a34939Shaad *
1356a34939Shaad * You should have received a copy of the GNU Lesser General Public License
1456a34939Shaad * along with this program; if not, write to the Free Software Foundation,
1556a34939Shaad * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1656a34939Shaad */
1756a34939Shaad
1856a34939Shaad #include "lib.h"
1956a34939Shaad #include "locking.h"
2056a34939Shaad #include "locking_types.h"
2156a34939Shaad #include "activate.h"
2256a34939Shaad #include "config.h"
2356a34939Shaad #include "defaults.h"
2456a34939Shaad #include "lvm-file.h"
2556a34939Shaad #include "lvm-string.h"
2656a34939Shaad #include "lvmcache.h"
2756a34939Shaad
2856a34939Shaad #include <limits.h>
2956a34939Shaad #include <unistd.h>
3056a34939Shaad #include <sys/stat.h>
3156a34939Shaad #include <sys/file.h>
3256a34939Shaad #include <fcntl.h>
3356a34939Shaad #include <signal.h>
3456a34939Shaad
3556a34939Shaad struct lock_list {
3656a34939Shaad struct dm_list list;
3756a34939Shaad int lf;
3856a34939Shaad char *res;
3956a34939Shaad };
4056a34939Shaad
4156a34939Shaad static struct dm_list _lock_list;
4256a34939Shaad static char _lock_dir[NAME_LEN];
437c604eeaShaad static int _prioritise_write_locks;
4456a34939Shaad
4556a34939Shaad static sig_t _oldhandler;
4656a34939Shaad static sigset_t _fullsigset, _intsigset;
4756a34939Shaad static volatile sig_atomic_t _handler_installed;
4856a34939Shaad
_undo_flock(const char * file,int fd)497c604eeaShaad static void _undo_flock(const char *file, int fd)
507c604eeaShaad {
517c604eeaShaad struct stat buf1, buf2;
527c604eeaShaad
537c604eeaShaad log_debug("_undo_flock %s", file);
547c604eeaShaad if (!flock(fd, LOCK_NB | LOCK_EX) &&
557c604eeaShaad !stat(file, &buf1) &&
567c604eeaShaad !fstat(fd, &buf2) &&
577c604eeaShaad is_same_inode(buf1, buf2))
587c604eeaShaad if (unlink(file))
597c604eeaShaad log_sys_error("unlink", file);
607c604eeaShaad
617c604eeaShaad if (close(fd) < 0)
627c604eeaShaad log_sys_error("close", file);
637c604eeaShaad }
647c604eeaShaad
_release_lock(const char * file,int unlock)6556a34939Shaad static int _release_lock(const char *file, int unlock)
6656a34939Shaad {
6756a34939Shaad struct lock_list *ll;
6856a34939Shaad struct dm_list *llh, *llt;
6956a34939Shaad
7056a34939Shaad dm_list_iterate_safe(llh, llt, &_lock_list) {
7156a34939Shaad ll = dm_list_item(llh, struct lock_list);
7256a34939Shaad
7356a34939Shaad if (!file || !strcmp(ll->res, file)) {
7456a34939Shaad dm_list_del(llh);
7556a34939Shaad if (unlock) {
7656a34939Shaad log_very_verbose("Unlocking %s", ll->res);
7756a34939Shaad if (flock(ll->lf, LOCK_NB | LOCK_UN))
7856a34939Shaad log_sys_error("flock", ll->res);
7956a34939Shaad }
8056a34939Shaad
817c604eeaShaad _undo_flock(ll->res, ll->lf);
8256a34939Shaad
8356a34939Shaad dm_free(ll->res);
8456a34939Shaad dm_free(llh);
8556a34939Shaad
8656a34939Shaad if (file)
8756a34939Shaad return 1;
8856a34939Shaad }
8956a34939Shaad }
9056a34939Shaad
9156a34939Shaad return 0;
9256a34939Shaad }
9356a34939Shaad
_fin_file_locking(void)9456a34939Shaad static void _fin_file_locking(void)
9556a34939Shaad {
9656a34939Shaad _release_lock(NULL, 1);
9756a34939Shaad }
9856a34939Shaad
_reset_file_locking(void)9956a34939Shaad static void _reset_file_locking(void)
10056a34939Shaad {
10156a34939Shaad _release_lock(NULL, 0);
10256a34939Shaad }
10356a34939Shaad
_remove_ctrl_c_handler(void)104bec4d750Shaad static void _remove_ctrl_c_handler(void)
10556a34939Shaad {
10656a34939Shaad siginterrupt(SIGINT, 0);
10756a34939Shaad if (!_handler_installed)
10856a34939Shaad return;
10956a34939Shaad
11056a34939Shaad _handler_installed = 0;
11156a34939Shaad
11256a34939Shaad sigprocmask(SIG_SETMASK, &_fullsigset, NULL);
11356a34939Shaad if (signal(SIGINT, _oldhandler) == SIG_ERR)
11456a34939Shaad log_sys_error("signal", "_remove_ctrl_c_handler");
11556a34939Shaad }
11656a34939Shaad
_trap_ctrl_c(int sig __attribute ((unused)))11756a34939Shaad static void _trap_ctrl_c(int sig __attribute((unused)))
11856a34939Shaad {
11956a34939Shaad _remove_ctrl_c_handler();
12056a34939Shaad log_error("CTRL-c detected: giving up waiting for lock");
12156a34939Shaad }
12256a34939Shaad
_install_ctrl_c_handler()12356a34939Shaad static void _install_ctrl_c_handler()
12456a34939Shaad {
12556a34939Shaad _handler_installed = 1;
12656a34939Shaad
12756a34939Shaad if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) {
12856a34939Shaad _handler_installed = 0;
12956a34939Shaad return;
13056a34939Shaad }
13156a34939Shaad
13256a34939Shaad sigprocmask(SIG_SETMASK, &_intsigset, NULL);
13356a34939Shaad siginterrupt(SIGINT, 1);
13456a34939Shaad }
13556a34939Shaad
_do_flock(const char * file,int * fd,int operation,uint32_t nonblock)1367c604eeaShaad static int _do_flock(const char *file, int *fd, int operation, uint32_t nonblock)
1377c604eeaShaad {
1387c604eeaShaad int r = 1;
1397c604eeaShaad int old_errno;
1407c604eeaShaad struct stat buf1, buf2;
1417c604eeaShaad
1427c604eeaShaad log_debug("_do_flock %s %c%c",
1437c604eeaShaad file, operation == LOCK_EX ? 'W' : 'R', nonblock ? ' ' : 'B');
1447c604eeaShaad do {
1457c604eeaShaad if ((*fd > -1) && close(*fd))
1467c604eeaShaad log_sys_error("close", file);
1477c604eeaShaad
1487c604eeaShaad if ((*fd = open(file, O_CREAT | O_APPEND | O_RDWR, 0777)) < 0) {
1497c604eeaShaad log_sys_error("open", file);
1507c604eeaShaad return 0;
1517c604eeaShaad }
1527c604eeaShaad
1537c604eeaShaad if (nonblock)
1547c604eeaShaad operation |= LOCK_NB;
1557c604eeaShaad else
1567c604eeaShaad _install_ctrl_c_handler();
1577c604eeaShaad
1587c604eeaShaad r = flock(*fd, operation);
1597c604eeaShaad old_errno = errno;
1607c604eeaShaad if (!nonblock)
1617c604eeaShaad _remove_ctrl_c_handler();
1627c604eeaShaad
1637c604eeaShaad if (r) {
1647c604eeaShaad errno = old_errno;
1657c604eeaShaad log_sys_error("flock", file);
1667c604eeaShaad close(*fd);
1677c604eeaShaad return 0;
1687c604eeaShaad }
1697c604eeaShaad
1707c604eeaShaad if (!stat(file, &buf1) && !fstat(*fd, &buf2) &&
1717c604eeaShaad is_same_inode(buf1, buf2))
1727c604eeaShaad return 1;
1737c604eeaShaad } while (!nonblock);
1747c604eeaShaad
1757c604eeaShaad return_0;
1767c604eeaShaad }
1777c604eeaShaad
1787c604eeaShaad #define AUX_LOCK_SUFFIX ":aux"
1797c604eeaShaad
_do_write_priority_flock(const char * file,int * fd,int operation,uint32_t nonblock)1807c604eeaShaad static int _do_write_priority_flock(const char *file, int *fd, int operation, uint32_t nonblock)
1817c604eeaShaad {
1827c604eeaShaad int r, fd_aux = -1;
1837c604eeaShaad char *file_aux = alloca(strlen(file) + sizeof(AUX_LOCK_SUFFIX));
1847c604eeaShaad
1857c604eeaShaad strcpy(file_aux, file);
1867c604eeaShaad strcat(file_aux, AUX_LOCK_SUFFIX);
1877c604eeaShaad
1887c604eeaShaad if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, 0))) {
1897c604eeaShaad if (operation == LOCK_EX) {
1907c604eeaShaad r = _do_flock(file, fd, operation, nonblock);
1917c604eeaShaad _undo_flock(file_aux, fd_aux);
1927c604eeaShaad } else {
1937c604eeaShaad _undo_flock(file_aux, fd_aux);
1947c604eeaShaad r = _do_flock(file, fd, operation, nonblock);
1957c604eeaShaad }
1967c604eeaShaad }
1977c604eeaShaad
1987c604eeaShaad return r;
1997c604eeaShaad }
2007c604eeaShaad
_lock_file(const char * file,uint32_t flags)20156a34939Shaad static int _lock_file(const char *file, uint32_t flags)
20256a34939Shaad {
20356a34939Shaad int operation;
2047c604eeaShaad uint32_t nonblock = flags & LCK_NONBLOCK;
2057c604eeaShaad int r;
20656a34939Shaad
20756a34939Shaad struct lock_list *ll;
20856a34939Shaad char state;
20956a34939Shaad
21056a34939Shaad switch (flags & LCK_TYPE_MASK) {
21156a34939Shaad case LCK_READ:
21256a34939Shaad operation = LOCK_SH;
21356a34939Shaad state = 'R';
21456a34939Shaad break;
21556a34939Shaad case LCK_WRITE:
21656a34939Shaad operation = LOCK_EX;
21756a34939Shaad state = 'W';
21856a34939Shaad break;
21956a34939Shaad case LCK_UNLOCK:
22056a34939Shaad return _release_lock(file, 1);
22156a34939Shaad default:
22256a34939Shaad log_error("Unrecognised lock type: %d", flags & LCK_TYPE_MASK);
22356a34939Shaad return 0;
22456a34939Shaad }
22556a34939Shaad
22656a34939Shaad if (!(ll = dm_malloc(sizeof(struct lock_list))))
2277c604eeaShaad return_0;
22856a34939Shaad
22956a34939Shaad if (!(ll->res = dm_strdup(file))) {
23056a34939Shaad dm_free(ll);
2317c604eeaShaad return_0;
23256a34939Shaad }
23356a34939Shaad
23456a34939Shaad ll->lf = -1;
23556a34939Shaad
23656a34939Shaad log_very_verbose("Locking %s %c%c", ll->res, state,
2377c604eeaShaad nonblock ? ' ' : 'B');
23856a34939Shaad
2397c604eeaShaad if (_prioritise_write_locks)
2407c604eeaShaad r = _do_write_priority_flock(file, &ll->lf, operation, nonblock);
24156a34939Shaad else
2427c604eeaShaad r = _do_flock(file, &ll->lf, operation, nonblock);
24356a34939Shaad
2447c604eeaShaad if (r)
24556a34939Shaad dm_list_add(&_lock_list, &ll->list);
2467c604eeaShaad else {
24756a34939Shaad dm_free(ll->res);
24856a34939Shaad dm_free(ll);
2497c604eeaShaad stack;
2507c604eeaShaad }
2517c604eeaShaad
2527c604eeaShaad return r;
25356a34939Shaad }
25456a34939Shaad
_file_lock_resource(struct cmd_context * cmd,const char * resource,uint32_t flags)25556a34939Shaad static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
25656a34939Shaad uint32_t flags)
25756a34939Shaad {
25856a34939Shaad char lockfile[PATH_MAX];
25956a34939Shaad
26056a34939Shaad switch (flags & LCK_SCOPE_MASK) {
26156a34939Shaad case LCK_VG:
26256a34939Shaad /* Skip cache refresh for VG_GLOBAL - the caller handles it */
26356a34939Shaad if (strcmp(resource, VG_GLOBAL))
26456a34939Shaad lvmcache_drop_metadata(resource);
26556a34939Shaad
26656a34939Shaad /* LCK_CACHE does not require a real lock */
26756a34939Shaad if (flags & LCK_CACHE)
26856a34939Shaad break;
26956a34939Shaad
27056a34939Shaad if (*resource == '#')
27156a34939Shaad dm_snprintf(lockfile, sizeof(lockfile),
27256a34939Shaad "%s/P_%s", _lock_dir, resource + 1);
27356a34939Shaad else
27456a34939Shaad dm_snprintf(lockfile, sizeof(lockfile),
27556a34939Shaad "%s/V_%s", _lock_dir, resource);
27656a34939Shaad
27756a34939Shaad if (!_lock_file(lockfile, flags))
27856a34939Shaad return_0;
27956a34939Shaad break;
28056a34939Shaad case LCK_LV:
28156a34939Shaad switch (flags & LCK_TYPE_MASK) {
28256a34939Shaad case LCK_UNLOCK:
28356a34939Shaad log_very_verbose("Unlocking LV %s", resource);
28456a34939Shaad if (!lv_resume_if_active(cmd, resource))
28556a34939Shaad return 0;
28656a34939Shaad break;
28756a34939Shaad case LCK_NULL:
28856a34939Shaad log_very_verbose("Locking LV %s (NL)", resource);
28956a34939Shaad if (!lv_deactivate(cmd, resource))
29056a34939Shaad return 0;
29156a34939Shaad break;
29256a34939Shaad case LCK_READ:
29356a34939Shaad log_very_verbose("Locking LV %s (R)", resource);
29456a34939Shaad if (!lv_activate_with_filter(cmd, resource, 0))
29556a34939Shaad return 0;
29656a34939Shaad break;
29756a34939Shaad case LCK_PREAD:
29856a34939Shaad log_very_verbose("Locking LV %s (PR) - ignored", resource);
29956a34939Shaad break;
30056a34939Shaad case LCK_WRITE:
30156a34939Shaad log_very_verbose("Locking LV %s (W)", resource);
30256a34939Shaad if (!lv_suspend_if_active(cmd, resource))
30356a34939Shaad return 0;
30456a34939Shaad break;
30556a34939Shaad case LCK_EXCL:
30656a34939Shaad log_very_verbose("Locking LV %s (EX)", resource);
30756a34939Shaad if (!lv_activate_with_filter(cmd, resource, 1))
30856a34939Shaad return 0;
30956a34939Shaad break;
31056a34939Shaad default:
31156a34939Shaad break;
31256a34939Shaad }
31356a34939Shaad break;
31456a34939Shaad default:
31556a34939Shaad log_error("Unrecognised lock scope: %d",
31656a34939Shaad flags & LCK_SCOPE_MASK);
31756a34939Shaad return 0;
31856a34939Shaad }
31956a34939Shaad
32056a34939Shaad return 1;
32156a34939Shaad }
32256a34939Shaad
init_file_locking(struct locking_type * locking,struct cmd_context * cmd)32356a34939Shaad int init_file_locking(struct locking_type *locking, struct cmd_context *cmd)
32456a34939Shaad {
325*912a040bShaad mode_t old_umask;
326*912a040bShaad
32756a34939Shaad locking->lock_resource = _file_lock_resource;
32856a34939Shaad locking->reset_locking = _reset_file_locking;
32956a34939Shaad locking->fin_locking = _fin_file_locking;
33056a34939Shaad locking->flags = 0;
33156a34939Shaad
33256a34939Shaad /* Get lockfile directory from config file */
33356a34939Shaad strncpy(_lock_dir, find_config_tree_str(cmd, "global/locking_dir",
33456a34939Shaad DEFAULT_LOCK_DIR),
33556a34939Shaad sizeof(_lock_dir));
33656a34939Shaad
3377c604eeaShaad _prioritise_write_locks =
3387c604eeaShaad find_config_tree_bool(cmd, "global/prioritise_write_locks",
3397c604eeaShaad DEFAULT_PRIORITISE_WRITE_LOCKS);
340*912a040bShaad old_umask = umask(LVM_LOCKDIR_MODE);
341*912a040bShaad if (!dm_create_dir(_lock_dir)){
342*912a040bShaad umask(old_umask);
34356a34939Shaad return 0;
344*912a040bShaad } else {
345*912a040bShaad /* Change lockfile directory owner to match with others */
346*912a040bShaad if (chown(_lock_dir, DM_DEVICE_UID, DM_DEVICE_GID) == -1) {
347*912a040bShaad if (errno == EPERM)
348*912a040bShaad goto next;
349*912a040bShaad log_sys_error("chown", _lock_dir);
350*912a040bShaad return 0;
351*912a040bShaad }
352*912a040bShaad }
353*912a040bShaad
354*912a040bShaad next:
355*912a040bShaad umask(old_umask);
35656a34939Shaad
35756a34939Shaad /* Trap a read-only file system */
35856a34939Shaad if ((access(_lock_dir, R_OK | W_OK | X_OK) == -1) && (errno == EROFS))
35956a34939Shaad return 0;
36056a34939Shaad
36156a34939Shaad dm_list_init(&_lock_list);
36256a34939Shaad
36356a34939Shaad if (sigfillset(&_intsigset) || sigfillset(&_fullsigset)) {
36456a34939Shaad log_sys_error("sigfillset", "init_file_locking");
36556a34939Shaad return 0;
36656a34939Shaad }
36756a34939Shaad
36856a34939Shaad if (sigdelset(&_intsigset, SIGINT)) {
36956a34939Shaad log_sys_error("sigdelset", "init_file_locking");
37056a34939Shaad return 0;
37156a34939Shaad }
37256a34939Shaad
37356a34939Shaad return 1;
37456a34939Shaad }
375