Coverage Report

Created: 2026-02-14 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glib/gio/gunixmounts.c
Line
Count
Source
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
 */
24
25
/* Prologue {{{1 */
26
27
#include "config.h"
28
29
#include <sys/types.h>
30
#include <sys/stat.h>
31
#include <sys/wait.h>
32
#ifndef HAVE_SYSCTLBYNAME
33
#ifdef HAVE_SYS_PARAM_H
34
#include <sys/param.h>
35
#endif
36
#endif
37
#ifdef HAVE_POLL
38
#include <poll.h>
39
#endif
40
#include <stdio.h>
41
#include <unistd.h>
42
#include <sys/time.h>
43
#include <errno.h>
44
#include <stdlib.h>
45
#include <string.h>
46
#include <signal.h>
47
#include <gstdio.h>
48
#include <dirent.h>
49
50
#if defined(__ANDROID__) && (__ANDROID_API__ < 26)
51
#include <mntent.h>
52
/* the shared object of recent bionic libc's have hasmntopt symbol, but
53
   some a possible common build environment for android, termux ends
54
   up with insufficient __ANDROID_API__ value for building.
55
*/
56
extern char* hasmntopt(const struct mntent* mnt, const char* opt);
57
#endif
58
59
#if HAVE_SYS_STATFS_H
60
#include <sys/statfs.h>
61
#endif
62
#if HAVE_SYS_STATVFS_H
63
#include <sys/statvfs.h>
64
#endif
65
#if HAVE_SYS_VFS_H
66
#include <sys/vfs.h>
67
#elif HAVE_SYS_MOUNT_H
68
#if HAVE_SYS_PARAM_H
69
#include <sys/param.h>
70
#endif
71
#include <sys/mount.h>
72
#endif
73
74
#ifndef O_BINARY
75
#define O_BINARY 0
76
#endif
77
78
#include "gunixmounts.h"
79
#include "gunixmounts-private.h"
80
#include "gfile.h"
81
#include "gfilemonitor.h"
82
#include "glibintl.h"
83
#include "glocalfile.h"
84
#include "gstdio.h"
85
#include "gthemedicon.h"
86
#include "gcontextspecificgroup.h"
87
88
89
#ifdef HAVE_MNTENT_H
90
static const char *_resolve_dev_root (void);
91
#endif
92
93
/**
94
 * GUnixMountType:
95
 * @G_UNIX_MOUNT_TYPE_UNKNOWN: Unknown Unix mount type.
96
 * @G_UNIX_MOUNT_TYPE_FLOPPY: Floppy disk Unix mount type.
97
 * @G_UNIX_MOUNT_TYPE_CDROM: CDROM Unix mount type.
98
 * @G_UNIX_MOUNT_TYPE_NFS: Network File System (NFS) Unix mount type.
99
 * @G_UNIX_MOUNT_TYPE_ZIP: ZIP Unix mount type.
100
 * @G_UNIX_MOUNT_TYPE_JAZ: JAZZ Unix mount type.
101
 * @G_UNIX_MOUNT_TYPE_MEMSTICK: Memory Stick Unix mount type.
102
 * @G_UNIX_MOUNT_TYPE_CF: Compact Flash Unix mount type.
103
 * @G_UNIX_MOUNT_TYPE_SM: Smart Media Unix mount type.
104
 * @G_UNIX_MOUNT_TYPE_SDMMC: SD/MMC Unix mount type.
105
 * @G_UNIX_MOUNT_TYPE_IPOD: iPod Unix mount type.
106
 * @G_UNIX_MOUNT_TYPE_CAMERA: Digital camera Unix mount type.
107
 * @G_UNIX_MOUNT_TYPE_HD: Hard drive Unix mount type.
108
 * 
109
 * Types of Unix mounts.
110
 **/
111
typedef enum {
112
  G_UNIX_MOUNT_TYPE_UNKNOWN,
113
  G_UNIX_MOUNT_TYPE_FLOPPY,
114
  G_UNIX_MOUNT_TYPE_CDROM,
115
  G_UNIX_MOUNT_TYPE_NFS,
116
  G_UNIX_MOUNT_TYPE_ZIP,
117
  G_UNIX_MOUNT_TYPE_JAZ,
118
  G_UNIX_MOUNT_TYPE_MEMSTICK,
119
  G_UNIX_MOUNT_TYPE_CF,
120
  G_UNIX_MOUNT_TYPE_SM,
121
  G_UNIX_MOUNT_TYPE_SDMMC,
122
  G_UNIX_MOUNT_TYPE_IPOD,
123
  G_UNIX_MOUNT_TYPE_CAMERA,
124
  G_UNIX_MOUNT_TYPE_HD
125
} GUnixMountType;
126
127
struct _GUnixMountEntry {
128
  char *mount_path;
129
  char *device_path;
130
  char *root_path;
131
  char *filesystem_type;
132
  char *options;
133
  gboolean is_read_only;
134
  gboolean is_system_internal;
135
};
136
137
0
G_DEFINE_BOXED_TYPE (GUnixMountEntry, g_unix_mount_entry, g_unix_mount_entry_copy, g_unix_mount_entry_free)
138
0
139
0
struct _GUnixMountPoint {
140
0
  char *mount_path;
141
0
  char *device_path;
142
0
  char *filesystem_type;
143
0
  char *options;
144
0
  gboolean is_read_only;
145
0
  gboolean is_user_mountable;
146
0
  gboolean is_loopback;
147
0
};
148
0
149
0
G_DEFINE_BOXED_TYPE (GUnixMountPoint, g_unix_mount_point,
150
0
                     g_unix_mount_point_copy, g_unix_mount_point_free)
151
0
152
0
static GList *_g_get_unix_mounts (void);
153
0
static GUnixMountEntry **_g_unix_mounts_get_from_file (const char *table_path,
154
0
                                                       uint64_t   *time_read_out,
155
0
                                                       size_t     *n_entries_out);
156
0
static GList *_g_get_unix_mount_points (void);
157
0
static GUnixMountPoint **_g_unix_mount_points_get_from_file (const char *table_path,
158
0
                                                             uint64_t   *time_read_out,
159
0
                                                             size_t     *n_points_out);
160
0
static gboolean proc_mounts_watch_is_running (void);
161
0
162
0
G_LOCK_DEFINE_STATIC (proc_mounts_source);
163
0
164
0
/* Protected by proc_mounts_source lock */
165
0
static guint64 mount_poller_time = 0;
166
0
static GSource *proc_mounts_watch_source = NULL;
167
0
168
0
#ifdef HAVE_SYS_MNTTAB_H
169
0
#define MNTOPT_RO "ro"
170
0
#endif
171
0
172
0
#ifdef HAVE_MNTENT_H
173
0
#include <mntent.h>
174
0
#ifdef HAVE_LIBMOUNT
175
0
#include <libmount.h>
176
0
#endif
177
0
#elif defined (HAVE_SYS_MNTTAB_H)
178
0
#include <sys/mnttab.h>
179
0
#if defined(__sun) && !defined(mnt_opts)
180
0
#define mnt_opts mnt_mntopts
181
0
#endif
182
0
#endif
183
0
184
0
#ifdef HAVE_SYS_VFSTAB_H
185
0
#include <sys/vfstab.h>
186
0
#endif
187
0
188
0
#if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
189
0
#include <sys/mntctl.h>
190
0
#include <sys/vfs.h>
191
0
#include <sys/vmount.h>
192
0
#include <fshelp.h>
193
0
#endif
194
0
195
0
#if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT) || defined(HAVE_GETFSENT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
196
0
#include <sys/param.h>
197
0
#include <sys/mount.h>
198
0
#include <fstab.h>
199
0
#ifdef HAVE_SYS_UCRED_H
200
0
#include <sys/ucred.h>
201
0
#endif
202
0
#ifdef HAVE_SYS_SYSCTL_H
203
0
#include <sys/sysctl.h>
204
0
#endif
205
0
#endif
206
0
207
0
#ifndef HAVE_SETMNTENT
208
0
#define setmntent(f,m) g_fopen (f, m)
209
0
#endif
210
0
#ifndef HAVE_ENDMNTENT
211
0
#define endmntent(f) fclose(f)
212
0
#endif
213
0
214
0
#ifdef HAVE_LIBMOUNT
215
0
/* Protected by proc_mounts_source lock */
216
0
static struct libmnt_monitor *proc_mounts_monitor = NULL;
217
0
#endif
218
0
219
0
static guint64 get_mounts_timestamp (void);
220
0
static guint64 get_mount_points_timestamp (void);
221
0
222
0
static int
223
0
compare_str (const char * key,
224
0
             const char * const *element)
225
0
{
226
0
  return strcmp (key, *element);
227
0
}
228
229
static gboolean
230
is_in (const char *value, const char *set[], gsize set_size)
231
0
{
232
0
  return bsearch (value, set, set_size, sizeof (char *), (GCompareFunc)compare_str) != NULL;
233
0
}
234
235
/* Marked as unused because these are only used on some platform variants, but
236
 * working out the #if sequence for that would be too much for my little brain. */
237
static GList *unix_mount_entry_array_free_to_list (GUnixMountEntry **entries,
238
                                                   size_t            n_entries) G_GNUC_UNUSED;
239
static GList *unix_mount_point_array_free_to_list (GUnixMountPoint **points,
240
                                                   size_t            n_points) G_GNUC_UNUSED;
241
242
/* Helper to convert to a list for the old API.
243
 * Steals ownership of the @entries array. */
244
static GList *
245
unix_mount_entry_array_free_to_list (GUnixMountEntry **entries,
246
                                     size_t            n_entries)
247
0
{
248
0
  GList *l = NULL;
249
250
0
  for (size_t i = 0; i < n_entries; i++)
251
0
    l = g_list_prepend (l, g_steal_pointer (&entries[i]));
252
253
0
  g_free (entries);
254
255
0
  return g_list_reverse (l);
256
0
}
257
258
/* Helper to convert to a list for the old API.
259
 * Steals ownership of the @entries array. */
260
static GList *
261
unix_mount_point_array_free_to_list (GUnixMountPoint **points,
262
                                     size_t            n_points)
263
0
{
264
0
  GList *l = NULL;
265
266
0
  for (size_t i = 0; i < n_points; i++)
267
0
    l = g_list_prepend (l, g_steal_pointer (&points[i]));
268
269
0
  g_free (points);
270
271
0
  return g_list_reverse (l);
272
0
}
273
274
/**
275
 * g_unix_is_mount_path_system_internal:
276
 * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr`
277
 *
278
 * Determines if @mount_path is considered an implementation of the
279
 * OS.
280
 *
281
 * This is primarily used for hiding mountable and mounted volumes
282
 * that only are used in the OS and has little to no relevance to the
283
 * casual user.
284
 *
285
 * Returns: true if @mount_path is considered an implementation detail
286
 *    of the OS; false otherwise
287
 **/
288
gboolean
289
g_unix_is_mount_path_system_internal (const char *mount_path)
290
0
{
291
0
  if (is_in (mount_path, system_mount_paths, G_N_ELEMENTS (system_mount_paths)))
292
0
    return TRUE;
293
294
  /* Kept separate from sorted list as they may vary */
295
0
  if (g_str_equal (GLIB_LOCALSTATEDIR, mount_path) ||
296
0
      g_str_equal (GLIB_RUNSTATEDIR, mount_path))
297
0
    return TRUE;
298
299
0
  if (g_str_has_prefix (mount_path, "/dev/") ||
300
0
      g_str_has_prefix (mount_path, "/proc/") ||
301
0
      g_str_has_prefix (mount_path, "/sys/"))
302
0
    return TRUE;
303
304
0
  if (g_str_has_suffix (mount_path, "/.gvfs"))
305
0
    return TRUE;
306
307
0
  return FALSE;
308
0
}
309
310
/**
311
 * g_unix_is_system_fs_type:
312
 * @fs_type: a file system type, e.g. `procfs` or `tmpfs`
313
 *
314
 * Determines if @fs_type is considered a type of file system which is only
315
 * used in implementation of the OS.
316
 *
317
 * This is primarily used for hiding mounted volumes that are intended as APIs
318
 * for programs to read, and system administrators at a shell; rather than
319
 * something that should, for example, appear in a GUI. For example, the Linux
320
 * `/proc` filesystem.
321
 *
322
 * The list of file system types considered ‘system’ ones may change over time.
323
 *
324
 * Returns: true if @fs_type is considered an implementation detail of the OS;
325
 *    false otherwise
326
 * Since: 2.56
327
 */
328
gboolean
329
g_unix_is_system_fs_type (const char *fs_type)
330
0
{
331
  /* keep sorted for bsearch */
332
0
  const char *ignore_fs[] = {
333
0
    "adfs",
334
0
    "afs",
335
0
    "auto",
336
0
    "autofs",
337
0
    "autofs4",
338
0
    "cgroup",
339
0
    "cgroup2",
340
0
    "configfs",
341
0
    "cxfs",
342
0
    "debugfs",
343
0
    "devfs",
344
0
    "devpts",
345
0
    "devtmpfs",
346
0
    "ecryptfs",
347
0
    "fdescfs",
348
0
    "fuse.gvfsd-fuse",
349
0
    "fuse.portal",
350
0
    "fusectl",
351
0
    "gfs",
352
0
    "gfs2",
353
0
    "gpfs",
354
0
    "hugetlbfs",
355
0
    "kernfs",
356
0
    "linprocfs",
357
0
    "linsysfs",
358
0
    "lustre",
359
0
    "lustre_lite",
360
0
    "mfs",
361
0
    "mqueue",
362
0
    "ncpfs",
363
0
    "nfsd",
364
0
    "nullfs",
365
0
    "ocfs2",
366
0
    "overlay",
367
0
    "proc",
368
0
    "procfs",
369
0
    "pstore",
370
0
    "ptyfs",
371
0
    "rootfs",
372
0
    "rpc_pipefs",
373
0
    "securityfs",
374
0
    "selinuxfs",
375
0
    "sysfs",
376
0
    "tmpfs",
377
0
    "usbfs",
378
0
  };
379
380
0
  g_return_val_if_fail (fs_type != NULL && *fs_type != '\0', FALSE);
381
382
0
  return is_in (fs_type, ignore_fs, G_N_ELEMENTS (ignore_fs));
383
0
}
384
385
/**
386
 * g_unix_is_system_device_path:
387
 * @device_path: a device path, e.g. `/dev/loop0` or `nfsd`
388
 *
389
 * Determines if @device_path is considered a block device path which is only
390
 * used in implementation of the OS.
391
 *
392
 * This is primarily used for hiding mounted volumes that are intended as APIs
393
 * for programs to read, and system administrators at a shell; rather than
394
 * something that should, for example, appear in a GUI. For example, the Linux
395
 * `/proc` filesystem.
396
 *
397
 * The list of device paths considered ‘system’ ones may change over time.
398
 *
399
 * Returns: true if @device_path is considered an implementation detail of
400
 *    the OS; false otherwise
401
 * Since: 2.56
402
 */
403
gboolean
404
g_unix_is_system_device_path (const char *device_path)
405
0
{
406
  /* keep sorted for bsearch */
407
0
  const char *ignore_devices[] = {
408
0
    "/dev/loop",
409
0
    "/dev/vn",
410
0
    "devpts",
411
0
    "nfsd",
412
0
    "none",
413
0
    "sunrpc",
414
0
  };
415
416
0
  g_return_val_if_fail (device_path != NULL && *device_path != '\0', FALSE);
417
418
0
  return is_in (device_path, ignore_devices, G_N_ELEMENTS (ignore_devices));
419
0
}
420
421
static gboolean
422
guess_system_internal (const char *mountpoint,
423
                       const char *fs,
424
                       const char *device,
425
                       const char *root)
426
0
{
427
0
  if (g_unix_is_system_fs_type (fs))
428
0
    return TRUE;
429
  
430
0
  if (g_unix_is_system_device_path (device))
431
0
    return TRUE;
432
433
0
  if (g_unix_is_mount_path_system_internal (mountpoint))
434
0
    return TRUE;
435
436
  /* It is not possible to reliably detect mounts which were created by bind
437
   * operation. mntent-based _g_get_unix_mounts() implementation blindly skips
438
   * mounts with a device path that is repeated (e.g. mounts created by bind
439
   * operation, btrfs subvolumes). This usually chooses the most important
440
   * mounts (i.e. which points to the root of filesystem), but it doesn't work
441
   * in all cases and also it is not ideal that those mounts are completely
442
   * ignored (e.g. x-gvfs-show doesn't work for them, trash backend can't handle
443
   * files on btrfs subvolumes). libmount-based _g_get_unix_mounts()
444
   * implementation provides a root path. So there is no need to completely
445
   * ignore those mounts, because e.g. our volume monitors can use the root path
446
   * to not mengle those mounts with the "regular" mounts (i.e. which points to
447
   * the root). But because those mounts usually just duplicate other mounts and
448
   * are completely ignored with mntend-based implementation, let's mark them as
449
   * system internal. Given the different approaches it doesn't mean that all
450
   * mounts which were ignored will be system internal now, but this should work
451
   * in most cases. For more info, see g_unix_mount_entry_get_root_path()
452
   * annotation, comment in mntent-based _g_get_unix_mounts() implementation and
453
   * the https://gitlab.gnome.org/GNOME/glib/issues/1271 issue.
454
   */
455
0
  if (root != NULL && g_strcmp0 (root, "/") != 0)
456
0
    return TRUE;
457
458
0
  return FALSE;
459
0
}
460
461
/* GUnixMounts (ie: mtab) implementations {{{1 */
462
463
static GUnixMountEntry *
464
create_unix_mount_entry (const char *device_path,
465
                         const char *mount_path,
466
                         const char *root_path,
467
                         const char *filesystem_type,
468
                         const char *options,
469
                         gboolean    is_read_only)
