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
  31/* Don't use syslog() in the function since that can cause write to disk */
  32static 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
  62static 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);
 106free_buffer:
 107        free(buffer);
 108        return ret;
 109}
 110
 111static 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;
 171err:
 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));
 185out:
 186        return error;
 187}
 188
 189void 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
 197int 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}
 329