2019-05-29 17:17:54 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-03-15 23:30:06 +04:00
/*
* An implementation of the host initiated guest snapshot for Hyper - V .
*
* Copyright ( C ) 2013 , Microsoft , Inc .
* Author : K . Y . Srinivasan < kys @ microsoft . com >
*/
# include <sys/types.h>
# include <sys/poll.h>
2013-04-24 18:48:52 +04:00
# include <sys/ioctl.h>
2017-08-06 23:12:52 +03:00
# include <sys/stat.h>
2018-03-05 08:17:14 +03:00
# include <sys/sysmacros.h>
2013-04-24 18:48:52 +04:00
# include <fcntl.h>
2013-03-15 23:30:06 +04:00
# include <stdio.h>
2013-04-24 18:48:51 +04:00
# include <mntent.h>
2013-03-15 23:30:06 +04:00
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
2013-04-24 18:48:52 +04:00
# include <linux/fs.h>
2017-08-06 23:12:52 +03:00
# include <linux/major.h>
2013-03-15 23:30:06 +04:00
# include <linux/hyperv.h>
# include <syslog.h>
2014-10-22 20:07:11 +04:00
# include <getopt.h>
2018-06-05 23:37:56 +03:00
# include <stdbool.h>
# include <dirent.h>
2013-03-15 23:30:06 +04:00
2014-09-26 08:52:04 +04:00
/* Don't use syslog() in the function since that can cause write to disk */
static int vss_do_freeze ( char * dir , unsigned int cmd )
2013-04-24 18:48:52 +04:00
{
int ret , fd = open ( dir , O_RDONLY ) ;
if ( fd < 0 )
return 1 ;
2014-09-26 08:52:04 +04:00
2013-04-24 18:48:52 +04:00
ret = ioctl ( fd , cmd , 0 ) ;
2014-09-26 08:52:04 +04:00
/*
* If a partition is mounted more than once , only the first
* FREEZE / THAW can succeed and the later ones will get
* EBUSY / EINVAL respectively : there could be 2 cases :
2019-05-06 19:51:24 +03:00
* 1 ) a user may mount the same partition to different directories
2014-09-26 08:52:04 +04:00
* by mistake or on purpose ;
* 2 ) The subvolume of btrfs appears to have the same partition
* mounted more than once .
*/
if ( ret ) {
if ( ( cmd = = FIFREEZE & & errno = = EBUSY ) | |
( cmd = = FITHAW & & errno = = EINVAL ) ) {
close ( fd ) ;
return 0 ;
}
}
2013-04-24 18:48:52 +04:00
close ( fd ) ;
return ! ! ret ;
}
2018-06-05 23:37:56 +03:00
static bool is_dev_loop ( const char * blkname )
{
char * buffer ;
DIR * dir ;
struct dirent * entry ;
bool ret = false ;
buffer = malloc ( PATH_MAX ) ;
if ( ! buffer ) {
syslog ( LOG_ERR , " Can't allocate memory! " ) ;
exit ( 1 ) ;
}
snprintf ( buffer , PATH_MAX , " %s/loop " , blkname ) ;
if ( ! access ( buffer , R_OK | X_OK ) ) {
ret = true ;
goto free_buffer ;
} else if ( errno ! = ENOENT ) {
syslog ( LOG_ERR , " Can't access: %s; error:%d %s! " ,
buffer , errno , strerror ( errno ) ) ;
}
snprintf ( buffer , PATH_MAX , " %s/slaves " , blkname ) ;
dir = opendir ( buffer ) ;
if ( ! dir ) {
if ( errno ! = ENOENT )
syslog ( LOG_ERR , " Can't opendir: %s; error:%d %s! " ,
buffer , errno , strerror ( errno ) ) ;
goto free_buffer ;
}
while ( ( entry = readdir ( dir ) ) ! = NULL ) {
if ( strcmp ( entry - > d_name , " . " ) = = 0 | |
strcmp ( entry - > d_name , " .. " ) = = 0 )
continue ;
snprintf ( buffer , PATH_MAX , " %s/slaves/%s " , blkname ,
entry - > d_name ) ;
if ( is_dev_loop ( buffer ) ) {
ret = true ;
break ;
}
}
closedir ( dir ) ;
free_buffer :
free ( buffer ) ;
return ret ;
}
2013-03-15 23:30:06 +04:00
static int vss_operate ( int operation )
{
2013-04-24 18:48:51 +04:00
char match [ ] = " /dev/ " ;
FILE * mounts ;
struct mntent * ent ;
2017-08-06 23:12:52 +03:00
struct stat sb ;
2015-03-18 22:29:28 +03:00
char errdir [ 1024 ] = { 0 } ;
2018-06-05 23:37:56 +03:00
char blkdir [ 23 ] ; /* /sys/dev/block/XXX:XXX */
2013-04-24 18:48:52 +04:00
unsigned int cmd ;
2014-11-10 19:37:01 +03:00
int error = 0 , root_seen = 0 , save_errno = 0 ;
2013-03-15 23:30:06 +04:00
switch ( operation ) {
case VSS_OP_FREEZE :
2013-04-24 18:48:52 +04:00
cmd = FIFREEZE ;
2013-03-15 23:30:06 +04:00
break ;
case VSS_OP_THAW :
2013-04-24 18:48:52 +04:00
cmd = FITHAW ;
2013-03-15 23:30:06 +04:00
break ;
2013-04-24 18:48:48 +04:00
default :
return - 1 ;
2013-03-15 23:30:06 +04:00
}
2013-04-24 18:48:51 +04:00
mounts = setmntent ( " /proc/mounts " , " r " ) ;
if ( mounts = = NULL )
2013-04-24 18:48:48 +04:00
return - 1 ;
2013-03-15 23:30:06 +04:00
2013-04-24 18:48:54 +04:00
while ( ( ent = getmntent ( mounts ) ) ) {
2013-04-24 18:48:51 +04:00
if ( strncmp ( ent - > mnt_fsname , match , strlen ( match ) ) )
2013-03-15 23:30:06 +04:00
continue ;
2018-06-05 23:37:56 +03:00
if ( stat ( ent - > mnt_fsname , & sb ) ) {
syslog ( LOG_ERR , " Can't stat: %s; error:%d %s! " ,
ent - > mnt_fsname , errno , strerror ( errno ) ) ;
} else {
sprintf ( blkdir , " /sys/dev/block/%d:%d " ,
major ( sb . st_rdev ) , minor ( sb . st_rdev ) ) ;
if ( is_dev_loop ( blkdir ) )
continue ;
}
2014-11-10 19:37:02 +03:00
if ( hasmntopt ( ent , MNTOPT_RO ) ! = NULL )
2013-04-24 18:48:53 +04:00
continue ;
2014-02-12 20:40:22 +04:00
if ( strcmp ( ent - > mnt_type , " vfat " ) = = 0 )
continue ;
2013-04-24 18:48:51 +04:00
if ( strcmp ( ent - > mnt_dir , " / " ) = = 0 ) {
root_seen = 1 ;
continue ;
}
2014-09-26 08:52:04 +04:00
error | = vss_do_freeze ( ent - > mnt_dir , cmd ) ;
if ( error & & operation = = VSS_OP_FREEZE )
goto err ;
2013-03-15 23:30:06 +04:00
}
2015-03-18 22:29:28 +03:00
endmntent ( mounts ) ;
2013-04-24 18:48:51 +04:00
if ( root_seen ) {
2014-09-26 08:52:04 +04:00
error | = vss_do_freeze ( " / " , cmd ) ;
if ( error & & operation = = VSS_OP_FREEZE )
goto err ;
2013-04-24 18:48:51 +04:00
}
2013-03-15 23:30:06 +04:00
2014-11-10 19:37:01 +03:00
goto out ;
2014-09-26 08:52:04 +04:00
err :
2014-11-10 19:37:01 +03:00
save_errno = errno ;
2015-03-18 22:29:28 +03:00
if ( ent ) {
strncpy ( errdir , ent - > mnt_dir , sizeof ( errdir ) - 1 ) ;
endmntent ( mounts ) ;
}
2014-09-26 08:52:04 +04:00
vss_operate ( VSS_OP_THAW ) ;
2014-11-10 19:37:01 +03:00
/* Call syslog after we thaw all filesystems */
if ( ent )
syslog ( LOG_ERR , " FREEZE of %s failed; error:%d %s " ,
2015-03-18 22:29:28 +03:00
errdir , save_errno , strerror ( save_errno ) ) ;
2014-11-10 19:37:01 +03:00
else
syslog ( LOG_ERR , " FREEZE of / failed; error:%d %s " , save_errno ,
strerror ( save_errno ) ) ;
out :
2013-03-15 23:30:06 +04:00
return error ;
}
2014-10-22 20:07:11 +04:00
void print_usage ( char * argv [ ] )
{
fprintf ( stderr , " Usage: %s [options] \n "
" Options are: \n "
" -n, --no-daemon stay in foreground, don't daemonize \n "
" -h, --help print this help \n " , argv [ 0 ] ) ;
}
int main ( int argc , char * argv [ ] )
2013-03-15 23:30:06 +04:00
{
2015-04-12 04:07:56 +03:00
int vss_fd , len ;
2013-03-15 23:30:06 +04:00
int error ;
struct pollfd pfd ;
int op ;
2015-04-12 04:07:56 +03:00
struct hv_vss_msg vss_msg [ 1 ] ;
2014-10-22 20:07:11 +04:00
int daemonize = 1 , long_index = 0 , opt ;
2015-04-12 04:07:57 +03:00
int in_handshake = 1 ;
__u32 kernel_modver ;
2014-10-22 20:07:11 +04:00
static struct option long_options [ ] = {
{ " help " , no_argument , 0 , ' h ' } ,
{ " no-daemon " , no_argument , 0 , ' n ' } ,
{ 0 , 0 , 0 , 0 }
} ;
while ( ( opt = getopt_long ( argc , argv , " hn " , long_options ,
& long_index ) ) ! = - 1 ) {
switch ( opt ) {
case ' n ' :
daemonize = 0 ;
break ;
case ' h ' :
2019-05-06 19:50:58 +03:00
print_usage ( argv ) ;
exit ( 0 ) ;
2014-10-22 20:07:11 +04:00
default :
print_usage ( argv ) ;
exit ( EXIT_FAILURE ) ;
}
}
2013-03-15 23:30:06 +04:00
2014-10-22 20:07:11 +04:00
if ( daemonize & & daemon ( 1 , 0 ) )
2013-04-24 18:48:48 +04:00
return 1 ;
2013-03-15 23:30:06 +04:00
openlog ( " Hyper-V VSS " , 0 , LOG_USER ) ;
syslog ( LOG_INFO , " VSS starting; pid is:%d " , getpid ( ) ) ;
2015-04-12 04:07:56 +03:00
vss_fd = open ( " /dev/vmbus/hv_vss " , O_RDWR ) ;
if ( vss_fd < 0 ) {
syslog ( LOG_ERR , " open /dev/vmbus/hv_vss failed; error: %d %s " ,
errno , strerror ( errno ) ) ;
2013-06-27 15:52:48 +04:00
exit ( EXIT_FAILURE ) ;
}
2013-03-15 23:30:06 +04:00
/*
* Register ourselves with the kernel .
*/
2015-04-12 04:07:56 +03:00
vss_msg - > vss_hdr . operation = VSS_OP_REGISTER1 ;
2013-03-15 23:30:06 +04:00
2015-04-12 04:07:56 +03:00
len = write ( vss_fd , vss_msg , sizeof ( struct hv_vss_msg ) ) ;
2013-03-15 23:30:06 +04:00
if ( len < 0 ) {
2015-04-12 04:07:56 +03:00
syslog ( LOG_ERR , " registration to kernel failed; error: %d %s " ,
errno , strerror ( errno ) ) ;
close ( vss_fd ) ;
2013-03-15 23:30:06 +04:00
exit ( EXIT_FAILURE ) ;
}
2015-04-12 04:07:56 +03:00
pfd . fd = vss_fd ;
2013-03-15 23:30:06 +04:00
while ( 1 ) {
pfd . events = POLLIN ;
pfd . revents = 0 ;
2013-06-27 15:52:49 +04:00
if ( poll ( & pfd , 1 , - 1 ) < 0 ) {
syslog ( LOG_ERR , " poll failed; error:%d %s " , errno , strerror ( errno ) ) ;
if ( errno = = EINVAL ) {
2015-04-12 04:07:56 +03:00
close ( vss_fd ) ;
2013-06-27 15:52:49 +04:00
exit ( EXIT_FAILURE ) ;
}
else
continue ;
}
2013-03-15 23:30:06 +04:00
2015-04-12 04:07:56 +03:00
len = read ( vss_fd , vss_msg , sizeof ( struct hv_vss_msg ) ) ;
2013-03-15 23:30:06 +04:00
2015-04-12 04:07:57 +03:00
if ( in_handshake ) {
if ( len ! = sizeof ( kernel_modver ) ) {
syslog ( LOG_ERR , " invalid version negotiation " ) ;
exit ( EXIT_FAILURE ) ;
}
kernel_modver = * ( __u32 * ) vss_msg ;
in_handshake = 0 ;
syslog ( LOG_INFO , " VSS: kernel module version: %d " ,
kernel_modver ) ;
continue ;
}
2015-04-12 04:07:56 +03:00
if ( len ! = sizeof ( struct hv_vss_msg ) ) {
syslog ( LOG_ERR , " read failed; error:%d %s " ,
errno , strerror ( errno ) ) ;
close ( vss_fd ) ;
return EXIT_FAILURE ;
2013-04-24 18:48:49 +04:00
}
2013-03-15 23:30:06 +04:00
op = vss_msg - > vss_hdr . operation ;
error = HV_S_OK ;
switch ( op ) {
case VSS_OP_FREEZE :
case VSS_OP_THAW :
error = vss_operate ( op ) ;
2014-09-26 08:52:04 +04:00
syslog ( LOG_INFO , " VSS: op=%s: %s \n " ,
op = = VSS_OP_FREEZE ? " FREEZE " : " THAW " ,
error ? " failed " : " succeeded " ) ;
if ( error ) {
2013-03-15 23:30:06 +04:00
error = HV_E_FAIL ;
2014-09-26 08:52:04 +04:00
syslog ( LOG_ERR , " op=%d failed! " , op ) ;
syslog ( LOG_ERR , " report it with these files: " ) ;
syslog ( LOG_ERR , " /etc/fstab and /proc/mounts " ) ;
}
2013-03-15 23:30:06 +04:00
break ;
2016-09-02 15:58:25 +03:00
case VSS_OP_HOT_BACKUP :
syslog ( LOG_INFO , " VSS: op=CHECK HOT BACKUP \n " ) ;
break ;
2013-03-15 23:30:06 +04:00
default :
syslog ( LOG_ERR , " Illegal op:%d \n " , op ) ;
}
vss_msg - > error = error ;
2015-12-15 03:01:58 +03:00
len = write ( vss_fd , vss_msg , sizeof ( struct hv_vss_msg ) ) ;
2015-04-12 04:07:56 +03:00
if ( len ! = sizeof ( struct hv_vss_msg ) ) {
syslog ( LOG_ERR , " write failed; error: %d %s " , errno ,
strerror ( errno ) ) ;
2017-05-01 02:21:14 +03:00
if ( op = = VSS_OP_FREEZE )
vss_operate ( VSS_OP_THAW ) ;
2013-03-15 23:30:06 +04:00
}
}
2015-04-12 04:07:56 +03:00
close ( vss_fd ) ;
exit ( 0 ) ;
2013-03-15 23:30:06 +04:00
}