470
0
{
471
0
  GUnixMountEntry *mount_entry = NULL;
472
473
0
  mount_entry = g_new0 (GUnixMountEntry, 1);
474
0
  mount_entry->device_path = g_strdup (device_path);
475
0
  mount_entry->mount_path = g_strdup (mount_path);
476
0
  mount_entry->root_path = g_strdup (root_path);
477
0
  mount_entry->filesystem_type = g_strdup (filesystem_type);
478
0
  mount_entry->options = g_strdup (options);
479
0
  mount_entry->is_read_only = is_read_only;
480
481
0
  mount_entry->is_system_internal =
482
0
    guess_system_internal (mount_entry->mount_path,
483
0
                           mount_entry->filesystem_type,
484
0
                           mount_entry->device_path,
485
0
                           mount_entry->root_path);
486
487
0
  return mount_entry;
488
0
}
489
490
static GUnixMountPoint *
491
create_unix_mount_point (const char *device_path,
492
                         const char *mount_path,
493
                         const char *filesystem_type,
494
                         const char *options,
495
                         gboolean    is_read_only,
496
                         gboolean    is_user_mountable,
497
                         gboolean    is_loopback)
498
0
{
499
0
  GUnixMountPoint *mount_point = NULL;
500
501
0
  mount_point = g_new0 (GUnixMountPoint, 1);
502
0
  mount_point->device_path = g_strdup (device_path);
503
0
  mount_point->mount_path = g_strdup (mount_path);
504
0
  mount_point->filesystem_type = g_strdup (filesystem_type);
505
0
  mount_point->options = g_strdup (options);
506
0
  mount_point->is_read_only = is_read_only;
507
0
  mount_point->is_user_mountable = is_user_mountable;
508
0
  mount_point->is_loopback = is_loopback;
509
510
0
  return mount_point;
511
0
}
512
513
/* mntent.h (Linux, GNU, NSS) {{{2 */
514
#ifdef HAVE_MNTENT_H
515
516
#ifdef HAVE_LIBMOUNT
517
518
/* For documentation on /proc/self/mountinfo see
519
 * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
520
 */
521
#define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
522
523
static GUnixMountEntry **
524
_g_unix_mounts_get_from_file (const char *table_path,
525
                              uint64_t   *time_read_out,
526
                              size_t     *n_entries_out)
527
{
528
  struct libmnt_table *table = NULL;
529
  struct libmnt_iter* iter = NULL;
530
  struct libmnt_fs *fs = NULL;
531
  GUnixMountEntry *mount_entry = NULL;
532
  GPtrArray *return_array = NULL;
533
534
  if (time_read_out != NULL)
535
    *time_read_out = get_mounts_timestamp ();
536
537
  return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_entry_free, TRUE);
538
  table = mnt_new_table ();
539
  if (mnt_table_parse_mtab (table, table_path) < 0)
540
    goto out;
541
542
  iter = mnt_new_iter (MNT_ITER_FORWARD);
543
  while (mnt_table_next_fs (table, iter, &fs) == 0)
544
    {
545
      const char *device_path = NULL;
546
      char *mount_options = NULL;
547
      unsigned long mount_flags = 0;
548
      gboolean is_read_only = FALSE;
549
550
      device_path = mnt_fs_get_source (fs);
551
      if (g_strcmp0 (device_path, "/dev/root") == 0)
552
        device_path = _resolve_dev_root ();
553
554
      mount_options = mnt_fs_strdup_options (fs);
555
      if (mount_options)
556
        {
557
          mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
558
          g_free (mount_options);
559
        }
560
      is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
561
562
      mount_entry = create_unix_mount_entry (device_path,
563
                                             mnt_fs_get_target (fs),
564
                                             mnt_fs_get_root (fs),
565
                                             mnt_fs_get_fstype (fs),
566
                                             mnt_fs_get_options (fs),
567
                                             is_read_only);
568
569
      g_ptr_array_add (return_array, g_steal_pointer (&mount_entry));
570
    }
571
  mnt_free_iter (iter);
572
573
 out:
574
  mnt_free_table (table);
575
576
  if (n_entries_out != NULL)
577
    *n_entries_out = return_array->len;
578
579
  return (GUnixMountEntry **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE);
580
}
581
582
static GList *
583
_g_get_unix_mounts (void)
584
{
585
  GUnixMountEntry **entries = NULL;
586
  size_t n_entries = 0;
587
588
  entries = _g_unix_mounts_get_from_file (NULL  /* default libmount filename */,
589
                                          NULL, &n_entries);
590
591
  return unix_mount_entry_array_free_to_list (g_steal_pointer (&entries), n_entries);
592
}
593
594
#else
595
596
static const char *
597
get_mtab_read_file (void)
598
0
{
599
0
#ifdef _PATH_MOUNTED
600
0
# ifdef __linux__
601
0
  return "/proc/mounts";
602
# else
603
  return _PATH_MOUNTED;
604
# endif
605
#else
606
  return "/etc/mtab";
607
#endif
608
0
}
609
610
#ifndef HAVE_GETMNTENT_R
611
G_LOCK_DEFINE_STATIC(getmntent);
612
#endif
613
614
static GUnixMountEntry **
615
_g_unix_mounts_get_from_file (const char *table_path,
616
                              uint64_t   *time_read_out,
617
                              size_t     *n_entries_out)
618
0
{
619
0
#ifdef HAVE_GETMNTENT_R
620
0
  struct mntent ent;
621
0
  char buf[1024];
622
0
#endif
623
0
  struct mntent *mntent;
624
0
  FILE *file;
625
0
  GUnixMountEntry *mount_entry;
626
0
  GHashTable *mounts_hash;
627
0
  GPtrArray *return_array = NULL;
628
629
0
  if (time_read_out != NULL)
630
0
    *time_read_out = get_mounts_timestamp ();
631
632
0
  file = setmntent (table_path, "re");
633
0
  if (file == NULL)
634
0
    return NULL;
635
636
0
  return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_entry_free, TRUE);
637
0
  mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
638
  
639
0
#ifdef HAVE_GETMNTENT_R
640
0
  while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
641
#else
642
  G_LOCK (getmntent);
643
  while ((mntent = getmntent (file)) != NULL)
644
#endif
645
0
    {
646
0
      const char *device_path = NULL;
647
0
      gboolean is_read_only = FALSE;
648
649
      /* ignore any mnt_fsname that is repeated and begins with a '/'
650
       *
651
       * We do this to avoid being fooled by --bind mounts, since
652
       * these have the same device as the location they bind to.
653
       * It's not an ideal solution to the problem, but it's likely that
654
       * the most important mountpoint is first and the --bind ones after
655
       * that aren't as important. So it should work.
656
       *
657
       * The '/' is to handle procfs, tmpfs and other no device mounts.
658
       */
659
0
      if (mntent->mnt_fsname != NULL &&
660
0
    mntent->mnt_fsname[0] == '/' &&
661
0
    g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
662
0
        continue;
663
664
0
      if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
665
0
        device_path = _resolve_dev_root ();
666
0
      else
667
0
        device_path = mntent->mnt_fsname;
668
669
0
#if defined (HAVE_HASMNTOPT)
670
0
      if (hasmntopt (mntent, MNTOPT_RO) != NULL)
671
0
  is_read_only = TRUE;
672
0
#endif
673
674
0
      mount_entry = create_unix_mount_entry (device_path,
675
0
                                             mntent->mnt_dir,
676
0
                                             NULL,
677
0
                                             mntent->mnt_type,
678
0
                                             mntent->mnt_opts,
679
0
                                             is_read_only);
680
681
0
      g_hash_table_insert (mounts_hash,
682
0
         mount_entry->device_path,
683
0
         mount_entry->device_path);
684
685
0
      g_ptr_array_add (return_array, g_steal_pointer (&mount_entry));
686
0
    }
687
0
  g_hash_table_destroy (mounts_hash);
688
  
689
0
  endmntent (file);
690
691
#ifndef HAVE_GETMNTENT_R
692
  G_UNLOCK (getmntent);
693
#endif
694
  
695
0
  if (n_entries_out != NULL)
696
0
    *n_entries_out = return_array->len;
697
698
0
  return (GUnixMountEntry **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE);
699
0
}
700
701
static GList *
702
_g_get_unix_mounts (void)
703
0
{
704
0
  GUnixMountEntry **entries = NULL;
705
0
  size_t n_entries = 0;
706
707
0
  entries = _g_unix_mounts_get_from_file (get_mtab_read_file (), NULL, &n_entries);
708
709
0
  return unix_mount_entry_array_free_to_list (g_steal_pointer (&entries), n_entries);
710
0
}
711
712
#endif /* HAVE_LIBMOUNT */
713
714
static const char *
715
get_mtab_monitor_file (void)
716
0
{
717
0
  static const char *mountinfo_path = NULL;
718
#ifdef HAVE_LIBMOUNT
719
  struct stat buf;
720
#endif
721
722
0
  if (mountinfo_path != NULL)
723
0
    return mountinfo_path;
724
725
#ifdef HAVE_LIBMOUNT
726
  /* The mtab file is still used by some distros, so it has to be monitored in
727
   * order to avoid races between g_unix_mounts_get and "mounts-changed" signal:
728
   * https://bugzilla.gnome.org/show_bug.cgi?id=782814
729
   */
730
  if (mnt_has_regular_mtab (&mountinfo_path, NULL))
731
    {
732
      return mountinfo_path;
733
    }
734
735
  if (stat (PROC_MOUNTINFO_PATH, &buf) == 0)
736
    {
737
      mountinfo_path = PROC_MOUNTINFO_PATH;
738
      return mountinfo_path;
739
    }
740
#endif
741
742
0
#ifdef _PATH_MOUNTED
743
0
# ifdef __linux__
744
0
  mountinfo_path = "/proc/mounts";
745
# else
746
  mountinfo_path = _PATH_MOUNTED;
747
# endif
748
#else
749
  mountinfo_path = "/etc/mtab";
750
#endif
751
752
0
  return mountinfo_path;
753
0
}
754
755
/* mnttab.h {{{2 */
756
#elif defined (HAVE_SYS_MNTTAB_H)
757
758
G_LOCK_DEFINE_STATIC(getmntent);
759
760
static const char *
761
get_mtab_read_file (void)
762
{
763
#ifdef _PATH_MOUNTED
764
  return _PATH_MOUNTED;
765
#else 
766
  return "/etc/mnttab";
767
#endif
768
}
769
770
static const char *
771
get_mtab_monitor_file (void)
772
{
773
  return get_mtab_read_file ();
774
}
775
776
static GUnixMountEntry **
777
_g_unix_mounts_get_from_file (const char *table_path,
778
                              uint64_t   *time_read_out,
779
                              size_t     *n_entries_out)
780
{
781
  struct mnttab mntent;
782
  FILE *file;
783
  GUnixMountEntry *mount_entry;
784
  GPtrArray *return_array = NULL;
785
786
  if (time_read_out != NULL)
787
    *time_read_out = get_mounts_timestamp ();
788
789
  file = setmntent (table_path, "re");
790
  if (file == NULL)
791
    return NULL;
792
793
  return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_entry_free, TRUE);
794
795
  G_LOCK (getmntent);
796
  while (! getmntent (file, &mntent))
797
    {
798
      gboolean is_read_only = FALSE;
799
800
#if defined (HAVE_HASMNTOPT)
801
      if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
802
  is_read_only = TRUE;
803
#endif
804
805
      mount_entry = create_unix_mount_entry (mntent.mnt_special,
806
                                             mntent.mnt_mountp,
807
                                             NULL,
808
                                             mntent.mnt_fstype,
809
                                             mntent.mnt_opts,
810
                                             is_read_only);
811
812
      g_ptr_array_add (return_array, g_steal_pointer (&mount_entry));
813
    }
814
  
815
  endmntent (file);
816
  
817
  G_UNLOCK (getmntent);
818
819
  if (n_entries_out != NULL)
820
    *n_entries_out = return_array->len;
821
822
  return (GUnixMountEntry **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE);
823
}
824
825
static GList *
826
_g_get_unix_mounts (void)
827
{
828
  GUnixMountEntry **entries = NULL;
829
  size_t n_entries = 0;
830
831
  entries = _g_unix_mounts_get_from_file (get_mtab_read_file (), NULL, &n_entries);
832
833
  return unix_mount_entry_array_free_to_list (g_steal_pointer (&entries), n_entries);
834
}
835
836
/* mntctl.h (AIX) {{{2 */
837
#elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
838
839
static const char *
840
get_mtab_monitor_file (void)
841
{
842
  return NULL;
843
}
844
845
static GList *
846
_g_get_unix_mounts (void)
847
{
848
  struct vfs_ent *fs_info;
849
  struct vmount *vmount_info;
850
  int vmount_number;
851
  unsigned int vmount_size;
852
  int current;
853
  GList *return_list;
854
  
855
  if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
856
    {
857
      g_warning ("Unable to know the number of mounted volumes");
858
      
859
      return NULL;
860
    }
861
862
  vmount_info = (struct vmount*)g_malloc (vmount_size);
863
864
  vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
865
  
866
  if (vmount_info->vmt_revision != VMT_REVISION)
867
    g_warning ("Bad vmount structure revision number, want %d, got %d", VMT_REVISION, vmount_info->vmt_revision);
868
869
  if (vmount_number < 0)
870
    {
871
      g_warning ("Unable to recover mounted volumes information");
872
      
873
      g_free (vmount_info);
874
      return NULL;
875
    }
876
  
877
  return_list = NULL;
878
  while (vmount_number > 0)
879
    {
880
      gboolean is_read_only = FALSE;
881
882
      fs_info = getvfsbytype (vmount_info->vmt_gfstype);
883
884
      /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
885
      is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
886
887
      mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
888
                                             vmt2dataptr (vmount_info, VMT_STUB),
889
                                             NULL,
890
                                             fs_info == NULL ? "unknown" : fs_info->vfsent_name,
891
                                             NULL,
892
                                             is_read_only);
893
894
      return_list = g_list_prepend (return_list, mount_entry);
895
      
896
      vmount_info = (struct vmount *)( (char*)vmount_info 
897
               + vmount_info->vmt_length);
898
      vmount_number--;
899
    }
900
  
901
  g_free (vmount_info);
902
  
903
  return g_list_reverse (return_list);
904
}
905
906
static GUnixMountEntry **
907
_g_unix_mounts_get_from_file (const char *table_path,
908
                              uint64_t   *time_read_out,
909
                              size_t     *n_entries_out)
910
{
911
  /* Not supported on mntctl() systems. */
912
  if (time_read_out != NULL)
913
    *time_read_out = 0;
914
  if (n_entries_out != NULL)
915
    *n_entries_out = 0;
916
917
  return NULL;
918
}
919
920
/* sys/mount.h {{{2 */
921
#elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
922
923
static const char *
924
get_mtab_monitor_file (void)
925
{
926
  return NULL;
927
}
928
929
static GList *
930
_g_get_unix_mounts (void)
931
{
932
#if defined(USE_STATVFS)
933
  struct statvfs *mntent = NULL;
934
#elif defined(USE_STATFS)
935
  struct statfs *mntent = NULL;
936
#else
937
  #error statfs juggling failed
938
#endif
939
  size_t bufsize;
940
  int num_mounts, i;
941
  GUnixMountEntry *mount_entry;
942
  GList *return_list;
943
  
944
  /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
945
#if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
946
  num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
947
#elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
948
  num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
949
#endif
950
  if (num_mounts == -1)
951
    return NULL;
952
953
  bufsize = num_mounts * sizeof (*mntent);
954
  mntent = g_malloc (bufsize);
955
#if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
956
  num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
957
#elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
958
  num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
959
#endif
960
  if (num_mounts == -1)
961
    return NULL;
962
  
963
  return_list = NULL;
964
  
965
  for (i = 0; i < num_mounts; i++)
966
    {
967
      gboolean is_read_only = FALSE;
968
969
#if defined(USE_STATVFS)
970
      if (mntent[i].f_flag & ST_RDONLY)
971
#elif defined(USE_STATFS)
972
      if (mntent[i].f_flags & MNT_RDONLY)
973
#else
974
      #error statfs juggling failed
975
#endif
976
        is_read_only = TRUE;
977
978
      mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
979
                                             mntent[i].f_mntonname,
980
                                             NULL,
981
                                             mntent[i].f_fstypename,
982
                                             NULL,
983
                                             is_read_only);
984
985
      return_list = g_list_prepend (return_list, mount_entry);
986
    }
987
988
  g_free (mntent);
989
  
990
  return g_list_reverse (return_list);
991
}
992
993
static GUnixMountEntry **
994
_g_unix_mounts_get_from_file (const char *table_path,
995
                              uint64_t   *time_read_out,
996
                              size_t     *n_entries_out)
997
{
998
  /* Not supported on getvfsstat()/getfsstat() systems. */
999
  if (time_read_out != NULL)
1000
    *time_read_out = 0;
1001
  if (n_entries_out != NULL)
1002
    *n_entries_out = 0;
1003
1004
  return NULL;
1005
}
1006
1007
/* Interix {{{2 */
1008
#elif defined(__INTERIX)
1009
1010
static const char *
1011
get_mtab_monitor_file (void)
1012
{
1013
  return NULL;
1014
}
1015
1016
static GList *
1017
_g_get_unix_mounts (void)
1018
{
1019
  DIR *dirp;
1020
  GList* return_list = NULL;
1021
  char filename[9 + NAME_MAX];
1022
1023
  dirp = opendir ("/dev/fs");
1024
  if (!dirp)
1025
    {
1026
      g_warning ("unable to read /dev/fs!");
1027
      return NULL;
1028
    }
1029
1030
  while (1)
1031
    {
1032
      struct statvfs statbuf;
1033
      struct dirent entry;
1034
      struct dirent* result;
1035
      
1036
      if (readdir_r (dirp, &entry, &result) || result == NULL)
1037
        break;
1038
      
1039
      strcpy (filename, "/dev/fs/");
1040
      strcat (filename, entry.d_name);
1041
      
1042
      if (statvfs (filename, &statbuf) == 0)
1043
        {
1044
          GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
1045
          
1046
          mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
1047
          mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
1048
          mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
1049
          
1050
          if (statbuf.f_flag & ST_RDONLY)
1051
            mount_entry->is_read_only = TRUE;
1052
          
1053
          return_list = g_list_prepend(return_list, mount_entry);
1054
        }
1055
    }
1056
  
1057
  return_list = g_list_reverse (return_list);
1058
  
1059
  closedir (dirp);
1060
1061
  return return_list;
1062
}
1063
1064
static GUnixMountEntry **
1065
_g_unix_mounts_get_from_file (const char *table_path,
1066
                              uint64_t   *time_read_out,
1067
                              size_t     *n_entries_out)
