linux/tools/hv/hv_fcopy_daemon.c
Thomas Gleixner 43aa31327b treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 280
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>
2019-06-05 17:36:36 +02:00

240 lines
5.0 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);
}
}
}