xref: /openbmc/linux/tools/hv/hv_fcopy_daemon.c (revision 9e3bd0f6)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * An implementation of host to guest copy functionality for Linux.
4  *
5  * Copyright (C) 2014, Microsoft, Inc.
6  *
7  * Author : K. Y. Srinivasan <kys@microsoft.com>
8  */
9 
10 
11 #include <sys/types.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <linux/hyperv.h>
18 #include <linux/limits.h>
19 #include <syslog.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <getopt.h>
23 
24 static int target_fd;
25 static char target_fname[PATH_MAX];
26 static unsigned long long filesize;
27 
28 static int hv_start_fcopy(struct hv_start_fcopy *smsg)
29 {
30 	int error = HV_E_FAIL;
31 	char *q, *p;
32 
33 	filesize = 0;
34 	p = (char *)smsg->path_name;
35 	snprintf(target_fname, sizeof(target_fname), "%s/%s",
36 		 (char *)smsg->path_name, (char *)smsg->file_name);
37 
38 	syslog(LOG_INFO, "Target file name: %s", target_fname);
39 	/*
40 	 * Check to see if the path is already in place; if not,
41 	 * create if required.
42 	 */
43 	while ((q = strchr(p, '/')) != NULL) {
44 		if (q == p) {
45 			p++;
46 			continue;
47 		}
48 		*q = '\0';
49 		if (access((char *)smsg->path_name, F_OK)) {
50 			if (smsg->copy_flags & CREATE_PATH) {
51 				if (mkdir((char *)smsg->path_name, 0755)) {
52 					syslog(LOG_ERR, "Failed to create %s",
53 						(char *)smsg->path_name);
54 					goto done;
55 				}
56 			} else {
57 				syslog(LOG_ERR, "Invalid path: %s",
58 					(char *)smsg->path_name);
59 				goto done;
60 			}
61 		}
62 		p = q + 1;
63 		*q = '/';
64 	}
65 
66 	if (!access(target_fname, F_OK)) {
67 		syslog(LOG_INFO, "File: %s exists", target_fname);
68 		if (!(smsg->copy_flags & OVER_WRITE)) {
69 			error = HV_ERROR_ALREADY_EXISTS;
70 			goto done;
71 		}
72 	}
73 
74 	target_fd = open(target_fname,
75 			 O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
76 	if (target_fd == -1) {
77 		syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
78 		goto done;
79 	}
80 
81 	error = 0;
82 done:
83 	return error;
84 }
85 
86 static int hv_copy_data(struct hv_do_fcopy *cpmsg)
87 {
88 	ssize_t bytes_written;
89 	int ret = 0;
90 
91 	bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
92 				cpmsg->offset);
93 
94 	filesize += cpmsg->size;
95 	if (bytes_written != cpmsg->size) {
96 		switch (errno) {
97 		case ENOSPC:
98 			ret = HV_ERROR_DISK_FULL;
99 			break;
100 		default:
101 			ret = HV_E_FAIL;
102 			break;
103 		}
104 		syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
105 		       filesize, (long)bytes_written, strerror(errno));
106 	}
107 
108 	return ret;
109 }
110 
111 static int hv_copy_finished(void)
112 {
113 	close(target_fd);
114 	return 0;
115 }
116 static int hv_copy_cancel(void)
117 {
118 	close(target_fd);
119 	unlink(target_fname);
120 	return 0;
121 
122 }
123 
124 void print_usage(char *argv[])
125 {
126 	fprintf(stderr, "Usage: %s [options]\n"
127 		"Options are:\n"
128 		"  -n, --no-daemon        stay in foreground, don't daemonize\n"
129 		"  -h, --help             print this help\n", argv[0]);
130 }
131 
132 int main(int argc, char *argv[])
133 {
134 	int fcopy_fd;
135 	int error;
136 	int daemonize = 1, long_index = 0, opt;
137 	int version = FCOPY_CURRENT_VERSION;
138 	union {
139 		struct hv_fcopy_hdr hdr;
140 		struct hv_start_fcopy start;
141 		struct hv_do_fcopy copy;
142 		__u32 kernel_modver;
143 	} buffer = { };
144 	int in_handshake = 1;
145 
146 	static struct option long_options[] = {
147 		{"help",	no_argument,	   0,  'h' },
148 		{"no-daemon",	no_argument,	   0,  'n' },
149 		{0,		0,		   0,  0   }
150 	};
151 
152 	while ((opt = getopt_long(argc, argv, "hn", long_options,
153 				  &long_index)) != -1) {
154 		switch (opt) {
155 		case 'n':
156 			daemonize = 0;
157 			break;
158 		case 'h':
159 		default:
160 			print_usage(argv);
161 			exit(EXIT_FAILURE);
162 		}
163 	}
164 
165 	if (daemonize && daemon(1, 0)) {
166 		syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
167 		exit(EXIT_FAILURE);
168 	}
169 
170 	openlog("HV_FCOPY", 0, LOG_USER);
171 	syslog(LOG_INFO, "starting; pid is:%d", getpid());
172 
173 	fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR);
174 
175 	if (fcopy_fd < 0) {
176 		syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s",
177 			errno, strerror(errno));
178 		exit(EXIT_FAILURE);
179 	}
180 
181 	/*
182 	 * Register with the kernel.
183 	 */
184 	if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) {
185 		syslog(LOG_ERR, "Registration failed: %s", strerror(errno));
186 		exit(EXIT_FAILURE);
187 	}
188 
189 	while (1) {
190 		/*
191 		 * In this loop we process fcopy messages after the
192 		 * handshake is complete.
193 		 */
194 		ssize_t len;
195 
196 		len = pread(fcopy_fd, &buffer, sizeof(buffer), 0);
197 		if (len < 0) {
198 			syslog(LOG_ERR, "pread failed: %s", strerror(errno));
199 			exit(EXIT_FAILURE);
200 		}
201 
202 		if (in_handshake) {
203 			if (len != sizeof(buffer.kernel_modver)) {
204 				syslog(LOG_ERR, "invalid version negotiation");
205 				exit(EXIT_FAILURE);
206 			}
207 			in_handshake = 0;
208 			syslog(LOG_INFO, "kernel module version: %u",
209 			       buffer.kernel_modver);
210 			continue;
211 		}
212 
213 		switch (buffer.hdr.operation) {
214 		case START_FILE_COPY:
215 			error = hv_start_fcopy(&buffer.start);
216 			break;
217 		case WRITE_TO_FILE:
218 			error = hv_copy_data(&buffer.copy);
219 			break;
220 		case COMPLETE_FCOPY:
221 			error = hv_copy_finished();
222 			break;
223 		case CANCEL_FCOPY:
224 			error = hv_copy_cancel();
225 			break;
226 
227 		default:
228 			error = HV_E_FAIL;
229 			syslog(LOG_ERR, "Unknown operation: %d",
230 				buffer.hdr.operation);
231 
232 		}
233 
234 		if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
235 			syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
236 			exit(EXIT_FAILURE);
237 		}
238 	}
239 }
240