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