mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
![Thomas Gleixner](/assets/img/avatar_default.png)
Based on 1 normalized pattern(s): 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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 9 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190529141900.459653302@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
239 lines
5 KiB
C
239 lines
5 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* 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>
|
|
#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);
|
|
}
|
|
}
|
|
}
|