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