2013-03-15 12:30:06 -07:00
/*
* An implementation of the host initiated guest snapshot for Hyper - V .
*
*
* Copyright ( C ) 2013 , Microsoft , Inc .
* Author : K . Y . Srinivasan < kys @ microsoft . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
*/
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/poll.h>
2013-04-24 07:48:52 -07:00
# include <sys/ioctl.h>
# include <fcntl.h>
2013-03-15 12:30:06 -07:00
# include <stdio.h>
2013-04-24 07:48:51 -07:00
# include <mntent.h>
2013-03-15 12:30:06 -07:00
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
# include <arpa/inet.h>
2013-04-24 07:48:52 -07:00
# include <linux/fs.h>
2013-03-15 12:30:06 -07:00
# include <linux/connector.h>
# include <linux/hyperv.h>
# include <linux/netlink.h>
# include <syslog.h>
static struct sockaddr_nl addr ;
# ifndef SOL_NETLINK
# define SOL_NETLINK 270
# endif
2013-04-24 07:48:52 -07:00
static int vss_do_freeze ( char * dir , unsigned int cmd , char * fs_op )
{
int ret , fd = open ( dir , O_RDONLY ) ;
if ( fd < 0 )
return 1 ;
ret = ioctl ( fd , cmd , 0 ) ;
syslog ( LOG_INFO , " VSS: %s of %s: %s \n " , fs_op , dir , strerror ( errno ) ) ;
close ( fd ) ;
return ! ! ret ;
}
2013-03-15 12:30:06 -07:00
static int vss_operate ( int operation )
{
char * fs_op ;
2013-04-24 07:48:51 -07:00
char match [ ] = " /dev/ " ;
FILE * mounts ;
struct mntent * ent ;
2013-04-24 07:48:52 -07:00
unsigned int cmd ;
2013-04-24 07:48:51 -07:00
int error = 0 , root_seen = 0 ;
2013-03-15 12:30:06 -07:00
switch ( operation ) {
case VSS_OP_FREEZE :
2013-04-24 07:48:52 -07:00
cmd = FIFREEZE ;
fs_op = " freeze " ;
2013-03-15 12:30:06 -07:00
break ;
case VSS_OP_THAW :
2013-04-24 07:48:52 -07:00
cmd = FITHAW ;
fs_op = " thaw " ;
2013-03-15 12:30:06 -07:00
break ;
2013-04-24 07:48:48 -07:00
default :
return - 1 ;
2013-03-15 12:30:06 -07:00
}
2013-04-24 07:48:51 -07:00
mounts = setmntent ( " /proc/mounts " , " r " ) ;
if ( mounts = = NULL )
2013-04-24 07:48:48 -07:00
return - 1 ;
2013-03-15 12:30:06 -07:00
2013-04-24 07:48:54 -07:00
while ( ( ent = getmntent ( mounts ) ) ) {
2013-04-24 07:48:51 -07:00
if ( strncmp ( ent - > mnt_fsname , match , strlen ( match ) ) )
2013-03-15 12:30:06 -07:00
continue ;
2013-04-24 07:48:53 -07:00
if ( strcmp ( ent - > mnt_type , " iso9660 " ) = = 0 )
continue ;
2013-04-24 07:48:51 -07:00
if ( strcmp ( ent - > mnt_dir , " / " ) = = 0 ) {
root_seen = 1 ;
continue ;
}
2013-04-24 07:48:52 -07:00
error | = vss_do_freeze ( ent - > mnt_dir , cmd , fs_op ) ;
2013-03-15 12:30:06 -07:00
}
2013-04-24 07:48:51 -07:00
endmntent ( mounts ) ;
2013-03-15 12:30:06 -07:00
2013-04-24 07:48:51 -07:00
if ( root_seen ) {
2013-04-24 07:48:52 -07:00
error | = vss_do_freeze ( " / " , cmd , fs_op ) ;
2013-04-24 07:48:51 -07:00
}
2013-03-15 12:30:06 -07:00
return error ;
}
static int netlink_send ( int fd , struct cn_msg * msg )
{
2013-08-07 15:45:12 +02:00
struct nlmsghdr nlh = { . nlmsg_type = NLMSG_DONE } ;
2013-03-15 12:30:06 -07:00
unsigned int size ;
struct msghdr message ;
struct iovec iov [ 2 ] ;
2013-08-07 15:07:21 +02:00
size = sizeof ( struct cn_msg ) + msg - > len ;
2013-03-15 12:30:06 -07:00
2013-08-07 15:45:12 +02:00
nlh . nlmsg_pid = getpid ( ) ;
nlh . nlmsg_len = NLMSG_LENGTH ( size ) ;
2013-03-15 12:30:06 -07:00
2013-08-07 15:45:12 +02:00
iov [ 0 ] . iov_base = & nlh ;
iov [ 0 ] . iov_len = sizeof ( nlh ) ;
2013-03-15 12:30:06 -07:00
iov [ 1 ] . iov_base = msg ;
iov [ 1 ] . iov_len = size ;
memset ( & message , 0 , sizeof ( message ) ) ;
message . msg_name = & addr ;
message . msg_namelen = sizeof ( addr ) ;
message . msg_iov = iov ;
message . msg_iovlen = 2 ;
return sendmsg ( fd , & message , 0 ) ;
}
int main ( void )
{
int fd , len , nl_group ;
int error ;
struct cn_msg * message ;
struct pollfd pfd ;
struct nlmsghdr * incoming_msg ;
struct cn_msg * incoming_cn_msg ;
int op ;
struct hv_vss_msg * vss_msg ;
2013-08-01 14:34:26 +02:00
char * vss_recv_buffer ;
size_t vss_recv_buffer_len ;
2013-03-15 12:30:06 -07:00
2013-04-24 07:48:48 -07:00
if ( daemon ( 1 , 0 ) )
return 1 ;
2013-03-15 12:30:06 -07:00
openlog ( " Hyper-V VSS " , 0 , LOG_USER ) ;
syslog ( LOG_INFO , " VSS starting; pid is:%d " , getpid ( ) ) ;
2013-08-06 20:55:38 +02:00
vss_recv_buffer_len = NLMSG_LENGTH ( 0 ) + sizeof ( struct cn_msg ) + sizeof ( struct hv_vss_msg ) ;
2013-08-01 14:34:26 +02:00
vss_recv_buffer = calloc ( 1 , vss_recv_buffer_len ) ;
2013-08-06 20:55:38 +02:00
if ( ! vss_recv_buffer ) {
2013-08-01 14:34:26 +02:00
syslog ( LOG_ERR , " Failed to allocate netlink buffers " ) ;
exit ( EXIT_FAILURE ) ;
}
2013-03-15 12:30:06 -07:00
fd = socket ( AF_NETLINK , SOCK_DGRAM , NETLINK_CONNECTOR ) ;
if ( fd < 0 ) {
2013-06-27 13:52:47 +02:00
syslog ( LOG_ERR , " netlink socket creation failed; error:%d %s " ,
errno , strerror ( errno ) ) ;
2013-03-15 12:30:06 -07:00
exit ( EXIT_FAILURE ) ;
}
addr . nl_family = AF_NETLINK ;
addr . nl_pad = 0 ;
addr . nl_pid = 0 ;
addr . nl_groups = 0 ;
error = bind ( fd , ( struct sockaddr * ) & addr , sizeof ( addr ) ) ;
if ( error < 0 ) {
2013-06-27 13:52:47 +02:00
syslog ( LOG_ERR , " bind failed; error:%d %s " , errno , strerror ( errno ) ) ;
2013-03-15 12:30:06 -07:00
close ( fd ) ;
exit ( EXIT_FAILURE ) ;
}
nl_group = CN_VSS_IDX ;
2013-06-27 13:52:48 +02:00
if ( setsockopt ( fd , SOL_NETLINK , NETLINK_ADD_MEMBERSHIP , & nl_group , sizeof ( nl_group ) ) < 0 ) {
syslog ( LOG_ERR , " setsockopt failed; error:%d %s " , errno , strerror ( errno ) ) ;
close ( fd ) ;
exit ( EXIT_FAILURE ) ;
}
2013-03-15 12:30:06 -07:00
/*
* Register ourselves with the kernel .
*/
2013-08-06 20:55:38 +02:00
message = ( struct cn_msg * ) vss_recv_buffer ;
2013-03-15 12:30:06 -07:00
message - > id . idx = CN_VSS_IDX ;
message - > id . val = CN_VSS_VAL ;
message - > ack = 0 ;
vss_msg = ( struct hv_vss_msg * ) message - > data ;
vss_msg - > vss_hdr . operation = VSS_OP_REGISTER ;
message - > len = sizeof ( struct hv_vss_msg ) ;
len = netlink_send ( fd , message ) ;
if ( len < 0 ) {
2013-06-27 13:52:47 +02:00
syslog ( LOG_ERR , " netlink_send failed; error:%d %s " , errno , strerror ( errno ) ) ;
2013-03-15 12:30:06 -07:00
close ( fd ) ;
exit ( EXIT_FAILURE ) ;
}
pfd . fd = fd ;
while ( 1 ) {
struct sockaddr * addr_p = ( struct sockaddr * ) & addr ;
socklen_t addr_l = sizeof ( addr ) ;
pfd . events = POLLIN ;
pfd . revents = 0 ;
2013-06-27 13:52:49 +02:00
if ( poll ( & pfd , 1 , - 1 ) < 0 ) {
syslog ( LOG_ERR , " poll failed; error:%d %s " , errno , strerror ( errno ) ) ;
if ( errno = = EINVAL ) {
close ( fd ) ;
exit ( EXIT_FAILURE ) ;
}
else
continue ;
}
2013-03-15 12:30:06 -07:00
2013-08-01 14:34:26 +02:00
len = recvfrom ( fd , vss_recv_buffer , vss_recv_buffer_len , 0 ,
2013-03-15 12:30:06 -07:00
addr_p , & addr_l ) ;
2013-04-24 07:48:49 -07:00
if ( len < 0 ) {
2013-03-15 12:30:06 -07:00
syslog ( LOG_ERR , " recvfrom failed; pid:%u error:%d %s " ,
addr . nl_pid , errno , strerror ( errno ) ) ;
close ( fd ) ;
return - 1 ;
}
2013-04-24 07:48:49 -07:00
if ( addr . nl_pid ) {
2013-04-24 07:48:50 -07:00
syslog ( LOG_WARNING ,
" Received packet from untrusted pid:%u " ,
addr . nl_pid ) ;
2013-04-24 07:48:49 -07:00
continue ;
}
2013-03-15 12:30:06 -07:00
incoming_msg = ( struct nlmsghdr * ) vss_recv_buffer ;
if ( incoming_msg - > nlmsg_type ! = NLMSG_DONE )
continue ;
incoming_cn_msg = ( struct cn_msg * ) NLMSG_DATA ( incoming_msg ) ;
vss_msg = ( struct hv_vss_msg * ) incoming_cn_msg - > data ;
op = vss_msg - > vss_hdr . operation ;
error = HV_S_OK ;
switch ( op ) {
case VSS_OP_FREEZE :
case VSS_OP_THAW :
error = vss_operate ( op ) ;
if ( error )
error = HV_E_FAIL ;
break ;
default :
syslog ( LOG_ERR , " Illegal op:%d \n " , op ) ;
}
vss_msg - > error = error ;
len = netlink_send ( fd , incoming_cn_msg ) ;
if ( len < 0 ) {
2013-06-27 13:52:47 +02:00
syslog ( LOG_ERR , " net_link send failed; error:%d %s " ,
errno , strerror ( errno ) ) ;
2013-03-15 12:30:06 -07:00
exit ( EXIT_FAILURE ) ;
}
}
}