2 * QEMU Guest Agent Linux-specific command implementations
4 * Copyright IBM Corp. 2011
7 * Michael Roth <mdroth@linux.vnet.ibm.com>
8 * Michal Privoznik <mprivozn@redhat.com>
10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
11 * See the COPYING file in the top-level directory.
14 #include "qemu/osdep.h"
15 #include "qapi/error.h"
16 #include "commands-common.h"
19 #include <sys/ioctl.h>
21 #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
22 static int dev_major_minor(const char *devpath
,
23 unsigned int *devmajor
, unsigned int *devminor
)
30 if (stat(devpath
, &st
) < 0) {
31 slog("failed to stat device file '%s': %s", devpath
, strerror(errno
));
34 if (S_ISDIR(st
.st_mode
)) {
35 /* It is bind mount */
38 if (S_ISBLK(st
.st_mode
)) {
39 *devmajor
= major(st
.st_rdev
);
40 *devminor
= minor(st
.st_rdev
);
46 static bool build_fs_mount_list_from_mtab(FsMountList
*mounts
, Error
**errp
)
50 char const *mtab
= "/proc/self/mounts";
52 unsigned int devmajor
, devminor
;
54 fp
= setmntent(mtab
, "r");
56 error_setg(errp
, "failed to open mtab file: '%s'", mtab
);
60 while ((ment
= getmntent(fp
))) {
62 * An entry which device name doesn't start with a '/' is
63 * either a dummy file system or a network file system.
64 * Add special handling for smbfs and cifs as is done by
67 if ((ment
->mnt_fsname
[0] != '/') ||
68 (strcmp(ment
->mnt_type
, "smbfs") == 0) ||
69 (strcmp(ment
->mnt_type
, "cifs") == 0)) {
72 if (dev_major_minor(ment
->mnt_fsname
, &devmajor
, &devminor
) == -2) {
73 /* Skip bind mounts */
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
;
83 QTAILQ_INSERT_TAIL(mounts
, mount
, next
);
90 static void decode_mntname(char *name
, int len
)
93 for (i
= 0; i
<= len
; i
++) {
94 if (name
[i
] != '\\') {
96 } else if (name
[i
+ 1] == '\\') {
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 +
113 * Walk the mount table and build a list of local file systems
115 bool build_fs_mount_list(FsMountList
*mounts
, Error
**errp
)
118 char const *mountinfo
= "/proc/self/mountinfo";
120 char *line
= NULL
, *dash
;
123 unsigned int devmajor
, devminor
;
124 int ret
, dir_s
, dir_e
, type_s
, type_e
, dev_s
, dev_e
;
126 fp
= fopen(mountinfo
, "r");
128 return build_fs_mount_list_from_mtab(mounts
, errp
);
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
);
137 dash
= strstr(line
+ dir_e
, " - ");
141 ret
= sscanf(dash
, " - %n%*s%n %n%*s%n%c",
142 &type_s
, &type_e
, &dev_s
, &dev_e
, &check
);
149 decode_mntname(line
+ dir_s
, dir_e
- dir_s
);
150 decode_mntname(dash
+ dev_s
, dev_e
- dev_s
);
152 /* btrfs reports major number = 0 */
153 if (strcmp("btrfs", dash
+ type_s
) != 0 ||
154 dev_major_minor(dash
+ dev_s
, &devmajor
, &devminor
) < 0) {
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
;
165 QTAILQ_INSERT_TAIL(mounts
, mount
, next
);
172 #endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */
174 #ifdef CONFIG_FSFREEZE
176 * Walk list of mounted file systems in the guest, and freeze the ones which
177 * are real local file systems.
179 int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints
,
180 strList
*mountpoints
,
184 struct FsMount
*mount
;
188 QTAILQ_FOREACH_REVERSE(mount
, &mounts
, next
) {
189 /* To issue fsfreeze in the reverse order of mounts, check if the
190 * mount is listed in the list here */
191 if (has_mountpoints
) {
192 for (list
= mountpoints
; list
; list
= list
->next
) {
193 if (strcmp(list
->value
, mount
->dirname
) == 0) {
202 fd
= qga_open_cloexec(mount
->dirname
, O_RDONLY
, 0);
204 error_setg_errno(errp
, errno
, "failed to open %s", mount
->dirname
);
208 /* we try to cull filesystems we know won't work in advance, but other
209 * filesystems may not implement fsfreeze for less obvious reasons.
210 * these will report EOPNOTSUPP. we simply ignore these when tallying
211 * the number of frozen filesystems.
212 * if a filesystem is mounted more than once (aka bind mount) a
213 * consecutive attempt to freeze an already frozen filesystem will
216 * any other error means a failure to freeze a filesystem we
217 * expect to be freezable, so return an error in those cases
218 * and return system to thawed state.
220 ret
= ioctl(fd
, FIFREEZE
);
222 if (errno
!= EOPNOTSUPP
&& errno
!= EBUSY
) {
223 error_setg_errno(errp
, errno
, "failed to freeze %s",
236 int qmp_guest_fsfreeze_do_thaw(Error
**errp
)
241 int fd
, i
= 0, logged
;
242 Error
*local_err
= NULL
;
244 QTAILQ_INIT(&mounts
);
245 if (!build_fs_mount_list(&mounts
, &local_err
)) {
246 error_propagate(errp
, local_err
);
250 QTAILQ_FOREACH(mount
, &mounts
, next
) {
252 fd
= qga_open_cloexec(mount
->dirname
, O_RDONLY
, 0);
256 /* we have no way of knowing whether a filesystem was actually unfrozen
257 * as a result of a successful call to FITHAW, only that if an error
258 * was returned the filesystem was *not* unfrozen by that particular
261 * since multiple preceding FIFREEZEs require multiple calls to FITHAW
262 * to unfreeze, continuing issuing FITHAW until an error is returned,
263 * in which case either the filesystem is in an unfreezable state, or,
264 * more likely, it was thawed previously (and remains so afterward).
266 * also, since the most recent successful call is the one that did
267 * the actual unfreeze, we can use this to provide an accurate count
268 * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
269 * may * be useful for determining whether a filesystem was unfrozen
270 * during the freeze/thaw phase by a process other than qemu-ga.
273 ret
= ioctl(fd
, FITHAW
);
274 if (ret
== 0 && !logged
) {
282 free_fs_mount_list(&mounts
);
286 #endif /* CONFIG_FSFREEZE */