Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * An implementation of host to guest copy functionality for Linux.
0004  *
0005  * Copyright (C) 2014, Microsoft, Inc.
0006  *
0007  * Author : K. Y. Srinivasan <kys@microsoft.com>
0008  */
0009 
0010 
0011 #include <sys/types.h>
0012 #include <stdio.h>
0013 #include <stdlib.h>
0014 #include <unistd.h>
0015 #include <string.h>
0016 #include <errno.h>
0017 #include <linux/hyperv.h>
0018 #include <linux/limits.h>
0019 #include <syslog.h>
0020 #include <sys/stat.h>
0021 #include <fcntl.h>
0022 #include <getopt.h>
0023 
0024 static int target_fd;
0025 static char target_fname[PATH_MAX];
0026 static unsigned long long filesize;
0027 
0028 static int hv_start_fcopy(struct hv_start_fcopy *smsg)
0029 {
0030     int error = HV_E_FAIL;
0031     char *q, *p;
0032 
0033     filesize = 0;
0034     p = (char *)smsg->path_name;
0035     snprintf(target_fname, sizeof(target_fname), "%s/%s",
0036          (char *)smsg->path_name, (char *)smsg->file_name);
0037 
0038     syslog(LOG_INFO, "Target file name: %s", target_fname);
0039     /*
0040      * Check to see if the path is already in place; if not,
0041      * create if required.
0042      */
0043     while ((q = strchr(p, '/')) != NULL) {
0044         if (q == p) {
0045             p++;
0046             continue;
0047         }
0048         *q = '\0';
0049         if (access((char *)smsg->path_name, F_OK)) {
0050             if (smsg->copy_flags & CREATE_PATH) {
0051                 if (mkdir((char *)smsg->path_name, 0755)) {
0052                     syslog(LOG_ERR, "Failed to create %s",
0053                         (char *)smsg->path_name);
0054                     goto done;
0055                 }
0056             } else {
0057                 syslog(LOG_ERR, "Invalid path: %s",
0058                     (char *)smsg->path_name);
0059                 goto done;
0060             }
0061         }
0062         p = q + 1;
0063         *q = '/';
0064     }
0065 
0066     if (!access(target_fname, F_OK)) {
0067         syslog(LOG_INFO, "File: %s exists", target_fname);
0068         if (!(smsg->copy_flags & OVER_WRITE)) {
0069             error = HV_ERROR_ALREADY_EXISTS;
0070             goto done;
0071         }
0072     }
0073 
0074     target_fd = open(target_fname,
0075              O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
0076     if (target_fd == -1) {
0077         syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
0078         goto done;
0079     }
0080 
0081     error = 0;
0082 done:
0083     if (error)
0084         target_fname[0] = '\0';
0085     return error;
0086 }
0087 
0088 static int hv_copy_data(struct hv_do_fcopy *cpmsg)
0089 {
0090     ssize_t bytes_written;
0091     int ret = 0;
0092 
0093     bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
0094                 cpmsg->offset);
0095 
0096     filesize += cpmsg->size;
0097     if (bytes_written != cpmsg->size) {
0098         switch (errno) {
0099         case ENOSPC:
0100             ret = HV_ERROR_DISK_FULL;
0101             break;
0102         default:
0103             ret = HV_E_FAIL;
0104             break;
0105         }
0106         syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
0107                filesize, (long)bytes_written, strerror(errno));
0108     }
0109 
0110     return ret;
0111 }
0112 
0113 /*
0114  * Reset target_fname to "" in the two below functions for hibernation: if
0115  * the fcopy operation is aborted by hibernation, the daemon should remove the
0116  * partially-copied file; to achieve this, the hv_utils driver always fakes a
0117  * CANCEL_FCOPY message upon suspend, and later when the VM resumes back,
0118  * the daemon calls hv_copy_cancel() to remove the file; if a file is copied
0119  * successfully before suspend, hv_copy_finished() must reset target_fname to
0120  * avoid that the file can be incorrectly removed upon resume, since the faked
0121  * CANCEL_FCOPY message is spurious in this case.
0122  */
0123 static int hv_copy_finished(void)
0124 {
0125     close(target_fd);
0126     target_fname[0] = '\0';
0127     return 0;
0128 }
0129 static int hv_copy_cancel(void)
0130 {
0131     close(target_fd);
0132     if (strlen(target_fname) > 0) {
0133         unlink(target_fname);
0134         target_fname[0] = '\0';
0135     }
0136     return 0;
0137 
0138 }
0139 
0140 void print_usage(char *argv[])
0141 {
0142     fprintf(stderr, "Usage: %s [options]\n"
0143         "Options are:\n"
0144         "  -n, --no-daemon        stay in foreground, don't daemonize\n"
0145         "  -h, --help             print this help\n", argv[0]);
0146 }
0147 
0148 int main(int argc, char *argv[])
0149 {
0150     int fcopy_fd = -1;
0151     int error;
0152     int daemonize = 1, long_index = 0, opt;
0153     int version = FCOPY_CURRENT_VERSION;
0154     union {
0155         struct hv_fcopy_hdr hdr;
0156         struct hv_start_fcopy start;
0157         struct hv_do_fcopy copy;
0158         __u32 kernel_modver;
0159     } buffer = { };
0160     int in_handshake;
0161 
0162     static struct option long_options[] = {
0163         {"help",    no_argument,       0,  'h' },
0164         {"no-daemon",   no_argument,       0,  'n' },
0165         {0,     0,         0,  0   }
0166     };
0167 
0168     while ((opt = getopt_long(argc, argv, "hn", long_options,
0169                   &long_index)) != -1) {
0170         switch (opt) {
0171         case 'n':
0172             daemonize = 0;
0173             break;
0174         case 'h':
0175         default:
0176             print_usage(argv);
0177             exit(EXIT_FAILURE);
0178         }
0179     }
0180 
0181     if (daemonize && daemon(1, 0)) {
0182         syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
0183         exit(EXIT_FAILURE);
0184     }
0185 
0186     openlog("HV_FCOPY", 0, LOG_USER);
0187     syslog(LOG_INFO, "starting; pid is:%d", getpid());
0188 
0189 reopen_fcopy_fd:
0190     if (fcopy_fd != -1)
0191         close(fcopy_fd);
0192     /* Remove any possible partially-copied file on error */
0193     hv_copy_cancel();
0194     in_handshake = 1;
0195     fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR);
0196 
0197     if (fcopy_fd < 0) {
0198         syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s",
0199             errno, strerror(errno));
0200         exit(EXIT_FAILURE);
0201     }
0202 
0203     /*
0204      * Register with the kernel.
0205      */
0206     if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) {
0207         syslog(LOG_ERR, "Registration failed: %s", strerror(errno));
0208         exit(EXIT_FAILURE);
0209     }
0210 
0211     while (1) {
0212         /*
0213          * In this loop we process fcopy messages after the
0214          * handshake is complete.
0215          */
0216         ssize_t len;
0217 
0218         len = pread(fcopy_fd, &buffer, sizeof(buffer), 0);
0219         if (len < 0) {
0220             syslog(LOG_ERR, "pread failed: %s", strerror(errno));
0221             goto reopen_fcopy_fd;
0222         }
0223 
0224         if (in_handshake) {
0225             if (len != sizeof(buffer.kernel_modver)) {
0226                 syslog(LOG_ERR, "invalid version negotiation");
0227                 exit(EXIT_FAILURE);
0228             }
0229             in_handshake = 0;
0230             syslog(LOG_INFO, "kernel module version: %u",
0231                    buffer.kernel_modver);
0232             continue;
0233         }
0234 
0235         switch (buffer.hdr.operation) {
0236         case START_FILE_COPY:
0237             error = hv_start_fcopy(&buffer.start);
0238             break;
0239         case WRITE_TO_FILE:
0240             error = hv_copy_data(&buffer.copy);
0241             break;
0242         case COMPLETE_FCOPY:
0243             error = hv_copy_finished();
0244             break;
0245         case CANCEL_FCOPY:
0246             error = hv_copy_cancel();
0247             break;
0248 
0249         default:
0250             error = HV_E_FAIL;
0251             syslog(LOG_ERR, "Unknown operation: %d",
0252                 buffer.hdr.operation);
0253 
0254         }
0255 
0256         /*
0257          * pwrite() may return an error due to the faked CANCEL_FCOPY
0258          * message upon hibernation. Ignore the error by resetting the
0259          * dev file, i.e. closing and re-opening it.
0260          */
0261         if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
0262             syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
0263             goto reopen_fcopy_fd;
0264         }
0265     }
0266 }