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