mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-26 05:56:14 +01:00
g_file_copy(): Clean up logic for info query
Previously, we called g_file_query_info() *again* on the source at the very end of the copy. This has the lame semantics that if the source happened to be deleted, we would fail to apply attributes to the destination. This could even be a security flaw. This commit changes things so that we query info from the source *stream* after opening - i.e. on Unix we use the proper fstat() and friends. That way we operate more atomically. https://bugzilla.gnome.org/show_bug.cgi?id=699959
This commit is contained in:
parent
b4df86fa19
commit
02aaef5a4d
128
gio/gfile.c
128
gio/gfile.c
@ -2510,7 +2510,7 @@ copy_symlink (GFile *destination,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GInputStream *
|
||||
static GFileInputStream *
|
||||
open_source_for_copy (GFile *source,
|
||||
GFile *destination,
|
||||
GFileCopyFlags flags,
|
||||
@ -2518,14 +2518,14 @@ open_source_for_copy (GFile *source,
|
||||
GError **error)
|
||||
{
|
||||
GError *my_error;
|
||||
GInputStream *in;
|
||||
GFileInputStream *ret;
|
||||
GFileInfo *info;
|
||||
GFileType file_type;
|
||||
|
||||
my_error = NULL;
|
||||
in = (GInputStream *)g_file_read (source, cancellable, &my_error);
|
||||
if (in != NULL)
|
||||
return in;
|
||||
ret = g_file_read (source, cancellable, &my_error);
|
||||
if (ret != NULL)
|
||||
return ret;
|
||||
|
||||
/* There was an error opening the source, try to set a good error for it: */
|
||||
if (my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_IS_DIRECTORY)
|
||||
@ -2588,26 +2588,48 @@ open_source_for_copy (GFile *source,
|
||||
|
||||
static gboolean
|
||||
should_copy (GFileAttributeInfo *info,
|
||||
gboolean as_move,
|
||||
gboolean copy_all_attributes,
|
||||
gboolean skip_perms)
|
||||
{
|
||||
if (skip_perms && strcmp(info->name, "unix::mode") == 0)
|
||||
return FALSE;
|
||||
|
||||
if (as_move)
|
||||
if (copy_all_attributes)
|
||||
return info->flags & G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED;
|
||||
return info->flags & G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE;
|
||||
}
|
||||
|
||||
static char *
|
||||
build_attribute_list_for_copy (GFileAttributeInfoList *attributes,
|
||||
GFileAttributeInfoList *namespaces,
|
||||
gboolean as_move,
|
||||
gboolean skip_perms)
|
||||
static gboolean
|
||||
build_attribute_list_for_copy (GFile *file,
|
||||
GFileCopyFlags flags,
|
||||
char **out_attributes,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GFileAttributeInfoList *attributes = NULL, *namespaces = NULL;
|
||||
GString *s;
|
||||
gboolean first;
|
||||
int i;
|
||||
gboolean copy_all_attributes;
|
||||
gboolean skip_perms;
|
||||
|
||||
copy_all_attributes = flags & G_FILE_COPY_ALL_METADATA;
|
||||
skip_perms = (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) != 0;
|
||||
|
||||
/* Ignore errors here, if the target supports no attributes there is
|
||||
* nothing to copy. We still honor the cancellable though.
|
||||
*/
|
||||
attributes = g_file_query_settable_attributes (file, cancellable, NULL);
|
||||
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||||
goto out;
|
||||
|
||||
namespaces = g_file_query_writable_namespaces (file, cancellable, NULL);
|
||||
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (attributes == NULL && namespaces == NULL)
|
||||
goto out;
|
||||
|
||||
first = TRUE;
|
||||
s = g_string_new ("");
|
||||
@ -2616,7 +2638,7 @@ build_attribute_list_for_copy (GFileAttributeInfoList *attributes,
|
||||
{
|
||||
for (i = 0; i < attributes->n_infos; i++)
|
||||
{
|
||||
if (should_copy (&attributes->infos[i], as_move, skip_perms))
|
||||
if (should_copy (&attributes->infos[i], copy_all_attributes, skip_perms))
|
||||
{
|
||||
if (first)
|
||||
first = FALSE;
|
||||
@ -2632,7 +2654,7 @@ build_attribute_list_for_copy (GFileAttributeInfoList *attributes,
|
||||
{
|
||||
for (i = 0; i < namespaces->n_infos; i++)
|
||||
{
|
||||
if (should_copy (&namespaces->infos[i], as_move, FALSE))
|
||||
if (should_copy (&namespaces->infos[i], copy_all_attributes, FALSE))
|
||||
{
|
||||
if (first)
|
||||
first = FALSE;
|
||||
@ -2645,7 +2667,18 @@ build_attribute_list_for_copy (GFileAttributeInfoList *attributes,
|
||||
}
|
||||
}
|
||||
|
||||
return g_string_free (s, FALSE);
|
||||
ret = TRUE;
|
||||
*out_attributes = g_string_free (s, FALSE);
|
||||
s = NULL;
|
||||
out:
|
||||
if (s)
|
||||
g_string_free (s, TRUE);
|
||||
if (attributes)
|
||||
g_file_attribute_info_list_unref (attributes);
|
||||
if (namespaces)
|
||||
g_file_attribute_info_list_unref (namespaces);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2676,28 +2709,16 @@ g_file_copy_attributes (GFile *source,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GFileAttributeInfoList *attributes, *namespaces;
|
||||
char *attrs_to_read;
|
||||
gboolean res;
|
||||
GFileInfo *info;
|
||||
gboolean as_move;
|
||||
gboolean source_nofollow_symlinks;
|
||||
gboolean skip_perms;
|
||||
|
||||
as_move = flags & G_FILE_COPY_ALL_METADATA;
|
||||
if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
source_nofollow_symlinks = flags & G_FILE_COPY_NOFOLLOW_SYMLINKS;
|
||||
skip_perms = (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) != 0;
|
||||
|
||||
/* Ignore errors here, if the target supports no attributes there is
|
||||
* nothing to copy
|
||||
*/
|
||||
attributes = g_file_query_settable_attributes (destination, cancellable, NULL);
|
||||
namespaces = g_file_query_writable_namespaces (destination, cancellable, NULL);
|
||||
|
||||
if (attributes == NULL && namespaces == NULL)
|
||||
return TRUE;
|
||||
|
||||
attrs_to_read = build_attribute_list_for_copy (attributes, namespaces, as_move, skip_perms);
|
||||
|
||||
/* Ignore errors here, if we can't read some info (e.g. if it doesn't exist)
|
||||
* we just don't copy it.
|
||||
@ -2720,9 +2741,6 @@ g_file_copy_attributes (GFile *source,
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
g_file_attribute_info_list_unref (attributes);
|
||||
g_file_attribute_info_list_unref (namespaces);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3012,10 +3030,13 @@ file_copy_fallback (GFile *source,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GFileInputStream *file_in = NULL;
|
||||
GInputStream *in = NULL;
|
||||
GOutputStream *out = NULL;
|
||||
GFileInfo *info = NULL;
|
||||
const char *target;
|
||||
char *attrs_to_read;
|
||||
gboolean do_set_attributes = FALSE;
|
||||
|
||||
/* need to know the file type */
|
||||
info = g_file_query_info (source,
|
||||
@ -3052,9 +3073,31 @@ file_copy_fallback (GFile *source,
|
||||
|
||||
/* Everything else should just fall back on a regular copy. */
|
||||
|
||||
in = open_source_for_copy (source, destination, flags, cancellable, error);
|
||||
if (!in)
|
||||
file_in = open_source_for_copy (source, destination, flags, cancellable, error);
|
||||
if (!file_in)
|
||||
goto out;
|
||||
in = G_INPUT_STREAM (file_in);
|
||||
|
||||
if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (attrs_to_read != NULL)
|
||||
{
|
||||
/* Ok, ditch the previous lightweight info (on Unix we just
|
||||
* called lstat()); at this point we gather all the information
|
||||
* we need about the source from the opened file descriptor.
|
||||
*/
|
||||
g_object_unref (info);
|
||||
|
||||
info = g_file_input_stream_query_info (file_in, attrs_to_read,
|
||||
cancellable, error);
|
||||
g_free (attrs_to_read);
|
||||
if (!info)
|
||||
goto out;
|
||||
|
||||
do_set_attributes = TRUE;
|
||||
}
|
||||
|
||||
if (flags & G_FILE_COPY_OVERWRITE)
|
||||
{
|
||||
@ -3151,10 +3194,15 @@ file_copy_fallback (GFile *source,
|
||||
}
|
||||
|
||||
/* Ignore errors here. Failure to copy metadata is not a hard error */
|
||||
if (ret)
|
||||
(void) g_file_copy_attributes (source, destination,
|
||||
flags, cancellable, NULL);
|
||||
|
||||
/* TODO: set these attributes /before/ we do the rename() on Unix */
|
||||
if (ret && do_set_attributes)
|
||||
{
|
||||
g_file_set_attributes_from_info (destination,
|
||||
info,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable,
|
||||
error);
|
||||
}
|
||||
|
||||
g_clear_object (&info);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user