xref: /netbsd-src/external/gpl2/lvm2/dist/lib/locking/file_locking.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: file_locking.c,v 1.1.1.1 2008/12/22 00:18:04 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "lib.h"
19 #include "locking.h"
20 #include "locking_types.h"
21 #include "activate.h"
22 #include "config.h"
23 #include "defaults.h"
24 #include "lvm-file.h"
25 #include "lvm-string.h"
26 #include "lvmcache.h"
27 
28 #include <limits.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <sys/file.h>
32 #include <fcntl.h>
33 #include <signal.h>
34 
35 struct lock_list {
36 	struct dm_list list;
37 	int lf;
38 	char *res;
39 };
40 
41 static struct dm_list _lock_list;
42 static char _lock_dir[NAME_LEN];
43 
44 static sig_t _oldhandler;
45 static sigset_t _fullsigset, _intsigset;
46 static volatile sig_atomic_t _handler_installed;
47 
48 static int _release_lock(const char *file, int unlock)
49 {
50 	struct lock_list *ll;
51 	struct dm_list *llh, *llt;
52 
53 	struct stat buf1, buf2;
54 
55 	dm_list_iterate_safe(llh, llt, &_lock_list) {
56 		ll = dm_list_item(llh, struct lock_list);
57 
58 		if (!file || !strcmp(ll->res, file)) {
59 			dm_list_del(llh);
60 			if (unlock) {
61 				log_very_verbose("Unlocking %s", ll->res);
62 				if (flock(ll->lf, LOCK_NB | LOCK_UN))
63 					log_sys_error("flock", ll->res);
64 			}
65 
66 			if (!flock(ll->lf, LOCK_NB | LOCK_EX) &&
67 			    !stat(ll->res, &buf1) &&
68 			    !fstat(ll->lf, &buf2) &&
69 			    is_same_inode(buf1, buf2))
70 				if (unlink(ll->res))
71 					log_sys_error("unlink", ll->res);
72 
73 			if (close(ll->lf) < 0)
74 				log_sys_error("close", ll->res);
75 
76 			dm_free(ll->res);
77 			dm_free(llh);
78 
79 			if (file)
80 				return 1;
81 		}
82 	}
83 
84 	return 0;
85 }
86 
87 static void _fin_file_locking(void)
88 {
89 	_release_lock(NULL, 1);
90 }
91 
92 static void _reset_file_locking(void)
93 {
94 	_release_lock(NULL, 0);
95 }
96 
97 static void _remove_ctrl_c_handler()
98 {
99 	siginterrupt(SIGINT, 0);
100 	if (!_handler_installed)
101 		return;
102 
103 	_handler_installed = 0;
104 
105 	sigprocmask(SIG_SETMASK, &_fullsigset, NULL);
106 	if (signal(SIGINT, _oldhandler) == SIG_ERR)
107 		log_sys_error("signal", "_remove_ctrl_c_handler");
108 }
109 
110 static void _trap_ctrl_c(int sig __attribute((unused)))
111 {
112 	_remove_ctrl_c_handler();
113 	log_error("CTRL-c detected: giving up waiting for lock");
114 }
115 
116 static void _install_ctrl_c_handler()
117 {
118 	_handler_installed = 1;
119 
120 	if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) {
121 		_handler_installed = 0;
122 		return;
123 	}
124 
125 	sigprocmask(SIG_SETMASK, &_intsigset, NULL);
126 	siginterrupt(SIGINT, 1);
127 }
128 
129 static int _lock_file(const char *file, uint32_t flags)
130 {
131 	int operation;
132 	int r = 1;
133 	int old_errno;
134 
135 	struct lock_list *ll;
136 	struct stat buf1, buf2;
137 	char state;
138 
139 	switch (flags & LCK_TYPE_MASK) {
140 	case LCK_READ:
141 		operation = LOCK_SH;
142 		state = 'R';
143 		break;
144 	case LCK_WRITE:
145 		operation = LOCK_EX;
146 		state = 'W';
147 		break;
148 	case LCK_UNLOCK:
149 		return _release_lock(file, 1);
150 	default:
151 		log_error("Unrecognised lock type: %d", flags & LCK_TYPE_MASK);
152 		return 0;
153 	}
154 
155 	if (!(ll = dm_malloc(sizeof(struct lock_list))))
156 		return 0;
157 
158 	if (!(ll->res = dm_strdup(file))) {
159 		dm_free(ll);
160 		return 0;
161 	}
162 
163 	ll->lf = -1;
164 
165 	log_very_verbose("Locking %s %c%c", ll->res, state,
166 			 flags & LCK_NONBLOCK ? ' ' : 'B');
167 	do {
168 		if ((ll->lf > -1) && close(ll->lf))
169 			log_sys_error("close", file);
170 
171 		if ((ll->lf = open(file, O_CREAT | O_APPEND | O_RDWR, 0777))
172 		    < 0) {
173 			log_sys_error("open", file);
174 			goto err;
175 		}
176 
177 		if ((flags & LCK_NONBLOCK))
178 			operation |= LOCK_NB;
179 		else
180 			_install_ctrl_c_handler();
181 
182 		r = flock(ll->lf, operation);
183 		old_errno = errno;
184 		if (!(flags & LCK_NONBLOCK))
185 			_remove_ctrl_c_handler();
186 
187 		if (r) {
188 			errno = old_errno;
189 			log_sys_error("flock", ll->res);
190 			close(ll->lf);
191 			goto err;
192 		}
193 
194 		if (!stat(ll->res, &buf1) && !fstat(ll->lf, &buf2) &&
195 		    is_same_inode(buf1, buf2))
196 			break;
197 	} while (!(flags & LCK_NONBLOCK));
198 
199 	dm_list_add(&_lock_list, &ll->list);
200 	return 1;
201 
202       err:
203 	dm_free(ll->res);
204 	dm_free(ll);
205 	return 0;
206 }
207 
208 static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
209 			       uint32_t flags)
210 {
211 	char lockfile[PATH_MAX];
212 
213 	switch (flags & LCK_SCOPE_MASK) {
214 	case LCK_VG:
215 		/* Skip cache refresh for VG_GLOBAL - the caller handles it */
216 		if (strcmp(resource, VG_GLOBAL))
217 			lvmcache_drop_metadata(resource);
218 
219 		/* LCK_CACHE does not require a real lock */
220 		if (flags & LCK_CACHE)
221 			break;
222 
223 		if (*resource == '#')
224 			dm_snprintf(lockfile, sizeof(lockfile),
225 				     "%s/P_%s", _lock_dir, resource + 1);
226 		else
227 			dm_snprintf(lockfile, sizeof(lockfile),
228 				     "%s/V_%s", _lock_dir, resource);
229 
230 		if (!_lock_file(lockfile, flags))
231 			return_0;
232 		break;
233 	case LCK_LV:
234 		switch (flags & LCK_TYPE_MASK) {
235 		case LCK_UNLOCK:
236 			log_very_verbose("Unlocking LV %s", resource);
237 			if (!lv_resume_if_active(cmd, resource))
238 				return 0;
239 			break;
240 		case LCK_NULL:
241 			log_very_verbose("Locking LV %s (NL)", resource);
242 			if (!lv_deactivate(cmd, resource))
243 				return 0;
244 			break;
245 		case LCK_READ:
246 			log_very_verbose("Locking LV %s (R)", resource);
247 			if (!lv_activate_with_filter(cmd, resource, 0))
248 				return 0;
249 			break;
250 		case LCK_PREAD:
251 			log_very_verbose("Locking LV %s (PR) - ignored", resource);
252 			break;
253 		case LCK_WRITE:
254 			log_very_verbose("Locking LV %s (W)", resource);
255 			if (!lv_suspend_if_active(cmd, resource))
256 				return 0;
257 			break;
258 		case LCK_EXCL:
259 			log_very_verbose("Locking LV %s (EX)", resource);
260 			if (!lv_activate_with_filter(cmd, resource, 1))
261 				return 0;
262 			break;
263 		default:
264 			break;
265 		}
266 		break;
267 	default:
268 		log_error("Unrecognised lock scope: %d",
269 			  flags & LCK_SCOPE_MASK);
270 		return 0;
271 	}
272 
273 	return 1;
274 }
275 
276 int init_file_locking(struct locking_type *locking, struct cmd_context *cmd)
277 {
278 	locking->lock_resource = _file_lock_resource;
279 	locking->reset_locking = _reset_file_locking;
280 	locking->fin_locking = _fin_file_locking;
281 	locking->flags = 0;
282 
283 	/* Get lockfile directory from config file */
284 	strncpy(_lock_dir, find_config_tree_str(cmd, "global/locking_dir",
285 						DEFAULT_LOCK_DIR),
286 		sizeof(_lock_dir));
287 
288 	if (!dm_create_dir(_lock_dir))
289 		return 0;
290 
291 	/* Trap a read-only file system */
292 	if ((access(_lock_dir, R_OK | W_OK | X_OK) == -1) && (errno == EROFS))
293 		return 0;
294 
295 	dm_list_init(&_lock_list);
296 
297 	if (sigfillset(&_intsigset) || sigfillset(&_fullsigset)) {
298 		log_sys_error("sigfillset", "init_file_locking");
299 		return 0;
300 	}
301 
302 	if (sigdelset(&_intsigset, SIGINT)) {
303 		log_sys_error("sigdelset", "init_file_locking");
304 		return 0;
305 	}
306 
307 	return 1;
308 }
309