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