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