• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gmount.h"
24 #include "gvolume.h"
25 #include "gthemedicon.h"
26 #include "gasyncresult.h"
27 #include "gtask.h"
28 #include "gioerror.h"
29 #include "glibintl.h"
30 
31 
32 /**
33  * SECTION:gvolume
34  * @short_description: Volume management
35  * @include: gio/gio.h
36  *
37  * The #GVolume interface represents user-visible objects that can be
38  * mounted. Note, when porting from GnomeVFS, #GVolume is the moral
39  * equivalent of #GnomeVFSDrive.
40  *
41  * Mounting a #GVolume instance is an asynchronous operation. For more
42  * information about asynchronous operations, see #GAsyncResult and
43  * #GTask. To mount a #GVolume, first call g_volume_mount() with (at
44  * least) the #GVolume instance, optionally a #GMountOperation object
45  * and a #GAsyncReadyCallback.
46  *
47  * Typically, one will only want to pass %NULL for the
48  * #GMountOperation if automounting all volumes when a desktop session
49  * starts since it's not desirable to put up a lot of dialogs asking
50  * for credentials.
51  *
52  * The callback will be fired when the operation has resolved (either
53  * with success or failure), and a #GAsyncResult instance will be
54  * passed to the callback.  That callback should then call
55  * g_volume_mount_finish() with the #GVolume instance and the
56  * #GAsyncResult data to see if the operation was completed
57  * successfully.  If an @error is present when g_volume_mount_finish()
58  * is called, then it will be filled with any error information.
59  *
60  * ## Volume Identifiers # {#volume-identifier}
61  *
62  * It is sometimes necessary to directly access the underlying
63  * operating system object behind a volume (e.g. for passing a volume
64  * to an application via the commandline). For this purpose, GIO
65  * allows to obtain an 'identifier' for the volume. There can be
66  * different kinds of identifiers, such as Hal UDIs, filesystem labels,
67  * traditional Unix devices (e.g. `/dev/sda2`), UUIDs. GIO uses predefined
68  * strings as names for the different kinds of identifiers:
69  * #G_VOLUME_IDENTIFIER_KIND_UUID, #G_VOLUME_IDENTIFIER_KIND_LABEL, etc.
70  * Use g_volume_get_identifier() to obtain an identifier for a volume.
71  *
72  *
73  * Note that #G_VOLUME_IDENTIFIER_KIND_HAL_UDI will only be available
74  * when the gvfs hal volume monitor is in use. Other volume monitors
75  * will generally be able to provide the #G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE
76  * identifier, which can be used to obtain a hal device by means of
77  * libhal_manager_find_device_string_match().
78  */
79 
80 typedef GVolumeIface GVolumeInterface;
G_DEFINE_INTERFACE(GVolume,g_volume,G_TYPE_OBJECT)81 G_DEFINE_INTERFACE(GVolume, g_volume, G_TYPE_OBJECT)
82 
83 static void
84 g_volume_default_init (GVolumeInterface *iface)
85 {
86   /**
87    * GVolume::changed:
88    *
89    * Emitted when the volume has been changed.
90    */
91   g_signal_new (I_("changed"),
92 		G_TYPE_VOLUME,
93 		G_SIGNAL_RUN_LAST,
94 		G_STRUCT_OFFSET (GVolumeIface, changed),
95 		NULL, NULL,
96 		NULL,
97 		G_TYPE_NONE, 0);
98 
99   /**
100    * GVolume::removed:
101    *
102    * This signal is emitted when the #GVolume have been removed. If
103    * the recipient is holding references to the object they should
104    * release them so the object can be finalized.
105    */
106   g_signal_new (I_("removed"),
107 		G_TYPE_VOLUME,
108 		G_SIGNAL_RUN_LAST,
109 		G_STRUCT_OFFSET (GVolumeIface, removed),
110 		NULL, NULL,
111 		NULL,
112 		G_TYPE_NONE, 0);
113 }
114 
115 /**
116  * g_volume_get_name:
117  * @volume: a #GVolume
118  *
119  * Gets the name of @volume.
120  *
121  * Returns: the name for the given @volume. The returned string should
122  *     be freed with g_free() when no longer needed.
123  */
124 char *
g_volume_get_name(GVolume * volume)125 g_volume_get_name (GVolume *volume)
126 {
127   GVolumeIface *iface;
128 
129   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
130 
131   iface = G_VOLUME_GET_IFACE (volume);
132 
133   return (* iface->get_name) (volume);
134 }
135 
136 /**
137  * g_volume_get_icon:
138  * @volume: a #GVolume
139  *
140  * Gets the icon for @volume.
141  *
142  * Returns: (transfer full): a #GIcon.
143  *     The returned object should be unreffed with g_object_unref()
144  *     when no longer needed.
145  */
146 GIcon *
g_volume_get_icon(GVolume * volume)147 g_volume_get_icon (GVolume *volume)
148 {
149   GVolumeIface *iface;
150 
151   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
152 
153   iface = G_VOLUME_GET_IFACE (volume);
154 
155   return (* iface->get_icon) (volume);
156 }
157 
158 /**
159  * g_volume_get_symbolic_icon:
160  * @volume: a #GVolume
161  *
162  * Gets the symbolic icon for @volume.
163  *
164  * Returns: (transfer full): a #GIcon.
165  *     The returned object should be unreffed with g_object_unref()
166  *     when no longer needed.
167  *
168  * Since: 2.34
169  */
170 GIcon *
g_volume_get_symbolic_icon(GVolume * volume)171 g_volume_get_symbolic_icon (GVolume *volume)
172 {
173   GVolumeIface *iface;
174   GIcon *ret;
175 
176   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
177 
178   iface = G_VOLUME_GET_IFACE (volume);
179 
180   if (iface->get_symbolic_icon != NULL)
181     ret = iface->get_symbolic_icon (volume);
182   else
183     ret = g_themed_icon_new_with_default_fallbacks ("folder-remote-symbolic");
184 
185   return ret;
186 
187 }
188 
189 /**
190  * g_volume_get_uuid:
191  * @volume: a #GVolume
192  *
193  * Gets the UUID for the @volume. The reference is typically based on
194  * the file system UUID for the volume in question and should be
195  * considered an opaque string. Returns %NULL if there is no UUID
196  * available.
197  *
198  * Returns: (nullable) (transfer full): the UUID for @volume or %NULL if no UUID
199  *     can be computed.
200  *     The returned string should be freed with g_free()
201  *     when no longer needed.
202  */
203 char *
g_volume_get_uuid(GVolume * volume)204 g_volume_get_uuid (GVolume *volume)
205 {
206   GVolumeIface *iface;
207 
208   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
209 
210   iface = G_VOLUME_GET_IFACE (volume);
211 
212   return (* iface->get_uuid) (volume);
213 }
214 
215 /**
216  * g_volume_get_drive:
217  * @volume: a #GVolume
218  *
219  * Gets the drive for the @volume.
220  *
221  * Returns: (transfer full) (nullable): a #GDrive or %NULL if @volume is not
222  *     associated with a drive. The returned object should be unreffed
223  *     with g_object_unref() when no longer needed.
224  */
225 GDrive *
g_volume_get_drive(GVolume * volume)226 g_volume_get_drive (GVolume *volume)
227 {
228   GVolumeIface *iface;
229 
230   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
231 
232   iface = G_VOLUME_GET_IFACE (volume);
233 
234   return (* iface->get_drive) (volume);
235 }
236 
237 /**
238  * g_volume_get_mount:
239  * @volume: a #GVolume
240  *
241  * Gets the mount for the @volume.
242  *
243  * Returns: (transfer full) (nullable): a #GMount or %NULL if @volume isn't mounted.
244  *     The returned object should be unreffed with g_object_unref()
245  *     when no longer needed.
246  */
247 GMount *
g_volume_get_mount(GVolume * volume)248 g_volume_get_mount (GVolume *volume)
249 {
250   GVolumeIface *iface;
251 
252   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
253 
254   iface = G_VOLUME_GET_IFACE (volume);
255 
256   return (* iface->get_mount) (volume);
257 }
258 
259 
260 /**
261  * g_volume_can_mount:
262  * @volume: a #GVolume
263  *
264  * Checks if a volume can be mounted.
265  *
266  * Returns: %TRUE if the @volume can be mounted. %FALSE otherwise
267  */
268 gboolean
g_volume_can_mount(GVolume * volume)269 g_volume_can_mount (GVolume *volume)
270 {
271   GVolumeIface *iface;
272 
273   g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
274 
275   iface = G_VOLUME_GET_IFACE (volume);
276 
277   if (iface->can_mount == NULL)
278     return FALSE;
279 
280   return (* iface->can_mount) (volume);
281 }
282 
283 /**
284  * g_volume_can_eject:
285  * @volume: a #GVolume
286  *
287  * Checks if a volume can be ejected.
288  *
289  * Returns: %TRUE if the @volume can be ejected. %FALSE otherwise
290  */
291 gboolean
g_volume_can_eject(GVolume * volume)292 g_volume_can_eject (GVolume *volume)
293 {
294   GVolumeIface *iface;
295 
296   g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
297 
298   iface = G_VOLUME_GET_IFACE (volume);
299 
300   if (iface->can_eject == NULL)
301     return FALSE;
302 
303   return (* iface->can_eject) (volume);
304 }
305 
306 /**
307  * g_volume_should_automount:
308  * @volume: a #GVolume
309  *
310  * Returns whether the volume should be automatically mounted.
311  *
312  * Returns: %TRUE if the volume should be automatically mounted
313  */
314 gboolean
g_volume_should_automount(GVolume * volume)315 g_volume_should_automount (GVolume *volume)
316 {
317   GVolumeIface *iface;
318 
319   g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
320 
321   iface = G_VOLUME_GET_IFACE (volume);
322 
323   if (iface->should_automount == NULL)
324     return FALSE;
325 
326   return (* iface->should_automount) (volume);
327 }
328 
329 
330 /**
331  * g_volume_mount:
332  * @volume: a #GVolume
333  * @flags: flags affecting the operation
334  * @mount_operation: (nullable): a #GMountOperation or %NULL to avoid user interaction
335  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
336  * @callback: (nullable): a #GAsyncReadyCallback, or %NULL
337  * @user_data: user data that gets passed to @callback
338  *
339  * Mounts a volume. This is an asynchronous operation, and is
340  * finished by calling g_volume_mount_finish() with the @volume
341  * and #GAsyncResult returned in the @callback.
342  *
343  * Virtual: mount_fn
344  */
345 void
g_volume_mount(GVolume * volume,GMountMountFlags flags,GMountOperation * mount_operation,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)346 g_volume_mount (GVolume             *volume,
347 		GMountMountFlags     flags,
348                 GMountOperation     *mount_operation,
349                 GCancellable        *cancellable,
350                 GAsyncReadyCallback  callback,
351                 gpointer             user_data)
352 {
353   GVolumeIface *iface;
354 
355   g_return_if_fail (G_IS_VOLUME (volume));
356 
357   iface = G_VOLUME_GET_IFACE (volume);
358 
359   if (iface->mount_fn == NULL)
360     {
361       g_task_report_new_error (volume, callback, user_data,
362                                g_volume_mount,
363                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
364                                _("volume doesn’t implement mount"));
365       return;
366     }
367 
368   (* iface->mount_fn) (volume, flags, mount_operation, cancellable, callback, user_data);
369 }
370 
371 /**
372  * g_volume_mount_finish:
373  * @volume: a #GVolume
374  * @result: a #GAsyncResult
375  * @error: a #GError location to store an error, or %NULL to ignore
376  *
377  * Finishes mounting a volume. If any errors occurred during the operation,
378  * @error will be set to contain the errors and %FALSE will be returned.
379  *
380  * If the mount operation succeeded, g_volume_get_mount() on @volume
381  * is guaranteed to return the mount right after calling this
382  * function; there's no need to listen for the 'mount-added' signal on
383  * #GVolumeMonitor.
384  *
385  * Returns: %TRUE, %FALSE if operation failed
386  */
387 gboolean
g_volume_mount_finish(GVolume * volume,GAsyncResult * result,GError ** error)388 g_volume_mount_finish (GVolume       *volume,
389                        GAsyncResult  *result,
390                        GError       **error)
391 {
392   GVolumeIface *iface;
393 
394   g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
395   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
396 
397   if (g_async_result_legacy_propagate_error (result, error))
398     return FALSE;
399   else if (g_async_result_is_tagged (result, g_volume_mount))
400     return g_task_propagate_boolean (G_TASK (result), error);
401 
402   iface = G_VOLUME_GET_IFACE (volume);
403   return (* iface->mount_finish) (volume, result, error);
404 }
405 
406 /**
407  * g_volume_eject:
408  * @volume: a #GVolume
409  * @flags: flags affecting the unmount if required for eject
410  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
411  * @callback: (nullable): a #GAsyncReadyCallback, or %NULL
412  * @user_data: user data that gets passed to @callback
413  *
414  * Ejects a volume. This is an asynchronous operation, and is
415  * finished by calling g_volume_eject_finish() with the @volume
416  * and #GAsyncResult returned in the @callback.
417  *
418  * Deprecated: 2.22: Use g_volume_eject_with_operation() instead.
419  */
420 void
g_volume_eject(GVolume * volume,GMountUnmountFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)421 g_volume_eject (GVolume             *volume,
422 		GMountUnmountFlags   flags,
423                 GCancellable        *cancellable,
424                 GAsyncReadyCallback  callback,
425                 gpointer             user_data)
426 {
427   GVolumeIface *iface;
428 
429   g_return_if_fail (G_IS_VOLUME (volume));
430 
431   iface = G_VOLUME_GET_IFACE (volume);
432 
433   if (iface->eject == NULL)
434     {
435       g_task_report_new_error (volume, callback, user_data,
436                                g_volume_eject_with_operation,
437                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
438                                _("volume doesn’t implement eject"));
439       return;
440     }
441 
442   (* iface->eject) (volume, flags, cancellable, callback, user_data);
443 }
444 
445 /**
446  * g_volume_eject_finish:
447  * @volume: pointer to a #GVolume
448  * @result: a #GAsyncResult
449  * @error: a #GError location to store an error, or %NULL to ignore
450  *
451  * Finishes ejecting a volume. If any errors occurred during the operation,
452  * @error will be set to contain the errors and %FALSE will be returned.
453  *
454  * Returns: %TRUE, %FALSE if operation failed
455  *
456  * Deprecated: 2.22: Use g_volume_eject_with_operation_finish() instead.
457  **/
458 gboolean
g_volume_eject_finish(GVolume * volume,GAsyncResult * result,GError ** error)459 g_volume_eject_finish (GVolume       *volume,
460                        GAsyncResult  *result,
461                        GError       **error)
462 {
463   GVolumeIface *iface;
464 
465   g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
466   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
467 
468   if (g_async_result_legacy_propagate_error (result, error))
469     return FALSE;
470   if (g_async_result_is_tagged (result, g_volume_eject_with_operation))
471     return g_task_propagate_boolean (G_TASK (result), error);
472 
473   iface = G_VOLUME_GET_IFACE (volume);
474   return (* iface->eject_finish) (volume, result, error);
475 }
476 
477 /**
478  * g_volume_eject_with_operation:
479  * @volume: a #GVolume
480  * @flags: flags affecting the unmount if required for eject
481  * @mount_operation: (nullable): a #GMountOperation or %NULL to
482  *     avoid user interaction
483  * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
484  * @callback: (nullable): a #GAsyncReadyCallback, or %NULL
485  * @user_data: user data passed to @callback
486  *
487  * Ejects a volume. This is an asynchronous operation, and is
488  * finished by calling g_volume_eject_with_operation_finish() with the @volume
489  * and #GAsyncResult data returned in the @callback.
490  *
491  * Since: 2.22
492  **/
493 void
g_volume_eject_with_operation(GVolume * volume,GMountUnmountFlags flags,GMountOperation * mount_operation,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)494 g_volume_eject_with_operation (GVolume              *volume,
495                                GMountUnmountFlags   flags,
496                                GMountOperation     *mount_operation,
497                                GCancellable        *cancellable,
498                                GAsyncReadyCallback  callback,
499                                gpointer             user_data)
500 {
501   GVolumeIface *iface;
502 
503   g_return_if_fail (G_IS_VOLUME (volume));
504 
505   iface = G_VOLUME_GET_IFACE (volume);
506 
507   if (iface->eject == NULL && iface->eject_with_operation == NULL)
508     {
509       g_task_report_new_error (volume, callback, user_data,
510                                g_volume_eject_with_operation,
511                                G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
512                                /* Translators: This is an error
513                                 * message for volume objects that
514                                 * don't implement any of eject or eject_with_operation. */
515                                _("volume doesn’t implement eject or eject_with_operation"));
516       return;
517     }
518 
519   if (iface->eject_with_operation != NULL)
520     (* iface->eject_with_operation) (volume, flags, mount_operation, cancellable, callback, user_data);
521   else
522     (* iface->eject) (volume, flags, cancellable, callback, user_data);
523 }
524 
525 /**
526  * g_volume_eject_with_operation_finish:
527  * @volume: a #GVolume
528  * @result: a #GAsyncResult
529  * @error: a #GError location to store the error occurring, or %NULL
530  *
531  * Finishes ejecting a volume. If any errors occurred during the operation,
532  * @error will be set to contain the errors and %FALSE will be returned.
533  *
534  * Returns: %TRUE if the volume was successfully ejected. %FALSE otherwise
535  *
536  * Since: 2.22
537  **/
538 gboolean
g_volume_eject_with_operation_finish(GVolume * volume,GAsyncResult * result,GError ** error)539 g_volume_eject_with_operation_finish (GVolume        *volume,
540                                       GAsyncResult  *result,
541                                       GError       **error)
542 {
543   GVolumeIface *iface;
544 
545   g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
546   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
547 
548   if (g_async_result_legacy_propagate_error (result, error))
549     return FALSE;
550   else if (g_async_result_is_tagged (result, g_volume_eject_with_operation))
551     return g_task_propagate_boolean (G_TASK (result), error);
552 
553   iface = G_VOLUME_GET_IFACE (volume);
554   if (iface->eject_with_operation_finish != NULL)
555     return (* iface->eject_with_operation_finish) (volume, result, error);
556   else
557     return (* iface->eject_finish) (volume, result, error);
558 }
559 
560 /**
561  * g_volume_get_identifier:
562  * @volume: a #GVolume
563  * @kind: the kind of identifier to return
564  *
565  * Gets the identifier of the given kind for @volume.
566  * See the [introduction][volume-identifier] for more
567  * information about volume identifiers.
568  *
569  * Returns: (nullable) (transfer full): a newly allocated string containing the
570  *     requested identifier, or %NULL if the #GVolume
571  *     doesn't have this kind of identifier
572  */
573 char *
g_volume_get_identifier(GVolume * volume,const char * kind)574 g_volume_get_identifier (GVolume    *volume,
575 			 const char *kind)
576 {
577   GVolumeIface *iface;
578 
579   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
580   g_return_val_if_fail (kind != NULL, NULL);
581 
582   iface = G_VOLUME_GET_IFACE (volume);
583 
584   if (iface->get_identifier == NULL)
585     return NULL;
586 
587   return (* iface->get_identifier) (volume, kind);
588 }
589 
590 /**
591  * g_volume_enumerate_identifiers:
592  * @volume: a #GVolume
593  *
594  * Gets the kinds of [identifiers][volume-identifier] that @volume has.
595  * Use g_volume_get_identifier() to obtain the identifiers themselves.
596  *
597  * Returns: (array zero-terminated=1) (transfer full): a %NULL-terminated array
598  *   of strings containing kinds of identifiers. Use g_strfreev() to free.
599  */
600 char **
g_volume_enumerate_identifiers(GVolume * volume)601 g_volume_enumerate_identifiers (GVolume *volume)
602 {
603   GVolumeIface *iface;
604 
605   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
606   iface = G_VOLUME_GET_IFACE (volume);
607 
608   if (iface->enumerate_identifiers == NULL)
609     return NULL;
610 
611   return (* iface->enumerate_identifiers) (volume);
612 }
613 
614 /**
615  * g_volume_get_activation_root:
616  * @volume: a #GVolume
617  *
618  * Gets the activation root for a #GVolume if it is known ahead of
619  * mount time. Returns %NULL otherwise. If not %NULL and if @volume
620  * is mounted, then the result of g_mount_get_root() on the
621  * #GMount object obtained from g_volume_get_mount() will always
622  * either be equal or a prefix of what this function returns. In
623  * other words, in code
624  *
625  * |[<!-- language="C" -->
626  *   GMount *mount;
627  *   GFile *mount_root
628  *   GFile *volume_activation_root;
629  *
630  *   mount = g_volume_get_mount (volume); // mounted, so never NULL
631  *   mount_root = g_mount_get_root (mount);
632  *   volume_activation_root = g_volume_get_activation_root (volume); // assume not NULL
633  * ]|
634  * then the expression
635  * |[<!-- language="C" -->
636  *   (g_file_has_prefix (volume_activation_root, mount_root) ||
637  *    g_file_equal (volume_activation_root, mount_root))
638  * ]|
639  * will always be %TRUE.
640  *
641  * Activation roots are typically used in #GVolumeMonitor
642  * implementations to find the underlying mount to shadow, see
643  * g_mount_is_shadowed() for more details.
644  *
645  * Returns: (nullable) (transfer full): the activation root of @volume
646  *     or %NULL. Use g_object_unref() to free.
647  *
648  * Since: 2.18
649  */
650 GFile *
g_volume_get_activation_root(GVolume * volume)651 g_volume_get_activation_root (GVolume *volume)
652 {
653   GVolumeIface *iface;
654 
655   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
656   iface = G_VOLUME_GET_IFACE (volume);
657 
658   if (iface->get_activation_root == NULL)
659     return NULL;
660 
661   return (* iface->get_activation_root) (volume);
662 }
663 
664 /**
665  * g_volume_get_sort_key:
666  * @volume: a #GVolume
667  *
668  * Gets the sort key for @volume, if any.
669  *
670  * Returns: (nullable): Sorting key for @volume or %NULL if no such key is available
671  *
672  * Since: 2.32
673  */
674 const gchar *
g_volume_get_sort_key(GVolume * volume)675 g_volume_get_sort_key (GVolume *volume)
676 {
677   const gchar *ret = NULL;
678   GVolumeIface *iface;
679 
680   g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
681 
682   iface = G_VOLUME_GET_IFACE (volume);
683   if (iface->get_sort_key != NULL)
684     ret = iface->get_sort_key (volume);
685 
686   return ret;
687 }
688