UFS supports FS freezing through ioctl UFSSUSPEND on /dev/ufssuspend. Freezed 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.iva...@virtuozzo.com> --- qga/commands-bsd.c | 109 +++++++++++++++++++++++++++++++++++++++--- qga/commands-common.h | 11 +++++ qga/main.c | 6 +++ 3 files changed, 120 insertions(+), 6 deletions(-) diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c index c1e3ed13e9..5d3f46804a 100644 --- a/qga/commands-bsd.c +++ b/qga/commands-bsd.c @@ -17,28 +17,125 @@ #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) { - error_setg(errp, QERR_UNSUPPORTED); - return false; + 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) { + continue; + } + + 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 #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) { - error_setg(errp, QERR_UNSUPPORTED); - return 0; + 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 (strcmp(list->value, mount->dirname) == 0) { + break; + } + } + if (!list) { + continue; + } + } + + /* Only UFS supports suspend */ + if (strcmp(mount->devtype, "ufs") != 0) { + 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; + } int qmp_guest_fsfreeze_do_thaw(Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); - return 0; + int ret = ufssuspend_cnt; + ufssuspend_cnt = 0; + if (ufssuspend_fd != -1) { + close(ufssuspend_fd); + ufssuspend_fd = -1; + } + return ret; } #endif diff --git a/qga/commands-common.h b/qga/commands-common.h index aa0472ea4c..c3be6db3a9 100644 --- a/qga/commands-common.h +++ b/qga/commands-common.h @@ -41,11 +41,22 @@ void ga_wait_child(pid_t pid, int *status, Error **errp); #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/main.c b/qga/main.c index 22b3c0df11..ab420051fb 100644 --- a/qga/main.c +++ b/qga/main.c @@ -43,6 +43,12 @@ #define CONFIG_FSFREEZE #endif #endif +#ifdef __FreeBSD__ +#include <ufs/ffs/fs.h> +#ifdef UFSSUSPEND +#define CONFIG_FSFREEZE +#endif +#endif #ifndef _WIN32 #ifdef __FreeBSD__ -- 2.34.1