mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
GFile: Add Btrfs clone ioctl support
The attached patch adds support for the btrfs "clone" ioctl which makes Copy-on-Write reflinks, resulting in cheap O(1) copies when source/destination are on the same filesystem. The ioctl itself is quite straightforward, and GNU coreutils has had support since 7.5 (--reflink=auto --sparse=auto). The ioctl only operates on regular files and symlinks, and always follows symlinks; checks have been added accordingly. This patch would be very useful for everyone who uses btrfs filesystems (Meego folks for instance). On systems that don't have btrfs, or if the the source is not on a btrfs filesystem, the ioctl returns EINVAL, and the fallback code is triggered. Hence this will cause no problems for non-btrfs users. https://bugzilla.gnome.org/show_bug.cgi?id=626497
This commit is contained in:
parent
f42347d82e
commit
5eba978497
@ -903,6 +903,7 @@ AC_CHECK_HEADERS([mntent.h sys/mnttab.h sys/vfstab.h sys/mntctl.h fstab.h])
|
||||
AC_CHECK_HEADERS([sys/uio.h sys/mkdev.h])
|
||||
AC_CHECK_HEADERS([linux/magic.h])
|
||||
AC_CHECK_HEADERS([sys/prctl.h])
|
||||
AC_CHECK_HEADERS([sys/ioctl.h])
|
||||
|
||||
AC_CHECK_HEADERS([sys/mount.h sys/sysctl.h], [], [],
|
||||
[#if HAVE_SYS_PARAM_H
|
||||
|
104
gio/gfile.c
104
gio/gfile.c
@ -23,17 +23,28 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
/* See linux.git/fs/btrfs/ioctl.h */
|
||||
#define BTRFS_IOCTL_MAGIC 0x94
|
||||
#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SPLICE
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_PWD_H
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#include "gfile.h"
|
||||
#include "gvfs.h"
|
||||
#include "gtask.h"
|
||||
@ -2905,6 +2916,64 @@ splice_stream_with_progress (GInputStream *in,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
static gboolean
|
||||
btrfs_reflink_with_progress (GInputStream *in,
|
||||
GOutputStream *out,
|
||||
GFileInfo *info,
|
||||
GCancellable *cancellable,
|
||||
GFileProgressCallback progress_callback,
|
||||
gpointer progress_callback_data,
|
||||
GError **error)
|
||||
{
|
||||
goffset source_size;
|
||||
int fd_in, fd_out;
|
||||
int ret;
|
||||
|
||||
fd_in = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (in));
|
||||
fd_out = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (out));
|
||||
|
||||
if (progress_callback)
|
||||
source_size = g_file_info_get_size (info);
|
||||
|
||||
/* Btrfs clone ioctl properties:
|
||||
* - Works at the inode level
|
||||
* - Doesn't work with directories
|
||||
* - Always follows symlinks (source and destination)
|
||||
*
|
||||
* By the time we get here, *in and *out are both regular files */
|
||||
ret = ioctl (fd_out, BTRFS_IOC_CLONE, fd_in);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
if (errno == EXDEV)
|
||||
g_set_error_literal (error, G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Copy (reflink/clone) between mounts is not supported"));
|
||||
else if (errno == EINVAL)
|
||||
g_set_error_literal (error, G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Copy (reflink/clone) is not supported or invalid"));
|
||||
else
|
||||
/* Most probably something odd happened; retry with fallback */
|
||||
g_set_error_literal (error, G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Copy (reflink/clone) is not supported or didn't work"));
|
||||
/* We retry with fallback for all error cases because Btrfs is currently
|
||||
* unstable, and so we can't trust it to do clone properly.
|
||||
* In addition, any hard errors here would cause the same failure in the
|
||||
* fallback manual copy as well. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Make sure we send full copied size */
|
||||
if (progress_callback)
|
||||
progress_callback (source_size, source_size, progress_callback_data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
file_copy_fallback (GFile *source,
|
||||
GFile *destination,
|
||||
@ -2919,9 +2988,6 @@ file_copy_fallback (GFile *source,
|
||||
GFileInfo *info;
|
||||
const char *target;
|
||||
gboolean result;
|
||||
#ifdef HAVE_SPLICE
|
||||
gboolean fallback = TRUE;
|
||||
#endif
|
||||
|
||||
/* need to know the file type */
|
||||
info = g_file_query_info (source,
|
||||
@ -2988,6 +3054,26 @@ file_copy_fallback (GFile *source,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
if (G_IS_FILE_DESCRIPTOR_BASED (in) && G_IS_FILE_DESCRIPTOR_BASED (out))
|
||||
{
|
||||
GError *reflink_err = NULL;
|
||||
|
||||
result = btrfs_reflink_with_progress (in, out, info, cancellable,
|
||||
progress_callback, progress_callback_data,
|
||||
&reflink_err);
|
||||
|
||||
if (result || !g_error_matches (reflink_err, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
|
||||
{
|
||||
if (!result)
|
||||
g_propagate_error (error, reflink_err);
|
||||
goto cleanup;
|
||||
}
|
||||
else
|
||||
g_clear_error (&reflink_err);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SPLICE
|
||||
if (G_IS_FILE_DESCRIPTOR_BASED (in) && G_IS_FILE_DESCRIPTOR_BASED (out))
|
||||
{
|
||||
@ -2999,20 +3085,20 @@ file_copy_fallback (GFile *source,
|
||||
|
||||
if (result || !g_error_matches (splice_err, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
|
||||
{
|
||||
fallback = FALSE;
|
||||
if (!result)
|
||||
g_propagate_error (error, splice_err);
|
||||
goto cleanup;
|
||||
}
|
||||
else
|
||||
g_clear_error (&splice_err);
|
||||
}
|
||||
|
||||
if (fallback)
|
||||
#endif
|
||||
result = copy_stream_with_progress (in, out, source, cancellable,
|
||||
progress_callback, progress_callback_data,
|
||||
error);
|
||||
result = copy_stream_with_progress (in, out, source, cancellable,
|
||||
progress_callback, progress_callback_data,
|
||||
error);
|
||||
|
||||
cleanup:
|
||||
/* Don't care about errors in source here */
|
||||
g_input_stream_close (in, cancellable, NULL);
|
||||
|
||||
@ -3026,7 +3112,7 @@ file_copy_fallback (GFile *source,
|
||||
if (result == FALSE)
|
||||
return FALSE;
|
||||
|
||||
copied_file:
|
||||
copied_file:
|
||||
/* Ignore errors here. Failure to copy metadata is not a hard error */
|
||||
g_file_copy_attributes (source, destination,
|
||||
flags, cancellable, NULL);
|
||||
|
Loading…
Reference in New Issue
Block a user