hv_vss_daemon.c 7.7 KB

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