~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/hv/hv_fcopy_uio_daemon.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 MAX_FOLDER_NAME         15
 39 #define MAX_PATH_LEN            15
 40 #define FCOPY_UIO               "/sys/bus/vmbus/devices/eb765408-105f-49b6-b4aa-c123b64d17d4/uio"
 41 
 42 #define FCOPY_VER_COUNT         1
 43 static const int fcopy_versions[] = {
 44         WIN8_SRV_VERSION
 45 };
 46 
 47 #define FW_VER_COUNT            1
 48 static const int fw_versions[] = {
 49         UTIL_FW_VERSION
 50 };
 51 
 52 #define HV_RING_SIZE            0x4000 /* 16KB ring buffer size */
 53 
 54 unsigned char desc[HV_RING_SIZE];
 55 
 56 static int target_fd;
 57 static char target_fname[PATH_MAX];
 58 static unsigned long long filesize;
 59 
 60 static int hv_fcopy_create_file(char *file_name, char *path_name, __u32 flags)
 61 {
 62         int error = HV_E_FAIL;
 63         char *q, *p;
 64 
 65         filesize = 0;
 66         p = path_name;
 67         snprintf(target_fname, sizeof(target_fname), "%s/%s",
 68                  path_name, file_name);
 69 
 70         /*
 71          * Check to see if the path is already in place; if not,
 72          * create if required.
 73          */
 74         while ((q = strchr(p, '/')) != NULL) {
 75                 if (q == p) {
 76                         p++;
 77                         continue;
 78                 }
 79                 *q = '\0';
 80                 if (access(path_name, F_OK)) {
 81                         if (flags & CREATE_PATH) {
 82                                 if (mkdir(path_name, 0755)) {
 83                                         syslog(LOG_ERR, "Failed to create %s",
 84                                                path_name);
 85                                         goto done;
 86                                 }
 87                         } else {
 88                                 syslog(LOG_ERR, "Invalid path: %s", path_name);
 89                                 goto done;
 90                         }
 91                 }
 92                 p = q + 1;
 93                 *q = '/';
 94         }
 95 
 96         if (!access(target_fname, F_OK)) {
 97                 syslog(LOG_INFO, "File: %s exists", target_fname);
 98                 if (!(flags & OVER_WRITE)) {
 99                         error = HV_ERROR_ALREADY_EXISTS;
100                         goto done;
101                 }
102         }
103 
104         target_fd = open(target_fname,
105                          O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
106         if (target_fd == -1) {
107                 syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
108                 goto done;
109         }
110 
111         error = 0;
112 done:
113         if (error)
114                 target_fname[0] = '\0';
115         return error;
116 }
117 
118 /* copy the data into the file */
119 static int hv_copy_data(struct hv_do_fcopy *cpmsg)
120 {
121         ssize_t len;
122         int ret = 0;
123 
124         len = pwrite(target_fd, cpmsg->data, cpmsg->size, cpmsg->offset);
125 
126         filesize += cpmsg->size;
127         if (len != cpmsg->size) {
128                 switch (errno) {
129                 case ENOSPC:
130                         ret = HV_ERROR_DISK_FULL;
131                         break;
132                 default:
133                         ret = HV_E_FAIL;
134                         break;
135                 }
136                 syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
137                        filesize, (long)len, strerror(errno));
138         }
139 
140         return ret;
141 }
142 
143 static int hv_copy_finished(void)
144 {
145         close(target_fd);
146         target_fname[0] = '\0';
147 
148         return 0;
149 }
150 
151 static void print_usage(char *argv[])
152 {
153         fprintf(stderr, "Usage: %s [options]\n"
154                 "Options are:\n"
155                 "  -n, --no-daemon        stay in foreground, don't daemonize\n"
156                 "  -h, --help             print this help\n", argv[0]);
157 }
158 
159 static bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, unsigned char *buf,
160                                       unsigned int buflen, const int *fw_version, int fw_vercnt,
161                                 const int *srv_version, int srv_vercnt,
162                                 int *nego_fw_version, int *nego_srv_version)
163 {
164         int icframe_major, icframe_minor;
165         int icmsg_major, icmsg_minor;
166         int fw_major, fw_minor;
167         int srv_major, srv_minor;
168         int i, j;
169         bool found_match = false;
170         struct icmsg_negotiate *negop;
171 
172         /* Check that there's enough space for icframe_vercnt, icmsg_vercnt */
173         if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) {
174                 syslog(LOG_ERR, "Invalid icmsg negotiate");
175                 return false;
176         }
177 
178         icmsghdrp->icmsgsize = 0x10;
179         negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR];
180 
181         icframe_major = negop->icframe_vercnt;
182         icframe_minor = 0;
183 
184         icmsg_major = negop->icmsg_vercnt;
185         icmsg_minor = 0;
186 
187         /* Validate negop packet */
188         if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
189             icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
190             ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) {
191                 syslog(LOG_ERR, "Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n",
192                        icframe_major, icmsg_major);
193                 goto fw_error;
194         }
195 
196         /*
197          * Select the framework version number we will
198          * support.
199          */
200 
201         for (i = 0; i < fw_vercnt; i++) {
202                 fw_major = (fw_version[i] >> 16);
203                 fw_minor = (fw_version[i] & 0xFFFF);
204 
205                 for (j = 0; j < negop->icframe_vercnt; j++) {
206                         if (negop->icversion_data[j].major == fw_major &&
207                             negop->icversion_data[j].minor == fw_minor) {
208                                 icframe_major = negop->icversion_data[j].major;
209                                 icframe_minor = negop->icversion_data[j].minor;
210                                 found_match = true;
211                                 break;
212                         }
213                 }
214 
215                 if (found_match)
216                         break;
217         }
218 
219         if (!found_match)
220                 goto fw_error;
221 
222         found_match = false;
223 
224         for (i = 0; i < srv_vercnt; i++) {
225                 srv_major = (srv_version[i] >> 16);
226                 srv_minor = (srv_version[i] & 0xFFFF);
227 
228                 for (j = negop->icframe_vercnt;
229                         (j < negop->icframe_vercnt + negop->icmsg_vercnt);
230                         j++) {
231                         if (negop->icversion_data[j].major == srv_major &&
232                             negop->icversion_data[j].minor == srv_minor) {
233                                 icmsg_major = negop->icversion_data[j].major;
234                                 icmsg_minor = negop->icversion_data[j].minor;
235                                 found_match = true;
236                                 break;
237                         }
238                 }
239 
240                 if (found_match)
241                         break;
242         }
243 
244         /*
245          * Respond with the framework and service
246          * version numbers we can support.
247          */
248 fw_error:
249         if (!found_match) {
250                 negop->icframe_vercnt = 0;
251                 negop->icmsg_vercnt = 0;
252         } else {
253                 negop->icframe_vercnt = 1;
254                 negop->icmsg_vercnt = 1;
255         }
256 
257         if (nego_fw_version)
258                 *nego_fw_version = (icframe_major << 16) | icframe_minor;
259 
260         if (nego_srv_version)
261                 *nego_srv_version = (icmsg_major << 16) | icmsg_minor;
262 
263         negop->icversion_data[0].major = icframe_major;
264         negop->icversion_data[0].minor = icframe_minor;
265         negop->icversion_data[1].major = icmsg_major;
266         negop->icversion_data[1].minor = icmsg_minor;
267 
268         return found_match;
269 }
270 
271 static void wcstoutf8(char *dest, const __u16 *src, size_t dest_size)
272 {
273         size_t len = 0;
274 
275         while (len < dest_size) {
276                 if (src[len] < 0x80)
277                         dest[len++] = (char)(*src++);
278                 else
279                         dest[len++] = 'X';
280         }
281 
282         dest[len] = '\0';
283 }
284 
285 static int hv_fcopy_start(struct hv_start_fcopy *smsg_in)
286 {
287         setlocale(LC_ALL, "en_US.utf8");
288         size_t file_size, path_size;
289         char *file_name, *path_name;
290         char *in_file_name = (char *)smsg_in->file_name;
291         char *in_path_name = (char *)smsg_in->path_name;
292 
293         file_size = wcstombs(NULL, (const wchar_t *restrict)in_file_name, 0) + 1;
294         path_size = wcstombs(NULL, (const wchar_t *restrict)in_path_name, 0) + 1;
295 
296         file_name = (char *)malloc(file_size * sizeof(char));
297         path_name = (char *)malloc(path_size * sizeof(char));
298 
299         if (!file_name || !path_name) {
300                 free(file_name);
301                 free(path_name);
302                 syslog(LOG_ERR, "Can't allocate memory for file name and/or path name");
303                 return HV_E_FAIL;
304         }
305 
306         wcstoutf8(file_name, (__u16 *)in_file_name, file_size);
307         wcstoutf8(path_name, (__u16 *)in_path_name, path_size);
308 
309         return hv_fcopy_create_file(file_name, path_name, smsg_in->copy_flags);
310 }
311 
312 static int hv_fcopy_send_data(struct hv_fcopy_hdr *fcopy_msg, int recvlen)
313 {
314         int operation = fcopy_msg->operation;
315 
316         /*
317          * The  strings sent from the host are encoded in
318          * utf16; convert it to utf8 strings.
319          * The host assures us that the utf16 strings will not exceed
320          * the max lengths specified. We will however, reserve room
321          * for the string terminating character - in the utf16s_utf8s()
322          * function we limit the size of the buffer where the converted
323          * string is placed to W_MAX_PATH -1 to guarantee
324          * that the strings can be properly terminated!
325          */
326 
327         switch (operation) {
328         case START_FILE_COPY:
329                 return hv_fcopy_start((struct hv_start_fcopy *)fcopy_msg);
330         case WRITE_TO_FILE:
331                 return hv_copy_data((struct hv_do_fcopy *)fcopy_msg);
332         case COMPLETE_FCOPY:
333                 return hv_copy_finished();
334         }
335 
336         return HV_E_FAIL;
337 }
338 
339 /* process the packet recv from host */
340 static int fcopy_pkt_process(struct vmbus_br *txbr)
341 {
342         int ret, offset, pktlen;
343         int fcopy_srv_version;
344         const struct vmbus_chanpkt_hdr *pkt;
345         struct hv_fcopy_hdr *fcopy_msg;
346         struct icmsg_hdr *icmsghdr;
347 
348         pkt = (const struct vmbus_chanpkt_hdr *)desc;
349         offset = pkt->hlen << 3;
350         pktlen = (pkt->tlen << 3) - offset;
351         icmsghdr = (struct icmsg_hdr *)&desc[offset + sizeof(struct vmbuspipe_hdr)];
352         icmsghdr->status = HV_E_FAIL;
353 
354         if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
355                 if (vmbus_prep_negotiate_resp(icmsghdr, desc + offset, pktlen, fw_versions,
356                                               FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT,
357                                               NULL, &fcopy_srv_version)) {
358                         syslog(LOG_INFO, "FCopy IC version %d.%d",
359                                fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF);
360                         icmsghdr->status = 0;
361                 }
362         } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) {
363                 /* Ensure recvlen is big enough to contain hv_fcopy_hdr */
364                 if (pktlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) {
365                         syslog(LOG_ERR, "Invalid Fcopy hdr. Packet length too small: %u",
366                                pktlen);
367                         return -ENOBUFS;
368                 }
369 
370                 fcopy_msg = (struct hv_fcopy_hdr *)&desc[offset + ICMSG_HDR];
371                 icmsghdr->status = hv_fcopy_send_data(fcopy_msg, pktlen);
372         }
373 
374         icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
375         ret = rte_vmbus_chan_send(txbr, 0x6, desc + offset, pktlen, 0);
376         if (ret) {
377                 syslog(LOG_ERR, "Write to ringbuffer failed err: %d", ret);
378                 return ret;
379         }
380 
381         return 0;
382 }
383 
384 static void fcopy_get_first_folder(char *path, char *chan_no)
385 {
386         DIR *dir = opendir(path);
387         struct dirent *entry;
388 
389         if (!dir) {
390                 syslog(LOG_ERR, "Failed to open directory (errno=%s).\n", strerror(errno));
391                 return;
392         }
393 
394         while ((entry = readdir(dir)) != NULL) {
395                 if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 &&
396                     strcmp(entry->d_name, "..") != 0) {
397                         strcpy(chan_no, entry->d_name);
398                         break;
399                 }
400         }
401 
402         closedir(dir);
403 }
404 
405 int main(int argc, char *argv[])
406 {
407         int fcopy_fd = -1, tmp = 1;
408         int daemonize = 1, long_index = 0, opt, ret = -EINVAL;
409         struct vmbus_br txbr, rxbr;
410         void *ring;
411         uint32_t len = HV_RING_SIZE;
412         char uio_name[MAX_FOLDER_NAME] = {0};
413         char uio_dev_path[MAX_PATH_LEN] = {0};
414 
415         static struct option long_options[] = {
416                 {"help",        no_argument,       0,  'h' },
417                 {"no-daemon",   no_argument,       0,  'n' },
418                 {0,             0,                 0,  0   }
419         };
420 
421         while ((opt = getopt_long(argc, argv, "hn", long_options,
422                                   &long_index)) != -1) {
423                 switch (opt) {
424                 case 'n':
425                         daemonize = 0;
426                         break;
427                 case 'h':
428                 default:
429                         print_usage(argv);
430                         goto exit;
431                 }
432         }
433 
434         if (daemonize && daemon(1, 0)) {
435                 syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
436                 goto exit;
437         }
438 
439         openlog("HV_UIO_FCOPY", 0, LOG_USER);
440         syslog(LOG_INFO, "starting; pid is:%d", getpid());
441 
442         fcopy_get_first_folder(FCOPY_UIO, uio_name);
443         snprintf(uio_dev_path, sizeof(uio_dev_path), "/dev/%s", uio_name);
444         fcopy_fd = open(uio_dev_path, O_RDWR);
445 
446         if (fcopy_fd < 0) {
447                 syslog(LOG_ERR, "open %s failed; error: %d %s",
448                        uio_dev_path, errno, strerror(errno));
449                 ret = fcopy_fd;
450                 goto exit;
451         }
452 
453         ring = vmbus_uio_map(&fcopy_fd, HV_RING_SIZE);
454         if (!ring) {
455                 ret = errno;
456                 syslog(LOG_ERR, "mmap ringbuffer failed; error: %d %s", ret, strerror(ret));
457                 goto close;
458         }
459         vmbus_br_setup(&txbr, ring, HV_RING_SIZE);
460         vmbus_br_setup(&rxbr, (char *)ring + HV_RING_SIZE, HV_RING_SIZE);
461 
462         rxbr.vbr->imask = 0;
463 
464         while (1) {
465                 /*
466                  * In this loop we process fcopy messages after the
467                  * handshake is complete.
468                  */
469                 ret = pread(fcopy_fd, &tmp, sizeof(int), 0);
470                 if (ret < 0) {
471                         syslog(LOG_ERR, "pread failed: %s", strerror(errno));
472                         continue;
473                 }
474 
475                 len = HV_RING_SIZE;
476                 ret = rte_vmbus_chan_recv_raw(&rxbr, desc, &len);
477                 if (unlikely(ret <= 0)) {
478                         /* This indicates a failure to communicate (or worse) */
479                         syslog(LOG_ERR, "VMBus channel recv error: %d", ret);
480                 } else {
481                         ret = fcopy_pkt_process(&txbr);
482                         if (ret < 0)
483                                 goto close;
484 
485                         /* Signal host */
486                         if ((write(fcopy_fd, &tmp, sizeof(int))) != sizeof(int)) {
487                                 ret = errno;
488                                 syslog(LOG_ERR, "Signal to host failed: %s\n", strerror(ret));
489                                 goto close;
490                         }
491                 }
492         }
493 close:
494         close(fcopy_fd);
495 exit:
496         return ret;
497 }
498 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php