2002-02-11 18:42:34 +03:00
/*
2004-03-30 23:35:44 +04: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"
2003-07-05 02:34:56 +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 {
struct list list ;
int lf ;
char * res ;
} ;
static struct list _lock_list ;
static char _lock_dir [ NAME_LEN ] ;
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
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 ;
struct list * llh , * llt ;
2002-03-15 19:07:38 +03:00
struct stat buf1 , buf2 ;
2002-02-11 18:42:34 +03:00
list_iterate_safe ( llh , llt , & _lock_list ) {
ll = list_item ( llh , struct lock_list ) ;
if ( ! file | | ! strcmp ( ll - > res , file ) ) {
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
if ( ! flock ( ll - > lf , LOCK_NB | LOCK_EX ) & &
! stat ( ll - > res , & buf1 ) & &
! fstat ( ll - > lf , & buf2 ) & &
2007-07-20 19:22:46 +04:00
is_same_inode ( buf1 , buf2 ) )
2002-02-11 18:42:34 +03:00
if ( unlink ( ll - > res ) )
log_sys_error ( " unlink " , ll - > res ) ;
if ( close ( ll - > lf ) < 0 )
log_sys_error ( " close " , ll - > res ) ;
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
}
2002-03-15 19:07:38 +03:00
static void _remove_ctrl_c_handler ( )
{
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 " ) ;
}
2006-05-11 21:58:58 +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 " ) ;
}
static void _install_ctrl_c_handler ( )
{
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 ) ;
}
2007-08-22 18:38:18 +04:00
static int _lock_file ( const char * file , uint32_t flags )
2002-02-11 18:42:34 +03:00
{
int operation ;
int r = 1 ;
2005-03-21 17:16:16 +03:00
int old_errno ;
2002-02-11 18:42:34 +03:00
struct lock_list * ll ;
2002-03-15 19:07:38 +03:00
struct stat buf1 , buf2 ;
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 ) ) ) )
2002-02-11 18:42:34 +03:00
return 0 ;
2005-10-17 03:03:59 +04:00
if ( ! ( ll - > res = dm_strdup ( file ) ) ) {
dm_free ( ll ) ;
2002-02-11 18:42:34 +03:00
return 0 ;
}
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 ,
flags & LCK_NONBLOCK ? ' ' : ' B ' ) ;
2002-03-15 19:07:38 +03:00
do {
2007-01-25 17:37:48 +03:00
if ( ( ll - > lf > - 1 ) & & close ( ll - > lf ) )
log_sys_error ( " close " , file ) ;
2002-03-15 19:07:38 +03:00
if ( ( ll - > lf = open ( file , O_CREAT | O_APPEND | O_RDWR , 0777 ) )
< 0 ) {
log_sys_error ( " open " , file ) ;
goto err ;
}
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
if ( ( flags & LCK_NONBLOCK ) )
operation | = LOCK_NB ;
else
_install_ctrl_c_handler ( ) ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
r = flock ( ll - > lf , operation ) ;
2005-03-21 17:16:16 +03:00
old_errno = errno ;
2002-03-15 19:07:38 +03:00
if ( ! ( flags & LCK_NONBLOCK ) )
_remove_ctrl_c_handler ( ) ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
if ( r ) {
2005-03-21 17:16:16 +03:00
errno = old_errno ;
2002-03-15 19:07:38 +03:00
log_sys_error ( " flock " , ll - > res ) ;
2007-07-20 16:12:52 +04:00
close ( ll - > lf ) ;
2002-03-15 19:07:38 +03:00
goto err ;
}
if ( ! stat ( ll - > res , & buf1 ) & & ! fstat ( ll - > lf , & buf2 ) & &
2007-07-20 19:22:46 +04:00
is_same_inode ( buf1 , buf2 ) )
2002-03-15 19:07:38 +03:00
break ;
} while ( ! ( flags & LCK_NONBLOCK ) ) ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
list_add ( & _lock_list , & ll - > list ) ;
return 1 ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
err :
2005-10-17 03:03:59 +04:00
dm_free ( ll - > res ) ;
dm_free ( ll ) ;
2002-03-15 19:07:38 +03:00
return 0 ;
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 ] ;
2006-05-10 21:49:25 +04:00
assert ( resource ) ;
2002-02-11 18:42:34 +03:00
switch ( flags & LCK_SCOPE_MASK ) {
case LCK_VG :
2007-08-23 19:02:26 +04:00
if ( ! * resource ) /* FIXME Deprecated */
2006-08-21 16:54:53 +04:00
dm_snprintf ( lockfile , sizeof ( lockfile ) ,
2002-02-11 18:42:34 +03:00
" %s/P_orphans " , _lock_dir ) ;
2007-08-23 19:02:26 +04:00
else if ( * resource = = ' # ' )
dm_snprintf ( lockfile , sizeof ( lockfile ) ,
" %s/P_%s " , _lock_dir , resource + 1 ) ;
2002-02-11 18:42:34 +03:00
else
2006-08-21 16:54:53 +04:00
dm_snprintf ( lockfile , sizeof ( lockfile ) ,
2002-02-11 18:42:34 +03:00
" %s/V_%s " , _lock_dir , resource ) ;
2003-07-05 02:34:56 +04:00
2002-02-25 15:56:16 +03:00
if ( ! _lock_file ( lockfile , flags ) )
return 0 ;
2003-07-05 02:34:56 +04:00
switch ( flags & LCK_TYPE_MASK ) {
case LCK_UNLOCK :
lvmcache_unlock_vgname ( resource ) ;
break ;
default :
lvmcache_lock_vgname ( resource ,
( flags & LCK_TYPE_MASK ) = =
LCK_READ ) ;
}
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 :
2003-07-05 02:34:56 +04:00
log_debug ( " Unlocking LV %s " , resource ) ;
2002-02-25 15:56:16 +03:00
if ( ! lv_resume_if_active ( cmd , resource ) )
return 0 ;
2002-02-21 00:30:27 +03:00
break ;
2004-05-05 16:03:07 +04:00
case LCK_NULL :
log_debug ( " Locking LV %s (NL) " , resource ) ;
if ( ! lv_deactivate ( cmd , resource ) )
return 0 ;
break ;
2002-02-27 15:26:41 +03:00
case LCK_READ :
2003-07-05 02:34:56 +04:00
log_debug ( " 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 ;
2002-02-21 00:30:27 +03:00
case LCK_WRITE :
2003-07-05 02:34:56 +04:00
log_debug ( " Locking LV %s (W) " , resource ) ;
2002-02-25 15:56:16 +03:00
if ( ! lv_suspend_if_active ( cmd , resource ) )
return 0 ;
2002-02-21 00:30:27 +03:00
break ;
2002-02-27 15:26:41 +03:00
case LCK_EXCL :
2003-07-05 02:34:56 +04:00
log_debug ( " 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 ;
}
2006-05-16 20:48:31 +04:00
int init_file_locking ( struct locking_type * locking , struct cmd_context * cmd )
2002-02-11 18:42:34 +03:00
{
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 ) ) ;
2007-07-28 16:26:21 +04:00
if ( ! dm_create_dir ( _lock_dir ) )
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 ;
2002-02-11 18:42:34 +03:00
list_init ( & _lock_list ) ;
2002-03-15 19:07:38 +03:00
if ( sigfillset ( & _intsigset ) | | sigfillset ( & _fullsigset ) ) {
log_sys_error ( " sigfillset " , " init_file_locking " ) ;
return 0 ;
}
if ( sigdelset ( & _intsigset , SIGINT ) ) {
log_sys_error ( " sigdelset " , " init_file_locking " ) ;
return 0 ;
}
2002-02-11 18:42:34 +03:00
return 1 ;
}