Message ID | 20220929075239.1675374-4-alexander.ivanov@virtuozzo.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | qga: Add FreeBSD support | expand |
On Thu, Sep 29, 2022 at 12:04 PM Alexander Ivanov < alexander.ivanov@virtuozzo.com> wrote: > UFS supports FS freezing through ioctl UFSSUSPEND on /dev/ufssuspend. > Frozen FS can be thawed by closing /dev/ufssuspend file descriptior. > > Use getmntinfo to get a list of mounted FS. > > Signed-off-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com> > lgtm, Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> > --- > qga/commands-bsd.c | 169 +++++++++++++++++++++++ > qga/commands-common.h | 11 ++ > qga/commands-posix.c | 308 ++++++++++++++++++++---------------------- > qga/main.c | 7 +- > qga/meson.build | 3 + > 5 files changed, 334 insertions(+), 164 deletions(-) > create mode 100644 qga/commands-bsd.c > > diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c > new file mode 100644 > index 0000000000..ca06692179 > --- /dev/null > +++ b/qga/commands-bsd.c > @@ -0,0 +1,169 @@ > +/* > + * QEMU Guest Agent BSD-specific command implementations > + * > + * Copyright (c) Virtuozzo International GmbH. > + * > + * Authors: > + * Alexander Ivanov <alexander.ivanov@virtuozzo.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > later. > + * See the COPYING file in the top-level directory. > + */ > + > +#include "qemu/osdep.h" > +#include "qga-qapi-commands.h" > +#include "qapi/qmp/qerror.h" > +#include "qapi/error.h" > +#include "qemu/queue.h" > +#include "commands-common.h" > +#include <sys/ioctl.h> > +#include <sys/param.h> > +#include <sys/ucred.h> > +#include <sys/mount.h> > +#include <paths.h> > + > +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) > +bool build_fs_mount_list(FsMountList *mounts, Error **errp) > +{ > + FsMount *mount; > + struct statfs *mntbuf, *mntp; > + struct stat statbuf; > + int i, count, ret; > + > + count = getmntinfo(&mntbuf, MNT_NOWAIT); > + if (count == 0) { > + error_setg_errno(errp, errno, "getmntinfo failed"); > + return false; > + } > + > + for (i = 0; i < count; i++) { > + mntp = &mntbuf[i]; > + ret = stat(mntp->f_mntonname, &statbuf); > + if (ret != 0) { > + error_setg_errno(errp, errno, "stat failed on %s", > + mntp->f_mntonname); > + return false; > + } > + > + mount = g_new0(FsMount, 1); > + > + mount->dirname = g_strdup(mntp->f_mntonname); > + mount->devtype = g_strdup(mntp->f_fstypename); > + mount->devmajor = major(mount->dev); > + mount->devminor = minor(mount->dev); > + mount->fsid = mntp->f_fsid; > + mount->dev = statbuf.st_dev; > + > + QTAILQ_INSERT_TAIL(mounts, mount, next); > + } > + return true; > +} > +#endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */ > + > +#if defined(CONFIG_FSFREEZE) > +static int ufssuspend_fd = -1; > +static int ufssuspend_cnt; > + > +int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, > + strList *mountpoints, > + FsMountList mounts, > + Error **errp) > +{ > + int ret; > + strList *list; > + struct FsMount *mount; > + > + if (ufssuspend_fd != -1) { > + error_setg(errp, "filesystems have already frozen"); > + return -1; > + } > + > + ufssuspend_cnt = 0; > + ufssuspend_fd = qemu_open(_PATH_UFSSUSPEND, O_RDWR, errp); > + if (ufssuspend_fd == -1) { > + return -1; > + } > + > + QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { > + /* > + * To issue fsfreeze in the reverse order of mounts, check if the > + * mount is listed in the list here > + */ > + if (has_mountpoints) { > + for (list = mountpoints; list; list = list->next) { > + if (g_str_equal(list->value, mount->dirname)) { > + break; > + } > + } > + if (!list) { > + continue; > + } > + } > + > + /* Only UFS supports suspend */ > + if (!g_str_equal(mount->devtype, "ufs")) { > + continue; > + } > + > + ret = ioctl(ufssuspend_fd, UFSSUSPEND, &mount->fsid); > + if (ret == -1) { > + /* > + * ioctl returns EBUSY for all the FS except the first one > + * that was suspended > + */ > + if (errno == EBUSY) { > + continue; > + } > + error_setg_errno(errp, errno, "failed to freeze %s", > + mount->dirname); > + goto error; > + } > + ufssuspend_cnt++; > + } > + return ufssuspend_cnt; > +error: > + close(ufssuspend_fd); > + ufssuspend_fd = -1; > + return -1; > + > +} > + > +/* > + * We don't need to call UFSRESUME ioctl because all the frozen FS > + * are thawed on /dev/ufssuspend closing. > + */ > +int qmp_guest_fsfreeze_do_thaw(Error **errp) > +{ > + int ret = ufssuspend_cnt; > + ufssuspend_cnt = 0; > + if (ufssuspend_fd != -1) { > + close(ufssuspend_fd); > + ufssuspend_fd = -1; > + } > + return ret; > +} > + > +GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) > +{ > + error_setg(errp, QERR_UNSUPPORTED); > + return NULL; > +} > + > +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) > +{ > + error_setg(errp, QERR_UNSUPPORTED); > + return NULL; > +} > + > +GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) > +{ > + error_setg(errp, QERR_UNSUPPORTED); > + return NULL; > +} > + > +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) > +{ > + error_setg(errp, QERR_UNSUPPORTED); > + return NULL; > +} > +#endif /* CONFIG_FSFREEZE */ > diff --git a/qga/commands-common.h b/qga/commands-common.h > index 181fc330aa..2d9878a634 100644 > --- a/qga/commands-common.h > +++ b/qga/commands-common.h > @@ -23,11 +23,22 @@ > #endif > #endif /* __linux__ */ > > +#ifdef __FreeBSD__ > +#include <ufs/ffs/fs.h> > +#ifdef UFSSUSPEND > +#define CONFIG_FSFREEZE > +#endif > +#endif /* __FreeBSD__ */ > + > #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) > typedef struct FsMount { > char *dirname; > char *devtype; > unsigned int devmajor, devminor; > +#if defined(__FreeBSD__) > + dev_t dev; > + fsid_t fsid; > +#endif > QTAILQ_ENTRY(FsMount) next; > } FsMount; > > diff --git a/qga/commands-posix.c b/qga/commands-posix.c > index 9574b83c92..49f9996a9c 100644 > --- a/qga/commands-posix.c > +++ b/qga/commands-posix.c > @@ -33,20 +33,12 @@ > > #if defined(__linux__) > #include <mntent.h> > -#include <linux/fs.h> > #include <sys/statvfs.h> > #include <linux/nvme_ioctl.h> > > #ifdef CONFIG_LIBUDEV > #include <libudev.h> > #endif > - > -#ifdef FIFREEZE > -#define CONFIG_FSFREEZE > -#endif > -#ifdef FITRIM > -#define CONFIG_FSTRIM > -#endif > #endif > > #ifdef __FreeBSD__ > @@ -623,9 +615,6 @@ void qmp_guest_file_flush(int64_t handle, Error **errp) > } > } > > -/* linux-specific implementations. avoid this if at all possible. */ > -#if defined(__linux__) > - > #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) > void free_fs_mount_list(FsMountList *mounts) > { > @@ -644,6 +633,156 @@ void free_fs_mount_list(FsMountList *mounts) > } > #endif > > +#if defined(CONFIG_FSFREEZE) > +typedef enum { > + FSFREEZE_HOOK_THAW = 0, > + FSFREEZE_HOOK_FREEZE, > +} FsfreezeHookArg; > + > +static const char *fsfreeze_hook_arg_string[] = { > + "thaw", > + "freeze", > +}; > + > +static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) > +{ > + int status; > + pid_t pid; > + const char *hook; > + const char *arg_str = fsfreeze_hook_arg_string[arg]; > + Error *local_err = NULL; > + > + hook = ga_fsfreeze_hook(ga_state); > + if (!hook) { > + return; > + } > + if (access(hook, X_OK) != 0) { > + error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", > hook); > + return; > + } > + > + slog("executing fsfreeze hook with arg '%s'", arg_str); > + pid = fork(); > + if (pid == 0) { > + setsid(); > + reopen_fd_to_null(0); > + reopen_fd_to_null(1); > + reopen_fd_to_null(2); > + > + execl(hook, hook, arg_str, NULL); > + _exit(EXIT_FAILURE); > + } else if (pid < 0) { > + error_setg_errno(errp, errno, "failed to create child process"); > + return; > + } > + > + ga_wait_child(pid, &status, &local_err); > + if (local_err) { > + error_propagate(errp, local_err); > + return; > + } > + > + if (!WIFEXITED(status)) { > + error_setg(errp, "fsfreeze hook has terminated abnormally"); > + return; > + } > + > + status = WEXITSTATUS(status); > + if (status) { > + error_setg(errp, "fsfreeze hook has failed with status %d", > status); > + return; > + } > +} > + > +/* > + * Return status of freeze/thaw > + */ > +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) > +{ > + if (ga_is_frozen(ga_state)) { > + return GUEST_FSFREEZE_STATUS_FROZEN; > + } > + > + return GUEST_FSFREEZE_STATUS_THAWED; > +} > + > +int64_t qmp_guest_fsfreeze_freeze(Error **errp) > +{ > + return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); > +} > + > +int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, > + strList *mountpoints, > + Error **errp) > +{ > + int ret; > + FsMountList mounts; > + Error *local_err = NULL; > + > + slog("guest-fsfreeze called"); > + > + execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); > + if (local_err) { > + error_propagate(errp, local_err); > + return -1; > + } > + > + QTAILQ_INIT(&mounts); > + if (!build_fs_mount_list(&mounts, &local_err)) { > + error_propagate(errp, local_err); > + return -1; > + } > + > + /* cannot risk guest agent blocking itself on a write in this state */ > + ga_set_frozen(ga_state); > + > + ret = qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints, > + mounts, errp); > + > + free_fs_mount_list(&mounts); > + /* We may not issue any FIFREEZE here. > + * Just unset ga_state here and ready for the next call. > + */ > + if (ret == 0) { > + ga_unset_frozen(ga_state); > + } else if (ret < 0) { > + qmp_guest_fsfreeze_thaw(NULL); > + } > + return ret; > +} > + > +int64_t qmp_guest_fsfreeze_thaw(Error **errp) > +{ > + int ret; > + > + ret = qmp_guest_fsfreeze_do_thaw(errp); > + if (ret >= 0) { > + ga_unset_frozen(ga_state); > + execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); > + } else { > + ret = 0; > + } > + > + return ret; > +} > + > +static void guest_fsfreeze_cleanup(void) > +{ > + Error *err = NULL; > + > + if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { > + qmp_guest_fsfreeze_thaw(&err); > + if (err) { > + slog("failed to clean up frozen filesystems: %s", > + error_get_pretty(err)); > + error_free(err); > + } > + } > +} > +#endif > + > +/* linux-specific implementations. avoid this if at all possible. */ > +#if defined(__linux__) > #if defined(CONFIG_FSFREEZE) > > static char *get_pci_driver(char const *syspath, int pathlen, Error > **errp) > @@ -1467,153 +1606,6 @@ GuestFilesystemInfoList > *qmp_guest_get_fsinfo(Error **errp) > free_fs_mount_list(&mounts); > return ret; > } > - > - > -typedef enum { > - FSFREEZE_HOOK_THAW = 0, > - FSFREEZE_HOOK_FREEZE, > -} FsfreezeHookArg; > - > -static const char *fsfreeze_hook_arg_string[] = { > - "thaw", > - "freeze", > -}; > - > -static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) > -{ > - int status; > - pid_t pid; > - const char *hook; > - const char *arg_str = fsfreeze_hook_arg_string[arg]; > - Error *local_err = NULL; > - > - hook = ga_fsfreeze_hook(ga_state); > - if (!hook) { > - return; > - } > - if (access(hook, X_OK) != 0) { > - error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", > hook); > - return; > - } > - > - slog("executing fsfreeze hook with arg '%s'", arg_str); > - pid = fork(); > - if (pid == 0) { > - setsid(); > - reopen_fd_to_null(0); > - reopen_fd_to_null(1); > - reopen_fd_to_null(2); > - > - execl(hook, hook, arg_str, NULL); > - _exit(EXIT_FAILURE); > - } else if (pid < 0) { > - error_setg_errno(errp, errno, "failed to create child process"); > - return; > - } > - > - ga_wait_child(pid, &status, &local_err); > - if (local_err) { > - error_propagate(errp, local_err); > - return; > - } > - > - if (!WIFEXITED(status)) { > - error_setg(errp, "fsfreeze hook has terminated abnormally"); > - return; > - } > - > - status = WEXITSTATUS(status); > - if (status) { > - error_setg(errp, "fsfreeze hook has failed with status %d", > status); > - return; > - } > -} > - > -/* > - * Return status of freeze/thaw > - */ > -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) > -{ > - if (ga_is_frozen(ga_state)) { > - return GUEST_FSFREEZE_STATUS_FROZEN; > - } > - > - return GUEST_FSFREEZE_STATUS_THAWED; > -} > - > -int64_t qmp_guest_fsfreeze_freeze(Error **errp) > -{ > - return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); > -} > - > -int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, > - strList *mountpoints, > - Error **errp) > -{ > - int ret; > - FsMountList mounts; > - Error *local_err = NULL; > - > - slog("guest-fsfreeze called"); > - > - execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); > - if (local_err) { > - error_propagate(errp, local_err); > - return -1; > - } > - > - QTAILQ_INIT(&mounts); > - if (!build_fs_mount_list(&mounts, &local_err)) { > - error_propagate(errp, local_err); > - return -1; > - } > - > - /* cannot risk guest agent blocking itself on a write in this state */ > - ga_set_frozen(ga_state); > - > - ret = qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints, > - mounts, errp); > - > - free_fs_mount_list(&mounts); > - /* We may not issue any FIFREEZE here. > - * Just unset ga_state here and ready for the next call. > - */ > - if (ret == 0) { > - ga_unset_frozen(ga_state); > - } else if (ret < 0) { > - qmp_guest_fsfreeze_thaw(NULL); > - } > - return ret; > -} > - > -int64_t qmp_guest_fsfreeze_thaw(Error **errp) > -{ > - int ret; > - > - ret = qmp_guest_fsfreeze_do_thaw(errp); > - if (ret >= 0) { > - ga_unset_frozen(ga_state); > - execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); > - } else { > - ret = 0; > - } > - > - return ret; > -} > - > -static void guest_fsfreeze_cleanup(void) > -{ > - Error *err = NULL; > - > - if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { > - qmp_guest_fsfreeze_thaw(&err); > - if (err) { > - slog("failed to clean up frozen filesystems: %s", > - error_get_pretty(err)); > - error_free(err); > - } > - } > -} > #endif /* CONFIG_FSFREEZE */ > > #if defined(CONFIG_FSTRIM) > diff --git a/qga/main.c b/qga/main.c > index 0d27c97d38..b3580508fa 100644 > --- a/qga/main.c > +++ b/qga/main.c > @@ -37,12 +37,7 @@ > #include "qga/service-win32.h" > #include "qga/vss-win32.h" > #endif > -#ifdef __linux__ > -#include <linux/fs.h> > -#ifdef FIFREEZE > -#define CONFIG_FSFREEZE > -#endif > -#endif > +#include "commands-common.h" > > #ifndef _WIN32 > #ifdef __FreeBSD__ > diff --git a/qga/meson.build b/qga/meson.build > index 409f49a000..456ba4c29f 100644 > --- a/qga/meson.build > +++ b/qga/meson.build > @@ -75,6 +75,9 @@ qga_ss.add(when: 'CONFIG_POSIX', if_true: files( > qga_ss.add(when: 'CONFIG_LINUX', if_true: files( > 'commands-linux.c', > )) > +qga_ss.add(when: 'CONFIG_BSD', if_true: files( > + 'commands-bsd.c', > +)) > qga_ss.add(when: 'CONFIG_WIN32', if_true: files( > 'channel-win32.c', > 'commands-win32.c', > -- > 2.34.1 > > >
diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c new file mode 100644 index 0000000000..ca06692179 --- /dev/null +++ b/qga/commands-bsd.c @@ -0,0 +1,169 @@ +/* + * QEMU Guest Agent BSD-specific command implementations + * + * Copyright (c) Virtuozzo International GmbH. + * + * Authors: + * Alexander Ivanov <alexander.ivanov@virtuozzo.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qga-qapi-commands.h" +#include "qapi/qmp/qerror.h" +#include "qapi/error.h" +#include "qemu/queue.h" +#include "commands-common.h" +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#include <paths.h> + +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +bool build_fs_mount_list(FsMountList *mounts, Error **errp) +{ + FsMount *mount; + struct statfs *mntbuf, *mntp; + struct stat statbuf; + int i, count, ret; + + count = getmntinfo(&mntbuf, MNT_NOWAIT); + if (count == 0) { + error_setg_errno(errp, errno, "getmntinfo failed"); + return false; + } + + for (i = 0; i < count; i++) { + mntp = &mntbuf[i]; + ret = stat(mntp->f_mntonname, &statbuf); + if (ret != 0) { + error_setg_errno(errp, errno, "stat failed on %s", + mntp->f_mntonname); + return false; + } + + mount = g_new0(FsMount, 1); + + mount->dirname = g_strdup(mntp->f_mntonname); + mount->devtype = g_strdup(mntp->f_fstypename); + mount->devmajor = major(mount->dev); + mount->devminor = minor(mount->dev); + mount->fsid = mntp->f_fsid; + mount->dev = statbuf.st_dev; + + QTAILQ_INSERT_TAIL(mounts, mount, next); + } + return true; +} +#endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */ + +#if defined(CONFIG_FSFREEZE) +static int ufssuspend_fd = -1; +static int ufssuspend_cnt; + +int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, + strList *mountpoints, + FsMountList mounts, + Error **errp) +{ + int ret; + strList *list; + struct FsMount *mount; + + if (ufssuspend_fd != -1) { + error_setg(errp, "filesystems have already frozen"); + return -1; + } + + ufssuspend_cnt = 0; + ufssuspend_fd = qemu_open(_PATH_UFSSUSPEND, O_RDWR, errp); + if (ufssuspend_fd == -1) { + return -1; + } + + QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { + /* + * To issue fsfreeze in the reverse order of mounts, check if the + * mount is listed in the list here + */ + if (has_mountpoints) { + for (list = mountpoints; list; list = list->next) { + if (g_str_equal(list->value, mount->dirname)) { + break; + } + } + if (!list) { + continue; + } + } + + /* Only UFS supports suspend */ + if (!g_str_equal(mount->devtype, "ufs")) { + continue; + } + + ret = ioctl(ufssuspend_fd, UFSSUSPEND, &mount->fsid); + if (ret == -1) { + /* + * ioctl returns EBUSY for all the FS except the first one + * that was suspended + */ + if (errno == EBUSY) { + continue; + } + error_setg_errno(errp, errno, "failed to freeze %s", + mount->dirname); + goto error; + } + ufssuspend_cnt++; + } + return ufssuspend_cnt; +error: + close(ufssuspend_fd); + ufssuspend_fd = -1; + return -1; + +} + +/* + * We don't need to call UFSRESUME ioctl because all the frozen FS + * are thawed on /dev/ufssuspend closing. + */ +int qmp_guest_fsfreeze_do_thaw(Error **errp) +{ + int ret = ufssuspend_cnt; + ufssuspend_cnt = 0; + if (ufssuspend_fd != -1) { + close(ufssuspend_fd); + ufssuspend_fd = -1; + } + return ret; +} + +GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} +#endif /* CONFIG_FSFREEZE */ diff --git a/qga/commands-common.h b/qga/commands-common.h index 181fc330aa..2d9878a634 100644 --- a/qga/commands-common.h +++ b/qga/commands-common.h @@ -23,11 +23,22 @@ #endif #endif /* __linux__ */ +#ifdef __FreeBSD__ +#include <ufs/ffs/fs.h> +#ifdef UFSSUSPEND +#define CONFIG_FSFREEZE +#endif +#endif /* __FreeBSD__ */ + #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) typedef struct FsMount { char *dirname; char *devtype; unsigned int devmajor, devminor; +#if defined(__FreeBSD__) + dev_t dev; + fsid_t fsid; +#endif QTAILQ_ENTRY(FsMount) next; } FsMount; diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9574b83c92..49f9996a9c 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -33,20 +33,12 @@ #if defined(__linux__) #include <mntent.h> -#include <linux/fs.h> #include <sys/statvfs.h> #include <linux/nvme_ioctl.h> #ifdef CONFIG_LIBUDEV #include <libudev.h> #endif - -#ifdef FIFREEZE -#define CONFIG_FSFREEZE -#endif -#ifdef FITRIM -#define CONFIG_FSTRIM -#endif #endif #ifdef __FreeBSD__ @@ -623,9 +615,6 @@ void qmp_guest_file_flush(int64_t handle, Error **errp) } } -/* linux-specific implementations. avoid this if at all possible. */ -#if defined(__linux__) - #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) void free_fs_mount_list(FsMountList *mounts) { @@ -644,6 +633,156 @@ void free_fs_mount_list(FsMountList *mounts) } #endif +#if defined(CONFIG_FSFREEZE) +typedef enum { + FSFREEZE_HOOK_THAW = 0, + FSFREEZE_HOOK_FREEZE, +} FsfreezeHookArg; + +static const char *fsfreeze_hook_arg_string[] = { + "thaw", + "freeze", +}; + +static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) +{ + int status; + pid_t pid; + const char *hook; + const char *arg_str = fsfreeze_hook_arg_string[arg]; + Error *local_err = NULL; + + hook = ga_fsfreeze_hook(ga_state); + if (!hook) { + return; + } + if (access(hook, X_OK) != 0) { + error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook); + return; + } + + slog("executing fsfreeze hook with arg '%s'", arg_str); + pid = fork(); + if (pid == 0) { + setsid(); + reopen_fd_to_null(0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + execl(hook, hook, arg_str, NULL); + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + return; + } + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (!WIFEXITED(status)) { + error_setg(errp, "fsfreeze hook has terminated abnormally"); + return; + } + + status = WEXITSTATUS(status); + if (status) { + error_setg(errp, "fsfreeze hook has failed with status %d", status); + return; + } +} + +/* + * Return status of freeze/thaw + */ +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) +{ + if (ga_is_frozen(ga_state)) { + return GUEST_FSFREEZE_STATUS_FROZEN; + } + + return GUEST_FSFREEZE_STATUS_THAWED; +} + +int64_t qmp_guest_fsfreeze_freeze(Error **errp) +{ + return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); +} + +int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, + strList *mountpoints, + Error **errp) +{ + int ret; + FsMountList mounts; + Error *local_err = NULL; + + slog("guest-fsfreeze called"); + + execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -1; + } + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, &local_err)) { + error_propagate(errp, local_err); + return -1; + } + + /* cannot risk guest agent blocking itself on a write in this state */ + ga_set_frozen(ga_state); + + ret = qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints, + mounts, errp); + + free_fs_mount_list(&mounts); + /* We may not issue any FIFREEZE here. + * Just unset ga_state here and ready for the next call. + */ + if (ret == 0) { + ga_unset_frozen(ga_state); + } else if (ret < 0) { + qmp_guest_fsfreeze_thaw(NULL); + } + return ret; +} + +int64_t qmp_guest_fsfreeze_thaw(Error **errp) +{ + int ret; + + ret = qmp_guest_fsfreeze_do_thaw(errp); + if (ret >= 0) { + ga_unset_frozen(ga_state); + execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); + } else { + ret = 0; + } + + return ret; +} + +static void guest_fsfreeze_cleanup(void) +{ + Error *err = NULL; + + if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); + } + } +} +#endif + +/* linux-specific implementations. avoid this if at all possible. */ +#if defined(__linux__) #if defined(CONFIG_FSFREEZE) static char *get_pci_driver(char const *syspath, int pathlen, Error **errp) @@ -1467,153 +1606,6 @@ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) free_fs_mount_list(&mounts); return ret; } - - -typedef enum { - FSFREEZE_HOOK_THAW = 0, - FSFREEZE_HOOK_FREEZE, -} FsfreezeHookArg; - -static const char *fsfreeze_hook_arg_string[] = { - "thaw", - "freeze", -}; - -static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) -{ - int status; - pid_t pid; - const char *hook; - const char *arg_str = fsfreeze_hook_arg_string[arg]; - Error *local_err = NULL; - - hook = ga_fsfreeze_hook(ga_state); - if (!hook) { - return; - } - if (access(hook, X_OK) != 0) { - error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook); - return; - } - - slog("executing fsfreeze hook with arg '%s'", arg_str); - pid = fork(); - if (pid == 0) { - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - execl(hook, hook, arg_str, NULL); - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "fsfreeze hook has terminated abnormally"); - return; - } - - status = WEXITSTATUS(status); - if (status) { - error_setg(errp, "fsfreeze hook has failed with status %d", status); - return; - } -} - -/* - * Return status of freeze/thaw - */ -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) -{ - if (ga_is_frozen(ga_state)) { - return GUEST_FSFREEZE_STATUS_FROZEN; - } - - return GUEST_FSFREEZE_STATUS_THAWED; -} - -int64_t qmp_guest_fsfreeze_freeze(Error **errp) -{ - return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); -} - -int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, - strList *mountpoints, - Error **errp) -{ - int ret; - FsMountList mounts; - Error *local_err = NULL; - - slog("guest-fsfreeze called"); - - execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return -1; - } - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, &local_err)) { - error_propagate(errp, local_err); - return -1; - } - - /* cannot risk guest agent blocking itself on a write in this state */ - ga_set_frozen(ga_state); - - ret = qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints, - mounts, errp); - - free_fs_mount_list(&mounts); - /* We may not issue any FIFREEZE here. - * Just unset ga_state here and ready for the next call. - */ - if (ret == 0) { - ga_unset_frozen(ga_state); - } else if (ret < 0) { - qmp_guest_fsfreeze_thaw(NULL); - } - return ret; -} - -int64_t qmp_guest_fsfreeze_thaw(Error **errp) -{ - int ret; - - ret = qmp_guest_fsfreeze_do_thaw(errp); - if (ret >= 0) { - ga_unset_frozen(ga_state); - execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); - } else { - ret = 0; - } - - return ret; -} - -static void guest_fsfreeze_cleanup(void) -{ - Error *err = NULL; - - if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { - qmp_guest_fsfreeze_thaw(&err); - if (err) { - slog("failed to clean up frozen filesystems: %s", - error_get_pretty(err)); - error_free(err); - } - } -} #endif /* CONFIG_FSFREEZE */ #if defined(CONFIG_FSTRIM) diff --git a/qga/main.c b/qga/main.c index 0d27c97d38..b3580508fa 100644 --- a/qga/main.c +++ b/qga/main.c @@ -37,12 +37,7 @@ #include "qga/service-win32.h" #include "qga/vss-win32.h" #endif -#ifdef __linux__ -#include <linux/fs.h> -#ifdef FIFREEZE -#define CONFIG_FSFREEZE -#endif -#endif +#include "commands-common.h" #ifndef _WIN32 #ifdef __FreeBSD__ diff --git a/qga/meson.build b/qga/meson.build index 409f49a000..456ba4c29f 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -75,6 +75,9 @@ qga_ss.add(when: 'CONFIG_POSIX', if_true: files( qga_ss.add(when: 'CONFIG_LINUX', if_true: files( 'commands-linux.c', )) +qga_ss.add(when: 'CONFIG_BSD', if_true: files( + 'commands-bsd.c', +)) qga_ss.add(when: 'CONFIG_WIN32', if_true: files( 'channel-win32.c', 'commands-win32.c',
UFS supports FS freezing through ioctl UFSSUSPEND on /dev/ufssuspend. Frozen FS can be thawed by closing /dev/ufssuspend file descriptior. Use getmntinfo to get a list of mounted FS. Signed-off-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com> --- qga/commands-bsd.c | 169 +++++++++++++++++++++++ qga/commands-common.h | 11 ++ qga/commands-posix.c | 308 ++++++++++++++++++++---------------------- qga/main.c | 7 +- qga/meson.build | 3 + 5 files changed, 334 insertions(+), 164 deletions(-) create mode 100644 qga/commands-bsd.c