diff options
Diffstat (limited to 'tools/hv')
| -rw-r--r-- | tools/hv/Makefile | 13 | ||||
| -rw-r--r-- | tools/hv/hv_fcopy_daemon.c | 197 | ||||
| -rw-r--r-- | tools/hv/hv_kvp_daemon.c | 30 | ||||
| -rw-r--r-- | tools/hv/hv_vss_daemon.c | 11 | 
4 files changed, 231 insertions, 20 deletions
diff --git a/tools/hv/Makefile b/tools/hv/Makefile new file mode 100644 index 00000000000..bd22f786a60 --- /dev/null +++ b/tools/hv/Makefile @@ -0,0 +1,13 @@ +# Makefile for Hyper-V tools + +CC = $(CROSS_COMPILE)gcc +PTHREAD_LIBS = -lpthread +WARNINGS = -Wall -Wextra +CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) + +all: hv_kvp_daemon hv_vss_daemon +%: %.c +	$(CC) $(CFLAGS) -o $@ $^ + +clean: +	$(RM) hv_kvp_daemon hv_vss_daemon diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c new file mode 100644 index 00000000000..fba1c75aa48 --- /dev/null +++ b/tools/hv/hv_fcopy_daemon.c @@ -0,0 +1,197 @@ +/* + * An implementation of host to guest copy functionality for Linux. + * + * Copyright (C) 2014, 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> +#include <linux/types.h> +#include <linux/kdev_t.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <linux/hyperv.h> +#include <syslog.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> + +static int target_fd; +static char target_fname[W_MAX_PATH]; + +static int hv_start_fcopy(struct hv_start_fcopy *smsg) +{ +	int error = HV_E_FAIL; +	char *q, *p; + +	/* +	 * If possile append a path seperator to the path. +	 */ +	if (strlen((char *)smsg->path_name) < (W_MAX_PATH - 2)) +		strcat((char *)smsg->path_name, "/"); + +	p = (char *)smsg->path_name; +	snprintf(target_fname, sizeof(target_fname), "%s/%s", +		(char *)smsg->path_name, smsg->file_name); + +	syslog(LOG_INFO, "Target file name: %s", target_fname); +	/* +	 * Check to see if the path is already in place; if not, +	 * create if required. +	 */ +	while ((q = strchr(p, '/')) != NULL) { +		if (q == p) { +			p++; +			continue; +		} +		*q = '\0'; +		if (access((char *)smsg->path_name, F_OK)) { +			if (smsg->copy_flags & CREATE_PATH) { +				if (mkdir((char *)smsg->path_name, 0755)) { +					syslog(LOG_ERR, "Failed to create %s", +						(char *)smsg->path_name); +					goto done; +				} +			} else { +				syslog(LOG_ERR, "Invalid path: %s", +					(char *)smsg->path_name); +				goto done; +			} +		} +		p = q + 1; +		*q = '/'; +	} + +	if (!access(target_fname, F_OK)) { +		syslog(LOG_INFO, "File: %s exists", target_fname); +		if (!(smsg->copy_flags & OVER_WRITE)) { +			error = HV_ERROR_ALREADY_EXISTS; +			goto done; +		} +	} + +	target_fd = open(target_fname, O_RDWR | O_CREAT | O_CLOEXEC, 0744); +	if (target_fd == -1) { +		syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); +		goto done; +	} + +	error = 0; +done: +	return error; +} + +static int hv_copy_data(struct hv_do_fcopy *cpmsg) +{ +	ssize_t bytes_written; + +	bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size, +				cpmsg->offset); + +	if (bytes_written != cpmsg->size) +		return HV_E_FAIL; + +	return 0; +} + +static int hv_copy_finished(void) +{ +	close(target_fd); +	return 0; +} +static int hv_copy_cancel(void) +{ +	close(target_fd); +	unlink(target_fname); +	return 0; + +} + +int main(void) +{ +	int fd, fcopy_fd, len; +	int error; +	int version = FCOPY_CURRENT_VERSION; +	char *buffer[4096 * 2]; +	struct hv_fcopy_hdr *in_msg; + +	if (daemon(1, 0)) { +		syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); +		exit(EXIT_FAILURE); +	} + +	openlog("HV_FCOPY", 0, LOG_USER); +	syslog(LOG_INFO, "HV_FCOPY starting; pid is:%d", getpid()); + +	fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR); + +	if (fcopy_fd < 0) { +		syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s", +			errno, strerror(errno)); +		exit(EXIT_FAILURE); +	} + +	/* +	 * Register with the kernel. +	 */ +	if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) { +		syslog(LOG_ERR, "Registration failed: %s", strerror(errno)); +		exit(EXIT_FAILURE); +	} + +	while (1) { +		/* +		 * In this loop we process fcopy messages after the +		 * handshake is complete. +		 */ +		len = pread(fcopy_fd, buffer, (4096 * 2), 0); +		if (len < 0) { +			syslog(LOG_ERR, "pread failed: %s", strerror(errno)); +			exit(EXIT_FAILURE); +		} +		in_msg = (struct hv_fcopy_hdr *)buffer; + +		switch (in_msg->operation) { +		case START_FILE_COPY: +			error = hv_start_fcopy((struct hv_start_fcopy *)in_msg); +			break; +		case WRITE_TO_FILE: +			error = hv_copy_data((struct hv_do_fcopy *)in_msg); +			break; +		case COMPLETE_FCOPY: +			error = hv_copy_finished(); +			break; +		case CANCEL_FCOPY: +			error = hv_copy_cancel(); +			break; + +		default: +			syslog(LOG_ERR, "Unknown operation: %d", +				in_msg->operation); + +		} + +		if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) { +			syslog(LOG_ERR, "pwrite failed: %s", strerror(errno)); +			exit(EXIT_FAILURE); +		} +	} +} diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 8fd9ec66121..4088b816a3e 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -26,7 +26,6 @@  #include <sys/socket.h>  #include <sys/poll.h>  #include <sys/utsname.h> -#include <linux/types.h>  #include <stdio.h>  #include <stdlib.h>  #include <unistd.h> @@ -89,6 +88,7 @@ static char *processor_arch;  static char *os_build;  static char *os_version;  static char *lic_version = "Unknown version"; +static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];  static struct utsname uts_buf;  /* @@ -1367,7 +1367,7 @@ setval_error:  } -static int +static void  kvp_get_domain_name(char *buffer, int length)  {  	struct addrinfo	hints, *info ; @@ -1381,12 +1381,12 @@ kvp_get_domain_name(char *buffer, int length)  	error = getaddrinfo(buffer, NULL, &hints, &info);  	if (error != 0) { -		strcpy(buffer, "getaddrinfo failed\n"); -		return error; +		snprintf(buffer, length, "getaddrinfo failed: 0x%x %s", +			error, gai_strerror(error)); +		return;  	} -	strcpy(buffer, info->ai_canonname); +	snprintf(buffer, length, "%s", info->ai_canonname);  	freeaddrinfo(info); -	return error;  }  static int @@ -1433,7 +1433,6 @@ int main(void)  	int	pool;  	char	*if_name;  	struct hv_kvp_ipaddr_value *kvp_ip_val; -	char *kvp_send_buffer;  	char *kvp_recv_buffer;  	size_t kvp_recv_buffer_len; @@ -1442,17 +1441,21 @@ int main(void)  	openlog("KVP", 0, LOG_USER);  	syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); -	kvp_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg); -	kvp_send_buffer = calloc(1, kvp_recv_buffer_len); +	kvp_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg);  	kvp_recv_buffer = calloc(1, kvp_recv_buffer_len); -	if (!(kvp_send_buffer && kvp_recv_buffer)) { -		syslog(LOG_ERR, "Failed to allocate netlink buffers"); +	if (!kvp_recv_buffer) { +		syslog(LOG_ERR, "Failed to allocate netlink buffer");  		exit(EXIT_FAILURE);  	}  	/*  	 * Retrieve OS release information.  	 */  	kvp_get_os_info(); +	/* +	 * Cache Fully Qualified Domain Name because getaddrinfo takes an +	 * unpredictable amount of time to finish. +	 */ +	kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));  	if (kvp_file_init()) {  		syslog(LOG_ERR, "Failed to initialize the pools"); @@ -1488,7 +1491,7 @@ int main(void)  	/*  	 * Register ourselves with the kernel.  	 */ -	message = (struct cn_msg *)kvp_send_buffer; +	message = (struct cn_msg *)kvp_recv_buffer;  	message->id.idx = CN_KVP_IDX;  	message->id.val = CN_KVP_VAL; @@ -1671,8 +1674,7 @@ int main(void)  		switch (hv_msg->body.kvp_enum_data.index) {  		case FullyQualifiedDomainName: -			kvp_get_domain_name(key_value, -					HV_KVP_EXCHANGE_MAX_VALUE_SIZE); +			strcpy(key_value, full_domain_name);  			strcpy(key_name, "FullyQualifiedDomainName");  			break;  		case IntegrationServicesVersion: diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 8611962c672..6a213b8cd7b 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -22,7 +22,6 @@  #include <sys/socket.h>  #include <sys/poll.h>  #include <sys/ioctl.h> -#include <linux/types.h>  #include <fcntl.h>  #include <stdio.h>  #include <mntent.h> @@ -88,6 +87,8 @@ static int vss_operate(int operation)  			continue;  		if (strcmp(ent->mnt_type, "iso9660") == 0)  			continue; +		if (strcmp(ent->mnt_type, "vfat") == 0) +			continue;  		if (strcmp(ent->mnt_dir, "/") == 0) {  			root_seen = 1;  			continue; @@ -140,7 +141,6 @@ int main(void)  	struct cn_msg	*incoming_cn_msg;  	int	op;  	struct hv_vss_msg *vss_msg; -	char *vss_send_buffer;  	char *vss_recv_buffer;  	size_t vss_recv_buffer_len; @@ -150,10 +150,9 @@ int main(void)  	openlog("Hyper-V VSS", 0, LOG_USER);  	syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); -	vss_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg); -	vss_send_buffer = calloc(1, vss_recv_buffer_len); +	vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);  	vss_recv_buffer = calloc(1, vss_recv_buffer_len); -	if (!(vss_send_buffer && vss_recv_buffer)) { +	if (!vss_recv_buffer) {  		syslog(LOG_ERR, "Failed to allocate netlink buffers");  		exit(EXIT_FAILURE);  	} @@ -185,7 +184,7 @@ int main(void)  	/*  	 * Register ourselves with the kernel.  	 */ -	message = (struct cn_msg *)vss_send_buffer; +	message = (struct cn_msg *)vss_recv_buffer;  	message->id.idx = CN_VSS_IDX;  	message->id.val = CN_VSS_VAL;  	message->ack = 0;  | 