1068
{
1069
  /* Not supported on Interix systems. */
1070
  if (time_read_out != NULL)
1071
    *time_read_out = 0;
1072
  if (n_entries_out != NULL)
1073
    *n_entries_out = 0;
1074
1075
  return NULL;
1076
}
1077
1078
/* QNX {{{2 */
1079
#elif defined (HAVE_QNX)
1080
1081
static char *
1082
get_mtab_monitor_file (void)
1083
{
1084
  /* TODO: Not implemented */
1085
  return NULL;
1086
}
1087
1088
static GUnixMountEntry **
1089
_g_unix_mounts_get_from_file (const char *table_path,
1090
                              uint64_t   *time_read_out,
1091
                              size_t     *n_entries_out)
1092
{
1093
  /* Not implemented, as per _g_get_unix_mounts() below */
1094
  if (time_read_out != NULL)
1095
    *time_read_out = 0;
1096
  if (n_entries_out != NULL)
1097
    *n_entries_out = 0;
1098
1099
  return NULL;
1100
}
1101
1102
static GList *
1103
_g_get_unix_mounts (void)
1104
{
1105
  /* TODO: Not implemented */
1106
  return NULL;
1107
}
1108
1109
/* Common code {{{2 */
1110
#else
1111
#error No _g_get_unix_mounts() implementation for system
1112
#endif
1113
1114
/* GUnixMountPoints (ie: fstab) implementations {{{1 */
1115
1116
/* _g_get_unix_mount_points():
1117
 * read the fstab.
1118
 * don't return swap and ignore mounts.
1119
 */
1120
1121
static char *
1122
get_fstab_file (void)
1123
0
{
1124
#ifdef HAVE_LIBMOUNT
1125
  return (char *) mnt_get_fstab_path ();
1126
#else
1127
#if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1128
  /* AIX */
1129
  return "/etc/filesystems";
1130
#elif defined(_PATH_MNTTAB)
1131
0
  return _PATH_MNTTAB;
1132
#elif defined(VFSTAB)
1133
  return VFSTAB;
1134
#else
1135
  return "/etc/fstab";
1136
#endif
1137
0
#endif
1138
0
}
1139
1140
/* mntent.h (Linux, GNU, NSS) {{{2 */
1141
#ifdef HAVE_MNTENT_H
1142
1143
#ifdef HAVE_LIBMOUNT
1144
1145
static GUnixMountPoint **
1146
_g_unix_mount_points_get_from_file (const char *table_path,
1147
                                    uint64_t   *time_read_out,
1148
                                    size_t     *n_points_out)
1149
{
1150
  struct libmnt_table *table = NULL;
1151
  struct libmnt_iter* iter = NULL;
1152
  struct libmnt_fs *fs = NULL;
1153
  GUnixMountPoint *mount_point = NULL;
1154
  GPtrArray *return_array = NULL;
1155
1156
  if (time_read_out != NULL)
1157
    *time_read_out = get_mount_points_timestamp ();
1158
1159
  return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_point_free, TRUE);
1160
  table = mnt_new_table ();
1161
  if (mnt_table_parse_fstab (table, table_path) < 0)
1162
    goto out;
1163
1164
  iter = mnt_new_iter (MNT_ITER_FORWARD);
1165
  while (mnt_table_next_fs (table, iter, &fs) == 0)
1166
    {
1167
      const char *device_path = NULL;
1168
      const char *mount_path = NULL;
1169
      const char *mount_fstype = NULL;
1170
      char *mount_options = NULL;
1171
      gboolean is_read_only = FALSE;
1172
      gboolean is_user_mountable = FALSE;
1173
      gboolean is_loopback = FALSE;
1174
1175
      mount_path = mnt_fs_get_target (fs);
1176
      if ((strcmp (mount_path, "ignore") == 0) ||
1177
          (strcmp (mount_path, "swap") == 0) ||
1178
          (strcmp (mount_path, "none") == 0))
1179
        continue;
1180
1181
      mount_fstype = mnt_fs_get_fstype (fs);
1182
      mount_options = mnt_fs_strdup_options (fs);
1183
      if (mount_options)
1184
        {
1185
          unsigned long mount_flags = 0;
1186
          unsigned long userspace_flags = 0;
1187
1188
          mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
1189
          mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
1190
1191
          /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1192
          if (mount_flags & MS_BIND)
1193
            {
1194
              g_free (mount_options);
1195
              continue;
1196
            }
1197
1198
          is_read_only = (mount_flags & MS_RDONLY) != 0;
1199
          is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
1200
1201
          if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
1202
              ((userspace_flags & MNT_MS_USER) &&
1203
               (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
1204
              (userspace_flags & MNT_MS_USERS) ||
1205
              (userspace_flags & MNT_MS_OWNER))
1206
            {
1207
              is_user_mountable = TRUE;
1208
            }
1209
        }
1210
1211
      device_path = mnt_fs_get_source (fs);
1212
      if (g_strcmp0 (device_path, "/dev/root") == 0)
1213
        device_path = _resolve_dev_root ();
1214
1215
      mount_point = create_unix_mount_point (device_path,
1216
                                             mount_path,
1217
                                             mount_fstype,
1218
                                             mount_options,
1219
                                             is_read_only,
1220
                                             is_user_mountable,
1221
                                             is_loopback);
1222
      if (mount_options)
1223
        g_free (mount_options);
1224
1225
      g_ptr_array_add (return_array, g_steal_pointer (&mount_point));
1226
    }
1227
  mnt_free_iter (iter);
1228
1229
 out:
1230
  mnt_free_table (table);
1231
1232
  if (n_points_out != NULL)
1233
    *n_points_out = return_array->len;
1234
1235
  return (GUnixMountPoint **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE);
1236
}
1237
1238
static GList *
1239
_g_get_unix_mount_points (void)
1240
{
1241
  GUnixMountPoint **points = NULL;
1242
  size_t n_points = 0;
1243
1244
  points = _g_unix_mount_points_get_from_file (NULL  /* default libmount filename */,
1245
                                               NULL, &n_points);
1246
1247
  return unix_mount_point_array_free_to_list (g_steal_pointer (&points), n_points);
1248
}
1249
1250
#else
1251
1252
static GUnixMountPoint **
1253
_g_unix_mount_points_get_from_file (const char *table_path,
1254
                                    uint64_t   *time_read_out,
1255
                                    size_t     *n_points_out)
1256
0
{
1257
0
#ifdef HAVE_GETMNTENT_R
1258
0
  struct mntent ent;
1259
0
  char buf[1024];
1260
0
#endif
1261
0
  struct mntent *mntent;
1262
0
  FILE *file;
1263
0
  GUnixMountPoint *mount_point;
1264
0
  GPtrArray *return_array = NULL;
1265
1266
0
  if (time_read_out != NULL)
1267
0
    *time_read_out = get_mount_points_timestamp ();
1268
1269
0
  file = setmntent (table_path, "re");
1270
0
  if (file == NULL)
1271
0
    {
1272
0
      if (n_points_out != NULL)
1273
0
        *n_points_out = 0;
1274
0
      return NULL;
1275
0
    }
1276
1277
0
  return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_point_free, TRUE);
1278
1279
0
#ifdef HAVE_GETMNTENT_R
1280
0
  while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
1281
#else
1282
  G_LOCK (getmntent);
1283
  while ((mntent = getmntent (file)) != NULL)
1284
#endif
1285
0
    {
1286
0
      const char *device_path = NULL;
1287
0
      gboolean is_read_only = FALSE;
1288
0
      gboolean is_user_mountable = FALSE;
1289
0
      gboolean is_loopback = FALSE;
1290
1291
0
      if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
1292
0
          (strcmp (mntent->mnt_dir, "swap") == 0) ||
1293
0
          (strcmp (mntent->mnt_dir, "none") == 0))
1294
0
  continue;
1295
1296
0
#ifdef HAVE_HASMNTOPT
1297
      /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1298
0
      if (hasmntopt (mntent, "bind"))
1299
0
        continue;
1300
0
#endif
1301
1302
0
      if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1303
0
        device_path = _resolve_dev_root ();
1304
0
      else
1305
0
        device_path = mntent->mnt_fsname;
1306
1307
0
#ifdef HAVE_HASMNTOPT
1308
0
      if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1309
0
  is_read_only = TRUE;
1310
1311
0
      if (hasmntopt (mntent, "loop") != NULL)
1312
0
  is_loopback = TRUE;
1313
1314
0
#endif
1315
1316
0
      if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1317
0
#ifdef HAVE_HASMNTOPT
1318
0
    || (hasmntopt (mntent, "user") != NULL
1319
0
        && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1320
0
    || hasmntopt (mntent, "users") != NULL
1321
0
    || hasmntopt (mntent, "owner") != NULL
1322
0
#endif
1323
0
    )
1324
0
  is_user_mountable = TRUE;
1325
1326
0
      mount_point = create_unix_mount_point (device_path,
1327
0
                                             mntent->mnt_dir,
1328
0
                                             mntent->mnt_type,
1329
0
                                             mntent->mnt_opts,
1330
0
                                             is_read_only,
1331
0
                                             is_user_mountable,
1332
0
                                             is_loopback);
1333
1334
0
      g_ptr_array_add (return_array, g_steal_pointer (&mount_point));
1335
0
    }
1336
  
1337
0
  endmntent (file);
1338
1339
#ifndef HAVE_GETMNTENT_R
1340
  G_UNLOCK (getmntent);
1341
#endif
1342
1343
0
  if (n_points_out != NULL)
1344
0
    *n_points_out = return_array->len;
1345
1346
0
  return (GUnixMountPoint **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE);
1347
0
}
1348
1349
static GList *
1350
_g_get_unix_mount_points (void)
1351
0
{
1352
0
  GUnixMountPoint **points = NULL;
1353
0
  size_t n_points = 0;
1354
1355
0
  points = _g_unix_mount_points_get_from_file (get_fstab_file (),
1356
0
                                               NULL, &n_points);
1357
1358
0
  return unix_mount_point_array_free_to_list (g_steal_pointer (&points), n_points);
1359
0
}
1360
1361
#endif /* HAVE_LIBMOUNT */
1362
1363
/* mnttab.h {{{2 */
1364
#elif defined (HAVE_SYS_MNTTAB_H)
1365
1366
static GUnixMountPoint **
1367
_g_unix_mount_points_get_from_file (const char *table_path,
1368
                                    uint64_t   *time_read_out,
1369
                                    size_t     *n_points_out)
1370
{
1371
  struct mnttab mntent;
1372
  FILE *file;
1373
  GUnixMountPoint *mount_point;
1374
  GPtrArray *return_array = NULL;
1375
1376
  if (time_read_out != NULL)
1377
    *time_read_out = get_mount_points_timestamp ();
1378
1379
  file = setmntent (table_path, "re");
1380
  if (file == NULL)
1381
    {
1382
      if (n_points_out != NULL)
1383
        *n_points_out = 0;
1384
      return NULL;
1385
    }
1386
1387
  return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_point_free, TRUE);
1388
  
1389
  G_LOCK (getmntent);
1390
  while (! getmntent (file, &mntent))
1391
    {
1392
      gboolean is_read_only = FALSE;
1393
      gboolean is_user_mountable = FALSE;
1394
      gboolean is_loopback = FALSE;
1395
1396
      if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1397
          (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1398
          (strcmp (mntent.mnt_mountp, "none") == 0))
1399
  continue;
1400
1401
#ifdef HAVE_HASMNTOPT
1402
      if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1403
  is_read_only = TRUE;
1404
1405
      if (hasmntopt (&mntent, "lofs") != NULL)
1406
  is_loopback = TRUE;
1407
#endif
1408
1409
      if ((mntent.mnt_fstype != NULL)
1410
#ifdef HAVE_HASMNTOPT
1411
    || (hasmntopt (&mntent, "user") != NULL
1412
        && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1413
    || hasmntopt (&mntent, "users") != NULL
1414
    || hasmntopt (&mntent, "owner") != NULL
1415
#endif
1416
    )
1417
  is_user_mountable = TRUE;
1418
1419
      mount_point = create_unix_mount_point (mntent.mnt_special,
1420
                                             mntent.mnt_mountp,
1421
                                             mntent.mnt_fstype,
1422
                                             mntent.mnt_mntopts,
1423
                                             is_read_only,
1424
                                             is_user_mountable,
1425
                                             is_loopback);
1426
1427
      g_ptr_array_add (return_array, g_steal_pointer (&mount_point));
1428
    }
1429
  
1430
  endmntent (file);
1431
  G_UNLOCK (getmntent);
1432
1433
  if (n_points_out != NULL)
1434
    *n_points_out = return_array->len;
1435
1436
  return (GUnixMountPoint **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE);
1437
}
1438
1439
static GList *
1440
_g_get_unix_mount_points (void)
1441
{
1442
  GUnixMountPoint **points = NULL;
1443
  size_t n_points = 0;
1444
1445
  points = _g_unix_mount_points_get_from_file (get_fstab_file (),
1446
                                               NULL, &n_points);
1447
1448
  return unix_mount_point_array_free_to_list (g_steal_pointer (&points), n_points);
1449
}
1450
1451
/* mntctl.h (AIX) {{{2 */
1452
#elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1453
1454
/* functions to parse /etc/filesystems on aix */
1455
1456
/* read character, ignoring comments (begin with '*', end with '\n' */
1457
static int
1458
aix_fs_getc (FILE *fd)
1459
{
1460
  int c;
1461
  
1462
  while ((c = getc (fd)) == '*')
1463
    {
1464
      while (((c = getc (fd)) != '\n') && (c != EOF))
1465
  ;
1466
    }
1467
}
1468
1469
/* eat all continuous spaces in a file */
1470
static int
1471
aix_fs_ignorespace (FILE *fd)
1472
{
1473
  int c;
1474
  
1475
  while ((c = aix_fs_getc (fd)) != EOF)
1476
    {
1477
      if (!g_ascii_isspace (c))
1478
  {
1479
    ungetc (c,fd);
1480
    return c;
1481
  }
1482
    }
1483
  
1484
  return EOF;
1485
}
1486
1487
/* read one word from file */
1488
static int
1489
aix_fs_getword (FILE *fd, 
1490
                char *word)
1491
{
1492
  int c;
1493
  
1494
  aix_fs_ignorespace (fd);
1495
1496
  while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1497
    {
1498
      if (c == '"')
1499
  {
1500
    while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1501
      *word++ = c;
1502
    else
1503
      *word++ = c;
1504
  }
1505
    }
1506
  *word = 0;
1507
  
1508
  return c;
1509
}
1510
1511
typedef struct {
1512
  char mnt_mount[PATH_MAX];
1513
  char mnt_special[PATH_MAX];
1514
  char mnt_fstype[16];
1515
  char mnt_options[128];
1516
} AixMountTableEntry;
1517
1518
/* read mount points properties */
1519
static int
1520
aix_fs_get (FILE               *fd, 
1521
            AixMountTableEntry *prop)
1522
{
1523
  static char word[PATH_MAX] = { 0 };
1524
  char value[PATH_MAX];
1525
  
1526
  /* read stanza */
1527
  if (word[0] == 0)
1528
    {
1529
      if (aix_fs_getword (fd, word) == EOF)
1530
  return EOF;
1531
    }
1532
1533
  word[strlen(word) - 1] = 0;
1534
  g_strlcpy (prop->mnt_mount, word, sizeof (prop->mnt_mount));
1535
  
1536
  /* read attributes and value */
1537
  
1538
  while (aix_fs_getword (fd, word) != EOF)
1539
    {
1540
      /* test if is attribute or new stanza */
1541
      if (word[strlen(word) - 1] == ':')
1542
  return 0;
1543
      
1544
      /* read "=" */
1545
      aix_fs_getword (fd, value);
1546
      
1547
      /* read value */
1548
      aix_fs_getword (fd, value);
1549
      
1550
      if (strcmp (word, "dev") == 0)
1551
  g_strlcpy (prop->mnt_special, value, sizeof (prop->mnt_special));
1552
      else if (strcmp (word, "vfs") == 0)
1553
  g_strlcpy (prop->mnt_fstype, value, sizeof (prop->mnt_fstype));
1554
      else if (strcmp (word, "options") == 0)
1555
  g_strlcpy (prop->mnt_options, value, sizeof (prop->mnt_options));
1556
    }
1557
  
1558
  return 0;
1559
}
1560
1561
static GUnixMountPoint **
1562
_g_unix_mount_points_get_from_file (const char *table_path,
1563
                                    uint64_t   *time_read_out,
1564
                                    size_t     *n_points_out)
1565
{
1566
  struct mntent *mntent;
1567
  FILE *file;
1568
  GUnixMountPoint *mount_point;
1569
  AixMountTableEntry mntent;
1570
  GPtrArray *return_array = NULL;
1571
1572
  if (time_read_out != NULL)
1573
    *time_read_out = get_mount_points_timestamp ();
1574
1575
  file = setmntent (table_path, "re");
1576
  if (file == NULL)
1577
    {
1578
      if (n_points_out != NULL)
1579
        *n_points_out = 0;
1580
      return NULL;
1581
    }
1582
1583
  return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_point_free, TRUE);
1584
1585
  while (!aix_fs_get (file, &mntent))
