2019-05-29 07:17:54 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2014-02-16 11:34:30 -08:00
/*
* An implementation of host to guest copy functionality for Linux .
*
* Copyright ( C ) 2014 , Microsoft , Inc .
*
* Author : K . Y . Srinivasan < kys @ microsoft . com >
*/
# include <sys/types.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
2018-03-04 22:17:15 -07:00
# include <string.h>
2014-02-16 11:34:30 -08:00
# include <errno.h>
# include <linux/hyperv.h>
2018-03-04 22:17:14 -07:00
# include <linux/limits.h>
2014-02-16 11:34:30 -08:00
# include <syslog.h>
# include <sys/stat.h>
# include <fcntl.h>
2014-10-22 18:07:11 +02:00
# include <getopt.h>
2014-02-16 11:34:30 -08:00
static int target_fd ;
2018-03-04 22:17:14 -07:00
static char target_fname [ PATH_MAX ] ;
2015-12-14 16:01:34 -08:00
static unsigned long long filesize ;
2014-02-16 11:34:30 -08:00
static int hv_start_fcopy ( struct hv_start_fcopy * smsg )
{
int error = HV_E_FAIL ;
char * q , * p ;
2015-12-14 16:01:34 -08:00
filesize = 0 ;
2014-02-16 11:34:30 -08:00
p = ( char * ) smsg - > path_name ;
snprintf ( target_fname , sizeof ( target_fname ) , " %s/%s " ,
2015-01-09 22:18:54 -08:00
( char * ) smsg - > path_name , ( char * ) smsg - > file_name ) ;
2014-02-16 11:34:30 -08:00
syslog ( LOG_INFO , " Target file name: %s " , target_fname ) ;
/*
* Check to see if the path is already in place ; if not ,
* create if required .
*/
while ( ( q = strchr ( p , ' / ' ) ) ! = NULL ) {
if ( q = = p ) {
p + + ;
continue ;
}
* q = ' \0 ' ;
if ( access ( ( char * ) smsg - > path_name , F_OK ) ) {
if ( smsg - > copy_flags & CREATE_PATH ) {
if ( mkdir ( ( char * ) smsg - > path_name , 0755 ) ) {
syslog ( LOG_ERR , " Failed to create %s " ,
( char * ) smsg - > path_name ) ;
goto done ;
}
} else {
syslog ( LOG_ERR , " Invalid path: %s " ,
( char * ) smsg - > path_name ) ;
goto done ;
}
}
p = q + 1 ;
* q = ' / ' ;
}
if ( ! access ( target_fname , F_OK ) ) {
syslog ( LOG_INFO , " File: %s exists " , target_fname ) ;
2014-04-09 17:24:16 -07:00
if ( ! ( smsg - > copy_flags & OVER_WRITE ) ) {
error = HV_ERROR_ALREADY_EXISTS ;
2014-02-16 11:34:30 -08:00
goto done ;
2014-04-09 17:24:16 -07:00
}
2014-02-16 11:34:30 -08:00
}
2014-06-27 18:19:48 -07:00
target_fd = open ( target_fname ,
O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC , 0744 ) ;
2014-02-16 11:34:30 -08:00
if ( target_fd = = - 1 ) {
syslog ( LOG_INFO , " Open Failed: %s " , strerror ( errno ) ) ;
goto done ;
}
error = 0 ;
done :
2020-01-25 21:49:41 -08:00
if ( error )
target_fname [ 0 ] = ' \0 ' ;
2014-02-16 11:34:30 -08:00
return error ;
}
static int hv_copy_data ( struct hv_do_fcopy * cpmsg )
{
ssize_t bytes_written ;
2015-12-14 16:01:34 -08:00
int ret = 0 ;
2014-02-16 11:34:30 -08:00
bytes_written = pwrite ( target_fd , cpmsg - > data , cpmsg - > size ,
cpmsg - > offset ) ;
2015-12-14 16:01:34 -08:00
filesize + = cpmsg - > size ;
if ( bytes_written ! = cpmsg - > size ) {
switch ( errno ) {
case ENOSPC :
ret = HV_ERROR_DISK_FULL ;
break ;
default :
ret = HV_E_FAIL ;
break ;
}
syslog ( LOG_ERR , " pwrite failed to write %llu bytes: %ld (%s) " ,
filesize , ( long ) bytes_written , strerror ( errno ) ) ;
}
2014-02-16 11:34:30 -08:00
2015-12-14 16:01:34 -08:00
return ret ;
2014-02-16 11:34:30 -08:00
}
2020-01-25 21:49:41 -08:00
/*
* Reset target_fname to " " in the two below functions for hibernation : if
* the fcopy operation is aborted by hibernation , the daemon should remove the
* partially - copied file ; to achieve this , the hv_utils driver always fakes a
* CANCEL_FCOPY message upon suspend , and later when the VM resumes back ,
* the daemon calls hv_copy_cancel ( ) to remove the file ; if a file is copied
* successfully before suspend , hv_copy_finished ( ) must reset target_fname to
* avoid that the file can be incorrectly removed upon resume , since the faked
* CANCEL_FCOPY message is spurious in this case .
*/
2014-02-16 11:34:30 -08:00
static int hv_copy_finished ( void )
{
close ( target_fd ) ;
2020-01-25 21:49:41 -08:00
target_fname [ 0 ] = ' \0 ' ;
2014-02-16 11:34:30 -08:00
return 0 ;
}
static int hv_copy_cancel ( void )
{
close ( target_fd ) ;
2020-01-25 21:49:41 -08:00
if ( strlen ( target_fname ) > 0 ) {
unlink ( target_fname ) ;
target_fname [ 0 ] = ' \0 ' ;
}
2014-02-16 11:34:30 -08:00
return 0 ;
}
2014-10-22 18:07:11 +02: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 [ ] )
2014-02-16 11:34:30 -08:00
{
2020-01-25 21:49:41 -08:00
int fcopy_fd = - 1 ;
2014-02-16 11:34:30 -08:00
int error ;
2014-10-22 18:07:11 +02:00
int daemonize = 1 , long_index = 0 , opt ;
2014-02-16 11:34:30 -08:00
int version = FCOPY_CURRENT_VERSION ;
2017-08-10 15:45:16 -07:00
union {
struct hv_fcopy_hdr hdr ;
struct hv_start_fcopy start ;
struct hv_do_fcopy copy ;
__u32 kernel_modver ;
} buffer = { } ;
2020-01-25 21:49:41 -08:00
int in_handshake ;
2014-02-16 11:34:30 -08:00
2014-10-22 18:07:11 +02: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 ' :
default :
print_usage ( argv ) ;
exit ( EXIT_FAILURE ) ;
}
}
if ( daemonize & & daemon ( 1 , 0 ) ) {
2014-02-16 11:34:30 -08:00
syslog ( LOG_ERR , " daemon() failed; error: %s " , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
openlog ( " HV_FCOPY " , 0 , LOG_USER ) ;
2015-12-14 16:01:35 -08:00
syslog ( LOG_INFO , " starting; pid is:%d " , getpid ( ) ) ;
2014-02-16 11:34:30 -08:00
2020-01-25 21:49:41 -08:00
reopen_fcopy_fd :
if ( fcopy_fd ! = - 1 )
close ( fcopy_fd ) ;
/* Remove any possible partially-copied file on error */
hv_copy_cancel ( ) ;
in_handshake = 1 ;
2014-02-16 11:34:30 -08:00
fcopy_fd = open ( " /dev/vmbus/hv_fcopy " , O_RDWR ) ;
if ( fcopy_fd < 0 ) {
syslog ( LOG_ERR , " open /dev/vmbus/hv_fcopy failed; error: %d %s " ,
errno , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
/*
* Register with the kernel .
*/
if ( ( write ( fcopy_fd , & version , sizeof ( int ) ) ) ! = sizeof ( int ) ) {
syslog ( LOG_ERR , " Registration failed: %s " , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
while ( 1 ) {
/*
* In this loop we process fcopy messages after the
* handshake is complete .
*/
2017-08-10 15:45:16 -07:00
ssize_t len ;
len = pread ( fcopy_fd , & buffer , sizeof ( buffer ) , 0 ) ;
2014-02-16 11:34:30 -08:00
if ( len < 0 ) {
syslog ( LOG_ERR , " pread failed: %s " , strerror ( errno ) ) ;
2020-01-25 21:49:41 -08:00
goto reopen_fcopy_fd ;
2014-02-16 11:34:30 -08:00
}
2015-04-11 18:07:58 -07:00
if ( in_handshake ) {
2017-08-10 15:45:16 -07:00
if ( len ! = sizeof ( buffer . kernel_modver ) ) {
2015-04-11 18:07:58 -07:00
syslog ( LOG_ERR , " invalid version negotiation " ) ;
exit ( EXIT_FAILURE ) ;
}
in_handshake = 0 ;
2017-08-10 15:45:16 -07:00
syslog ( LOG_INFO , " kernel module version: %u " ,
buffer . kernel_modver ) ;
2015-04-11 18:07:58 -07:00
continue ;
}
2017-08-10 15:45:16 -07:00
switch ( buffer . hdr . operation ) {
2014-02-16 11:34:30 -08:00
case START_FILE_COPY :
2017-08-10 15:45:16 -07:00
error = hv_start_fcopy ( & buffer . start ) ;
2014-02-16 11:34:30 -08:00
break ;
case WRITE_TO_FILE :
2017-08-10 15:45:16 -07:00
error = hv_copy_data ( & buffer . copy ) ;
2014-02-16 11:34:30 -08:00
break ;
case COMPLETE_FCOPY :
error = hv_copy_finished ( ) ;
break ;
case CANCEL_FCOPY :
error = hv_copy_cancel ( ) ;
break ;
default :
2018-09-17 04:14:55 +00:00
error = HV_E_FAIL ;
2014-02-16 11:34:30 -08:00
syslog ( LOG_ERR , " Unknown operation: %d " ,
2017-08-10 15:45:16 -07:00
buffer . hdr . operation ) ;
2014-02-16 11:34:30 -08:00
}
2020-01-25 21:49:41 -08:00
/*
* pwrite ( ) may return an error due to the faked CANCEL_FCOPY
* message upon hibernation . Ignore the error by resetting the
* dev file , i . e . closing and re - opening it .
*/
2014-02-16 11:34:30 -08:00
if ( pwrite ( fcopy_fd , & error , sizeof ( int ) , 0 ) ! = sizeof ( int ) ) {
syslog ( LOG_ERR , " pwrite failed: %s " , strerror ( errno ) ) ;
2020-01-25 21:49:41 -08:00
goto reopen_fcopy_fd ;
2014-02-16 11:34:30 -08:00
}
}
}