File Index
Files and directories
Sign in
FILE

hv_fcopy_uio_daemon.c

File preview with syntax highlighting.

linux-6.19.14/tools/hv/hv_fcopy_uio_daemon.c C
  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
 44static const int fcopy_versions[] = {
 45	WIN8_SRV_VERSION
 46};
 47
 48#define FW_VER_COUNT		1
 49static const int fw_versions[] = {
 50	UTIL_FW_VERSION
 51};
 52
 53static 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
 73retry_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
108static unsigned char *desc;
109
110static int target_fd;
111static char target_fname[PATH_MAX];
112static unsigned long long filesize;
113
114static 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;
169done:
170	if (error)
171		target_fname[0] = '\0';
172	return error;
173}
174
175/* copy the data into the file */
176static 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
200static int hv_copy_finished(void)
201{
202	close(target_fd);
203	target_fname[0] = '\0';
204
205	return 0;
206}
207
208static 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
216static 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	 */
305fw_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
328static 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
342static 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
357static 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 */
385static 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
429static 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
450int 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	}
553close:
554	close(fcopy_fd);
555free_desc:
556	free(desc);
557exit:
558	return ret;
559}
~/ Read-only file preview.