1586
    {
1587
      if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1588
  {
1589
          mount_point = create_unix_mount_point (mntent.mnt_special,
1590
                                                 mntent.mnt_mount,
1591
                                                 mntent.mnt_fstype,
1592
                                                 mntent.mnt_options,
1593
                                                 TRUE,
1594
                                                 TRUE,
1595
                                                 FALSE);
1596
1597
          g_ptr_array_add (return_array, g_steal_pointer (&mount_point));
1598
  }
1599
    }
1600
  
1601
  endmntent (file);
1602
1603
  if (n_points_out != NULL)
1604
    *n_points_out = return_array->len;
1605
1606
  return (GUnixMountPoint **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE);
1607
}
1608
1609
static GList *
1610
_g_get_unix_mount_points (void)
1611
{
1612
  GUnixMountPoint **points = NULL;
1613
  size_t n_points = 0;
1614
1615
  points = _g_unix_mount_points_get_from_file (get_fstab_file (),
1616
                                               NULL, &n_points);
1617
1618
  return unix_mount_point_array_free_to_list (g_steal_pointer (&points), n_points);
1619
}
1620
1621
#elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT) || defined(HAVE_GETFSENT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1622
1623
static GList *
1624
_g_get_unix_mount_points (void)
1625
{
1626
  struct fstab *fstab = NULL;
1627
  GUnixMountPoint *mount_point;
1628
  GList *return_list = NULL;
1629
  G_LOCK_DEFINE_STATIC (fsent);
1630
#ifdef HAVE_SYS_SYSCTL_H
1631
  uid_t uid = getuid ();
1632
  int usermnt = 0;
1633
  struct stat sb;
1634
#endif
1635
1636
#ifdef HAVE_SYS_SYSCTL_H
1637
#if defined(HAVE_SYSCTLBYNAME)
1638
  {
1639
    size_t len = sizeof(usermnt);
1640
1641
    sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1642
  }
1643
#elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1644
  {
1645
    int mib[2];
1646
    size_t len = sizeof(usermnt);
1647
    
1648
    mib[0] = CTL_VFS;
1649
    mib[1] = VFS_USERMOUNT;
1650
    sysctl (mib, 2, &usermnt, &len, NULL, 0);
1651
  }
1652
#elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1653
  {
1654
    int mib[2];
1655
    size_t len = sizeof(usermnt);
1656
    
1657
    mib[0] = CTL_KERN;
1658
    mib[1] = KERN_USERMOUNT;
1659
    sysctl (mib, 2, &usermnt, &len, NULL, 0);
1660
  }
1661
#endif
1662
#endif
1663
1664
  G_LOCK (fsent);
1665
  if (!setfsent ())
1666
    {
1667
      G_UNLOCK (fsent);
1668
      return NULL;
1669
    }
1670
1671
  while ((fstab = getfsent ()) != NULL)
1672
    {
1673
      gboolean is_read_only = FALSE;
1674
      gboolean is_user_mountable = FALSE;
1675
1676
      if (strcmp (fstab->fs_vfstype, "swap") == 0)
1677
  continue;
1678
1679
      if (strcmp (fstab->fs_type, "ro") == 0)
1680
  is_read_only = TRUE;
1681
1682
#ifdef HAVE_SYS_SYSCTL_H
1683
      if (usermnt != 0)
1684
        {
1685
          if (uid == 0 ||
1686
              (stat (fstab->fs_file, &sb) == 0 && sb.st_uid == uid))
1687
            {
1688
              is_user_mountable = TRUE;
1689
            }
1690
        }
1691
#endif
1692
1693
      mount_point = create_unix_mount_point (fstab->fs_spec,
1694
                                             fstab->fs_file,
1695
                                             fstab->fs_vfstype,
1696
                                             fstab->fs_mntops,
1697
                                             is_read_only,
1698
                                             is_user_mountable,
1699
                                             FALSE);
1700
1701
      return_list = g_list_prepend (return_list, mount_point);
1702
    }
1703
1704
  endfsent ();
1705
  G_UNLOCK (fsent);
1706
1707
  return g_list_reverse (return_list);
1708
}
1709
1710
static GUnixMountPoint **
1711
_g_unix_mount_points_get_from_file (const char *table_path,
1712
                                    uint64_t   *time_read_out,
1713
                                    size_t     *n_points_out)
1714
{
1715
  /* Not supported on getfsent() systems. */
1716
  if (time_read_out != NULL)
1717
    *time_read_out = 0;
1718
  if (n_points_out != NULL)
1719
    *n_points_out = 0;
1720
  return NULL;
1721
}
1722
1723
/* Common code {{{2 */
1724
#else
1725
#error No g_get_mount_table() implementation for system
1726
#endif
1727
1728
static guint64
1729
get_mounts_timestamp (void)
1730
0
{
1731
0
  const char *monitor_file;
1732
0
  struct stat buf;
1733
0
  guint64 timestamp = 0;
1734
1735
0
  G_LOCK (proc_mounts_source);
1736
1737
0
  monitor_file = get_mtab_monitor_file ();
1738
  /* Don't return mtime for /proc/ files */
1739
0
  if (monitor_file && !g_str_has_prefix (monitor_file, "/proc/"))
1740
0
    {
1741
0
      if (stat (monitor_file, &buf) == 0)
1742
0
        timestamp = buf.st_mtime;
1743
0
    }
1744
0
  else if (proc_mounts_watch_is_running ())
1745
0
    {
1746
      /* it's being monitored by poll, so return mount_poller_time */
1747
0
      timestamp = mount_poller_time;
1748
0
    }
1749
0
  else
1750
0
    {
1751
      /* Case of /proc/ file not being monitored - Be on the safe side and
1752
       * send a new timestamp to force g_unix_mount_entries_changed_since() to
1753
       * return TRUE so any application caches depending on it (like eg.
1754
       * the one in GIO) get invalidated and don't hold possibly outdated
1755
       * data - see Bug 787731 */
1756
0
     timestamp = g_get_monotonic_time ();
1757
0
    }
1758
1759
0
  G_UNLOCK (proc_mounts_source);
1760
1761
0
  return timestamp;
1762
0
}
1763
1764
static guint64
1765
get_mount_points_timestamp (void)
1766
0
{
1767
0
  const char *monitor_file;
1768
0
  struct stat buf;
1769
1770
0
  monitor_file = get_fstab_file ();
1771
0
  if (monitor_file)
1772
0
    {
1773
0
      if (stat (monitor_file, &buf) == 0)
1774
0
        return (guint64)buf.st_mtime;
1775
0
    }
1776
0
  return 0;
1777
0
}
1778
1779
/**
1780
 * g_unix_mounts_get:
1781
 * @time_read: (out) (optional): return location for a timestamp
1782
 *
1783
 * Gets a list of [struct@GioUnix.MountEntry] instances representing the Unix
1784
 * mounts.
1785
 *
1786
 * If @time_read is set, it will be filled with the mount timestamp, allowing
1787
 * for checking if the mounts have changed with
1788
 * [func@GioUnix.mount_entries_changed_since].
1789
 *
1790
 * Returns: (element-type GUnixMountEntry) (transfer full): a list of the
1791
 *    Unix mounts
1792
 * Deprecated: 2.84: Use [func@GioUnix.mount_entries_get] instead.
1793
 */
1794
GList *
1795
g_unix_mounts_get (guint64 *time_read)
1796
0
{
1797
0
  return g_unix_mount_entries_get (time_read);
1798
0
}
1799
1800
/**
1801
 * g_unix_mount_entries_get:
1802
 * @time_read: (out) (optional): return location for a timestamp
1803
 *
1804
 * Gets a list of [struct@GioUnix.MountEntry] instances representing the Unix
1805
 * mounts.
1806
 *
1807
 * If @time_read is set, it will be filled with the mount timestamp, allowing
1808
 * for checking if the mounts have changed with
1809
 * [func@GioUnix.mount_entries_changed_since].
1810
 *
1811
 * Returns: (element-type GUnixMountEntry) (transfer full): a list of the
1812
 *    Unix mounts
1813
 * Since: 2.84
1814
 */
1815
GList *
1816
g_unix_mount_entries_get (guint64 *time_read)
1817
0
{
1818
0
  if (time_read)
1819
0
    *time_read = get_mounts_timestamp ();
1820
1821
0
  return _g_get_unix_mounts ();
1822
0
}
1823
1824
/**
1825
 * g_unix_mounts_get_from_file:
1826
 * @table_path: path to the mounts table file (for example `/proc/self/mountinfo`)
1827
 * @time_read_out: (optional) (out caller-allocates): return location for the
1828
 *   modification time of @table_path
1829
 * @n_entries_out: (optional) (out caller-allocates): return location for the
1830
 *   number of mount entries returned
1831
 *
1832
 * Gets an array of [struct@GioUnix.MountEntry]s containing the Unix mounts
1833
 * listed in @table_path.
1834
 *
1835
 * This is a generalized version of [func@GioUnix.mount_entries_get], mainly
1836
 * intended for internal testing use. Note that [func@GioUnix.mount_entries_get]
1837
 * may parse multiple hierarchical table files, so this function is not a direct
1838
 * superset of its functionality.
1839
 *
1840
 * If there is an error reading or parsing the file, `NULL` will be returned
1841
 * and both out parameters will be set to `0`.
1842
 *
1843
 * Returns: (transfer full) (array length=n_entries_out) (nullable): mount
1844
 *   entries, or `NULL` if there was an error loading them
1845
 * Since: 2.82
1846
 * Deprecated: 2.84: Use [func@GioUnix.mount_entries_get_from_file] instead.
1847
 */
1848
GUnixMountEntry **
1849
g_unix_mounts_get_from_file (const char *table_path,
1850
                             uint64_t   *time_read_out,
1851
                             size_t     *n_entries_out)
1852
0
{
1853
0
  return g_unix_mount_entries_get_from_file (table_path, time_read_out, n_entries_out);
1854
0
}
1855
1856
/**
1857
 * g_unix_mount_entries_get_from_file:
1858
 * @table_path: path to the mounts table file (for example `/proc/self/mountinfo`)
1859
 * @time_read_out: (optional) (out caller-allocates): return location for the
1860
 *   modification time of @table_path
1861
 * @n_entries_out: (optional) (out caller-allocates): return location for the
1862
 *   number of mount entries returned
1863
 *
1864
 * Gets an array of [struct@GioUnix.MountEntry]s containing the Unix mounts
1865
 * listed in @table_path.
1866
 *
1867
 * This is a generalized version of [func@GioUnix.mount_entries_get], mainly
1868
 * intended for internal testing use. Note that [func@GioUnix.mount_entries_get]
1869
 * may parse multiple hierarchical table files, so this function is not a direct
1870
 * superset of its functionality.
1871
 *
1872
 * If there is an error reading or parsing the file, `NULL` will be returned
1873
 * and both out parameters will be set to `0`.
1874
 *
1875
 * Returns: (transfer full) (array length=n_entries_out) (nullable): mount
1876
 *   entries, or `NULL` if there was an error loading them
1877
 * Since: 2.84
1878
 */
1879
GUnixMountEntry **
1880
g_unix_mount_entries_get_from_file (const char *table_path,
1881
                                    uint64_t   *time_read_out,
1882
                                    size_t     *n_entries_out)
1883
0
{
1884
0
  return _g_unix_mounts_get_from_file (table_path, time_read_out, n_entries_out);
1885
0
}
1886
1887
/**
1888
 * g_unix_mount_at:
1889
 * @mount_path: (type filename): path for a possible Unix mount
1890
 * @time_read: (out) (optional): return location for a timestamp
1891
 * 
1892
 * Gets a [struct@GioUnix.MountEntry] for a given mount path.
1893
 *
1894
 * If @time_read is set, it will be filled with a Unix timestamp for checking
1895
 * if the mounts have changed since with
1896
 * [func@GioUnix.mount_entries_changed_since].
1897
 * 
1898
 * If more mounts have the same mount path, the last matching mount
1899
 * is returned.
1900
 *
1901
 * This will return `NULL` if there is no mount point at @mount_path.
1902
 *
1903
 * Returns: (transfer full) (nullable): a [struct@GioUnix.MountEntry]
1904
 * Deprecated: 2.84: Use [func@GioUnix.MountEntry.at] instead.
1905
 **/
1906
GUnixMountEntry *
1907
g_unix_mount_at (const char *mount_path,
1908
     guint64    *time_read)
1909
0
{
1910
0
  return g_unix_mount_entry_at (mount_path, time_read);
1911
0
}
1912
1913
/**
1914
 * g_unix_mount_entry_at:
1915
 * @mount_path: (type filename): path for a possible Unix mount
1916
 * @time_read: (out) (optional): return location for a timestamp
1917
 *
1918
 * Gets a [struct@GioUnix.MountEntry] for a given mount path.
1919
 *
1920
 * If @time_read is set, it will be filled with a Unix timestamp for checking
1921
 * if the mounts have changed since with
1922
 * [func@GioUnix.mount_entries_changed_since].
1923
 *
1924
 * If more mounts have the same mount path, the last matching mount
1925
 * is returned.
1926
 *
1927
 * This will return `NULL` if there is no mount point at @mount_path.
1928
 *
1929
 * Returns: (transfer full) (nullable): a [struct@GioUnix.MountEntry]
1930
 * Since: 2.84
1931
 **/
1932
GUnixMountEntry *
1933
g_unix_mount_entry_at (const char *mount_path,
1934
           guint64    *time_read)
1935
0
{
1936
0
  GList *mounts, *l;
1937
0
  GUnixMountEntry *mount_entry, *found;
1938
  
1939
0
  mounts = g_unix_mount_entries_get (time_read);
1940
1941
0
  found = NULL;
1942
0
  for (l = mounts; l != NULL; l = l->next)
1943
0
    {
1944
0
      mount_entry = l->data;
1945
1946
0
      if (strcmp (mount_path, mount_entry->mount_path) == 0)
1947
0
        {
1948
0
          if (found != NULL)
1949
0
            g_unix_mount_entry_free (found);
1950
1951
0
          found = mount_entry;
1952
0
        }
1953
0
      else
1954
0
        g_unix_mount_entry_free (mount_entry);
1955
0
    }
1956
0
  g_list_free (mounts);
1957
1958
0
  return found;
1959
0
}
1960
1961
/**
1962
 * g_unix_mount_for:
1963
 * @file_path: (type filename): file path on some Unix mount
1964
 * @time_read: (out) (optional): return location for a timestamp
1965
 *
1966
 * Gets a [struct@GioUnix.MountEntry] for a given file path.
1967
 *
1968
 * If @time_read is set, it will be filled with a Unix timestamp for checking
1969
 * if the mounts have changed since with
1970
 * [func@GioUnix.mount_entries_changed_since].
1971
 *
1972
 * If more mounts have the same mount path, the last matching mount
1973
 * is returned.
1974
 *
1975
 * This will return `NULL` if looking up the mount entry fails, if
1976
 * @file_path doesn’t exist or there is an I/O error.
1977
 *
1978
 * Returns: (transfer full)  (nullable): a [struct@GioUnix.MountEntry]
1979
 * Since: 2.52
1980
 * Deprecated: 2.84: Use [func@GioUnix.MountEntry.for] instead.
1981
 **/
1982
GUnixMountEntry *
1983
g_unix_mount_for (const char *file_path,
1984
                  guint64    *time_read)
1985
0
{
1986
0
  return g_unix_mount_entry_for (file_path, time_read);
1987
0
}
1988
1989
/**
1990
 * g_unix_mount_entry_for:
1991
 * @file_path: (type filename): file path on some Unix mount
1992
 * @time_read: (out) (optional): return location for a timestamp
1993
 *
1994
 * Gets a [struct@GioUnix.MountEntry] for a given file path.
1995
 *
1996
 * If @time_read is set, it will be filled with a Unix timestamp for checking
1997
 * if the mounts have changed since with
1998
 * [func@GioUnix.mount_entries_changed_since].
1999
 *
2000
 * If more mounts have the same mount path, the last matching mount
2001
 * is returned.
2002
 *
2003
 * This will return `NULL` if looking up the mount entry fails, if
2004
 * @file_path doesn’t exist or there is an I/O error.
2005
 *
2006
 * Returns: (transfer full)  (nullable): a [struct@GioUnix.MountEntry]
2007
 * Since: 2.84
2008
 **/
2009
GUnixMountEntry *
2010
g_unix_mount_entry_for (const char *file_path,
2011
                        guint64    *time_read)
2012
0
{
2013
0
  GUnixMountEntry *entry;
2014
2015
0
  g_return_val_if_fail (file_path != NULL, NULL);
2016
2017
0
  entry = g_unix_mount_entry_at (file_path, time_read);
2018
0
  if (entry == NULL)
2019
0
    {
2020
0
      char *topdir;
2021
2022
0
      topdir = _g_local_file_find_topdir_for (file_path);
2023
0
      if (topdir != NULL)
2024
0
        {
2025
0
          entry = g_unix_mount_entry_at (topdir, time_read);
2026
0
          g_free (topdir);
2027
0
        }
2028
0
    }
2029
2030
0
  return entry;
2031
0
}
2032
2033
static gpointer
2034
copy_mount_point_cb (gconstpointer src,
2035
                     gpointer      data)
2036
0
{
2037
0
  GUnixMountPoint *src_mount_point = (GUnixMountPoint *) src;
2038
0
  return g_unix_mount_point_copy (src_mount_point);
2039
0
}
2040
2041
/**
2042
 * g_unix_mount_points_get:
2043
 * @time_read: (out) (optional): return location for a timestamp
2044
 *
2045
 * Gets a list of [struct@GioUnix.MountPoint] instances representing the Unix
2046
 * mount points.
2047
 *
2048
 * If @time_read is set, it will be filled with the mount timestamp, allowing
2049
 * for checking if the mounts have changed with
2050
 * [func@GioUnix.mount_points_changed_since].
2051
 *
2052
 * Returns: (element-type GUnixMountPoint) (transfer full): a list of the Unix
2053
 *    mount points
2054
 **/
