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