Coverage Report

Created: 2025-07-23 08:13

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