2004-06-24 12:02:38 +04:00
/*
* Copyright ( C ) 2002 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 Red Hat , Inc . All rights reserved .
*
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU General Public License v .2 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/* Routines dealing with the System LV */
# include <pthread.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/socket.h>
# include <sys/uio.h>
# include <sys/un.h>
# include <sys/time.h>
# include <sys/ioctl.h>
# include <sys/mount.h>
# include <sys/utsname.h>
# include <syslog.h>
# include <netinet/in.h>
# include <stdio.h>
# include <stdlib.h>
# include <stddef.h>
# include <signal.h>
# include <unistd.h>
# include <fcntl.h>
# include <dirent.h>
# include <errno.h>
# include <mntent.h>
# include "libdlm.h"
# include "log.h"
# include "list.h"
# include "locking.h"
# include "system-lv.h"
2005-01-13 16:24:02 +03:00
# include "clvm.h"
2004-06-24 12:02:38 +04:00
# include "clvmd-comms.h"
2005-01-13 16:24:02 +03:00
# include "clvmd.h"
2004-06-24 12:02:38 +04:00
# ifdef HAVE_CCS
# include "ccs.h"
# endif
# define SYSTEM_LV_FILESYSTEM "ext2"
# define SYSTEM_LV_MOUNTPOINT " / tmp / .clvmd-XXXXXX"
extern char * config_filename ( void ) ;
static char system_lv_name [ PATH_MAX ] = { ' \0 ' } ;
static char mount_point [ PATH_MAX ] = { ' \0 ' } ;
static int mounted = 0 ;
static int mounted_rw = 0 ;
static int lockid ;
static const char * lock_name = " CLVM_SYSTEM_LV " ;
/* Look in /proc/mounts or (as a last resort) /etc/mtab to
see if the system - lv is mounted . If it is mounted and we
think it ' s not then abort because we don ' t have the right
lock status and we don ' t know what other processes are doing with it .
Returns 1 for mounted , 0 for not mounted so it matches the condition
of the " mounted " static variable above .
*/
static int is_really_mounted ( void )
{
FILE * mountfile ;
struct mntent * ment ;
mountfile = setmntent ( " /proc/mounts " , " r " ) ;
if ( ! mountfile ) {
mountfile = setmntent ( " /etc/mtab " , " r " ) ;
if ( ! mountfile ) {
log_error ( " Unable to open /proc/mounts or /etc/mtab " ) ;
return - 1 ;
}
}
/* Look for system LV name in the file */
do {
ment = getmntent ( mountfile ) ;
if ( ment ) {
if ( strcmp ( ment - > mnt_fsname , system_lv_name ) = = 0 ) {
endmntent ( mountfile ) ;
return 1 ;
}
}
}
while ( ment ) ;
endmntent ( mountfile ) ;
return 0 ;
}
/* Get the system LV name from the config file */
static int find_system_lv ( void )
{
if ( system_lv_name [ 0 ] = = ' \0 ' ) {
# ifdef HAVE_CCS
int error ;
ccs_node_t * ctree ;
/* Read the cluster config file */
/* Open the config file */
error = open_ccs_file ( & ctree , " clvm.ccs " ) ;
if ( error ) {
perror ( " reading config file " ) ;
return - 1 ;
}
strcpy ( system_lv_name , find_ccs_str ( ctree ,
" cluster/systemlv " , ' / ' ,
" /dev/vg/system_lv " ) ) ;
/* Finished with config file */
close_ccs_file ( ctree ) ;
# else
if ( getenv ( " CLVMD_SYSTEM_LV " ) )
strcpy ( system_lv_name , getenv ( " CLVMD_SYSTEM_LV " ) ) ;
else
return - 1 ;
# endif
}
/* See if it has been mounted outside our control */
if ( is_really_mounted ( ) ! = mounted ) {
log_error
( " The system LV state has been mounted/umounted outside the control of clvmd \n "
" it cannot not be used for cluster communications until this is fixed. \n " ) ;
return - 1 ;
}
return 0 ;
}
/* No prizes */
int system_lv_umount ( void )
{
if ( ! mounted )
return 0 ;
if ( umount ( mount_point ) < 0 ) {
log_error ( " umount of system LV (%s) failed: %m \n " ,
system_lv_name ) ;
return - 1 ;
}
sync_unlock ( lock_name , lockid ) ;
mounted = 0 ;
/* Remove the mount point */
rmdir ( mount_point ) ;
return 0 ;
}
int system_lv_mount ( int readwrite )
{
int status ;
int saved_errno ;
int fd ;
if ( find_system_lv ( ) ) {
errno = EBUSY ;
return - 1 ;
}
/* Is it already mounted suitably? */
if ( mounted ) {
if ( ! readwrite | | ( readwrite & & mounted_rw ) ) {
return 0 ;
} else {
/* Mounted RO and we need RW */
if ( system_lv_umount ( ) < 0 )
return - 1 ;
}
}
/* Randomize the mount point */
strcpy ( mount_point , SYSTEM_LV_MOUNTPOINT ) ;
fd = mkstemp ( mount_point ) ;
if ( fd < 0 ) {
log_error ( " mkstemp for system LV mount point failed: %m \n " ) ;
return - 1 ;
}
/* Race condition here but there's no mkstemp for directories */
close ( fd ) ;
unlink ( mount_point ) ;
mkdir ( mount_point , 0600 ) ;
/* Make sure we have a system-lv lock */
status =
sync_lock ( lock_name , ( readwrite ) ? LKM_EXMODE : LKM_CRMODE , 0 ,
& lockid ) ;
if ( status < 0 )
return - 1 ;
/* Mount it */
if ( mount ( system_lv_name , mount_point , SYSTEM_LV_FILESYSTEM ,
MS_MGC_VAL | MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_SYNCHRONOUS
| ( readwrite ? 0 : MS_RDONLY ) , NULL ) < 0 ) {
/* mount(2) returns EINVAL if the volume has no FS on it. So, if we want to
write to it we try to make a filesystem in it and retry the mount */
if ( errno = = EINVAL & & readwrite ) {
char cmd [ 256 ] ;
log_error ( " Attempting mkfs on system LV device %s \n " ,
system_lv_name ) ;
snprintf ( cmd , sizeof ( cmd ) , " /sbin/mkfs -t %s %s " ,
SYSTEM_LV_FILESYSTEM , system_lv_name ) ;
system ( cmd ) ;
if ( mount
( system_lv_name , mount_point , SYSTEM_LV_FILESYSTEM ,
MS_MGC_VAL | MS_NOSUID | MS_NODEV | MS_NOEXEC |
MS_SYNCHRONOUS | ( readwrite ? 0 : MS_RDONLY ) ,
NULL ) = = 0 )
goto mounted ;
}
saved_errno = errno ;
log_error ( " mount of system LV (%s, %s, %s) failed: %m \n " ,
system_lv_name , mount_point , SYSTEM_LV_FILESYSTEM ) ;
sync_unlock ( lock_name , lockid ) ;
errno = saved_errno ;
return - 1 ;
}
mounted :
/* Set the internal flags */
mounted = 1 ;
mounted_rw = readwrite ;
return 0 ;
}
/* Erase *all* files in the root directory of the system LV.
This * MUST * be called with an appropriate lock held !
The LV is left mounted RW because it is assumed that the
caller wants to write something here after clearing some space */
int system_lv_eraseall ( void )
{
DIR * dir ;
struct dirent * ent ;
char fname [ PATH_MAX ] ;
/* Must be mounted R/W */
system_lv_mount ( 1 ) ;
dir = opendir ( mount_point ) ;
if ( ! dir )
return - 1 ;
while ( ( ent = readdir ( dir ) ) ) {
struct stat st ;
snprintf ( fname , sizeof ( fname ) , " %s/%s " , mount_point ,
ent - > d_name ) ;
if ( stat ( fname , & st ) ) {
if ( S_ISREG ( st . st_mode ) )
unlink ( fname ) ;
}
}
closedir ( dir ) ;
return 0 ;
}
/* This is a "high-level" routine - it mounts the system LV, writes
the data into a file named after this node and then umounts the LV
again */
int system_lv_write_data ( char * data , ssize_t len )
{
struct utsname nodeinfo ;
char fname [ PATH_MAX ] ;
int outfile ;
ssize_t thiswrite ;
ssize_t written ;
if ( system_lv_mount ( 1 ) )
return - 1 ;
/* Build the file name we are goingto use. */
uname ( & nodeinfo ) ;
snprintf ( fname , sizeof ( fname ) , " %s/%s " , mount_point , nodeinfo . nodename ) ;
/* Open the file for output */
outfile = open ( fname , O_RDWR | O_CREAT | O_TRUNC , 0600 ) ;
if ( outfile < 0 ) {
int saved_errno = errno ;
system_lv_umount ( ) ;
errno = saved_errno ;
return - 1 ;
}
written = 0 ;
do {
thiswrite = write ( outfile , data + written , len - written ) ;
if ( thiswrite > 0 )
written + = thiswrite ;
} while ( written < len & & thiswrite > 0 ) ;
close ( outfile ) ;
system_lv_umount ( ) ;
return ( thiswrite < 0 ) ? - 1 : 0 ;
}
/* This is a "high-level" routine - it mounts the system LV, reads
the data from a named file and then umounts the LV
again */
int system_lv_read_data ( char * fname_base , char * data , ssize_t * len )
{
char fname [ PATH_MAX ] ;
int outfile ;
struct stat st ;
ssize_t filesize ;
ssize_t thisread ;
ssize_t readbytes ;
if ( system_lv_mount ( 0 ) )
return - 1 ;
/* Build the file name we are going to use. */
snprintf ( fname , sizeof ( fname ) , " %s/%s " , mount_point , fname_base ) ;
/* Get the file size and stuff. Actually we only need the file size but
this will also check that the file exists */
if ( stat ( fname , & st ) < 0 ) {
int saved_errno = errno ;
log_error ( " stat of file %s on system LV failed: %m \n " , fname ) ;
system_lv_umount ( ) ;
errno = saved_errno ;
return - 1 ;
}
filesize = st . st_size ;
outfile = open ( fname , O_RDONLY ) ;
if ( outfile < 0 ) {
int saved_errno = errno ;
log_error ( " open of file %s on system LV failed: %m \n " , fname ) ;
system_lv_umount ( ) ;
errno = saved_errno ;
return - 1 ;
}
readbytes = 0 ;
do {
thisread =
read ( outfile , data + readbytes , filesize - readbytes ) ;
if ( thisread > 0 )
readbytes + = thisread ;
} while ( readbytes < filesize & & thisread > 0 ) ;
close ( outfile ) ;
system_lv_umount ( ) ;
* len = readbytes ;
return ( thisread < 0 ) ? - 1 : 0 ;
}