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