Coverage Report

Created: 2025-07-01 07:09

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