/src/fwupd/libfwupdplugin/fu-volume-locker.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2025 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuVolumeLocker" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-volume-locker.h" |
12 | | |
13 | | /** |
14 | | * FuVolumeLocker: |
15 | | * |
16 | | * Easily unmount a volume when an object goes out of scope. |
17 | | * |
18 | | * See also: [class@FuVolume] |
19 | | */ |
20 | | |
21 | | struct _FuVolumeLocker { |
22 | | GObject parent_instance; |
23 | | FuVolume *volume; |
24 | | gboolean is_open; |
25 | | }; |
26 | | |
27 | 0 | G_DEFINE_TYPE(FuVolumeLocker, fu_volume_locker, G_TYPE_OBJECT) |
28 | 0 |
|
29 | 0 | /** |
30 | 0 | * fu_volume_locker_close: |
31 | 0 | * @self: a #FuVolumeLocker |
32 | 0 | * @error: (nullable): optional return location for an error |
33 | 0 | * |
34 | 0 | * Closes the volume before it gets cleaned up. |
35 | 0 | * |
36 | 0 | * This function can be used to manually unmount a volume managed by a locker, |
37 | 0 | * and allows the caller to properly handle the error. |
38 | 0 | * |
39 | 0 | * Returns: %TRUE for success |
40 | 0 | * |
41 | 0 | * Since: 2.0.15 |
42 | 0 | **/ |
43 | 0 | gboolean |
44 | 0 | fu_volume_locker_close(FuVolumeLocker *self, GError **error) |
45 | 0 | { |
46 | 0 | g_autoptr(GError) error_local = NULL; |
47 | |
|
48 | 0 | g_return_val_if_fail(FU_IS_VOLUME_LOCKER(self), FALSE); |
49 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
50 | | |
51 | 0 | if (!self->is_open) |
52 | 0 | return TRUE; |
53 | 0 | if (!fu_volume_unmount(self->volume, &error_local)) { |
54 | 0 | if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { |
55 | 0 | g_debug("ignoring: %s", error_local->message); |
56 | 0 | return TRUE; |
57 | 0 | } |
58 | 0 | g_propagate_error(error, g_steal_pointer(&error_local)); |
59 | 0 | return FALSE; |
60 | 0 | } |
61 | | |
62 | | /* success */ |
63 | 0 | self->is_open = FALSE; |
64 | 0 | return TRUE; |
65 | 0 | } |
66 | | |
67 | | static void |
68 | | fu_volume_locker_finalize(GObject *obj) |
69 | 0 | { |
70 | 0 | FuVolumeLocker *self = FU_VOLUME_LOCKER(obj); |
71 | |
|
72 | 0 | if (self->is_open) { |
73 | 0 | g_autoptr(GError) error_local = NULL; |
74 | 0 | if (!fu_volume_unmount(self->volume, &error_local)) |
75 | 0 | g_warning("failed to close volume: %s", error_local->message); |
76 | 0 | } |
77 | 0 | if (self->volume != NULL) |
78 | 0 | g_object_unref(self->volume); |
79 | 0 | G_OBJECT_CLASS(fu_volume_locker_parent_class)->finalize(obj); |
80 | 0 | } |
81 | | |
82 | | static void |
83 | | fu_volume_locker_class_init(FuVolumeLockerClass *klass) |
84 | 0 | { |
85 | 0 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
86 | 0 | object_class->finalize = fu_volume_locker_finalize; |
87 | 0 | } |
88 | | |
89 | | static void |
90 | | fu_volume_locker_init(FuVolumeLocker *self) |
91 | 0 | { |
92 | 0 | } |
93 | | |
94 | | /** |
95 | | * fu_volume_locker_new: |
96 | | * @volume: a #GObject |
97 | | * @error: (nullable): optional return location for an error |
98 | | * |
99 | | * Locks the volume, mounting it and unmounting it as required. If the volume is |
100 | | * already mounted then it is is _not_ unmounted when the locker is closed. |
101 | | * |
102 | | * Returns: (transfer full): a volume locker if mounted, or %NULL |
103 | | * |
104 | | * Since: 2.0.15 |
105 | | **/ |
106 | | FuVolumeLocker * |
107 | | fu_volume_locker_new(FuVolume *volume, GError **error) |
108 | 0 | { |
109 | 0 | g_autoptr(FuVolumeLocker) self = g_object_new(FU_TYPE_VOLUME_LOCKER, NULL); |
110 | |
|
111 | 0 | g_return_val_if_fail(FU_IS_VOLUME(volume), NULL); |
112 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
113 | | |
114 | | /* already open, so NOP */ |
115 | 0 | if (fu_volume_is_mounted(volume)) |
116 | 0 | return g_steal_pointer(&self); |
117 | | |
118 | | /* open volume */ |
119 | 0 | if (!fu_volume_mount(volume, error)) { |
120 | 0 | g_autoptr(GError) error_local = NULL; |
121 | 0 | if (!fu_volume_unmount(volume, &error_local)) { |
122 | 0 | if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { |
123 | 0 | g_debug("ignoring unmount error on aborted mount: %s", |
124 | 0 | error_local->message); |
125 | 0 | } |
126 | 0 | } |
127 | 0 | return NULL; |
128 | 0 | } |
129 | | |
130 | | /* create object */ |
131 | 0 | self = g_object_new(FU_TYPE_VOLUME_LOCKER, NULL); |
132 | 0 | self->is_open = TRUE; |
133 | 0 | self->volume = g_object_ref(volume); |
134 | 0 | return g_steal_pointer(&self); |
135 | 0 | } |