diff options
author | K. Y. Srinivasan <kys@microsoft.com> | 2014-02-16 14:34:30 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-18 13:53:48 -0500 |
commit | 01325476d6e46185031be4a9bc6443832dbc807c (patch) | |
tree | 0c173657fa89e7612b7ccc643e3b443f2123189f /tools/hv | |
parent | e0270addae23aef8401b1150cbca20b8fb373cd0 (diff) |
Drivers: hv: Implement the file copy service
Implement the file copy service for Linux guests on Hyper-V. This permits the
host to copy a file (over VMBUS) into the guest. This facility is part of
"guest integration services" supported on the Windows platform.
Here is a link that provides additional details on this functionality:
http://technet.microsoft.com/en-us/library/dn464282.aspx
In V1 version of the patch I have addressed comments from
Olaf Hering <olaf@aepfle.de> and Dan Carpenter <dan.carpenter@oracle.com>
In V2 version of this patch I did some minor cleanup (making some globals
static). In V4 version of the patch I have addressed all of Olaf's
most recent set of comments/concerns.
In V5 version of the patch I had addressed Greg's most recent comments.
I would like to thank Greg for suggesting that I use misc device; it has
significantly simplified the code.
In V6 version of the patch I have cleaned up error message based on Olaf's
comments. I have also rebased the patch based on the current tip.
In this version of the patch, I have addressed the latest comments from Greg.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'tools/hv')
-rw-r--r-- | tools/hv/hv_fcopy_daemon.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c new file mode 100644 index 000000000000..4ecc4fd0bc1b --- /dev/null +++ b/tools/hv/hv_fcopy_daemon.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * An implementation of host to guest copy functionality for Linux. | ||
3 | * | ||
4 | * Copyright (C) 2014, Microsoft, Inc. | ||
5 | * | ||
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 | #include <sys/types.h> | ||
21 | #include <sys/socket.h> | ||
22 | #include <sys/poll.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/kdev_t.h> | ||
25 | #include <stdio.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <unistd.h> | ||
28 | #include <string.h> | ||
29 | #include <ctype.h> | ||
30 | #include <errno.h> | ||
31 | #include <linux/hyperv.h> | ||
32 | #include <syslog.h> | ||
33 | #include <sys/stat.h> | ||
34 | #include <fcntl.h> | ||
35 | #include <dirent.h> | ||
36 | |||
37 | static int target_fd; | ||
38 | static char target_fname[W_MAX_PATH]; | ||
39 | |||
40 | static int hv_start_fcopy(struct hv_start_fcopy *smsg) | ||
41 | { | ||
42 | int error = HV_E_FAIL; | ||
43 | char *q, *p; | ||
44 | |||
45 | /* | ||
46 | * If possile append a path seperator to the path. | ||
47 | */ | ||
48 | if (strlen((char *)smsg->path_name) < (W_MAX_PATH - 2)) | ||
49 | strcat((char *)smsg->path_name, "/"); | ||
50 | |||
51 | p = (char *)smsg->path_name; | ||
52 | snprintf(target_fname, sizeof(target_fname), "%s/%s", | ||
53 | (char *)smsg->path_name, smsg->file_name); | ||
54 | |||
55 | syslog(LOG_INFO, "Target file name: %s", target_fname); | ||
56 | /* | ||
57 | * Check to see if the path is already in place; if not, | ||
58 | * create if required. | ||
59 | */ | ||
60 | while ((q = strchr(p, '/')) != NULL) { | ||
61 | if (q == p) { | ||
62 | p++; | ||
63 | continue; | ||
64 | } | ||
65 | *q = '\0'; | ||
66 | if (access((char *)smsg->path_name, F_OK)) { | ||
67 | if (smsg->copy_flags & CREATE_PATH) { | ||
68 | if (mkdir((char *)smsg->path_name, 0755)) { | ||
69 | syslog(LOG_ERR, "Failed to create %s", | ||
70 | (char *)smsg->path_name); | ||
71 | goto done; | ||
72 | } | ||
73 | } else { | ||
74 | syslog(LOG_ERR, "Invalid path: %s", | ||
75 | (char *)smsg->path_name); | ||
76 | goto done; | ||
77 | } | ||
78 | } | ||
79 | p = q + 1; | ||
80 | *q = '/'; | ||
81 | } | ||
82 | |||
83 | if (!access(target_fname, F_OK)) { | ||
84 | syslog(LOG_INFO, "File: %s exists", target_fname); | ||
85 | if (!smsg->copy_flags & OVER_WRITE) | ||
86 | goto done; | ||
87 | } | ||
88 | |||
89 | target_fd = open(target_fname, O_RDWR | O_CREAT | O_CLOEXEC, 0744); | ||
90 | if (target_fd == -1) { | ||
91 | syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); | ||
92 | goto done; | ||
93 | } | ||
94 | |||
95 | error = 0; | ||
96 | done: | ||
97 | return error; | ||
98 | } | ||
99 | |||
100 | static int hv_copy_data(struct hv_do_fcopy *cpmsg) | ||
101 | { | ||
102 | ssize_t bytes_written; | ||
103 | |||
104 | bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size, | ||
105 | cpmsg->offset); | ||
106 | |||
107 | if (bytes_written != cpmsg->size) | ||
108 | return HV_E_FAIL; | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int hv_copy_finished(void) | ||
114 | { | ||
115 | close(target_fd); | ||
116 | return 0; | ||
117 | } | ||
118 | static int hv_copy_cancel(void) | ||
119 | { | ||
120 | close(target_fd); | ||
121 | unlink(target_fname); | ||
122 | return 0; | ||
123 | |||
124 | } | ||
125 | |||
126 | int main(void) | ||
127 | { | ||
128 | int fd, fcopy_fd, len; | ||
129 | int error; | ||
130 | int version = FCOPY_CURRENT_VERSION; | ||
131 | char *buffer[4096 * 2]; | ||
132 | struct hv_fcopy_hdr *in_msg; | ||
133 | |||
134 | if (daemon(1, 0)) { | ||
135 | syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); | ||
136 | exit(EXIT_FAILURE); | ||
137 | } | ||
138 | |||
139 | openlog("HV_FCOPY", 0, LOG_USER); | ||
140 | syslog(LOG_INFO, "HV_FCOPY starting; pid is:%d", getpid()); | ||
141 | |||
142 | fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR); | ||
143 | |||
144 | if (fcopy_fd < 0) { | ||
145 | syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s", | ||
146 | errno, strerror(errno)); | ||
147 | exit(EXIT_FAILURE); | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Register with the kernel. | ||
152 | */ | ||
153 | if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) { | ||
154 | syslog(LOG_ERR, "Registration failed: %s", strerror(errno)); | ||
155 | exit(EXIT_FAILURE); | ||
156 | } | ||
157 | |||
158 | while (1) { | ||
159 | /* | ||
160 | * In this loop we process fcopy messages after the | ||
161 | * handshake is complete. | ||
162 | */ | ||
163 | len = pread(fcopy_fd, buffer, (4096 * 2), 0); | ||
164 | if (len < 0) { | ||
165 | syslog(LOG_ERR, "pread failed: %s", strerror(errno)); | ||
166 | exit(EXIT_FAILURE); | ||
167 | } | ||
168 | in_msg = (struct hv_fcopy_hdr *)buffer; | ||
169 | |||
170 | switch (in_msg->operation) { | ||
171 | case START_FILE_COPY: | ||
172 | error = hv_start_fcopy((struct hv_start_fcopy *)in_msg); | ||
173 | break; | ||
174 | case WRITE_TO_FILE: | ||
175 | error = hv_copy_data((struct hv_do_fcopy *)in_msg); | ||
176 | break; | ||
177 | case COMPLETE_FCOPY: | ||
178 | error = hv_copy_finished(); | ||
179 | break; | ||
180 | case CANCEL_FCOPY: | ||
181 | error = hv_copy_cancel(); | ||
182 | break; | ||
183 | |||
184 | default: | ||
185 | syslog(LOG_ERR, "Unknown operation: %d", | ||
186 | in_msg->operation); | ||
187 | |||
188 | } | ||
189 | |||
190 | if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) { | ||
191 | syslog(LOG_ERR, "pwrite failed: %s", strerror(errno)); | ||
192 | exit(EXIT_FAILURE); | ||
193 | } | ||
194 | } | ||
195 | } | ||