1 /*	$NetBSD: file_locking.c,v 1.1.1.3 2009/12/02 00:26:24 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 static int _prioritise_write_locks;
44 
45 static sig_t _oldhandler;
46 static sigset_t _fullsigset, _intsigset;
47 static volatile sig_atomic_t _handler_installed;
48 
49 static void _undo_flock(const char *file, int fd)
50 {
51 	struct stat buf1, buf2;
52 
53 	log_debug("_undo_flock %s", file);
54 	if (!flock(fd, LOCK_NB | LOCK_EX) &&
55 	    !stat(file, &buf1) &&
56 	    !fstat(fd, &buf2) &&
57 	    is_same_inode(buf1, buf2))
58 		if (unlink(file))
59 			log_sys_error("unlink", file);
60 
61 	if (close(fd) < 0)
62 		log_sys_error("close", file);
63 }
64 
65 static int _release_lock(const char *file, int unlock)
66 {
67 	struct lock_list *ll;
68 	struct dm_list *llh, *llt;
69 
70 	dm_list_iterate_safe(llh, llt, &_lock_list) {
71 		ll = dm_list_item(llh, struct lock_list);
72 
73 		if (!file || !strcmp(ll->res, file)) {
74 			dm_list_del(llh);
75 			if (unlock) {
76 				log_very_verbose("Unlocking %s", ll->res);
77 				if (flock(ll->lf, LOCK_NB | LOCK_UN))
78 					log_sys_error("flock", ll->res);
79 			}
80 
81 			_undo_flock(ll->res, ll->lf);
82 
83 			dm_free(ll->res);
84 			dm_free(llh);
85 
86 			if (file)
87 				return 1;
88 		}
89 	}
90 
91 	return 0;
92 }
93 
94 static void _fin_file_locking(void)
95 {
96 	_release_lock(NULL, 1);
97 }
98 
99 static void _reset_file_locking(void)
100 {
101 	_release_lock(NULL, 0);
102 }
103 
104 static void _remove_ctrl_c_handler(void)
105 {
106 	siginterrupt(SIGINT, 0);
107 	if (!_handler_installed)
108 		return;
109 
110 	_handler_installed = 0;
111 
112 	sigprocmask(SIG_SETMASK, &_fullsigset, NULL);
113 	if (signal(SIGINT, _oldhandler) == SIG_ERR)
114 		log_sys_error("signal", "_remove_ctrl_c_handler");
115 }
116 
117 static void _trap_ctrl_c(int sig __attribute((unused)))
118 {
119 	_remove_ctrl_c_handler();
120 	log_error("CTRL-c detected: giving up waiting for lock");
121 }
122 
123 static void _install_ctrl_c_handler()
124 {
125 	_handler_installed = 1;
126 
127 	if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) {
128 		_handler_installed = 0;
129 		return;
130 	}
131 
132 	sigprocmask(SIG_SETMASK, &_intsigset, NULL);
133 	siginterrupt(SIGINT, 1);
134 }
135 
136 static int _do_flock(const char *file, int *fd, int operation, uint32_t nonblock)
137 {
138 	int r = 1;
139 	int old_errno;
140 	struct stat buf1, buf2;
141 
142 	log_debug("_do_flock %s %c%c",
143 		  file, operation == LOCK_EX ? 'W' : 'R', nonblock ? ' ' : 'B');
144 	do {
145 		if ((*fd > -1) && close(*fd))
146 			log_sys_error("close", file);
147 
148 		if ((*fd = open(file, O_CREAT | O_APPEND | O_RDWR, 0777)) < 0) {
149 			log_sys_error("open", file);
150 			return 0;
151 		}
152 
153 		if (nonblock)
154 			operation |= LOCK_NB;
155 		else
156 			_install_ctrl_c_handler();
157 
158 		r = flock(*fd, operation);
159 		old_errno = errno;
160 		if (!nonblock)
161 			_remove_ctrl_c_handler();
162 
163 		if (r) {
164 			errno = old_errno;
165 			log_sys_error("flock", file);
166 			close(*fd);
167 			return 0;
168 		}
169 
170 		if (!stat(file, &buf1) && !fstat(*fd, &buf2) &&
171 		    is_same_inode(buf1, buf2))
172 			return 1;
173 	} while (!nonblock);
174 
175 	return_0;
176 }
177 
178 #define AUX_LOCK_SUFFIX ":aux"
179 
180 static int _do_write_priority_flock(const char *file, int *fd, int operation, uint32_t nonblock)
181 {
182 	int r, fd_aux = -1;
183 	char *file_aux = alloca(strlen(file) + sizeof(AUX_LOCK_SUFFIX));
184 
185 	strcpy(file_aux, file);
186 	strcat(file_aux, AUX_LOCK_SUFFIX);
187 
188 	if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, 0))) {
189 		if (operation == LOCK_EX) {
190 			r = _do_flock(file, fd, operation, nonblock);
191 			_undo_flock(file_aux, fd_aux);
192 		} else {
193 			_undo_flock(file_aux, fd_aux);
194 			r = _do_flock(file, fd, operation, nonblock);
195 		}
196 	}
197 
198 	return r;
199 }
200 
201 static int _lock_file(const char *file, uint32_t flags)
202 {
203 	int operation;
204 	uint32_t nonblock = flags & LCK_NONBLOCK;
205 	int r;
206 
207 	struct lock_list *ll;
208 	char state;
209 
210 	switch (flags & LCK_TYPE_MASK) {
211 	case LCK_READ:
212 		operation = LOCK_SH;
213 		state = 'R';
214 		break;
215 	case LCK_WRITE:
216 		operation = LOCK_EX;
217 		state = 'W';
218 		break;
219 	case LCK_UNLOCK:
220 		return _release_lock(file, 1);
221 	default:
222 		log_error("Unrecognised lock type: %d", flags & LCK_TYPE_MASK);
223 		return 0;
224 	}
225 
226 	if (!(ll = dm_malloc(sizeof(struct lock_list))))
227 		return_0;
228 
229 	if (!(ll->res = dm_strdup(file))) {
230 		dm_free(ll);
231 		return_0;
232 	}
233 
234 	ll->lf = -1;
235 
236 	log_very_verbose("Locking %s %c%c", ll->res, state,
237 			 nonblock ? ' ' : 'B');
238 
239 	if (_prioritise_write_locks)
240 		r = _do_write_priority_flock(file, &ll->lf, operation, nonblock);
241 	else
242 		r = _do_flock(file, &ll->lf, operation, nonblock);
243 
244 	if (r)
245 		dm_list_add(&_lock_list, &ll->list);
246 	else {
247 		dm_free(ll->res);
248 		dm_free(ll);
249 		stack;
250 	}
251 
252 	return r;
253 }
254 
255 static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
256 			       uint32_t flags)
257 {
258 	char lockfile[PATH_MAX];
259 
260 	switch (flags & LCK_SCOPE_MASK) {
261 	case LCK_VG:
262 		/* Skip cache refresh for VG_GLOBAL - the caller handles it */
263 		if (strcmp(resource, VG_GLOBAL))
264 			lvmcache_drop_metadata(resource);
265 
266 		/* LCK_CACHE does not require a real lock */
267 		if (flags & LCK_CACHE)
268 			break;
269 
270 		if (*resource == '#')
271 			dm_snprintf(lockfile, sizeof(lockfile),
272 				     "%s/P_%s", _lock_dir, resource + 1);
273 		else
274 			dm_snprintf(lockfile, sizeof(lockfile),
275 				     "%s/V_%s", _lock_dir, resource);
276 
277 		if (!_lock_file(lockfile, flags))
278 			return_0;
279 		break;
280 	case LCK_LV:
281 		switch (flags & LCK_TYPE_MASK) {
282 		case LCK_UNLOCK:
283 			log_very_verbose("Unlocking LV %s", resource);
284 			if (!lv_resume_if_active(cmd, resource))
285 				return 0;
286 			break;
287 		case LCK_NULL:
288 			log_very_verbose("Locking LV %s (NL)", resource);
289 			if (!lv_deactivate(cmd, resource))
290 				return 0;
291 			break;
292 		case LCK_READ:
293 			log_very_verbose("Locking LV %s (R)", resource);
294 			if (!lv_activate_with_filter(cmd, resource, 0))
295 				return 0;
296 			break;
297 		case LCK_PREAD:
298 			log_very_verbose("Locking LV %s (PR) - ignored", resource);
299 			break;
300 		case LCK_WRITE:
301 			log_very_verbose("Locking LV %s (W)", resource);
302 			if (!lv_suspend_if_active(cmd, resource))
303 				return 0;
304 			break;
305 		case LCK_EXCL:
306 			log_very_verbose("Locking LV %s (EX)", resource);
307 			if (!lv_activate_with_filter(cmd, resource, 1))
308 				return 0;
309 			break;
310 		default:
311 			break;
312 		}
313 		break;
314 	default:
315 		log_error("Unrecognised lock scope: %d",
316 			  flags & LCK_SCOPE_MASK);
317 		return 0;
318 	}
319 
320 	return 1;
321 }
322 
323 int init_file_locking(struct locking_type *locking, struct cmd_context *cmd)
324 {
325 	locking->lock_resource = _file_lock_resource;
326 	locking->reset_locking = _reset_file_locking;
327 	locking->fin_locking = _fin_file_locking;
328 	locking->flags = 0;
329 
330 	/* Get lockfile directory from config file */
331 	strncpy(_lock_dir, find_config_tree_str(cmd, "global/locking_dir",
332 						DEFAULT_LOCK_DIR),
333 		sizeof(_lock_dir));
334 
335 	_prioritise_write_locks =
336 	    find_config_tree_bool(cmd, "global/prioritise_write_locks",
337 				  DEFAULT_PRIORITISE_WRITE_LOCKS);
338 
339 	if (!dm_create_dir(_lock_dir))
340 		return 0;
341 
342 	/* Trap a read-only file system */
343 	if ((access(_lock_dir, R_OK | W_OK | X_OK) == -1) && (errno == EROFS))
344 		return 0;
345 
346 	dm_list_init(&_lock_list);
347 
348 	if (sigfillset(&_intsigset) || sigfillset(&_fullsigset)) {
349 		log_sys_error("sigfillset", "init_file_locking");
350 		return 0;
351 	}
352 
353 	if (sigdelset(&_intsigset, SIGINT)) {
354 		log_sys_error("sigdelset", "init_file_locking");
355 		return 0;
356 	}
357 
358 	return 1;
359 }
360