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 static bool fs_frozen; 32 33 /* Don't use syslog() in the function since that can cause write to disk */ 34 static 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 64 static 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); 108 free_buffer: 109 free(buffer); 110 return ret; 111 } 112 113 static 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; 182 err: 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)); 197 out: 198 return error; 199 } 200 201 void 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 209 int 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 247 reopen_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