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