Coverage Report

Created: 2025-11-16 06:24

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