root/tools/hv/hv_vss_daemon.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vss_do_freeze
  2. is_dev_loop
  3. vss_operate
  4. print_usage
  5. main

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * An implementation of the host initiated guest snapshot for Hyper-V.
   4  *
   5  * Copyright (C) 2013, Microsoft, Inc.
   6  * Author : K. Y. Srinivasan <kys@microsoft.com>
   7  */
   8 
   9 
  10 #include <sys/types.h>
  11 #include <sys/poll.h>
  12 #include <sys/ioctl.h>
  13 #include <sys/stat.h>
  14 #include <sys/sysmacros.h>
  15 #include <fcntl.h>
  16 #include <stdio.h>
  17 #include <mntent.h>
  18 #include <stdlib.h>
  19 #include <unistd.h>
  20 #include <string.h>
  21 #include <ctype.h>
  22 #include <errno.h>
  23 #include <linux/fs.h>
  24 #include <linux/major.h>
  25 #include <linux/hyperv.h>
  26 #include <syslog.h>
  27 #include <getopt.h>
  28 #include <stdbool.h>
  29 #include <dirent.h>
  30 
  31 /* Don't use syslog() in the function since that can cause write to disk */
  32 static int vss_do_freeze(char *dir, unsigned int cmd)
  33 {
  34         int ret, fd = open(dir, O_RDONLY);
  35 
  36         if (fd < 0)
  37                 return 1;
  38 
  39         ret = ioctl(fd, cmd, 0);
  40 
  41         /*
  42          * If a partition is mounted more than once, only the first
  43          * FREEZE/THAW can succeed and the later ones will get
  44          * EBUSY/EINVAL respectively: there could be 2 cases:
  45          * 1) a user may mount the same partition to different directories
  46          *  by mistake or on purpose;
  47          * 2) The subvolume of btrfs appears to have the same partition
  48          * mounted more than once.
  49          */
  50         if (ret) {
  51                 if ((cmd == FIFREEZE && errno == EBUSY) ||
  52                     (cmd == FITHAW && errno == EINVAL)) {
  53                         close(fd);
  54                         return 0;
  55                 }
  56         }
  57 
  58         close(fd);
  59         return !!ret;
  60 }
  61 
  62 static bool is_dev_loop(const char *blkname)
  63 {
  64         char *buffer;
  65         DIR *dir;
  66         struct dirent *entry;
  67         bool ret = false;
  68 
  69         buffer = malloc(PATH_MAX);
  70         if (!buffer) {
  71                 syslog(LOG_ERR, "Can't allocate memory!");
  72                 exit(1);
  73         }
  74 
  75         snprintf(buffer, PATH_MAX, "%s/loop", blkname);
  76         if (!access(buffer, R_OK | X_OK)) {
  77                 ret = true;
  78                 goto free_buffer;
  79         } else if (errno != ENOENT) {
  80                 syslog(LOG_ERR, "Can't access: %s; error:%d %s!",
  81                        buffer, errno, strerror(errno));
  82         }
  83 
  84         snprintf(buffer, PATH_MAX, "%s/slaves", blkname);
  85         dir = opendir(buffer);
  86         if (!dir) {
  87                 if (errno != ENOENT)
  88                         syslog(LOG_ERR, "Can't opendir: %s; error:%d %s!",
  89                                buffer, errno, strerror(errno));
  90                 goto free_buffer;
  91         }
  92 
  93         while ((entry = readdir(dir)) != NULL) {
  94                 if (strcmp(entry->d_name, ".") == 0 ||
  95                     strcmp(entry->d_name, "..") == 0)
  96                         continue;
  97 
  98                 snprintf(buffer, PATH_MAX, "%s/slaves/%s", blkname,
  99                          entry->d_name);
 100                 if (is_dev_loop(buffer)) {
 101                         ret = true;
 102                         break;
 103                 }
 104         }
 105         closedir(dir);
 106 free_buffer:
 107         free(buffer);
 108         return ret;
 109 }
 110 
 111 static int vss_operate(int operation)
 112 {
 113         char match[] = "/dev/";
 114         FILE *mounts;
 115         struct mntent *ent;
 116         struct stat sb;
 117         char errdir[1024] = {0};
 118         char blkdir[23]; /* /sys/dev/block/XXX:XXX */
 119         unsigned int cmd;
 120         int error = 0, root_seen = 0, save_errno = 0;
 121 
 122         switch (operation) {
 123         case VSS_OP_FREEZE:
 124                 cmd = FIFREEZE;
 125                 break;
 126         case VSS_OP_THAW:
 127                 cmd = FITHAW;
 128                 break;
 129         default:
 130                 return -1;
 131         }
 132 
 133         mounts = setmntent("/proc/mounts", "r");
 134         if (mounts == NULL)
 135                 return -1;
 136 
 137         while ((ent = getmntent(mounts))) {
 138                 if (strncmp(ent->mnt_fsname, match, strlen(match)))
 139                         continue;
 140                 if (stat(ent->mnt_fsname, &sb)) {
 141                         syslog(LOG_ERR, "Can't stat: %s; error:%d %s!",
 142                                ent->mnt_fsname, errno, strerror(errno));
 143                 } else {
 144                         sprintf(blkdir, "/sys/dev/block/%d:%d",
 145                                 major(sb.st_rdev), minor(sb.st_rdev));
 146                         if (is_dev_loop(blkdir))
 147                                 continue;
 148                 }
 149                 if (hasmntopt(ent, MNTOPT_RO) != NULL)
 150                         continue;
 151                 if (strcmp(ent->mnt_type, "vfat") == 0)
 152                         continue;
 153                 if (strcmp(ent->mnt_dir, "/") == 0) {
 154                         root_seen = 1;
 155                         continue;
 156                 }
 157                 error |= vss_do_freeze(ent->mnt_dir, cmd);
 158                 if (error && operation == VSS_OP_FREEZE)
 159                         goto err;
 160         }
 161 
 162         endmntent(mounts);
 163 
 164         if (root_seen) {
 165                 error |= vss_do_freeze("/", cmd);
 166                 if (error && operation == VSS_OP_FREEZE)
 167                         goto err;
 168         }
 169 
 170         goto out;
 171 err:
 172         save_errno = errno;
 173         if (ent) {
 174                 strncpy(errdir, ent->mnt_dir, sizeof(errdir)-1);
 175                 endmntent(mounts);
 176         }
 177         vss_operate(VSS_OP_THAW);
 178         /* Call syslog after we thaw all filesystems */
 179         if (ent)
 180                 syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s",
 181                        errdir, save_errno, strerror(save_errno));
 182         else
 183                 syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno,
 184                        strerror(save_errno));
 185 out:
 186         return error;
 187 }
 188 
 189 void print_usage(char *argv[])
 190 {
 191         fprintf(stderr, "Usage: %s [options]\n"
 192                 "Options are:\n"
 193                 "  -n, --no-daemon        stay in foreground, don't daemonize\n"
 194                 "  -h, --help             print this help\n", argv[0]);
 195 }
 196 
 197 int main(int argc, char *argv[])
 198 {
 199         int vss_fd, len;
 200         int error;
 201         struct pollfd pfd;
 202         int     op;
 203         struct hv_vss_msg vss_msg[1];
 204         int daemonize = 1, long_index = 0, opt;
 205         int in_handshake = 1;
 206         __u32 kernel_modver;
 207 
 208         static struct option long_options[] = {
 209                 {"help",        no_argument,       0,  'h' },
 210                 {"no-daemon",   no_argument,       0,  'n' },
 211                 {0,             0,                 0,  0   }
 212         };
 213 
 214         while ((opt = getopt_long(argc, argv, "hn", long_options,
 215                                   &long_index)) != -1) {
 216                 switch (opt) {
 217                 case 'n':
 218                         daemonize = 0;
 219                         break;
 220                 case 'h':
 221                         print_usage(argv);
 222                         exit(0);
 223                 default:
 224                         print_usage(argv);
 225                         exit(EXIT_FAILURE);
 226                 }
 227         }
 228 
 229         if (daemonize && daemon(1, 0))
 230                 return 1;
 231 
 232         openlog("Hyper-V VSS", 0, LOG_USER);
 233         syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
 234 
 235         vss_fd = open("/dev/vmbus/hv_vss", O_RDWR);
 236         if (vss_fd < 0) {
 237                 syslog(LOG_ERR, "open /dev/vmbus/hv_vss failed; error: %d %s",
 238                        errno, strerror(errno));
 239                 exit(EXIT_FAILURE);
 240         }
 241         /*
 242          * Register ourselves with the kernel.
 243          */
 244         vss_msg->vss_hdr.operation = VSS_OP_REGISTER1;
 245 
 246         len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
 247         if (len < 0) {
 248                 syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
 249                        errno, strerror(errno));
 250                 close(vss_fd);
 251                 exit(EXIT_FAILURE);
 252         }
 253 
 254         pfd.fd = vss_fd;
 255 
 256         while (1) {
 257                 pfd.events = POLLIN;
 258                 pfd.revents = 0;
 259 
 260                 if (poll(&pfd, 1, -1) < 0) {
 261                         syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
 262                         if (errno == EINVAL) {
 263                                 close(vss_fd);
 264                                 exit(EXIT_FAILURE);
 265                         }
 266                         else
 267                                 continue;
 268                 }
 269 
 270                 len = read(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
 271 
 272                 if (in_handshake) {
 273                         if (len != sizeof(kernel_modver)) {
 274                                 syslog(LOG_ERR, "invalid version negotiation");
 275                                 exit(EXIT_FAILURE);
 276                         }
 277                         kernel_modver = *(__u32 *)vss_msg;
 278                         in_handshake = 0;
 279                         syslog(LOG_INFO, "VSS: kernel module version: %d",
 280                                kernel_modver);
 281                         continue;
 282                 }
 283 
 284                 if (len != sizeof(struct hv_vss_msg)) {
 285                         syslog(LOG_ERR, "read failed; error:%d %s",
 286                                errno, strerror(errno));
 287                         close(vss_fd);
 288                         return EXIT_FAILURE;
 289                 }
 290 
 291                 op = vss_msg->vss_hdr.operation;
 292                 error =  HV_S_OK;
 293 
 294                 switch (op) {
 295                 case VSS_OP_FREEZE:
 296                 case VSS_OP_THAW:
 297                         error = vss_operate(op);
 298                         syslog(LOG_INFO, "VSS: op=%s: %s\n",
 299                                 op == VSS_OP_FREEZE ? "FREEZE" : "THAW",
 300                                 error ? "failed" : "succeeded");
 301 
 302                         if (error) {
 303                                 error = HV_E_FAIL;
 304                                 syslog(LOG_ERR, "op=%d failed!", op);
 305                                 syslog(LOG_ERR, "report it with these files:");
 306                                 syslog(LOG_ERR, "/etc/fstab and /proc/mounts");
 307                         }
 308                         break;
 309                 case VSS_OP_HOT_BACKUP:
 310                         syslog(LOG_INFO, "VSS: op=CHECK HOT BACKUP\n");
 311                         break;
 312                 default:
 313                         syslog(LOG_ERR, "Illegal op:%d\n", op);
 314                 }
 315                 vss_msg->error = error;
 316                 len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
 317                 if (len != sizeof(struct hv_vss_msg)) {
 318                         syslog(LOG_ERR, "write failed; error: %d %s", errno,
 319                                strerror(errno));
 320 
 321                         if (op == VSS_OP_FREEZE)
 322                                 vss_operate(VSS_OP_THAW);
 323                 }
 324         }
 325 
 326         close(vss_fd);
 327         exit(0);
 328 }

/* [<][>][^][v][top][bottom][index][help] */