2002-02-11 18:42:34 +03:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2002-02-11 18:42:34 +03:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2 .
2002-02-11 18:42:34 +03:00
*
2004-03-30 23:35:44 +04:00
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2004-03-30 23:35:44 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2002-02-11 18:42:34 +03:00
*/
2002-11-18 17:01:16 +03:00
# include "lib.h"
2002-02-11 18:42:34 +03:00
# include "locking.h"
# include "locking_types.h"
# include "lvm-string.h"
# include "activate.h"
2002-11-01 22:57:25 +03:00
# include "toolcontext.h"
2004-03-26 23:49:35 +03:00
# include "memlock.h"
2006-09-02 05:18:17 +04:00
# include "defaults.h"
2008-04-07 23:17:29 +04:00
# include "lvmcache.h"
2002-02-11 18:42:34 +03:00
2008-11-04 18:07:45 +03:00
# include <assert.h>
2002-02-11 18:42:34 +03:00
# include <signal.h>
2002-11-01 22:57:25 +03:00
# include <sys/stat.h>
# include <limits.h>
2003-07-05 02:34:56 +04:00
# include <unistd.h>
2002-02-11 18:42:34 +03:00
static struct locking_type _locking ;
2002-03-15 19:07:38 +03:00
static sigset_t _oldset ;
2002-02-11 18:42:34 +03:00
2003-11-21 22:54:40 +03:00
static int _vg_lock_count = 0 ; /* Number of locks held */
static int _vg_write_lock_held = 0 ; /* VG write lock held? */
2002-03-15 19:07:38 +03:00
static int _signals_blocked = 0 ;
2002-02-11 18:42:34 +03:00
2007-06-15 14:11:14 +04:00
static volatile sig_atomic_t _sigint_caught = 0 ;
static volatile sig_atomic_t _handler_installed ;
static struct sigaction _oldhandler ;
static int _oldmasked ;
static void _catch_sigint ( int unused __attribute__ ( ( unused ) ) )
{
_sigint_caught = 1 ;
}
2007-06-16 00:46:04 +04:00
int sigint_caught ( void ) {
2007-06-15 14:11:14 +04:00
return _sigint_caught ;
}
2007-06-16 00:46:04 +04:00
void sigint_clear ( void )
2007-06-15 14:11:14 +04:00
{
_sigint_caught = 0 ;
}
2007-06-16 00:46:04 +04:00
/*
* Temporarily allow keyboard interrupts to be intercepted and noted ;
* saves interrupt handler state for sigint_restore ( ) . Users should
* use the sigint_caught ( ) predicate to check whether interrupt was
* requested and act appropriately . Interrupt flags are never
* cleared automatically by this code , but the tools clear the flag
* before running each command in lvm_run_command ( ) . All other places
* where the flag needs to be cleared need to call sigint_clear ( ) .
*/
2007-06-15 14:11:14 +04:00
2007-06-16 00:46:04 +04:00
void sigint_allow ( void )
2007-06-15 14:11:14 +04:00
{
struct sigaction handler ;
sigset_t sigs ;
2007-06-16 00:46:04 +04:00
/*
* Do not overwrite the backed - up handler data -
* just increase nesting count .
*/
2007-06-15 14:11:14 +04:00
if ( _handler_installed ) {
_handler_installed + + ;
return ;
}
2007-06-16 00:46:04 +04:00
/* Grab old sigaction for SIGINT: shall not fail. */
2007-06-15 14:11:14 +04:00
sigaction ( SIGINT , NULL , & handler ) ;
2007-06-16 00:46:04 +04:00
handler . sa_flags & = ~ SA_RESTART ; /* Clear restart flag */
2007-06-15 14:11:14 +04:00
handler . sa_handler = _catch_sigint ;
_handler_installed = 1 ;
2007-06-16 00:46:04 +04:00
/* Override the signal handler: shall not fail. */
2007-06-15 14:11:14 +04:00
sigaction ( SIGINT , & handler , & _oldhandler ) ;
2007-06-16 00:46:04 +04:00
/* Unmask SIGINT. Remember to mask it again on restore. */
2007-06-15 14:11:14 +04:00
sigprocmask ( 0 , NULL , & sigs ) ;
if ( ( _oldmasked = sigismember ( & sigs , SIGINT ) ) ) {
sigdelset ( & sigs , SIGINT ) ;
sigprocmask ( SIG_SETMASK , & sigs , NULL ) ;
}
}
2007-06-16 00:46:04 +04:00
void sigint_restore ( void )
2007-06-15 14:11:14 +04:00
{
if ( ! _handler_installed )
return ;
if ( _handler_installed > 1 ) {
_handler_installed - - ;
return ;
}
2007-06-16 00:46:04 +04:00
/* Nesting count went down to 0. */
2007-06-15 14:11:14 +04:00
_handler_installed = 0 ;
if ( _oldmasked ) {
sigset_t sigs ;
sigprocmask ( 0 , NULL , & sigs ) ;
sigaddset ( & sigs , SIGINT ) ;
sigprocmask ( SIG_SETMASK , & sigs , NULL ) ;
}
sigaction ( SIGINT , & _oldhandler , NULL ) ;
}
2007-08-22 18:38:18 +04:00
static void _block_signals ( uint32_t flags __attribute ( ( unused ) ) )
2002-02-11 18:42:34 +03:00
{
2002-03-15 19:07:38 +03:00
sigset_t set ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
if ( _signals_blocked )
2002-02-11 18:42:34 +03:00
return ;
2002-03-15 19:07:38 +03:00
if ( sigfillset ( & set ) ) {
log_sys_error ( " sigfillset " , " _block_signals " ) ;
return ;
}
if ( sigprocmask ( SIG_SETMASK , & set , & _oldset ) ) {
log_sys_error ( " sigprocmask " , " _block_signals " ) ;
return ;
}
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
_signals_blocked = 1 ;
2002-02-11 18:42:34 +03:00
return ;
}
2002-03-15 19:07:38 +03:00
static void _unblock_signals ( void )
2002-02-11 18:42:34 +03:00
{
2002-03-15 19:07:38 +03:00
/* Don't unblock signals while any locks are held */
2003-11-21 22:54:40 +03:00
if ( ! _signals_blocked | | _vg_lock_count )
2002-02-11 18:42:34 +03:00
return ;
2002-03-15 19:07:38 +03:00
if ( sigprocmask ( SIG_SETMASK , & _oldset , NULL ) ) {
log_sys_error ( " sigprocmask " , " _block_signals " ) ;
return ;
}
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
_signals_blocked = 0 ;
2002-02-11 18:42:34 +03:00
return ;
}
2007-08-22 18:38:18 +04:00
static void _lock_memory ( uint32_t flags )
2004-03-26 23:49:35 +03:00
{
if ( ! ( _locking . flags & LCK_PRE_MEMLOCK ) )
return ;
if ( ( flags & ( LCK_SCOPE_MASK | LCK_TYPE_MASK ) ) = = LCK_LV_SUSPEND )
memlock_inc ( ) ;
}
2007-08-22 18:38:18 +04:00
static void _unlock_memory ( uint32_t flags )
2004-03-26 23:49:35 +03:00
{
if ( ! ( _locking . flags & LCK_PRE_MEMLOCK ) )
return ;
if ( ( flags & ( LCK_SCOPE_MASK | LCK_TYPE_MASK ) ) = = LCK_LV_RESUME )
memlock_dec ( ) ;
}
2003-05-06 16:03:13 +04:00
void reset_locking ( void )
{
2003-11-21 22:54:40 +03:00
int was_locked = _vg_lock_count ;
2003-05-06 16:03:13 +04:00
2003-11-21 22:54:40 +03:00
_vg_lock_count = 0 ;
_vg_write_lock_held = 0 ;
2003-05-06 16:03:13 +04:00
_locking . reset_locking ( ) ;
if ( was_locked )
_unblock_signals ( ) ;
}
2007-08-22 18:38:18 +04:00
static void _update_vg_lock_count ( uint32_t flags )
2002-02-11 18:42:34 +03:00
{
2003-11-21 22:54:40 +03:00
if ( ( flags & LCK_SCOPE_MASK ) ! = LCK_VG )
return ;
2002-04-04 15:18:45 +04:00
if ( ( flags & LCK_TYPE_MASK ) = = LCK_UNLOCK )
2003-11-21 22:54:40 +03:00
_vg_lock_count - - ;
2002-02-11 18:42:34 +03:00
else
2003-11-21 22:54:40 +03:00
_vg_lock_count + + ;
/* We don't bother to reset this until all VG locks are dropped */
if ( ( flags & LCK_TYPE_MASK ) = = LCK_WRITE )
_vg_write_lock_held = 1 ;
else if ( ! _vg_lock_count )
_vg_write_lock_held = 0 ;
2002-02-11 18:42:34 +03:00
}
/*
* Select a locking type
*/
2006-05-16 20:48:31 +04:00
int init_locking ( int type , struct cmd_context * cmd )
2002-02-11 18:42:34 +03:00
{
2005-03-22 01:55:12 +03:00
init_lockingfailed ( 0 ) ;
2002-02-11 18:42:34 +03:00
switch ( type ) {
case 0 :
2006-05-16 20:48:31 +04:00
init_no_locking ( & _locking , cmd ) ;
2007-06-28 21:33:44 +04:00
log_warn ( " WARNING: Locking disabled. Be careful! "
2002-02-11 18:42:34 +03:00
" This could corrupt your metadata. " ) ;
2002-07-11 00:43:32 +04:00
return 1 ;
2002-02-11 18:42:34 +03:00
case 1 :
2006-09-01 02:21:00 +04:00
log_very_verbose ( " File-based locking selected. " ) ;
2006-05-16 20:48:31 +04:00
if ( ! init_file_locking ( & _locking , cmd ) )
2002-07-11 00:43:32 +04:00
break ;
return 1 ;
2002-04-08 20:04:50 +04:00
2003-03-24 21:08:53 +03:00
# ifdef HAVE_LIBDL
2002-02-11 18:42:34 +03:00
case 2 :
2006-09-01 02:21:00 +04:00
if ( ! cmd - > is_static ) {
log_very_verbose ( " External locking selected. " ) ;
2006-10-14 20:37:54 +04:00
if ( init_external_locking ( & _locking , cmd ) )
return 1 ;
2006-09-01 02:21:00 +04:00
}
2006-09-02 05:18:17 +04:00
if ( ! find_config_tree_int ( cmd , " locking/fallback_to_clustered_locking " ,
2008-10-24 05:16:16 +04:00
find_config_tree_int ( cmd , " global/fallback_to_clustered_locking " ,
DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING ) ) )
2006-09-02 05:18:17 +04:00
break ;
2003-03-24 21:08:53 +03:00
# endif
2002-04-08 20:04:50 +04:00
2004-06-24 12:02:38 +04:00
# ifdef CLUSTER_LOCKING_INTERNAL
2006-10-14 20:37:54 +04:00
log_very_verbose ( " Falling back to internal clustered locking. " ) ;
/* Fall through */
2004-06-24 12:02:38 +04:00
case 3 :
2006-09-01 02:21:00 +04:00
log_very_verbose ( " Cluster locking selected. " ) ;
2006-05-16 20:48:31 +04:00
if ( ! init_cluster_locking ( & _locking , cmd ) )
2004-06-24 12:02:38 +04:00
break ;
return 1 ;
# endif
2002-02-11 18:42:34 +03:00
default :
log_error ( " Unknown locking type requested. " ) ;
return 0 ;
}
2006-09-02 05:18:17 +04:00
if ( ( type = = 2 | | type = = 3 ) & &
2008-01-30 17:00:02 +03:00
find_config_tree_int ( cmd , " locking/fallback_to_local_locking " ,
2008-10-24 05:16:16 +04:00
find_config_tree_int ( cmd , " global/fallback_to_local_locking " ,
DEFAULT_FALLBACK_TO_LOCAL_LOCKING ) ) ) {
2007-06-28 21:33:44 +04:00
log_warn ( " WARNING: Falling back to local file-based locking. " ) ;
log_warn ( " Volume Groups with the clustered attribute will "
2006-09-02 05:18:17 +04:00
" be inaccessible. " ) ;
if ( init_file_locking ( & _locking , cmd ) )
return 1 ;
}
2002-07-11 00:43:32 +04:00
if ( ! ignorelockingfailure ( ) )
return 0 ;
2002-11-01 22:57:25 +03:00
/* FIXME Ensure only read ops are permitted */
2002-07-11 00:43:32 +04:00
log_verbose ( " Locking disabled - only read operations permitted. " ) ;
2006-05-16 20:48:31 +04:00
init_no_locking ( & _locking , cmd ) ;
2005-03-22 01:55:12 +03:00
init_lockingfailed ( 1 ) ;
2002-07-11 00:43:32 +04:00
2002-02-11 18:42:34 +03:00
return 1 ;
}
void fin_locking ( void )
{
_locking . fin_locking ( ) ;
}
2002-11-01 22:57:25 +03:00
/*
* Does the LVM1 driver know of this VG name ?
*/
int check_lvm1_vg_inactive ( struct cmd_context * cmd , const char * vgname )
{
struct stat info ;
char path [ PATH_MAX ] ;
2002-11-18 17:01:16 +03:00
/* We'll allow operations on orphans */
2007-11-02 16:06:42 +03:00
if ( is_orphan_vg ( vgname ) )
2002-11-18 17:01:16 +03:00
return 1 ;
2008-09-10 14:14:59 +04:00
/* LVM1 is only present in 2.4 kernels. */
if ( strncmp ( cmd - > kernel_vsn , " 2.4. " , 4 ) )
return 1 ;
2006-08-21 16:54:53 +04:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/lvm/VGs/%s " , cmd - > proc_dir ,
2002-11-18 17:01:16 +03:00
vgname ) < 0 ) {
2002-11-01 22:57:25 +03:00
log_error ( " LVM1 proc VG pathname too long for %s " , vgname ) ;
return 0 ;
}
if ( stat ( path , & info ) = = 0 ) {
log_error ( " %s exists: Is the original LVM driver using "
" this volume group? " , path ) ;
return 0 ;
} else if ( errno ! = ENOENT & & errno ! = ENOTDIR ) {
log_sys_error ( " stat " , path ) ;
return 0 ;
}
return 1 ;
}
2002-02-11 18:42:34 +03:00
/*
2002-03-15 19:07:38 +03:00
* VG locking is by VG name .
* FIXME This should become VG uuid .
2002-02-11 18:42:34 +03:00
*/
2007-08-22 18:38:18 +04:00
static int _lock_vol ( struct cmd_context * cmd , const char * resource , uint32_t flags )
2002-03-05 23:03:09 +03:00
{
2008-04-07 23:17:29 +04:00
int ret = 0 ;
2003-01-04 00:10:28 +03:00
_block_signals ( flags ) ;
2004-03-26 23:49:35 +03:00
_lock_memory ( flags ) ;
2002-03-05 23:03:09 +03:00
2007-11-17 00:16:20 +03:00
assert ( resource ) ;
2008-05-09 22:45:15 +04:00
if ( ! * resource ) {
log_error ( " Internal error: Use of P_orphans is deprecated. " ) ;
return 0 ;
}
if ( * resource = = ' # ' & & ( flags & LCK_CACHE ) ) {
log_error ( " Internal error: P_%s referenced " , resource ) ;
return 0 ;
}
2008-04-07 23:17:29 +04:00
if ( ( ret = _locking . lock_resource ( cmd , resource , flags ) ) ) {
2008-04-15 18:46:19 +04:00
if ( ( flags & LCK_SCOPE_MASK ) = = LCK_VG & &
! ( flags & LCK_CACHE ) ) {
2008-04-07 23:17:29 +04:00
if ( ( flags & LCK_TYPE_MASK ) = = LCK_UNLOCK )
lvmcache_unlock_vgname ( resource ) ;
else
lvmcache_lock_vgname ( resource , ( flags & LCK_TYPE_MASK )
= = LCK_READ ) ;
}
_update_vg_lock_count ( flags ) ;
2002-03-05 23:03:09 +03:00
}
2004-03-26 23:49:35 +03:00
_unlock_memory ( flags ) ;
2002-03-15 19:07:38 +03:00
_unblock_signals ( ) ;
2002-03-05 23:03:09 +03:00
2008-04-07 23:17:29 +04:00
return ret ;
2002-03-05 23:03:09 +03:00
}
2007-08-22 18:38:18 +04:00
int lock_vol ( struct cmd_context * cmd , const char * vol , uint32_t flags )
2002-02-11 18:42:34 +03:00
{
2006-12-01 02:11:42 +03:00
char resource [ 258 ] __attribute ( ( aligned ( 8 ) ) ) ;
2002-02-11 18:42:34 +03:00
2007-11-16 00:30:52 +03:00
if ( flags = = LCK_NONE ) {
log_debug ( " Internal error: %s: LCK_NONE lock requested " , vol ) ;
return 1 ;
}
2002-02-11 18:42:34 +03:00
switch ( flags & LCK_SCOPE_MASK ) {
2002-11-01 22:57:25 +03:00
case LCK_VG :
/* Lock VG to change on-disk metadata. */
/* If LVM1 driver knows about the VG, it can't be accessed. */
if ( ! check_lvm1_vg_inactive ( cmd , vol ) )
return 0 ;
case LCK_LV :
/* Suspend LV if it's active. */
2002-12-20 02:25:55 +03:00
strncpy ( resource , vol , sizeof ( resource ) ) ;
2002-02-11 18:42:34 +03:00
break ;
default :
log_error ( " Unrecognised lock scope: %d " ,
flags & LCK_SCOPE_MASK ) ;
return 0 ;
}
2002-03-05 23:03:09 +03:00
if ( ! _lock_vol ( cmd , resource , flags ) )
2002-02-11 18:42:34 +03:00
return 0 ;
2008-05-08 22:35:58 +04:00
/*
* If a real lock was acquired ( i . e . not LCK_CACHE ) ,
* perform an immediate unlock unless LCK_HOLD was requested .
*/
if ( ! ( flags & LCK_CACHE ) & & ! ( flags & LCK_HOLD ) & &
( ( flags & LCK_TYPE_MASK ) ! = LCK_UNLOCK ) ) {
2002-03-15 19:07:38 +03:00
if ( ! _lock_vol ( cmd , resource ,
2002-04-04 15:18:45 +04:00
( flags & ~ LCK_TYPE_MASK ) | LCK_UNLOCK ) )
2002-03-05 23:03:09 +03:00
return 0 ;
}
2002-02-11 18:42:34 +03:00
return 1 ;
}
2003-11-21 22:54:40 +03:00
2004-03-26 23:49:35 +03:00
/* Unlock list of LVs */
2008-11-04 01:14:30 +03:00
int resume_lvs ( struct cmd_context * cmd , struct dm_list * lvs )
2004-03-26 23:49:35 +03:00
{
2005-06-01 20:51:55 +04:00
struct lv_list * lvl ;
2004-03-26 23:49:35 +03:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( lvl , lvs )
2005-08-15 16:00:04 +04:00
resume_lv ( cmd , lvl - > lv ) ;
2004-03-26 23:49:35 +03:00
return 1 ;
}
/* Lock a list of LVs */
2008-11-04 01:14:30 +03:00
int suspend_lvs ( struct cmd_context * cmd , struct dm_list * lvs )
2004-03-26 23:49:35 +03:00
{
2008-11-04 01:14:30 +03:00
struct dm_list * lvh ;
2005-06-01 20:51:55 +04:00
struct lv_list * lvl ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( lvl , lvs ) {
2005-08-15 16:00:04 +04:00
if ( ! suspend_lv ( cmd , lvl - > lv ) ) {
2005-06-01 20:51:55 +04:00
log_error ( " Failed to suspend %s " , lvl - > lv - > name ) ;
2008-11-04 01:14:30 +03:00
dm_list_uniterate ( lvh , lvs , & lvl - > list ) {
lvl = dm_list_item ( lvh , struct lv_list ) ;
2005-08-15 16:00:04 +04:00
resume_lv ( cmd , lvl - > lv ) ;
2004-05-05 16:03:07 +04:00
}
return 0 ;
}
}
return 1 ;
}
/* Lock a list of LVs */
2008-11-04 01:14:30 +03:00
int activate_lvs ( struct cmd_context * cmd , struct dm_list * lvs , unsigned exclusive )
2004-05-05 16:03:07 +04:00
{
2008-11-04 01:14:30 +03:00
struct dm_list * lvh ;
2005-06-01 20:51:55 +04:00
struct lv_list * lvl ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( lvl , lvs ) {
2008-04-09 16:56:34 +04:00
if ( ! exclusive ) {
if ( ! activate_lv ( cmd , lvl - > lv ) ) {
log_error ( " Failed to activate %s " , lvl - > lv - > name ) ;
return 0 ;
}
} else if ( ! activate_lv_excl ( cmd , lvl - > lv ) ) {
2005-06-01 20:51:55 +04:00
log_error ( " Failed to activate %s " , lvl - > lv - > name ) ;
2008-11-04 01:14:30 +03:00
dm_list_uniterate ( lvh , lvs , & lvl - > list ) {
lvl = dm_list_item ( lvh , struct lv_list ) ;
2005-08-15 16:00:04 +04:00
activate_lv ( cmd , lvl - > lv ) ;
2004-03-26 23:49:35 +03:00
}
return 0 ;
}
}
return 1 ;
}
2003-11-21 22:54:40 +03:00
int vg_write_lock_held ( void )
{
return _vg_write_lock_held ;
}
2005-03-22 01:55:12 +03:00
int locking_is_clustered ( void )
{
return ( _locking . flags & LCK_CLUSTERED ) ? 1 : 0 ;
}