xref: /netbsd-src/external/gpl2/lvm2/dist/lib/locking/file_locking.c (revision 912a040b47adaee3e4d8d304ef66a69fff663934)
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