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 if (!file_name || !path_name) { 300 free(file_name); 301 free(path_name); 302 syslog(LOG_ERR, "Can't allocate memory for file name and/or path name"); 303 return HV_E_FAIL; 304 } 305 306 wcstoutf8(file_name, (__u16 *)in_file_name, file_size); 307 wcstoutf8(path_name, (__u16 *)in_path_name, path_size); 308 309 return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags); 310 } 311 312 static int hv_fcopy_send_data(struct hv_fcopy_hdr *fcopy_msg, int recvlen) 313 { 314 int operation = fcopy_msg->operation; 315 316 /* 317 * The strings sent from the host are encoded in 318 * utf16; convert it to utf8 strings. 319 * The host assures us that the utf16 strings will not exceed 320 * the max lengths specified. We will however, reserve room 321 * for the string terminating character - in the utf16s_utf8s() 322 * function we limit the size of the buffer where the converted 323 * string is placed to W_MAX_PATH -1 to guarantee 324 * that the strings can be properly terminated! 325 */ 326 327 switch (operation) { 328 case START_FILE_COPY: 329 return hv_fcopy_start((struct hv_start_fcopy *)fcopy_msg); 330 case WRITE_TO_FILE: 331 return hv_copy_data((struct hv_do_fcopy *)fcopy_msg); 332 case COMPLETE_FCOPY: 333 return hv_copy_finished(); 334 } 335 336 return HV_E_FAIL; 337 } 338 339 /* process the packet recv from host */ 340 static int fcopy_pkt_process(struct vmbus_br *txbr) 341 { 342 int ret, offset, pktlen; 343 int fcopy_srv_version; 344 const struct vmbus_chanpkt_hdr *pkt; 345 struct hv_fcopy_hdr *fcopy_msg; 346 struct icmsg_hdr *icmsghdr; 347 348 pkt = (const struct vmbus_chanpkt_hdr *)desc; 349 offset = pkt->hlen << 3; 350 pktlen = (pkt->tlen << 3) - offset; 351 icmsghdr = (struct icmsg_hdr *)&desc[offset + sizeof(struct vmbuspipe_hdr)]; 352 icmsghdr->status = HV_E_FAIL; 353 354 if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { 355 if (vmbus_prep_negotiate_resp(icmsghdr, desc + offset, pktlen, fw_versions, 356 FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT, 357 NULL, &fcopy_srv_version)) { 358 syslog(LOG_INFO, "FCopy IC version %d.%d", 359 fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF); 360 icmsghdr->status = 0; 361 } 362 } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) { 363 /* Ensure recvlen is big enough to contain hv_fcopy_hdr */ 364 if (pktlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) { 365 syslog(LOG_ERR, "Invalid Fcopy hdr. Packet length too small: %u", 366 pktlen); 367 return -ENOBUFS; 368 } 369 370 fcopy_msg = (struct hv_fcopy_hdr *)&desc[offset + ICMSG_HDR]; 371 icmsghdr->status = hv_fcopy_send_data(fcopy_msg, pktlen); 372 } 373 374 icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 375 ret = rte_vmbus_chan_send(txbr, 0x6, desc + offset, pktlen, 0); 376 if (ret) { 377 syslog(LOG_ERR, "Write to ringbuffer failed err: %d", ret); 378 return ret; 379 } 380 381 return 0; 382 } 383 384 static void fcopy_get_first_folder(char *path, char *chan_no) 385 { 386 DIR *dir = opendir(path); 387 struct dirent *entry; 388 389 if (!dir) { 390 syslog(LOG_ERR, "Failed to open directory (errno=%s).\n", strerror(errno)); 391 return; 392 } 393 394 while ((entry = readdir(dir)) != NULL) { 395 if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && 396 strcmp(entry->d_name, "..") != 0) { 397 strcpy(chan_no, entry->d_name); 398 break; 399 } 400 } 401 402 closedir(dir); 403 } 404 405 int main(int argc, char *argv[]) 406 { 407 int fcopy_fd = -1, tmp = 1; 408 int daemonize = 1, long_index = 0, opt, ret = -EINVAL; 409 struct vmbus_br txbr, rxbr; 410 void *ring; 411 uint32_t len = HV_RING_SIZE; 412 char uio_name[MAX_FOLDER_NAME] = {0}; 413 char uio_dev_path[MAX_PATH_LEN] = {0}; 414 415 static struct option long_options[] = { 416 {"help", no_argument, 0, 'h' }, 417 {"no-daemon", no_argument, 0, 'n' }, 418 {0, 0, 0, 0 } 419 }; 420 421 while ((opt = getopt_long(argc, argv, "hn", long_options, 422 &long_index)) != -1) { 423 switch (opt) { 424 case 'n': 425 daemonize = 0; 426 break; 427 case 'h': 428 default: 429 print_usage(argv); 430 goto exit; 431 } 432 } 433 434 if (daemonize && daemon(1, 0)) { 435 syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); 436 goto exit; 437 } 438 439 openlog("HV_UIO_FCOPY", 0, LOG_USER); 440 syslog(LOG_INFO, "starting; pid is:%d", getpid()); 441 442 fcopy_get_first_folder(FCOPY_UIO, uio_name); 443 snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name); 444 fcopy_fd = open(uio_dev_path, O_RDWR); 445 446 if (fcopy_fd < 0) { 447 syslog(LOG_ERR, "open %s failed; error: %d %s", 448 uio_dev_path, errno, strerror(errno)); 449 ret = fcopy_fd; 450 goto exit; 451 } 452 453 ring = vmbus_uio_map(&fcopy_fd, HV_RING_SIZE); 454 if (!ring) { 455 ret = errno; 456 syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret)); 457 goto close; 458 } 459 vmbus_br_setup(&txbr, ring, HV_RING_SIZE); 460 vmbus_br_setup(&rxbr, (char *)ring + HV_RING_SIZE, HV_RING_SIZE); 461 462 rxbr.vbr->imask = 0; 463 464 while (1) { 465 /* 466 * In this loop we process fcopy messages after the 467 * handshake is complete. 468 */ 469 ret = pread(fcopy_fd, &tmp, sizeof(int), 0); 470 if (ret < 0) { 471 syslog(LOG_ERR, "pread failed: %s", strerror(errno)); 472 continue; 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.