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 <sys/socket.h> 22 #include <sys/poll.h> 23 #include <linux/types.h> 24 #include <linux/kdev_t.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <unistd.h> 28 #include <string.h> 29 #include <ctype.h> 30 #include <errno.h> 31 #include <linux/hyperv.h> 32 #include <syslog.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <dirent.h> 36 37 static int target_fd; 38 static char target_fname[W_MAX_PATH]; 39 40 static int hv_start_fcopy(struct hv_start_fcopy *smsg) 41 { 42 int error = HV_E_FAIL; 43 char *q, *p; 44 45 /* 46 * If possile append a path seperator to the path. 47 */ 48 if (strlen((char *)smsg->path_name) < (W_MAX_PATH - 2)) 49 strcat((char *)smsg->path_name, "/"); 50 51 p = (char *)smsg->path_name; 52 snprintf(target_fname, sizeof(target_fname), "%s/%s", 53 (char *)smsg->path_name, smsg->file_name); 54 55 syslog(LOG_INFO, "Target file name: %s", target_fname); 56 /* 57 * Check to see if the path is already in place; if not, 58 * create if required. 59 */ 60 while ((q = strchr(p, '/')) != NULL) { 61 if (q == p) { 62 p++; 63 continue; 64 } 65 *q = '\0'; 66 if (access((char *)smsg->path_name, F_OK)) { 67 if (smsg->copy_flags & CREATE_PATH) { 68 if (mkdir((char *)smsg->path_name, 0755)) { 69 syslog(LOG_ERR, "Failed to create %s", 70 (char *)smsg->path_name); 71 goto done; 72 } 73 } else { 74 syslog(LOG_ERR, "Invalid path: %s", 75 (char *)smsg->path_name); 76 goto done; 77 } 78 } 79 p = q + 1; 80 *q = '/'; 81 } 82 83 if (!access(target_fname, F_OK)) { 84 syslog(LOG_INFO, "File: %s exists", target_fname); 85 if (!smsg->copy_flags & OVER_WRITE) 86 goto done; 87 } 88 89 target_fd = open(target_fname, O_RDWR | O_CREAT | O_CLOEXEC, 0744); 90 if (target_fd == -1) { 91 syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); 92 goto done; 93 } 94 95 error = 0; 96 done: 97 return error; 98 } 99 100 static int hv_copy_data(struct hv_do_fcopy *cpmsg) 101 { 102 ssize_t bytes_written; 103 104 bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size, 105 cpmsg->offset); 106 107 if (bytes_written != cpmsg->size) 108 return HV_E_FAIL; 109 110 return 0; 111 } 112 113 static int hv_copy_finished(void) 114 { 115 close(target_fd); 116 return 0; 117 } 118 static int hv_copy_cancel(void) 119 { 120 close(target_fd); 121 unlink(target_fname); 122 return 0; 123 124 } 125 126 int main(void) 127 { 128 int fd, fcopy_fd, len; 129 int error; 130 int version = FCOPY_CURRENT_VERSION; 131 char *buffer[4096 * 2]; 132 struct hv_fcopy_hdr *in_msg; 133 134 if (daemon(1, 0)) { 135 syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); 136 exit(EXIT_FAILURE); 137 } 138 139 openlog("HV_FCOPY", 0, LOG_USER); 140 syslog(LOG_INFO, "HV_FCOPY starting; pid is:%d", getpid()); 141 142 fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR); 143 144 if (fcopy_fd < 0) { 145 syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s", 146 errno, strerror(errno)); 147 exit(EXIT_FAILURE); 148 } 149 150 /* 151 * Register with the kernel. 152 */ 153 if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) { 154 syslog(LOG_ERR, "Registration failed: %s", strerror(errno)); 155 exit(EXIT_FAILURE); 156 } 157 158 while (1) { 159 /* 160 * In this loop we process fcopy messages after the 161 * handshake is complete. 162 */ 163 len = pread(fcopy_fd, buffer, (4096 * 2), 0); 164 if (len < 0) { 165 syslog(LOG_ERR, "pread failed: %s", strerror(errno)); 166 exit(EXIT_FAILURE); 167 } 168 in_msg = (struct hv_fcopy_hdr *)buffer; 169 170 switch (in_msg->operation) { 171 case START_FILE_COPY: 172 error = hv_start_fcopy((struct hv_start_fcopy *)in_msg); 173 break; 174 case WRITE_TO_FILE: 175 error = hv_copy_data((struct hv_do_fcopy *)in_msg); 176 break; 177 case COMPLETE_FCOPY: 178 error = hv_copy_finished(); 179 break; 180 case CANCEL_FCOPY: 181 error = hv_copy_cancel(); 182 break; 183 184 default: 185 syslog(LOG_ERR, "Unknown operation: %d", 186 in_msg->operation); 187 188 } 189 190 if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) { 191 syslog(LOG_ERR, "pwrite failed: %s", strerror(errno)); 192 exit(EXIT_FAILURE); 193 } 194 } 195 } 196