2055
GList *
2056
g_unix_mount_points_get (guint64 *time_read)
2057
0
{
2058
0
  static GList *mnt_pts_last = NULL;
2059
0
  static guint64 time_read_last = 0;
2060
0
  GList *mnt_pts = NULL;
2061
0
  guint64 time_read_now;
2062
0
  G_LOCK_DEFINE_STATIC (unix_mount_points);
2063
2064
0
  G_LOCK (unix_mount_points);
2065
2066
0
  time_read_now = get_mount_points_timestamp ();
2067
0
  if (time_read_now != time_read_last || mnt_pts_last == NULL)
2068
0
    {
2069
0
      time_read_last = time_read_now;
2070
0
      g_list_free_full (mnt_pts_last, (GDestroyNotify) g_unix_mount_point_free);
2071
0
      mnt_pts_last = _g_get_unix_mount_points ();
2072
0
    }
2073
0
  mnt_pts = g_list_copy_deep (mnt_pts_last, copy_mount_point_cb, NULL);
2074
2075
0
  G_UNLOCK (unix_mount_points);
2076
2077
0
  if (time_read)
2078
0
    *time_read = time_read_now;
2079
2080
0
  return mnt_pts;
2081
0
}
2082
2083
/**
2084
 * g_unix_mount_points_get_from_file:
2085
 * @table_path: path to the mount points table file (for example `/etc/fstab`)
2086
 * @time_read_out: (optional) (out caller-allocates): return location for the
2087
 *   modification time of @table_path
2088
 * @n_points_out: (optional) (out caller-allocates): return location for the
2089
 *   number of mount points returned
2090
 *
2091
 * Gets an array of [struct@GioUnix.MountPoint]s containing the Unix mount
2092
 * points listed in @table_path.
2093
 *
2094
 * This is a generalized version of [func@GioUnix.mount_points_get], mainly
2095
 * intended for internal testing use. Note that [func@GioUnix.mount_points_get]
2096
 * may parse multiple hierarchical table files, so this function is not a direct
2097
 * superset of its functionality.
2098
 *
2099
 * If there is an error reading or parsing the file, `NULL` will be returned
2100
 * and both out parameters will be set to `0`.
2101
 *
2102
 * Returns: (transfer full) (array length=n_points_out) (nullable): mount
2103
 *   points, or `NULL` if there was an error loading them
2104
 * Since: 2.82
2105
 */
2106
GUnixMountPoint **
2107
g_unix_mount_points_get_from_file (const char *table_path,
2108
                                   uint64_t   *time_read_out,
2109
                                   size_t     *n_points_out)
2110
0
{
2111
0
  return _g_unix_mount_points_get_from_file (table_path, time_read_out, n_points_out);
2112
0
}
2113
2114
/**
2115
 * g_unix_mount_point_at:
2116
 * @mount_path: (type filename): path for a possible Unix mount point
2117
 * @time_read: (out) (optional): return location for a timestamp
2118
 *
2119
 * Gets a [struct@GioUnix.MountPoint] for a given mount path.
2120
 *
2121
 * If @time_read is set, it will be filled with a Unix timestamp for checking if
2122
 * the mount points have changed since with
2123
 * [func@GioUnix.mount_points_changed_since].
2124
 *
2125
 * If more mount points have the same mount path, the last matching mount point
2126
 * is returned.
2127
 *
2128
 * Returns: (transfer full) (nullable): a [struct@GioUnix.MountPoint], or `NULL`
2129
 *    if no match is found
2130
 * Since: 2.66
2131
 **/
2132
GUnixMountPoint *
2133
g_unix_mount_point_at (const char *mount_path,
2134
                       guint64    *time_read)
2135
0
{
2136
0
  GList *mount_points, *l;
2137
0
  GUnixMountPoint *mount_point, *found;
2138
2139
0
  mount_points = g_unix_mount_points_get (time_read);
2140
2141
0
  found = NULL;
2142
0
  for (l = mount_points; l != NULL; l = l->next)
2143
0
    {
2144
0
      mount_point = l->data;
2145
2146
0
      if (strcmp (mount_path, mount_point->mount_path) == 0)
2147
0
        {
2148
0
          if (found != NULL)
2149
0
            g_unix_mount_point_free (found);
2150
2151
0
          found = mount_point;
2152
0
        }
2153
0
      else
2154
0
        g_unix_mount_point_free (mount_point);
2155
0
    }
2156
0
  g_list_free (mount_points);
2157
2158
0
  return found;
2159
0
}
2160
2161
/**
2162
 * g_unix_mounts_changed_since:
2163
 * @time: a timestamp
2164
 * 
2165
 * Checks if the Unix mounts have changed since a given Unix time.
2166
 * 
2167
 * Returns: true if the mounts have changed since @time; false otherwise
2168
 * Deprecated: 2.84: Use [func@GioUnix.mount_entries_changed_since] instead.
2169
 **/
2170
gboolean
2171
g_unix_mounts_changed_since (guint64 time)
2172
0
{
2173
0
  return g_unix_mount_entries_changed_since (time);
2174
0
}
2175
2176
/**
2177
 * g_unix_mount_entries_changed_since:
2178
 * @time: a timestamp
2179
 *
2180
 * Checks if the Unix mounts have changed since a given Unix time.
2181
 *
2182
 * This can only work reliably if a [class@GioUnix.MountMonitor] is running in
2183
 * the process, otherwise changes in the mount entries file (such as
2184
 * `/proc/self/mountinfo` on Linux) cannot be detected and, as a result, this
2185
 * function has to conservatively always return `TRUE`.
2186
 *
2187
 * It is more efficient to use [signal@GioUnix.MountMonitor::mounts-changed] to
2188
 * be signalled of changes to the mount entries, rather than polling using this
2189
 * function. This function is more appropriate for infrequently determining
2190
 * cache validity.
2191
 *
2192
 * Returns: true if the mounts have changed since @time; false otherwise
2193
 * Since 2.84
2194
 **/
2195
gboolean
2196
g_unix_mount_entries_changed_since (guint64 time)
2197
0
{
2198
0
  return get_mounts_timestamp () != time;
2199
0
}
2200
2201
/**
2202
 * g_unix_mount_points_changed_since:
2203
 * @time: a timestamp
2204
 * 
2205
 * Checks if the Unix mount points have changed since a given Unix time.
2206
 * 
2207
 * Unlike [func@GioUnix.mount_entries_changed_since], this function can work
2208
 * reliably without a [class@GioUnix.MountMonitor] running, as it accesses the
2209
 * static mount point information (such as `/etc/fstab` on Linux), which has a
2210
 * valid modification time.
2211
 *
2212
 * It is more efficient to use [signal@GioUnix.MountMonitor::mountpoints-changed]
2213
 * to be signalled of changes to the mount points, rather than polling using
2214
 * this function. This function is more appropriate for infrequently determining
2215
 * cache validity.
2216
 *
2217
 * Returns: true if the mount points have changed since @time; false otherwise
2218
 **/
2219
gboolean
2220
g_unix_mount_points_changed_since (guint64 time)
2221
0
{
2222
0
  return get_mount_points_timestamp () != time;
2223
0
}
2224
2225
/* GUnixMountMonitor {{{1 */
2226
2227
enum {
2228
  MOUNTS_CHANGED,
2229
  MOUNTPOINTS_CHANGED,
2230
  LAST_SIGNAL
2231
};
2232
2233
static guint signals[LAST_SIGNAL];
2234
2235
struct _GUnixMountMonitor {
2236
  GObject parent;
2237
2238
  GMainContext *context;
2239
};
2240
2241
struct _GUnixMountMonitorClass {
2242
  GObjectClass parent_class;
2243
};
2244
2245
2246
0
G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT)
2247
0
2248
0
static GContextSpecificGroup  mount_monitor_group;
2249
0
static GFileMonitor          *fstab_monitor;
2250
0
static GFileMonitor          *mtab_monitor;
2251
0
static GList                 *mount_poller_mounts;
2252
0
static guint                  mtab_file_changed_id;
2253
0
2254
0
/* Called with proc_mounts_source lock held. */
2255
0
static gboolean
2256
0
proc_mounts_watch_is_running (void)
2257
0
{
2258
0
  return proc_mounts_watch_source != NULL &&
2259
0
         !g_source_is_destroyed (proc_mounts_watch_source);
2260
0
}
2261
2262
static void
2263
fstab_file_changed (GFileMonitor      *monitor,
2264
                    GFile             *file,
2265
                    GFile             *other_file,
2266
                    GFileMonitorEvent  event_type,
2267
                    gpointer           user_data)
2268
0
{
2269
0
  if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
2270
0
      event_type != G_FILE_MONITOR_EVENT_CREATED &&
2271
0
      event_type != G_FILE_MONITOR_EVENT_DELETED)
2272
0
    return;
2273
2274
0
  g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
2275
0
}
2276
2277
static gboolean
2278
mtab_file_changed_cb (gpointer user_data)
2279
0
{
2280
0
  mtab_file_changed_id = 0;
2281
0
  g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
2282
2283
0
  return G_SOURCE_REMOVE;
2284
0
}
2285
2286
static void
2287
mtab_file_changed (GFileMonitor      *monitor,
2288
                   GFile             *file,
2289
                   GFile             *other_file,
2290
                   GFileMonitorEvent  event_type,
2291
                   gpointer           user_data)
2292
0
{
2293
0
  GMainContext *context;
2294
0
  GSource *source;
2295
2296
0
  if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
2297
0
      event_type != G_FILE_MONITOR_EVENT_CREATED &&
2298
0
      event_type != G_FILE_MONITOR_EVENT_DELETED)
2299
0
    return;
2300
2301
  /* Skip accumulated events from file monitor which we are not able to handle
2302
   * in a real time instead of emitting mounts_changed signal several times.
2303
   * This should behave equally to GIOChannel based monitoring. See Bug 792235.
2304
   */
2305
0
  if (mtab_file_changed_id > 0)
2306
0
    return;
2307
2308
0
  context = g_main_context_get_thread_default ();
2309
0
  if (!context)
2310
0
    context = g_main_context_default ();
2311
2312
0
  source = g_idle_source_new ();
2313
0
  g_source_set_priority (source, G_PRIORITY_DEFAULT);
2314
0
  g_source_set_callback (source, mtab_file_changed_cb, NULL, NULL);
2315
0
  g_source_set_static_name (source, "[gio] mtab_file_changed_cb");
2316
0
  g_source_attach (source, context);
2317
0
  g_source_unref (source);
2318
0
}
2319
2320
static gboolean
2321
proc_mounts_changed (GIOChannel   *channel,
2322
                     GIOCondition  cond,
2323
                     gpointer      user_data)
2324
0
{
2325
0
  gboolean has_changed = FALSE;
2326
2327
#ifdef HAVE_LIBMOUNT
2328
  if (cond & G_IO_IN)
2329
    {
2330
      G_LOCK (proc_mounts_source);
2331
      if (proc_mounts_monitor != NULL)
2332
        {
2333
          int ret;
2334
2335
          /* The mnt_monitor_next_change function needs to be used to avoid false-positives. */
2336
          ret = mnt_monitor_next_change (proc_mounts_monitor, NULL, NULL);
2337
          if (ret == 0)
2338
            {
2339
              has_changed = TRUE;
2340
              ret = mnt_monitor_event_cleanup (proc_mounts_monitor);
2341
            }
2342
2343
          if (ret < 0)
2344
            g_debug ("mnt_monitor_next_change failed: %s", g_strerror (-ret));
2345
        }
2346
      G_UNLOCK (proc_mounts_source);
2347
    }
2348
#endif
2349
2350
0
  if (cond & G_IO_ERR)
2351
0
    has_changed = TRUE;
2352
2353
0
  if (has_changed)
2354
0
    {
2355
0
      G_LOCK (proc_mounts_source);
2356
0
      mount_poller_time = (guint64) g_get_monotonic_time ();
2357
0
      G_UNLOCK (proc_mounts_source);
2358
2359
0
      g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
2360
0
    }
2361
2362
0
  return TRUE;
2363
0
}
2364
2365
static gboolean
2366
mount_change_poller (gpointer user_data)
2367
0
{
2368
0
  GList *current_mounts, *new_it, *old_it;
2369
0
  gboolean has_changed = FALSE;
2370
2371
0
  current_mounts = _g_get_unix_mounts ();
2372
2373
0
  for ( new_it = current_mounts, old_it = mount_poller_mounts;
2374
0
        new_it != NULL && old_it != NULL;
2375
0
        new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
2376
0
    {
2377
0
      if (g_unix_mount_entry_compare (new_it->data, old_it->data) != 0)
2378
0
        {
2379
0
          has_changed = TRUE;
2380
0
          break;
2381
0
        }
2382
0
    }
2383
0
  if (!(new_it == NULL && old_it == NULL))
2384
0
    has_changed = TRUE;
2385
2386
0
  g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_entry_free);
2387
2388
0
  mount_poller_mounts = current_mounts;
2389
2390
0
  if (has_changed)
2391
0
    {
2392
0
      G_LOCK (proc_mounts_source);
2393
0
      mount_poller_time = (guint64) g_get_monotonic_time ();
2394
0
      G_UNLOCK (proc_mounts_source);
2395
2396
0
      g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
2397
0
    }
2398
2399
0
  return TRUE;
2400
0
}
2401
2402
2403
static void
2404
mount_monitor_stop (void)
2405
0
{
2406
0
  if (fstab_monitor)
2407
0
    {
2408
0
      g_file_monitor_cancel (fstab_monitor);
2409
0
      g_object_unref (fstab_monitor);
2410
0
    }
2411
2412
0
  G_LOCK (proc_mounts_source);
2413
0
  if (proc_mounts_watch_source != NULL)
2414
0
    {
2415
0
      g_source_destroy (proc_mounts_watch_source);
2416
0
      proc_mounts_watch_source = NULL;
2417
0
    }
2418
2419
#ifdef HAVE_LIBMOUNT
2420
  g_clear_pointer (&proc_mounts_monitor, mnt_unref_monitor);
2421
#endif
2422
0
  G_UNLOCK (proc_mounts_source);
2423
2424
0
  if (mtab_monitor)
2425
0
    {
2426
0
      g_file_monitor_cancel (mtab_monitor);
2427
0
      g_object_unref (mtab_monitor);
2428
0
    }
2429
2430
0
  if (mtab_file_changed_id)
2431
0
    {
2432
0
      g_source_remove (mtab_file_changed_id);
2433
0
      mtab_file_changed_id = 0;
2434
0
    }
2435
2436
0
  g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_entry_free);
2437
0
}
2438
2439
static void
2440
mount_monitor_start (void)
2441
0
{
2442
0
  GFile *file;
2443
2444
0
  if (get_fstab_file () != NULL)
2445
0
    {
2446
0
      file = g_file_new_for_path (get_fstab_file ());
2447
0
      fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
2448
0
      g_object_unref (file);
2449
2450
0
      g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
2451
0
    }
2452
2453
0
  if (get_mtab_monitor_file () != NULL)
2454
0
    {
2455
0
      const gchar *mtab_path;
2456
2457
0
      mtab_path = get_mtab_monitor_file ();
2458
      /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
2459
       * See 'man proc' for more details.
2460
       */
2461
0
      if (g_str_has_prefix (mtab_path, "/proc/"))
2462
0
        {
2463
0
          GIOChannel *proc_mounts_channel = NULL;
2464
0
          GError *error = NULL;
2465
#ifdef HAVE_LIBMOUNT
2466
          int ret;
2467
2468
          G_LOCK (proc_mounts_source);
2469
2470
          proc_mounts_monitor = mnt_new_monitor ();
2471
          ret = mnt_monitor_enable_kernel (proc_mounts_monitor, TRUE);
2472
          if (ret < 0)
2473
            g_warning ("mnt_monitor_enable_kernel failed: %s", g_strerror (-ret));
2474
2475
          ret = mnt_monitor_enable_userspace (proc_mounts_monitor, TRUE, NULL);
2476
          if (ret < 0)
2477
            g_warning ("mnt_monitor_enable_userspace failed: %s", g_strerror (-ret));
2478
2479
#ifdef HAVE_MNT_MONITOR_VEIL_KERNEL
2480
          ret = mnt_monitor_veil_kernel (proc_mounts_monitor, TRUE);
2481
          if (ret < 0)
2482
            g_warning ("mnt_monitor_veil_kernel failed: %s", g_strerror (-ret));
2483
#endif
2484
2485
          ret = mnt_monitor_get_fd (proc_mounts_monitor);
2486
          if (ret >= 0)
2487
            {
2488
              proc_mounts_channel = g_io_channel_unix_new (ret);
2489
            }
2490
          else
2491
            {
2492
              g_debug ("mnt_monitor_get_fd failed: %s", g_strerror (-ret));
2493
              g_clear_pointer (&proc_mounts_monitor, mnt_unref_monitor);
2494
2495
              /* The mnt_monitor_get_fd function failed e.g. inotify limits are
2496
               * exceeded. Let's try to silently fallback to the old behavior.
2497
               * See: https://gitlab.gnome.org/GNOME/tracker-miners/-/issues/315
2498
               */
2499
            }
2500
2501
          G_UNLOCK (proc_mounts_source);
2502
#endif
2503
0
          if (proc_mounts_channel == NULL)
2504
0
            proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
2505
2506
0
          if (error != NULL)
2507
0
            {
2508
0
              g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
2509
0
                         error->message, g_quark_to_string (error->domain), error->code);
2510
0
              g_error_free (error);
2511
0
            }
2512
0
          else
2513
0
            {
2514
0
              G_LOCK (proc_mounts_source);
2515
2516
#ifdef HAVE_LIBMOUNT
2517
              if (proc_mounts_monitor != NULL)
2518
                proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_IN);
2519
#endif
2520
0
              if (proc_mounts_watch_source == NULL)
2521
0
                proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
2522
2523
0
              mount_poller_time = (guint64) g_get_monotonic_time ();
2524
0
              g_source_set_callback (proc_mounts_watch_source,
2525
0
                                     (GSourceFunc) proc_mounts_changed,
2526
0
                                     NULL, NULL);
2527
0
              g_source_attach (proc_mounts_watch_source,
2528
0
                               g_main_context_get_thread_default ());
2529
0
              g_source_unref (proc_mounts_watch_source);
2530
0
              g_io_channel_unref (proc_mounts_channel);
2531
2532
0
              G_UNLOCK (proc_mounts_source);
2533
0
            }
2534
0
        }
