Imported Upstream version 2.66.6
[platform/upstream/glib.git] / gio / gdrive.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: Alexander Larsson <alexl@redhat.com>
19  *         David Zeuthen <davidz@redhat.com>
20  */
21
22 #include "config.h"
23 #include "gdrive.h"
24 #include "gtask.h"
25 #include "gthemedicon.h"
26 #include "gasyncresult.h"
27 #include "gioerror.h"
28 #include "glibintl.h"
29
30
31 /**
32  * SECTION:gdrive
33  * @short_description: Drive management
34  * @include: gio/gio.h
35  *
36  * #GDrive - this represent a piece of hardware connected to the machine.
37  * It's generally only created for removable hardware or hardware with
38  * removable media.
39  *
40  * #GDrive is a container class for #GVolume objects that stem from
41  * the same piece of media. As such, #GDrive abstracts a drive with
42  * (or without) removable media and provides operations for querying
43  * whether media is available, determining whether media change is
44  * automatically detected and ejecting the media.
45  *
46  * If the #GDrive reports that media isn't automatically detected, one
47  * can poll for media; typically one should not do this periodically
48  * as a poll for media operation is potentially expensive and may
49  * spin up the drive creating noise.
50  *
51  * #GDrive supports starting and stopping drives with authentication
52  * support for the former. This can be used to support a diverse set
53  * of use cases including connecting/disconnecting iSCSI devices,
54  * powering down external disk enclosures and starting/stopping
55  * multi-disk devices such as RAID devices. Note that the actual
56  * semantics and side-effects of starting/stopping a #GDrive may vary
57  * according to implementation. To choose the correct verbs in e.g. a
58  * file manager, use g_drive_get_start_stop_type().
59  *
60  * For porting from GnomeVFS note that there is no equivalent of
61  * #GDrive in that API.
62  **/
63
64 typedef GDriveIface GDriveInterface;
65 G_DEFINE_INTERFACE(GDrive, g_drive, G_TYPE_OBJECT)
66
67 static void
68 g_drive_default_init (GDriveInterface *iface)
69 {
70   /**
71    * GDrive::changed:
72    * @drive: a #GDrive.
73    *
74    * Emitted when the drive's state has changed.
75    **/
76   g_signal_new (I_("changed"),
77                 G_TYPE_DRIVE,
78                 G_SIGNAL_RUN_LAST,
79                 G_STRUCT_OFFSET (GDriveIface, changed),
80                 NULL, NULL,
81                 NULL,
82                 G_TYPE_NONE, 0);
83
84   /**
85    * GDrive::disconnected:
86    * @drive: a #GDrive.
87    *
88    * This signal is emitted when the #GDrive have been
89    * disconnected. If the recipient is holding references to the
90    * object they should release them so the object can be
91    * finalized.
92    **/
93   g_signal_new (I_("disconnected"),
94                 G_TYPE_DRIVE,
95                 G_SIGNAL_RUN_LAST,
96                 G_STRUCT_OFFSET (GDriveIface, disconnected),
97                 NULL, NULL,
98                 NULL,
99                 G_TYPE_NONE, 0);
100
101   /**
102    * GDrive::eject-button:
103    * @drive: a #GDrive.
104    *
105    * Emitted when the physical eject button (if any) of a drive has
106    * been pressed.
107    **/
108   g_signal_new (I_("eject-button"),
109                 G_TYPE_DRIVE,
110                 G_SIGNAL_RUN_LAST,
111                 G_STRUCT_OFFSET (GDriveIface, eject_button),
112                 NULL, NULL,
113                 NULL,
114                 G_TYPE_NONE, 0);
115
116   /**
117    * GDrive::stop-button:
118    * @drive: a #GDrive.
119    *
120    * Emitted when the physical stop button (if any) of a drive has
121    * been pressed.
122    *
123    * Since: 2.22
124    **/
125   g_signal_new (I_("stop-button"),
126                 G_TYPE_DRIVE,
127                 G_SIGNAL_RUN_LAST,
128                 G_STRUCT_OFFSET (GDriveIface, stop_button),
129                 NULL, NULL,
130                 NULL,
131                 G_TYPE_NONE, 0);
132 }
133
134 /**
135  * g_drive_get_name:
136  * @drive: a #GDrive.
137  * 
138  * Gets the name of @drive.
139  *
140  * Returns: a string containing @drive's name. The returned 
141  *     string should be freed when no longer needed.
142  **/
143 char *
144 g_drive_get_name (GDrive *drive)
145 {
146   GDriveIface *iface;
147
148   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
149
150   iface = G_DRIVE_GET_IFACE (drive);
151
152   return (* iface->get_name) (drive);
153 }
154
155 /**
156  * g_drive_get_icon:
157  * @drive: a #GDrive.
158  * 
159  * Gets the icon for @drive.
160  * 
161  * Returns: (transfer full): #GIcon for the @drive.
162  *    Free the returned object with g_object_unref().
163  **/
164 GIcon *
165 g_drive_get_icon (GDrive *drive)
166 {
167   GDriveIface *iface;
168   
169   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
170
171   iface = G_DRIVE_GET_IFACE (drive);
172
173   return (* iface->get_icon) (drive);
174 }
175
176 /**
177  * g_drive_get_symbolic_icon:
178  * @drive: a #GDrive.
179  * 
180  * Gets the icon for @drive.
181  * 
182  * Returns: (transfer full): symbolic #GIcon for the @drive.
183  *    Free the returned object with g_object_unref().
184  *
185  * Since: 2.34
186  **/
187 GIcon *
188 g_drive_get_symbolic_icon (GDrive *drive)
189 {
190   GDriveIface *iface;
191   GIcon *ret;
192
193   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
194
195   iface = G_DRIVE_GET_IFACE (drive);
196
197   if (iface->get_symbolic_icon != NULL)
198     ret = iface->get_symbolic_icon (drive);
199   else
200     ret = g_themed_icon_new_with_default_fallbacks ("drive-removable-media-symbolic");
201
202   return ret;
203 }
204
205 /**
206  * g_drive_has_volumes:
207  * @drive: a #GDrive.
208  * 
209  * Check if @drive has any mountable volumes.
210  * 
211  * Returns: %TRUE if the @drive contains volumes, %FALSE otherwise.
212  **/
213 gboolean
214 g_drive_has_volumes (GDrive *drive)
215 {
216   GDriveIface *iface;
217
218   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
219
220   iface = G_DRIVE_GET_IFACE (drive);
221
222   return (* iface->has_volumes) (drive);
223 }
224
225 /**
226  * g_drive_get_volumes:
227  * @drive: a #GDrive.
228  * 
229  * Get a list of mountable volumes for @drive.
230  *
231  * The returned list should be freed with g_list_free(), after
232  * its elements have been unreffed with g_object_unref().
233  * 
234  * Returns: (element-type GVolume) (transfer full): #GList containing any #GVolume objects on the given @drive.
235  **/
236 GList *
237 g_drive_get_volumes (GDrive *drive)
238 {
239   GDriveIface *iface;
240
241   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
242
243   iface = G_DRIVE_GET_IFACE (drive);
244
245   return (* iface->get_volumes) (drive);
246 }
247
248 /**
249  * g_drive_is_media_check_automatic:
250  * @drive: a #GDrive.
251  * 
252  * Checks if @drive is capable of automatically detecting media changes.
253  * 
254  * Returns: %TRUE if the @drive is capable of automatically detecting
255  *     media changes, %FALSE otherwise.
256  **/
257 gboolean
258 g_drive_is_media_check_automatic (GDrive *drive)
259 {
260   GDriveIface *iface;
261
262   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
263
264   iface = G_DRIVE_GET_IFACE (drive);
265
266   return (* iface->is_media_check_automatic) (drive);
267 }
268
269 /**
270  * g_drive_is_removable:
271  * @drive: a #GDrive.
272  *
273  * Checks if the #GDrive and/or its media is considered removable by the user.
274  * See g_drive_is_media_removable().
275  *
276  * Returns: %TRUE if @drive and/or its media is considered removable, %FALSE otherwise.
277  *
278  * Since: 2.50
279  **/
280 gboolean
281 g_drive_is_removable (GDrive *drive)
282 {
283   GDriveIface *iface;
284
285   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
286
287   iface = G_DRIVE_GET_IFACE (drive);
288   if (iface->is_removable != NULL)
289     return iface->is_removable (drive);
290
291   return FALSE;
292 }
293
294 /**
295  * g_drive_is_media_removable:
296  * @drive: a #GDrive.
297  * 
298  * Checks if the @drive supports removable media.
299  * 
300  * Returns: %TRUE if @drive supports removable media, %FALSE otherwise.
301  **/
302 gboolean
303 g_drive_is_media_removable (GDrive *drive)
304 {
305   GDriveIface *iface;
306
307   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
308
309   iface = G_DRIVE_GET_IFACE (drive);
310
311   return (* iface->is_media_removable) (drive);
312 }
313
314 /**
315  * g_drive_has_media:
316  * @drive: a #GDrive.
317  * 
318  * Checks if the @drive has media. Note that the OS may not be polling
319  * the drive for media changes; see g_drive_is_media_check_automatic()
320  * for more details.
321  * 
322  * Returns: %TRUE if @drive has media, %FALSE otherwise.
323  **/
324 gboolean
325 g_drive_has_media (GDrive *drive)
326 {
327   GDriveIface *iface;
328
329   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
330
331   iface = G_DRIVE_GET_IFACE (drive);
332
333   return (* iface->has_media) (drive);
334 }
335
336 /**
337  * g_drive_can_eject:
338  * @drive: a #GDrive.
339  * 
340  * Checks if a drive can be ejected.
341  * 
342  * Returns: %TRUE if the @drive can be ejected, %FALSE otherwise.
343  **/
344 gboolean
345 g_drive_can_eject (GDrive *drive)
346 {
347   GDriveIface *iface;
348
349   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
350
351   iface = G_DRIVE_GET_IFACE (drive);
352
353   if (iface->can_eject == NULL)
354     return FALSE;
355
356   return (* iface->can_eject) (drive);
357 }
358
359 /**
360  * g_drive_can_poll_for_media:
361  * @drive: a #GDrive.
362  * 
363  * Checks if a drive can be polled for media changes.
364  * 
365  * Returns: %TRUE if the @drive can be polled for media changes,
366  *     %FALSE otherwise.
367  **/
368 gboolean
369 g_drive_can_poll_for_media (GDrive *drive)
370 {
371   GDriveIface *iface;
372
373   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
374
375   iface = G_DRIVE_GET_IFACE (drive);
376
377   if (iface->poll_for_media == NULL)
378     return FALSE;
379
380   return (* iface->can_poll_for_media) (drive);
381 }
382
383 /**
384  * g_drive_eject:
385  * @drive: a #GDrive.
386  * @flags: flags affecting the unmount if required for eject
387  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
388  * @callback: (nullable): a #GAsyncReadyCallback, or %NULL.
389  * @user_data: user data to pass to @callback
390  * 
391  * Asynchronously ejects a drive.
392  *
393  * When the operation is finished, @callback will be called.
394  * You can then call g_drive_eject_finish() to obtain the
395  * result of the operation.
396  *
397  * Deprecated: 2.22: Use g_drive_eject_with_operation() instead.
398  **/
399 void
400 g_drive_eject (GDrive              *drive,
401                GMountUnmountFlags   flags,
402                GCancellable        *cancellable,
403                GAsyncReadyCallback  callback,
404                gpointer             user_data)
405 {
406   GDriveIface *iface;
407
408   g_return_if_fail (G_IS_DRIVE (drive));
409
410   iface = G_DRIVE_GET_IFACE (drive);
411
412   if (iface->eject == NULL)
413     {
414       g_task_report_new_error (drive, callback, user_data,
415                                g_drive_eject_with_operation,
416                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
417                                _("drive doesn’t implement eject"));
418       return;
419     }
420   
421   (* iface->eject) (drive, flags, cancellable, callback, user_data);
422 }
423
424 /**
425  * g_drive_eject_finish:
426  * @drive: a #GDrive.
427  * @result: a #GAsyncResult.
428  * @error: a #GError, or %NULL
429  * 
430  * Finishes ejecting a drive.
431  * 
432  * Returns: %TRUE if the drive has been ejected successfully,
433  *     %FALSE otherwise.
434  *
435  * Deprecated: 2.22: Use g_drive_eject_with_operation_finish() instead.
436  **/
437 gboolean
438 g_drive_eject_finish (GDrive        *drive,
439                       GAsyncResult  *result,
440                       GError       **error)
441 {
442   GDriveIface *iface;
443
444   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
445   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
446
447   if (g_async_result_legacy_propagate_error (result, error))
448     return FALSE;
449   else if (g_async_result_is_tagged (result, g_drive_eject_with_operation))
450     return g_task_propagate_boolean (G_TASK (result), error);
451
452   iface = G_DRIVE_GET_IFACE (drive);
453   
454   return (* iface->eject_finish) (drive, result, error);
455 }
456
457 /**
458  * g_drive_eject_with_operation:
459  * @drive: a #GDrive.
460  * @flags: flags affecting the unmount if required for eject
461  * @mount_operation: (nullable): a #GMountOperation or %NULL to avoid
462  *     user interaction.
463  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
464  * @callback: (nullable): a #GAsyncReadyCallback, or %NULL.
465  * @user_data: user data passed to @callback.
466  *
467  * Ejects a drive. This is an asynchronous operation, and is
468  * finished by calling g_drive_eject_with_operation_finish() with the @drive
469  * and #GAsyncResult data returned in the @callback.
470  *
471  * Since: 2.22
472  **/
473 void
474 g_drive_eject_with_operation (GDrive              *drive,
475                               GMountUnmountFlags   flags,
476                               GMountOperation     *mount_operation,
477                               GCancellable        *cancellable,
478                               GAsyncReadyCallback  callback,
479                               gpointer             user_data)
480 {
481   GDriveIface *iface;
482
483   g_return_if_fail (G_IS_DRIVE (drive));
484
485   iface = G_DRIVE_GET_IFACE (drive);
486
487   if (iface->eject == NULL && iface->eject_with_operation == NULL)
488     {
489       g_task_report_new_error (drive, callback, user_data,
490                                g_drive_eject_with_operation,
491                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
492                                /* Translators: This is an error
493                                 * message for drive objects that
494                                 * don't implement any of eject or eject_with_operation. */
495                                _("drive doesn’t implement eject or eject_with_operation"));
496       return;
497     }
498
499   if (iface->eject_with_operation != NULL)
500     (* iface->eject_with_operation) (drive, flags, mount_operation, cancellable, callback, user_data);
501   else
502     (* iface->eject) (drive, flags, cancellable, callback, user_data);
503 }
504
505 /**
506  * g_drive_eject_with_operation_finish:
507  * @drive: a #GDrive.
508  * @result: a #GAsyncResult.
509  * @error: a #GError location to store the error occurring, or %NULL to
510  *     ignore.
511  *
512  * Finishes ejecting a drive. If any errors occurred during the operation,
513  * @error will be set to contain the errors and %FALSE will be returned.
514  *
515  * Returns: %TRUE if the drive was successfully ejected. %FALSE otherwise.
516  *
517  * Since: 2.22
518  **/
519 gboolean
520 g_drive_eject_with_operation_finish (GDrive        *drive,
521                                      GAsyncResult  *result,
522                                      GError       **error)
523 {
524   GDriveIface *iface;
525
526   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
527   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
528
529   if (g_async_result_legacy_propagate_error (result, error))
530     return FALSE;
531   else if (g_async_result_is_tagged (result, g_drive_eject_with_operation))
532     return g_task_propagate_boolean (G_TASK (result), error);
533
534   iface = G_DRIVE_GET_IFACE (drive);
535   if (iface->eject_with_operation_finish != NULL)
536     return (* iface->eject_with_operation_finish) (drive, result, error);
537   else
538     return (* iface->eject_finish) (drive, result, error);
539 }
540
541 /**
542  * g_drive_poll_for_media:
543  * @drive: a #GDrive.
544  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
545  * @callback: (nullable): a #GAsyncReadyCallback, or %NULL.
546  * @user_data: user data to pass to @callback
547  * 
548  * Asynchronously polls @drive to see if media has been inserted or removed.
549  * 
550  * When the operation is finished, @callback will be called.
551  * You can then call g_drive_poll_for_media_finish() to obtain the
552  * result of the operation.
553  **/
554 void
555 g_drive_poll_for_media (GDrive              *drive,
556                         GCancellable        *cancellable,
557                         GAsyncReadyCallback  callback,
558                         gpointer             user_data)
559 {
560   GDriveIface *iface;
561
562   g_return_if_fail (G_IS_DRIVE (drive));
563
564   iface = G_DRIVE_GET_IFACE (drive);
565
566   if (iface->poll_for_media == NULL)
567     {
568       g_task_report_new_error (drive, callback, user_data,
569                                g_drive_poll_for_media,
570                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
571                                _("drive doesn’t implement polling for media"));
572       return;
573     }
574   
575   (* iface->poll_for_media) (drive, cancellable, callback, user_data);
576 }
577
578 /**
579  * g_drive_poll_for_media_finish:
580  * @drive: a #GDrive.
581  * @result: a #GAsyncResult.
582  * @error: a #GError, or %NULL
583  * 
584  * Finishes an operation started with g_drive_poll_for_media() on a drive.
585  * 
586  * Returns: %TRUE if the drive has been poll_for_mediaed successfully,
587  *     %FALSE otherwise.
588  **/
589 gboolean
590 g_drive_poll_for_media_finish (GDrive        *drive,
591                                GAsyncResult  *result,
592                                GError       **error)
593 {
594   GDriveIface *iface;
595
596   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
597   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
598
599   if (g_async_result_legacy_propagate_error (result, error))
600     return FALSE;
601   else if (g_async_result_is_tagged (result, g_drive_poll_for_media))
602     return g_task_propagate_boolean (G_TASK (result), error);
603
604   iface = G_DRIVE_GET_IFACE (drive);
605   
606   return (* iface->poll_for_media_finish) (drive, result, error);
607 }
608
609 /**
610  * g_drive_get_identifier:
611  * @drive: a #GDrive
612  * @kind: the kind of identifier to return
613  *
614  * Gets the identifier of the given kind for @drive. The only
615  * identifier currently available is
616  * #G_DRIVE_IDENTIFIER_KIND_UNIX_DEVICE.
617  *
618  * Returns: (nullable) (transfer full): a newly allocated string containing the
619  *     requested identifier, or %NULL if the #GDrive
620  *     doesn't have this kind of identifier.
621  */
622 char *
623 g_drive_get_identifier (GDrive     *drive,
624                         const char *kind)
625 {
626   GDriveIface *iface;
627
628   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
629   g_return_val_if_fail (kind != NULL, NULL);
630
631   iface = G_DRIVE_GET_IFACE (drive);
632
633   if (iface->get_identifier == NULL)
634     return NULL;
635   
636   return (* iface->get_identifier) (drive, kind);
637 }
638
639 /**
640  * g_drive_enumerate_identifiers:
641  * @drive: a #GDrive
642  *
643  * Gets the kinds of identifiers that @drive has. 
644  * Use g_drive_get_identifier() to obtain the identifiers
645  * themselves.
646  *
647  * Returns: (transfer full) (array zero-terminated=1): a %NULL-terminated
648  *     array of strings containing kinds of identifiers. Use g_strfreev()
649  *     to free.
650  */
651 char **
652 g_drive_enumerate_identifiers (GDrive *drive)
653 {
654   GDriveIface *iface;
655
656   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
657   iface = G_DRIVE_GET_IFACE (drive);
658
659   if (iface->enumerate_identifiers == NULL)
660     return NULL;
661   
662   return (* iface->enumerate_identifiers) (drive);
663 }
664
665 /**
666  * g_drive_get_start_stop_type:
667  * @drive: a #GDrive.
668  *
669  * Gets a hint about how a drive can be started/stopped.
670  *
671  * Returns: A value from the #GDriveStartStopType enumeration.
672  *
673  * Since: 2.22
674  */
675 GDriveStartStopType
676 g_drive_get_start_stop_type (GDrive *drive)
677 {
678   GDriveIface *iface;
679
680   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
681
682   iface = G_DRIVE_GET_IFACE (drive);
683
684   if (iface->get_start_stop_type == NULL)
685     return G_DRIVE_START_STOP_TYPE_UNKNOWN;
686
687   return (* iface->get_start_stop_type) (drive);
688 }
689
690
691 /**
692  * g_drive_can_start:
693  * @drive: a #GDrive.
694  *
695  * Checks if a drive can be started.
696  *
697  * Returns: %TRUE if the @drive can be started, %FALSE otherwise.
698  *
699  * Since: 2.22
700  */
701 gboolean
702 g_drive_can_start (GDrive *drive)
703 {
704   GDriveIface *iface;
705
706   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
707
708   iface = G_DRIVE_GET_IFACE (drive);
709
710   if (iface->can_start == NULL)
711     return FALSE;
712
713   return (* iface->can_start) (drive);
714 }
715
716 /**
717  * g_drive_can_start_degraded:
718  * @drive: a #GDrive.
719  *
720  * Checks if a drive can be started degraded.
721  *
722  * Returns: %TRUE if the @drive can be started degraded, %FALSE otherwise.
723  *
724  * Since: 2.22
725  */
726 gboolean
727 g_drive_can_start_degraded (GDrive *drive)
728 {
729   GDriveIface *iface;
730
731   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
732
733   iface = G_DRIVE_GET_IFACE (drive);
734
735   if (iface->can_start_degraded == NULL)
736     return FALSE;
737
738   return (* iface->can_start_degraded) (drive);
739 }
740
741 /**
742  * g_drive_start:
743  * @drive: a #GDrive.
744  * @flags: flags affecting the start operation.
745  * @mount_operation: (nullable): a #GMountOperation or %NULL to avoid
746  *     user interaction.
747  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
748  * @callback: (nullable): a #GAsyncReadyCallback, or %NULL.
749  * @user_data: user data to pass to @callback
750  *
751  * Asynchronously starts a drive.
752  *
753  * When the operation is finished, @callback will be called.
754  * You can then call g_drive_start_finish() to obtain the
755  * result of the operation.
756  *
757  * Since: 2.22
758  */
759 void
760 g_drive_start (GDrive              *drive,
761                GDriveStartFlags     flags,
762                GMountOperation     *mount_operation,
763                GCancellable        *cancellable,
764                GAsyncReadyCallback  callback,
765                gpointer             user_data)
766 {
767   GDriveIface *iface;
768
769   g_return_if_fail (G_IS_DRIVE (drive));
770
771   iface = G_DRIVE_GET_IFACE (drive);
772
773   if (iface->start == NULL)
774     {
775       g_task_report_new_error (drive, callback, user_data,
776                                g_drive_start,
777                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
778                                _("drive doesn’t implement start"));
779       return;
780     }
781
782   (* iface->start) (drive, flags, mount_operation, cancellable, callback, user_data);
783 }
784
785 /**
786  * g_drive_start_finish:
787  * @drive: a #GDrive.
788  * @result: a #GAsyncResult.
789  * @error: a #GError, or %NULL
790  *
791  * Finishes starting a drive.
792  *
793  * Returns: %TRUE if the drive has been started successfully,
794  *     %FALSE otherwise.
795  *
796  * Since: 2.22
797  */
798 gboolean
799 g_drive_start_finish (GDrive         *drive,
800                       GAsyncResult   *result,
801                       GError        **error)
802 {
803   GDriveIface *iface;
804
805   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
806   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
807
808   if (g_async_result_legacy_propagate_error (result, error))
809     return FALSE;
810   else if (g_async_result_is_tagged (result, g_drive_start))
811     return g_task_propagate_boolean (G_TASK (result), error);
812
813   iface = G_DRIVE_GET_IFACE (drive);
814
815   return (* iface->start_finish) (drive, result, error);
816 }
817
818 /**
819  * g_drive_can_stop:
820  * @drive: a #GDrive.
821  *
822  * Checks if a drive can be stopped.
823  *
824  * Returns: %TRUE if the @drive can be stopped, %FALSE otherwise.
825  *
826  * Since: 2.22
827  */
828 gboolean
829 g_drive_can_stop (GDrive *drive)
830 {
831   GDriveIface *iface;
832
833   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
834
835   iface = G_DRIVE_GET_IFACE (drive);
836
837   if (iface->can_stop == NULL)
838     return FALSE;
839
840   return (* iface->can_stop) (drive);
841 }
842
843 /**
844  * g_drive_stop:
845  * @drive: a #GDrive.
846  * @flags: flags affecting the unmount if required for stopping.
847  * @mount_operation: (nullable): a #GMountOperation or %NULL to avoid
848  *     user interaction.
849  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
850  * @callback: (nullable): a #GAsyncReadyCallback, or %NULL.
851  * @user_data: user data to pass to @callback
852  *
853  * Asynchronously stops a drive.
854  *
855  * When the operation is finished, @callback will be called.
856  * You can then call g_drive_stop_finish() to obtain the
857  * result of the operation.
858  *
859  * Since: 2.22
860  */
861 void
862 g_drive_stop (GDrive               *drive,
863               GMountUnmountFlags    flags,
864               GMountOperation      *mount_operation,
865               GCancellable         *cancellable,
866               GAsyncReadyCallback   callback,
867               gpointer              user_data)
868 {
869   GDriveIface *iface;
870
871   g_return_if_fail (G_IS_DRIVE (drive));
872
873   iface = G_DRIVE_GET_IFACE (drive);
874
875   if (iface->stop == NULL)
876     {
877       g_task_report_new_error (drive, callback, user_data,
878                                g_drive_start,
879                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
880                                _("drive doesn’t implement stop"));
881       return;
882     }
883
884   (* iface->stop) (drive, flags, mount_operation, cancellable, callback, user_data);
885 }
886
887 /**
888  * g_drive_stop_finish:
889  * @drive: a #GDrive.
890  * @result: a #GAsyncResult.
891  * @error: a #GError, or %NULL
892  *
893  * Finishes stopping a drive.
894  *
895  * Returns: %TRUE if the drive has been stopped successfully,
896  *     %FALSE otherwise.
897  *
898  * Since: 2.22
899  */
900 gboolean
901 g_drive_stop_finish (GDrive        *drive,
902                      GAsyncResult  *result,
903                      GError       **error)
904 {
905   GDriveIface *iface;
906
907   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
908   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
909
910   if (g_async_result_legacy_propagate_error (result, error))
911     return FALSE;
912   else if (g_async_result_is_tagged (result, g_drive_start))
913     return g_task_propagate_boolean (G_TASK (result), error);
914
915   iface = G_DRIVE_GET_IFACE (drive);
916
917   return (* iface->stop_finish) (drive, result, error);
918 }
919
920 /**
921  * g_drive_get_sort_key:
922  * @drive: A #GDrive.
923  *
924  * Gets the sort key for @drive, if any.
925  *
926  * Returns: (nullable): Sorting key for @drive or %NULL if no such key is available.
927  *
928  * Since: 2.32
929  */
930 const gchar *
931 g_drive_get_sort_key (GDrive  *drive)
932 {
933   const gchar *ret = NULL;
934   GDriveIface *iface;
935
936   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
937
938   iface = G_DRIVE_GET_IFACE (drive);
939   if (iface->get_sort_key != NULL)
940     ret = iface->get_sort_key (drive);
941
942   return ret;
943 }