Coverage Report

Created: 2025-06-13 06:55

/src/glib/gio/gunixvolume.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3
/* GIO - GLib Input, Output and Streaming Library
4
 * 
5
 * Copyright (C) 2006-2007 Red Hat, Inc.
6
 *
7
 * SPDX-License-Identifier: LGPL-2.1-or-later
8
 *
9
 * This library is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public
11
 * License as published by the Free Software Foundation; either
12
 * version 2.1 of the License, or (at your option) any later version.
13
 *
14
 * This library is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 * Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General
20
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
21
 *
22
 * Author: Alexander Larsson <alexl@redhat.com>
23
 *         David Zeuthen <davidz@redhat.com>
24
 */
25
26
#include "config.h"
27
28
#include <string.h>
29
#include <sys/wait.h>
30
#include <unistd.h>
31
32
#include <glib.h>
33
#include "gunixvolume.h"
34
#include "gunixmount.h"
35
#include "gunixmounts.h"
36
#include "gthemedicon.h"
37
#include "gvolume.h"
38
#include "gvolumemonitor.h"
39
#include "gtask.h"
40
#include "gioerror.h"
41
#include "glibintl.h"
42
/* for BUFSIZ */
43
#include <stdio.h>
44
45
46
struct _GUnixVolume {
47
  GObject parent;
48
49
  GVolumeMonitor *volume_monitor;
50
  GUnixMount     *mount; /* owned by volume monitor */
51
  
52
  char *device_path;
53
  char *mount_path;
54
  gboolean can_eject;
55
56
  char *identifier;
57
  char *identifier_type;
58
  
59
  char *name;
60
  GIcon *icon;
61
  GIcon *symbolic_icon;
62
};
63
64
static void g_unix_volume_volume_iface_init (GVolumeIface *iface);
65
66
#define g_unix_volume_get_type _g_unix_volume_get_type
67
G_DEFINE_TYPE_WITH_CODE (GUnixVolume, g_unix_volume, G_TYPE_OBJECT,
68
       G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
69
            g_unix_volume_volume_iface_init))
70
71
static void
72
g_unix_volume_finalize (GObject *object)
73
0
{
74
0
  GUnixVolume *volume;
75
  
76
0
  volume = G_UNIX_VOLUME (object);
77
78
0
  if (volume->volume_monitor != NULL)
79
0
    g_object_unref (volume->volume_monitor);
80
81
0
  if (volume->mount)
82
0
    _g_unix_mount_unset_volume (volume->mount, volume);
83
  
84
0
  g_object_unref (volume->icon);
85
0
  g_object_unref (volume->symbolic_icon);
86
0
  g_free (volume->name);
87
0
  g_free (volume->mount_path);
88
0
  g_free (volume->device_path);
89
0
  g_free (volume->identifier);
90
0
  g_free (volume->identifier_type);
91
92
0
  G_OBJECT_CLASS (g_unix_volume_parent_class)->finalize (object);
93
0
}
94
95
static void
96
g_unix_volume_class_init (GUnixVolumeClass *klass)
97
0
{
98
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
99
100
0
  gobject_class->finalize = g_unix_volume_finalize;
101
0
}
102
103
static void
104
g_unix_volume_init (GUnixVolume *unix_volume)
105
0
{
106
0
}
107
108
GUnixVolume *
109
_g_unix_volume_new (GVolumeMonitor  *volume_monitor,
110
                    GUnixMountPoint *mountpoint)
111
0
{
112
0
  GUnixVolume *volume;
113
  
114
0
  if (!(g_unix_mount_point_is_user_mountable (mountpoint) ||
115
0
  g_str_has_prefix (g_unix_mount_point_get_device_path (mountpoint), "/vol/")) ||
116
0
      g_unix_mount_point_is_loopback (mountpoint))
117
0
    return NULL;
118
  
119
0
  volume = g_object_new (G_TYPE_UNIX_VOLUME, NULL);
120
0
  volume->volume_monitor = volume_monitor != NULL ? g_object_ref (volume_monitor) : NULL;
121
0
  volume->mount_path = g_strdup (g_unix_mount_point_get_mount_path (mountpoint));
122
0
  volume->device_path = g_strdup (g_unix_mount_point_get_device_path (mountpoint));
123
0
  volume->can_eject = g_unix_mount_point_guess_can_eject (mountpoint);
124
125
0
  volume->name = g_unix_mount_point_guess_name (mountpoint);
126
0
  volume->icon = g_unix_mount_point_guess_icon (mountpoint);
127
0
  volume->symbolic_icon = g_unix_mount_point_guess_symbolic_icon (mountpoint);
128
129
130
0
  if (strcmp (g_unix_mount_point_get_fs_type (mountpoint), "nfs") == 0)
131
0
    {
132
0
      volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_NFS_MOUNT);
133
0
      volume->identifier = g_strdup (volume->device_path);
134
0
    }
135
0
  else if (g_str_has_prefix (volume->device_path, "LABEL="))
136
0
    {
137
0
      volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_LABEL);
138
0
      volume->identifier = g_strdup (volume->device_path + 6);
139
0
    }
