| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * An implementation of the host initiated guest snapshot for Hyper-V. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2013, Microsoft, Inc. | 
					
						
							|  |  |  |  * Author : K. Y. Srinivasan <kys@microsoft.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms of the GNU General Public License version 2 as published | 
					
						
							|  |  |  |  * by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, but | 
					
						
							|  |  |  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 
					
						
							|  |  |  |  * NON INFRINGEMENT.  See the GNU General Public License for more | 
					
						
							|  |  |  |  * details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <sys/types.h>
 | 
					
						
							|  |  |  | #include <sys/socket.h>
 | 
					
						
							|  |  |  | #include <sys/poll.h>
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:52 -07:00
										 |  |  | #include <sys/ioctl.h>
 | 
					
						
							|  |  |  | #include <fcntl.h>
 | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | #include <mntent.h>
 | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <ctype.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <arpa/inet.h>
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:52 -07:00
										 |  |  | #include <linux/fs.h>
 | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | #include <linux/connector.h>
 | 
					
						
							|  |  |  | #include <linux/hyperv.h>
 | 
					
						
							|  |  |  | #include <linux/netlink.h>
 | 
					
						
							|  |  |  | #include <syslog.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct sockaddr_nl addr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef SOL_NETLINK
 | 
					
						
							|  |  |  | #define SOL_NETLINK 270
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:52 -07:00
										 |  |  | static int vss_do_freeze(char *dir, unsigned int cmd, char *fs_op) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret, fd = open(dir, O_RDONLY); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (fd < 0) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	ret = ioctl(fd, cmd, 0); | 
					
						
							|  |  |  | 	syslog(LOG_INFO, "VSS: %s of %s: %s\n", fs_op, dir, strerror(errno)); | 
					
						
							|  |  |  | 	close(fd); | 
					
						
							|  |  |  | 	return !!ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | static int vss_operate(int operation) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char *fs_op; | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | 	char match[] = "/dev/"; | 
					
						
							|  |  |  | 	FILE *mounts; | 
					
						
							|  |  |  | 	struct mntent *ent; | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:52 -07:00
										 |  |  | 	unsigned int cmd; | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | 	int error = 0, root_seen = 0; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (operation) { | 
					
						
							|  |  |  | 	case VSS_OP_FREEZE: | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:52 -07:00
										 |  |  | 		cmd = FIFREEZE; | 
					
						
							|  |  |  | 		fs_op = "freeze"; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case VSS_OP_THAW: | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:52 -07:00
										 |  |  | 		cmd = FITHAW; | 
					
						
							|  |  |  | 		fs_op = "thaw"; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:48 -07:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return -1; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | 	mounts = setmntent("/proc/mounts", "r"); | 
					
						
							|  |  |  | 	if (mounts == NULL) | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:48 -07:00
										 |  |  | 		return -1; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:54 -07:00
										 |  |  | 	while ((ent = getmntent(mounts))) { | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | 		if (strncmp(ent->mnt_fsname, match, strlen(match))) | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:53 -07:00
										 |  |  | 		if (strcmp(ent->mnt_type, "iso9660") == 0) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2014-02-12 08:40:22 -08:00
										 |  |  | 		if (strcmp(ent->mnt_type, "vfat") == 0) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | 		if (strcmp(ent->mnt_dir, "/") == 0) { | 
					
						
							|  |  |  | 			root_seen = 1; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:52 -07:00
										 |  |  | 		error |= vss_do_freeze(ent->mnt_dir, cmd, fs_op); | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | 	endmntent(mounts); | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | 	if (root_seen) { | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:52 -07:00
										 |  |  | 		error |= vss_do_freeze("/", cmd, fs_op); | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:51 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int netlink_send(int fd, struct cn_msg *msg) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-07 15:45:12 +02:00
										 |  |  | 	struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE }; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 	unsigned int size; | 
					
						
							|  |  |  | 	struct msghdr message; | 
					
						
							|  |  |  | 	struct iovec iov[2]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-07 15:07:21 +02:00
										 |  |  | 	size = sizeof(struct cn_msg) + msg->len; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-07 15:45:12 +02:00
										 |  |  | 	nlh.nlmsg_pid = getpid(); | 
					
						
							|  |  |  | 	nlh.nlmsg_len = NLMSG_LENGTH(size); | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-07 15:45:12 +02:00
										 |  |  | 	iov[0].iov_base = &nlh; | 
					
						
							|  |  |  | 	iov[0].iov_len = sizeof(nlh); | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	iov[1].iov_base = msg; | 
					
						
							|  |  |  | 	iov[1].iov_len = size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(&message, 0, sizeof(message)); | 
					
						
							|  |  |  | 	message.msg_name = &addr; | 
					
						
							|  |  |  | 	message.msg_namelen = sizeof(addr); | 
					
						
							|  |  |  | 	message.msg_iov = iov; | 
					
						
							|  |  |  | 	message.msg_iovlen = 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return sendmsg(fd, &message, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int main(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int fd, len, nl_group; | 
					
						
							|  |  |  | 	int error; | 
					
						
							|  |  |  | 	struct cn_msg *message; | 
					
						
							|  |  |  | 	struct pollfd pfd; | 
					
						
							|  |  |  | 	struct nlmsghdr *incoming_msg; | 
					
						
							|  |  |  | 	struct cn_msg	*incoming_cn_msg; | 
					
						
							|  |  |  | 	int	op; | 
					
						
							|  |  |  | 	struct hv_vss_msg *vss_msg; | 
					
						
							| 
									
										
										
										
											2013-08-01 14:34:26 +02:00
										 |  |  | 	char *vss_recv_buffer; | 
					
						
							|  |  |  | 	size_t vss_recv_buffer_len; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:48 -07:00
										 |  |  | 	if (daemon(1, 0)) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 	openlog("Hyper-V VSS", 0, LOG_USER); | 
					
						
							|  |  |  | 	syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-06 20:55:38 +02:00
										 |  |  | 	vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg); | 
					
						
							| 
									
										
										
										
											2013-08-01 14:34:26 +02:00
										 |  |  | 	vss_recv_buffer = calloc(1, vss_recv_buffer_len); | 
					
						
							| 
									
										
										
										
											2013-08-06 20:55:38 +02:00
										 |  |  | 	if (!vss_recv_buffer) { | 
					
						
							| 
									
										
										
										
											2013-08-01 14:34:26 +02:00
										 |  |  | 		syslog(LOG_ERR, "Failed to allocate netlink buffers"); | 
					
						
							|  |  |  | 		exit(EXIT_FAILURE); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 	fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); | 
					
						
							|  |  |  | 	if (fd < 0) { | 
					
						
							| 
									
										
										
										
											2013-06-27 13:52:47 +02:00
										 |  |  | 		syslog(LOG_ERR, "netlink socket creation failed; error:%d %s", | 
					
						
							|  |  |  | 				errno, strerror(errno)); | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 		exit(EXIT_FAILURE); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	addr.nl_family = AF_NETLINK; | 
					
						
							|  |  |  | 	addr.nl_pad = 0; | 
					
						
							|  |  |  | 	addr.nl_pid = 0; | 
					
						
							|  |  |  | 	addr.nl_groups = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); | 
					
						
							|  |  |  | 	if (error < 0) { | 
					
						
							| 
									
										
										
										
											2013-06-27 13:52:47 +02:00
										 |  |  | 		syslog(LOG_ERR, "bind failed; error:%d %s", errno, strerror(errno)); | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 		close(fd); | 
					
						
							|  |  |  | 		exit(EXIT_FAILURE); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	nl_group = CN_VSS_IDX; | 
					
						
							| 
									
										
										
										
											2013-06-27 13:52:48 +02:00
										 |  |  | 	if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) { | 
					
						
							|  |  |  | 		syslog(LOG_ERR, "setsockopt failed; error:%d %s", errno, strerror(errno)); | 
					
						
							|  |  |  | 		close(fd); | 
					
						
							|  |  |  | 		exit(EXIT_FAILURE); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Register ourselves with the kernel. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-08-06 20:55:38 +02:00
										 |  |  | 	message = (struct cn_msg *)vss_recv_buffer; | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 	message->id.idx = CN_VSS_IDX; | 
					
						
							|  |  |  | 	message->id.val = CN_VSS_VAL; | 
					
						
							|  |  |  | 	message->ack = 0; | 
					
						
							|  |  |  | 	vss_msg = (struct hv_vss_msg *)message->data; | 
					
						
							|  |  |  | 	vss_msg->vss_hdr.operation = VSS_OP_REGISTER; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	message->len = sizeof(struct hv_vss_msg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	len = netlink_send(fd, message); | 
					
						
							|  |  |  | 	if (len < 0) { | 
					
						
							| 
									
										
										
										
											2013-06-27 13:52:47 +02:00
										 |  |  | 		syslog(LOG_ERR, "netlink_send failed; error:%d %s", errno, strerror(errno)); | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 		close(fd); | 
					
						
							|  |  |  | 		exit(EXIT_FAILURE); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pfd.fd = fd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (1) { | 
					
						
							|  |  |  | 		struct sockaddr *addr_p = (struct sockaddr *) &addr; | 
					
						
							|  |  |  | 		socklen_t addr_l = sizeof(addr); | 
					
						
							|  |  |  | 		pfd.events = POLLIN; | 
					
						
							|  |  |  | 		pfd.revents = 0; | 
					
						
							| 
									
										
										
										
											2013-06-27 13:52:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (poll(&pfd, 1, -1) < 0) { | 
					
						
							|  |  |  | 			syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno)); | 
					
						
							|  |  |  | 			if (errno == EINVAL) { | 
					
						
							|  |  |  | 				close(fd); | 
					
						
							|  |  |  | 				exit(EXIT_FAILURE); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-01 14:34:26 +02:00
										 |  |  | 		len = recvfrom(fd, vss_recv_buffer, vss_recv_buffer_len, 0, | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 				addr_p, &addr_l); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:49 -07:00
										 |  |  | 		if (len < 0) { | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 			syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", | 
					
						
							|  |  |  | 					addr.nl_pid, errno, strerror(errno)); | 
					
						
							|  |  |  | 			close(fd); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:49 -07:00
										 |  |  | 		if (addr.nl_pid) { | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:50 -07:00
										 |  |  | 			syslog(LOG_WARNING, | 
					
						
							|  |  |  | 				"Received packet from untrusted pid:%u", | 
					
						
							|  |  |  | 				addr.nl_pid); | 
					
						
							| 
									
										
										
										
											2013-04-24 07:48:49 -07:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 		incoming_msg = (struct nlmsghdr *)vss_recv_buffer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (incoming_msg->nlmsg_type != NLMSG_DONE) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); | 
					
						
							|  |  |  | 		vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data; | 
					
						
							|  |  |  | 		op = vss_msg->vss_hdr.operation; | 
					
						
							|  |  |  | 		error =  HV_S_OK; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (op) { | 
					
						
							|  |  |  | 		case VSS_OP_FREEZE: | 
					
						
							|  |  |  | 		case VSS_OP_THAW: | 
					
						
							|  |  |  | 			error = vss_operate(op); | 
					
						
							|  |  |  | 			if (error) | 
					
						
							|  |  |  | 				error = HV_E_FAIL; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			syslog(LOG_ERR, "Illegal op:%d\n", op); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		vss_msg->error = error; | 
					
						
							|  |  |  | 		len = netlink_send(fd, incoming_cn_msg); | 
					
						
							|  |  |  | 		if (len < 0) { | 
					
						
							| 
									
										
										
										
											2013-06-27 13:52:47 +02:00
										 |  |  | 			syslog(LOG_ERR, "net_link send failed; error:%d %s", | 
					
						
							|  |  |  | 					errno, strerror(errno)); | 
					
						
							| 
									
										
										
										
											2013-03-15 12:30:06 -07:00
										 |  |  | 			exit(EXIT_FAILURE); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |