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