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