xref: /openbmc/linux/tools/hv/hv_fcopy_daemon.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
143aa3132SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
201325476SK. Y. Srinivasan /*
301325476SK. Y. Srinivasan  * An implementation of host to guest copy functionality for Linux.
401325476SK. Y. Srinivasan  *
501325476SK. Y. Srinivasan  * Copyright (C) 2014, Microsoft, Inc.
601325476SK. Y. Srinivasan  *
701325476SK. Y. Srinivasan  * Author : K. Y. Srinivasan <kys@microsoft.com>
801325476SK. Y. Srinivasan  */
901325476SK. Y. Srinivasan 
1001325476SK. Y. Srinivasan 
1101325476SK. Y. Srinivasan #include <sys/types.h>
1201325476SK. Y. Srinivasan #include <stdio.h>
1301325476SK. Y. Srinivasan #include <stdlib.h>
1401325476SK. Y. Srinivasan #include <unistd.h>
15aba8a532SOlaf Hering #include <string.h>
1601325476SK. Y. Srinivasan #include <errno.h>
1701325476SK. Y. Srinivasan #include <linux/hyperv.h>
181330fc35SDexuan Cui #include <linux/limits.h>
1901325476SK. Y. Srinivasan #include <syslog.h>
2001325476SK. Y. Srinivasan #include <sys/stat.h>
2101325476SK. Y. Srinivasan #include <fcntl.h>
22170f4beaSVitaly Kuznetsov #include <getopt.h>
2301325476SK. Y. Srinivasan 
2401325476SK. Y. Srinivasan static int target_fd;
251330fc35SDexuan Cui static char target_fname[PATH_MAX];
26b4ed5d16SOlaf Hering static unsigned long long filesize;
2701325476SK. Y. Srinivasan 
hv_start_fcopy(struct hv_start_fcopy * smsg)2801325476SK. Y. Srinivasan static int hv_start_fcopy(struct hv_start_fcopy *smsg)
2901325476SK. Y. Srinivasan {
3001325476SK. Y. Srinivasan 	int error = HV_E_FAIL;
3101325476SK. Y. Srinivasan 	char *q, *p;
3201325476SK. Y. Srinivasan 
33b4ed5d16SOlaf Hering 	filesize = 0;
3401325476SK. Y. Srinivasan 	p = (char *)smsg->path_name;
3501325476SK. Y. Srinivasan 	snprintf(target_fname, sizeof(target_fname), "%s/%s",
36aba474b8SVitaly Kuznetsov 		 (char *)smsg->path_name, (char *)smsg->file_name);
3701325476SK. Y. Srinivasan 
3801325476SK. Y. Srinivasan 	syslog(LOG_INFO, "Target file name: %s", target_fname);
3901325476SK. Y. Srinivasan 	/*
4001325476SK. Y. Srinivasan 	 * Check to see if the path is already in place; if not,
4101325476SK. Y. Srinivasan 	 * create if required.
4201325476SK. Y. Srinivasan 	 */
4301325476SK. Y. Srinivasan 	while ((q = strchr(p, '/')) != NULL) {
4401325476SK. Y. Srinivasan 		if (q == p) {
4501325476SK. Y. Srinivasan 			p++;
4601325476SK. Y. Srinivasan 			continue;
4701325476SK. Y. Srinivasan 		}
4801325476SK. Y. Srinivasan 		*q = '\0';
4901325476SK. Y. Srinivasan 		if (access((char *)smsg->path_name, F_OK)) {
5001325476SK. Y. Srinivasan 			if (smsg->copy_flags & CREATE_PATH) {
5101325476SK. Y. Srinivasan 				if (mkdir((char *)smsg->path_name, 0755)) {
5201325476SK. Y. Srinivasan 					syslog(LOG_ERR, "Failed to create %s",
5301325476SK. Y. Srinivasan 						(char *)smsg->path_name);
5401325476SK. Y. Srinivasan 					goto done;
5501325476SK. Y. Srinivasan 				}
5601325476SK. Y. Srinivasan 			} else {
5701325476SK. Y. Srinivasan 				syslog(LOG_ERR, "Invalid path: %s",
5801325476SK. Y. Srinivasan 					(char *)smsg->path_name);
5901325476SK. Y. Srinivasan 				goto done;
6001325476SK. Y. Srinivasan 			}
6101325476SK. Y. Srinivasan 		}
6201325476SK. Y. Srinivasan 		p = q + 1;
6301325476SK. Y. Srinivasan 		*q = '/';
6401325476SK. Y. Srinivasan 	}
6501325476SK. Y. Srinivasan 
6601325476SK. Y. Srinivasan 	if (!access(target_fname, F_OK)) {
6701325476SK. Y. Srinivasan 		syslog(LOG_INFO, "File: %s exists", target_fname);
68314672a2SK. Y. Srinivasan 		if (!(smsg->copy_flags & OVER_WRITE)) {
69314672a2SK. Y. Srinivasan 			error = HV_ERROR_ALREADY_EXISTS;
7001325476SK. Y. Srinivasan 			goto done;
7101325476SK. Y. Srinivasan 		}
72314672a2SK. Y. Srinivasan 	}
7301325476SK. Y. Srinivasan 
74e013ac31SYue Zhang 	target_fd = open(target_fname,
75e013ac31SYue Zhang 			 O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
7601325476SK. Y. Srinivasan 	if (target_fd == -1) {
7701325476SK. Y. Srinivasan 		syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
7801325476SK. Y. Srinivasan 		goto done;
7901325476SK. Y. Srinivasan 	}
8001325476SK. Y. Srinivasan 
8101325476SK. Y. Srinivasan 	error = 0;
8201325476SK. Y. Srinivasan done:
83*9fc3c01aSDexuan Cui 	if (error)
84*9fc3c01aSDexuan Cui 		target_fname[0] = '\0';
8501325476SK. Y. Srinivasan 	return error;
8601325476SK. Y. Srinivasan }
8701325476SK. Y. Srinivasan 
hv_copy_data(struct hv_do_fcopy * cpmsg)8801325476SK. Y. Srinivasan static int hv_copy_data(struct hv_do_fcopy *cpmsg)
8901325476SK. Y. Srinivasan {
9001325476SK. Y. Srinivasan 	ssize_t bytes_written;
91b4ed5d16SOlaf Hering 	int ret = 0;
9201325476SK. Y. Srinivasan 
9301325476SK. Y. Srinivasan 	bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
9401325476SK. Y. Srinivasan 				cpmsg->offset);
9501325476SK. Y. Srinivasan 
96b4ed5d16SOlaf Hering 	filesize += cpmsg->size;
97b4ed5d16SOlaf Hering 	if (bytes_written != cpmsg->size) {
98b4ed5d16SOlaf Hering 		switch (errno) {
99b4ed5d16SOlaf Hering 		case ENOSPC:
100b4ed5d16SOlaf Hering 			ret = HV_ERROR_DISK_FULL;
101b4ed5d16SOlaf Hering 			break;
102b4ed5d16SOlaf Hering 		default:
103b4ed5d16SOlaf Hering 			ret = HV_E_FAIL;
104b4ed5d16SOlaf Hering 			break;
105b4ed5d16SOlaf Hering 		}
106b4ed5d16SOlaf Hering 		syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
107b4ed5d16SOlaf Hering 		       filesize, (long)bytes_written, strerror(errno));
108b4ed5d16SOlaf Hering 	}
10901325476SK. Y. Srinivasan 
110b4ed5d16SOlaf Hering 	return ret;
11101325476SK. Y. Srinivasan }
11201325476SK. Y. Srinivasan 
113*9fc3c01aSDexuan Cui /*
114*9fc3c01aSDexuan Cui  * Reset target_fname to "" in the two below functions for hibernation: if
115*9fc3c01aSDexuan Cui  * the fcopy operation is aborted by hibernation, the daemon should remove the
116*9fc3c01aSDexuan Cui  * partially-copied file; to achieve this, the hv_utils driver always fakes a
117*9fc3c01aSDexuan Cui  * CANCEL_FCOPY message upon suspend, and later when the VM resumes back,
118*9fc3c01aSDexuan Cui  * the daemon calls hv_copy_cancel() to remove the file; if a file is copied
119*9fc3c01aSDexuan Cui  * successfully before suspend, hv_copy_finished() must reset target_fname to
120*9fc3c01aSDexuan Cui  * avoid that the file can be incorrectly removed upon resume, since the faked
121*9fc3c01aSDexuan Cui  * CANCEL_FCOPY message is spurious in this case.
122*9fc3c01aSDexuan Cui  */
hv_copy_finished(void)12301325476SK. Y. Srinivasan static int hv_copy_finished(void)
12401325476SK. Y. Srinivasan {
12501325476SK. Y. Srinivasan 	close(target_fd);
126*9fc3c01aSDexuan Cui 	target_fname[0] = '\0';
12701325476SK. Y. Srinivasan 	return 0;
12801325476SK. Y. Srinivasan }
hv_copy_cancel(void)12901325476SK. Y. Srinivasan static int hv_copy_cancel(void)
13001325476SK. Y. Srinivasan {
13101325476SK. Y. Srinivasan 	close(target_fd);
132*9fc3c01aSDexuan Cui 	if (strlen(target_fname) > 0) {
13301325476SK. Y. Srinivasan 		unlink(target_fname);
134*9fc3c01aSDexuan Cui 		target_fname[0] = '\0';
135*9fc3c01aSDexuan Cui 	}
13601325476SK. Y. Srinivasan 	return 0;
13701325476SK. Y. Srinivasan 
13801325476SK. Y. Srinivasan }
13901325476SK. Y. Srinivasan 
print_usage(char * argv[])140170f4beaSVitaly Kuznetsov void print_usage(char *argv[])
141170f4beaSVitaly Kuznetsov {
142170f4beaSVitaly Kuznetsov 	fprintf(stderr, "Usage: %s [options]\n"
143170f4beaSVitaly Kuznetsov 		"Options are:\n"
144170f4beaSVitaly Kuznetsov 		"  -n, --no-daemon        stay in foreground, don't daemonize\n"
145170f4beaSVitaly Kuznetsov 		"  -h, --help             print this help\n", argv[0]);
146170f4beaSVitaly Kuznetsov }
147170f4beaSVitaly Kuznetsov 
main(int argc,char * argv[])148170f4beaSVitaly Kuznetsov int main(int argc, char *argv[])
14901325476SK. Y. Srinivasan {
150*9fc3c01aSDexuan Cui 	int fcopy_fd = -1;
15101325476SK. Y. Srinivasan 	int error;
152170f4beaSVitaly Kuznetsov 	int daemonize = 1, long_index = 0, opt;
15301325476SK. Y. Srinivasan 	int version = FCOPY_CURRENT_VERSION;
1543f2baa8aSOlaf Hering 	union {
1553f2baa8aSOlaf Hering 		struct hv_fcopy_hdr hdr;
1563f2baa8aSOlaf Hering 		struct hv_start_fcopy start;
1573f2baa8aSOlaf Hering 		struct hv_do_fcopy copy;
158a4d1ee5bSVitaly Kuznetsov 		__u32 kernel_modver;
1593f2baa8aSOlaf Hering 	} buffer = { };
160*9fc3c01aSDexuan Cui 	int in_handshake;
16101325476SK. Y. Srinivasan 
162170f4beaSVitaly Kuznetsov 	static struct option long_options[] = {
163170f4beaSVitaly Kuznetsov 		{"help",	no_argument,	   0,  'h' },
164170f4beaSVitaly Kuznetsov 		{"no-daemon",	no_argument,	   0,  'n' },
165170f4beaSVitaly Kuznetsov 		{0,		0,		   0,  0   }
166170f4beaSVitaly Kuznetsov 	};
167170f4beaSVitaly Kuznetsov 
168170f4beaSVitaly Kuznetsov 	while ((opt = getopt_long(argc, argv, "hn", long_options,
169170f4beaSVitaly Kuznetsov 				  &long_index)) != -1) {
170170f4beaSVitaly Kuznetsov 		switch (opt) {
171170f4beaSVitaly Kuznetsov 		case 'n':
172170f4beaSVitaly Kuznetsov 			daemonize = 0;
173170f4beaSVitaly Kuznetsov 			break;
174170f4beaSVitaly Kuznetsov 		case 'h':
175170f4beaSVitaly Kuznetsov 		default:
176170f4beaSVitaly Kuznetsov 			print_usage(argv);
177170f4beaSVitaly Kuznetsov 			exit(EXIT_FAILURE);
178170f4beaSVitaly Kuznetsov 		}
179170f4beaSVitaly Kuznetsov 	}
180170f4beaSVitaly Kuznetsov 
181170f4beaSVitaly Kuznetsov 	if (daemonize && daemon(1, 0)) {
18201325476SK. Y. Srinivasan 		syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
18301325476SK. Y. Srinivasan 		exit(EXIT_FAILURE);
18401325476SK. Y. Srinivasan 	}
18501325476SK. Y. Srinivasan 
18601325476SK. Y. Srinivasan 	openlog("HV_FCOPY", 0, LOG_USER);
1876dfb867cSOlaf Hering 	syslog(LOG_INFO, "starting; pid is:%d", getpid());
18801325476SK. Y. Srinivasan 
189*9fc3c01aSDexuan Cui reopen_fcopy_fd:
190*9fc3c01aSDexuan Cui 	if (fcopy_fd != -1)
191*9fc3c01aSDexuan Cui 		close(fcopy_fd);
192*9fc3c01aSDexuan Cui 	/* Remove any possible partially-copied file on error */
193*9fc3c01aSDexuan Cui 	hv_copy_cancel();
194*9fc3c01aSDexuan Cui 	in_handshake = 1;
19501325476SK. Y. Srinivasan 	fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR);
19601325476SK. Y. Srinivasan 
19701325476SK. Y. Srinivasan 	if (fcopy_fd < 0) {
19801325476SK. Y. Srinivasan 		syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s",
19901325476SK. Y. Srinivasan 			errno, strerror(errno));
20001325476SK. Y. Srinivasan 		exit(EXIT_FAILURE);
20101325476SK. Y. Srinivasan 	}
20201325476SK. Y. Srinivasan 
20301325476SK. Y. Srinivasan 	/*
20401325476SK. Y. Srinivasan 	 * Register with the kernel.
20501325476SK. Y. Srinivasan 	 */
20601325476SK. Y. Srinivasan 	if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) {
20701325476SK. Y. Srinivasan 		syslog(LOG_ERR, "Registration failed: %s", strerror(errno));
20801325476SK. Y. Srinivasan 		exit(EXIT_FAILURE);
20901325476SK. Y. Srinivasan 	}
21001325476SK. Y. Srinivasan 
21101325476SK. Y. Srinivasan 	while (1) {
21201325476SK. Y. Srinivasan 		/*
21301325476SK. Y. Srinivasan 		 * In this loop we process fcopy messages after the
21401325476SK. Y. Srinivasan 		 * handshake is complete.
21501325476SK. Y. Srinivasan 		 */
2163f2baa8aSOlaf Hering 		ssize_t len;
2173f2baa8aSOlaf Hering 
2183f2baa8aSOlaf Hering 		len = pread(fcopy_fd, &buffer, sizeof(buffer), 0);
21901325476SK. Y. Srinivasan 		if (len < 0) {
22001325476SK. Y. Srinivasan 			syslog(LOG_ERR, "pread failed: %s", strerror(errno));
221*9fc3c01aSDexuan Cui 			goto reopen_fcopy_fd;
22201325476SK. Y. Srinivasan 		}
223a4d1ee5bSVitaly Kuznetsov 
224a4d1ee5bSVitaly Kuznetsov 		if (in_handshake) {
2253f2baa8aSOlaf Hering 			if (len != sizeof(buffer.kernel_modver)) {
226a4d1ee5bSVitaly Kuznetsov 				syslog(LOG_ERR, "invalid version negotiation");
227a4d1ee5bSVitaly Kuznetsov 				exit(EXIT_FAILURE);
228a4d1ee5bSVitaly Kuznetsov 			}
229a4d1ee5bSVitaly Kuznetsov 			in_handshake = 0;
2303f2baa8aSOlaf Hering 			syslog(LOG_INFO, "kernel module version: %u",
2313f2baa8aSOlaf Hering 			       buffer.kernel_modver);
232a4d1ee5bSVitaly Kuznetsov 			continue;
233a4d1ee5bSVitaly Kuznetsov 		}
234a4d1ee5bSVitaly Kuznetsov 
2353f2baa8aSOlaf Hering 		switch (buffer.hdr.operation) {
23601325476SK. Y. Srinivasan 		case START_FILE_COPY:
2373f2baa8aSOlaf Hering 			error = hv_start_fcopy(&buffer.start);
23801325476SK. Y. Srinivasan 			break;
23901325476SK. Y. Srinivasan 		case WRITE_TO_FILE:
2403f2baa8aSOlaf Hering 			error = hv_copy_data(&buffer.copy);
24101325476SK. Y. Srinivasan 			break;
24201325476SK. Y. Srinivasan 		case COMPLETE_FCOPY:
24301325476SK. Y. Srinivasan 			error = hv_copy_finished();
24401325476SK. Y. Srinivasan 			break;
24501325476SK. Y. Srinivasan 		case CANCEL_FCOPY:
24601325476SK. Y. Srinivasan 			error = hv_copy_cancel();
24701325476SK. Y. Srinivasan 			break;
24801325476SK. Y. Srinivasan 
24901325476SK. Y. Srinivasan 		default:
250c2d68afbSVitaly Kuznetsov 			error = HV_E_FAIL;
25101325476SK. Y. Srinivasan 			syslog(LOG_ERR, "Unknown operation: %d",
2523f2baa8aSOlaf Hering 				buffer.hdr.operation);
25301325476SK. Y. Srinivasan 
25401325476SK. Y. Srinivasan 		}
25501325476SK. Y. Srinivasan 
256*9fc3c01aSDexuan Cui 		/*
257*9fc3c01aSDexuan Cui 		 * pwrite() may return an error due to the faked CANCEL_FCOPY
258*9fc3c01aSDexuan Cui 		 * message upon hibernation. Ignore the error by resetting the
259*9fc3c01aSDexuan Cui 		 * dev file, i.e. closing and re-opening it.
260*9fc3c01aSDexuan Cui 		 */
26101325476SK. Y. Srinivasan 		if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
26201325476SK. Y. Srinivasan 			syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
263*9fc3c01aSDexuan Cui 			goto reopen_fcopy_fd;
26401325476SK. Y. Srinivasan 		}
26501325476SK. Y. Srinivasan 	}
26601325476SK. Y. Srinivasan }
267