2535
0
      else
2536
0
        {
2537
0
          file = g_file_new_for_path (mtab_path);
2538
0
          mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
2539
0
          g_object_unref (file);
2540
0
          g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
2541
0
        }
2542
0
    }
2543
0
  else
2544
0
    {
2545
0
      G_LOCK (proc_mounts_source);
2546
2547
0
      proc_mounts_watch_source = g_timeout_source_new_seconds (3);
2548
0
      mount_poller_mounts = _g_get_unix_mounts ();
2549
0
      mount_poller_time = (guint64)g_get_monotonic_time ();
2550
0
      g_source_set_callback (proc_mounts_watch_source,
2551
0
                             mount_change_poller,
2552
0
                             NULL, NULL);
2553
0
      g_source_attach (proc_mounts_watch_source,
2554
0
                       g_main_context_get_thread_default ());
2555
0
      g_source_unref (proc_mounts_watch_source);
2556
2557
0
      G_UNLOCK (proc_mounts_source);
2558
0
    }
2559
0
}
2560
2561
static void
2562
g_unix_mount_monitor_finalize (GObject *object)
2563
0
{
2564
0
  GUnixMountMonitor *monitor;
2565
2566
0
  monitor = G_UNIX_MOUNT_MONITOR (object);
2567
2568
0
  g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
2569
2570
0
  G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
2571
0
}
2572
2573
static void
2574
g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
2575
0
{
2576
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2577
2578
0
  gobject_class->finalize = g_unix_mount_monitor_finalize;
2579
 
2580
  /**
2581
   * GUnixMountMonitor::mounts-changed:
2582
   * @monitor: the object on which the signal is emitted
2583
   * 
2584
   * Emitted when the Unix mount entries have changed.
2585
   */ 
2586
0
  signals[MOUNTS_CHANGED] =
2587
0
    g_signal_new (I_("mounts-changed"),
2588
0
      G_TYPE_FROM_CLASS (klass),
2589
0
      G_SIGNAL_RUN_LAST,
2590
0
      0,
2591
0
      NULL, NULL,
2592
0
      NULL,
2593
0
      G_TYPE_NONE, 0);
2594
2595
  /**
2596
   * GUnixMountMonitor::mountpoints-changed:
2597
   * @monitor: the object on which the signal is emitted
2598
   * 
2599
   * Emitted when the Unix mount points have changed.
2600
   */
2601
0
  signals[MOUNTPOINTS_CHANGED] =
2602
0
    g_signal_new (I_("mountpoints-changed"),
2603
0
      G_TYPE_FROM_CLASS (klass),
2604
0
      G_SIGNAL_RUN_LAST,
2605
0
      0,
2606
0
      NULL, NULL,
2607
0
      NULL,
2608
0
      G_TYPE_NONE, 0);
2609
0
}
2610
2611
static void
2612
g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
2613
0
{
2614
0
}
2615
2616
/**
2617
 * g_unix_mount_monitor_set_rate_limit:
2618
 * @mount_monitor: a [class@GioUnix.MountMonitor]
2619
 * @limit_msec: a integer with the limit (in milliseconds) to poll for changes
2620
 *
2621
 * This function does nothing.
2622
 *
2623
 * Before 2.44, this was a partially-effective way of controlling the
2624
 * rate at which events would be reported under some uncommon
2625
 * circumstances.  Since @mount_monitor is a singleton, it also meant
2626
 * that calling this function would have side effects for other users of
2627
 * the monitor.
2628
 *
2629
 * Since: 2.18
2630
 * Deprecated: 2.44: This function does nothing. Don’t call it.
2631
 */
2632
void
2633
g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
2634
                                     gint               limit_msec)
2635
0
{
2636
0
}
2637
2638
/**
2639
 * g_unix_mount_monitor_get:
2640
 *
2641
 * Gets the [class@GioUnix.MountMonitor] for the current thread-default main
2642
 * context.
2643
 *
2644
 * The mount monitor can be used to monitor for changes to the list of
2645
 * mounted filesystems as well as the list of mount points (ie: fstab
2646
 * entries).
2647
 *
2648
 * You must only call [method@GObject.Object.unref] on the return value from
2649
 * under the same main context as you called this function.
2650
 *
2651
 * Returns: (transfer full): the [class@GioUnix.MountMonitor]
2652
 * Since: 2.44
2653
 **/
2654
GUnixMountMonitor *
2655
g_unix_mount_monitor_get (void)
2656
0
{
2657
0
  return g_context_specific_group_get (&mount_monitor_group,
2658
0
                                       G_TYPE_UNIX_MOUNT_MONITOR,
2659
0
                                       G_STRUCT_OFFSET(GUnixMountMonitor, context),
2660
0
                                       mount_monitor_start);
2661
0
}
2662
2663
/**
2664
 * g_unix_mount_monitor_new:
2665
 *
2666
 * Deprecated alias for [func@GioUnix.MountMonitor.get].
2667
 *
2668
 * This function was never a true constructor, which is why it was
2669
 * renamed.
2670
 *
2671
 * Returns: a [class@GioUnix.MountMonitor]
2672
 * Deprecated: 2.44: Use [func@GioUnix.MountMonitor.get] instead.
2673
 */
2674
GUnixMountMonitor *
2675
g_unix_mount_monitor_new (void)
2676
0
{
2677
0
  return g_unix_mount_monitor_get ();
2678
0
}
2679
2680
/* GUnixMount {{{1 */
2681
/**
2682
 * g_unix_mount_free:
2683
 * @mount_entry: a [struct@GioUnix.MountEntry]
2684
 * 
2685
 * Frees a Unix mount.
2686
 *
2687
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.free] instead.
2688
 */
2689
void
2690
g_unix_mount_free (GUnixMountEntry *mount_entry)
2691
0
{
2692
0
  g_unix_mount_entry_free (mount_entry);
2693
0
}
2694
2695
/**
2696
 * g_unix_mount_entry_free:
2697
 * @mount_entry: a [struct@GioUnix.MountEntry]
2698
 *
2699
 * Frees a Unix mount.
2700
 *
2701
 * Since: 2.84
2702
 */
2703
void
2704
g_unix_mount_entry_free (GUnixMountEntry *mount_entry)
2705
0
{
2706
0
  g_return_if_fail (mount_entry != NULL);
2707
2708
0
  g_free (mount_entry->mount_path);
2709
0
  g_free (mount_entry->device_path);
2710
0
  g_free (mount_entry->root_path);
2711
0
  g_free (mount_entry->filesystem_type);
2712
0
  g_free (mount_entry->options);
2713
0
  g_free (mount_entry);
2714
0
}
2715
2716
/**
2717
 * g_unix_mount_copy:
2718
 * @mount_entry: a [struct@GioUnix.MountEntry]
2719
 *
2720
 * Makes a copy of @mount_entry.
2721
 *
2722
 * Returns: (transfer full): a new [struct@GioUnix.MountEntry]
2723
 * Since: 2.54
2724
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.copy] instead.
2725
 */
2726
GUnixMountEntry *
2727
g_unix_mount_copy (GUnixMountEntry *mount_entry)
2728
0
{
2729
0
  return g_unix_mount_entry_copy (mount_entry);
2730
0
}
2731
2732
/**
2733
 * g_unix_mount_entry_copy:
2734
 * @mount_entry: a [struct@GioUnix.MountEntry]
2735
 *
2736
 * Makes a copy of @mount_entry.
2737
 *
2738
 * Returns: (transfer full): a new [struct@GioUnix.MountEntry]
2739
 * Since: 2.84
2740
 */
2741
GUnixMountEntry *
2742
g_unix_mount_entry_copy (GUnixMountEntry *mount_entry)
2743
0
{
2744
0
  GUnixMountEntry *copy;
2745
2746
0
  g_return_val_if_fail (mount_entry != NULL, NULL);
2747
2748
0
  copy = g_new0 (GUnixMountEntry, 1);
2749
0
  copy->mount_path = g_strdup (mount_entry->mount_path);
2750
0
  copy->device_path = g_strdup (mount_entry->device_path);
2751
0
  copy->root_path = g_strdup (mount_entry->root_path);
2752
0
  copy->filesystem_type = g_strdup (mount_entry->filesystem_type);
2753
0
  copy->options = g_strdup (mount_entry->options);
2754
0
  copy->is_read_only = mount_entry->is_read_only;
2755
0
  copy->is_system_internal = mount_entry->is_system_internal;
2756
2757
0
  return copy;
2758
0
}
2759
2760
/**
2761
 * g_unix_mount_point_free:
2762
 * @mount_point: Unix mount point to free.
2763
 * 
2764
 * Frees a Unix mount point.
2765
 */
2766
void
2767
g_unix_mount_point_free (GUnixMountPoint *mount_point)
2768
0
{
2769
0
  g_return_if_fail (mount_point != NULL);
2770
2771
0
  g_free (mount_point->mount_path);
2772
0
  g_free (mount_point->device_path);
2773
0
  g_free (mount_point->filesystem_type);
2774
0
  g_free (mount_point->options);
2775
0
  g_free (mount_point);
2776
0
}
2777
2778
/**
2779
 * g_unix_mount_point_copy:
2780
 * @mount_point: a [struct@GioUnix.MountPoint]
2781
 *
2782
 * Makes a copy of @mount_point.
2783
 *
2784
 * Returns: (transfer full): a new [struct@GioUnix.MountPoint]
2785
 * Since: 2.54
2786
 */
2787
GUnixMountPoint*
2788
g_unix_mount_point_copy (GUnixMountPoint *mount_point)
2789
0
{
2790
0
  GUnixMountPoint *copy;
2791
2792
0
  g_return_val_if_fail (mount_point != NULL, NULL);
2793
2794
0
  copy = g_new0 (GUnixMountPoint, 1);
2795
0
  copy->mount_path = g_strdup (mount_point->mount_path);
2796
0
  copy->device_path = g_strdup (mount_point->device_path);
2797
0
  copy->filesystem_type = g_strdup (mount_point->filesystem_type);
2798
0
  copy->options = g_strdup (mount_point->options);
2799
0
  copy->is_read_only = mount_point->is_read_only;
2800
0
  copy->is_user_mountable = mount_point->is_user_mountable;
2801
0
  copy->is_loopback = mount_point->is_loopback;
2802
2803
0
  return copy;
2804
0
}
2805
2806
/**
2807
 * g_unix_mount_compare:
2808
 * @mount1: first [struct@GioUnix.MountEntry] to compare
2809
 * @mount2: second [struct@GioUnix.MountEntry] to compare
2810
 * 
2811
 * Compares two Unix mounts.
2812
 * 
2813
 * Returns: `1`, `0` or `-1` if @mount1 is greater than, equal to,
2814
 *    or less than @mount2, respectively
2815
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.compare] instead.
2816
 */
2817
gint
2818
g_unix_mount_compare (GUnixMountEntry *mount1,
2819
          GUnixMountEntry *mount2)
2820
0
{
2821
0
  return g_unix_mount_entry_compare (mount1, mount2);
2822
0
}
2823
2824
/**
2825
 * g_unix_mount_entry_compare:
2826
 * @mount1: first [struct@GioUnix.MountEntry] to compare
2827
 * @mount2: second [struct@GioUnix.MountEntry] to compare
2828
 *
2829
 * Compares two Unix mounts.
2830
 *
2831
 * Returns: `1`, `0` or `-1` if @mount1 is greater than, equal to,
2832
 *    or less than @mount2, respectively
2833
 * Since: 2.84
2834
 */
2835
gint
2836
g_unix_mount_entry_compare (GUnixMountEntry *mount1,
2837
                            GUnixMountEntry *mount2)
2838
0
{
2839
0
  int res;
2840
2841
0
  g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2842
  
2843
0
  res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2844
0
  if (res != 0)
2845
0
    return res;
2846
  
2847
0
  res = g_strcmp0 (mount1->device_path, mount2->device_path);
2848
0
  if (res != 0)
2849
0
    return res;
2850
2851
0
  res = g_strcmp0 (mount1->root_path, mount2->root_path);
2852
0
  if (res != 0)
2853
0
    return res;
2854
2855
0
  res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2856
0
  if (res != 0)
2857
0
    return res;
2858
2859
0
  res = g_strcmp0 (mount1->options, mount2->options);
2860
0
  if (res != 0)
2861
0
    return res;
2862
2863
0
  res =  mount1->is_read_only - mount2->is_read_only;
2864
0
  if (res != 0)
2865
0
    return res;
2866
  
2867
0
  return 0;
2868
0
}
2869
2870
/**
2871
 * g_unix_mount_get_mount_path:
2872
 * @mount_entry: a [struct@GioUnix.MountEntry] to get the mount path for
2873
 * 
2874
 * Gets the mount path for a Unix mount.
2875
 * 
2876
 * Returns: (type filename): the mount path for @mount_entry
2877
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.get_mount_path] instead.
2878
 */
2879
const gchar *
2880
g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
2881
0
{
2882
0
  return g_unix_mount_entry_get_mount_path (mount_entry);
2883
0
}
2884
2885
/**
2886
 * g_unix_mount_entry_get_mount_path:
2887
 * @mount_entry: a [struct@GioUnix.MountEntry] to get the mount path for
2888
 *
2889
 * Gets the mount path for a Unix mount.
2890
 *
2891
 * Returns: (type filename): the mount path for @mount_entry
2892
 * Since: 2.84
2893
 */
2894
const gchar *
2895
g_unix_mount_entry_get_mount_path (GUnixMountEntry *mount_entry)
2896
0
{
2897
0
  g_return_val_if_fail (mount_entry != NULL, NULL);
2898
2899
0
  return mount_entry->mount_path;
2900
0
}
2901
2902
/**
2903
 * g_unix_mount_get_device_path:
2904
 * @mount_entry: a [struct@GioUnix.MountEntry]
2905
 * 
2906
 * Gets the device path for a Unix mount.
2907
 * 
2908
 * Returns: (type filename): a string containing the device path
2909
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.get_device_path] instead.
2910
 */
2911
const gchar *
2912
g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
2913
0
{
2914
0
  return g_unix_mount_entry_get_device_path (mount_entry);
2915
0
}
2916
2917
/**
2918
 * g_unix_mount_entry_get_device_path:
2919
 * @mount_entry: a [struct@GioUnix.MountEntry]
2920
 *
2921
 * Gets the device path for a Unix mount.
2922
 *
2923
 * Returns: (type filename): a string containing the device path
2924
 * Since: 2.84
2925
 */
2926
const gchar *
2927
g_unix_mount_entry_get_device_path (GUnixMountEntry *mount_entry)
2928
0
{
2929
0
  g_return_val_if_fail (mount_entry != NULL, NULL);
2930
2931
0
  return mount_entry->device_path;
2932
0
}
2933
2934
/**
2935
 * g_unix_mount_get_root_path:
2936
 * @mount_entry: a [struct@GioUnix.MountEntry]
2937
 * 
2938
 * Gets the root of the mount within the filesystem.
2939
 *
2940
 * This is useful e.g. for mounts created by bind operation, or btrfs subvolumes.
2941
 * 
2942
 * For example, the root path is equal to `/` for a mount created by
2943
 * `mount /dev/sda1 /mnt/foo` and `/bar` for
2944
 * `mount --bind /mnt/foo/bar /mnt/bar`.
2945
 *
2946
 * Returns: (nullable): a string containing the root, or `NULL` if not supported
2947
 * Since: 2.60
2948
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.get_root_path] instead.
2949
 */
2950
const gchar *
2951
g_unix_mount_get_root_path (GUnixMountEntry *mount_entry)
2952
0
{
2953
0
  return g_unix_mount_entry_get_root_path (mount_entry);
2954
0
}
2955
2956
/**
2957
 * g_unix_mount_entry_get_root_path:
2958
 * @mount_entry: a [struct@GioUnix.MountEntry]
2959
 *
2960
 * Gets the root of the mount within the filesystem. This is useful e.g. for
2961
 * mounts created by bind operation, or btrfs subvolumes.
2962
 *
2963
 * For example, the root path is equal to `/` for a mount created by
2964
 * `mount /dev/sda1 /mnt/foo` and `/bar` for
2965
 * `mount --bind /mnt/foo/bar /mnt/bar`.
2966
 *
2967
 * Returns: (nullable): a string containing the root, or `NULL` if not supported
2968
 * Since: 2.84
2969
 */
2970
const gchar *
2971
g_unix_mount_entry_get_root_path (GUnixMountEntry *mount_entry)
2972
0
{
2973
0
  g_return_val_if_fail (mount_entry != NULL, NULL);
2974
2975
0
  return mount_entry->root_path;
2976
0
}
2977
2978
/**
2979
 * g_unix_mount_get_fs_type:
2980
 * @mount_entry: a [struct@GioUnix.MountEntry]
2981
 * 
2982
 * Gets the filesystem type for the Unix mount.
2983
 * 
2984
 * Returns: a string containing the file system type
2985
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.get_fs_type] instead.
2986
 */
2987
const gchar *
2988
g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
2989
0
{
2990
0
  return g_unix_mount_entry_get_fs_type (mount_entry);
2991
0
}
2992
2993
/**
2994
 * g_unix_mount_entry_get_fs_type:
2995
 * @mount_entry: a [struct@GioUnix.MountEntry]
2996
 *
2997
 * Gets the filesystem type for the Unix mount.
2998
 *
2999
 * Returns: a string containing the file system type
3000
 * Since: 2.84
3001
 */
3002
const gchar *
3003
g_unix_mount_entry_get_fs_type (GUnixMountEntry *mount_entry)
3004
0
{
3005
0
  g_return_val_if_fail (mount_entry != NULL, NULL);
3006
3007
0
  return mount_entry->filesystem_type;
3008
0
}
3009
3010
/**
3011
 * g_unix_mount_get_options:
3012
 * @mount_entry: a [struct@GioUnix.MountEntry]
3013
 * 
3014
 * Gets a comma separated list of mount options for the Unix mount.
3015
 * 
3016
 * For example: `rw,relatime,seclabel,data=ordered`.
3017
 * 
3018
 * This is similar to [method@GioUnix.MountPoint.get_options], but it takes
3019
 * a [struct@GioUnix.MountEntry] as an argument.
3020
 *
3021
 * Returns: (nullable): a string containing the options, or `NULL` if not
3022
 *    available.
3023
 * Since: 2.58
3024
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.get_options] instead.
3025
 */
3026
const gchar *
3027
g_unix_mount_get_options (GUnixMountEntry *mount_entry)
3028
0
{
3029
0
  return g_unix_mount_entry_get_options (mount_entry);
3030
0
}
3031
3032
/**
3033
 * g_unix_mount_entry_get_options:
3034
 * @mount_entry: a [struct@GioUnix.MountEntry]
3035
 *
3036
 * Gets a comma separated list of mount options for the Unix mount.
3037
 *
3038
 * For example: `rw,relatime,seclabel,data=ordered`.
3039
 *
3040
 * This is similar to [method@GioUnix.MountPoint.get_options], but it takes
3041
 * a [struct@GioUnix.MountEntry] as an argument.
3042
 *
3043
 * Returns: (nullable): a string containing the options, or `NULL` if not
3044
 *    available.
3045
 * Since: 2.84
3046
 */
3047
const gchar *
3048
g_unix_mount_entry_get_options (GUnixMountEntry *mount_entry)
3049
0
{
3050
0
  g_return_val_if_fail (mount_entry != NULL, NULL);
3051
3052
0
  return mount_entry->options;
3053
0
}
3054
3055
/**
3056
 * g_unix_mount_is_readonly:
3057
 * @mount_entry: a [struct@GioUnix.MountEntry]
3058
 * 
3059
 * Checks if a Unix mount is mounted read only.
3060
 * 
3061
 * Returns: true if @mount_entry is read only; false otherwise
3062
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.is_readonly] instead.
3063
 */
3064
gboolean
3065
g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
3066
0
{
3067
0
  return g_unix_mount_entry_is_readonly (mount_entry);
3068
0
}
3069
3070
/**
3071
 * g_unix_mount_entry_is_readonly:
3072
 * @mount_entry: a [struct@GioUnix.MountEntry]
3073
 *
3074
 * Checks if a Unix mount is mounted read only.
3075
 *
3076
 * Returns: true if @mount_entry is read only; false otherwise
3077
 * Since: 2.84
3078
 */
3079
gboolean
3080
g_unix_mount_entry_is_readonly (GUnixMountEntry *mount_entry)
3081
0
{
3082
0
  g_return_val_if_fail (mount_entry != NULL, FALSE);
3083
3084
0
  return mount_entry->is_read_only;
3085
0
}
3086
3087
/**
3088
 * g_unix_mount_is_system_internal:
3089
 * @mount_entry: a [struct@GioUnix.MountEntry]
3090
 *
3091
 * Checks if a Unix mount is a system mount.
3092
 *
3093
 * This is the Boolean OR of
3094
 * [func@GioUnix.is_system_fs_type], [func@GioUnix.is_system_device_path] and
3095
 * [func@GioUnix.is_mount_path_system_internal] on @mount_entry’s properties.
3096
 * 
3097
 * The definition of what a ‘system’ mount entry is may change over time as new
3098
 * file system types and device paths are ignored.
3099
 *
3100
 * Returns: true if the Unix mount is for a system path; false otherwise
3101
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.is_system_internal] instead.
3102
 */
3103
gboolean
3104
g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
3105
0
{
3106
0
  return g_unix_mount_entry_is_system_internal (mount_entry);
3107
0
}
3108
3109
/**
3110
 * g_unix_mount_entry_is_system_internal:
3111
 * @mount_entry: a [struct@GioUnix.MountEntry]
3112
 *
3113
 * Checks if a Unix mount is a system mount.
3114
 *
3115
 * This is the Boolean OR of
3116
 * [func@GioUnix.is_system_fs_type], [func@GioUnix.is_system_device_path] and
3117
 * [func@GioUnix.is_mount_path_system_internal] on @mount_entry’s properties.
3118
 *
3119
 * The definition of what a ‘system’ mount entry is may change over time as new
3120
 * file system types and device paths are ignored.
3121
 *
3122
 * Returns: true if the Unix mount is for a system path; false otherwise
3123
 * Since: 2.84
3124
 */
3125
gboolean
3126
g_unix_mount_entry_is_system_internal (GUnixMountEntry *mount_entry)
3127
0
{
3128
0
  g_return_val_if_fail (mount_entry != NULL, FALSE);
3129
3130
0
  return mount_entry->is_system_internal;
3131
0
}
3132
3133
/* GUnixMountPoint {{{1 */
3134
/**
3135
 * g_unix_mount_point_compare:
3136
 * @mount1: a [struct@GioUnix.MountPoint]
3137
 * @mount2: a [struct@GioUnix.MountPoint]
3138
 * 
3139
 * Compares two Unix mount points.
3140
 * 
3141
 * Returns: `1`, `0` or `-1` if @mount1 is greater than, equal to,
3142
 *    or less than @mount2, respectively
3143
 */
3144
gint
3145
g_unix_mount_point_compare (GUnixMountPoint *mount1,
3146
          GUnixMountPoint *mount2)
3147
0
{
3148
0
  int res;
3149
3150
0
  g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
3151
3152
0
  res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
3153
0
  if (res != 0) 
3154
0
    return res;
3155
  
3156
0
  res = g_strcmp0 (mount1->device_path, mount2->device_path);
3157
0
  if (res != 0) 
3158
0
    return res;
3159
  
3160
0
  res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
3161
0
  if (res != 0) 
3162
0
    return res;
3163
3164
0
  res = g_strcmp0 (mount1->options, mount2->options);
3165
0
  if (res != 0) 
3166
0
    return res;
3167
3168
0
  res =  mount1->is_read_only - mount2->is_read_only;
3169
0
  if (res != 0) 
3170
0
    return res;
3171
3172
0
  res = mount1->is_user_mountable - mount2->is_user_mountable;
3173
0
  if (res != 0) 
3174
0
    return res;
3175
3176
0
  res = mount1->is_loopback - mount2->is_loopback;
3177
0
  if (res != 0)
3178
0
    return res;
3179
  
3180
0
  return 0;
3181
0
}
3182
3183
/**
3184
 * g_unix_mount_point_get_mount_path:
3185
 * @mount_point: a [struct@GioUnix.MountPoint]
3186
 * 
3187
 * Gets the mount path for a Unix mount point.
3188
 * 
3189
 * Returns: (type filename): a string containing the mount path
3190
 */
3191
const gchar *
3192
g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
3193
0
{
3194
0
  g_return_val_if_fail (mount_point != NULL, NULL);
3195
3196
0
  return mount_point->mount_path;
3197
0
}
3198
3199
/**
3200
 * g_unix_mount_point_get_device_path:
3201
 * @mount_point: a [struct@GioUnix.MountPoint]
3202
 * 
3203
 * Gets the device path for a Unix mount point.
3204
 * 
3205
 * Returns: (type filename): a string containing the device path
3206
 */
3207
const gchar *
3208
g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
3209
0
{
3210
0
  g_return_val_if_fail (mount_point != NULL, NULL);
3211
3212
0
  return mount_point->device_path;
3213
0
}
3214
3215
/**
3216
 * g_unix_mount_point_get_fs_type:
3217
 * @mount_point: a [struct@GioUnix.MountPoint]
3218
 * 
3219
 * Gets the file system type for the mount point.
3220
 * 
3221
 * Returns: a string containing the file system type
3222
 */
3223
const gchar *
3224
g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
3225
0
{
3226
0
  g_return_val_if_fail (mount_point != NULL, NULL);
3227
3228
0
  return mount_point->filesystem_type;
3229
0
}
3230
3231
/**
3232
 * g_unix_mount_point_get_options:
3233
 * @mount_point: a [struct@GioUnix.MountPoint]
3234
 * 
3235
 * Gets the options for the mount point.
3236
 * 
3237
 * Returns: (nullable): a string containing the options
3238
 * Since: 2.32
3239
 */
3240
const gchar *
3241
g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
3242
0
{
3243
0
  g_return_val_if_fail (mount_point != NULL, NULL);
3244
3245
0
  return mount_point->options;
3246
0
}
3247
3248
/**
3249
 * g_unix_mount_point_is_readonly:
3250
 * @mount_point: a [struct@GioUnix.MountPoint]
3251
 * 
3252
 * Checks if a Unix mount point is read only.
3253
 * 
3254
 * Returns: true if a mount point is read only; false otherwise
3255
 */
3256
gboolean
3257
g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
3258
0
{
3259
0
  g_return_val_if_fail (mount_point != NULL, FALSE);
3260
3261
0
  return mount_point->is_read_only;
3262
0
}
3263
3264
/**
3265
 * g_unix_mount_point_is_user_mountable:
3266
 * @mount_point: a [struct@GioUnix.MountPoint]
3267
 * 
3268
 * Checks if a Unix mount point is mountable by the user.
3269
 * 
3270
 * Returns: true if the mount point is user mountable; false otherwise
3271
 */
3272
gboolean
3273
g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
3274
0
{
3275
0
  g_return_val_if_fail (mount_point != NULL, FALSE);
3276
3277
0
  return mount_point->is_user_mountable;
3278
0
}
3279
3280
/**
3281
 * g_unix_mount_point_is_loopback:
3282
 * @mount_point: a [struct@GioUnix.MountPoint]
3283
 * 
3284
 * Checks if a Unix mount point is a loopback device.
3285
 * 
3286
 * Returns: true if the mount point is a loopback device; false otherwise
3287
 */
3288
gboolean
3289
g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
3290
0
{
3291
0
  g_return_val_if_fail (mount_point != NULL, FALSE);
3292
3293
0
  return mount_point->is_loopback;
3294
0
}
3295
3296
static GUnixMountType
3297
guess_mount_type (const char *mount_path,
3298
      const char *device_path,
3299
      const char *filesystem_type)
3300
0
{
3301
0
  GUnixMountType type;
3302
0
  char *basename;
3303
3304
0
  type = G_UNIX_MOUNT_TYPE_UNKNOWN;
3305
  
3306
0
  if ((strcmp (filesystem_type, "udf") == 0) ||
3307
0
      (strcmp (filesystem_type, "iso9660") == 0) ||
3308
0
      (strcmp (filesystem_type, "cd9660") == 0))
3309
0
    type = G_UNIX_MOUNT_TYPE_CDROM;
3310
0
  else if ((strcmp (filesystem_type, "nfs") == 0) ||
3311
0
           (strcmp (filesystem_type, "nfs4") == 0))
3312
0
    type = G_UNIX_MOUNT_TYPE_NFS;
3313
0
  else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
3314
0
     g_str_has_prefix (device_path, "/dev/fd") ||
3315
0
     g_str_has_prefix (device_path, "/dev/floppy"))
3316
0
    type = G_UNIX_MOUNT_TYPE_FLOPPY;
3317
0
  else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
3318
0
     g_str_has_prefix (device_path, "/dev/acd") ||
3319
0
     g_str_has_prefix (device_path, "/dev/cd"))
3320
0
    type = G_UNIX_MOUNT_TYPE_CDROM;
3321
0
  else if (g_str_has_prefix (device_path, "/vol/"))
3322
0
    {
3323
0
      const char *name = mount_path + strlen ("/");
3324
      
3325
0
      if (g_str_has_prefix (name, "cdrom"))
3326
0
  type = G_UNIX_MOUNT_TYPE_CDROM;
3327
0
      else if (g_str_has_prefix (name, "floppy") ||
3328
0
         g_str_has_prefix (device_path, "/vol/dev/diskette/")) 
3329
0
  type = G_UNIX_MOUNT_TYPE_FLOPPY;
3330
0
      else if (g_str_has_prefix (name, "rmdisk")) 
3331
0
  type = G_UNIX_MOUNT_TYPE_ZIP;
3332
0
      else if (g_str_has_prefix (name, "jaz"))
3333
0
  type = G_UNIX_MOUNT_TYPE_JAZ;
3334
0
      else if (g_str_has_prefix (name, "memstick"))
3335
0
  type = G_UNIX_MOUNT_TYPE_MEMSTICK;
3336
0
    }
3337
0
  else
3338
0
    {
3339
0
      basename = g_path_get_basename (mount_path);
3340
      
3341
0
      if (g_str_has_prefix (basename, "cdr") ||
3342
0
    g_str_has_prefix (basename, "cdwriter") ||
3343
0
    g_str_has_prefix (basename, "burn") ||
3344
0
    g_str_has_prefix (basename, "dvdr"))
3345
0
  type = G_UNIX_MOUNT_TYPE_CDROM;
3346
0
      else if (g_str_has_prefix (basename, "floppy"))
3347
0
  type = G_UNIX_MOUNT_TYPE_FLOPPY;
3348
0
      else if (g_str_has_prefix (basename, "zip"))
3349
0
  type = G_UNIX_MOUNT_TYPE_ZIP;
3350
0
      else if (g_str_has_prefix (basename, "jaz"))
3351
0
  type = G_UNIX_MOUNT_TYPE_JAZ;
3352
0
      else if (g_str_has_prefix (basename, "camera"))
3353
0
  type = G_UNIX_MOUNT_TYPE_CAMERA;
3354
0
      else if (g_str_has_prefix (basename, "memstick") ||
3355
0
         g_str_has_prefix (basename, "memory_stick") ||
3356
0
         g_str_has_prefix (basename, "ram"))
3357
0
  type = G_UNIX_MOUNT_TYPE_MEMSTICK;
3358
0
      else if (g_str_has_prefix (basename, "compact_flash"))
3359
0
  type = G_UNIX_MOUNT_TYPE_CF;
3360
0
      else if (g_str_has_prefix (basename, "smart_media"))
3361
0
  type = G_UNIX_MOUNT_TYPE_SM;
3362
0
      else if (g_str_has_prefix (basename, "sd_mmc"))
3363
0
  type = G_UNIX_MOUNT_TYPE_SDMMC;
3364
0
      else if (g_str_has_prefix (basename, "ipod"))
3365
0
  type = G_UNIX_MOUNT_TYPE_IPOD;
3366
      
3367
0
      g_free (basename);
3368
0
    }
3369
  
3370
0
  if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
3371
0
    type = G_UNIX_MOUNT_TYPE_HD;
3372
  
3373
0
  return type;
3374
0
}
3375
3376
/**
3377
 * g_unix_mount_entry_guess_type:
3378
 * @mount_entry: a [struct@GioUnix.MountEntry]
3379
 *
3380
 * Guesses the type of a Unix mount entry.
3381
 * 
3382
 * If the mount type cannot be determined, returns
3383
 * [enum@GioUnix.MountType.UNKNOWN].
3384
 * 
3385
 * Returns: a [enum@GioUnix.MountType]
3386
 */
3387
static GUnixMountType
3388
g_unix_mount_entry_guess_type (GUnixMountEntry *mount_entry)
3389
0
{
3390
0
  g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
3391
0
  g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
3392
0
  g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
3393
0
  g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
3394
3395
0
  return guess_mount_type (mount_entry->mount_path,
3396
0
         mount_entry->device_path,
3397
0
         mount_entry->filesystem_type);
3398
0
}
3399
3400
/**
3401
 * g_unix_mount_point_guess_type:
3402
 * @mount_point: a [struct@GioUnix.MountPoint]
3403
 * 
3404
 * Guesses the type of a Unix mount point.
3405
 *
3406
 * If the mount type cannot be determined, returns
3407
 * [enum@GioUnix.MountType.UNKNOWN].
3408
 * 
3409
 * Returns: a [enum@GioUnix.MountType]
3410
 */
3411
static GUnixMountType
3412
g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
3413
0
{
3414
0
  g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
3415
0
  g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
3416
0
  g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
3417
0
  g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
3418
3419
0
  return guess_mount_type (mount_point->mount_path,
3420
0
         mount_point->device_path,
3421
0
         mount_point->filesystem_type);
3422
0
}
3423
3424
static const char *
3425
type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
3426
0
{
3427
0
  const char *icon_name;
3428
  
3429
0
  switch (type)
3430
0
    {
3431
0
    case G_UNIX_MOUNT_TYPE_HD:
3432
0
      if (is_mount_point)
3433
0
        icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
3434
0
      else
3435
0
        icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
3436
0
      break;
3437
0
    case G_UNIX_MOUNT_TYPE_FLOPPY:
3438
0
    case G_UNIX_MOUNT_TYPE_ZIP:
3439
0
    case G_UNIX_MOUNT_TYPE_JAZ:
3440
0
      if (is_mount_point)
3441
0
        icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
3442
0
      else
3443
0
        icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
3444
0
      break;
3445
0
    case G_UNIX_MOUNT_TYPE_CDROM:
3446
0
      if (is_mount_point)
3447
0
        icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
3448
0
      else
3449
0
        icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
3450
0
      break;
3451
0
    case G_UNIX_MOUNT_TYPE_NFS:
3452
0
        icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
3453
0
      break;
3454
0
    case G_UNIX_MOUNT_TYPE_MEMSTICK:
3455
0
      if (is_mount_point)
3456
0
        icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
3457
0
      else
3458
0
        icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
3459
0
      break;
3460
0
    case G_UNIX_MOUNT_TYPE_CAMERA:
3461
0
      if (is_mount_point)
3462
0
        icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
3463
0
      else
3464
0
        icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
3465
0
      break;
3466
0
    case G_UNIX_MOUNT_TYPE_IPOD:
3467
0
      if (is_mount_point)
3468
0
        icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
3469
0
      else
3470
0
        icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
3471
0
      break;
3472
0
    case G_UNIX_MOUNT_TYPE_UNKNOWN:
3473
0
    default:
3474
0
      if (is_mount_point)
3475
0
        icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
3476
0
      else
3477
0
        icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
3478
0
      break;
3479
0
    }
3480
3481
0
  return icon_name;
3482
0
}
3483
3484
/**
3485
 * g_unix_mount_guess_name:
3486
 * @mount_entry: a [struct@GioUnix.MountEntry]
3487
 *
3488
 * Guesses the name of a Unix mount entry.
3489
 * 
3490
 * The result is a translated string.
3491
 *
3492
 * Returns: (transfer full): a newly allocated translated string
3493
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.guess_name] instead.
3494
 */
