mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-12 15:36:17 +01:00
GFile: add new g_file_measure_disk_usage() API
This is essentially the equivalent of 'du'. This is currently only supported on local files. gvfs will add support for the interface later. https://bugzilla.gnome.org/show_bug.cgi?id=704893
This commit is contained in:
parent
242a735fe0
commit
6ec2bb17c3
@ -71,6 +71,7 @@ GFileQueryInfoFlags
|
|||||||
GFileCreateFlags
|
GFileCreateFlags
|
||||||
GFileCopyFlags
|
GFileCopyFlags
|
||||||
GFileMonitorFlags
|
GFileMonitorFlags
|
||||||
|
GFileDiskUsageFlags
|
||||||
GFilesystemPreviewType
|
GFilesystemPreviewType
|
||||||
GFileProgressCallback
|
GFileProgressCallback
|
||||||
GFileReadMoreCallback
|
GFileReadMoreCallback
|
||||||
@ -118,6 +119,9 @@ g_file_query_filesystem_info
|
|||||||
g_file_query_filesystem_info_async
|
g_file_query_filesystem_info_async
|
||||||
g_file_query_filesystem_info_finish
|
g_file_query_filesystem_info_finish
|
||||||
g_file_query_default_handler
|
g_file_query_default_handler
|
||||||
|
g_file_query_disk_usage
|
||||||
|
g_file_query_disk_usage_async
|
||||||
|
g_file_query_disk_usage_finish
|
||||||
g_file_find_enclosing_mount
|
g_file_find_enclosing_mount
|
||||||
g_file_find_enclosing_mount_async
|
g_file_find_enclosing_mount_async
|
||||||
g_file_find_enclosing_mount_finish
|
g_file_find_enclosing_mount_finish
|
||||||
|
301
gio/gfile.c
301
gio/gfile.c
@ -316,6 +316,30 @@ static gboolean g_file_real_copy_finish (GFile
|
|||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
GError **error);
|
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;
|
typedef GFileIface GFileInterface;
|
||||||
G_DEFINE_INTERFACE (GFile, g_file, G_TYPE_OBJECT)
|
G_DEFINE_INTERFACE (GFile, g_file, G_TYPE_OBJECT)
|
||||||
|
|
||||||
@ -357,6 +381,9 @@ g_file_default_init (GFileIface *iface)
|
|||||||
iface->set_attributes_from_info = g_file_real_set_attributes_from_info;
|
iface->set_attributes_from_info = g_file_real_set_attributes_from_info;
|
||||||
iface->copy_async = g_file_real_copy_async;
|
iface->copy_async = g_file_real_copy_async;
|
||||||
iface->copy_finish = g_file_real_copy_finish;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -7336,6 +7363,280 @@ g_file_replace_contents_finish (GFile *file,
|
|||||||
return TRUE;
|
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,
|
||||||
|
measure_disk_usage_progress, 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)
|
||||||
|
{
|
||||||
|
guint64 *reported_usage;
|
||||||
|
|
||||||
|
g_return_val_if_fail (g_task_is_valid (result, file), FALSE);
|
||||||
|
|
||||||
|
reported_usage = g_task_propagate_pointer (G_TASK (result), error);
|
||||||
|
|
||||||
|
if (reported_usage == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (disk_usage)
|
||||||
|
*disk_usage = *reported_usage;
|
||||||
|
|
||||||
|
g_free (reported_usage);
|
||||||
|
|
||||||
|
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 '<literal>du</literal>' 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 <link linkend="io-priority">I/O priority</link>
|
||||||
|
* 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));
|
||||||
|
|
||||||
|
return 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:
|
* g_file_start_mountable:
|
||||||
* @file: input #GFile
|
* @file: input #GFile
|
||||||
|
53
gio/gfile.h
53
gio/gfile.h
@ -561,6 +561,30 @@ struct _GFileIface
|
|||||||
gboolean (* poll_mountable_finish) (GFile *file,
|
gboolean (* poll_mountable_finish) (GFile *file,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
gboolean (* 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);
|
||||||
|
void (* measure_disk_usage_async) (GFile *file,
|
||||||
|
GFileMeasureFlags flags,
|
||||||
|
gint io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GFileMeasureProgressCallback progress_callback,
|
||||||
|
gpointer progress_data,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean (* measure_disk_usage_finish) (GFile *file,
|
||||||
|
GAsyncResult *result,
|
||||||
|
guint64 *disk_usage,
|
||||||
|
guint64 *num_dirs,
|
||||||
|
guint64 *num_files,
|
||||||
|
GError **error);
|
||||||
};
|
};
|
||||||
|
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
@ -1085,6 +1109,35 @@ GFileMonitor* g_file_monitor (GFile
|
|||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_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);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_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);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_38
|
||||||
|
gboolean g_file_measure_disk_usage_finish (GFile *file,
|
||||||
|
GAsyncResult *result,
|
||||||
|
guint64 *disk_usage,
|
||||||
|
guint64 *num_dirs,
|
||||||
|
guint64 *num_files,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
void g_file_start_mountable (GFile *file,
|
void g_file_start_mountable (GFile *file,
|
||||||
GDriveStartFlags flags,
|
GDriveStartFlags flags,
|
||||||
|
@ -211,6 +211,29 @@ typedef enum {
|
|||||||
G_FILE_CREATE_REPLACE_DESTINATION = (1 << 1)
|
G_FILE_CREATE_REPLACE_DESTINATION = (1 << 1)
|
||||||
} GFileCreateFlags;
|
} GFileCreateFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GFileMeasureFlags:
|
||||||
|
* @G_FILE_MEASURE_NONE: No flags set.
|
||||||
|
* @G_FILE_MEASURE_REPORT_ANY_ERROR: Report any error encountered
|
||||||
|
* while traversing the directory tree. Normally errors are only
|
||||||
|
* reported for the toplevel file.
|
||||||
|
* @G_FILE_MEASURE_APPARENT_SIZE: Tally usage based on apparent file
|
||||||
|
* sizes. Normally, the block-size is used, if available, as this is a
|
||||||
|
* more accurate representation of disk space used.
|
||||||
|
* Compare with '<literal>du --apparent-size</literal>'.
|
||||||
|
* @G_FILE_MEASURE_NO_XDEV: Do not cross mount point boundaries.
|
||||||
|
* Compare with '<literal>du -x</literal>'.
|
||||||
|
*
|
||||||
|
* Flags that can be used with g_file_measure_disk_usage().
|
||||||
|
*
|
||||||
|
* Since: 2.38
|
||||||
|
**/
|
||||||
|
typedef enum {
|
||||||
|
G_FILE_MEASURE_NONE = 0,
|
||||||
|
G_FILE_MEASURE_REPORT_ANY_ERROR = (1 << 1),
|
||||||
|
G_FILE_MEASURE_APPARENT_SIZE = (1 << 2),
|
||||||
|
G_FILE_MEASURE_NO_XDEV = (1 << 3)
|
||||||
|
} GFileMeasureFlags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GMountMountFlags:
|
* GMountMountFlags:
|
||||||
|
@ -295,6 +295,49 @@ typedef gboolean (* GFileReadMoreCallback) (const char *file_contents,
|
|||||||
goffset file_size,
|
goffset file_size,
|
||||||
gpointer callback_data);
|
gpointer callback_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GFileMeasureProgressCallback:
|
||||||
|
* @reporting: %TRUE if more reports will come
|
||||||
|
* @current_size: the current cumulative size measurement
|
||||||
|
* @num_dirs: the number of directories visited so far
|
||||||
|
* @num_files: the number of non-directory files encountered
|
||||||
|
* @user_data: the data passed to the original request for this callback
|
||||||
|
*
|
||||||
|
* This callback type is used by g_file_measure_disk_usage() to make
|
||||||
|
* periodic progress reports when measuring the amount of disk spaced
|
||||||
|
* used by a directory.
|
||||||
|
*
|
||||||
|
* These calls are made on a best-effort basis and not all types of
|
||||||
|
* #GFile will support them. At the minimum, however, one call will
|
||||||
|
* always be made immediately.
|
||||||
|
*
|
||||||
|
* In the case that there is no support, @reporting will be set to
|
||||||
|
* %FALSE (and the other values undefined) and no further calls will be
|
||||||
|
* made. Otherwise, the @reporting will be %TRUE and the other values
|
||||||
|
* all-zeros during the first (immediate) call. In this way, you can
|
||||||
|
* know which type of progress UI to show without a delay.
|
||||||
|
*
|
||||||
|
* For g_file_measure_disk_usage() the callback is made directly. For
|
||||||
|
* g_file_measure_disk_usage_async() the callback is made via the
|
||||||
|
* default main context of the calling thread (ie: the same way that the
|
||||||
|
* final async result would be reported).
|
||||||
|
*
|
||||||
|
* @current_size is in the same units as requested by the operation (see
|
||||||
|
* %G_FILE_DISK_USAGE_APPARENT_SIZE).
|
||||||
|
*
|
||||||
|
* The frequency of the updates is implementation defined, but is
|
||||||
|
* ideally about once every 200ms.
|
||||||
|
*
|
||||||
|
* The last progress callback may or may not be equal to the final
|
||||||
|
* result. Always check the async result to get the final value.
|
||||||
|
*
|
||||||
|
* Since: 2.38
|
||||||
|
**/
|
||||||
|
typedef void (* GFileMeasureProgressCallback) (gboolean reporting,
|
||||||
|
guint64 current_size,
|
||||||
|
guint64 num_dirs,
|
||||||
|
guint64 num_files,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GIOSchedulerJobFunc:
|
* GIOSchedulerJobFunc:
|
||||||
|
289
gio/glocalfile.c
289
gio/glocalfile.c
@ -27,6 +27,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
@ -2514,6 +2515,293 @@ g_local_file_monitor_file (GFile *file,
|
|||||||
return _g_local_file_monitor_new (local_file->filename, flags, is_remote (local_file->filename), error);
|
return _g_local_file_monitor_new (local_file->filename, flags, is_remote (local_file->filename), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Here is the GLocalFile implementation of g_file_measure_disk_usage().
|
||||||
|
*
|
||||||
|
* If available, we use fopenat() in preference to filenames for
|
||||||
|
* efficiency and safety reasons. We know that fopenat() is available
|
||||||
|
* based on if AT_FDCWD is defined. POSIX guarantees that this will be
|
||||||
|
* defined as a macro.
|
||||||
|
*
|
||||||
|
* We use a linked list of stack-allocated GSList nodes in order to be
|
||||||
|
* able to reconstruct the filename for error messages. We actually
|
||||||
|
* pass the filename to operate on through the top node of the list.
|
||||||
|
*
|
||||||
|
* In case we're using openat(), this top filename will be a basename
|
||||||
|
* which should be opened in the directory which has also had its fd
|
||||||
|
* passed along. If we're not using openat() then it will be a full
|
||||||
|
* absolute filename.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_local_file_measure_size_error (GFileMeasureFlags flags,
|
||||||
|
gint saved_errno,
|
||||||
|
GSList *name,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
/* Only report an error if we were at the toplevel or if the caller
|
||||||
|
* requested reporting of all errors.
|
||||||
|
*/
|
||||||
|
if ((name->next == NULL) || (flags & G_FILE_MEASURE_REPORT_ANY_ERROR))
|
||||||
|
{
|
||||||
|
GString *filename;
|
||||||
|
GSList *node;
|
||||||
|
|
||||||
|
/* Skip some work if there is no error return */
|
||||||
|
if (!error)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
#ifdef AT_FDCWD
|
||||||
|
/* If using openat() we need to rebuild the filename for the message */
|
||||||
|
filename = g_string_new (name->data);
|
||||||
|
for (node = name->next; node; node = node->next)
|
||||||
|
{
|
||||||
|
g_string_prepend_c (filename, G_DIR_SEPARATOR);
|
||||||
|
g_string_prepend (filename, node->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_string_prepend (filename, "file://");
|
||||||
|
#else
|
||||||
|
/* Otherwise, we already have it, so just use it. */
|
||||||
|
node = name;
|
||||||
|
filename = g_string_new ("file://");
|
||||||
|
g_string_append (filename, node->data);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (saved_errno),
|
||||||
|
_("Could not determine the disk usage of %s: %s"),
|
||||||
|
filename->str, g_strerror (saved_errno));
|
||||||
|
|
||||||
|
g_string_free (filename, TRUE);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
/* We're not reporting this error... */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GFileMeasureFlags flags;
|
||||||
|
dev_t contained_on;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
|
||||||
|
GFileMeasureProgressCallback progress_callback;
|
||||||
|
gpointer progress_data;
|
||||||
|
|
||||||
|
guint64 disk_usage;
|
||||||
|
guint64 num_dirs;
|
||||||
|
guint64 num_files;
|
||||||
|
|
||||||
|
guint64 last_progress_report;
|
||||||
|
} MeasureState;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_local_file_measure_size_of_contents (gint fd,
|
||||||
|
GSList *dir_name,
|
||||||
|
MeasureState *state,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_local_file_measure_size_of_file (gint parent_fd,
|
||||||
|
GSList *name,
|
||||||
|
MeasureState *state,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
struct stat buf;
|
||||||
|
|
||||||
|
if (g_cancellable_set_error_if_cancelled (state->cancellable, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
#if defined (AT_FDCWD)
|
||||||
|
if (fstatat (parent_fd, name->data, &buf, AT_SYMLINK_NOFOLLOW) != 0)
|
||||||
|
#elif defined (HAVE_LSTAT)
|
||||||
|
if (lstat (name->data, &buf) != 0)
|
||||||
|
#else
|
||||||
|
if (stat (name->data, &buf) != 0)
|
||||||
|
#endif
|
||||||
|
return g_local_file_measure_size_error (state->flags, errno, name, error);
|
||||||
|
|
||||||
|
if (name->next)
|
||||||
|
{
|
||||||
|
/* If not at the toplevel, check for a device boundary. */
|
||||||
|
|
||||||
|
if (state->flags & G_FILE_MEASURE_NO_XDEV)
|
||||||
|
if (state->contained_on != buf.st_dev)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If, however, this is the toplevel, set the device number so
|
||||||
|
* that recursive invocations can compare against it.
|
||||||
|
*/
|
||||||
|
state->contained_on = buf.st_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
|
||||||
|
if (~state->flags & G_FILE_MEASURE_APPARENT_SIZE)
|
||||||
|
state->disk_usage += buf.st_blocks * G_GUINT64_CONSTANT (512);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
state->disk_usage += buf.st_size;
|
||||||
|
|
||||||
|
if (S_ISDIR (buf.st_mode))
|
||||||
|
state->num_dirs++;
|
||||||
|
else
|
||||||
|
state->num_files++;
|
||||||
|
|
||||||
|
if (state->progress_callback)
|
||||||
|
{
|
||||||
|
/* We could attempt to do some cleverness here in order to avoid
|
||||||
|
* calling clock_gettime() so much, but we're doing stats and opens
|
||||||
|
* all over the place already...
|
||||||
|
*/
|
||||||
|
if (state->last_progress_report)
|
||||||
|
{
|
||||||
|
guint64 now;
|
||||||
|
|
||||||
|
now = g_get_monotonic_time ();
|
||||||
|
|
||||||
|
if (state->last_progress_report + 200 * G_TIME_SPAN_MILLISECOND < now)
|
||||||
|
{
|
||||||
|
(* state->progress_callback) (TRUE,
|
||||||
|
state->disk_usage, state->num_dirs, state->num_files,
|
||||||
|
state->progress_data);
|
||||||
|
state->last_progress_report = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We must do an initial report to inform that more reports
|
||||||
|
* will be coming.
|
||||||
|
*/
|
||||||
|
(* state->progress_callback) (TRUE, 0, 0, 0, state->progress_data);
|
||||||
|
state->last_progress_report = g_get_monotonic_time ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR (buf.st_mode))
|
||||||
|
{
|
||||||
|
int dir_fd = -1;
|
||||||
|
|
||||||
|
if (g_cancellable_set_error_if_cancelled (state->cancellable, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
#ifdef AT_FDCWD
|
||||||
|
dir_fd = openat (parent_fd, name->data, O_RDONLY | O_DIRECTORY);
|
||||||
|
if (dir_fd < 0)
|
||||||
|
return g_local_file_measure_size_error (state->flags, errno, name, error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!g_local_file_measure_size_of_contents (dir_fd, name, state, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_local_file_measure_size_of_contents (gint fd,
|
||||||
|
GSList *dir_name,
|
||||||
|
MeasureState *state,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gboolean success = TRUE;
|
||||||
|
struct dirent *entry;
|
||||||
|
DIR *dirp;
|
||||||
|
|
||||||
|
#ifdef AT_FDCWD
|
||||||
|
dirp = fdopendir (fd);
|
||||||
|
#else
|
||||||
|
dirp = opendir (dir_name->data);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dirp == NULL)
|
||||||
|
{
|
||||||
|
gint saved_errno = errno;
|
||||||
|
|
||||||
|
#ifdef AT_FDCWD
|
||||||
|
close (fd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return g_local_file_measure_size_error (state->flags, saved_errno, dir_name, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (success && (entry = readdir (dirp)))
|
||||||
|
{
|
||||||
|
gchar *name = entry->d_name;
|
||||||
|
GSList node;
|
||||||
|
|
||||||
|
node.next = dir_name;
|
||||||
|
#ifdef AT_FDCWD
|
||||||
|
node.data = name;
|
||||||
|
#else
|
||||||
|
node.data = g_build_filename (dir_name->data, name, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* skip '.' and '..' */
|
||||||
|
if (name[0] == '.' &&
|
||||||
|
(name[1] == '\0' ||
|
||||||
|
(name[1] == '.' && name[2] == '\0')))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
success = g_local_file_measure_size_of_file (fd, &node, state, error);
|
||||||
|
|
||||||
|
#ifndef AT_FDCWD
|
||||||
|
g_free (node.data);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir (dirp);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_local_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)
|
||||||
|
{
|
||||||
|
GLocalFile *local_file = G_LOCAL_FILE (file);
|
||||||
|
MeasureState state = { 0, };
|
||||||
|
gint root_fd = -1;
|
||||||
|
GSList node;
|
||||||
|
|
||||||
|
state.flags = flags;
|
||||||
|
state.cancellable = cancellable;
|
||||||
|
state.progress_callback = progress_callback;
|
||||||
|
state.progress_data = progress_data;
|
||||||
|
|
||||||
|
#ifdef AT_FDCWD
|
||||||
|
root_fd = AT_FDCWD;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
node.data = local_file->filename;
|
||||||
|
node.next = NULL;
|
||||||
|
|
||||||
|
if (!g_local_file_measure_size_of_file (root_fd, &node, &state, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (disk_usage)
|
||||||
|
*disk_usage = state.disk_usage;
|
||||||
|
|
||||||
|
if (num_dirs)
|
||||||
|
*num_dirs = state.num_dirs;
|
||||||
|
|
||||||
|
if (num_files)
|
||||||
|
*num_files = state.num_files;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
g_local_file_file_iface_init (GFileIface *iface)
|
g_local_file_file_iface_init (GFileIface *iface)
|
||||||
{
|
{
|
||||||
@ -2556,6 +2844,7 @@ g_local_file_file_iface_init (GFileIface *iface)
|
|||||||
iface->move = g_local_file_move;
|
iface->move = g_local_file_move;
|
||||||
iface->monitor_dir = g_local_file_monitor_dir;
|
iface->monitor_dir = g_local_file_monitor_dir;
|
||||||
iface->monitor_file = g_local_file_monitor_file;
|
iface->monitor_file = g_local_file_monitor_file;
|
||||||
|
iface->measure_disk_usage = g_local_file_measure_disk_usage;
|
||||||
|
|
||||||
iface->supports_thread_contexts = TRUE;
|
iface->supports_thread_contexts = TRUE;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user