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
_undo_flock(const char * file,int fd)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
_release_lock(const char * file,int unlock)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
_fin_file_locking(void)94 static void _fin_file_locking(void)
95 {
96 _release_lock(NULL, 1);
97 }
98
_reset_file_locking(void)99 static void _reset_file_locking(void)
100 {
101 _release_lock(NULL, 0);
102 }
103
_remove_ctrl_c_handler(void)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
_trap_ctrl_c(int sig __attribute ((unused)))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
_install_ctrl_c_handler()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
_do_flock(const char * file,int * fd,int operation,uint32_t nonblock)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
_do_write_priority_flock(const char * file,int * fd,int operation,uint32_t nonblock)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
_lock_file(const char * file,uint32_t flags)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
_file_lock_resource(struct cmd_context * cmd,const char * resource,uint32_t flags)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
init_file_locking(struct locking_type * locking,struct cmd_context * cmd)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