Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / tools / hv / hv_fcopy_daemon.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) 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 }