18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * An implementation of host to guest copy functionality for Linux. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014, Microsoft, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author : K. Y. Srinivasan <kys@microsoft.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <sys/types.h> 128c2ecf20Sopenharmony_ci#include <stdio.h> 138c2ecf20Sopenharmony_ci#include <stdlib.h> 148c2ecf20Sopenharmony_ci#include <unistd.h> 158c2ecf20Sopenharmony_ci#include <string.h> 168c2ecf20Sopenharmony_ci#include <errno.h> 178c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 188c2ecf20Sopenharmony_ci#include <linux/limits.h> 198c2ecf20Sopenharmony_ci#include <syslog.h> 208c2ecf20Sopenharmony_ci#include <sys/stat.h> 218c2ecf20Sopenharmony_ci#include <fcntl.h> 228c2ecf20Sopenharmony_ci#include <getopt.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int target_fd; 258c2ecf20Sopenharmony_cistatic char target_fname[PATH_MAX]; 268c2ecf20Sopenharmony_cistatic unsigned long long filesize; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int hv_start_fcopy(struct hv_start_fcopy *smsg) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci int error = HV_E_FAIL; 318c2ecf20Sopenharmony_ci char *q, *p; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci filesize = 0; 348c2ecf20Sopenharmony_ci p = (char *)smsg->path_name; 358c2ecf20Sopenharmony_ci snprintf(target_fname, sizeof(target_fname), "%s/%s", 368c2ecf20Sopenharmony_ci (char *)smsg->path_name, (char *)smsg->file_name); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci syslog(LOG_INFO, "Target file name: %s", target_fname); 398c2ecf20Sopenharmony_ci /* 408c2ecf20Sopenharmony_ci * Check to see if the path is already in place; if not, 418c2ecf20Sopenharmony_ci * create if required. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci while ((q = strchr(p, '/')) != NULL) { 448c2ecf20Sopenharmony_ci if (q == p) { 458c2ecf20Sopenharmony_ci p++; 468c2ecf20Sopenharmony_ci continue; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci *q = '\0'; 498c2ecf20Sopenharmony_ci if (access((char *)smsg->path_name, F_OK)) { 508c2ecf20Sopenharmony_ci if (smsg->copy_flags & CREATE_PATH) { 518c2ecf20Sopenharmony_ci if (mkdir((char *)smsg->path_name, 0755)) { 528c2ecf20Sopenharmony_ci syslog(LOG_ERR, "Failed to create %s", 538c2ecf20Sopenharmony_ci (char *)smsg->path_name); 548c2ecf20Sopenharmony_ci goto done; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci } else { 578c2ecf20Sopenharmony_ci syslog(LOG_ERR, "Invalid path: %s", 588c2ecf20Sopenharmony_ci (char *)smsg->path_name); 598c2ecf20Sopenharmony_ci goto done; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci p = q + 1; 638c2ecf20Sopenharmony_ci *q = '/'; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (!access(target_fname, F_OK)) { 678c2ecf20Sopenharmony_ci syslog(LOG_INFO, "File: %s exists", target_fname); 688c2ecf20Sopenharmony_ci if (!(smsg->copy_flags & OVER_WRITE)) { 698c2ecf20Sopenharmony_ci error = HV_ERROR_ALREADY_EXISTS; 708c2ecf20Sopenharmony_ci goto done; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci target_fd = open(target_fname, 758c2ecf20Sopenharmony_ci O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744); 768c2ecf20Sopenharmony_ci if (target_fd == -1) { 778c2ecf20Sopenharmony_ci syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); 788c2ecf20Sopenharmony_ci goto done; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci error = 0; 828c2ecf20Sopenharmony_cidone: 838c2ecf20Sopenharmony_ci if (error) 848c2ecf20Sopenharmony_ci target_fname[0] = '\0'; 858c2ecf20Sopenharmony_ci return error; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int hv_copy_data(struct hv_do_fcopy *cpmsg) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci ssize_t bytes_written; 918c2ecf20Sopenharmony_ci int ret = 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size, 948c2ecf20Sopenharmony_ci cpmsg->offset); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci filesize += cpmsg->size; 978c2ecf20Sopenharmony_ci if (bytes_written != cpmsg->size) { 988c2ecf20Sopenharmony_ci switch (errno) { 998c2ecf20Sopenharmony_ci case ENOSPC: 1008c2ecf20Sopenharmony_ci ret = HV_ERROR_DISK_FULL; 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci default: 1038c2ecf20Sopenharmony_ci ret = HV_E_FAIL; 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)", 1078c2ecf20Sopenharmony_ci filesize, (long)bytes_written, strerror(errno)); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * Reset target_fname to "" in the two below functions for hibernation: if 1158c2ecf20Sopenharmony_ci * the fcopy operation is aborted by hibernation, the daemon should remove the 1168c2ecf20Sopenharmony_ci * partially-copied file; to achieve this, the hv_utils driver always fakes a 1178c2ecf20Sopenharmony_ci * CANCEL_FCOPY message upon suspend, and later when the VM resumes back, 1188c2ecf20Sopenharmony_ci * the daemon calls hv_copy_cancel() to remove the file; if a file is copied 1198c2ecf20Sopenharmony_ci * successfully before suspend, hv_copy_finished() must reset target_fname to 1208c2ecf20Sopenharmony_ci * avoid that the file can be incorrectly removed upon resume, since the faked 1218c2ecf20Sopenharmony_ci * CANCEL_FCOPY message is spurious in this case. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_cistatic int hv_copy_finished(void) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci close(target_fd); 1268c2ecf20Sopenharmony_ci target_fname[0] = '\0'; 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_cistatic int hv_copy_cancel(void) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci close(target_fd); 1328c2ecf20Sopenharmony_ci if (strlen(target_fname) > 0) { 1338c2ecf20Sopenharmony_ci unlink(target_fname); 1348c2ecf20Sopenharmony_ci target_fname[0] = '\0'; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_civoid print_usage(char *argv[]) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci fprintf(stderr, "Usage: %s [options]\n" 1438c2ecf20Sopenharmony_ci "Options are:\n" 1448c2ecf20Sopenharmony_ci " -n, --no-daemon stay in foreground, don't daemonize\n" 1458c2ecf20Sopenharmony_ci " -h, --help print this help\n", argv[0]); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ciint main(int argc, char *argv[]) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int fcopy_fd = -1; 1518c2ecf20Sopenharmony_ci int error; 1528c2ecf20Sopenharmony_ci int daemonize = 1, long_index = 0, opt; 1538c2ecf20Sopenharmony_ci int version = FCOPY_CURRENT_VERSION; 1548c2ecf20Sopenharmony_ci union { 1558c2ecf20Sopenharmony_ci struct hv_fcopy_hdr hdr; 1568c2ecf20Sopenharmony_ci struct hv_start_fcopy start; 1578c2ecf20Sopenharmony_ci struct hv_do_fcopy copy; 1588c2ecf20Sopenharmony_ci __u32 kernel_modver; 1598c2ecf20Sopenharmony_ci } buffer = { }; 1608c2ecf20Sopenharmony_ci int in_handshake; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci static struct option long_options[] = { 1638c2ecf20Sopenharmony_ci {"help", no_argument, 0, 'h' }, 1648c2ecf20Sopenharmony_ci {"no-daemon", no_argument, 0, 'n' }, 1658c2ecf20Sopenharmony_ci {0, 0, 0, 0 } 1668c2ecf20Sopenharmony_ci }; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci while ((opt = getopt_long(argc, argv, "hn", long_options, 1698c2ecf20Sopenharmony_ci &long_index)) != -1) { 1708c2ecf20Sopenharmony_ci switch (opt) { 1718c2ecf20Sopenharmony_ci case 'n': 1728c2ecf20Sopenharmony_ci daemonize = 0; 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci case 'h': 1758c2ecf20Sopenharmony_ci default: 1768c2ecf20Sopenharmony_ci print_usage(argv); 1778c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (daemonize && daemon(1, 0)) { 1828c2ecf20Sopenharmony_ci syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); 1838c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci openlog("HV_FCOPY", 0, LOG_USER); 1878c2ecf20Sopenharmony_ci syslog(LOG_INFO, "starting; pid is:%d", getpid()); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cireopen_fcopy_fd: 1908c2ecf20Sopenharmony_ci if (fcopy_fd != -1) 1918c2ecf20Sopenharmony_ci close(fcopy_fd); 1928c2ecf20Sopenharmony_ci /* Remove any possible partially-copied file on error */ 1938c2ecf20Sopenharmony_ci hv_copy_cancel(); 1948c2ecf20Sopenharmony_ci in_handshake = 1; 1958c2ecf20Sopenharmony_ci fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (fcopy_fd < 0) { 1988c2ecf20Sopenharmony_ci syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s", 1998c2ecf20Sopenharmony_ci errno, strerror(errno)); 2008c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * Register with the kernel. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) { 2078c2ecf20Sopenharmony_ci syslog(LOG_ERR, "Registration failed: %s", strerror(errno)); 2088c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci while (1) { 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * In this loop we process fcopy messages after the 2148c2ecf20Sopenharmony_ci * handshake is complete. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci ssize_t len; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci len = pread(fcopy_fd, &buffer, sizeof(buffer), 0); 2198c2ecf20Sopenharmony_ci if (len < 0) { 2208c2ecf20Sopenharmony_ci syslog(LOG_ERR, "pread failed: %s", strerror(errno)); 2218c2ecf20Sopenharmony_ci goto reopen_fcopy_fd; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (in_handshake) { 2258c2ecf20Sopenharmony_ci if (len != sizeof(buffer.kernel_modver)) { 2268c2ecf20Sopenharmony_ci syslog(LOG_ERR, "invalid version negotiation"); 2278c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci in_handshake = 0; 2308c2ecf20Sopenharmony_ci syslog(LOG_INFO, "kernel module version: %u", 2318c2ecf20Sopenharmony_ci buffer.kernel_modver); 2328c2ecf20Sopenharmony_ci continue; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci switch (buffer.hdr.operation) { 2368c2ecf20Sopenharmony_ci case START_FILE_COPY: 2378c2ecf20Sopenharmony_ci error = hv_start_fcopy(&buffer.start); 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci case WRITE_TO_FILE: 2408c2ecf20Sopenharmony_ci error = hv_copy_data(&buffer.copy); 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case COMPLETE_FCOPY: 2438c2ecf20Sopenharmony_ci error = hv_copy_finished(); 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci case CANCEL_FCOPY: 2468c2ecf20Sopenharmony_ci error = hv_copy_cancel(); 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci default: 2508c2ecf20Sopenharmony_ci error = HV_E_FAIL; 2518c2ecf20Sopenharmony_ci syslog(LOG_ERR, "Unknown operation: %d", 2528c2ecf20Sopenharmony_ci buffer.hdr.operation); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * pwrite() may return an error due to the faked CANCEL_FCOPY 2588c2ecf20Sopenharmony_ci * message upon hibernation. Ignore the error by resetting the 2598c2ecf20Sopenharmony_ci * dev file, i.e. closing and re-opening it. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) { 2628c2ecf20Sopenharmony_ci syslog(LOG_ERR, "pwrite failed: %s", strerror(errno)); 2638c2ecf20Sopenharmony_ci goto reopen_fcopy_fd; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci} 267