mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-24 14:36:13 +01:00
gfile: Implement interface API to make symbolic links asynchronously
The interface was ready for this API but it was not provided. So implement this, using a thread that calls the sync API for now. Add tests. Helps with: GNOME/glib#157
This commit is contained in:
parent
fe9e35624a
commit
04718a9692
@ -156,6 +156,8 @@ g_file_make_directory_async
|
||||
g_file_make_directory_finish
|
||||
g_file_make_directory_with_parents
|
||||
g_file_make_symbolic_link
|
||||
g_file_make_symbolic_link_async
|
||||
g_file_make_symbolic_link_finish
|
||||
g_file_query_settable_attributes
|
||||
g_file_query_writable_namespaces
|
||||
g_file_set_attribute
|
||||
|
128
gio/gfile.c
128
gio/gfile.c
@ -269,6 +269,15 @@ static void g_file_real_make_directory_async (GFile
|
||||
static gboolean g_file_real_make_directory_finish (GFile *file,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
static void g_file_real_make_symbolic_link_async (GFile *file,
|
||||
const char *symlink_value,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
static gboolean g_file_real_make_symbolic_link_finish (GFile *file,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
static void g_file_real_open_readwrite_async (GFile *file,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
@ -399,6 +408,8 @@ g_file_default_init (GFileIface *iface)
|
||||
iface->move_finish = g_file_real_move_finish;
|
||||
iface->make_directory_async = g_file_real_make_directory_async;
|
||||
iface->make_directory_finish = g_file_real_make_directory_finish;
|
||||
iface->make_symbolic_link_async = g_file_real_make_symbolic_link_async;
|
||||
iface->make_symbolic_link_finish = g_file_real_make_symbolic_link_finish;
|
||||
iface->open_readwrite_async = g_file_real_open_readwrite_async;
|
||||
iface->open_readwrite_finish = g_file_real_open_readwrite_finish;
|
||||
iface->create_readwrite_async = g_file_real_create_readwrite_async;
|
||||
@ -4154,6 +4165,123 @@ g_file_make_symbolic_link (GFile *file,
|
||||
return (* iface->make_symbolic_link) (file, symlink_value, cancellable, error);
|
||||
}
|
||||
|
||||
static void
|
||||
make_symbolic_link_async_thread (GTask *task,
|
||||
gpointer object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
const char *symlink_value = task_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (g_file_make_symbolic_link (G_FILE (object), symlink_value, cancellable, &error))
|
||||
g_task_return_boolean (task, TRUE);
|
||||
else
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
}
|
||||
|
||||
static void
|
||||
g_file_real_make_symbolic_link_async (GFile *file,
|
||||
const char *symlink_value,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (G_IS_FILE (file));
|
||||
g_return_if_fail (symlink_value != NULL);
|
||||
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
task = g_task_new (file, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, g_file_real_make_symbolic_link_async);
|
||||
g_task_set_task_data (task, g_strdup (symlink_value), g_free);
|
||||
g_task_set_priority (task, io_priority);
|
||||
|
||||
g_task_run_in_thread (task, make_symbolic_link_async_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_file_make_symbolic_link_async:
|
||||
* @file: a #GFile with the name of the symlink to create
|
||||
* @symlink_value: (type filename): a string with the path for the target
|
||||
* of the new symlink
|
||||
* @io_priority: the [I/O priority][io-priority] of the request
|
||||
* @cancellable: (nullable): optional #GCancellable object,
|
||||
* %NULL to ignore
|
||||
* @callback: a #GAsyncReadyCallback to call
|
||||
* when the request is satisfied
|
||||
* @user_data: the data to pass to callback function
|
||||
*
|
||||
* Asynchronously creates a symbolic link named @file which contains the
|
||||
* string @symlink_value.
|
||||
*
|
||||
* Virtual: make_symbolic_link_async
|
||||
* Since: 2.74
|
||||
*/
|
||||
void
|
||||
g_file_make_symbolic_link_async (GFile *file,
|
||||
const char *symlink_value,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GFileIface *iface;
|
||||
|
||||
g_return_if_fail (G_IS_FILE (file));
|
||||
|
||||
iface = G_FILE_GET_IFACE (file);
|
||||
|
||||
/* Default implementation should always be provided by GFileIface */
|
||||
g_assert (iface->make_symbolic_link_async != NULL);
|
||||
|
||||
(* iface->make_symbolic_link_async) (file, symlink_value, io_priority,
|
||||
cancellable, callback, user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_file_real_make_symbolic_link_finish (GFile *file,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, file), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_file_make_symbolic_link_finish:
|
||||
* @file: input #GFile
|
||||
* @result: a #GAsyncResult
|
||||
* @error: a #GError, or %NULL
|
||||
*
|
||||
* Finishes an asynchronous symbolic link creation, started with
|
||||
* g_file_make_symbolic_link_async().
|
||||
*
|
||||
* Virtual: make_symbolic_link_finish
|
||||
* Returns: %TRUE on successful directory creation, %FALSE otherwise.
|
||||
* Since: 2.74
|
||||
*/
|
||||
gboolean
|
||||
g_file_make_symbolic_link_finish (GFile *file,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
GFileIface *iface;
|
||||
|
||||
g_return_val_if_fail (G_IS_FILE (file), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
iface = G_FILE_GET_IFACE (file);
|
||||
/* Default implementation should always be provided by GFileIface */
|
||||
g_assert (iface->make_symbolic_link_finish != NULL);
|
||||
|
||||
return (* iface->make_symbolic_link_finish) (file, result, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_file_delete:
|
||||
* @file: input #GFile
|
||||
|
26
gio/gfile.h
26
gio/gfile.h
@ -115,8 +115,8 @@ typedef struct _GFileIface GFileIface;
|
||||
* @make_directory_finish: Finishes making a directory asynchronously.
|
||||
* @make_symbolic_link: (nullable): Makes a symbolic link. %NULL if symbolic
|
||||
* links are unsupported.
|
||||
* @_make_symbolic_link_async: Asynchronously makes a symbolic link
|
||||
* @_make_symbolic_link_finish: Finishes making a symbolic link asynchronously.
|
||||
* @make_symbolic_link_async: Asynchronously makes a symbolic link
|
||||
* @make_symbolic_link_finish: Finishes making a symbolic link asynchronously.
|
||||
* @copy: (nullable): Copies a file. %NULL if copying is unsupported, which will
|
||||
* cause `GFile` to use a fallback copy method where it reads from the
|
||||
* source and writes to the destination.
|
||||
@ -396,8 +396,15 @@ struct _GFileIface
|
||||
const char *symlink_value,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
void (* _make_symbolic_link_async) (void);
|
||||
void (* _make_symbolic_link_finish) (void);
|
||||
void (* make_symbolic_link_async) (GFile *file,
|
||||
const char *symlink_value,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* make_symbolic_link_finish) (GFile *file,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
gboolean (* copy) (GFile *source,
|
||||
GFile *destination,
|
||||
@ -976,6 +983,17 @@ gboolean g_file_make_symbolic_link (GFile
|
||||
const char *symlink_value,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_74
|
||||
void g_file_make_symbolic_link_async (GFile *file,
|
||||
const char *symlink_value,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_2_74
|
||||
gboolean g_file_make_symbolic_link_finish (GFile *file,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GFileAttributeInfoList *g_file_query_settable_attributes (GFile *file,
|
||||
GCancellable *cancellable,
|
||||
|
134
gio/tests/file.c
134
gio/tests/file.c
@ -8,6 +8,12 @@
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GError **error;
|
||||
} AsyncErrorData;
|
||||
|
||||
static void
|
||||
test_basic_for_file (GFile *file,
|
||||
const gchar *suffix)
|
||||
@ -2012,6 +2018,133 @@ test_async_delete (void)
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
static void
|
||||
on_symlink_done (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GFile *file = (GFile *) object;
|
||||
GError *error = NULL;
|
||||
GMainLoop *loop = user_data;
|
||||
|
||||
g_assert_true (g_file_make_symbolic_link_finish (file, result, &error));
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_main_loop_quit (loop);
|
||||
}
|
||||
|
||||
static void
|
||||
on_symlink_error (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GFile *file = (GFile *) object;
|
||||
GError *error = NULL;
|
||||
AsyncErrorData *data = user_data;
|
||||
|
||||
g_assert_false (g_file_make_symbolic_link_finish (file, result, &error));
|
||||
g_assert_nonnull (error);
|
||||
g_propagate_error (data->error, g_steal_pointer (&error));
|
||||
|
||||
g_main_loop_quit (data->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_async_make_symlink (void)
|
||||
{
|
||||
GFile *link;
|
||||
GFile *parent_dir;
|
||||
GFile *target;
|
||||
GFileInfo *link_info;
|
||||
GFileIOStream *iostream;
|
||||
GError *error = NULL;
|
||||
GCancellable *cancellable;
|
||||
GMainLoop *loop;
|
||||
AsyncErrorData error_data = {0};
|
||||
gchar *tmpdir_path;
|
||||
gchar *target_path;
|
||||
|
||||
target = g_file_new_tmp ("g_file_symlink_target_XXXXXX", &iostream, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_io_stream_close ((GIOStream *) iostream, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
g_object_unref (iostream);
|
||||
|
||||
g_assert_true (g_file_query_exists (target, NULL));
|
||||
|
||||
loop = g_main_loop_new (NULL, TRUE);
|
||||
error_data.loop = loop;
|
||||
error_data.error = &error;
|
||||
|
||||
tmpdir_path = g_dir_make_tmp ("g_file_symlink_XXXXXX", &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
parent_dir = g_file_new_for_path (tmpdir_path);
|
||||
g_assert_true (g_file_query_exists (parent_dir, NULL));
|
||||
|
||||
link = g_file_get_child (parent_dir, "symlink");
|
||||
g_assert_false (g_file_query_exists (link, NULL));
|
||||
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
|
||||
"*assertion*symlink_value*failed*");
|
||||
g_file_make_symbolic_link_async (link, NULL,
|
||||
G_PRIORITY_DEFAULT, NULL,
|
||||
on_symlink_done, loop);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
g_file_make_symbolic_link_async (link, "",
|
||||
G_PRIORITY_DEFAULT, NULL,
|
||||
on_symlink_error, &error_data);
|
||||
g_main_loop_run (loop);
|
||||
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
|
||||
g_clear_error (&error);
|
||||
|
||||
target_path = g_file_get_path (target);
|
||||
g_file_make_symbolic_link_async (link, target_path,
|
||||
G_PRIORITY_DEFAULT, NULL,
|
||||
on_symlink_done, loop);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_assert_true (g_file_query_exists (link, NULL));
|
||||
link_info = g_file_query_info (link,
|
||||
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_assert_true (g_file_info_get_is_symlink (link_info));
|
||||
g_assert_cmpstr (target_path, ==, g_file_info_get_symlink_target (link_info));
|
||||
|
||||
/* Try creating it again, it fails */
|
||||
g_file_make_symbolic_link_async (link, target_path,
|
||||
G_PRIORITY_DEFAULT, NULL,
|
||||
on_symlink_error, &error_data);
|
||||
g_main_loop_run (loop);
|
||||
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
|
||||
g_clear_error (&error);
|
||||
|
||||
cancellable = g_cancellable_new ();
|
||||
g_file_make_symbolic_link_async (link, target_path,
|
||||
G_PRIORITY_DEFAULT, cancellable,
|
||||
on_symlink_error, &error_data);
|
||||
g_cancellable_cancel (cancellable);
|
||||
g_main_loop_run (loop);
|
||||
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
|
||||
g_clear_error (&error);
|
||||
g_clear_object (&cancellable);
|
||||
|
||||
g_main_loop_unref (loop);
|
||||
g_object_unref (target);
|
||||
g_object_unref (parent_dir);
|
||||
g_object_unref (link);
|
||||
g_object_unref (link_info);
|
||||
g_free (tmpdir_path);
|
||||
g_free (target_path);
|
||||
}
|
||||
|
||||
static void
|
||||
test_copy_preserve_mode (void)
|
||||
{
|
||||
@ -3163,6 +3296,7 @@ main (int argc, char *argv[])
|
||||
g_test_add_data_func ("/file/replace/write-only", GUINT_TO_POINTER (FALSE), test_replace);
|
||||
g_test_add_data_func ("/file/replace/read-write", GUINT_TO_POINTER (TRUE), test_replace);
|
||||
g_test_add_func ("/file/async-delete", test_async_delete);
|
||||
g_test_add_func ("/file/async-make-symlink", test_async_make_symlink);
|
||||
g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
|
||||
g_test_add_func ("/file/measure", test_measure);
|
||||
g_test_add_func ("/file/measure-async", test_measure_async);
|
||||
|
Loading…
Reference in New Issue
Block a user