diff options
author | Colin Walters <walters@verbum.org> | 2013-01-06 12:25:56 -0500 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2013-01-06 12:25:56 -0500 |
commit | b1424432d7cc7af622ef1dbaaf71282cec683df6 (patch) | |
tree | 5a3a9bd4e2b88d905fe236660cc924f2acc55a95 | |
parent | fe898ee93bdc8a625c460876c8b49428c8977219 (diff) | |
download | libgsystem-b1424432d7cc7af622ef1dbaaf71282cec683df6.tar.gz |
fileutils: Extend linkcopy API
* Add gs_file_linkcopy()
* Honor some GFileCopyFlags (namely G_FILE_COPY_OVERWRITE)
-rw-r--r-- | gsystem-file-utils.c | 131 | ||||
-rw-r--r-- | gsystem-file-utils.h | 7 |
2 files changed, 116 insertions, 22 deletions
diff --git a/gsystem-file-utils.c b/gsystem-file-utils.c index 8e5b4dc..1da1755 100644 --- a/gsystem-file-utils.c +++ b/gsystem-file-utils.c @@ -290,31 +290,59 @@ gen_tmp_name (const char *prefix, return g_string_free (str, FALSE); } -/** - * gs_file_linkcopy_sync_data: - * @src: Source file - * @dest: Destination file - * @cancellable: - * @error: - * - * Copy the file @src to @dest, using gs_file_sync_data() to ensure - * that @dest is in stable storage. As an optimization, this function - * will first try the UNIX link() call, but if the files are on - * separate devices, it will fall back to copying. - */ -gboolean -gs_file_linkcopy_sync_data (GFile *src, - GFile *dest, - GCancellable *cancellable, - GError **error) +static gboolean +linkcopy_internal (GFile *src, + GFile *dest, + GFileCopyFlags flags, + gboolean sync_data, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; + gboolean dest_exists; int i; gboolean enable_guestfs_fuse_workaround; + struct stat src_stat; + struct stat dest_stat; gs_unref_object GFile *dest_parent = NULL; + flags |= G_FILE_COPY_NOFOLLOW_SYMLINKS; + + g_return_val_if_fail ((flags & (G_FILE_COPY_BACKUP | G_FILE_COPY_TARGET_DEFAULT_PERMS)) == 0, FALSE); + dest_parent = g_file_get_parent (dest); + if (lstat (gs_file_get_path_cached (src), &dest_stat) == -1) + { + int errsv = errno; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), + g_strerror (errsv)); + goto out; + } + + if (lstat (gs_file_get_path_cached (dest), &dest_stat) == -1) + dest_exists = FALSE; + else + dest_exists = TRUE; + + if (((flags & G_FILE_COPY_OVERWRITE) == 0) && dest_exists) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_EXISTS, + "File exists"); + goto out; + } + + /* Work around the behavior of link() where it's a no-op if src and + * dest are the same. + */ + if (dest_exists && + src_stat.st_dev == dest_stat.st_dev && + src_stat.st_ino == dest_stat.st_ino) + { + ret = TRUE; + goto out; + } + enable_guestfs_fuse_workaround = getenv ("LIBGSYSTEM_ENABLE_GUESTFS_FUSE_WORKAROUND") != NULL; /* 128 attempts seems reasonable... */ @@ -338,8 +366,7 @@ gs_file_linkcopy_sync_data (GFile *src, else if (errno == EXDEV || errno == EMLINK || errno == EPERM || (enable_guestfs_fuse_workaround && errno == ENOENT)) { - if (!g_file_copy (src, tmp_dest, - G_FILE_COPY_OVERWRITE | G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS, + if (!g_file_copy (src, tmp_dest, flags, cancellable, NULL, NULL, error)) goto out; } @@ -352,9 +379,12 @@ gs_file_linkcopy_sync_data (GFile *src, } } - /* Now, we need to fdatasync */ - if (!gs_file_sync_data (tmp_dest, cancellable, error)) - goto out; + if (sync_data) + { + /* Now, we need to fdatasync */ + if (!gs_file_sync_data (tmp_dest, cancellable, error)) + goto out; + } if (!gs_file_rename (tmp_dest, dest, cancellable, error)) goto out; @@ -365,6 +395,63 @@ gs_file_linkcopy_sync_data (GFile *src, ret = TRUE; out: return ret; + +} + +/** + * gs_file_linkcopy: + * @src: Source file + * @dest: Destination file + * @flags: flags + * @cancellable: + * @error: + * + * First tries to use the UNIX link() call, but if the files are on + * separate devices, fall back to copying via g_file_copy(). + * + * The given @flags have different semantics than those documented + * when hardlinking is used. Specifically, both + * #G_FILE_COPY_TARGET_DEFAULT_PERMS and #G_FILE_COPY_BACKUP are not + * supported. #G_FILE_COPY_NOFOLLOW_SYMLINKS treated as if it was + * always given - if you want to follow symbolic links, you will need + * to resolve them manually. + * + * Beware - do not use this function if @src may be modified, and it's + * undesirable for the changes to also be reflected in @dest. The + * best use of this function is in the case where @src and @dest are + * read-only, or where @src is a temporary file, and you want to put + * it in the final place. + */ +gboolean +gs_file_linkcopy (GFile *src, + GFile *dest, + GFileCopyFlags flags, + GCancellable *cancellable, + GError **error) +{ + return linkcopy_internal (src, dest, flags, FALSE, cancellable, error); +} + +/** + * gs_file_linkcopy_sync_data: + * @src: Source file + * @dest: Destination file + * @flags: flags + * @cancellable: + * @error: + * + * This function is similar to gs_file_linkcopy(), except it also uses + * gs_file_sync_data() to ensure that @dest is in stable storage + * before it is moved into place. + */ +gboolean +gs_file_linkcopy_sync_data (GFile *src, + GFile *dest, + GFileCopyFlags flags, + GCancellable *cancellable, + GError **error) +{ + return linkcopy_internal (src, dest, flags, TRUE, cancellable, error); } /** diff --git a/gsystem-file-utils.h b/gsystem-file-utils.h index b88ce31..420ec50 100644 --- a/gsystem-file-utils.h +++ b/gsystem-file-utils.h @@ -44,8 +44,15 @@ gboolean gs_file_sync_data (GFile *file, GCancellable *cancellable, GError **error); +gboolean gs_file_linkcopy (GFile *src, + GFile *dest, + GFileCopyFlags flags, + GCancellable *cancellable, + GError **error); + gboolean gs_file_linkcopy_sync_data (GFile *src, GFile *dest, + GFileCopyFlags flags, GCancellable *cancellable, GError **error); |