util: add a helper method for controlling the COW flag on btrfs

btrfs defaults to performing copy-on-write for files. This is often
undesirable for VM images, so we need to be able to control whether this
behaviour is used.

The virFileSetCOW() will allow for this. We use a tristate, since out of
the box, we want the default behaviour attempt to disable cow, but only
on btrfs, silently do nothing on non-btrfs. If someone explicitly asks
to disable/enable cow, then we want to raise a hard error on non-btrfs.

Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2020-07-17 19:16:27 +01:00
parent d3aa28925f
commit 7230bc95aa
3 changed files with 97 additions and 0 deletions

View File

@ -2125,6 +2125,7 @@ virFileRewrite;
virFileRewriteStr;
virFileSanitizePath;
virFileSetACLs;
virFileSetCOW;
virFileSetupDev;
virFileSetXAttr;
virFileTouch;

View File

@ -71,6 +71,7 @@
# endif
# include <sys/ioctl.h>
# include <linux/cdrom.h>
# include <linux/fs.h>
#endif
#if HAVE_LIBATTR
@ -4504,3 +4505,95 @@ virFileDataSync(int fd)
return fdatasync(fd);
#endif
}
/**
* virFileSetCow:
* @path: file or directory to control the COW flag on
* @state: the desired state of the COW flag
*
* When @state is VIR_TRISTATE_BOOL_ABSENT, some helpful
* default logic will be used. Specifically if the filesystem
* containing @path is 'btrfs', then it will attempt to
* disable the COW flag, but errors will be ignored. For
* any other filesystem no change will be made.
*
* When @state is VIR_TRISTATE_BOOL_YES or VIR_TRISTATE_BOOL_NO,
* it will attempt to set the COW flag state to that explicit
* value, and always return an error if it fails. Note this
* means it will always return error if the filesystem is not
* 'btrfs'.
*/
int
virFileSetCOW(const char *path,
virTristateBool state)
{
#if __linux__
int val = 0;
struct statfs buf;
VIR_AUTOCLOSE fd = -1;
VIR_DEBUG("Setting COW flag on '%s' to '%s'",
path, virTristateBoolTypeToString(state));
fd = open(path, O_RDONLY|O_NONBLOCK|O_LARGEFILE);
if (fd < 0) {
virReportSystemError(errno, _("unable to open '%s'"),
path);
return -1;
}
if (fstatfs(fd, &buf) < 0) {
virReportSystemError(errno, _("unable query filesystem type on '%s'"),
path);
return -1;
}
if (buf.f_type != BTRFS_SUPER_MAGIC) {
if (state == VIR_TRISTATE_BOOL_ABSENT) {
virReportSystemError(ENOSYS,
_("unable to control COW flag on '%s', not btrfs"),
path);
return -1;
}
return 0;
}
if (ioctl(fd, FS_IOC_GETFLAGS, &val) < 0) {
virReportSystemError(errno, _("unable get directory flags on '%s'"),
path);
return -1;
}
VIR_DEBUG("Current flags on '%s' are 0x%x", path, val);
if (state == VIR_TRISTATE_BOOL_YES) {
val &= ~FS_NOCOW_FL;
} else {
val |= FS_NOCOW_FL;
}
VIR_DEBUG("New flags on '%s' will be 0x%x", path, val);
if (ioctl(fd, FS_IOC_SETFLAGS, &val) < 0) {
int saved_err = errno;
VIR_DEBUG("Failed to set flags on '%s': %s", path, g_strerror(saved_err));
if (state != VIR_TRISTATE_BOOL_ABSENT) {
virReportSystemError(saved_err,
_("unable control COW flag on '%s'"),
path);
return -1;
} else {
VIR_DEBUG("Ignoring failure to set COW");
}
}
return 0;
#else /* ! __linux__ */
if (state != VIR_TRISTATE_BOOL_ABSENT) {
virReportSystemError(ENOSYS,
_("Unable to set copy-on-write state on '%s' to '%s'"),
path, virTristateBoolTypeToString(state));
return -1;
}
return 0;
#endif /* ! __linux__ */
}

View File

@ -374,3 +374,6 @@ int virFileRemoveXAttr(const char *path,
G_GNUC_NO_INLINE;
int virFileDataSync(int fd);
int virFileSetCOW(const char *path,
virTristateBool state);