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