Work around test failure in gdbus-names
[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 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 potententially 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                 g_cclosure_marshal_VOID__VOID,
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                 g_cclosure_marshal_VOID__VOID,
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                 g_cclosure_marshal_VOID__VOID,
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                 g_cclosure_marshal_VOID__VOID,
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 capabable of automatically detecting media changes.
253  * 
254  * Returns: %TRUE if the @drive is capabable 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_media_removable:
271  * @drive: a #GDrive.
272  * 
273  * Checks if the @drive supports removable media.
274  * 
275  * Returns: %TRUE if @drive supports removable media, %FALSE otherwise.
276  **/
277 gboolean
278 g_drive_is_media_removable (GDrive *drive)
279 {
280   GDriveIface *iface;
281
282   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
283
284   iface = G_DRIVE_GET_IFACE (drive);
285
286   return (* iface->is_media_removable) (drive);
287 }
288
289 /**
290  * g_drive_has_media:
291  * @drive: a #GDrive.
292  * 
293  * Checks if the @drive has media. Note that the OS may not be polling
294  * the drive for media changes; see g_drive_is_media_check_automatic()
295  * for more details.
296  * 
297  * Returns: %TRUE if @drive has media, %FALSE otherwise.
298  **/
299 gboolean
300 g_drive_has_media (GDrive *drive)
301 {
302   GDriveIface *iface;
303
304   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
305
306   iface = G_DRIVE_GET_IFACE (drive);
307
308   return (* iface->has_media) (drive);
309 }
310
311 /**
312  * g_drive_can_eject:
313  * @drive: a #GDrive.
314  * 
315  * Checks if a drive can be ejected.
316  * 
317  * Returns: %TRUE if the @drive can be ejected, %FALSE otherwise.
318  **/
319 gboolean
320 g_drive_can_eject (GDrive *drive)
321 {
322   GDriveIface *iface;
323
324   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
325
326   iface = G_DRIVE_GET_IFACE (drive);
327
328   if (iface->can_eject == NULL)
329     return FALSE;
330
331   return (* iface->can_eject) (drive);
332 }
333
334 /**
335  * g_drive_can_poll_for_media:
336  * @drive: a #GDrive.
337  * 
338  * Checks if a drive can be polled for media changes.
339  * 
340  * Returns: %TRUE if the @drive can be polled for media changes,
341  *     %FALSE otherwise.
342  **/
343 gboolean
344 g_drive_can_poll_for_media (GDrive *drive)
345 {
346   GDriveIface *iface;
347
348   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
349
350   iface = G_DRIVE_GET_IFACE (drive);
351
352   if (iface->poll_for_media == NULL)
353     return FALSE;
354
355   return (* iface->can_poll_for_media) (drive);
356 }
357
358 /**
359  * g_drive_eject:
360  * @drive: a #GDrive.
361  * @flags: flags affecting the unmount if required for eject
362  * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
363  * @callback: (allow-none): a #GAsyncReadyCallback, or %NULL.
364  * @user_data: user data to pass to @callback
365  * 
366  * Asynchronously ejects a drive.
367  *
368  * When the operation is finished, @callback will be called.
369  * You can then call g_drive_eject_finish() to obtain the
370  * result of the operation.
371  *
372  * Deprecated: 2.22: Use g_drive_eject_with_operation() instead.
373  **/
374 void
375 g_drive_eject (GDrive              *drive,
376                GMountUnmountFlags   flags,
377                GCancellable        *cancellable,
378                GAsyncReadyCallback  callback,
379                gpointer             user_data)
380 {
381   GDriveIface *iface;
382
383   g_return_if_fail (G_IS_DRIVE (drive));
384
385   iface = G_DRIVE_GET_IFACE (drive);
386
387   if (iface->eject == NULL)
388     {
389       g_task_report_new_error (drive, callback, user_data,
390                                g_drive_eject_with_operation,
391                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
392                                _("drive doesn't implement eject"));
393       return;
394     }
395   
396   (* iface->eject) (drive, flags, cancellable, callback, user_data);
397 }
398
399 /**
400  * g_drive_eject_finish:
401  * @drive: a #GDrive.
402  * @result: a #GAsyncResult.
403  * @error: a #GError, or %NULL
404  * 
405  * Finishes ejecting a drive.
406  * 
407  * Returns: %TRUE if the drive has been ejected successfully,
408  *     %FALSE otherwise.
409  *
410  * Deprecated: 2.22: Use g_drive_eject_with_operation_finish() instead.
411  **/
412 gboolean
413 g_drive_eject_finish (GDrive        *drive,
414                       GAsyncResult  *result,
415                       GError       **error)
416 {
417   GDriveIface *iface;
418
419   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
420   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
421
422   if (g_async_result_legacy_propagate_error (result, error))
423     return FALSE;
424   else if (g_async_result_is_tagged (result, g_drive_eject_with_operation))
425     return g_task_propagate_boolean (G_TASK (result), error);
426
427   iface = G_DRIVE_GET_IFACE (drive);
428   
429   return (* iface->eject_finish) (drive, result, error);
430 }
431
432 /**
433  * g_drive_eject_with_operation:
434  * @drive: a #GDrive.
435  * @flags: flags affecting the unmount if required for eject
436  * @mount_operation: (allow-none): a #GMountOperation or %NULL to avoid
437  *     user interaction.
438  * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
439  * @callback: (allow-none): a #GAsyncReadyCallback, or %NULL.
440  * @user_data: user data passed to @callback.
441  *
442  * Ejects a drive. This is an asynchronous operation, and is
443  * finished by calling g_drive_eject_with_operation_finish() with the @drive
444  * and #GAsyncResult data returned in the @callback.
445  *
446  * Since: 2.22
447  **/
448 void
449 g_drive_eject_with_operation (GDrive              *drive,
450                               GMountUnmountFlags   flags,
451                               GMountOperation     *mount_operation,
452                               GCancellable        *cancellable,
453                               GAsyncReadyCallback  callback,
454                               gpointer             user_data)
455 {
456   GDriveIface *iface;
457
458   g_return_if_fail (G_IS_DRIVE (drive));
459
460   iface = G_DRIVE_GET_IFACE (drive);
461
462   if (iface->eject == NULL && iface->eject_with_operation == NULL)
463     {
464       g_task_report_new_error (drive, callback, user_data,
465                                g_drive_eject_with_operation,
466                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
467                                /* Translators: This is an error
468                                 * message for drive objects that
469                                 * don't implement any of eject or eject_with_operation. */
470                                _("drive doesn't implement eject or eject_with_operation"));
471       return;
472     }
473
474   if (iface->eject_with_operation != NULL)
475     (* iface->eject_with_operation) (drive, flags, mount_operation, cancellable, callback, user_data);
476   else
477     (* iface->eject) (drive, flags, cancellable, callback, user_data);
478 }
479
480 /**
481  * g_drive_eject_with_operation_finish:
482  * @drive: a #GDrive.
483  * @result: a #GAsyncResult.
484  * @error: a #GError location to store the error occurring, or %NULL to
485  *     ignore.
486  *
487  * Finishes ejecting a drive. If any errors occurred during the operation,
488  * @error will be set to contain the errors and %FALSE will be returned.
489  *
490  * Returns: %TRUE if the drive was successfully ejected. %FALSE otherwise.
491  *
492  * Since: 2.22
493  **/
494 gboolean
495 g_drive_eject_with_operation_finish (GDrive        *drive,
496                                      GAsyncResult  *result,
497                                      GError       **error)
498 {
499   GDriveIface *iface;
500
501   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
502   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
503
504   if (g_async_result_legacy_propagate_error (result, error))
505     return FALSE;
506   else if (g_async_result_is_tagged (result, g_drive_eject_with_operation))
507     return g_task_propagate_boolean (G_TASK (result), error);
508
509   iface = G_DRIVE_GET_IFACE (drive);
510   if (iface->eject_with_operation_finish != NULL)
511     return (* iface->eject_with_operation_finish) (drive, result, error);
512   else
513     return (* iface->eject_finish) (drive, result, error);
514 }
515
516 /**
517  * g_drive_poll_for_media:
518  * @drive: a #GDrive.
519  * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
520  * @callback: (allow-none): a #GAsyncReadyCallback, or %NULL.
521  * @user_data: user data to pass to @callback
522  * 
523  * Asynchronously polls @drive to see if media has been inserted or removed.
524  * 
525  * When the operation is finished, @callback will be called.
526  * You can then call g_drive_poll_for_media_finish() to obtain the
527  * result of the operation.
528  **/
529 void
530 g_drive_poll_for_media (GDrive              *drive,
531                         GCancellable        *cancellable,
532                         GAsyncReadyCallback  callback,
533                         gpointer             user_data)
534 {
535   GDriveIface *iface;
536
537   g_return_if_fail (G_IS_DRIVE (drive));
538
539   iface = G_DRIVE_GET_IFACE (drive);
540
541   if (iface->poll_for_media == NULL)
542     {
543       g_task_report_new_error (drive, callback, user_data,
544                                g_drive_poll_for_media,
545                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
546                                _("drive doesn't implement polling for media"));
547       return;
548     }
549   
550   (* iface->poll_for_media) (drive, cancellable, callback, user_data);
551 }
552
553 /**
554  * g_drive_poll_for_media_finish:
555  * @drive: a #GDrive.
556  * @result: a #GAsyncResult.
557  * @error: a #GError, or %NULL
558  * 
559  * Finishes an operation started with g_drive_poll_for_media() on a drive.
560  * 
561  * Returns: %TRUE if the drive has been poll_for_mediaed successfully,
562  *     %FALSE otherwise.
563  **/
564 gboolean
565 g_drive_poll_for_media_finish (GDrive        *drive,
566                                GAsyncResult  *result,
567                                GError       **error)
568 {
569   GDriveIface *iface;
570
571   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
572   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
573
574   if (g_async_result_legacy_propagate_error (result, error))
575     return FALSE;
576   else if (g_async_result_is_tagged (result, g_drive_poll_for_media))
577     return g_task_propagate_boolean (G_TASK (result), error);
578
579   iface = G_DRIVE_GET_IFACE (drive);
580   
581   return (* iface->poll_for_media_finish) (drive, result, error);
582 }
583
584 /**
585  * g_drive_get_identifier:
586  * @drive: a #GDrive
587  * @kind: the kind of identifier to return
588  *
589  * Gets the identifier of the given kind for @drive.
590  *
591  * Returns: a newly allocated string containing the
592  *     requested identfier, or %NULL if the #GDrive
593  *     doesn't have this kind of identifier.
594  */
595 char *
596 g_drive_get_identifier (GDrive     *drive,
597                         const char *kind)
598 {
599   GDriveIface *iface;
600
601   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
602   g_return_val_if_fail (kind != NULL, NULL);
603
604   iface = G_DRIVE_GET_IFACE (drive);
605
606   if (iface->get_identifier == NULL)
607     return NULL;
608   
609   return (* iface->get_identifier) (drive, kind);
610 }
611
612 /**
613  * g_drive_enumerate_identifiers:
614  * @drive: a #GDrive
615  *
616  * Gets the kinds of identifiers that @drive has. 
617  * Use g_drive_get_identifier() to obtain the identifiers
618  * themselves.
619  *
620  * Returns: (transfer full) (array zero-terminated=1): a %NULL-terminated
621  *     array of strings containing kinds of identifiers. Use g_strfreev()
622  *     to free.
623  */
624 char **
625 g_drive_enumerate_identifiers (GDrive *drive)
626 {
627   GDriveIface *iface;
628
629   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
630   iface = G_DRIVE_GET_IFACE (drive);
631
632   if (iface->enumerate_identifiers == NULL)
633     return NULL;
634   
635   return (* iface->enumerate_identifiers) (drive);
636 }
637
638 /**
639  * g_drive_get_start_stop_type:
640  * @drive: a #GDrive.
641  *
642  * Gets a hint about how a drive can be started/stopped.
643  *
644  * Returns: A value from the #GDriveStartStopType enumeration.
645  *
646  * Since: 2.22
647  */
648 GDriveStartStopType
649 g_drive_get_start_stop_type (GDrive *drive)
650 {
651   GDriveIface *iface;
652
653   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
654
655   iface = G_DRIVE_GET_IFACE (drive);
656
657   if (iface->get_start_stop_type == NULL)
658     return G_DRIVE_START_STOP_TYPE_UNKNOWN;
659
660   return (* iface->get_start_stop_type) (drive);
661 }
662
663
664 /**
665  * g_drive_can_start:
666  * @drive: a #GDrive.
667  *
668  * Checks if a drive can be started.
669  *
670  * Returns: %TRUE if the @drive can be started, %FALSE otherwise.
671  *
672  * Since: 2.22
673  */
674 gboolean
675 g_drive_can_start (GDrive *drive)
676 {
677   GDriveIface *iface;
678
679   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
680
681   iface = G_DRIVE_GET_IFACE (drive);
682
683   if (iface->can_start == NULL)
684     return FALSE;
685
686   return (* iface->can_start) (drive);
687 }
688
689 /**
690  * g_drive_can_start_degraded:
691  * @drive: a #GDrive.
692  *
693  * Checks if a drive can be started degraded.
694  *
695  * Returns: %TRUE if the @drive can be started degraded, %FALSE otherwise.
696  *
697  * Since: 2.22
698  */
699 gboolean
700 g_drive_can_start_degraded (GDrive *drive)
701 {
702   GDriveIface *iface;
703
704   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
705
706   iface = G_DRIVE_GET_IFACE (drive);
707
708   if (iface->can_start_degraded == NULL)
709     return FALSE;
710
711   return (* iface->can_start_degraded) (drive);
712 }
713
714 /**
715  * g_drive_start:
716  * @drive: a #GDrive.
717  * @flags: flags affecting the start operation.
718  * @mount_operation: (allow-none): a #GMountOperation or %NULL to avoid
719  *     user interaction.
720  * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
721  * @callback: (allow-none): a #GAsyncReadyCallback, or %NULL.
722  * @user_data: user data to pass to @callback
723  *
724  * Asynchronously starts a drive.
725  *
726  * When the operation is finished, @callback will be called.
727  * You can then call g_drive_start_finish() to obtain the
728  * result of the operation.
729  *
730  * Since: 2.22
731  */
732 void
733 g_drive_start (GDrive              *drive,
734                GDriveStartFlags     flags,
735                GMountOperation     *mount_operation,
736                GCancellable        *cancellable,
737                GAsyncReadyCallback  callback,
738                gpointer             user_data)
739 {
740   GDriveIface *iface;
741
742   g_return_if_fail (G_IS_DRIVE (drive));
743
744   iface = G_DRIVE_GET_IFACE (drive);
745
746   if (iface->start == NULL)
747     {
748       g_task_report_new_error (drive, callback, user_data,
749                                g_drive_start,
750                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
751                                _("drive doesn't implement start"));
752       return;
753     }
754
755   (* iface->start) (drive, flags, mount_operation, cancellable, callback, user_data);
756 }
757
758 /**
759  * g_drive_start_finish:
760  * @drive: a #GDrive.
761  * @result: a #GAsyncResult.
762  * @error: a #GError, or %NULL
763  *
764  * Finishes starting a drive.
765  *
766  * Returns: %TRUE if the drive has been started successfully,
767  *     %FALSE otherwise.
768  *
769  * Since: 2.22
770  */
771 gboolean
772 g_drive_start_finish (GDrive         *drive,
773                       GAsyncResult   *result,
774                       GError        **error)
775 {
776   GDriveIface *iface;
777
778   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
779   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
780
781   if (g_async_result_legacy_propagate_error (result, error))
782     return FALSE;
783   else if (g_async_result_is_tagged (result, g_drive_start))
784     return g_task_propagate_boolean (G_TASK (result), error);
785
786   iface = G_DRIVE_GET_IFACE (drive);
787
788   return (* iface->start_finish) (drive, result, error);
789 }
790
791 /**
792  * g_drive_can_stop:
793  * @drive: a #GDrive.
794  *
795  * Checks if a drive can be stopped.
796  *
797  * Returns: %TRUE if the @drive can be stopped, %FALSE otherwise.
798  *
799  * Since: 2.22
800  */
801 gboolean
802 g_drive_can_stop (GDrive *drive)
803 {
804   GDriveIface *iface;
805
806   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
807
808   iface = G_DRIVE_GET_IFACE (drive);
809
810   if (iface->can_stop == NULL)
811     return FALSE;
812
813   return (* iface->can_stop) (drive);
814 }
815
816 /**
817  * g_drive_stop:
818  * @drive: a #GDrive.
819  * @flags: flags affecting the unmount if required for stopping.
820  * @mount_operation: (allow-none): a #GMountOperation or %NULL to avoid
821  *     user interaction.
822  * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
823  * @callback: (allow-none): a #GAsyncReadyCallback, or %NULL.
824  * @user_data: user data to pass to @callback
825  *
826  * Asynchronously stops a drive.
827  *
828  * When the operation is finished, @callback will be called.
829  * You can then call g_drive_stop_finish() to obtain the
830  * result of the operation.
831  *
832  * Since: 2.22
833  */
834 void
835 g_drive_stop (GDrive               *drive,
836               GMountUnmountFlags    flags,
837               GMountOperation      *mount_operation,
838               GCancellable         *cancellable,
839               GAsyncReadyCallback   callback,
840               gpointer              user_data)
841 {
842   GDriveIface *iface;
843
844   g_return_if_fail (G_IS_DRIVE (drive));
845
846   iface = G_DRIVE_GET_IFACE (drive);
847
848   if (iface->stop == NULL)
849     {
850       g_task_report_new_error (drive, callback, user_data,
851                                g_drive_start,
852                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
853                                _("drive doesn't implement stop"));
854       return;
855     }
856
857   (* iface->stop) (drive, flags, mount_operation, cancellable, callback, user_data);
858 }
859
860 /**
861  * g_drive_stop_finish:
862  * @drive: a #GDrive.
863  * @result: a #GAsyncResult.
864  * @error: a #GError, or %NULL
865  *
866  * Finishes stopping a drive.
867  *
868  * Returns: %TRUE if the drive has been stopped successfully,
869  *     %FALSE otherwise.
870  *
871  * Since: 2.22
872  */
873 gboolean
874 g_drive_stop_finish (GDrive        *drive,
875                      GAsyncResult  *result,
876                      GError       **error)
877 {
878   GDriveIface *iface;
879
880   g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
881   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
882
883   if (g_async_result_legacy_propagate_error (result, error))
884     return FALSE;
885   else if (g_async_result_is_tagged (result, g_drive_start))
886     return g_task_propagate_boolean (G_TASK (result), error);
887
888   iface = G_DRIVE_GET_IFACE (drive);
889
890   return (* iface->stop_finish) (drive, result, error);
891 }
892
893 /**
894  * g_drive_get_sort_key:
895  * @drive: A #GDrive.
896  *
897  * Gets the sort key for @drive, if any.
898  *
899  * Returns: Sorting key for @drive or %NULL if no such key is available.
900  *
901  * Since: 2.32
902  */
903 const gchar *
904 g_drive_get_sort_key (GDrive  *drive)
905 {
906   const gchar *ret = NULL;
907   GDriveIface *iface;
908
909   g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
910
911   iface = G_DRIVE_GET_IFACE (drive);
912   if (iface->get_sort_key != NULL)
913     ret = iface->get_sort_key (drive);
914
915   return ret;
916 }