X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgfile.c;h=9cf3f4dd0fe9582a26ce30b8e31c8bacac912bf2;hb=958da1e9dc82fbb91862501226b8928faf2f9558;hp=3d78d7205681196f14e95d9e757e39d64dad6036;hpb=166766a89fcd173dcd6ffda11f902029928f7f28;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gfile.c b/gio/gfile.c index 3d78d72..9cf3f4d 100644 --- a/gio/gfile.c +++ b/gio/gfile.c @@ -15,16 +15,14 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . * * Author: Alexander Larsson */ #include "config.h" -#if HAVE_SYS_IOCTL_H +#ifdef __linux__ #include #include /* See linux.git/fs/btrfs/ioctl.h */ @@ -41,11 +39,9 @@ #include #include -#ifdef HAVE_PWD_H -#include -#endif #include "gfile.h" +#include "glib/gstdio.h" #ifdef G_OS_UNIX #include "glib-unix.h" #endif @@ -59,6 +55,7 @@ #include "gfileoutputstream.h" #include "glocalfileoutputstream.h" #include "glocalfileiostream.h" +#include "glocalfile.h" #include "gcancellable.h" #include "gasyncresult.h" #include "gioerror.h" @@ -79,13 +76,11 @@ * (see #GInputStream and #GOutputStream). * * To construct a #GFile, you can use: - * - * g_file_new_for_path() if you have a path. - * g_file_new_for_uri() if you have a URI. - * g_file_new_for_commandline_arg() for a command line argument. - * g_file_new_tmp() to create a temporary file from a template. - * g_file_parse_name() from a UTF-8 string gotten from g_file_get_parse_name(). - * + * - g_file_new_for_path() if you have a path. + * - g_file_new_for_uri() if you have a URI. + * - g_file_new_for_commandline_arg() for a command line argument. + * - g_file_new_tmp() to create a temporary file from a template. + * - g_file_parse_name() from a UTF-8 string gotten from g_file_get_parse_name(). * * One way to think of a #GFile is as an abstraction of a pathname. For * normal files the system pathname is what is stored internally, but as @@ -134,21 +129,20 @@ * Some #GFile operations do not have synchronous analogs, as they may * take a very long time to finish, and blocking may leave an application * unusable. Notable cases include: - * - * g_file_mount_mountable() to mount a mountable file. - * g_file_unmount_mountable_with_operation() to unmount a mountable file. - * g_file_eject_mountable_with_operation() to eject a mountable file. - * + * - g_file_mount_mountable() to mount a mountable file. + * - g_file_unmount_mountable_with_operation() to unmount a mountable file. + * - g_file_eject_mountable_with_operation() to eject a mountable file. + * + * ## Entity Tags # {#gfile-etag} * - * entity tag * One notable feature of #GFiles are entity tags, or "etags" for * short. Entity tags are somewhat like a more abstract version of the - * traditional mtime, and can be used to quickly determine if the file has - * been modified from the version on the file system. See the HTTP 1.1 - * specification + * traditional mtime, and can be used to quickly determine if the file + * has been modified from the version on the file system. See the + * HTTP 1.1 + * [specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) * for HTTP Etag headers, which are a very similar concept. - * - **/ + */ static void g_file_real_query_info_async (GFile *file, const char *attributes, @@ -224,6 +218,22 @@ static void g_file_real_delete_async (GFile static gboolean g_file_real_delete_finish (GFile *file, GAsyncResult *res, GError **error); +static void g_file_real_trash_async (GFile *file, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +static gboolean g_file_real_trash_finish (GFile *file, + GAsyncResult *res, + GError **error); +static void g_file_real_make_directory_async (GFile *file, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +static gboolean g_file_real_make_directory_finish (GFile *file, + GAsyncResult *res, + GError **error); static void g_file_real_open_readwrite_async (GFile *file, int io_priority, GCancellable *cancellable, @@ -298,6 +308,30 @@ static gboolean g_file_real_copy_finish (GFile GAsyncResult *res, GError **error); +static gboolean g_file_real_measure_disk_usage (GFile *file, + GFileMeasureFlags flags, + GCancellable *cancellable, + GFileMeasureProgressCallback progress_callback, + gpointer progress_data, + guint64 *disk_usage, + guint64 *num_dirs, + guint64 *num_files, + GError **error); +static void g_file_real_measure_disk_usage_async (GFile *file, + GFileMeasureFlags flags, + gint io_priority, + GCancellable *cancellable, + GFileMeasureProgressCallback progress_callback, + gpointer progress_data, + GAsyncReadyCallback callback, + gpointer user_data); +static gboolean g_file_real_measure_disk_usage_finish (GFile *file, + GAsyncResult *result, + guint64 *disk_usage, + guint64 *num_dirs, + guint64 *num_files, + GError **error); + typedef GFileIface GFileInterface; G_DEFINE_INTERFACE (GFile, g_file, G_TYPE_OBJECT) @@ -324,6 +358,10 @@ g_file_default_init (GFileIface *iface) iface->replace_finish = g_file_real_replace_finish; iface->delete_file_async = g_file_real_delete_async; iface->delete_file_finish = g_file_real_delete_finish; + iface->trash_async = g_file_real_trash_async; + iface->trash_finish = g_file_real_trash_finish; + iface->make_directory_async = g_file_real_make_directory_async; + iface->make_directory_finish = g_file_real_make_directory_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; @@ -335,6 +373,9 @@ g_file_default_init (GFileIface *iface) iface->set_attributes_from_info = g_file_real_set_attributes_from_info; iface->copy_async = g_file_real_copy_async; iface->copy_finish = g_file_real_copy_finish; + iface->measure_disk_usage = g_file_real_measure_disk_usage; + iface->measure_disk_usage_async = g_file_real_measure_disk_usage_async; + iface->measure_disk_usage_finish = g_file_real_measure_disk_usage_finish; } @@ -403,9 +444,9 @@ g_file_has_uri_scheme (GFile *file, * * Gets the URI scheme for a #GFile. * RFC 3986 decodes the scheme as: - * + * |[ * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] - * + * ]| * Common schemes include "file", "http", "ftp", etc. * * This call does no blocking I/O. @@ -445,8 +486,8 @@ g_file_get_uri_scheme (GFile *file) * * This call does no blocking I/O. * - * Returns: string containing the #GFile's base name, or %NULL - * if given #GFile is invalid. The returned string should be + * Returns: (nullable): string containing the #GFile's base name, or + * %NULL if given #GFile is invalid. The returned string should be * freed with g_free() when no longer needed. */ char * @@ -469,9 +510,9 @@ g_file_get_basename (GFile *file) * * This call does no blocking I/O. * - * Returns: string containing the #GFile's path, or %NULL if - * no such path exists. The returned string should be - * freed with g_free() when no longer needed. + * Returns: (nullable): string containing the #GFile's path, or %NULL + * if no such path exists. The returned string should be freed + * with g_free() when no longer needed. */ char * g_file_get_path (GFile *file) @@ -609,7 +650,6 @@ g_file_hash (gconstpointer file) * This call does no blocking I/O. * * Returns: %TRUE if @file1 and @file2 are equal. - * %FALSE if either is not a #GFile. */ gboolean g_file_equal (GFile *file1, @@ -639,9 +679,9 @@ g_file_equal (GFile *file1, * * This call does no blocking I/O. * - * Returns: (transfer full): a #GFile structure to the - * parent of the given #GFile or %NULL if there is - * no parent. Free the returned object with g_object_unref(). + * Returns: (nullable) (transfer full): a #GFile structure to the + * parent of the given #GFile or %NULL if there is no parent. Free + * the returned object with g_object_unref(). */ GFile * g_file_get_parent (GFile *file) @@ -808,10 +848,10 @@ g_file_has_prefix (GFile *file, * * This call does no blocking I/O. * - * Returns: string with the relative path from @descendant - * to @parent, or %NULL if @descendant doesn't have @parent - * as prefix. The returned string should be freed with g_free() - * when no longer needed. + * Returns: (nullable): string with the relative path from @descendant + * to @parent, or %NULL if @descendant doesn't have @parent as + * prefix. The returned string should be freed with g_free() when + * no longer needed. */ char * g_file_get_relative_path (GFile *parent, @@ -926,8 +966,7 @@ g_file_enumerate_children (GFile *file, * @file: input #GFile * @attributes: an attribute query string * @flags: a set of #GFileQueryInfoFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call when the @@ -1164,8 +1203,7 @@ g_file_query_info (GFile *file, * @file: input #GFile * @attributes: an attribute query string * @flags: a set of #GFileQueryInfoFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call when the @@ -1302,8 +1340,7 @@ g_file_query_filesystem_info (GFile *file, * g_file_query_filesystem_info_async: * @file: input #GFile * @attributes: an attribute query string - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -1425,8 +1462,7 @@ g_file_find_enclosing_mount (GFile *file, /** * g_file_find_enclosing_mount_async: * @file: a #GFile - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -1654,7 +1690,7 @@ g_file_create (GFile *file, /** * g_file_replace: * @file: input #GFile - * @etag: (allow-none): an optional entity tag + * @etag: (allow-none): an optional [entity tag][gfile-etag] * for the current #GFile, or #NULL to ignore * @make_backup: %TRUE if a backup should be created * @flags: a set of #GFileCreateFlags @@ -1859,7 +1895,7 @@ g_file_create_readwrite (GFile *file, /** * g_file_replace_readwrite: * @file: a #GFile - * @etag: (allow-none): an optional entity tag + * @etag: (allow-none): an optional [entity tag][gfile-etag] * for the current #GFile, or #NULL to ignore * @make_backup: %TRUE if a backup should be created * @flags: a set of #GFileCreateFlags @@ -1914,8 +1950,7 @@ g_file_replace_readwrite (GFile *file, /** * g_file_read_async: * @file: input #GFile - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -1983,8 +2018,7 @@ g_file_read_finish (GFile *file, * g_file_append_to_async: * @file: input #GFile * @flags: a set of #GFileCreateFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -2055,8 +2089,7 @@ g_file_append_to_finish (GFile *file, * g_file_create_async: * @file: input #GFile * @flags: a set of #GFileCreateFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -2126,12 +2159,11 @@ g_file_create_finish (GFile *file, /** * g_file_replace_async: * @file: input #GFile - * @etag: (allow-none): an entity tag - * for the current #GFile, or NULL to ignore + * @etag: (allow-none): an [entity tag][gfile-etag] for the current #GFile, + * or %NULL to ignore * @make_backup: %TRUE if a backup should be created * @flags: a set of #GFileCreateFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -2205,8 +2237,7 @@ g_file_replace_finish (GFile *file, /** * g_file_open_readwrite_async * @file: input #GFile - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -2278,8 +2309,7 @@ g_file_open_readwrite_finish (GFile *file, * g_file_create_readwrite_async: * @file: input #GFile * @flags: a set of #GFileCreateFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -2353,12 +2383,11 @@ g_file_create_readwrite_finish (GFile *file, /** * g_file_replace_readwrite_async: * @file: input #GFile - * @etag: (allow-none): an entity tag - * for the current #GFile, or NULL to ignore + * @etag: (allow-none): an [entity tag][gfile-etag] for the current #GFile, + * or %NULL to ignore * @make_backup: %TRUE if a backup should be created * @flags: a set of #GFileCreateFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -2456,7 +2485,7 @@ copy_symlink (GFile *destination, if (!tried_delete && (flags & G_FILE_COPY_OVERWRITE) && my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_EXISTS) { - g_error_free (my_error); + g_clear_error (&my_error); /* Don't overwrite if the destination is a directory */ info = g_file_query_info (destination, G_FILE_ATTRIBUTE_STANDARD_TYPE, @@ -2489,7 +2518,7 @@ copy_symlink (GFile *destination, return TRUE; } -static GInputStream * +static GFileInputStream * open_source_for_copy (GFile *source, GFile *destination, GFileCopyFlags flags, @@ -2497,14 +2526,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) @@ -2567,26 +2596,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) { - GString *s; + gboolean ret = FALSE; + GFileAttributeInfoList *attributes = NULL, *namespaces = NULL; + GString *s = NULL; 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 (""); @@ -2595,7 +2646,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; @@ -2611,7 +2662,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; @@ -2624,7 +2675,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; } /** @@ -2655,28 +2717,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; - 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; + if (!build_attribute_list_for_copy (destination, flags, &attrs_to_read, + cancellable, error)) + return FALSE; - attrs_to_read = build_attribute_list_for_copy (attributes, namespaces, as_move, skip_perms); + source_nofollow_symlinks = flags & G_FILE_COPY_NOFOLLOW_SYMLINKS; /* Ignore errors here, if we can't read some info (e.g. if it doesn't exist) * we just don't copy it. @@ -2699,9 +2749,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; } @@ -2843,7 +2890,7 @@ splice_stream_with_progress (GInputStream *in, gpointer progress_callback_data, GError **error) { - int buffer[2]; + int buffer[2] = { -1, -1 }; gboolean res; goffset total_size; loff_t offset_in; @@ -2907,15 +2954,23 @@ splice_stream_with_progress (GInputStream *in, if (progress_callback) progress_callback (offset_in, total_size, progress_callback_data); + if (!g_close (buffer[0], error)) + goto out; + buffer[0] = -1; + if (!g_close (buffer[1], error)) + goto out; + buffer[1] = -1; out: - close (buffer[0]); - close (buffer[1]); + if (buffer[0] != -1) + (void) g_close (buffer[0], NULL); + if (buffer[1] != -1) + (void) g_close (buffer[1], NULL); return res; } #endif -#ifdef HAVE_SYS_IOCTL_H +#ifdef __linux__ static gboolean btrfs_reflink_with_progress (GInputStream *in, GOutputStream *out, @@ -2983,10 +3038,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, @@ -3023,11 +3081,75 @@ 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) + { + GError *tmp_error = 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, &tmp_error); + if (!info) + { + /* Not all gvfs backends implement query_info_on_read(), we + * can just fall back to the pathname again. + * https://bugzilla.gnome.org/706254 + */ + if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) + { + g_clear_error (&tmp_error); + info = g_file_query_info (source, attrs_to_read, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + } + else + { + g_free (attrs_to_read); + g_propagate_error (error, tmp_error); + goto out; + } + } + g_free (attrs_to_read); + if (!info) + goto out; - if (flags & G_FILE_COPY_OVERWRITE) + do_set_attributes = TRUE; + } + + /* In the local file path, we pass down the source info which + * includes things like unix::mode, to ensure that the target file + * is not created with different permissions from the source file. + * + * If a future API like g_file_replace_with_info() is added, switch + * this code to use that. + */ + if (G_IS_LOCAL_FILE (destination)) + { + if (flags & G_FILE_COPY_OVERWRITE) + out = (GOutputStream*)_g_local_file_output_stream_replace (_g_local_file_get_filename (G_LOCAL_FILE (destination)), + FALSE, NULL, + flags & G_FILE_COPY_BACKUP, + G_FILE_CREATE_REPLACE_DESTINATION, + info, + cancellable, error); + else + out = (GOutputStream*)_g_local_file_output_stream_create (_g_local_file_get_filename (G_LOCAL_FILE (destination)), + FALSE, 0, info, + cancellable, error); + } + else if (flags & G_FILE_COPY_OVERWRITE) { out = (GOutputStream *)g_file_replace (destination, NULL, @@ -3043,7 +3165,7 @@ file_copy_fallback (GFile *source, if (!out) goto out; -#ifdef HAVE_SYS_IOCTL_H +#ifdef __linux__ if (G_IS_FILE_DESCRIPTOR_BASED (in) && G_IS_FILE_DESCRIPTOR_BASED (out)) { GError *reflink_err = NULL; @@ -3097,7 +3219,7 @@ file_copy_fallback (GFile *source, } #endif - + /* A plain read/write loop */ if (!copy_stream_with_progress (in, out, source, cancellable, progress_callback, progress_callback_data, @@ -3106,11 +3228,6 @@ file_copy_fallback (GFile *source, ret = TRUE; out: - /* Ignore errors here. Failure to copy metadata is not a hard error */ - if (ret) - (void) g_file_copy_attributes (source, destination, - flags, cancellable, NULL); - if (in) { /* Don't care about errors in source here */ @@ -3125,7 +3242,18 @@ file_copy_fallback (GFile *source, ret = FALSE; g_object_unref (out); } - + + /* Ignore errors here. Failure to copy metadata is not a hard error */ + /* 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, + NULL); + } + g_clear_object (&info); return ret; @@ -3260,8 +3388,7 @@ g_file_copy (GFile *source, * @source: input #GFile * @destination: destination #GFile * @flags: set of #GFileCopyFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @progress_callback: (allow-none): function to callback with progress @@ -3274,8 +3401,9 @@ g_file_copy (GFile *source, * asynchronously. For details of the behaviour, see g_file_copy(). * * If @progress_callback is not %NULL, then that function that will be called - * just like in g_file_copy(), however the callback will run in the main loop, - * not in the thread that is doing the I/O operation. + * just like in g_file_copy(). The callback will run in the default main context + * of the thread calling g_file_copy_async() — the same context as @callback is + * run in. * * When the operation is finished, @callback will be called. You can then call * g_file_copy_finish() to get the result of the operation. @@ -3519,6 +3647,67 @@ g_file_make_directory (GFile *file, } /** + * g_file_make_directory_async: + * @file: input #GFile + * @io_priority: the [I/O priority][io-priority] of the request + * @cancellable: (allow-none): 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 directory. + * + * Virtual: make_directory_async + * Since: 2.38 + */ +void +g_file_make_directory_async (GFile *file, + 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); + (* iface->make_directory_async) (file, + io_priority, + cancellable, + callback, + user_data); +} + +/** + * g_file_make_directory_finish: + * @file: input #GFile + * @result: a #GAsyncResult + * @error: a #GError, or %NULL + * + * Finishes an asynchronous directory creation, started with + * g_file_make_directory_async(). + * + * Virtual: make_directory_finish + * Returns: %TRUE on successful directory creation, %FALSE otherwise. + * Since: 2.38 + */ +gboolean +g_file_make_directory_finish (GFile *file, + GAsyncResult *result, + GError **error) +{ + GFileIface *iface; + + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + + iface = G_FILE_GET_IFACE (file); + return (* iface->make_directory_finish) (file, result, error); +} + +/** * g_file_make_directory_with_parents: * @file: input #GFile * @cancellable: (allow-none): optional #GCancellable object, @@ -3709,8 +3898,7 @@ g_file_delete (GFile *file, /** * g_file_delete_async: * @file: input #GFile - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: a #GAsyncReadyCallback to call @@ -3752,6 +3940,7 @@ g_file_delete_async (GFile *file, * Finishes deleting a file started with g_file_delete_async(). * * Virtual: delete_file_finish + * Returns: %TRUE if the file was deleted. %FALSE otherwise. * Since: 2.34 **/ gboolean @@ -3787,6 +3976,7 @@ g_file_delete_finish (GFile *file, * triggering the cancellable object from another thread. If the operation * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. * + * Virtual: trash * Returns: %TRUE on successful trash, %FALSE otherwise. */ gboolean @@ -3815,6 +4005,67 @@ g_file_trash (GFile *file, } /** + * g_file_trash_async: + * @file: input #GFile + * @io_priority: the [I/O priority][io-priority] of the request + * @cancellable: (allow-none): 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 sends @file to the Trash location, if possible. + * + * Virtual: trash_async + * Since: 2.38 + */ +void +g_file_trash_async (GFile *file, + 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); + (* iface->trash_async) (file, + io_priority, + cancellable, + callback, + user_data); +} + +/** + * g_file_trash_finish: + * @file: input #GFile + * @result: a #GAsyncResult + * @error: a #GError, or %NULL + * + * Finishes an asynchronous file trashing operation, started with + * g_file_trash_async(). + * + * Virtual: trash_finish + * Returns: %TRUE on successful trash, %FALSE otherwise. + * Since: 2.38 + */ +gboolean +g_file_trash_finish (GFile *file, + GAsyncResult *result, + GError **error) +{ + GFileIface *iface; + + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + + iface = G_FILE_GET_IFACE (file); + return (* iface->trash_finish) (file, result, error); +} + +/** * g_file_set_display_name: * @file: input #GFile * @display_name: a string @@ -3874,8 +4125,7 @@ g_file_set_display_name (GFile *file, * g_file_set_display_name_async: * @file: input #GFile * @display_name: a string - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback to call @@ -4204,8 +4454,7 @@ g_file_real_set_attributes_from_info (GFile *file, * @file: input #GFile * @info: a #GFileInfo * @flags: a #GFileQueryInfoFlags - * @io_priority: the I/O priority - * of the request + * @io_priority: the [I/O priority][io-priority] of the request * @cancellable: (allow-none): optional #GCancellable object, * %NULL to ignore * @callback: (scope async): a #GAsyncReadyCallback @@ -5236,21 +5485,10 @@ open_read_async_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - GFileIface *iface; GFileInputStream *stream; GError *error = NULL; - iface = G_FILE_GET_IFACE (object); - - if (iface->read_fn == NULL) - { - g_task_return_new_error (task, G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - _("Operation not supported")); - return; - } - - stream = iface->read_fn (G_FILE (object), cancellable, &error); + stream = g_file_read (G_FILE (object), cancellable, &error); if (stream) g_task_return_pointer (task, stream, g_object_unref); else @@ -5288,14 +5526,11 @@ append_to_async_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - GFileIface *iface; GFileCreateFlags *data = task_data; GFileOutputStream *stream; GError *error = NULL; - iface = G_FILE_GET_IFACE (source_object); - - stream = iface->append_to (G_FILE (source_object), *data, cancellable, &error); + stream = g_file_append_to (G_FILE (source_object), *data, cancellable, &error); if (stream) g_task_return_pointer (task, stream, g_object_unref); else @@ -5340,14 +5575,11 @@ create_async_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - GFileIface *iface; GFileCreateFlags *data = task_data; GFileOutputStream *stream; GError *error = NULL; - iface = G_FILE_GET_IFACE (source_object); - - stream = iface->create (G_FILE (source_object), *data, cancellable, &error); + stream = g_file_create (G_FILE (source_object), *data, cancellable, &error); if (stream) g_task_return_pointer (task, stream, g_object_unref); else @@ -5408,14 +5640,11 @@ replace_async_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - GFileIface *iface; GFileOutputStream *stream; ReplaceAsyncData *data = task_data; GError *error = NULL; - iface = G_FILE_GET_IFACE (source_object); - - stream = iface->replace (G_FILE (source_object), + stream = g_file_replace (G_FILE (source_object), data->etag, data->make_backup, data->flags, @@ -5470,15 +5699,9 @@ delete_async_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - GFile *file = object; - GFileIface *iface; GError *error = NULL; - iface = G_FILE_GET_IFACE (object); - - if (iface->delete_file (file, - cancellable, - &error)) + if (g_file_delete (G_FILE (object), cancellable, &error)) g_task_return_boolean (task, TRUE); else g_task_return_error (task, error); @@ -5510,25 +5733,93 @@ g_file_real_delete_finish (GFile *file, } static void -open_readwrite_async_thread (GTask *task, +trash_async_thread (GTask *task, + gpointer object, + gpointer task_data, + GCancellable *cancellable) +{ + GError *error = NULL; + + if (g_file_trash (G_FILE (object), cancellable, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); +} + +static void +g_file_real_trash_async (GFile *file, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (file, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + g_task_run_in_thread (task, trash_async_thread); + g_object_unref (task); +} + +static gboolean +g_file_real_trash_finish (GFile *file, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (res, file), FALSE); + + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +make_directory_async_thread (GTask *task, gpointer object, gpointer task_data, GCancellable *cancellable) { - GFileIface *iface; - GFileIOStream *stream; GError *error = NULL; - iface = G_FILE_GET_IFACE (object); + if (g_file_make_directory (G_FILE (object), cancellable, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); +} - if (iface->open_readwrite == NULL) - { - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("Operation not supported")); - return; - } +static void +g_file_real_make_directory_async (GFile *file, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; - stream = iface->open_readwrite (G_FILE (object), cancellable, &error); + task = g_task_new (file, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + g_task_run_in_thread (task, make_directory_async_thread); + g_object_unref (task); +} + +static gboolean +g_file_real_make_directory_finish (GFile *file, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (res, file), FALSE); + + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +open_readwrite_async_thread (GTask *task, + gpointer object, + gpointer task_data, + GCancellable *cancellable) +{ + GFileIOStream *stream; + GError *error = NULL; + + stream = g_file_open_readwrite (G_FILE (object), cancellable, &error); if (stream == NULL) g_task_return_error (task, error); @@ -5568,21 +5859,11 @@ create_readwrite_async_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - GFileIface *iface; GFileCreateFlags *data = task_data; GFileIOStream *stream; GError *error = NULL; - iface = G_FILE_GET_IFACE (object); - - if (iface->create_readwrite == NULL) - { - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("Operation not supported")); - return; - } - - stream = iface->create_readwrite (G_FILE (object), *data, cancellable, &error); + stream = g_file_create_readwrite (G_FILE (object), *data, cancellable, &error); if (stream == NULL) g_task_return_error (task, error); @@ -5641,14 +5922,11 @@ replace_readwrite_async_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - GFileIface *iface; GFileIOStream *stream; GError *error = NULL; ReplaceRWAsyncData *data = task_data; - iface = G_FILE_GET_IFACE (object); - - stream = iface->replace_readwrite (G_FILE (object), + stream = g_file_replace_readwrite (G_FILE (object), data->etag, data->make_backup, data->flags, @@ -6162,6 +6440,15 @@ new_for_cmdline_arg (const gchar *arg, * This operation never fails, but the returned object might not * support any I/O operation if @arg points to a malformed path. * + * Note that on Windows, this function expects its argument to be in + * UTF-8 -- not the system code page. This means that you + * should not use this function with string from argv as it is passed + * to main(). g_win32_get_command_line() will return a UTF-8 version of + * the commandline. #GApplication also uses UTF-8 but + * g_application_command_line_create_file_for_arg() may be more useful + * for you there. It is also always possible to use this function with + * #GOptionContext arguments of type %G_OPTION_ARG_FILENAME. + * * Returns: (transfer full): a new #GFile. * Free the returned object with g_object_unref(). */ @@ -6785,11 +7072,11 @@ g_file_load_contents_finish (GFile *file, * @file: input #GFile * @contents: (element-type guint8) (array length=length): a string containing the new contents for @file * @length: the length of @contents in bytes - * @etag: (allow-none): the old entity tag - * for the document, or %NULL + * @etag: (allow-none): the old [entity-tag][gfile-etag] for the document, + * or %NULL * @make_backup: %TRUE if a backup should be created * @flags: a set of #GFileCreateFlags - * @new_etag: (allow-none) (out): a location to a new entity tag + * @new_etag: (allow-none) (out): a location to a new [entity tag][gfile-etag] * for the document. This should be freed with g_free() when no longer * needed, or %NULL * @cancellable: optional #GCancellable object, %NULL to ignore @@ -6871,8 +7158,7 @@ g_file_replace_contents (GFile *file, typedef struct { GTask *task; - const char *content; - gsize length; + GBytes *content; gsize pos; char *etag; gboolean failed; @@ -6881,6 +7167,7 @@ typedef struct { static void replace_contents_data_free (ReplaceContentsData *data) { + g_bytes_unref (data->content); g_free (data->etag); g_free (data); } @@ -6931,16 +7218,20 @@ replace_contents_write_callback (GObject *obj, } else if (write_size > 0) { + const gchar *content; + gsize length; + + content = g_bytes_get_data (data->content, &length); data->pos += write_size; - if (data->pos >= data->length) + if (data->pos >= length) g_output_stream_close_async (stream, 0, g_task_get_cancellable (data->task), replace_contents_close_callback, data); else g_output_stream_write_async (stream, - data->content + data->pos, - data->length - data->pos, + content + data->pos, + length - data->pos, 0, g_task_get_cancellable (data->task), replace_contents_write_callback, @@ -6962,9 +7253,13 @@ replace_contents_open_callback (GObject *obj, if (stream) { + const gchar *content; + gsize length; + + content = g_bytes_get_data (data->content, &length); g_output_stream_write_async (G_OUTPUT_STREAM (stream), - data->content + data->pos, - data->length - data->pos, + content + data->pos, + length - data->pos, 0, g_task_get_cancellable (data->task), replace_contents_write_callback, @@ -6982,7 +7277,7 @@ replace_contents_open_callback (GObject *obj, * @file: input #GFile * @contents: (element-type guint8) (array length=length): string of contents to replace the file with * @length: the length of @contents in bytes - * @etag: (allow-none): a new entity tag for the @file, or %NULL + * @etag: (allow-none): a new [entity tag][gfile-etag] for the @file, or %NULL * @make_backup: %TRUE if a backup should be created * @flags: a set of #GFileCreateFlags * @cancellable: optional #GCancellable object, %NULL to ignore @@ -7003,6 +7298,11 @@ replace_contents_open_callback (GObject *obj, * * If @make_backup is %TRUE, this function will attempt to * make a backup of @file. + * + * Note that no copy of @content will be made, so it must stay valid + * until @callback is called. See g_file_replace_contents_bytes_async() + * for a #GBytes version that will automatically hold a reference to the + * contents (without copying) for the duration of the call. */ void g_file_replace_contents_async (GFile *file, @@ -7015,6 +7315,46 @@ g_file_replace_contents_async (GFile *file, GAsyncReadyCallback callback, gpointer user_data) { + GBytes *bytes; + + bytes = g_bytes_new_static (contents, length); + g_file_replace_contents_bytes_async (file, bytes, etag, make_backup, flags, + cancellable, callback, user_data); + g_bytes_unref (bytes); +} + +/** + * g_file_replace_contents_bytes_async: + * @file: input #GFile + * @contents: a #GBytes + * @etag: (allow-none): a new [entity tag][gfile-etag] for the @file, or %NULL + * @make_backup: %TRUE if a backup should be created + * @flags: a set of #GFileCreateFlags + * @cancellable: 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 + * + * Same as g_file_replace_contents_async() but takes a #GBytes input instead. + * This function will keep a ref on @contents until the operation is done. + * Unlike g_file_replace_contents_async() this allows forgetting about the + * content without waiting for the callback. + * + * When this operation has completed, @callback will be called with + * @user_user data, and the operation can be finalized with + * g_file_replace_contents_finish(). + * + * Since: 2.40 + */ +void +g_file_replace_contents_bytes_async (GFile *file, + GBytes *contents, + const char *etag, + gboolean make_backup, + GFileCreateFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ ReplaceContentsData *data; g_return_if_fail (G_IS_FILE (file)); @@ -7022,8 +7362,7 @@ g_file_replace_contents_async (GFile *file, data = g_new0 (ReplaceContentsData, 1); - data->content = contents; - data->length = length; + data->content = g_bytes_ref (contents); data->task = g_task_new (file, cancellable, callback, user_data); g_task_set_task_data (data->task, data, (GDestroyNotify)replace_contents_data_free); @@ -7042,7 +7381,7 @@ g_file_replace_contents_async (GFile *file, * g_file_replace_contents_finish: * @file: input #GFile * @res: a #GAsyncResult - * @new_etag: (out) (allow-none): a location of a new entity tag + * @new_etag: (out) (allow-none): a location of a new [entity tag][gfile-etag] * for the document. This should be freed with g_free() when it is no * longer needed, or %NULL * @error: a #GError, or %NULL @@ -7081,6 +7420,285 @@ g_file_replace_contents_finish (GFile *file, return TRUE; } +gboolean +g_file_real_measure_disk_usage (GFile *file, + GFileMeasureFlags flags, + GCancellable *cancellable, + GFileMeasureProgressCallback progress_callback, + gpointer progress_data, + guint64 *disk_usage, + guint64 *num_dirs, + guint64 *num_files, + GError **error) +{ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Operation not supported for the current backend."); + return FALSE; +} + +typedef struct +{ + GFileMeasureFlags flags; + GFileMeasureProgressCallback progress_callback; + gpointer progress_data; +} MeasureTaskData; + +typedef struct +{ + guint64 disk_usage; + guint64 num_dirs; + guint64 num_files; +} MeasureResult; + +typedef struct +{ + GFileMeasureProgressCallback callback; + gpointer user_data; + gboolean reporting; + guint64 current_size; + guint64 num_dirs; + guint64 num_files; +} MeasureProgress; + +static gboolean +measure_disk_usage_invoke_progress (gpointer user_data) +{ + MeasureProgress *progress = user_data; + + (* progress->callback) (progress->reporting, + progress->current_size, progress->num_dirs, progress->num_files, + progress->user_data); + + return FALSE; +} + +static void +measure_disk_usage_progress (gboolean reporting, + guint64 current_size, + guint64 num_dirs, + guint64 num_files, + gpointer user_data) +{ + MeasureProgress progress; + GTask *task = user_data; + MeasureTaskData *data; + + data = g_task_get_task_data (task); + + progress.callback = data->progress_callback; + progress.user_data = data->progress_data; + progress.reporting = reporting; + progress.current_size = current_size; + progress.num_dirs = num_dirs; + progress.num_files = num_files; + + g_main_context_invoke_full (g_task_get_context (task), + g_task_get_priority (task), + measure_disk_usage_invoke_progress, + g_memdup (&progress, sizeof progress), + g_free); +} + +static void +measure_disk_usage_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + MeasureTaskData *data = task_data; + GError *error = NULL; + MeasureResult result; + + if (g_file_measure_disk_usage (source_object, data->flags, cancellable, + data->progress_callback ? measure_disk_usage_progress : NULL, task, + &result.disk_usage, &result.num_dirs, &result.num_files, + &error)) + g_task_return_pointer (task, g_memdup (&result, sizeof result), g_free); + else + g_task_return_error (task, error); +} + +static void +g_file_real_measure_disk_usage_async (GFile *file, + GFileMeasureFlags flags, + gint io_priority, + GCancellable *cancellable, + GFileMeasureProgressCallback progress_callback, + gpointer progress_data, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MeasureTaskData data; + GTask *task; + + data.flags = flags; + data.progress_callback = progress_callback; + data.progress_data = progress_data; + + task = g_task_new (file, cancellable, callback, user_data); + g_task_set_task_data (task, g_memdup (&data, sizeof data), g_free); + g_task_set_priority (task, io_priority); + + g_task_run_in_thread (task, measure_disk_usage_thread); + g_object_unref (task); +} + +static gboolean +g_file_real_measure_disk_usage_finish (GFile *file, + GAsyncResult *result, + guint64 *disk_usage, + guint64 *num_dirs, + guint64 *num_files, + GError **error) +{ + MeasureResult *measure_result; + + g_return_val_if_fail (g_task_is_valid (result, file), FALSE); + + measure_result = g_task_propagate_pointer (G_TASK (result), error); + + if (measure_result == NULL) + return FALSE; + + if (disk_usage) + *disk_usage = measure_result->disk_usage; + + if (num_dirs) + *num_dirs = measure_result->num_dirs; + + if (num_files) + *num_files = measure_result->num_files; + + g_free (measure_result); + + return TRUE; +} + +/** + * g_file_measure_disk_usage: + * @file: a #GFile + * @flags: #GFileMeasureFlags + * @cancellable: (allow-none): optional #GCancellable + * @progress_callback: (allow-none): a #GFileMeasureProgressCallback + * @progress_data: user_data for @progress_callback + * @disk_usage: (allow-none) (out): the number of bytes of disk space used + * @num_dirs: (allow-none) (out): the number of directories encountered + * @num_files: (allow-none) (out): the number of non-directories encountered + * @error: (allow-none): %NULL, or a pointer to a %NULL #GError pointer + * + * Recursively measures the disk usage of @file. + * + * This is essentially an analog of the 'du' command, but it also + * reports the number of directories and non-directory files encountered + * (including things like symbolic links). + * + * By default, errors are only reported against the toplevel file + * itself. Errors found while recursing are silently ignored, unless + * %G_FILE_DISK_USAGE_REPORT_ALL_ERRORS is given in @flags. + * + * The returned size, @disk_usage, is in bytes and should be formatted + * with g_format_size() in order to get something reasonable for showing + * in a user interface. + * + * @progress_callback and @progress_data can be given to request + * periodic progress updates while scanning. See the documentation for + * #GFileMeasureProgressCallback for information about when and how the + * callback will be invoked. + * + * Returns: %TRUE if successful, with the out parameters set. + * %FALSE otherwise, with @error set. + * + * Since: 2.38 + **/ +gboolean +g_file_measure_disk_usage (GFile *file, + GFileMeasureFlags flags, + GCancellable *cancellable, + GFileMeasureProgressCallback progress_callback, + gpointer progress_data, + guint64 *disk_usage, + guint64 *num_dirs, + guint64 *num_files, + GError **error) +{ + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return G_FILE_GET_IFACE (file)->measure_disk_usage (file, flags, cancellable, + progress_callback, progress_data, + disk_usage, num_dirs, num_files, + error); +} + +/** + * g_file_measure_disk_usage_async: + * @file: a #GFile + * @flags: #GFileMeasureFlags + * @io_priority: the [I/O priority][io-priority] of the request + * @cancellable: (allow-none): optional #GCancellable + * @progress_callback: (allow-none): a #GFileMeasureProgressCallback + * @progress_data: user_data for @progress_callback + * @callback: (allow-none): a #GAsyncReadyCallback to call when complete + * @user_data: the data to pass to callback function + * + * Recursively measures the disk usage of @file. + * + * This is the asynchronous version of g_file_measure_disk_usage(). See + * there for more information. + * + * Since: 2.38 + **/ +void +g_file_measure_disk_usage_async (GFile *file, + GFileMeasureFlags flags, + gint io_priority, + GCancellable *cancellable, + GFileMeasureProgressCallback progress_callback, + gpointer progress_data, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (G_IS_FILE (file)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + G_FILE_GET_IFACE (file)->measure_disk_usage_async (file, flags, io_priority, cancellable, + progress_callback, progress_data, + callback, user_data); +} + +/** + * g_file_measure_disk_usage_finish: + * @file: a #GFile + * @result: the #GAsyncResult passed to your #GAsyncReadyCallback + * @disk_usage: (allow-none) (out): the number of bytes of disk space used + * @num_dirs: (allow-none) (out): the number of directories encountered + * @num_files: (allow-none) (out): the number of non-directories encountered + * @error: (allow-none): %NULL, or a pointer to a %NULL #GError pointer + * + * Collects the results from an earlier call to + * g_file_measure_disk_usage_async(). See g_file_measure_disk_usage() for + * more information. + * + * Returns: %TRUE if successful, with the out parameters set. + * %FALSE otherwise, with @error set. + * + * Since: 2.38 + **/ +gboolean +g_file_measure_disk_usage_finish (GFile *file, + GAsyncResult *result, + guint64 *disk_usage, + guint64 *num_dirs, + guint64 *num_files, + GError **error) +{ + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return G_FILE_GET_IFACE (file)->measure_disk_usage_finish (file, result, disk_usage, num_dirs, num_files, error); +} + /** * g_file_start_mountable: * @file: input #GFile @@ -7346,11 +7964,10 @@ g_file_poll_mountable_finish (GFile *file, * g_file_supports_thread_contexts: * @file: a #GFile * - * Checks if @file supports thread-default - * contexts. If this returns %FALSE, you cannot perform - * asynchronous operations on @file in a thread that has a - * thread-default context. + * Checks if @file supports + * [thread-default contexts][g-main-context-push-thread-default-context]. + * If this returns %FALSE, you cannot perform asynchronous operations on + * @file in a thread that has a thread-default context. * * Returns: Whether or not @file supports thread-default contexts. *