143aa3132SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 201325476SK. Y. Srinivasan /* 301325476SK. Y. Srinivasan * An implementation of host to guest copy functionality for Linux. 401325476SK. Y. Srinivasan * 501325476SK. Y. Srinivasan * Copyright (C) 2014, Microsoft, Inc. 601325476SK. Y. Srinivasan * 701325476SK. Y. Srinivasan * Author : K. Y. Srinivasan <kys@microsoft.com> 801325476SK. Y. Srinivasan */ 901325476SK. Y. Srinivasan 1001325476SK. Y. Srinivasan 1101325476SK. Y. Srinivasan #include <sys/types.h> 1201325476SK. Y. Srinivasan #include <stdio.h> 1301325476SK. Y. Srinivasan #include <stdlib.h> 1401325476SK. Y. Srinivasan #include <unistd.h> 15aba8a532SOlaf Hering #include <string.h> 1601325476SK. Y. Srinivasan #include <errno.h> 1701325476SK. Y. Srinivasan #include <linux/hyperv.h> 181330fc35SDexuan Cui #include <linux/limits.h> 1901325476SK. Y. Srinivasan #include <syslog.h> 2001325476SK. Y. Srinivasan #include <sys/stat.h> 2101325476SK. Y. Srinivasan #include <fcntl.h> 22170f4beaSVitaly Kuznetsov #include <getopt.h> 2301325476SK. Y. Srinivasan 2401325476SK. Y. Srinivasan static int target_fd; 251330fc35SDexuan Cui static char target_fname[PATH_MAX]; 26b4ed5d16SOlaf Hering static unsigned long long filesize; 2701325476SK. Y. Srinivasan 2801325476SK. Y. Srinivasan static int hv_start_fcopy(struct hv_start_fcopy *smsg) 2901325476SK. Y. Srinivasan { 3001325476SK. Y. Srinivasan int error = HV_E_FAIL; 3101325476SK. Y. Srinivasan char *q, *p; 3201325476SK. Y. Srinivasan 33b4ed5d16SOlaf Hering filesize = 0; 3401325476SK. Y. Srinivasan p = (char *)smsg->path_name; 3501325476SK. Y. Srinivasan snprintf(target_fname, sizeof(target_fname), "%s/%s", 36aba474b8SVitaly Kuznetsov (char *)smsg->path_name, (char *)smsg->file_name); 3701325476SK. Y. Srinivasan 3801325476SK. Y. Srinivasan syslog(LOG_INFO, "Target file name: %s", target_fname); 3901325476SK. Y. Srinivasan /* 4001325476SK. Y. Srinivasan * Check to see if the path is already in place; if not, 4101325476SK. Y. Srinivasan * create if required. 4201325476SK. Y. Srinivasan */ 4301325476SK. Y. Srinivasan while ((q = strchr(p, '/')) != NULL) { 4401325476SK. Y. Srinivasan if (q == p) { 4501325476SK. Y. Srinivasan p++; 4601325476SK. Y. Srinivasan continue; 4701325476SK. Y. Srinivasan } 4801325476SK. Y. Srinivasan *q = '\0'; 4901325476SK. Y. Srinivasan if (access((char *)smsg->path_name, F_OK)) { 5001325476SK. Y. Srinivasan if (smsg->copy_flags & CREATE_PATH) { 5101325476SK. Y. Srinivasan if (mkdir((char *)smsg->path_name, 0755)) { 5201325476SK. Y. Srinivasan syslog(LOG_ERR, "Failed to create %s", 5301325476SK. Y. Srinivasan (char *)smsg->path_name); 5401325476SK. Y. Srinivasan goto done; 5501325476SK. Y. Srinivasan } 5601325476SK. Y. Srinivasan } else { 5701325476SK. Y. Srinivasan syslog(LOG_ERR, "Invalid path: %s", 5801325476SK. Y. Srinivasan (char *)smsg->path_name); 5901325476SK. Y. Srinivasan goto done; 6001325476SK. Y. Srinivasan } 6101325476SK. Y. Srinivasan } 6201325476SK. Y. Srinivasan p = q + 1; 6301325476SK. Y. Srinivasan *q = '/'; 6401325476SK. Y. Srinivasan } 6501325476SK. Y. Srinivasan 6601325476SK. Y. Srinivasan if (!access(target_fname, F_OK)) { 6701325476SK. Y. Srinivasan syslog(LOG_INFO, "File: %s exists", target_fname); 68314672a2SK. Y. Srinivasan if (!(smsg->copy_flags & OVER_WRITE)) { 69314672a2SK. Y. Srinivasan error = HV_ERROR_ALREADY_EXISTS; 7001325476SK. Y. Srinivasan goto done; 7101325476SK. Y. Srinivasan } 72314672a2SK. Y. Srinivasan } 7301325476SK. Y. Srinivasan 74e013ac31SYue Zhang target_fd = open(target_fname, 75e013ac31SYue Zhang O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744); 7601325476SK. Y. Srinivasan if (target_fd == -1) { 7701325476SK. Y. Srinivasan syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); 7801325476SK. Y. Srinivasan goto done; 7901325476SK. Y. Srinivasan } 8001325476SK. Y. Srinivasan 8101325476SK. Y. Srinivasan error = 0; 8201325476SK. Y. Srinivasan done: 83*9fc3c01aSDexuan Cui if (error) 84*9fc3c01aSDexuan Cui target_fname[0] = '\0'; 8501325476SK. Y. Srinivasan return error; 8601325476SK. Y. Srinivasan } 8701325476SK. Y. Srinivasan 8801325476SK. Y. Srinivasan static int hv_copy_data(struct hv_do_fcopy *cpmsg) 8901325476SK. Y. Srinivasan { 9001325476SK. Y. Srinivasan ssize_t bytes_written; 91b4ed5d16SOlaf Hering int ret = 0; 9201325476SK. Y. Srinivasan 9301325476SK. Y. Srinivasan bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size, 9401325476SK. Y. Srinivasan cpmsg->offset); 9501325476SK. Y. Srinivasan 96b4ed5d16SOlaf Hering filesize += cpmsg->size; 97b4ed5d16SOlaf Hering if (bytes_written != cpmsg->size) { 98b4ed5d16SOlaf Hering switch (errno) { 99b4ed5d16SOlaf Hering case ENOSPC: 100b4ed5d16SOlaf Hering ret = HV_ERROR_DISK_FULL; 101b4ed5d16SOlaf Hering break; 102b4ed5d16SOlaf Hering default: 103b4ed5d16SOlaf Hering ret = HV_E_FAIL; 104b4ed5d16SOlaf Hering break; 105b4ed5d16SOlaf Hering } 106b4ed5d16SOlaf Hering syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)", 107b4ed5d16SOlaf Hering filesize, (long)bytes_written, strerror(errno)); 108b4ed5d16SOlaf Hering } 10901325476SK. Y. Srinivasan 110b4ed5d16SOlaf Hering return ret; 11101325476SK. Y. Srinivasan } 11201325476SK. Y. Srinivasan 113*9fc3c01aSDexuan Cui /* 114*9fc3c01aSDexuan Cui * Reset target_fname to "" in the two below functions for hibernation: if 115*9fc3c01aSDexuan Cui * the fcopy operation is aborted by hibernation, the daemon should remove the 116*9fc3c01aSDexuan Cui * partially-copied file; to achieve this, the hv_utils driver always fakes a 117*9fc3c01aSDexuan Cui * CANCEL_FCOPY message upon suspend, and later when the VM resumes back, 118*9fc3c01aSDexuan Cui * the daemon calls hv_copy_cancel() to remove the file; if a file is copied 119*9fc3c01aSDexuan Cui * successfully before suspend, hv_copy_finished() must reset target_fname to 120*9fc3c01aSDexuan Cui * avoid that the file can be incorrectly removed upon resume, since the faked 121*9fc3c01aSDexuan Cui * CANCEL_FCOPY message is spurious in this case. 122*9fc3c01aSDexuan Cui */ 12301325476SK. Y. Srinivasan static int hv_copy_finished(void) 12401325476SK. Y. Srinivasan { 12501325476SK. Y. Srinivasan close(target_fd); 126*9fc3c01aSDexuan Cui target_fname[0] = '\0'; 12701325476SK. Y. Srinivasan return 0; 12801325476SK. Y. Srinivasan } 12901325476SK. Y. Srinivasan static int hv_copy_cancel(void) 13001325476SK. Y. Srinivasan { 13101325476SK. Y. Srinivasan close(target_fd); 132*9fc3c01aSDexuan Cui if (strlen(target_fname) > 0) { 13301325476SK. Y. Srinivasan unlink(target_fname); 134*9fc3c01aSDexuan Cui target_fname[0] = '\0'; 135*9fc3c01aSDexuan Cui } 13601325476SK. Y. Srinivasan return 0; 13701325476SK. Y. Srinivasan 13801325476SK. Y. Srinivasan } 13901325476SK. Y. Srinivasan 140170f4beaSVitaly Kuznetsov void print_usage(char *argv[]) 141170f4beaSVitaly Kuznetsov { 142170f4beaSVitaly Kuznetsov fprintf(stderr, "Usage: %s [options]\n" 143170f4beaSVitaly Kuznetsov "Options are:\n" 144170f4beaSVitaly Kuznetsov " -n, --no-daemon stay in foreground, don't daemonize\n" 145170f4beaSVitaly Kuznetsov " -h, --help print this help\n", argv[0]); 146170f4beaSVitaly Kuznetsov } 147170f4beaSVitaly Kuznetsov 148170f4beaSVitaly Kuznetsov int main(int argc, char *argv[]) 14901325476SK. Y. Srinivasan { 150*9fc3c01aSDexuan Cui int fcopy_fd = -1; 15101325476SK. Y. Srinivasan int error; 152170f4beaSVitaly Kuznetsov int daemonize = 1, long_index = 0, opt; 15301325476SK. Y. Srinivasan int version = FCOPY_CURRENT_VERSION; 1543f2baa8aSOlaf Hering union { 1553f2baa8aSOlaf Hering struct hv_fcopy_hdr hdr; 1563f2baa8aSOlaf Hering struct hv_start_fcopy start; 1573f2baa8aSOlaf Hering struct hv_do_fcopy copy; 158a4d1ee5bSVitaly Kuznetsov __u32 kernel_modver; 1593f2baa8aSOlaf Hering } buffer = { }; 160*9fc3c01aSDexuan Cui int in_handshake; 16101325476SK. Y. Srinivasan 162170f4beaSVitaly Kuznetsov static struct option long_options[] = { 163170f4beaSVitaly Kuznetsov {"help", no_argument, 0, 'h' }, 164170f4beaSVitaly Kuznetsov {"no-daemon", no_argument, 0, 'n' }, 165170f4beaSVitaly Kuznetsov {0, 0, 0, 0 } 166170f4beaSVitaly Kuznetsov }; 167170f4beaSVitaly Kuznetsov 168170f4beaSVitaly Kuznetsov while ((opt = getopt_long(argc, argv, "hn", long_options, 169170f4beaSVitaly Kuznetsov &long_index)) != -1) { 170170f4beaSVitaly Kuznetsov switch (opt) { 171170f4beaSVitaly Kuznetsov case 'n': 172170f4beaSVitaly Kuznetsov daemonize = 0; 173170f4beaSVitaly Kuznetsov break; 174170f4beaSVitaly Kuznetsov case 'h': 175170f4beaSVitaly Kuznetsov default: 176170f4beaSVitaly Kuznetsov print_usage(argv); 177170f4beaSVitaly Kuznetsov exit(EXIT_FAILURE); 178170f4beaSVitaly Kuznetsov } 179170f4beaSVitaly Kuznetsov } 180170f4beaSVitaly Kuznetsov 181170f4beaSVitaly Kuznetsov if (daemonize && daemon(1, 0)) { 18201325476SK. Y. Srinivasan syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); 18301325476SK. Y. Srinivasan exit(EXIT_FAILURE); 18401325476SK. Y. Srinivasan } 18501325476SK. Y. Srinivasan 18601325476SK. Y. Srinivasan openlog("HV_FCOPY", 0, LOG_USER); 1876dfb867cSOlaf Hering syslog(LOG_INFO, "starting; pid is:%d", getpid()); 18801325476SK. Y. Srinivasan 189*9fc3c01aSDexuan Cui reopen_fcopy_fd: 190*9fc3c01aSDexuan Cui if (fcopy_fd != -1) 191*9fc3c01aSDexuan Cui close(fcopy_fd); 192*9fc3c01aSDexuan Cui /* Remove any possible partially-copied file on error */ 193*9fc3c01aSDexuan Cui hv_copy_cancel(); 194*9fc3c01aSDexuan Cui in_handshake = 1; 19501325476SK. Y. Srinivasan fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR); 19601325476SK. Y. Srinivasan 19701325476SK. Y. Srinivasan if (fcopy_fd < 0) { 19801325476SK. Y. Srinivasan syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s", 19901325476SK. Y. Srinivasan errno, strerror(errno)); 20001325476SK. Y. Srinivasan exit(EXIT_FAILURE); 20101325476SK. Y. Srinivasan } 20201325476SK. Y. Srinivasan 20301325476SK. Y. Srinivasan /* 20401325476SK. Y. Srinivasan * Register with the kernel. 20501325476SK. Y. Srinivasan */ 20601325476SK. Y. Srinivasan if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) { 20701325476SK. Y. Srinivasan syslog(LOG_ERR, "Registration failed: %s", strerror(errno)); 20801325476SK. Y. Srinivasan exit(EXIT_FAILURE); 20901325476SK. Y. Srinivasan } 21001325476SK. Y. Srinivasan 21101325476SK. Y. Srinivasan while (1) { 21201325476SK. Y. Srinivasan /* 21301325476SK. Y. Srinivasan * In this loop we process fcopy messages after the 21401325476SK. Y. Srinivasan * handshake is complete. 21501325476SK. Y. Srinivasan */ 2163f2baa8aSOlaf Hering ssize_t len; 2173f2baa8aSOlaf Hering 2183f2baa8aSOlaf Hering len = pread(fcopy_fd, &buffer, sizeof(buffer), 0); 21901325476SK. Y. Srinivasan if (len < 0) { 22001325476SK. Y. Srinivasan syslog(LOG_ERR, "pread failed: %s", strerror(errno)); 221*9fc3c01aSDexuan Cui goto reopen_fcopy_fd; 22201325476SK. Y. Srinivasan } 223a4d1ee5bSVitaly Kuznetsov 224a4d1ee5bSVitaly Kuznetsov if (in_handshake) { 2253f2baa8aSOlaf Hering if (len != sizeof(buffer.kernel_modver)) { 226a4d1ee5bSVitaly Kuznetsov syslog(LOG_ERR, "invalid version negotiation"); 227a4d1ee5bSVitaly Kuznetsov exit(EXIT_FAILURE); 228a4d1ee5bSVitaly Kuznetsov } 229a4d1ee5bSVitaly Kuznetsov in_handshake = 0; 2303f2baa8aSOlaf Hering syslog(LOG_INFO, "kernel module version: %u", 2313f2baa8aSOlaf Hering buffer.kernel_modver); 232a4d1ee5bSVitaly Kuznetsov continue; 233a4d1ee5bSVitaly Kuznetsov } 234a4d1ee5bSVitaly Kuznetsov 2353f2baa8aSOlaf Hering switch (buffer.hdr.operation) { 23601325476SK. Y. Srinivasan case START_FILE_COPY: 2373f2baa8aSOlaf Hering error = hv_start_fcopy(&buffer.start); 23801325476SK. Y. Srinivasan break; 23901325476SK. Y. Srinivasan case WRITE_TO_FILE: 2403f2baa8aSOlaf Hering error = hv_copy_data(&buffer.copy); 24101325476SK. Y. Srinivasan break; 24201325476SK. Y. Srinivasan case COMPLETE_FCOPY: 24301325476SK. Y. Srinivasan error = hv_copy_finished(); 24401325476SK. Y. Srinivasan break; 24501325476SK. Y. Srinivasan case CANCEL_FCOPY: 24601325476SK. Y. Srinivasan error = hv_copy_cancel(); 24701325476SK. Y. Srinivasan break; 24801325476SK. Y. Srinivasan 24901325476SK. Y. Srinivasan default: 250c2d68afbSVitaly Kuznetsov error = HV_E_FAIL; 25101325476SK. Y. Srinivasan syslog(LOG_ERR, "Unknown operation: %d", 2523f2baa8aSOlaf Hering buffer.hdr.operation); 25301325476SK. Y. Srinivasan 25401325476SK. Y. Srinivasan } 25501325476SK. Y. Srinivasan 256*9fc3c01aSDexuan Cui /* 257*9fc3c01aSDexuan Cui * pwrite() may return an error due to the faked CANCEL_FCOPY 258*9fc3c01aSDexuan Cui * message upon hibernation. Ignore the error by resetting the 259*9fc3c01aSDexuan Cui * dev file, i.e. closing and re-opening it. 260*9fc3c01aSDexuan Cui */ 26101325476SK. Y. Srinivasan if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) { 26201325476SK. Y. Srinivasan syslog(LOG_ERR, "pwrite failed: %s", strerror(errno)); 263*9fc3c01aSDexuan Cui goto reopen_fcopy_fd; 26401325476SK. Y. Srinivasan } 26501325476SK. Y. Srinivasan } 26601325476SK. Y. Srinivasan } 267