(root)/
glib-2.79.0/
gio/
gvolume.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   * 
       3   * Copyright (C) 2006-2007 Red Hat, Inc.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General
      18   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19   *
      20   * Author: Alexander Larsson <alexl@redhat.com>
      21   *         David Zeuthen <davidz@redhat.com>
      22   */
      23  
      24  #include "config.h"
      25  #include "gmount.h"
      26  #include "gvolume.h"
      27  #include "gthemedicon.h"
      28  #include "gasyncresult.h"
      29  #include "gtask.h"
      30  #include "gioerror.h"
      31  #include "glibintl.h"
      32  
      33  
      34  /**
      35   * GVolume:
      36   * 
      37   * The `GVolume` interface represents user-visible objects that can be
      38   * mounted. Note, when [porting from GnomeVFS](migrating-gnome-vfs.html),
      39   * `GVolume` is the moral equivalent of `GnomeVFSDrive`.
      40   *
      41   * Mounting a `GVolume` instance is an asynchronous operation. For more
      42   * information about asynchronous operations, see [iface@Gio.AsyncResult] and
      43   * [class@Gio.Task]. To mount a `GVolume`, first call [method@Gio.Volume.mount]
      44   * with (at least) the `GVolume` instance, optionally a
      45   * [class@Gio.MountOperation] object and a [type@Gio.AsyncReadyCallback].
      46   *
      47   * Typically, one will only want to pass `NULL` for the
      48   * [class@Gio.MountOperation] 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 [iface@Gio.AsyncResult] instance will be
      54   * passed to the callback.  That callback should then call
      55   * [method@Gio.Volume.mount_finish] with the `GVolume` instance and the
      56   * [iface@Gio.AsyncResult] data to see if the operation was completed
      57   * successfully.  If a [type@GLib.Error] is present when
      58   * [method@Gio.Volume.mount_finish] is called, then it will be filled with any
      59   * error information.
      60   *
      61   * ## Volume Identifiers
      62   *
      63   * It is sometimes necessary to directly access the underlying
      64   * operating system object behind a volume (e.g. for passing a volume
      65   * to an application via the command line). For this purpose, GIO
      66   * allows to obtain an ‘identifier’ for the volume. There can be
      67   * different kinds of identifiers, such as Hal UDIs, filesystem labels,
      68   * traditional Unix devices (e.g. `/dev/sda2`), UUIDs. GIO uses predefined
      69   * strings as names for the different kinds of identifiers:
      70   * `G_VOLUME_IDENTIFIER_KIND_UUID`, `G_VOLUME_IDENTIFIER_KIND_LABEL`, etc.
      71   * Use [method@Gio.Volume.get_identifier] to obtain an identifier for a volume.
      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;
      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 *
     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 *
     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 *
     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 *
     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 *
     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 *
     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
     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
     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
     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: (virtual mount_fn)
     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  void
     344  g_volume_mount (GVolume             *volume,
     345  		GMountMountFlags     flags,
     346                  GMountOperation     *mount_operation,
     347                  GCancellable        *cancellable,
     348                  GAsyncReadyCallback  callback,
     349                  gpointer             user_data)
     350  {
     351    GVolumeIface *iface;
     352  
     353    g_return_if_fail (G_IS_VOLUME (volume));
     354  
     355    iface = G_VOLUME_GET_IFACE (volume);
     356  
     357    if (iface->mount_fn == NULL)
     358      {
     359        g_task_report_new_error (volume, callback, user_data,
     360                                 g_volume_mount,
     361                                 G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     362                                 _("volume doesn’t implement mount"));
     363        return;
     364      }
     365    
     366    (* iface->mount_fn) (volume, flags, mount_operation, cancellable, callback, user_data);
     367  }
     368  
     369  /**
     370   * g_volume_mount_finish:
     371   * @volume: a #GVolume
     372   * @result: a #GAsyncResult
     373   * @error: a #GError location to store an error, or %NULL to ignore
     374   * 
     375   * Finishes mounting a volume. If any errors occurred during the operation,
     376   * @error will be set to contain the errors and %FALSE will be returned.
     377   *
     378   * If the mount operation succeeded, g_volume_get_mount() on @volume
     379   * is guaranteed to return the mount right after calling this
     380   * function; there's no need to listen for the 'mount-added' signal on
     381   * #GVolumeMonitor.
     382   * 
     383   * Returns: %TRUE, %FALSE if operation failed
     384   */
     385  gboolean
     386  g_volume_mount_finish (GVolume       *volume,
     387                         GAsyncResult  *result,
     388                         GError       **error)
     389  {
     390    GVolumeIface *iface;
     391  
     392    g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
     393    g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
     394  
     395    if (g_async_result_legacy_propagate_error (result, error))
     396      return FALSE;
     397    else if (g_async_result_is_tagged (result, g_volume_mount))
     398      return g_task_propagate_boolean (G_TASK (result), error);
     399    
     400    iface = G_VOLUME_GET_IFACE (volume);
     401    return (* iface->mount_finish) (volume, result, error);
     402  }
     403  
     404  /**
     405   * g_volume_eject:
     406   * @volume: a #GVolume
     407   * @flags: flags affecting the unmount if required for eject
     408   * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
     409   * @callback: (nullable): a #GAsyncReadyCallback, or %NULL
     410   * @user_data: user data that gets passed to @callback
     411   * 
     412   * Ejects a volume. This is an asynchronous operation, and is
     413   * finished by calling g_volume_eject_finish() with the @volume
     414   * and #GAsyncResult returned in the @callback.
     415   *
     416   * Deprecated: 2.22: Use g_volume_eject_with_operation() instead.
     417   */
     418  void
     419  g_volume_eject (GVolume             *volume,
     420  		GMountUnmountFlags   flags,
     421                  GCancellable        *cancellable,
     422                  GAsyncReadyCallback  callback,
     423                  gpointer             user_data)
     424  {
     425    GVolumeIface *iface;
     426  
     427    g_return_if_fail (G_IS_VOLUME (volume));
     428  
     429    iface = G_VOLUME_GET_IFACE (volume);
     430  
     431    if (iface->eject == NULL)
     432      {
     433        g_task_report_new_error (volume, callback, user_data,
     434                                 g_volume_eject_with_operation,
     435                                 G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     436                                 _("volume doesn’t implement eject"));
     437        return;
     438      }
     439    
     440    (* iface->eject) (volume, flags, cancellable, callback, user_data);
     441  }
     442  
     443  /**
     444   * g_volume_eject_finish:
     445   * @volume: pointer to a #GVolume
     446   * @result: a #GAsyncResult
     447   * @error: a #GError location to store an error, or %NULL to ignore
     448   * 
     449   * Finishes ejecting a volume. If any errors occurred during the operation,
     450   * @error will be set to contain the errors and %FALSE will be returned.
     451   * 
     452   * Returns: %TRUE, %FALSE if operation failed
     453   *
     454   * Deprecated: 2.22: Use g_volume_eject_with_operation_finish() instead.
     455   **/
     456  gboolean
     457  g_volume_eject_finish (GVolume       *volume,
     458                         GAsyncResult  *result,
     459                         GError       **error)
     460  {
     461    GVolumeIface *iface;
     462  
     463    g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
     464    g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
     465  
     466    if (g_async_result_legacy_propagate_error (result, error))
     467      return FALSE;
     468    if (g_async_result_is_tagged (result, g_volume_eject_with_operation))
     469      return g_task_propagate_boolean (G_TASK (result), error);
     470    
     471    iface = G_VOLUME_GET_IFACE (volume);
     472    return (* iface->eject_finish) (volume, result, error);
     473  }
     474  
     475  /**
     476   * g_volume_eject_with_operation:
     477   * @volume: a #GVolume
     478   * @flags: flags affecting the unmount if required for eject
     479   * @mount_operation: (nullable): a #GMountOperation or %NULL to
     480   *     avoid user interaction
     481   * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
     482   * @callback: (nullable): a #GAsyncReadyCallback, or %NULL
     483   * @user_data: user data passed to @callback
     484   *
     485   * Ejects a volume. This is an asynchronous operation, and is
     486   * finished by calling g_volume_eject_with_operation_finish() with the @volume
     487   * and #GAsyncResult data returned in the @callback.
     488   *
     489   * Since: 2.22
     490   **/
     491  void
     492  g_volume_eject_with_operation (GVolume              *volume,
     493                                 GMountUnmountFlags   flags,
     494                                 GMountOperation     *mount_operation,
     495                                 GCancellable        *cancellable,
     496                                 GAsyncReadyCallback  callback,
     497                                 gpointer             user_data)
     498  {
     499    GVolumeIface *iface;
     500  
     501    g_return_if_fail (G_IS_VOLUME (volume));
     502  
     503    iface = G_VOLUME_GET_IFACE (volume);
     504  
     505    if (iface->eject == NULL && iface->eject_with_operation == NULL)
     506      {
     507        g_task_report_new_error (volume, callback, user_data,
     508                                 g_volume_eject_with_operation,
     509                                 G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     510                                 /* Translators: This is an error
     511                                  * message for volume objects that
     512                                  * don't implement any of eject or eject_with_operation. */
     513                                 _("volume doesn’t implement eject or eject_with_operation"));
     514        return;
     515      }
     516  
     517    if (iface->eject_with_operation != NULL)
     518      (* iface->eject_with_operation) (volume, flags, mount_operation, cancellable, callback, user_data);
     519    else
     520      (* iface->eject) (volume, flags, cancellable, callback, user_data);
     521  }
     522  
     523  /**
     524   * g_volume_eject_with_operation_finish:
     525   * @volume: a #GVolume
     526   * @result: a #GAsyncResult
     527   * @error: a #GError location to store the error occurring, or %NULL
     528   *
     529   * Finishes ejecting a volume. If any errors occurred during the operation,
     530   * @error will be set to contain the errors and %FALSE will be returned.
     531   *
     532   * Returns: %TRUE if the volume was successfully ejected. %FALSE otherwise
     533   *
     534   * Since: 2.22
     535   **/
     536  gboolean
     537  g_volume_eject_with_operation_finish (GVolume        *volume,
     538                                        GAsyncResult  *result,
     539                                        GError       **error)
     540  {
     541    GVolumeIface *iface;
     542  
     543    g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
     544    g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
     545  
     546    if (g_async_result_legacy_propagate_error (result, error))
     547      return FALSE;
     548    else if (g_async_result_is_tagged (result, g_volume_eject_with_operation))
     549      return g_task_propagate_boolean (G_TASK (result), error);
     550  
     551    iface = G_VOLUME_GET_IFACE (volume);
     552    if (iface->eject_with_operation_finish != NULL)
     553      return (* iface->eject_with_operation_finish) (volume, result, error);
     554    else
     555      return (* iface->eject_finish) (volume, result, error);
     556  }
     557  
     558  /**
     559   * g_volume_get_identifier:
     560   * @volume: a #GVolume
     561   * @kind: the kind of identifier to return
     562   *
     563   * Gets the identifier of the given kind for @volume. 
     564   * See the [introduction](#volume-identifiers) for more
     565   * information about volume identifiers.
     566   *
     567   * Returns: (nullable) (transfer full): a newly allocated string containing the
     568   *     requested identifier, or %NULL if the #GVolume
     569   *     doesn't have this kind of identifier
     570   */
     571  char *
     572  g_volume_get_identifier (GVolume    *volume,
     573  			 const char *kind)
     574  {
     575    GVolumeIface *iface;
     576  
     577    g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
     578    g_return_val_if_fail (kind != NULL, NULL);
     579  
     580    iface = G_VOLUME_GET_IFACE (volume);
     581  
     582    if (iface->get_identifier == NULL)
     583      return NULL;
     584    
     585    return (* iface->get_identifier) (volume, kind);
     586  }
     587  
     588  /**
     589   * g_volume_enumerate_identifiers:
     590   * @volume: a #GVolume
     591   * 
     592   * Gets the kinds of [identifiers](#volume-identifiers) that @volume has.
     593   * Use g_volume_get_identifier() to obtain the identifiers themselves.
     594   *
     595   * Returns: (array zero-terminated=1) (transfer full): a %NULL-terminated array
     596   *   of strings containing kinds of identifiers. Use g_strfreev() to free.
     597   */
     598  char **
     599  g_volume_enumerate_identifiers (GVolume *volume)
     600  {
     601    GVolumeIface *iface;
     602  
     603    g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
     604    iface = G_VOLUME_GET_IFACE (volume);
     605  
     606    if (iface->enumerate_identifiers == NULL)
     607      return NULL;
     608    
     609    return (* iface->enumerate_identifiers) (volume);
     610  }
     611  
     612  /**
     613   * g_volume_get_activation_root:
     614   * @volume: a #GVolume
     615   *
     616   * Gets the activation root for a #GVolume if it is known ahead of
     617   * mount time. Returns %NULL otherwise. If not %NULL and if @volume
     618   * is mounted, then the result of g_mount_get_root() on the
     619   * #GMount object obtained from g_volume_get_mount() will always
     620   * either be equal or a prefix of what this function returns. In
     621   * other words, in code
     622   *
     623   * |[<!-- language="C" -->
     624   *   GMount *mount;
     625   *   GFile *mount_root
     626   *   GFile *volume_activation_root;
     627   *
     628   *   mount = g_volume_get_mount (volume); // mounted, so never NULL
     629   *   mount_root = g_mount_get_root (mount);
     630   *   volume_activation_root = g_volume_get_activation_root (volume); // assume not NULL
     631   * ]|
     632   * then the expression
     633   * |[<!-- language="C" -->
     634   *   (g_file_has_prefix (volume_activation_root, mount_root) ||
     635   *    g_file_equal (volume_activation_root, mount_root))
     636   * ]|
     637   * will always be %TRUE.
     638   *
     639   * Activation roots are typically used in #GVolumeMonitor
     640   * implementations to find the underlying mount to shadow, see
     641   * g_mount_is_shadowed() for more details.
     642   *
     643   * Returns: (nullable) (transfer full): the activation root of @volume
     644   *     or %NULL. Use g_object_unref() to free.
     645   *
     646   * Since: 2.18
     647   */
     648  GFile *
     649  g_volume_get_activation_root (GVolume *volume)
     650  {
     651    GVolumeIface *iface;
     652  
     653    g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
     654    iface = G_VOLUME_GET_IFACE (volume);
     655  
     656    if (iface->get_activation_root == NULL)
     657      return NULL;
     658  
     659    return (* iface->get_activation_root) (volume);
     660  }
     661  
     662  /**
     663   * g_volume_get_sort_key:
     664   * @volume: a #GVolume
     665   *
     666   * Gets the sort key for @volume, if any.
     667   *
     668   * Returns: (nullable): Sorting key for @volume or %NULL if no such key is available
     669   *
     670   * Since: 2.32
     671   */
     672  const gchar *
     673  g_volume_get_sort_key (GVolume *volume)
     674  {
     675    const gchar *ret = NULL;
     676    GVolumeIface *iface;
     677  
     678    g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
     679  
     680    iface = G_VOLUME_GET_IFACE (volume);
     681    if (iface->get_sort_key != NULL)
     682      ret = iface->get_sort_key (volume);
     683  
     684    return ret;
     685  }