Coverage Report

Created: 2025-09-27 07:50

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