140
0
  else if (g_str_has_prefix (volume->device_path, "UUID="))
141
0
    {
142
0
      volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_UUID);
143
0
      volume->identifier = g_strdup (volume->device_path + 5);
144
0
    }
145
0
  else if (g_path_is_absolute (volume->device_path))
146
0
    {
147
0
      volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
148
0
      volume->identifier = g_strdup (volume->device_path);
149
0
    }
150
  
151
0
  return volume;
152
0
}
153
154
void
155
_g_unix_volume_disconnected (GUnixVolume *volume)
156
0
{
157
0
  if (volume->mount)
158
0
    {
159
0
      _g_unix_mount_unset_volume (volume->mount, volume);
160
0
      volume->mount = NULL;
161
0
    }
162
0
}
163
164
void
165
_g_unix_volume_set_mount (GUnixVolume *volume,
166
                          GUnixMount  *mount)
167
0
{
168
0
  if (volume->mount == mount)
169
0
    return;
170
  
171
0
  if (volume->mount)
172
0
    _g_unix_mount_unset_volume (volume->mount, volume);
173
  
174
0
  volume->mount = mount;
175
  
176
  /* TODO: Emit changed in idle to avoid locking issues */
177
0
  g_signal_emit_by_name (volume, "changed");
178
0
  if (volume->volume_monitor != NULL)
179
0
    g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
180
0
}
181
182
void
183
_g_unix_volume_unset_mount (GUnixVolume  *volume,
184
                            GUnixMount *mount)
185
0
{
186
0
  if (volume->mount == mount)
187
0
    {
188
0
      volume->mount = NULL;
189
      /* TODO: Emit changed in idle to avoid locking issues */
190
0
      g_signal_emit_by_name (volume, "changed");
191
0
      if (volume->volume_monitor != NULL)
192
0
        g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
193
0
    }
194
0
}
195
196
static GIcon *
197
g_unix_volume_get_icon (GVolume *volume)
198
0
{
199
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
200
0
  return g_object_ref (unix_volume->icon);
201
0
}
202
203
static GIcon *
204
g_unix_volume_get_symbolic_icon (GVolume *volume)
205
0
{
206
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
207
0
  return g_object_ref (unix_volume->symbolic_icon);
208
0
}
209
210
static char *
211
g_unix_volume_get_name (GVolume *volume)
212
0
{
213
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
214
0
  return g_strdup (unix_volume->name);
215
0
}
216
217
static char *
218
g_unix_volume_get_uuid (GVolume *volume)
219
0
{
220
0
  return NULL;
221
0
}
222
223
static gboolean
224
g_unix_volume_can_mount (GVolume *volume)
225
0
{
226
0
  return TRUE;
227
0
}
228
229
static gboolean
230
g_unix_volume_can_eject (GVolume *volume)
231
0
{
232
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
233
0
  return unix_volume->can_eject;
234
0
}
235
236
static gboolean
237
g_unix_volume_should_automount (GVolume *volume)
238
0
{
239
  /* We automount all local volumes because we don't even
240
   * make the internal stuff visible
241
   */
242
0
  return TRUE;
243
0
}
244
245
static GDrive *
246
g_unix_volume_get_drive (GVolume *volume)
247
0
{
248
0
  return NULL;
249
0
}
250
251
static GMount *
252
g_unix_volume_get_mount (GVolume *volume)
253
0
{
254
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
255
256
0
  if (unix_volume->mount != NULL)
257
0
    return g_object_ref (G_MOUNT (unix_volume->mount));
258
259
0
  return NULL;
260
0
}
261
262
263
gboolean
264
_g_unix_volume_has_mount_path (GUnixVolume *volume,
265
                               const char  *mount_path)
266
0
{
267
0
  return strcmp (volume->mount_path, mount_path) == 0;
268
0
}
269
270
static void
271
eject_mount_done (GObject      *source,
272
                  GAsyncResult *result,
273
                  gpointer      user_data)
274
0
{
275
0
  GSubprocess *subprocess = G_SUBPROCESS (source);
276
0
  GTask *task = user_data;
277
0
  GError *error = NULL;
278
0
  gchar *stderr_str;
279
0
  GUnixVolume *unix_volume;
280
281
0
  if (!g_subprocess_communicate_utf8_finish (subprocess, result, NULL, &stderr_str, &error))
282
0
    {
283
0
      g_task_return_error (task, error);
284
0
      g_error_free (error);
285
0
    }
286
0
  else /* successful communication */
287
0
    {
288
0
      if (!g_subprocess_get_successful (subprocess))
289
        /* ...but bad exit code */
290
0
        g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", stderr_str);
291
0
      else
292
0
        {
293
          /* ...and successful exit code */
294
0
          unix_volume = G_UNIX_VOLUME (g_task_get_source_object (task));
295
0
          _g_unix_volume_monitor_update (G_UNIX_VOLUME_MONITOR (unix_volume->volume_monitor));
296
0
          g_task_return_boolean (task, TRUE);
297
0
        }
298
299
0
      g_free (stderr_str);
300
0
    }
301
302
0
  g_object_unref (task);
303
0
}
304
305
static void
306
eject_mount_do (GVolume              *volume,
307
                GCancellable         *cancellable,
308
                GAsyncReadyCallback   callback,
309
                gpointer              user_data,
310
                const gchar * const  *argv,
311
                const gchar          *task_name)
