1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * An implementation of host to guest copy functionality for Linux. 4 * 5 * Copyright (C) 2023, Microsoft, Inc. 6 * 7 * Author : K. Y. Srinivasan <kys@microsoft.com> 8 * Author : Saurabh Sengar <ssengar@microsoft.com> 9 * 10 */ 11 12 #include <dirent.h> 13 #include <errno.h> 14 #include <fcntl.h> 15 #include <getopt.h> 16 #include <locale.h> 17 #include <stdbool.h> 18 #include <stddef.h> 19 #include <stdint.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <syslog.h> 24 #include <unistd.h> 25 #include <wchar.h> 26 #include <sys/stat.h> 27 #include <linux/hyperv.h> 28 #include <linux/limits.h> 29 #include "vmbus_bufring.h" 30 31 #define ICMSGTYPE_NEGOTIATE 0 32 #define ICMSGTYPE_FCOPY 7 33 34 #define WIN8_SRV_MAJOR 1 35 #define WIN8_SRV_MINOR 1 36 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) 37 38 #define MAX_FOLDER_NAME 15 39 #define MAX_PATH_LEN 15 40 #define FCOPY_UIO "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/uio" 41 42 #define FCOPY_VER_COUNT 1 43 static const int fcopy_versions[] = { 44 WIN8_SRV_VERSION 45 }; 46 47 #define FW_VER_COUNT 1 48 static const int fw_versions[] = { 49 UTIL_FW_VERSION 50 }; 51 52 #define HV_RING_SIZE 0x4000 /* 16KB ring buffer size */ 53 54 unsigned char desc[HV_RING_SIZE]; 55 56 static int target_fd; 57 static char target_fname[PATH_MAX]; 58 static unsigned long long filesize; 59 60 static int hv_fcopy_create_file(char *file_name, char *path_name, __u32 flags) 61 { 62 int error = HV_E_FAIL; 63 char *q, *p; 64 65 filesize = 0; 66 p = path_name; 67 snprintf(target_fname, sizeof(target_fname), "%s/%s", 68 path_name, file_name); 69 70 /* 71 * Check to see if the path is already in place; if not, 72 * create if required. 73 */ 74 while ((q = strchr(p, '/')) != NULL) { 75 if (q == p) { 76 p++; 77 continue; 78 } 79 *q = '\0'; 80 if (access(path_name, F_OK)) { 81 if (flags & CREATE_PATH) { 82 if (mkdir(path_name, 0755)) { 83 syslog(LOG_ERR, "Failed to create %s", 84 path_name); 85 goto done; 86 } 87 } else { 88 syslog(LOG_ERR, "Invalid path: %s", path_name); 89 goto done; 90 } 91 } 92 p = q + 1; 93 *q = '/'; 94 } 95 96 if (!access(target_fname, F_OK)) { 97 syslog(LOG_INFO, "File: %s exists", target_fname); 98 if (!(flags & OVER_WRITE)) { 99 error = HV_ERROR_ALREADY_EXISTS; 100 goto done; 101 } 102 } 103 104 target_fd = open(target_fname, 105 O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744); 106 if (target_fd == -1) { 107 syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); 108 goto done; 109 } 110 111 error = 0; 112 done: 113 if (error) 114 target_fname[0] = '\0'; 115 return error; 116 } 117 118 /* copy the data into the file */ 119 static int hv_copy_data(struct hv_do_fcopy *cpmsg) 120 { 121 ssize_t len; 122 int ret = 0; 123 124 len = pwrite(target_fd, cpmsg->data, cpmsg->size, cpmsg->offset); 125 126 filesize += cpmsg->size; 127 if (len != cpmsg->size) { 128 switch (errno) { 129 case ENOSPC: 130 ret = HV_ERROR_DISK_FULL; 131 break; 132 default: 133 ret = HV_E_FAIL; 134 break; 135 } 136 syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)", 137 filesize, (long)len, strerror(errno)); 138 } 139 140 return ret; 141 } 142 143 static int hv_copy_finished(void) 144 { 145 close(target_fd); 146 target_fname[0] = '\0'; 147 148 return 0; 149 } 150 151 static void print_usage(char *argv[]) 152 { 153 fprintf(stderr, "Usage: %s [options]\n" 154 "Options are:\n" 155 " -n, --no-daemon stay in foreground, don't daemonize\n" 156 " -h, --help print this help\n", argv[0]); 157 } 158 159 static bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, unsigned char *buf, 160 unsigned int buflen, const int *fw_version, int fw_vercnt, 161 const int *srv_version, int srv_vercnt, 162 int *nego_fw_version, int *nego_srv_version) 163 { 164 int icframe_major, icframe_minor; 165 int icmsg_major, icmsg_minor; 166 int fw_major, fw_minor; 167 int srv_major, srv_minor; 168 int i, j; 169 bool found_match = false; 170 struct icmsg_negotiate *negop; 171 172 /* Check that there's enough space for icframe_vercnt, icmsg_vercnt */ 173 if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) { 174 syslog(LOG_ERR, "Invalid icmsg negotiate"); 175 return false; 176 } 177 178 icmsghdrp->icmsgsize = 0x10; 179 negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR]; 180 181 icframe_major = negop->icframe_vercnt; 182 icframe_minor = 0; 183 184 icmsg_major = negop->icmsg_vercnt; 185 icmsg_minor = 0; 186 187 /* Validate negop packet */ 188 if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT || 189 icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT || 190 ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) { 191 syslog(LOG_ERR, "Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n", 192 icframe_major, icmsg_major); 193 goto fw_error; 194 } 195 196 /* 197 * Select the framework version number we will 198 * support. 199 */ 200 201 for (i = 0; i < fw_vercnt; i++) { 202 fw_major = (fw_version[i] >> 16); 203 fw_minor = (fw_version[i] & 0xFFFF); 204 205 for (j = 0; j < negop->icframe_vercnt; j++) { 206 if (negop->icversion_data[j].major == fw_major && 207 negop->icversion_data[j].minor == fw_minor) { 208 icframe_major = negop->icversion_data[j].major; 209 icframe_minor = negop->icversion_data[j].minor; 210 found_match = true; 211 break; 212 } 213 } 214 215 if (found_match) 216 break; 217 } 218 219 if (!found_match) 220 goto fw_error; 221 222 found_match = false; 223 224 for (i = 0; i < srv_vercnt; i++) { 225 srv_major = (srv_version[i] >> 16); 226 srv_minor = (srv_version[i] & 0xFFFF); 227 228 for (j = negop->icframe_vercnt; 229 (j < negop->icframe_vercnt + negop->icmsg_vercnt); 230 j++) { 231 if (negop->icversion_data[j].major == srv_major && 232 negop->icversion_data[j].minor == srv_minor) { 233 icmsg_major = negop->icversion_data[j].major; 234 icmsg_minor = negop->icversion_data[j].minor; 235 found_match = true; 236 break; 237 } 238 } 239 240 if (found_match) 241 break; 242 } 243 244 /* 245 * Respond with the framework and service 246 * version numbers we can support. 247 */ 248 fw_error: 249 if (!found_match) { 250 negop->icframe_vercnt = 0; 251 negop->icmsg_vercnt = 0; 252 } else { 253 negop->icframe_vercnt = 1; 254 negop->icmsg_vercnt = 1; 255 } 256 257 if (nego_fw_version) 258 *nego_fw_version = (icframe_major << 16) | icframe_minor; 259 260 if (nego_srv_version) 261 *nego_srv_version = (icmsg_major << 16) | icmsg_minor; 262 263 negop->icversion_data[0].major = icframe_major; 264 negop->icversion_data[0].minor = icframe_minor; 265 negop->icversion_data[1].major = icmsg_major; 266 negop->icversion_data[1].minor = icmsg_minor; 267 268 return found_match; 269 } 270 271 static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size) 272 { 273 size_t len = 0; 274 275 while (len < dest_size) { 276 if (src[len] < 0x80) 277 dest[len++] = (char)(*src++); 278 else 279 dest[len++] = 'X'; 280 } 281 282 dest[len] = '\0'; 283 } 284 285 static int hv_fcopy_start(struct hv_start_fcopy *smsg_in) 286 { 287 setlocale(LC_ALL, "en_US.utf8"); 288 size_t file_size, path_size; 289 char *file_name, *path_name; 290 char *in_file_name = (char *)smsg_in->file_name; 291 char *in_path_name = (char *)smsg_in->path_name; 292 293 file_size = wcstombs(NULL, (const wchar_t *restrict)in_file_name, 0) + 1; 294 path_size = wcstombs(NULL, (const wchar_t *restrict)in_path_name, 0) + 1; 295 296 file_name = (char *)malloc(file_size * sizeof(char)); 297 path_name = (char *)malloc(path_size * sizeof(char)); 298 299 wcstoutf8(file_name, (__u16 *)in_file_name, file_size); 300 wcstoutf8(path_name, (__u16 *)in_path_name, path_size); 301 302 return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags); 303 } 304 305 static int hv_fcopy_send_data(struct hv_fcopy_hdr *fcopy_msg, int recvlen) 306 { 307 int operation = fcopy_msg->operation; 308 309 /* 310 * The strings sent from the host are encoded in 311 * utf16; convert it to utf8 strings. 312 * The host assures us that the utf16 strings will not exceed 313 * the max lengths specified. We will however, reserve room 314 * for the string terminating character - in the utf16s_utf8s() 315 * function we limit the size of the buffer where the converted 316 * string is placed to W_MAX_PATH -1 to guarantee 317 * that the strings can be properly terminated! 318 */ 319 320 switch (operation) { 321 case START_FILE_COPY: 322 return hv_fcopy_start((struct hv_start_fcopy *)fcopy_msg); 323 case WRITE_TO_FILE: 324 return hv_copy_data((struct hv_do_fcopy *)fcopy_msg); 325 case COMPLETE_FCOPY: 326 return hv_copy_finished(); 327 } 328 329 return HV_E_FAIL; 330 } 331 332 /* process the packet recv from host */ 333 static int fcopy_pkt_process(struct vmbus_br *txbr) 334 { 335 int ret, offset, pktlen; 336 int fcopy_srv_version; 337 const struct vmbus_chanpkt_hdr *pkt; 338 struct hv_fcopy_hdr *fcopy_msg; 339 struct icmsg_hdr *icmsghdr; 340 341 pkt = (const struct vmbus_chanpkt_hdr *)desc; 342 offset = pkt->hlen << 3; 343 pktlen = (pkt->tlen << 3) - offset; 344 icmsghdr = (struct icmsg_hdr *)&desc[offset + sizeof(struct vmbuspipe_hdr)]; 345 icmsghdr->status = HV_E_FAIL; 346 347 if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { 348 if (vmbus_prep_negotiate_resp(icmsghdr, desc + offset, pktlen, fw_versions, 349 FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT, 350 NULL, &fcopy_srv_version)) { 351 syslog(LOG_INFO, "FCopy IC version %d.%d", 352 fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF); 353 icmsghdr->status = 0; 354 } 355 } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) { 356 /* Ensure recvlen is big enough to contain hv_fcopy_hdr */ 357 if (pktlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) { 358 syslog(LOG_ERR, "Invalid Fcopy hdr. Packet length too small: %u", 359 pktlen); 360 return -ENOBUFS; 361 } 362 363 fcopy_msg = (struct hv_fcopy_hdr *)&desc[offset + ICMSG_HDR]; 364 icmsghdr->status = hv_fcopy_send_data(fcopy_msg, pktlen); 365 } 366 367 icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 368 ret = rte_vmbus_chan_send(txbr, 0x6, desc + offset, pktlen, 0); 369 if (ret) { 370 syslog(LOG_ERR, "Write to ringbuffer failed err: %d", ret); 371 return ret; 372 } 373 374 return 0; 375 } 376 377 static void fcopy_get_first_folder(char *path, char *chan_no) 378 { 379 DIR *dir = opendir(path); 380 struct dirent *entry; 381 382 if (!dir) { 383 syslog(LOG_ERR, "Failed to open directory (errno=%s).\n", strerror(errno)); 384 return; 385 } 386 387 while ((entry = readdir(dir)) != NULL) { 388 if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && 389 strcmp(entry->d_name, "..") != 0) { 390 strcpy(chan_no, entry->d_name); 391 break; 392 } 393 } 394 395 closedir(dir); 396 } 397 398 int main(int argc, char *argv[]) 399 { 400 int fcopy_fd = -1, tmp = 1; 401 int daemonize = 1, long_index = 0, opt, ret = -EINVAL; 402 struct vmbus_br txbr, rxbr; 403 void *ring; 404 uint32_t len = HV_RING_SIZE; 405 char uio_name[MAX_FOLDER_NAME] = {0}; 406 char uio_dev_path[MAX_PATH_LEN] = {0}; 407 408 static struct option long_options[] = { 409 {"help", no_argument, 0, 'h' }, 410 {"no-daemon", no_argument, 0, 'n' }, 411 {0, 0, 0, 0 } 412 }; 413 414 while ((opt = getopt_long(argc, argv, "hn", long_options, 415 &long_index)) != -1) { 416 switch (opt) { 417 case 'n': 418 daemonize = 0; 419 break; 420 case 'h': 421 default: 422 print_usage(argv); 423 goto exit; 424 } 425 } 426 427 if (daemonize && daemon(1, 0)) { 428 syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); 429 goto exit; 430 } 431 432 openlog("HV_UIO_FCOPY", 0, LOG_USER); 433 syslog(LOG_INFO, "starting; pid is:%d", getpid()); 434 435 fcopy_get_first_folder(FCOPY_UIO, uio_name); 436 snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name); 437 fcopy_fd = open(uio_dev_path, O_RDWR); 438 439 if (fcopy_fd < 0) { 440 syslog(LOG_ERR, "open %s failed; error: %d %s", 441 uio_dev_path, errno, strerror(errno)); 442 ret = fcopy_fd; 443 goto exit; 444 } 445 446 ring = vmbus_uio_map(&fcopy_fd, HV_RING_SIZE); 447 if (!ring) { 448 ret = errno; 449 syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret)); 450 goto close; 451 } 452 vmbus_br_setup(&txbr, ring, HV_RING_SIZE); 453 vmbus_br_setup(&rxbr, (char *)ring + HV_RING_SIZE, HV_RING_SIZE); 454 455 rxbr.vbr->imask = 0; 456 457 while (1) { 458 /* 459 * In this loop we process fcopy messages after the 460 * handshake is complete. 461 */ 462 ret = pread(fcopy_fd, &tmp, sizeof(int), 0); 463 if (ret < 0) { 464 syslog(LOG_ERR, "pread failed: %s", strerror(errno)); 465 continue; 466 } 467 468 len = HV_RING_SIZE; 469 ret = rte_vmbus_chan_recv_raw(&rxbr, desc, &len); 470 if (unlikely(ret <= 0)) { 471 /* This indicates a failure to communicate (or worse) */ 472 syslog(LOG_ERR, "VMBus channel recv error: %d", ret); 473 } else { 474 ret = fcopy_pkt_process(&txbr); 475 if (ret < 0) 476 goto close; 477 478 /* Signal host */ 479 if ((write(fcopy_fd, &tmp, sizeof(int))) != sizeof(int)) { 480 ret = errno; 481 syslog(LOG_ERR, "Signal to host failed: %s\n", strerror(ret)); 482 goto close; 483 } 484 } 485 } 486 close: 487 close(fcopy_fd); 488 exit: 489 return ret; 490 } 491