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 error = HV_ERROR_ALREADY_EXISTS; 87 goto done; 88 } 89 } 90 91 target_fd = open(target_fname, O_RDWR | O_CREAT | O_CLOEXEC, 0744); 92 if (target_fd == -1) { 93 syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); 94 goto done; 95 } 96 97 error = 0; 98 done: 99 return error; 100 } 101 102 static int hv_copy_data(struct hv_do_fcopy *cpmsg) 103 { 104 ssize_t bytes_written; 105 106 bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size, 107 cpmsg->offset); 108 109 if (bytes_written != cpmsg->size) 110 return HV_E_FAIL; 111 112 return 0; 113 } 114 115 static int hv_copy_finished(void) 116 { 117 close(target_fd); 118 return 0; 119 } 120 static int hv_copy_cancel(void) 121 { 122 close(target_fd); 123 unlink(target_fname); 124 return 0; 125 126 } 127 128 int main(void) 129 { 130 int fd, fcopy_fd, len; 131 int error; 132 int version = FCOPY_CURRENT_VERSION; 133 char *buffer[4096 * 2]; 134 struct hv_fcopy_hdr *in_msg; 135 136 if (daemon(1, 0)) { 137 syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); 138 exit(EXIT_FAILURE); 139 } 140 141 openlog("HV_FCOPY", 0, LOG_USER); 142 syslog(LOG_INFO, "HV_FCOPY starting; pid is:%d", getpid()); 143 144 fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR); 145 146 if (fcopy_fd < 0) { 147 syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s", 148 errno, strerror(errno)); 149 exit(EXIT_FAILURE); 150 } 151 152 /* 153 * Register with the kernel. 154 */ 155 if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) { 156 syslog(LOG_ERR, "Registration failed: %s", strerror(errno)); 157 exit(EXIT_FAILURE); 158 } 159 160 while (1) { 161 /* 162 * In this loop we process fcopy messages after the 163 * handshake is complete. 164 */ 165 len = pread(fcopy_fd, buffer, (4096 * 2), 0); 166 if (len < 0) { 167 syslog(LOG_ERR, "pread failed: %s", strerror(errno)); 168 exit(EXIT_FAILURE); 169 } 170 in_msg = (struct hv_fcopy_hdr *)buffer; 171 172 switch (in_msg->operation) { 173 case START_FILE_COPY: 174 error = hv_start_fcopy((struct hv_start_fcopy *)in_msg); 175 break; 176 case WRITE_TO_FILE: 177 error = hv_copy_data((struct hv_do_fcopy *)in_msg); 178 break; 179 case COMPLETE_FCOPY: 180 error = hv_copy_finished(); 181 break; 182 case CANCEL_FCOPY: 183 error = hv_copy_cancel(); 184 break; 185 186 default: 187 syslog(LOG_ERR, "Unknown operation: %d", 188 in_msg->operation); 189 190 } 191 192 if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) { 193 syslog(LOG_ERR, "pwrite failed: %s", strerror(errno)); 194 exit(EXIT_FAILURE); 195 } 196 } 197 } 198