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