1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * An implementation of host to guest copy functionality for Linux. 4 * 5 * Copyright (C) 2014, Microsoft, Inc. 6 * 7 * Author : K. Y. Srinivasan <kys@microsoft.com> 8 */ 9 10 11 #include <sys/types.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <unistd.h> 15 #include <string.h> 16 #include <errno.h> 17 #include <linux/hyperv.h> 18 #include <linux/limits.h> 19 #include <syslog.h> 20 #include <sys/stat.h> 21 #include <fcntl.h> 22 #include <getopt.h> 23 24 static int target_fd; 25 static char target_fname[PATH_MAX]; 26 static unsigned long long filesize; 27 28 static int hv_start_fcopy(struct hv_start_fcopy *smsg) 29 { 30 int error = HV_E_FAIL; 31 char *q, *p; 32 33 filesize = 0; 34 p = (char *)smsg->path_name; 35 snprintf(target_fname, sizeof(target_fname), "%s/%s", 36 (char *)smsg->path_name, (char *)smsg->file_name); 37 38 syslog(LOG_INFO, "Target file name: %s", target_fname); 39 /* 40 * Check to see if the path is already in place; if not, 41 * create if required. 42 */ 43 while ((q = strchr(p, '/')) != NULL) { 44 if (q == p) { 45 p++; 46 continue; 47 } 48 *q = '\0'; 49 if (access((char *)smsg->path_name, F_OK)) { 50 if (smsg->copy_flags & CREATE_PATH) { 51 if (mkdir((char *)smsg->path_name, 0755)) { 52 syslog(LOG_ERR, "Failed to create %s", 53 (char *)smsg->path_name); 54 goto done; 55 } 56 } else { 57 syslog(LOG_ERR, "Invalid path: %s", 58 (char *)smsg->path_name); 59 goto done; 60 } 61 } 62 p = q + 1; 63 *q = '/'; 64 } 65 66 if (!access(target_fname, F_OK)) { 67 syslog(LOG_INFO, "File: %s exists", target_fname); 68 if (!(smsg->copy_flags & OVER_WRITE)) { 69 error = HV_ERROR_ALREADY_EXISTS; 70 goto done; 71 } 72 } 73 74 target_fd = open(target_fname, 75 O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744); 76 if (target_fd == -1) { 77 syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); 78 goto done; 79 } 80 81 error = 0; 82 done: 83 return error; 84 } 85 86 static int hv_copy_data(struct hv_do_fcopy *cpmsg) 87 { 88 ssize_t bytes_written; 89 int ret = 0; 90 91 bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size, 92 cpmsg->offset); 93 94 filesize += cpmsg->size; 95 if (bytes_written != cpmsg->size) { 96 switch (errno) { 97 case ENOSPC: 98 ret = HV_ERROR_DISK_FULL; 99 break; 100 default: 101 ret = HV_E_FAIL; 102 break; 103 } 104 syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)", 105 filesize, (long)bytes_written, strerror(errno)); 106 } 107 108 return ret; 109 } 110 111 static int hv_copy_finished(void) 112 { 113 close(target_fd); 114 return 0; 115 } 116 static int hv_copy_cancel(void) 117 { 118 close(target_fd); 119 unlink(target_fname); 120 return 0; 121 122 } 123 124 void print_usage(char *argv[]) 125 { 126 fprintf(stderr, "Usage: %s [options]\n" 127 "Options are:\n" 128 " -n, --no-daemon stay in foreground, don't daemonize\n" 129 " -h, --help print this help\n", argv[0]); 130 } 131 132 int main(int argc, char *argv[]) 133 { 134 int fcopy_fd; 135 int error; 136 int daemonize = 1, long_index = 0, opt; 137 int version = FCOPY_CURRENT_VERSION; 138 union { 139 struct hv_fcopy_hdr hdr; 140 struct hv_start_fcopy start; 141 struct hv_do_fcopy copy; 142 __u32 kernel_modver; 143 } buffer = { }; 144 int in_handshake = 1; 145 146 static struct option long_options[] = { 147 {"help", no_argument, 0, 'h' }, 148 {"no-daemon", no_argument, 0, 'n' }, 149 {0, 0, 0, 0 } 150 }; 151 152 while ((opt = getopt_long(argc, argv, "hn", long_options, 153 &long_index)) != -1) { 154 switch (opt) { 155 case 'n': 156 daemonize = 0; 157 break; 158 case 'h': 159 default: 160 print_usage(argv); 161 exit(EXIT_FAILURE); 162 } 163 } 164 165 if (daemonize && daemon(1, 0)) { 166 syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); 167 exit(EXIT_FAILURE); 168 } 169 170 openlog("HV_FCOPY", 0, LOG_USER); 171 syslog(LOG_INFO, "starting; pid is:%d", getpid()); 172 173 fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR); 174 175 if (fcopy_fd < 0) { 176 syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s", 177 errno, strerror(errno)); 178 exit(EXIT_FAILURE); 179 } 180 181 /* 182 * Register with the kernel. 183 */ 184 if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) { 185 syslog(LOG_ERR, "Registration failed: %s", strerror(errno)); 186 exit(EXIT_FAILURE); 187 } 188 189 while (1) { 190 /* 191 * In this loop we process fcopy messages after the 192 * handshake is complete. 193 */ 194 ssize_t len; 195 196 len = pread(fcopy_fd, &buffer, sizeof(buffer), 0); 197 if (len < 0) { 198 syslog(LOG_ERR, "pread failed: %s", strerror(errno)); 199 exit(EXIT_FAILURE); 200 } 201 202 if (in_handshake) { 203 if (len != sizeof(buffer.kernel_modver)) { 204 syslog(LOG_ERR, "invalid version negotiation"); 205 exit(EXIT_FAILURE); 206 } 207 in_handshake = 0; 208 syslog(LOG_INFO, "kernel module version: %u", 209 buffer.kernel_modver); 210 continue; 211 } 212 213 switch (buffer.hdr.operation) { 214 case START_FILE_COPY: 215 error = hv_start_fcopy(&buffer.start); 216 break; 217 case WRITE_TO_FILE: 218 error = hv_copy_data(&buffer.copy); 219 break; 220 case COMPLETE_FCOPY: 221 error = hv_copy_finished(); 222 break; 223 case CANCEL_FCOPY: 224 error = hv_copy_cancel(); 225 break; 226 227 default: 228 error = HV_E_FAIL; 229 syslog(LOG_ERR, "Unknown operation: %d", 230 buffer.hdr.operation); 231 232 } 233 234 if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) { 235 syslog(LOG_ERR, "pwrite failed: %s", strerror(errno)); 236 exit(EXIT_FAILURE); 237 } 238 } 239 } 240