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