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 "activate.h"
# include "config.h"
# include "defaults.h"
# include "lvm-file.h"
# include "lvm-string.h"
2008-04-15 18:46:19 +04:00
# include "lvmcache.h"
2002-02-11 18:42:34 +03:00
# include <limits.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/file.h>
# include <fcntl.h>
# include <signal.h>
struct lock_list {
2008-11-04 01:14:30 +03:00
struct dm_list list ;
2002-02-11 18:42:34 +03:00
int lf ;
char * res ;
} ;
2008-11-04 01:14:30 +03:00
static struct dm_list _lock_list ;
2002-02-11 18:42:34 +03:00
static char _lock_dir [ NAME_LEN ] ;
2009-09-02 18:47:39 +04:00
static int _prioritise_write_locks ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
static sig_t _oldhandler ;
static sigset_t _fullsigset , _intsigset ;
2005-03-21 17:16:16 +03:00
static volatile sig_atomic_t _handler_installed ;
2002-03-15 19:07:38 +03:00
2009-08-13 17:23:51 +04:00
static void _undo_flock ( const char * file , int fd )
{
struct stat buf1 , buf2 ;
2009-09-02 18:47:39 +04:00
log_debug ( " _undo_flock %s " , file ) ;
2009-08-13 17:23:51 +04:00
if ( ! flock ( fd , LOCK_NB | LOCK_EX ) & &
! stat ( file , & buf1 ) & &
! fstat ( fd , & buf2 ) & &
is_same_inode ( buf1 , buf2 ) )
if ( unlink ( file ) )
log_sys_error ( " unlink " , file ) ;
if ( close ( fd ) < 0 )
log_sys_error ( " close " , file ) ;
}
2003-05-06 16:03:13 +04:00
static int _release_lock ( const char * file , int unlock )
2002-02-11 18:42:34 +03:00
{
struct lock_list * ll ;
2008-11-04 01:14:30 +03:00
struct dm_list * llh , * llt ;
2002-02-11 18:42:34 +03:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_safe ( llh , llt , & _lock_list ) {
ll = dm_list_item ( llh , struct lock_list ) ;
2002-02-11 18:42:34 +03:00
if ( ! file | | ! strcmp ( ll - > res , file ) ) {
2008-11-04 01:14:30 +03:00
dm_list_del ( llh ) ;
2003-05-06 16:03:13 +04:00
if ( unlock ) {
log_very_verbose ( " Unlocking %s " , ll - > res ) ;
if ( flock ( ll - > lf , LOCK_NB | LOCK_UN ) )
log_sys_error ( " flock " , ll - > res ) ;
}
2002-03-15 19:07:38 +03:00
2009-08-13 17:23:51 +04:00
_undo_flock ( ll - > res , ll - > lf ) ;
2002-02-11 18:42:34 +03:00
2005-10-17 03:03:59 +04:00
dm_free ( ll - > res ) ;
dm_free ( llh ) ;
2002-02-11 18:42:34 +03:00
if ( file )
return 1 ;
}
}
return 0 ;
}
2002-12-20 02:25:55 +03:00
static void _fin_file_locking ( void )
2002-02-11 18:42:34 +03:00
{
2003-05-06 16:03:13 +04:00
_release_lock ( NULL , 1 ) ;
}
static void _reset_file_locking ( void )
{
_release_lock ( NULL , 0 ) ;
2002-02-11 18:42:34 +03:00
}
2008-11-12 12:30:52 +03:00
static void _remove_ctrl_c_handler ( void )
2002-03-15 19:07:38 +03:00
{
siginterrupt ( SIGINT , 0 ) ;
2005-03-21 17:16:16 +03:00
if ( ! _handler_installed )
2002-03-15 19:07:38 +03:00
return ;
2005-03-21 17:16:16 +03:00
_handler_installed = 0 ;
2002-03-15 19:07:38 +03:00
sigprocmask ( SIG_SETMASK , & _fullsigset , NULL ) ;
if ( signal ( SIGINT , _oldhandler ) = = SIG_ERR )
log_sys_error ( " signal " , " _remove_ctrl_c_handler " ) ;
}
2010-07-09 19:34:40 +04:00
static void _trap_ctrl_c ( int sig __attribute__ ( ( unused ) ) )
2002-02-11 18:42:34 +03:00
{
2002-03-15 19:07:38 +03:00
_remove_ctrl_c_handler ( ) ;
2002-02-11 18:42:34 +03:00
log_error ( " CTRL-c detected: giving up waiting for lock " ) ;
}
2010-08-03 17:06:35 +04:00
static void _install_ctrl_c_handler ( void )
2002-02-11 18:42:34 +03:00
{
2005-03-21 17:16:16 +03:00
_handler_installed = 1 ;
if ( ( _oldhandler = signal ( SIGINT , _trap_ctrl_c ) ) = = SIG_ERR ) {
_handler_installed = 0 ;
2002-03-15 19:07:38 +03:00
return ;
2005-03-21 17:16:16 +03:00
}
2002-03-15 19:07:38 +03:00
sigprocmask ( SIG_SETMASK , & _intsigset , NULL ) ;
2002-02-11 18:42:34 +03:00
siginterrupt ( SIGINT , 1 ) ;
}
2009-08-13 17:23:51 +04:00
static int _do_flock ( const char * file , int * fd , int operation , uint32_t nonblock )
2002-02-11 18:42:34 +03:00
{
int r = 1 ;
2005-03-21 17:16:16 +03:00
int old_errno ;
2009-08-13 17:23:51 +04:00
struct stat buf1 , buf2 ;
2009-09-02 18:47:39 +04:00
log_debug ( " _do_flock %s %c%c " ,
file , operation = = LOCK_EX ? ' W ' : ' R ' , nonblock ? ' ' : ' B ' ) ;
2009-08-13 17:23:51 +04:00
do {
if ( ( * fd > - 1 ) & & close ( * fd ) )
log_sys_error ( " close " , file ) ;
if ( ( * fd = open ( file , O_CREAT | O_APPEND | O_RDWR , 0777 ) ) < 0 ) {
log_sys_error ( " open " , file ) ;
return 0 ;
}
if ( nonblock )
operation | = LOCK_NB ;
else
_install_ctrl_c_handler ( ) ;
r = flock ( * fd , operation ) ;
old_errno = errno ;
if ( ! nonblock )
_remove_ctrl_c_handler ( ) ;
if ( r ) {
errno = old_errno ;
log_sys_error ( " flock " , file ) ;
close ( * fd ) ;
return 0 ;
}
if ( ! stat ( file , & buf1 ) & & ! fstat ( * fd , & buf2 ) & &
is_same_inode ( buf1 , buf2 ) )
return 1 ;
} while ( ! nonblock ) ;
return_0 ;
}
2009-09-02 18:47:39 +04:00
# define AUX_LOCK_SUFFIX ":aux"
static int _do_write_priority_flock ( const char * file , int * fd , int operation , uint32_t nonblock )
{
int r , fd_aux = - 1 ;
char * file_aux = alloca ( strlen ( file ) + sizeof ( AUX_LOCK_SUFFIX ) ) ;
strcpy ( file_aux , file ) ;
strcat ( file_aux , AUX_LOCK_SUFFIX ) ;
if ( ( r = _do_flock ( file_aux , & fd_aux , LOCK_EX , 0 ) ) ) {
if ( operation = = LOCK_EX ) {
r = _do_flock ( file , fd , operation , nonblock ) ;
_undo_flock ( file_aux , fd_aux ) ;
} else {
_undo_flock ( file_aux , fd_aux ) ;
r = _do_flock ( file , fd , operation , nonblock ) ;
}
}
return r ;
}
2009-08-13 17:23:51 +04:00
static int _lock_file ( const char * file , uint32_t flags )
{
int operation ;
uint32_t nonblock = flags & LCK_NONBLOCK ;
int r ;
2002-02-11 18:42:34 +03:00
struct lock_list * ll ;
2002-11-18 17:01:16 +03:00
char state ;
2002-02-11 18:42:34 +03:00
switch ( flags & LCK_TYPE_MASK ) {
case LCK_READ :
operation = LOCK_SH ;
2002-11-18 17:01:16 +03:00
state = ' R ' ;
2002-02-11 18:42:34 +03:00
break ;
case LCK_WRITE :
operation = LOCK_EX ;
2002-11-18 17:01:16 +03:00
state = ' W ' ;
2002-02-11 18:42:34 +03:00
break ;
2002-04-04 15:18:45 +04:00
case LCK_UNLOCK :
2003-05-06 16:03:13 +04:00
return _release_lock ( file , 1 ) ;
2002-02-11 18:42:34 +03:00
default :
log_error ( " Unrecognised lock type: %d " , flags & LCK_TYPE_MASK ) ;
return 0 ;
}
2005-10-17 03:03:59 +04:00
if ( ! ( ll = dm_malloc ( sizeof ( struct lock_list ) ) ) )
2009-08-13 17:23:51 +04:00
return_0 ;
2002-02-11 18:42:34 +03:00
2005-10-17 03:03:59 +04:00
if ( ! ( ll - > res = dm_strdup ( file ) ) ) {
dm_free ( ll ) ;
2009-08-13 17:23:51 +04:00
return_0 ;
2002-02-11 18:42:34 +03:00
}
2002-03-15 19:07:38 +03:00
ll - > lf = - 1 ;
2002-11-18 17:01:16 +03:00
log_very_verbose ( " Locking %s %c%c " , ll - > res , state ,
2009-08-13 17:23:51 +04:00
nonblock ? ' ' : ' B ' ) ;
2002-02-11 18:42:34 +03:00
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( file , S_IFREG ) ;
2009-09-02 18:47:39 +04:00
if ( _prioritise_write_locks )
r = _do_write_priority_flock ( file , & ll - > lf , operation , nonblock ) ;
else
r = _do_flock ( file , & ll - > lf , operation , nonblock ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
2009-09-02 18:47:39 +04:00
2009-08-13 17:23:51 +04:00
if ( r )
dm_list_add ( & _lock_list , & ll - > list ) ;
else {
dm_free ( ll - > res ) ;
dm_free ( ll ) ;
stack ;
}
2002-02-11 18:42:34 +03:00
2009-08-13 17:23:51 +04:00
return r ;
2002-02-11 18:42:34 +03:00
}
2002-12-20 02:25:55 +03:00
static int _file_lock_resource ( struct cmd_context * cmd , const char * resource ,
2007-08-22 18:38:18 +04:00
uint32_t flags )
2002-02-11 18:42:34 +03:00
{
char lockfile [ PATH_MAX ] ;
2010-08-17 23:25:05 +04:00
unsigned origin_only = ( flags & LCK_ORIGIN_ONLY ) ? 1 : 0 ;
2002-02-11 18:42:34 +03:00
switch ( flags & LCK_SCOPE_MASK ) {
case LCK_VG :
2008-05-09 23:26:58 +04:00
/* Skip cache refresh for VG_GLOBAL - the caller handles it */
if ( strcmp ( resource , VG_GLOBAL ) )
2010-01-05 19:06:42 +03:00
lvmcache_drop_metadata ( resource , 0 ) ;
2008-05-09 23:26:58 +04:00
2011-01-12 23:42:50 +03:00
if ( ! strcmp ( resource , VG_SYNC_NAMES ) )
fs_unlock ( ) ;
2008-05-09 23:26:58 +04:00
/* LCK_CACHE does not require a real lock */
if ( flags & LCK_CACHE )
2008-04-15 18:46:19 +04:00
break ;
2008-05-09 23:26:58 +04:00
2011-01-05 18:10:30 +03:00
if ( is_orphan_vg ( resource ) | | is_global_vg ( resource ) ) {
if ( dm_snprintf ( lockfile , sizeof ( lockfile ) ,
" %s/P_%s " , _lock_dir , resource + 1 ) < 0 ) {
log_error ( " Too long locking filename %s/P_%s. " ,
_lock_dir , resource + 1 ) ;
return 0 ;
}
} else
if ( dm_snprintf ( lockfile , sizeof ( lockfile ) ,
" %s/V_%s " , _lock_dir , resource ) < 0 ) {
log_error ( " Too long locking filename %s/V_%s. " ,
_lock_dir , resource ) ;
return 0 ;
}
2003-07-05 02:34:56 +04:00
2002-02-25 15:56:16 +03:00
if ( ! _lock_file ( lockfile , flags ) )
2008-04-03 22:56:40 +04:00
return_0 ;
2002-02-11 18:42:34 +03:00
break ;
case LCK_LV :
2002-02-21 00:30:27 +03:00
switch ( flags & LCK_TYPE_MASK ) {
2002-04-04 15:18:45 +04:00
case LCK_UNLOCK :
2010-08-17 23:25:05 +04:00
log_very_verbose ( " Unlocking LV %s%s " , resource , origin_only ? " without snapshots " : " " ) ;
2011-02-18 03:36:04 +03:00
if ( ! lv_resume_if_active ( cmd , resource , origin_only , 0 ) )
2002-02-25 15:56:16 +03:00
return 0 ;
2002-02-21 00:30:27 +03:00
break ;
2004-05-05 16:03:07 +04:00
case LCK_NULL :
2007-11-17 00:16:20 +03:00
log_very_verbose ( " Locking LV %s (NL) " , resource ) ;
2004-05-05 16:03:07 +04:00
if ( ! lv_deactivate ( cmd , resource ) )
return 0 ;
break ;
2002-02-27 15:26:41 +03:00
case LCK_READ :
2007-11-17 00:16:20 +03:00
log_very_verbose ( " Locking LV %s (R) " , resource ) ;
2005-08-15 03:18:28 +04:00
if ( ! lv_activate_with_filter ( cmd , resource , 0 ) )
2002-02-27 15:26:41 +03:00
return 0 ;
break ;
2007-11-17 00:16:20 +03:00
case LCK_PREAD :
log_very_verbose ( " Locking LV %s (PR) - ignored " , resource ) ;
break ;
2002-02-21 00:30:27 +03:00
case LCK_WRITE :
2010-08-17 23:25:05 +04:00
log_very_verbose ( " Locking LV %s (W)%s " , resource , origin_only ? " without snapshots " : " " ) ;
if ( ! lv_suspend_if_active ( cmd , resource , origin_only ) )
2002-02-25 15:56:16 +03:00
return 0 ;
2002-02-21 00:30:27 +03:00
break ;
2002-02-27 15:26:41 +03:00
case LCK_EXCL :
2007-11-17 00:16:20 +03:00
log_very_verbose ( " Locking LV %s (EX) " , resource ) ;
2005-08-15 03:18:28 +04:00
if ( ! lv_activate_with_filter ( cmd , resource , 1 ) )
2002-02-27 15:26:41 +03:00
return 0 ;
break ;
2002-02-21 00:30:27 +03:00
default :
break ;
}
2002-02-25 15:56:16 +03:00
break ;
default :
log_error ( " Unrecognised lock scope: %d " ,
flags & LCK_SCOPE_MASK ) ;
return 0 ;
2002-02-21 00:30:27 +03:00
}
2002-02-25 15:56:16 +03:00
return 1 ;
}
2011-08-09 15:44:57 +04:00
int init_file_locking ( struct locking_type * locking , struct cmd_context * cmd ,
int suppress_messages )
2002-02-11 18:42:34 +03:00
{
2011-04-08 18:13:08 +04:00
int r ;
2002-12-20 02:25:55 +03:00
locking - > lock_resource = _file_lock_resource ;
2003-05-06 16:03:13 +04:00
locking - > reset_locking = _reset_file_locking ;
2002-12-20 02:25:55 +03:00
locking - > fin_locking = _fin_file_locking ;
2004-03-26 23:17:11 +03:00
locking - > flags = 0 ;
2002-02-11 18:42:34 +03:00
/* Get lockfile directory from config file */
2006-05-16 20:48:31 +04:00
strncpy ( _lock_dir , find_config_tree_str ( cmd , " global/locking_dir " ,
DEFAULT_LOCK_DIR ) ,
2002-02-11 18:42:34 +03:00
sizeof ( _lock_dir ) ) ;
2009-09-02 18:47:39 +04:00
_prioritise_write_locks =
find_config_tree_bool ( cmd , " global/prioritise_write_locks " ,
DEFAULT_PRIORITISE_WRITE_LOCKS ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( _lock_dir , S_IFDIR ) ;
r = dm_create_dir ( _lock_dir ) ;
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
if ( ! r )
2002-02-11 18:42:34 +03:00
return 0 ;
2002-07-11 00:43:32 +04:00
/* Trap a read-only file system */
if ( ( access ( _lock_dir , R_OK | W_OK | X_OK ) = = - 1 ) & & ( errno = = EROFS ) )
return 0 ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & _lock_list ) ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
if ( sigfillset ( & _intsigset ) | | sigfillset ( & _fullsigset ) ) {
2011-08-09 15:44:57 +04:00
log_sys_error_suppress ( suppress_messages , " sigfillset " ,
" init_file_locking " ) ;
2002-03-15 19:07:38 +03:00
return 0 ;
}
if ( sigdelset ( & _intsigset , SIGINT ) ) {
2011-08-09 15:44:57 +04:00
log_sys_error_suppress ( suppress_messages , " sigdelset " ,
" init_file_locking " ) ;
2002-03-15 19:07:38 +03:00
return 0 ;
}
2002-02-11 18:42:34 +03:00
return 1 ;
}