312
0
{
313
0
  GSubprocess *subprocess;
314
0
  GError *error = NULL;
315
0
  GTask *task;
316
317
0
  task = g_task_new (volume, cancellable, callback, user_data);
318
0
  g_task_set_source_tag (task, eject_mount_do);
319
0
  g_task_set_name (task, task_name);
320
321
0
  if (g_task_return_error_if_cancelled (task))
322
0
    {
323
0
      g_object_unref (task);
324
0
      return;
325
0
    }
326
327
0
  subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_PIPE, &error);
328
0
  g_assert_no_error (error);
329
330
0
  g_subprocess_communicate_utf8_async (subprocess, NULL,
331
0
                                       g_task_get_cancellable (task),
332
0
                                       eject_mount_done, task);
333
0
}
334
335
static void
336
g_unix_volume_mount (GVolume            *volume,
337
                     GMountMountFlags    flags,
338
                     GMountOperation     *mount_operation,
339
                     GCancellable        *cancellable,
340
                     GAsyncReadyCallback  callback,
341
                     gpointer             user_data)
342
0
{
343
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
344
0
  const gchar *argv[] = { "mount", NULL, NULL };
345
346
0
  if (unix_volume->mount_path != NULL)
347
0
    argv[1] = unix_volume->mount_path;
348
0
  else
349
0
    argv[1] = unix_volume->device_path;
350
351
0
  eject_mount_do (volume, cancellable, callback, user_data, argv, "[gio] mount volume");
352
0
}
353
354
static gboolean
355
g_unix_volume_mount_finish (GVolume        *volume,
356
                            GAsyncResult  *result,
357
                            GError       **error)
358
0
{
359
0
  g_return_val_if_fail (g_task_is_valid (result, volume), FALSE);
360
361
0
  return g_task_propagate_boolean (G_TASK (result), error);
362
0
}
363
364
static void
365
g_unix_volume_eject (GVolume             *volume,
366
                     GMountUnmountFlags   flags,
367
                     GCancellable        *cancellable,
368
                     GAsyncReadyCallback  callback,
369
                     gpointer             user_data)
370
0
{
371
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
372
0
  const gchar *argv[] = { "eject", NULL, NULL };
373
374
0
  argv[1] = unix_volume->device_path;
375
376
0
  eject_mount_do (volume, cancellable, callback, user_data, argv, "[gio] eject volume");
377
0
}
378
379
static gboolean
380
g_unix_volume_eject_finish (GVolume       *volume,
381
                            GAsyncResult  *result,
382
                            GError       **error)
383
0
{
384
0
  g_return_val_if_fail (g_task_is_valid (result, volume), FALSE);
385
386
0
  return g_task_propagate_boolean (G_TASK (result), error);
387
0
}
388
389
static gchar *
390
g_unix_volume_get_identifier (GVolume     *volume,
391
                              const gchar *kind)
392
0
{
393
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
394
395
0
  if (unix_volume->identifier_type != NULL &&
396
0
      strcmp (kind, unix_volume->identifier_type) == 0)
397
0
    return g_strdup (unix_volume->identifier);
398
399
0
  return NULL;
400
0
}
401
402
static gchar **
403
g_unix_volume_enumerate_identifiers (GVolume *volume)
404
0
{
405
0
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
406
0
  gchar **res;
407
408
0
  if (unix_volume->identifier_type)
409
0
    {
410
0
      res = g_new (gchar *, 2);
411
0
      res[0] = g_strdup (unix_volume->identifier_type);
412
0
      res[1] = NULL;
413
0
    }
414
0
  else
415
0
    {
416
0
      res = g_new (gchar *, 1);
417
0
      res[0] = NULL;
418
0
    }
419
420
0
  return res;
421
0
}
422
423
static void
424
g_unix_volume_volume_iface_init (GVolumeIface *iface)
425
0
{
426
0
  iface->get_name = g_unix_volume_get_name;
427
0
  iface->get_icon = g_unix_volume_get_icon;
428
0
  iface->get_symbolic_icon = g_unix_volume_get_symbolic_icon;
429
0
  iface->get_uuid = g_unix_volume_get_uuid;
430
0
  iface->get_drive = g_unix_volume_get_drive;
431
0
  iface->get_mount = g_unix_volume_get_mount;
432
0
  iface->can_mount = g_unix_volume_can_mount;
433
0
  iface->can_eject = g_unix_volume_can_eject;
434
0
  iface->should_automount = g_unix_volume_should_automount;
435
0
  iface->mount_fn = g_unix_volume_mount;
436
0
  iface->mount_finish = g_unix_volume_mount_finish;
437
0
  iface->eject = g_unix_volume_eject;
438
0
  iface->eject_finish = g_unix_volume_eject_finish;
439
0
  iface->get_identifier = g_unix_volume_get_identifier;
440
0
  iface->enumerate_identifiers = g_unix_volume_enumerate_identifiers;
441
0
}