forked from Minki/linux
c2d68afba8
'error' variable is left uninitialized in case we see an unknown operation. As we don't immediately return and proceed to pwrite() we need to set it to something, HV_E_FAIL sounds good enough. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
249 lines
5.4 KiB
C
249 lines
5.4 KiB
C
/*
|
|
* An implementation of host to guest copy functionality for Linux.
|
|
*
|
|
* Copyright (C) 2014, 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <linux/hyperv.h>
|
|
#include <linux/limits.h>
|
|
#include <syslog.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
|
|
static int target_fd;
|
|
static char target_fname[PATH_MAX];
|
|
static unsigned long long filesize;
|
|
|
|
static int hv_start_fcopy(struct hv_start_fcopy *smsg)
|
|
{
|
|
int error = HV_E_FAIL;
|
|
char *q, *p;
|
|
|
|
filesize = 0;
|
|
p = (char *)smsg->path_name;
|
|
snprintf(target_fname, sizeof(target_fname), "%s/%s",
|
|
(char *)smsg->path_name, (char *)smsg->file_name);
|
|
|
|
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);
|
|
if (!(smsg->copy_flags & OVER_WRITE)) {
|
|
error = HV_ERROR_ALREADY_EXISTS;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
target_fd = open(target_fname,
|
|
O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
|
|
if (target_fd == -1) {
|
|
syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
|
|
goto done;
|
|
}
|
|
|
|
error = 0;
|
|
done:
|
|
return error;
|
|
}
|
|
|
|
static int hv_copy_data(struct hv_do_fcopy *cpmsg)
|
|
{
|
|
ssize_t bytes_written;
|
|
int ret = 0;
|
|
|
|
bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
|
|
cpmsg->offset);
|
|
|
|
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));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hv_copy_finished(void)
|
|
{
|
|
close(target_fd);
|
|
return 0;
|
|
}
|
|
static int hv_copy_cancel(void)
|
|
{
|
|
close(target_fd);
|
|
unlink(target_fname);
|
|
return 0;
|
|
|
|
}
|
|
|
|
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[])
|
|
{
|
|
int fcopy_fd;
|
|
int error;
|
|
int daemonize = 1, long_index = 0, opt;
|
|
int version = FCOPY_CURRENT_VERSION;
|
|
union {
|
|
struct hv_fcopy_hdr hdr;
|
|
struct hv_start_fcopy start;
|
|
struct hv_do_fcopy copy;
|
|
__u32 kernel_modver;
|
|
} buffer = { };
|
|
int in_handshake = 1;
|
|
|
|
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)) {
|
|
syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
openlog("HV_FCOPY", 0, LOG_USER);
|
|
syslog(LOG_INFO, "starting; pid is:%d", getpid());
|
|
|
|
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.
|
|
*/
|
|
ssize_t len;
|
|
|
|
len = pread(fcopy_fd, &buffer, sizeof(buffer), 0);
|
|
if (len < 0) {
|
|
syslog(LOG_ERR, "pread failed: %s", strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (in_handshake) {
|
|
if (len != sizeof(buffer.kernel_modver)) {
|
|
syslog(LOG_ERR, "invalid version negotiation");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
in_handshake = 0;
|
|
syslog(LOG_INFO, "kernel module version: %u",
|
|
buffer.kernel_modver);
|
|
continue;
|
|
}
|
|
|
|
switch (buffer.hdr.operation) {
|
|
case START_FILE_COPY:
|
|
error = hv_start_fcopy(&buffer.start);
|
|
break;
|
|
case WRITE_TO_FILE:
|
|
error = hv_copy_data(&buffer.copy);
|
|
break;
|
|
case COMPLETE_FCOPY:
|
|
error = hv_copy_finished();
|
|
break;
|
|
case CANCEL_FCOPY:
|
|
error = hv_copy_cancel();
|
|
break;
|
|
|
|
default:
|
|
error = HV_E_FAIL;
|
|
syslog(LOG_ERR, "Unknown operation: %d",
|
|
buffer.hdr.operation);
|
|
|
|
}
|
|
|
|
if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
|
|
syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|