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; |
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 | signal_flags: G_SIGNAL_RUN_LAST, |
94 | G_STRUCT_OFFSET (GVolumeIface, changed), |
95 | NULL, NULL, |
96 | NULL, |
97 | G_TYPE_NONE, n_params: 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 | signal_flags: G_SIGNAL_RUN_LAST, |
109 | G_STRUCT_OFFSET (GVolumeIface, removed), |
110 | NULL, NULL, |
111 | NULL, |
112 | G_TYPE_NONE, n_params: 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 (iconname: "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: |
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 |
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 (source_object: volume, callback, callback_data: user_data, |
362 | source_tag: g_volume_mount, |
363 | G_IO_ERROR, code: 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 |
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 (res: result, error)) |
398 | return FALSE; |
399 | else if (g_async_result_is_tagged (res: result, source_tag: 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 |
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 (source_object: volume, callback, callback_data: user_data, |
436 | source_tag: g_volume_eject_with_operation, |
437 | G_IO_ERROR, code: 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 |
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 (res: result, error)) |
469 | return FALSE; |
470 | if (g_async_result_is_tagged (res: result, source_tag: 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 |
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 (source_object: volume, callback, callback_data: user_data, |
510 | source_tag: g_volume_eject_with_operation, |
511 | G_IO_ERROR, code: 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 |
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 (res: result, error)) |
549 | return FALSE; |
550 | else if (g_async_result_is_tagged (res: result, source_tag: 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 * |
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 ** |
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 * |
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 * |
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 | |