1
2
3
4
5
6
7
8
9
10
11
12
13
14#include "qemu/osdep.h"
15#include "qapi/error.h"
16#include "commands-common.h"
17#include "cutils.h"
18#include <mntent.h>
19#include <sys/ioctl.h>
20
21#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
22static int dev_major_minor(const char *devpath,
23 unsigned int *devmajor, unsigned int *devminor)
24{
25 struct stat st;
26
27 *devmajor = 0;
28 *devminor = 0;
29
30 if (stat(devpath, &st) < 0) {
31 slog("failed to stat device file '%s': %s", devpath, strerror(errno));
32 return -1;
33 }
34 if (S_ISDIR(st.st_mode)) {
35
36 return -2;
37 }
38 if (S_ISBLK(st.st_mode)) {
39 *devmajor = major(st.st_rdev);
40 *devminor = minor(st.st_rdev);
41 return 0;
42 }
43 return -1;
44}
45
46static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp)
47{
48 struct mntent *ment;
49 FsMount *mount;
50 char const *mtab = "/proc/self/mounts";
51 FILE *fp;
52 unsigned int devmajor, devminor;
53
54 fp = setmntent(mtab, "r");
55 if (!fp) {
56 error_setg(errp, "failed to open mtab file: '%s'", mtab);
57 return false;
58 }
59
60 while ((ment = getmntent(fp))) {
61
62
63
64
65
66
67 if ((ment->mnt_fsname[0] != '/') ||
68 (strcmp(ment->mnt_type, "smbfs") == 0) ||
69 (strcmp(ment->mnt_type, "cifs") == 0)) {
70 continue;
71 }
72 if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) == -2) {
73
74 continue;
75 }
76
77 mount = g_new0(FsMount, 1);
78 mount->dirname = g_strdup(ment->mnt_dir);
79 mount->devtype = g_strdup(ment->mnt_type);
80 mount->devmajor = devmajor;
81 mount->devminor = devminor;
82
83 QTAILQ_INSERT_TAIL(mounts, mount, next);
84 }
85
86 endmntent(fp);
87 return true;
88}
89
90static void decode_mntname(char *name, int len)
91{
92 int i, j = 0;
93 for (i = 0; i <= len; i++) {
94 if (name[i] != '\\') {
95 name[j++] = name[i];
96 } else if (name[i + 1] == '\\') {
97 name[j++] = '\\';
98 i++;
99 } else if (name[i + 1] >= '0' && name[i + 1] <= '3' &&
100 name[i + 2] >= '0' && name[i + 2] <= '7' &&
101 name[i + 3] >= '0' && name[i + 3] <= '7') {
102 name[j++] = (name[i + 1] - '0') * 64 +
103 (name[i + 2] - '0') * 8 +
104 (name[i + 3] - '0');
105 i += 3;
106 } else {
107 name[j++] = name[i];
108 }
109 }
110}
111
112
113
114
115bool build_fs_mount_list(FsMountList *mounts, Error **errp)
116{
117 FsMount *mount;
118 char const *mountinfo = "/proc/self/mountinfo";
119 FILE *fp;
120 char *line = NULL, *dash;
121 size_t n;
122 char check;
123 unsigned int devmajor, devminor;
124 int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e;
125
126 fp = fopen(mountinfo, "r");
127 if (!fp) {
128 return build_fs_mount_list_from_mtab(mounts, errp);
129 }
130
131 while (getline(&line, &n, fp) != -1) {
132 ret = sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c",
133 &devmajor, &devminor, &dir_s, &dir_e, &check);
134 if (ret < 3) {
135 continue;
136 }
137 dash = strstr(line + dir_e, " - ");
138 if (!dash) {
139 continue;
140 }
141 ret = sscanf(dash, " - %n%*s%n %n%*s%n%c",
142 &type_s, &type_e, &dev_s, &dev_e, &check);
143 if (ret < 1) {
144 continue;
145 }
146 line[dir_e] = 0;
147 dash[type_e] = 0;
148 dash[dev_e] = 0;
149 decode_mntname(line + dir_s, dir_e - dir_s);
150 decode_mntname(dash + dev_s, dev_e - dev_s);
151 if (devmajor == 0) {
152
153 if (strcmp("btrfs", dash + type_s) != 0 ||
154 dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) {
155 continue;
156 }
157 }
158
159 mount = g_new0(FsMount, 1);
160 mount->dirname = g_strdup(line + dir_s);
161 mount->devtype = g_strdup(dash + type_s);
162 mount->devmajor = devmajor;
163 mount->devminor = devminor;
164
165 QTAILQ_INSERT_TAIL(mounts, mount, next);
166 }
167 free(line);
168
169 fclose(fp);
170 return true;
171}
172#endif
173
174#ifdef CONFIG_FSFREEZE
175
176
177
178
179int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints,
180 strList *mountpoints,
181 FsMountList mounts,
182 Error **errp)
183{
184 struct FsMount *mount;
185 strList *list;
186 int fd, ret, i = 0;
187
188 QTAILQ_FOREACH_REVERSE(mount, &mounts, next) {
189
190
191 if (has_mountpoints) {
192 for (list = mountpoints; list; list = list->next) {
193 if (strcmp(list->value, mount->dirname) == 0) {
194 break;
195 }
196 }
197 if (!list) {
198 continue;
199 }
200 }
201
202 fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
203 if (fd == -1) {
204 error_setg_errno(errp, errno, "failed to open %s", mount->dirname);
205 return -1;
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220 ret = ioctl(fd, FIFREEZE);
221 if (ret == -1) {
222 if (errno != EOPNOTSUPP && errno != EBUSY) {
223 error_setg_errno(errp, errno, "failed to freeze %s",
224 mount->dirname);
225 close(fd);
226 return -1;
227 }
228 } else {
229 i++;
230 }
231 close(fd);
232 }
233 return i;
234}
235
236int qmp_guest_fsfreeze_do_thaw(Error **errp)
237{
238 int ret;
239 FsMountList mounts;
240 FsMount *mount;
241 int fd, i = 0, logged;
242 Error *local_err = NULL;
243
244 QTAILQ_INIT(&mounts);
245 if (!build_fs_mount_list(&mounts, &local_err)) {
246 error_propagate(errp, local_err);
247 return -1;
248 }
249
250 QTAILQ_FOREACH(mount, &mounts, next) {
251 logged = false;
252 fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
253 if (fd == -1) {
254 continue;
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 do {
273 ret = ioctl(fd, FITHAW);
274 if (ret == 0 && !logged) {
275 i++;
276 logged = true;
277 }
278 } while (ret == 0);
279 close(fd);
280 }
281
282 free_fs_mount_list(&mounts);
283
284 return i;
285}
286#endif
287