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