3495
gchar *
3496
g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
3497
0
{
3498
0
  return g_unix_mount_entry_guess_name (mount_entry);
3499
0
}
3500
3501
/**
3502
 * g_unix_mount_entry_guess_name:
3503
 * @mount_entry: a [struct@GioUnix.MountEntry]
3504
 *
3505
 * Guesses the name of a Unix mount entry.
3506
 *
3507
 * The result is a translated string.
3508
 *
3509
 * Returns: (transfer full): a newly allocated translated string
3510
 * Since: 2.84
3511
 */
3512
gchar *
3513
g_unix_mount_entry_guess_name (GUnixMountEntry *mount_entry)
3514
0
{
3515
0
  char *name;
3516
3517
0
  if (strcmp (mount_entry->mount_path, "/") == 0)
3518
0
    name = g_strdup (_("Filesystem root"));
3519
0
  else
3520
0
    name = g_filename_display_basename (mount_entry->mount_path);
3521
3522
0
  return name;
3523
0
}
3524
3525
/**
3526
 * g_unix_mount_guess_icon:
3527
 * @mount_entry: a [struct@GioUnix.MountEntry]
3528
 * 
3529
 * Guesses the icon of a Unix mount entry.
3530
 *
3531
 * Returns: (transfer full): a [iface@Gio.Icon]
3532
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.guess_icon] instead.
3533
 */
3534
GIcon *
3535
g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
3536
0
{
3537
0
  return g_unix_mount_entry_guess_icon (mount_entry);
3538
0
}
3539
3540
/**
3541
 * g_unix_mount_entry_guess_icon:
3542
 * @mount_entry: a [struct@GioUnix.MountEntry]
3543
 *
3544
 * Guesses the icon of a Unix mount entry.
3545
 *
3546
 * Returns: (transfer full): a [iface@Gio.Icon]
3547
 * Since: 2.84
3548
 */
3549
GIcon *
3550
g_unix_mount_entry_guess_icon (GUnixMountEntry *mount_entry)
3551
0
{
3552
0
  return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_entry_guess_type (mount_entry), FALSE, FALSE));
3553
0
}
3554
3555
/**
3556
 * g_unix_mount_guess_symbolic_icon:
3557
 * @mount_entry: a [struct@GioUnix.MountEntry]
3558
 *
3559
 * Guesses the symbolic icon of a Unix mount entry.
3560
 *
3561
 * Returns: (transfer full): a [iface@Gio.Icon]
3562
 * Since: 2.34
3563
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.guess_symbolic_icon] instead.
3564
 */
3565
GIcon *
3566
g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
3567
0
{
3568
0
  return g_unix_mount_entry_guess_symbolic_icon (mount_entry);
3569
0
}
3570
3571
/**
3572
 * g_unix_mount_entry_guess_symbolic_icon:
3573
 * @mount_entry: a [struct@GioUnix.MountEntry]
3574
 *
3575
 * Guesses the symbolic icon of a Unix mount entry.
3576
 *
3577
 * Returns: (transfer full): a [iface@Gio.Icon]
3578
 * Since: 2.84
3579
 */
3580
GIcon *
3581
g_unix_mount_entry_guess_symbolic_icon (GUnixMountEntry *mount_entry)
3582
0
{
3583
0
  return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_entry_guess_type (mount_entry), FALSE, TRUE));
3584
0
}
3585
3586
/**
3587
 * g_unix_mount_point_guess_name:
3588
 * @mount_point: a [struct@GioUnix.MountPoint]
3589
 *
3590
 * Guesses the name of a Unix mount point.
3591
 * 
3592
 * The result is a translated string.
3593
 *
3594
 * Returns: (transfer full): a newly allocated translated string
3595
 */
3596
gchar *
3597
g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
3598
0
{
3599
0
  char *name;
3600
3601
0
  if (strcmp (mount_point->mount_path, "/") == 0)
3602
0
    name = g_strdup (_("Filesystem root"));
3603
0
  else
3604
0
    name = g_filename_display_basename (mount_point->mount_path);
3605
3606
0
  return name;
3607
0
}
3608
3609
/**
3610
 * g_unix_mount_point_guess_icon:
3611
 * @mount_point: a [struct@GioUnix.MountPoint]
3612
 * 
3613
 * Guesses the icon of a Unix mount point.
3614
 *
3615
 * Returns: (transfer full): a [iface@Gio.Icon]
3616
 */
3617
GIcon *
3618
g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
3619
0
{
3620
0
  return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
3621
0
}
3622
3623
/**
3624
 * g_unix_mount_point_guess_symbolic_icon:
3625
 * @mount_point: a [struct@GioUnix.MountPoint]
3626
 *
3627
 * Guesses the symbolic icon of a Unix mount point.
3628
 *
3629
 * Returns: (transfer full): a [iface@Gio.Icon]
3630
 * Since: 2.34
3631
 */
3632
GIcon *
3633
g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
3634
0
{
3635
0
  return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
3636
0
}
3637
3638
/**
3639
 * g_unix_mount_guess_can_eject:
3640
 * @mount_entry: a [struct@GioUnix.MountEntry]
3641
 * 
3642
 * Guesses whether a Unix mount entry can be ejected.
3643
 *
3644
 * Returns: true if @mount_entry is deemed to be ejectable; false otherwise
3645
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.guess_can_eject] instead.
3646
 */
3647
gboolean
3648
g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
3649
0
{
3650
0
  return g_unix_mount_entry_guess_can_eject (mount_entry);
3651
0
}
3652
3653
/**
3654
 * g_unix_mount_entry_guess_can_eject:
3655
 * @mount_entry: a [struct@GioUnix.MountEntry]
3656
 *
3657
 * Guesses whether a Unix mount entry can be ejected.
3658
 *
3659
 * Returns: true if @mount_entry is deemed to be ejectable; false otherwise
3660
 * Since: 2.84
3661
 */
3662
gboolean
3663
g_unix_mount_entry_guess_can_eject (GUnixMountEntry *mount_entry)
3664
0
{
3665
0
  GUnixMountType guessed_type;
3666
3667
0
  guessed_type = g_unix_mount_entry_guess_type (mount_entry);
3668
0
  if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
3669
0
      guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
3670
0
    return TRUE;
3671
3672
0
  return FALSE;
3673
0
}
3674
3675
/**
3676
 * g_unix_mount_guess_should_display:
3677
 * @mount_entry: a [struct@GioUnix.MountEntry]
3678
 * 
3679
 * Guesses whether a Unix mount entry should be displayed in the UI.
3680
 *
3681
 * Returns: true if @mount_entry is deemed to be displayable; false otherwise
3682
 * Deprecated: 2.84: Use [method@GioUnix.MountEntry.guess_should_display] instead.
3683
 */
3684
gboolean
3685
g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
3686
0
{
3687
0
  return g_unix_mount_entry_guess_should_display (mount_entry);
3688
0
}
3689
3690
/**
3691
 * g_unix_mount_entry_guess_should_display:
3692
 * @mount_entry: a [struct@GioUnix.MountEntry]
3693
 *
3694
 * Guesses whether a Unix mount entry should be displayed in the UI.
3695
 *
3696
 * Returns: true if @mount_entry is deemed to be displayable; false otherwise
3697
 * Since: 2.84
3698
 */
3699
gboolean
3700
g_unix_mount_entry_guess_should_display (GUnixMountEntry *mount_entry)
3701
0
{
3702
0
  const char *mount_path;
3703
0
  const gchar *user_name;
3704
0
  gsize user_name_len;
3705
3706
  /* Never display internal mountpoints */
3707
0
  if (g_unix_mount_entry_is_system_internal (mount_entry))
3708
0
    return FALSE;
3709
  
3710
  /* Only display things in /media (which are generally user mountable)
3711
     and home dir (fuse stuff) and /run/media/$USER */
3712
0
  mount_path = mount_entry->mount_path;
3713
0
  if (mount_path != NULL)
3714
0
    {
3715
0
      const gboolean running_as_root = (getuid () == 0);
3716
0
      gboolean is_in_runtime_dir = FALSE;
3717
3718
      /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
3719
0
      if (g_strstr_len (mount_path, -1, "/.") != NULL)
3720
0
        return FALSE;
3721
3722
      /* Check /run/media/$USER/. If running as root, display any mounts below
3723
       * /run/media/. */
3724
0
      if (running_as_root)
3725
0
        {
3726
0
          if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0)
3727
0
            is_in_runtime_dir = TRUE;
3728
0
        }
3729
0
      else
3730
0
        {
3731
0
          user_name = g_get_user_name ();
3732
0
          user_name_len = strlen (user_name);
3733
0
          if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0 &&
3734
0
              strncmp (mount_path + strlen ("/run/media/"), user_name, user_name_len) == 0 &&
3735
0
              mount_path[strlen ("/run/media/") + user_name_len] == '/')
3736
0
            is_in_runtime_dir = TRUE;
3737
0
        }
3738
3739
0
      if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
3740
0
        {
3741
0
          char *path;
3742
          /* Avoid displaying mounts that are not accessible to the user.
3743
           *
3744
           * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
3745
           * want to avoid g_access() for mount points which can potentially
3746
           * block or fail stat()'ing, such as network mounts.
3747
           */
3748
0
          path = g_path_get_dirname (mount_path);
3749
0
          if (g_str_has_prefix (path, "/media/"))
3750
0
            {
3751
0
              if (g_access (path, R_OK|X_OK) != 0) 
3752
0
                {
3753
0
                  g_free (path);
3754
0
                  return FALSE;
3755
0
                }
3756
0
            }
3757
0
          g_free (path);
3758
3759
0
          if (mount_entry->device_path && mount_entry->device_path[0] == '/')
3760
0
           {
3761
0
             struct stat st;
3762
0
             if (g_stat (mount_entry->device_path, &st) == 0 &&
3763
0
                 S_ISBLK(st.st_mode) &&
3764
0
                 g_access (mount_path, R_OK|X_OK) != 0)
3765
0
               return FALSE;
3766
0
           }
3767
0
          return TRUE;
3768
0
        }
3769
      
3770
0
      if (g_str_has_prefix (mount_path, g_get_home_dir ()) && 
3771
0
          mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
3772
0
        return TRUE;
3773
0
    }
3774
  
3775
0
  return FALSE;
3776
0
}
3777
3778
/**
3779
 * g_unix_mount_point_guess_can_eject:
3780
 * @mount_point: a [struct@GioUnix.MountPoint]
3781
 * 
3782
 * Guesses whether a Unix mount point can be ejected.
3783
 *
3784
 * Returns: true if @mount_point is deemed to be ejectable; false otherwise
3785
 */
3786
gboolean
3787
g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
3788
0
{
3789
0
  GUnixMountType guessed_type;
3790
3791
0
  guessed_type = g_unix_mount_point_guess_type (mount_point);
3792
0
  if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
3793
0
      guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
3794
0
    return TRUE;
3795
3796
0
  return FALSE;
3797
0
}
3798
3799
/* Utility functions {{{1 */
3800
3801
#ifdef HAVE_MNTENT_H
3802
/* borrowed from gtk/gtkfilesystemunix.c in GTK on 02/23/2006 */
3803
static void
3804
_canonicalize_filename (gchar *filename)
3805
0
{
3806
0
  gchar *p, *q;
3807
0
  gboolean last_was_slash = FALSE;
3808
  
3809
0
  p = filename;
3810
0
  q = filename;
3811
  
3812
0
  while (*p)
3813
0
    {
3814
0
      if (*p == G_DIR_SEPARATOR)
3815
0
        {
3816
0
          if (!last_was_slash)
3817
0
            *q++ = G_DIR_SEPARATOR;
3818
          
3819
0
          last_was_slash = TRUE;
3820
0
        }
3821
0
      else
3822
0
        {
3823
0
          if (last_was_slash && *p == '.')
3824
0
            {
3825
0
              if (*(p + 1) == G_DIR_SEPARATOR ||
3826
0
                  *(p + 1) == '\0')
3827
0
                {
3828
0
                  if (*(p + 1) == '\0')
3829
0
                    break;
3830
                  
3831
0
                  p += 1;
3832
0
                }
3833
0
              else if (*(p + 1) == '.' &&
3834
0
                       (*(p + 2) == G_DIR_SEPARATOR ||
3835
0
                        *(p + 2) == '\0'))
3836
0
                {
3837
0
                  if (q > filename + 1)
3838
0
                    {
3839
0
                      q--;
3840
0
                      while (q > filename + 1 &&
3841
0
                             *(q - 1) != G_DIR_SEPARATOR)
3842
0
                        q--;
3843
0
                    }
3844
                  
3845
0
                  if (*(p + 2) == '\0')
3846
0
                    break;
3847
                  
3848
0
                  p += 2;
3849
0
                }
3850
0
              else
3851
0
                {
3852
0
                  *q++ = *p;
3853
0
                  last_was_slash = FALSE;
3854
0
                }
3855
0
            }
3856
0
          else
3857
0
            {
3858
0
              *q++ = *p;
3859
0
              last_was_slash = FALSE;
3860
0
            }
3861
0
        }
3862
      
3863
0
      p++;
3864
0
    }
3865
  
3866
0
  if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
3867
0
    q--;
3868
  
3869
0
  *q = '\0';
3870
0
}
3871
3872
static char *
3873
_resolve_symlink (const char *file)
3874
0
{
3875
0
  GError *error;
3876
0
  char *dir;
3877
0
  char *link;
3878
0
  char *f;
3879
0
  char *f1;
3880
  
3881
0
  f = g_strdup (file);
3882
  
3883
0
  while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) 
3884
0
    {
3885
0
      link = g_file_read_link (f, &error);
3886
0
      if (link == NULL) 
3887
0
        {
3888
0
          g_error_free (error);
3889
0
          g_free (f);
3890
0
          f = NULL;
3891
0
          goto out;
3892
0
        }
3893
    
3894
0
      dir = g_path_get_dirname (f);
3895
0
      f1 = g_strdup_printf ("%s/%s", dir, link);
3896
0
      g_free (dir);
3897
0
      g_free (link);
3898
0
      g_free (f);
3899
0
      f = f1;
3900
0
    }
3901
  
3902
0
 out:
3903
0
  if (f != NULL)
3904
0
    _canonicalize_filename (f);
3905
0
  return f;
3906
0
}
3907
3908
static const char *
3909
_resolve_dev_root (void)
3910
0
{
3911
0
  static gboolean have_real_dev_root = FALSE;
3912
0
  static char real_dev_root[256];
3913
0
  struct stat statbuf;
3914
  
3915
  /* see if it's cached already */
3916
0
  if (have_real_dev_root)
3917
0
    goto found;
3918
  
3919
  /* otherwise we're going to find it right away.. */
3920
0
  have_real_dev_root = TRUE;
3921
  
3922
0
  if (stat ("/dev/root", &statbuf) == 0) 
3923
0
    {
3924
0
      if (! S_ISLNK (statbuf.st_mode)) 
3925
0
        {
3926
0
          dev_t root_dev = statbuf.st_dev;
3927
0
          FILE *f;
3928
      
3929
          /* see if device with similar major:minor as /dev/root is mention
3930
           * in /etc/mtab (it usually is) 
3931
           */
3932
0
          f = g_fopen ("/etc/mtab", "re");
3933
0
          if (f != NULL) 
3934
0
            {
3935
0
        struct mntent *entp;
3936
0
#ifdef HAVE_GETMNTENT_R        
3937
0
              struct mntent ent;
3938
0
              char buf[1024];
3939
0
              while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL) 
3940
0
                {
3941
#else
3942
        G_LOCK (getmntent);
3943
        while ((entp = getmntent (f)) != NULL) 
3944
                { 
3945
#endif          
3946
0
                  if (stat (entp->mnt_fsname, &statbuf) == 0 &&
3947
0
                      statbuf.st_dev == root_dev) 
3948
0
                    {
3949
0
                      strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
3950
0
                      real_dev_root[sizeof (real_dev_root) - 1] = '\0';
3951
0
                      fclose (f);
3952
0
                      goto found;
3953
0
                    }
3954
0
                }
3955
3956
              /* endmntent() calls fclose() for us, but scan-build doesn’t know that */
3957
0
#if !G_ANALYZER_ANALYZING
3958
0
              endmntent (f);
3959
#else
3960
              fclose (f);
3961
#endif
3962
3963
#ifndef HAVE_GETMNTENT_R
3964
        G_UNLOCK (getmntent);
3965
#endif
3966
0
            }                                        
3967
      
3968
          /* no, that didn't work.. next we could scan /dev ... but I digress.. */
3969
      
3970
0
        } 
3971
0
       else 
3972
0
        {
3973
0
          char *resolved;
3974
0
          resolved = _resolve_symlink ("/dev/root");
3975
0
          if (resolved != NULL)
3976
0
            {
3977
0
              strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
3978
0
              real_dev_root[sizeof (real_dev_root) - 1] = '\0';
3979
0
              g_free (resolved);
3980
0
              goto found;
3981
0
            }
3982
0
        }
3983
0
    }
3984
  
3985
  /* bah sucks.. */
3986
0
  strcpy (real_dev_root, "/dev/root");
3987
  
3988
0
found:
3989
0
  return real_dev_root;
3990
0
}
3991
#endif
3992
3993
/* Epilogue {{{1 */
3994
/* vim:set foldmethod=marker: */