Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * An implementation of the host initiated guest snapshot for Hyper-V.
0004  *
0005  * Copyright (C) 2013, Microsoft, Inc.
0006  * Author : K. Y. Srinivasan <kys@microsoft.com>
0007  */
0008 
0009 
0010 #include <sys/types.h>
0011 #include <sys/poll.h>
0012 #include <sys/ioctl.h>
0013 #include <sys/stat.h>
0014 #include <sys/sysmacros.h>
0015 #include <fcntl.h>
0016 #include <stdio.h>
0017 #include <mntent.h>
0018 #include <stdlib.h>
0019 #include <unistd.h>
0020 #include <string.h>
0021 #include <ctype.h>
0022 #include <errno.h>
0023 #include <linux/fs.h>
0024 #include <linux/major.h>
0025 #include <linux/hyperv.h>
0026 #include <syslog.h>
0027 #include <getopt.h>
0028 #include <stdbool.h>
0029 #include <dirent.h>
0030 
0031 static bool fs_frozen;
0032 
0033 /* Don't use syslog() in the function since that can cause write to disk */
0034 static int vss_do_freeze(char *dir, unsigned int cmd)
0035 {
0036     int ret, fd = open(dir, O_RDONLY);
0037 
0038     if (fd < 0)
0039         return 1;
0040 
0041     ret = ioctl(fd, cmd, 0);
0042 
0043     /*
0044      * If a partition is mounted more than once, only the first
0045      * FREEZE/THAW can succeed and the later ones will get
0046      * EBUSY/EINVAL respectively: there could be 2 cases:
0047      * 1) a user may mount the same partition to different directories
0048      *  by mistake or on purpose;
0049      * 2) The subvolume of btrfs appears to have the same partition
0050      * mounted more than once.
0051      */
0052     if (ret) {
0053         if ((cmd == FIFREEZE && errno == EBUSY) ||
0054             (cmd == FITHAW && errno == EINVAL)) {
0055             close(fd);
0056             return 0;
0057         }
0058     }
0059 
0060     close(fd);
0061     return !!ret;
0062 }
0063 
0064 static bool is_dev_loop(const char *blkname)
0065 {
0066     char *buffer;
0067     DIR *dir;
0068     struct dirent *entry;
0069     bool ret = false;
0070 
0071     buffer = malloc(PATH_MAX);
0072     if (!buffer) {
0073         syslog(LOG_ERR, "Can't allocate memory!");
0074         exit(1);
0075     }
0076 
0077     snprintf(buffer, PATH_MAX, "%s/loop", blkname);
0078     if (!access(buffer, R_OK | X_OK)) {
0079         ret = true;
0080         goto free_buffer;
0081     } else if (errno != ENOENT) {
0082         syslog(LOG_ERR, "Can't access: %s; error:%d %s!",
0083                buffer, errno, strerror(errno));
0084     }
0085 
0086     snprintf(buffer, PATH_MAX, "%s/slaves", blkname);
0087     dir = opendir(buffer);
0088     if (!dir) {
0089         if (errno != ENOENT)
0090             syslog(LOG_ERR, "Can't opendir: %s; error:%d %s!",
0091                    buffer, errno, strerror(errno));
0092         goto free_buffer;
0093     }
0094 
0095     while ((entry = readdir(dir)) != NULL) {
0096         if (strcmp(entry->d_name, ".") == 0 ||
0097             strcmp(entry->d_name, "..") == 0)
0098             continue;
0099 
0100         snprintf(buffer, PATH_MAX, "%s/slaves/%s", blkname,
0101              entry->d_name);
0102         if (is_dev_loop(buffer)) {
0103             ret = true;
0104             break;
0105         }
0106     }
0107     closedir(dir);
0108 free_buffer:
0109     free(buffer);
0110     return ret;
0111 }
0112 
0113 static int vss_operate(int operation)
0114 {
0115     char match[] = "/dev/";
0116     FILE *mounts;
0117     struct mntent *ent;
0118     struct stat sb;
0119     char errdir[1024] = {0};
0120     char blkdir[23]; /* /sys/dev/block/XXX:XXX */
0121     unsigned int cmd;
0122     int error = 0, root_seen = 0, save_errno = 0;
0123 
0124     switch (operation) {
0125     case VSS_OP_FREEZE:
0126         cmd = FIFREEZE;
0127         break;
0128     case VSS_OP_THAW:
0129         cmd = FITHAW;
0130         break;
0131     default:
0132         return -1;
0133     }
0134 
0135     mounts = setmntent("/proc/mounts", "r");
0136     if (mounts == NULL)
0137         return -1;
0138 
0139     while ((ent = getmntent(mounts))) {
0140         if (strncmp(ent->mnt_fsname, match, strlen(match)))
0141             continue;
0142         if (stat(ent->mnt_fsname, &sb)) {
0143             syslog(LOG_ERR, "Can't stat: %s; error:%d %s!",
0144                    ent->mnt_fsname, errno, strerror(errno));
0145         } else {
0146             sprintf(blkdir, "/sys/dev/block/%d:%d",
0147                 major(sb.st_rdev), minor(sb.st_rdev));
0148             if (is_dev_loop(blkdir))
0149                 continue;
0150         }
0151         if (hasmntopt(ent, MNTOPT_RO) != NULL)
0152             continue;
0153         if (strcmp(ent->mnt_type, "vfat") == 0)
0154             continue;
0155         if (strcmp(ent->mnt_dir, "/") == 0) {
0156             root_seen = 1;
0157             continue;
0158         }
0159         error |= vss_do_freeze(ent->mnt_dir, cmd);
0160         if (operation == VSS_OP_FREEZE) {
0161             if (error)
0162                 goto err;
0163             fs_frozen = true;
0164         }
0165     }
0166 
0167     endmntent(mounts);
0168 
0169     if (root_seen) {
0170         error |= vss_do_freeze("/", cmd);
0171         if (operation == VSS_OP_FREEZE) {
0172             if (error)
0173                 goto err;
0174             fs_frozen = true;
0175         }
0176     }
0177 
0178     if (operation == VSS_OP_THAW && !error)
0179         fs_frozen = false;
0180 
0181     goto out;
0182 err:
0183     save_errno = errno;
0184     if (ent) {
0185         strncpy(errdir, ent->mnt_dir, sizeof(errdir)-1);
0186         endmntent(mounts);
0187     }
0188     vss_operate(VSS_OP_THAW);
0189     fs_frozen = false;
0190     /* Call syslog after we thaw all filesystems */
0191     if (ent)
0192         syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s",
0193                errdir, save_errno, strerror(save_errno));
0194     else
0195         syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno,
0196                strerror(save_errno));
0197 out:
0198     return error;
0199 }
0200 
0201 void print_usage(char *argv[])
0202 {
0203     fprintf(stderr, "Usage: %s [options]\n"
0204         "Options are:\n"
0205         "  -n, --no-daemon        stay in foreground, don't daemonize\n"
0206         "  -h, --help             print this help\n", argv[0]);
0207 }
0208 
0209 int main(int argc, char *argv[])
0210 {
0211     int vss_fd = -1, len;
0212     int error;
0213     struct pollfd pfd;
0214     int op;
0215     struct hv_vss_msg vss_msg[1];
0216     int daemonize = 1, long_index = 0, opt;
0217     int in_handshake;
0218     __u32 kernel_modver;
0219 
0220     static struct option long_options[] = {
0221         {"help",    no_argument,       0,  'h' },
0222         {"no-daemon",   no_argument,       0,  'n' },
0223         {0,     0,         0,  0   }
0224     };
0225 
0226     while ((opt = getopt_long(argc, argv, "hn", long_options,
0227                   &long_index)) != -1) {
0228         switch (opt) {
0229         case 'n':
0230             daemonize = 0;
0231             break;
0232         case 'h':
0233             print_usage(argv);
0234             exit(0);
0235         default:
0236             print_usage(argv);
0237             exit(EXIT_FAILURE);
0238         }
0239     }
0240 
0241     if (daemonize && daemon(1, 0))
0242         return 1;
0243 
0244     openlog("Hyper-V VSS", 0, LOG_USER);
0245     syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
0246 
0247 reopen_vss_fd:
0248     if (vss_fd != -1)
0249         close(vss_fd);
0250     if (fs_frozen) {
0251         if (vss_operate(VSS_OP_THAW) || fs_frozen) {
0252             syslog(LOG_ERR, "failed to thaw file system: err=%d",
0253                    errno);
0254             exit(EXIT_FAILURE);
0255         }
0256     }
0257 
0258     in_handshake = 1;
0259     vss_fd = open("/dev/vmbus/hv_vss", O_RDWR);
0260     if (vss_fd < 0) {
0261         syslog(LOG_ERR, "open /dev/vmbus/hv_vss failed; error: %d %s",
0262                errno, strerror(errno));
0263         exit(EXIT_FAILURE);
0264     }
0265     /*
0266      * Register ourselves with the kernel.
0267      */
0268     vss_msg->vss_hdr.operation = VSS_OP_REGISTER1;
0269 
0270     len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
0271     if (len < 0) {
0272         syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
0273                errno, strerror(errno));
0274         close(vss_fd);
0275         exit(EXIT_FAILURE);
0276     }
0277 
0278     pfd.fd = vss_fd;
0279 
0280     while (1) {
0281         pfd.events = POLLIN;
0282         pfd.revents = 0;
0283 
0284         if (poll(&pfd, 1, -1) < 0) {
0285             syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
0286             if (errno == EINVAL) {
0287                 close(vss_fd);
0288                 exit(EXIT_FAILURE);
0289             }
0290             else
0291                 continue;
0292         }
0293 
0294         len = read(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
0295 
0296         if (in_handshake) {
0297             if (len != sizeof(kernel_modver)) {
0298                 syslog(LOG_ERR, "invalid version negotiation");
0299                 exit(EXIT_FAILURE);
0300             }
0301             kernel_modver = *(__u32 *)vss_msg;
0302             in_handshake = 0;
0303             syslog(LOG_INFO, "VSS: kernel module version: %d",
0304                    kernel_modver);
0305             continue;
0306         }
0307 
0308         if (len != sizeof(struct hv_vss_msg)) {
0309             syslog(LOG_ERR, "read failed; error:%d %s",
0310                    errno, strerror(errno));
0311             goto reopen_vss_fd;
0312         }
0313 
0314         op = vss_msg->vss_hdr.operation;
0315         error =  HV_S_OK;
0316 
0317         switch (op) {
0318         case VSS_OP_FREEZE:
0319         case VSS_OP_THAW:
0320             error = vss_operate(op);
0321             syslog(LOG_INFO, "VSS: op=%s: %s\n",
0322                 op == VSS_OP_FREEZE ? "FREEZE" : "THAW",
0323                 error ? "failed" : "succeeded");
0324 
0325             if (error) {
0326                 error = HV_E_FAIL;
0327                 syslog(LOG_ERR, "op=%d failed!", op);
0328                 syslog(LOG_ERR, "report it with these files:");
0329                 syslog(LOG_ERR, "/etc/fstab and /proc/mounts");
0330             }
0331             break;
0332         case VSS_OP_HOT_BACKUP:
0333             syslog(LOG_INFO, "VSS: op=CHECK HOT BACKUP\n");
0334             break;
0335         default:
0336             syslog(LOG_ERR, "Illegal op:%d\n", op);
0337         }
0338 
0339         /*
0340          * The write() may return an error due to the faked VSS_OP_THAW
0341          * message upon hibernation. Ignore the error by resetting the
0342          * dev file, i.e. closing and re-opening it.
0343          */
0344         vss_msg->error = error;
0345         len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
0346         if (len != sizeof(struct hv_vss_msg)) {
0347             syslog(LOG_ERR, "write failed; error: %d %s", errno,
0348                    strerror(errno));
0349             goto reopen_vss_fd;
0350         }
0351     }
0352 
0353     close(vss_fd);
0354     exit(0);
0355 }