1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3/* GIO - GLib Input, Output and Streaming Library
4 *
5 * Copyright (C) 2006-2007 Red Hat, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23/* Prologue {{{1 */
24
25#include "config.h"
26
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <sys/wait.h>
30#ifndef HAVE_SYSCTLBYNAME
31#ifdef HAVE_SYS_PARAM_H
32#include <sys/param.h>
33#endif
34#endif
35#ifdef HAVE_POLL
36#include <poll.h>
37#endif
38#include <stdio.h>
39#include <unistd.h>
40#include <sys/time.h>
41#include <errno.h>
42#include <string.h>
43#include <signal.h>
44#include <gstdio.h>
45#include <dirent.h>
46
47#if HAVE_SYS_STATFS_H
48#include <sys/statfs.h>
49#endif
50#if HAVE_SYS_STATVFS_H
51#include <sys/statvfs.h>
52#endif
53#if HAVE_SYS_VFS_H
54#include <sys/vfs.h>
55#elif HAVE_SYS_MOUNT_H
56#if HAVE_SYS_PARAM_H
57#include <sys/param.h>
58#endif
59#include <sys/mount.h>
60#endif
61
62#ifndef O_BINARY
63#define O_BINARY 0
64#endif
65
66#include "gunixmounts.h"
67#include "gfile.h"
68#include "gfilemonitor.h"
69#include "glibintl.h"
70#include "glocalfile.h"
71#include "gthemedicon.h"
72#include "gcontextspecificgroup.h"
73
74
75#ifdef HAVE_MNTENT_H
76static const char *_resolve_dev_root (void);
77#endif
78
79/**
80 * SECTION:gunixmounts
81 * @include: gio/gunixmounts.h
82 * @short_description: UNIX mounts
83 *
84 * Routines for managing mounted UNIX mount points and paths.
85 *
86 * Note that `<gio/gunixmounts.h>` belongs to the UNIX-specific GIO
87 * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
88 * file when using it.
89 */
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 **/
109typedef 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
125struct _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
135G_DEFINE_BOXED_TYPE (GUnixMountEntry, g_unix_mount_entry,
136 g_unix_mount_copy, g_unix_mount_free)
137
138struct _GUnixMountPoint {
139 char *mount_path;
140 char *device_path;
141 char *filesystem_type;
142 char *options;
143 gboolean is_read_only;
144 gboolean is_user_mountable;
145 gboolean is_loopback;
146};
147
148G_DEFINE_BOXED_TYPE (GUnixMountPoint, g_unix_mount_point,
149 g_unix_mount_point_copy, g_unix_mount_point_free)
150
151static GList *_g_get_unix_mounts (void);
152static GList *_g_get_unix_mount_points (void);
153static gboolean proc_mounts_watch_is_running (void);
154
155G_LOCK_DEFINE_STATIC (proc_mounts_source);
156
157/* Protected by proc_mounts_source lock */
158static guint64 mount_poller_time = 0;
159static GSource *proc_mounts_watch_source;
160
161#ifdef HAVE_SYS_MNTTAB_H
162#define MNTOPT_RO "ro"
163#endif
164
165#ifdef HAVE_MNTENT_H
166#include <mntent.h>
167#ifdef HAVE_LIBMOUNT
168#include <libmount.h>
169#endif
170#elif defined (HAVE_SYS_MNTTAB_H)
171#include <sys/mnttab.h>
172#if defined(__sun) && !defined(mnt_opts)
173#define mnt_opts mnt_mntopts
174#endif
175#endif
176
177#ifdef HAVE_SYS_VFSTAB_H
178#include <sys/vfstab.h>
179#endif
180
181#if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
182#include <sys/mntctl.h>
183#include <sys/vfs.h>
184#include <sys/vmount.h>
185#include <fshelp.h>
186#endif
187
188#if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
189#include <sys/param.h>
190#include <sys/ucred.h>
191#include <sys/mount.h>
192#include <fstab.h>
193#ifdef HAVE_SYS_SYSCTL_H
194#include <sys/sysctl.h>
195#endif
196#endif
197
198#ifndef HAVE_SETMNTENT
199#define setmntent(f,m) fopen(f,m)
200#endif
201#ifndef HAVE_ENDMNTENT
202#define endmntent(f) fclose(f)
203#endif
204
205static gboolean
206is_in (const char *value, const char *set[])
207{
208 int i;
209 for (i = 0; set[i] != NULL; i++)
210 {
211 if (strcmp (s1: set[i], s2: value) == 0)
212 return TRUE;
213 }
214 return FALSE;
215}
216
217/**
218 * g_unix_is_mount_path_system_internal:
219 * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr`
220 *
221 * Determines if @mount_path is considered an implementation of the
222 * OS. This is primarily used for hiding mountable and mounted volumes
223 * that only are used in the OS and has little to no relevance to the
224 * casual user.
225 *
226 * Returns: %TRUE if @mount_path is considered an implementation detail
227 * of the OS.
228 **/
229gboolean
230g_unix_is_mount_path_system_internal (const char *mount_path)
231{
232 const char *ignore_mountpoints[] = {
233 /* Includes all FHS 2.3 toplevel dirs and other specialized
234 * directories that we want to hide from the user.
235 */
236 "/", /* we already have "Filesystem root" in Nautilus */
237 "/bin",
238 "/boot",
239 "/compat/linux/proc",
240 "/compat/linux/sys",
241 "/dev",
242 "/etc",
243 "/home",
244 "/lib",
245 "/lib64",
246 "/libexec",
247 "/live/cow",
248 "/live/image",
249 "/media",
250 "/mnt",
251 "/opt",
252 "/rescue",
253 "/root",
254 "/sbin",
255 "/srv",
256 "/tmp",
257 "/usr",
258 "/usr/X11R6",
259 "/usr/local",
260 "/usr/obj",
261 "/usr/ports",
262 "/usr/src",
263 "/usr/xobj",
264 "/var",
265 "/var/crash",
266 "/var/local",
267 "/var/log",
268 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
269 "/var/mail",
270 "/var/run",
271 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
272 "/proc",
273 "/sbin",
274 "/net",
275 "/sys",
276 NULL
277 };
278
279 if (is_in (value: mount_path, set: ignore_mountpoints))
280 return TRUE;
281
282 if (g_str_has_prefix (str: mount_path, prefix: "/dev/") ||
283 g_str_has_prefix (str: mount_path, prefix: "/proc/") ||
284 g_str_has_prefix (str: mount_path, prefix: "/sys/"))
285 return TRUE;
286
287 if (g_str_has_suffix (str: mount_path, suffix: "/.gvfs"))
288 return TRUE;
289
290 return FALSE;
291}
292
293/**
294 * g_unix_is_system_fs_type:
295 * @fs_type: a file system type, e.g. `procfs` or `tmpfs`
296 *
297 * Determines if @fs_type is considered a type of file system which is only
298 * used in implementation of the OS. This is primarily used for hiding
299 * mounted volumes that are intended as APIs for programs to read, and system
300 * administrators at a shell; rather than something that should, for example,
301 * appear in a GUI. For example, the Linux `/proc` filesystem.
302 *
303 * The list of file system types considered ‘system’ ones may change over time.
304 *
305 * Returns: %TRUE if @fs_type is considered an implementation detail of the OS.
306 * Since: 2.56
307 */
308gboolean
309g_unix_is_system_fs_type (const char *fs_type)
310{
311 const char *ignore_fs[] = {
312 "adfs",
313 "afs",
314 "auto",
315 "autofs",
316 "autofs4",
317 "cgroup",
318 "configfs",
319 "cxfs",
320 "debugfs",
321 "devfs",
322 "devpts",
323 "devtmpfs",
324 "ecryptfs",
325 "fdescfs",
326 "fusectl",
327 "gfs",
328 "gfs2",
329 "gpfs",
330 "hugetlbfs",
331 "kernfs",
332 "linprocfs",
333 "linsysfs",
334 "lustre",
335 "lustre_lite",
336 "mfs",
337 "mqueue",
338 "ncpfs",
339 "nfsd",
340 "nullfs",
341 "ocfs2",
342 "overlay",
343 "proc",
344 "procfs",
345 "pstore",
346 "ptyfs",
347 "rootfs",
348 "rpc_pipefs",
349 "securityfs",
350 "selinuxfs",
351 "sysfs",
352 "tmpfs",
353 "usbfs",
354 NULL
355 };
356
357 g_return_val_if_fail (fs_type != NULL && *fs_type != '\0', FALSE);
358
359 return is_in (value: fs_type, set: ignore_fs);
360}
361
362/**
363 * g_unix_is_system_device_path:
364 * @device_path: a device path, e.g. `/dev/loop0` or `nfsd`
365 *
366 * Determines if @device_path is considered a block device path which is only
367 * used in implementation of the OS. This is primarily used for hiding
368 * mounted volumes that are intended as APIs for programs to read, and system
369 * administrators at a shell; rather than something that should, for example,
370 * appear in a GUI. For example, the Linux `/proc` filesystem.
371 *
372 * The list of device paths considered ‘system’ ones may change over time.
373 *
374 * Returns: %TRUE if @device_path is considered an implementation detail of
375 * the OS.
376 * Since: 2.56
377 */
378gboolean
379g_unix_is_system_device_path (const char *device_path)
380{
381 const char *ignore_devices[] = {
382 "none",
383 "sunrpc",
384 "devpts",
385 "nfsd",
386 "/dev/loop",
387 "/dev/vn",
388 NULL
389 };
390
391 g_return_val_if_fail (device_path != NULL && *device_path != '\0', FALSE);
392
393 return is_in (value: device_path, set: ignore_devices);
394}
395
396static gboolean
397guess_system_internal (const char *mountpoint,
398 const char *fs,
399 const char *device,
400 const char *root)
401{
402 if (g_unix_is_system_fs_type (fs_type: fs))
403 return TRUE;
404
405 if (g_unix_is_system_device_path (device_path: device))
406 return TRUE;
407
408 if (g_unix_is_mount_path_system_internal (mount_path: mountpoint))
409 return TRUE;
410
411 /* It is not possible to reliably detect mounts which were created by bind
412 * operation. mntent-based _g_get_unix_mounts() implementation blindly skips
413 * mounts with a device path that is repeated (e.g. mounts created by bind
414 * operation, btrfs subvolumes). This usually chooses the most important
415 * mounts (i.e. which points to the root of filesystem), but it doesn't work
416 * in all cases and also it is not ideal that those mounts are completely
417 * ignored (e.g. x-gvfs-show doesn't work for them, trash backend can't handle
418 * files on btrfs subvolumes). libmount-based _g_get_unix_mounts()
419 * implementation provides a root path. So there is no need to completely
420 * ignore those mounts, because e.g. our volume monitors can use the root path
421 * to not mengle those mounts with the "regular" mounts (i.e. which points to
422 * the root). But because those mounts usually just duplicate other mounts and
423 * are completely ignored with mntend-based implementation, let's mark them as
424 * system internal. Given the different approaches it doesn't mean that all
425 * mounts which were ignored will be system internal now, but this should work
426 * in most cases. For more info, see g_unix_mount_get_root_path() annotation,
427 * comment in mntent-based _g_get_unix_mounts() implementation and the
428 * https://gitlab.gnome.org/GNOME/glib/issues/1271 issue.
429 */
430 if (root != NULL && g_strcmp0 (str1: root, str2: "/") != 0)
431 return TRUE;
432
433 return FALSE;
434}
435
436/* GUnixMounts (ie: mtab) implementations {{{1 */
437
438static GUnixMountEntry *
439create_unix_mount_entry (const char *device_path,
440 const char *mount_path,
441 const char *root_path,
442 const char *filesystem_type,
443 const char *options,
444 gboolean is_read_only)
445{
446 GUnixMountEntry *mount_entry = NULL;
447
448 mount_entry = g_new0 (GUnixMountEntry, 1);
449 mount_entry->device_path = g_strdup (str: device_path);
450 mount_entry->mount_path = g_strdup (str: mount_path);
451 mount_entry->root_path = g_strdup (str: root_path);
452 mount_entry->filesystem_type = g_strdup (str: filesystem_type);
453 mount_entry->options = g_strdup (str: options);
454 mount_entry->is_read_only = is_read_only;
455
456 mount_entry->is_system_internal =
457 guess_system_internal (mountpoint: mount_entry->mount_path,
458 fs: mount_entry->filesystem_type,
459 device: mount_entry->device_path,
460 root: mount_entry->root_path);
461
462 return mount_entry;
463}
464
465static GUnixMountPoint *
466create_unix_mount_point (const char *device_path,
467 const char *mount_path,
468 const char *filesystem_type,
469 const char *options,
470 gboolean is_read_only,
471 gboolean is_user_mountable,
472 gboolean is_loopback)
473{
474 GUnixMountPoint *mount_point = NULL;
475
476 mount_point = g_new0 (GUnixMountPoint, 1);
477 mount_point->device_path = g_strdup (str: device_path);
478 mount_point->mount_path = g_strdup (str: mount_path);
479 mount_point->filesystem_type = g_strdup (str: filesystem_type);
480 mount_point->options = g_strdup (str: options);
481 mount_point->is_read_only = is_read_only;
482 mount_point->is_user_mountable = is_user_mountable;
483 mount_point->is_loopback = is_loopback;
484
485 return mount_point;
486}
487
488/* mntent.h (Linux, GNU, NSS) {{{2 */
489#ifdef HAVE_MNTENT_H
490
491#ifdef HAVE_LIBMOUNT
492
493/* For documentation on /proc/self/mountinfo see
494 * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
495 */
496#define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
497
498static GList *
499_g_get_unix_mounts (void)
500{
501 struct libmnt_table *table = NULL;
502 struct libmnt_iter* iter = NULL;
503 struct libmnt_fs *fs = NULL;
504 GUnixMountEntry *mount_entry = NULL;
505 GList *return_list = NULL;
506
507 table = mnt_new_table ();
508 if (mnt_table_parse_mtab (tb: table, NULL) < 0)
509 goto out;
510
511 iter = mnt_new_iter (direction: MNT_ITER_FORWARD);
512 while (mnt_table_next_fs (tb: table, itr: iter, fs: &fs) == 0)
513 {
514 const char *device_path = NULL;
515 char *mount_options = NULL;
516 unsigned long mount_flags = 0;
517 gboolean is_read_only = FALSE;
518
519 device_path = mnt_fs_get_source (fs);
520 if (g_strcmp0 (str1: device_path, str2: "/dev/root") == 0)
521 device_path = _resolve_dev_root ();
522
523 mount_options = mnt_fs_strdup_options (fs);
524 if (mount_options)
525 {
526 mnt_optstr_get_flags (optstr: mount_options, flags: &mount_flags, map: mnt_get_builtin_optmap (id: MNT_LINUX_MAP));
527 g_free (mem: mount_options);
528 }
529 is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
530
531 mount_entry = create_unix_mount_entry (device_path,
532 mount_path: mnt_fs_get_target (fs),
533 root_path: mnt_fs_get_root (fs),
534 filesystem_type: mnt_fs_get_fstype (fs),
535 options: mnt_fs_get_options (fs),
536 is_read_only);
537
538 return_list = g_list_prepend (list: return_list, data: mount_entry);
539 }
540 mnt_free_iter (itr: iter);
541
542 out:
543 mnt_free_table (tb: table);
544
545 return g_list_reverse (list: return_list);
546}
547
548#else
549
550static const char *
551get_mtab_read_file (void)
552{
553#ifdef _PATH_MOUNTED
554# ifdef __linux__
555 return "/proc/mounts";
556# else
557 return _PATH_MOUNTED;
558# endif
559#else
560 return "/etc/mtab";
561#endif
562}
563
564#ifndef HAVE_GETMNTENT_R
565G_LOCK_DEFINE_STATIC(getmntent);
566#endif
567
568static GList *
569_g_get_unix_mounts (void)
570{
571#ifdef HAVE_GETMNTENT_R
572 struct mntent ent;
573 char buf[1024];
574#endif
575 struct mntent *mntent;
576 FILE *file;
577 const char *read_file;
578 GUnixMountEntry *mount_entry;
579 GHashTable *mounts_hash;
580 GList *return_list;
581
582 read_file = get_mtab_read_file ();
583
584 file = setmntent (read_file, "r");
585 if (file == NULL)
586 return NULL;
587
588 return_list = NULL;
589
590 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
591
592#ifdef HAVE_GETMNTENT_R
593 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
594#else
595 G_LOCK (getmntent);
596 while ((mntent = getmntent (file)) != NULL)
597#endif
598 {
599 const char *device_path = NULL;
600 gboolean is_read_only = FALSE;
601
602 /* ignore any mnt_fsname that is repeated and begins with a '/'
603 *
604 * We do this to avoid being fooled by --bind mounts, since
605 * these have the same device as the location they bind to.
606 * It's not an ideal solution to the problem, but it's likely that
607 * the most important mountpoint is first and the --bind ones after
608 * that aren't as important. So it should work.
609 *
610 * The '/' is to handle procfs, tmpfs and other no device mounts.
611 */
612 if (mntent->mnt_fsname != NULL &&
613 mntent->mnt_fsname[0] == '/' &&
614 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
615 continue;
616
617 if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
618 device_path = _resolve_dev_root ();
619 else
620 device_path = mntent->mnt_fsname;
621
622#if defined (HAVE_HASMNTOPT)
623 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
624 is_read_only = TRUE;
625#endif
626
627 mount_entry = create_unix_mount_entry (device_path,
628 mntent->mnt_dir,
629 NULL,
630 mntent->mnt_type,
631 mntent->mnt_opts,
632 is_read_only);
633
634 g_hash_table_insert (mounts_hash,
635 mount_entry->device_path,
636 mount_entry->device_path);
637
638 return_list = g_list_prepend (return_list, mount_entry);
639 }
640 g_hash_table_destroy (mounts_hash);
641
642 endmntent (file);
643
644#ifndef HAVE_GETMNTENT_R
645 G_UNLOCK (getmntent);
646#endif
647
648 return g_list_reverse (return_list);
649}
650
651#endif /* HAVE_LIBMOUNT */
652
653static const char *
654get_mtab_monitor_file (void)
655{
656 static const char *mountinfo_path = NULL;
657#ifdef HAVE_LIBMOUNT
658 struct stat buf;
659#endif
660
661 if (mountinfo_path != NULL)
662 return mountinfo_path;
663
664#ifdef HAVE_LIBMOUNT
665 /* The mtab file is still used by some distros, so it has to be monitored in
666 * order to avoid races between g_unix_mounts_get and "mounts-changed" signal:
667 * https://bugzilla.gnome.org/show_bug.cgi?id=782814
668 */
669 if (mnt_has_regular_mtab (mtab: &mountinfo_path, NULL))
670 {
671 return mountinfo_path;
672 }
673
674 if (stat (PROC_MOUNTINFO_PATH, buf: &buf) == 0)
675 {
676 mountinfo_path = PROC_MOUNTINFO_PATH;
677 return mountinfo_path;
678 }
679#endif
680
681#ifdef _PATH_MOUNTED
682# ifdef __linux__
683 mountinfo_path = "/proc/mounts";
684# else
685 mountinfo_path = _PATH_MOUNTED;
686# endif
687#else
688 mountinfo_path = "/etc/mtab";
689#endif
690
691 return mountinfo_path;
692}
693
694/* mnttab.h {{{2 */
695#elif defined (HAVE_SYS_MNTTAB_H)
696
697G_LOCK_DEFINE_STATIC(getmntent);
698
699static const char *
700get_mtab_read_file (void)
701{
702#ifdef _PATH_MOUNTED
703 return _PATH_MOUNTED;
704#else
705 return "/etc/mnttab";
706#endif
707}
708
709static const char *
710get_mtab_monitor_file (void)
711{
712 return get_mtab_read_file ();
713}
714
715static GList *
716_g_get_unix_mounts (void)
717{
718 struct mnttab mntent;
719 FILE *file;
720 const char *read_file;
721 GUnixMountEntry *mount_entry;
722 GList *return_list;
723
724 read_file = get_mtab_read_file ();
725
726 file = setmntent (read_file, "r");
727 if (file == NULL)
728 return NULL;
729
730 return_list = NULL;
731
732 G_LOCK (getmntent);
733 while (! getmntent (file, &mntent))
734 {
735 gboolean is_read_only = FALSE;
736
737#if defined (HAVE_HASMNTOPT)
738 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
739 is_read_only = TRUE;
740#endif
741
742 mount_entry = create_unix_mount_entry (mntent.mnt_special,
743 mntent.mnt_mountp,
744 NULL,
745 mntent.mnt_fstype,
746 mntent.mnt_opts,
747 is_read_only);
748
749 return_list = g_list_prepend (return_list, mount_entry);
750 }
751
752 endmntent (file);
753
754 G_UNLOCK (getmntent);
755
756 return g_list_reverse (return_list);
757}
758
759/* mntctl.h (AIX) {{{2 */
760#elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
761
762static const char *
763get_mtab_monitor_file (void)
764{
765 return NULL;
766}
767
768static GList *
769_g_get_unix_mounts (void)
770{
771 struct vfs_ent *fs_info;
772 struct vmount *vmount_info;
773 int vmount_number;
774 unsigned int vmount_size;
775 int current;
776 GList *return_list;
777
778 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
779 {
780 g_warning ("Unable to know the number of mounted volumes");
781
782 return NULL;
783 }
784
785 vmount_info = (struct vmount*)g_malloc (vmount_size);
786
787 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
788
789 if (vmount_info->vmt_revision != VMT_REVISION)
790 g_warning ("Bad vmount structure revision number, want %d, got %d", VMT_REVISION, vmount_info->vmt_revision);
791
792 if (vmount_number < 0)
793 {
794 g_warning ("Unable to recover mounted volumes information");
795
796 g_free (vmount_info);
797 return NULL;
798 }
799
800 return_list = NULL;
801 while (vmount_number > 0)
802 {
803 gboolean is_read_only = FALSE;
804
805 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
806
807 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
808 is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
809
810 mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
811 vmt2dataptr (vmount_info, VMT_STUB),
812 NULL,
813 fs_info == NULL ? "unknown" : fs_info->vfsent_name,
814 NULL,
815 is_read_only);
816
817 return_list = g_list_prepend (return_list, mount_entry);
818
819 vmount_info = (struct vmount *)( (char*)vmount_info
820 + vmount_info->vmt_length);
821 vmount_number--;
822 }
823
824 g_free (vmount_info);
825
826 return g_list_reverse (return_list);
827}
828
829/* sys/mount.h {{{2 */
830#elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
831
832static const char *
833get_mtab_monitor_file (void)
834{
835 return NULL;
836}
837
838static GList *
839_g_get_unix_mounts (void)
840{
841#if defined(USE_STATVFS)
842 struct statvfs *mntent = NULL;
843#elif defined(USE_STATFS)
844 struct statfs *mntent = NULL;
845#else
846 #error statfs juggling failed
847#endif
848 size_t bufsize;
849 int num_mounts, i;
850 GUnixMountEntry *mount_entry;
851 GList *return_list;
852
853 /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
854#if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
855 num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
856#elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
857 num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
858#endif
859 if (num_mounts == -1)
860 return NULL;
861
862 bufsize = num_mounts * sizeof (*mntent);
863 mntent = g_malloc (bufsize);
864#if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
865 num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
866#elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
867 num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
868#endif
869 if (num_mounts == -1)
870 return NULL;
871
872 return_list = NULL;
873
874 for (i = 0; i < num_mounts; i++)
875 {
876 gboolean is_read_only = FALSE;
877
878#if defined(USE_STATVFS)
879 if (mntent[i].f_flag & ST_RDONLY)
880#elif defined(USE_STATFS)
881 if (mntent[i].f_flags & MNT_RDONLY)
882#else
883 #error statfs juggling failed
884#endif
885 is_read_only = TRUE;
886
887 mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
888 mntent[i].f_mntonname,
889 NULL,
890 mntent[i].f_fstypename,
891 NULL,
892 is_read_only);
893
894 return_list = g_list_prepend (return_list, mount_entry);
895 }
896
897 g_free (mntent);
898
899 return g_list_reverse (return_list);
900}
901
902/* Interix {{{2 */
903#elif defined(__INTERIX)
904
905static const char *
906get_mtab_monitor_file (void)
907{
908 return NULL;
909}
910
911static GList *
912_g_get_unix_mounts (void)
913{
914 DIR *dirp;
915 GList* return_list = NULL;
916 char filename[9 + NAME_MAX];
917
918 dirp = opendir ("/dev/fs");
919 if (!dirp)
920 {
921 g_warning ("unable to read /dev/fs!");
922 return NULL;
923 }
924
925 while (1)
926 {
927 struct statvfs statbuf;
928 struct dirent entry;
929 struct dirent* result;
930
931 if (readdir_r (dirp, &entry, &result) || result == NULL)
932 break;
933
934 strcpy (filename, "/dev/fs/");
935 strcat (filename, entry.d_name);
936
937 if (statvfs (filename, &statbuf) == 0)
938 {
939 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
940
941 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
942 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
943 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
944
945 if (statbuf.f_flag & ST_RDONLY)
946 mount_entry->is_read_only = TRUE;
947
948 return_list = g_list_prepend(return_list, mount_entry);
949 }
950 }
951
952 return_list = g_list_reverse (return_list);
953
954 closedir (dirp);
955
956 return return_list;
957}
958
959/* QNX {{{2 */
960#elif defined (HAVE_QNX)
961
962static char *
963get_mtab_monitor_file (void)
964{
965 /* TODO: Not implemented */
966 return NULL;
967}
968
969static GList *
970_g_get_unix_mounts (void)
971{
972 /* TODO: Not implemented */
973 return NULL;
974}
975
976/* Common code {{{2 */
977#else
978#error No _g_get_unix_mounts() implementation for system
979#endif
980
981/* GUnixMountPoints (ie: fstab) implementations {{{1 */
982
983/* _g_get_unix_mount_points():
984 * read the fstab.
985 * don't return swap and ignore mounts.
986 */
987
988static char *
989get_fstab_file (void)
990{
991#ifdef HAVE_LIBMOUNT
992 return (char *) mnt_get_fstab_path ();
993#else
994#if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
995 /* AIX */
996 return "/etc/filesystems";
997#elif defined(_PATH_MNTTAB)
998 return _PATH_MNTTAB;
999#elif defined(VFSTAB)
1000 return VFSTAB;
1001#else
1002 return "/etc/fstab";
1003#endif
1004#endif
1005}
1006
1007/* mntent.h (Linux, GNU, NSS) {{{2 */
1008#ifdef HAVE_MNTENT_H
1009
1010#ifdef HAVE_LIBMOUNT
1011
1012static GList *
1013_g_get_unix_mount_points (void)
1014{
1015 struct libmnt_table *table = NULL;
1016 struct libmnt_iter* iter = NULL;
1017 struct libmnt_fs *fs = NULL;
1018 GUnixMountPoint *mount_point = NULL;
1019 GList *return_list = NULL;
1020
1021 table = mnt_new_table ();
1022 if (mnt_table_parse_fstab (tb: table, NULL) < 0)
1023 goto out;
1024
1025 iter = mnt_new_iter (direction: MNT_ITER_FORWARD);
1026 while (mnt_table_next_fs (tb: table, itr: iter, fs: &fs) == 0)
1027 {
1028 const char *device_path = NULL;
1029 const char *mount_path = NULL;
1030 const char *mount_fstype = NULL;
1031 char *mount_options = NULL;
1032 gboolean is_read_only = FALSE;
1033 gboolean is_user_mountable = FALSE;
1034 gboolean is_loopback = FALSE;
1035
1036 mount_path = mnt_fs_get_target (fs);
1037 if ((strcmp (s1: mount_path, s2: "ignore") == 0) ||
1038 (strcmp (s1: mount_path, s2: "swap") == 0) ||
1039 (strcmp (s1: mount_path, s2: "none") == 0))
1040 continue;
1041
1042 mount_fstype = mnt_fs_get_fstype (fs);
1043 mount_options = mnt_fs_strdup_options (fs);
1044 if (mount_options)
1045 {
1046 unsigned long mount_flags = 0;
1047 unsigned long userspace_flags = 0;
1048
1049 mnt_optstr_get_flags (optstr: mount_options, flags: &mount_flags, map: mnt_get_builtin_optmap (id: MNT_LINUX_MAP));
1050 mnt_optstr_get_flags (optstr: mount_options, flags: &userspace_flags, map: mnt_get_builtin_optmap (id: MNT_USERSPACE_MAP));
1051
1052 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1053 if (mount_flags & MS_BIND)
1054 {
1055 g_free (mem: mount_options);
1056 continue;
1057 }
1058
1059 is_read_only = (mount_flags & MS_RDONLY) != 0;
1060 is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
1061
1062 if ((mount_fstype != NULL && g_strcmp0 (str1: "supermount", str2: mount_fstype) == 0) ||
1063 ((userspace_flags & MNT_MS_USER) &&
1064 (g_strstr_len (haystack: mount_options, haystack_len: -1, needle: "user_xattr") == NULL)) ||
1065 (g_strstr_len (haystack: mount_options, haystack_len: -1, needle: "pamconsole") == NULL) ||
1066 (userspace_flags & MNT_MS_USERS) ||
1067 (userspace_flags & MNT_MS_OWNER))
1068 {
1069 is_user_mountable = TRUE;
1070 }
1071 }
1072
1073 device_path = mnt_fs_get_source (fs);
1074 if (g_strcmp0 (str1: device_path, str2: "/dev/root") == 0)
1075 device_path = _resolve_dev_root ();
1076
1077 mount_point = create_unix_mount_point (device_path,
1078 mount_path,
1079 filesystem_type: mount_fstype,
1080 options: mount_options,
1081 is_read_only,
1082 is_user_mountable,
1083 is_loopback);
1084 if (mount_options)
1085 g_free (mem: mount_options);
1086
1087 return_list = g_list_prepend (list: return_list, data: mount_point);
1088 }
1089 mnt_free_iter (itr: iter);
1090
1091 out:
1092 mnt_free_table (tb: table);
1093
1094 return g_list_reverse (list: return_list);
1095}
1096
1097#else
1098
1099static GList *
1100_g_get_unix_mount_points (void)
1101{
1102#ifdef HAVE_GETMNTENT_R
1103 struct mntent ent;
1104 char buf[1024];
1105#endif
1106 struct mntent *mntent;
1107 FILE *file;
1108 char *read_file;
1109 GUnixMountPoint *mount_point;
1110 GList *return_list;
1111
1112 read_file = get_fstab_file ();
1113
1114 file = setmntent (read_file, "r");
1115 if (file == NULL)
1116 return NULL;
1117
1118 return_list = NULL;
1119
1120#ifdef HAVE_GETMNTENT_R
1121 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
1122#else
1123 G_LOCK (getmntent);
1124 while ((mntent = getmntent (file)) != NULL)
1125#endif
1126 {
1127 const char *device_path = NULL;
1128 gboolean is_read_only = FALSE;
1129 gboolean is_user_mountable = FALSE;
1130 gboolean is_loopback = FALSE;
1131
1132 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
1133 (strcmp (mntent->mnt_dir, "swap") == 0) ||
1134 (strcmp (mntent->mnt_dir, "none") == 0))
1135 continue;
1136
1137#ifdef HAVE_HASMNTOPT
1138 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1139 if (hasmntopt (mntent, "bind"))
1140 continue;
1141#endif
1142
1143 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1144 device_path = _resolve_dev_root ();
1145 else
1146 device_path = mntent->mnt_fsname;
1147
1148#ifdef HAVE_HASMNTOPT
1149 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1150 is_read_only = TRUE;
1151
1152 if (hasmntopt (mntent, "loop") != NULL)
1153 is_loopback = TRUE;
1154
1155#endif
1156
1157 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1158#ifdef HAVE_HASMNTOPT
1159 || (hasmntopt (mntent, "user") != NULL
1160 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1161 || hasmntopt (mntent, "pamconsole") != NULL
1162 || hasmntopt (mntent, "users") != NULL
1163 || hasmntopt (mntent, "owner") != NULL
1164#endif
1165 )
1166 is_user_mountable = TRUE;
1167
1168 mount_point = create_unix_mount_point (device_path,
1169 mntent->mnt_dir,
1170 mntent->mnt_type,
1171 mntent->mnt_opts,
1172 is_read_only,
1173 is_user_mountable,
1174 is_loopback);
1175
1176 return_list = g_list_prepend (return_list, mount_point);
1177 }
1178
1179 endmntent (file);
1180
1181#ifndef HAVE_GETMNTENT_R
1182 G_UNLOCK (getmntent);
1183#endif
1184
1185 return g_list_reverse (return_list);
1186}
1187
1188#endif /* HAVE_LIBMOUNT */
1189
1190/* mnttab.h {{{2 */
1191#elif defined (HAVE_SYS_MNTTAB_H)
1192
1193static GList *
1194_g_get_unix_mount_points (void)
1195{
1196 struct mnttab mntent;
1197 FILE *file;
1198 char *read_file;
1199 GUnixMountPoint *mount_point;
1200 GList *return_list;
1201
1202 read_file = get_fstab_file ();
1203
1204 file = setmntent (read_file, "r");
1205 if (file == NULL)
1206 return NULL;
1207
1208 return_list = NULL;
1209
1210 G_LOCK (getmntent);
1211 while (! getmntent (file, &mntent))
1212 {
1213 gboolean is_read_only = FALSE;
1214 gboolean is_user_mountable = FALSE;
1215 gboolean is_loopback = FALSE;
1216
1217 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1218 (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1219 (strcmp (mntent.mnt_mountp, "none") == 0))
1220 continue;
1221
1222#ifdef HAVE_HASMNTOPT
1223 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1224 is_read_only = TRUE;
1225
1226 if (hasmntopt (&mntent, "lofs") != NULL)
1227 is_loopback = TRUE;
1228#endif
1229
1230 if ((mntent.mnt_fstype != NULL)
1231#ifdef HAVE_HASMNTOPT
1232 || (hasmntopt (&mntent, "user") != NULL
1233 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1234 || hasmntopt (&mntent, "pamconsole") != NULL
1235 || hasmntopt (&mntent, "users") != NULL
1236 || hasmntopt (&mntent, "owner") != NULL
1237#endif
1238 )
1239 is_user_mountable = TRUE;
1240
1241 mount_point = create_unix_mount_point (mntent.mnt_special,
1242 mntent.mnt_mountp,
1243 mntent.mnt_fstype,
1244 mntent.mnt_mntopts,
1245 is_read_only,
1246 is_user_mountable,
1247 is_loopback);
1248
1249 return_list = g_list_prepend (return_list, mount_point);
1250 }
1251
1252 endmntent (file);
1253 G_UNLOCK (getmntent);
1254
1255 return g_list_reverse (return_list);
1256}
1257
1258/* mntctl.h (AIX) {{{2 */
1259#elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1260
1261/* functions to parse /etc/filesystems on aix */
1262
1263/* read character, ignoring comments (begin with '*', end with '\n' */
1264static int
1265aix_fs_getc (FILE *fd)
1266{
1267 int c;
1268
1269 while ((c = getc (fd)) == '*')
1270 {
1271 while (((c = getc (fd)) != '\n') && (c != EOF))
1272 ;
1273 }
1274}
1275
1276/* eat all continuous spaces in a file */
1277static int
1278aix_fs_ignorespace (FILE *fd)
1279{
1280 int c;
1281
1282 while ((c = aix_fs_getc (fd)) != EOF)
1283 {
1284 if (!g_ascii_isspace (c))
1285 {
1286 ungetc (c,fd);
1287 return c;
1288 }
1289 }
1290
1291 return EOF;
1292}
1293
1294/* read one word from file */
1295static int
1296aix_fs_getword (FILE *fd,
1297 char *word)
1298{
1299 int c;
1300
1301 aix_fs_ignorespace (fd);
1302
1303 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1304 {
1305 if (c == '"')
1306 {
1307 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1308 *word++ = c;
1309 else
1310 *word++ = c;
1311 }
1312 }
1313 *word = 0;
1314
1315 return c;
1316}
1317
1318typedef struct {
1319 char mnt_mount[PATH_MAX];
1320 char mnt_special[PATH_MAX];
1321 char mnt_fstype[16];
1322 char mnt_options[128];
1323} AixMountTableEntry;
1324
1325/* read mount points properties */
1326static int
1327aix_fs_get (FILE *fd,
1328 AixMountTableEntry *prop)
1329{
1330 static char word[PATH_MAX] = { 0 };
1331 char value[PATH_MAX];
1332
1333 /* read stanza */
1334 if (word[0] == 0)
1335 {
1336 if (aix_fs_getword (fd, word) == EOF)
1337 return EOF;
1338 }
1339
1340 word[strlen(word) - 1] = 0;
1341 strcpy (prop->mnt_mount, word);
1342
1343 /* read attributes and value */
1344
1345 while (aix_fs_getword (fd, word) != EOF)
1346 {
1347 /* test if is attribute or new stanza */
1348 if (word[strlen(word) - 1] == ':')
1349 return 0;
1350
1351 /* read "=" */
1352 aix_fs_getword (fd, value);
1353
1354 /* read value */
1355 aix_fs_getword (fd, value);
1356
1357 if (strcmp (word, "dev") == 0)
1358 strcpy (prop->mnt_special, value);
1359 else if (strcmp (word, "vfs") == 0)
1360 strcpy (prop->mnt_fstype, value);
1361 else if (strcmp (word, "options") == 0)
1362 strcpy(prop->mnt_options, value);
1363 }
1364
1365 return 0;
1366}
1367
1368static GList *
1369_g_get_unix_mount_points (void)
1370{
1371 struct mntent *mntent;
1372 FILE *file;
1373 char *read_file;
1374 GUnixMountPoint *mount_point;
1375 AixMountTableEntry mntent;
1376 GList *return_list;
1377
1378 read_file = get_fstab_file ();
1379
1380 file = setmntent (read_file, "r");
1381 if (file == NULL)
1382 return NULL;
1383
1384 return_list = NULL;
1385
1386 while (!aix_fs_get (file, &mntent))
1387 {
1388 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1389 {
1390 mount_point = create_unix_mount_point (mntent.mnt_special,
1391 mntent.mnt_mount,
1392 mntent.mnt_fstype,
1393 mntent.mnt_options,
1394 TRUE,
1395 TRUE,
1396 FALSE);
1397
1398 return_list = g_list_prepend (return_list, mount_point);
1399 }
1400 }
1401
1402 endmntent (file);
1403
1404 return g_list_reverse (return_list);
1405}
1406
1407#elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1408
1409static GList *
1410_g_get_unix_mount_points (void)
1411{
1412 struct fstab *fstab = NULL;
1413 GUnixMountPoint *mount_point;
1414 GList *return_list;
1415#ifdef HAVE_SYS_SYSCTL_H
1416 int usermnt = 0;
1417 struct stat sb;
1418#endif
1419
1420 if (!setfsent ())
1421 return NULL;
1422
1423 return_list = NULL;
1424
1425#ifdef HAVE_SYS_SYSCTL_H
1426#if defined(HAVE_SYSCTLBYNAME)
1427 {
1428 size_t len = sizeof(usermnt);
1429
1430 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1431 }
1432#elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1433 {
1434 int mib[2];
1435 size_t len = sizeof(usermnt);
1436
1437 mib[0] = CTL_VFS;
1438 mib[1] = VFS_USERMOUNT;
1439 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1440 }
1441#elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1442 {
1443 int mib[2];
1444 size_t len = sizeof(usermnt);
1445
1446 mib[0] = CTL_KERN;
1447 mib[1] = KERN_USERMOUNT;
1448 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1449 }
1450#endif
1451#endif
1452
1453 while ((fstab = getfsent ()) != NULL)
1454 {
1455 gboolean is_read_only = FALSE;
1456 gboolean is_user_mountable = FALSE;
1457
1458 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1459 continue;
1460
1461 if (strcmp (fstab->fs_type, "ro") == 0)
1462 is_read_only = TRUE;
1463
1464#ifdef HAVE_SYS_SYSCTL_H
1465 if (usermnt != 0)
1466 {
1467 uid_t uid = getuid ();
1468 if (stat (fstab->fs_file, &sb) == 0)
1469 {
1470 if (uid == 0 || sb.st_uid == uid)
1471 is_user_mountable = TRUE;
1472 }
1473 }
1474#endif
1475
1476 mount_point = create_unix_mount_point (fstab->fs_spec,
1477 fstab->fs_file,
1478 fstab->fs_vfstype,
1479 fstab->fs_mntops,
1480 is_read_only,
1481 is_user_mountable,
1482 FALSE);
1483
1484 return_list = g_list_prepend (return_list, mount_point);
1485 }
1486
1487 endfsent ();
1488
1489 return g_list_reverse (return_list);
1490}
1491/* Interix {{{2 */
1492#elif defined(__INTERIX)
1493static GList *
1494_g_get_unix_mount_points (void)
1495{
1496 return _g_get_unix_mounts ();
1497}
1498
1499/* QNX {{{2 */
1500#elif defined (HAVE_QNX)
1501static GList *
1502_g_get_unix_mount_points (void)
1503{
1504 return _g_get_unix_mounts ();
1505}
1506
1507/* Common code {{{2 */
1508#else
1509#error No g_get_mount_table() implementation for system
1510#endif
1511
1512static guint64
1513get_mounts_timestamp (void)
1514{
1515 const char *monitor_file;
1516 struct stat buf;
1517 guint64 timestamp = 0;
1518
1519 G_LOCK (proc_mounts_source);
1520
1521 monitor_file = get_mtab_monitor_file ();
1522 /* Don't return mtime for /proc/ files */
1523 if (monitor_file && !g_str_has_prefix (str: monitor_file, prefix: "/proc/"))
1524 {
1525 if (stat (file: monitor_file, buf: &buf) == 0)
1526 timestamp = buf.st_mtime;
1527 }
1528 else if (proc_mounts_watch_is_running ())
1529 {
1530 /* it's being monitored by poll, so return mount_poller_time */
1531 timestamp = mount_poller_time;
1532 }
1533 else
1534 {
1535 /* Case of /proc/ file not being monitored - Be on the safe side and
1536 * send a new timestamp to force g_unix_mounts_changed_since() to
1537 * return TRUE so any application caches depending on it (like eg.
1538 * the one in GIO) get invalidated and don't hold possibly outdated
1539 * data - see Bug 787731 */
1540 timestamp = g_get_monotonic_time ();
1541 }
1542
1543 G_UNLOCK (proc_mounts_source);
1544
1545 return timestamp;
1546}
1547
1548static guint64
1549get_mount_points_timestamp (void)
1550{
1551 const char *monitor_file;
1552 struct stat buf;
1553
1554 monitor_file = get_fstab_file ();
1555 if (monitor_file)
1556 {
1557 if (stat (file: monitor_file, buf: &buf) == 0)
1558 return (guint64)buf.st_mtime;
1559 }
1560 return 0;
1561}
1562
1563/**
1564 * g_unix_mounts_get:
1565 * @time_read: (out) (optional): guint64 to contain a timestamp, or %NULL
1566 *
1567 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1568 * If @time_read is set, it will be filled with the mount
1569 * timestamp, allowing for checking if the mounts have changed
1570 * with g_unix_mounts_changed_since().
1571 *
1572 * Returns: (element-type GUnixMountEntry) (transfer full):
1573 * a #GList of the UNIX mounts.
1574 **/
1575GList *
1576g_unix_mounts_get (guint64 *time_read)
1577{
1578 if (time_read)
1579 *time_read = get_mounts_timestamp ();
1580
1581 return _g_get_unix_mounts ();
1582}
1583
1584/**
1585 * g_unix_mount_at:
1586 * @mount_path: (type filename): path for a possible unix mount.
1587 * @time_read: (out) (optional): guint64 to contain a timestamp.
1588 *
1589 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1590 * is set, it will be filled with a unix timestamp for checking
1591 * if the mounts have changed since with g_unix_mounts_changed_since().
1592 *
1593 * If more mounts have the same mount path, the last matching mount
1594 * is returned.
1595 *
1596 * Returns: (transfer full): a #GUnixMountEntry.
1597 **/
1598GUnixMountEntry *
1599g_unix_mount_at (const char *mount_path,
1600 guint64 *time_read)
1601{
1602 GList *mounts, *l;
1603 GUnixMountEntry *mount_entry, *found;
1604
1605 mounts = g_unix_mounts_get (time_read);
1606
1607 found = NULL;
1608 for (l = mounts; l != NULL; l = l->next)
1609 {
1610 mount_entry = l->data;
1611
1612 if (strcmp (s1: mount_path, s2: mount_entry->mount_path) == 0)
1613 {
1614 if (found != NULL)
1615 g_unix_mount_free (mount_entry: found);
1616
1617 found = mount_entry;
1618 }
1619 else
1620 g_unix_mount_free (mount_entry);
1621 }
1622 g_list_free (list: mounts);
1623
1624 return found;
1625}
1626
1627/**
1628 * g_unix_mount_for:
1629 * @file_path: (type filename): file path on some unix mount.
1630 * @time_read: (out) (optional): guint64 to contain a timestamp.
1631 *
1632 * Gets a #GUnixMountEntry for a given file path. If @time_read
1633 * is set, it will be filled with a unix timestamp for checking
1634 * if the mounts have changed since with g_unix_mounts_changed_since().
1635 *
1636 * If more mounts have the same mount path, the last matching mount
1637 * is returned.
1638 *
1639 * Returns: (transfer full): a #GUnixMountEntry.
1640 *
1641 * Since: 2.52
1642 **/
1643GUnixMountEntry *
1644g_unix_mount_for (const char *file_path,
1645 guint64 *time_read)
1646{
1647 GUnixMountEntry *entry;
1648
1649 g_return_val_if_fail (file_path != NULL, NULL);
1650
1651 entry = g_unix_mount_at (mount_path: file_path, time_read);
1652 if (entry == NULL)
1653 {
1654 char *topdir;
1655
1656 topdir = _g_local_file_find_topdir_for (file_path);
1657 if (topdir != NULL)
1658 {
1659 entry = g_unix_mount_at (mount_path: topdir, time_read);
1660 g_free (mem: topdir);
1661 }
1662 }
1663
1664 return entry;
1665}
1666
1667/**
1668 * g_unix_mount_points_get:
1669 * @time_read: (out) (optional): guint64 to contain a timestamp.
1670 *
1671 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1672 * If @time_read is set, it will be filled with the mount timestamp,
1673 * allowing for checking if the mounts have changed with
1674 * g_unix_mount_points_changed_since().
1675 *
1676 * Returns: (element-type GUnixMountPoint) (transfer full):
1677 * a #GList of the UNIX mountpoints.
1678 **/
1679GList *
1680g_unix_mount_points_get (guint64 *time_read)
1681{
1682 if (time_read)
1683 *time_read = get_mount_points_timestamp ();
1684
1685 return _g_get_unix_mount_points ();
1686}
1687
1688/**
1689 * g_unix_mount_point_at:
1690 * @mount_path: (type filename): path for a possible unix mount point.
1691 * @time_read: (out) (optional): guint64 to contain a timestamp.
1692 *
1693 * Gets a #GUnixMountPoint for a given mount path. If @time_read is set, it
1694 * will be filled with a unix timestamp for checking if the mount points have
1695 * changed since with g_unix_mount_points_changed_since().
1696 *
1697 * If more mount points have the same mount path, the last matching mount point
1698 * is returned.
1699 *
1700 * Returns: (transfer full) (nullable): a #GUnixMountPoint, or %NULL if no match
1701 * is found.
1702 *
1703 * Since: 2.66
1704 **/
1705GUnixMountPoint *
1706g_unix_mount_point_at (const char *mount_path,
1707 guint64 *time_read)
1708{
1709 GList *mount_points, *l;
1710 GUnixMountPoint *mount_point, *found;
1711
1712 mount_points = g_unix_mount_points_get (time_read);
1713
1714 found = NULL;
1715 for (l = mount_points; l != NULL; l = l->next)
1716 {
1717 mount_point = l->data;
1718
1719 if (strcmp (s1: mount_path, s2: mount_point->mount_path) == 0)
1720 {
1721 if (found != NULL)
1722 g_unix_mount_point_free (mount_point: found);
1723
1724 found = mount_point;
1725 }
1726 else
1727 g_unix_mount_point_free (mount_point);
1728 }
1729 g_list_free (list: mount_points);
1730
1731 return found;
1732}
1733
1734/**
1735 * g_unix_mounts_changed_since:
1736 * @time: guint64 to contain a timestamp.
1737 *
1738 * Checks if the unix mounts have changed since a given unix time.
1739 *
1740 * Returns: %TRUE if the mounts have changed since @time.
1741 **/
1742gboolean
1743g_unix_mounts_changed_since (guint64 time)
1744{
1745 return get_mounts_timestamp () != time;
1746}
1747
1748/**
1749 * g_unix_mount_points_changed_since:
1750 * @time: guint64 to contain a timestamp.
1751 *
1752 * Checks if the unix mount points have changed since a given unix time.
1753 *
1754 * Returns: %TRUE if the mount points have changed since @time.
1755 **/
1756gboolean
1757g_unix_mount_points_changed_since (guint64 time)
1758{
1759 return get_mount_points_timestamp () != time;
1760}
1761
1762/* GUnixMountMonitor {{{1 */
1763
1764enum {
1765 MOUNTS_CHANGED,
1766 MOUNTPOINTS_CHANGED,
1767 LAST_SIGNAL
1768};
1769
1770static guint signals[LAST_SIGNAL];
1771
1772struct _GUnixMountMonitor {
1773 GObject parent;
1774
1775 GMainContext *context;
1776};
1777
1778struct _GUnixMountMonitorClass {
1779 GObjectClass parent_class;
1780};
1781
1782
1783G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT)
1784
1785static GContextSpecificGroup mount_monitor_group;
1786static GFileMonitor *fstab_monitor;
1787static GFileMonitor *mtab_monitor;
1788static GList *mount_poller_mounts;
1789static guint mtab_file_changed_id;
1790
1791/* Called with proc_mounts_source lock held. */
1792static gboolean
1793proc_mounts_watch_is_running (void)
1794{
1795 return proc_mounts_watch_source != NULL &&
1796 !g_source_is_destroyed (source: proc_mounts_watch_source);
1797}
1798
1799static void
1800fstab_file_changed (GFileMonitor *monitor,
1801 GFile *file,
1802 GFile *other_file,
1803 GFileMonitorEvent event_type,
1804 gpointer user_data)
1805{
1806 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1807 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1808 event_type != G_FILE_MONITOR_EVENT_DELETED)
1809 return;
1810
1811 g_context_specific_group_emit (group: &mount_monitor_group, signal_id: signals[MOUNTPOINTS_CHANGED]);
1812}
1813
1814static gboolean
1815mtab_file_changed_cb (gpointer user_data)
1816{
1817 mtab_file_changed_id = 0;
1818 g_context_specific_group_emit (group: &mount_monitor_group, signal_id: signals[MOUNTS_CHANGED]);
1819
1820 return G_SOURCE_REMOVE;
1821}
1822
1823static void
1824mtab_file_changed (GFileMonitor *monitor,
1825 GFile *file,
1826 GFile *other_file,
1827 GFileMonitorEvent event_type,
1828 gpointer user_data)
1829{
1830 GMainContext *context;
1831 GSource *source;
1832
1833 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1834 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1835 event_type != G_FILE_MONITOR_EVENT_DELETED)
1836 return;
1837
1838 /* Skip accumulated events from file monitor which we are not able to handle
1839 * in a real time instead of emitting mounts_changed signal several times.
1840 * This should behave equally to GIOChannel based monitoring. See Bug 792235.
1841 */
1842 if (mtab_file_changed_id > 0)
1843 return;
1844
1845 context = g_main_context_get_thread_default ();
1846 if (!context)
1847 context = g_main_context_default ();
1848
1849 source = g_idle_source_new ();
1850 g_source_set_priority (source, G_PRIORITY_DEFAULT);
1851 g_source_set_callback (source, func: mtab_file_changed_cb, NULL, NULL);
1852 g_source_set_name (source, name: "[gio] mtab_file_changed_cb");
1853 g_source_attach (source, context);
1854 g_source_unref (source);
1855}
1856
1857static gboolean
1858proc_mounts_changed (GIOChannel *channel,
1859 GIOCondition cond,
1860 gpointer user_data)
1861{
1862 if (cond & G_IO_ERR)
1863 {
1864 G_LOCK (proc_mounts_source);
1865 mount_poller_time = (guint64) g_get_monotonic_time ();
1866 G_UNLOCK (proc_mounts_source);
1867
1868 g_context_specific_group_emit (group: &mount_monitor_group, signal_id: signals[MOUNTS_CHANGED]);
1869 }
1870
1871 return TRUE;
1872}
1873
1874static gboolean
1875mount_change_poller (gpointer user_data)
1876{
1877 GList *current_mounts, *new_it, *old_it;
1878 gboolean has_changed = FALSE;
1879
1880 current_mounts = _g_get_unix_mounts ();
1881
1882 for ( new_it = current_mounts, old_it = mount_poller_mounts;
1883 new_it != NULL && old_it != NULL;
1884 new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1885 {
1886 if (g_unix_mount_compare (mount1: new_it->data, mount2: old_it->data) != 0)
1887 {
1888 has_changed = TRUE;
1889 break;
1890 }
1891 }
1892 if (!(new_it == NULL && old_it == NULL))
1893 has_changed = TRUE;
1894
1895 g_list_free_full (list: mount_poller_mounts, free_func: (GDestroyNotify) g_unix_mount_free);
1896
1897 mount_poller_mounts = current_mounts;
1898
1899 if (has_changed)
1900 {
1901 G_LOCK (proc_mounts_source);
1902 mount_poller_time = (guint64) g_get_monotonic_time ();
1903 G_UNLOCK (proc_mounts_source);
1904
1905 g_context_specific_group_emit (group: &mount_monitor_group, signal_id: signals[MOUNTPOINTS_CHANGED]);
1906 }
1907
1908 return TRUE;
1909}
1910
1911
1912static void
1913mount_monitor_stop (void)
1914{
1915 if (fstab_monitor)
1916 {
1917 g_file_monitor_cancel (monitor: fstab_monitor);
1918 g_object_unref (object: fstab_monitor);
1919 }
1920
1921 G_LOCK (proc_mounts_source);
1922 if (proc_mounts_watch_source != NULL)
1923 {
1924 g_source_destroy (source: proc_mounts_watch_source);
1925 proc_mounts_watch_source = NULL;
1926 }
1927 G_UNLOCK (proc_mounts_source);
1928
1929 if (mtab_monitor)
1930 {
1931 g_file_monitor_cancel (monitor: mtab_monitor);
1932 g_object_unref (object: mtab_monitor);
1933 }
1934
1935 if (mtab_file_changed_id)
1936 {
1937 g_source_remove (tag: mtab_file_changed_id);
1938 mtab_file_changed_id = 0;
1939 }
1940
1941 g_list_free_full (list: mount_poller_mounts, free_func: (GDestroyNotify) g_unix_mount_free);
1942}
1943
1944static void
1945mount_monitor_start (void)
1946{
1947 GFile *file;
1948
1949 if (get_fstab_file () != NULL)
1950 {
1951 file = g_file_new_for_path (path: get_fstab_file ());
1952 fstab_monitor = g_file_monitor_file (file, flags: 0, NULL, NULL);
1953 g_object_unref (object: file);
1954
1955 g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1956 }
1957
1958 if (get_mtab_monitor_file () != NULL)
1959 {
1960 const gchar *mtab_path;
1961
1962 mtab_path = get_mtab_monitor_file ();
1963 /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1964 * See 'man proc' for more details.
1965 */
1966 if (g_str_has_prefix (str: mtab_path, prefix: "/proc/"))
1967 {
1968 GIOChannel *proc_mounts_channel;
1969 GError *error = NULL;
1970 proc_mounts_channel = g_io_channel_new_file (filename: mtab_path, mode: "r", error: &error);
1971 if (proc_mounts_channel == NULL)
1972 {
1973 g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1974 error->message, g_quark_to_string (error->domain), error->code);
1975 g_error_free (error);
1976 }
1977 else
1978 {
1979 G_LOCK (proc_mounts_source);
1980
1981 proc_mounts_watch_source = g_io_create_watch (channel: proc_mounts_channel, condition: G_IO_ERR);
1982 mount_poller_time = (guint64) g_get_monotonic_time ();
1983 g_source_set_callback (source: proc_mounts_watch_source,
1984 func: (GSourceFunc) proc_mounts_changed,
1985 NULL, NULL);
1986 g_source_attach (source: proc_mounts_watch_source,
1987 context: g_main_context_get_thread_default ());
1988 g_source_unref (source: proc_mounts_watch_source);
1989 g_io_channel_unref (channel: proc_mounts_channel);
1990
1991 G_UNLOCK (proc_mounts_source);
1992 }
1993 }
1994 else
1995 {
1996 file = g_file_new_for_path (path: mtab_path);
1997 mtab_monitor = g_file_monitor_file (file, flags: 0, NULL, NULL);
1998 g_object_unref (object: file);
1999 g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
2000 }
2001 }
2002 else
2003 {
2004 G_LOCK (proc_mounts_source);
2005
2006 proc_mounts_watch_source = g_timeout_source_new_seconds (interval: 3);
2007 mount_poller_mounts = _g_get_unix_mounts ();
2008 mount_poller_time = (guint64)g_get_monotonic_time ();
2009 g_source_set_callback (source: proc_mounts_watch_source,
2010 func: mount_change_poller,
2011 NULL, NULL);
2012 g_source_attach (source: proc_mounts_watch_source,
2013 context: g_main_context_get_thread_default ());
2014 g_source_unref (source: proc_mounts_watch_source);
2015
2016 G_UNLOCK (proc_mounts_source);
2017 }
2018}
2019
2020static void
2021g_unix_mount_monitor_finalize (GObject *object)
2022{
2023 GUnixMountMonitor *monitor;
2024
2025 monitor = G_UNIX_MOUNT_MONITOR (object);
2026
2027 g_context_specific_group_remove (group: &mount_monitor_group, context: monitor->context, instance: monitor, stop_func: mount_monitor_stop);
2028
2029 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
2030}
2031
2032static void
2033g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
2034{
2035 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2036
2037 gobject_class->finalize = g_unix_mount_monitor_finalize;
2038
2039 /**
2040 * GUnixMountMonitor::mounts-changed:
2041 * @monitor: the object on which the signal is emitted
2042 *
2043 * Emitted when the unix mounts have changed.
2044 */
2045 signals[MOUNTS_CHANGED] =
2046 g_signal_new (I_("mounts-changed"),
2047 G_TYPE_FROM_CLASS (klass),
2048 signal_flags: G_SIGNAL_RUN_LAST,
2049 class_offset: 0,
2050 NULL, NULL,
2051 NULL,
2052 G_TYPE_NONE, n_params: 0);
2053
2054 /**
2055 * GUnixMountMonitor::mountpoints-changed:
2056 * @monitor: the object on which the signal is emitted
2057 *
2058 * Emitted when the unix mount points have changed.
2059 */
2060 signals[MOUNTPOINTS_CHANGED] =
2061 g_signal_new (I_("mountpoints-changed"),
2062 G_TYPE_FROM_CLASS (klass),
2063 signal_flags: G_SIGNAL_RUN_LAST,
2064 class_offset: 0,
2065 NULL, NULL,
2066 NULL,
2067 G_TYPE_NONE, n_params: 0);
2068}
2069
2070static void
2071g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
2072{
2073}
2074
2075/**
2076 * g_unix_mount_monitor_set_rate_limit:
2077 * @mount_monitor: a #GUnixMountMonitor
2078 * @limit_msec: a integer with the limit in milliseconds to
2079 * poll for changes.
2080 *
2081 * This function does nothing.
2082 *
2083 * Before 2.44, this was a partially-effective way of controlling the
2084 * rate at which events would be reported under some uncommon
2085 * circumstances. Since @mount_monitor is a singleton, it also meant
2086 * that calling this function would have side effects for other users of
2087 * the monitor.
2088 *
2089 * Since: 2.18
2090 *
2091 * Deprecated:2.44:This function does nothing. Don't call it.
2092 */
2093void
2094g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
2095 gint limit_msec)
2096{
2097}
2098
2099/**
2100 * g_unix_mount_monitor_get:
2101 *
2102 * Gets the #GUnixMountMonitor for the current thread-default main
2103 * context.
2104 *
2105 * The mount monitor can be used to monitor for changes to the list of
2106 * mounted filesystems as well as the list of mount points (ie: fstab
2107 * entries).
2108 *
2109 * You must only call g_object_unref() on the return value from under
2110 * the same main context as you called this function.
2111 *
2112 * Returns: (transfer full): the #GUnixMountMonitor.
2113 *
2114 * Since: 2.44
2115 **/
2116GUnixMountMonitor *
2117g_unix_mount_monitor_get (void)
2118{
2119 return g_context_specific_group_get (group: &mount_monitor_group,
2120 G_TYPE_UNIX_MOUNT_MONITOR,
2121 G_STRUCT_OFFSET(GUnixMountMonitor, context),
2122 start_func: mount_monitor_start);
2123}
2124
2125/**
2126 * g_unix_mount_monitor_new:
2127 *
2128 * Deprecated alias for g_unix_mount_monitor_get().
2129 *
2130 * This function was never a true constructor, which is why it was
2131 * renamed.
2132 *
2133 * Returns: a #GUnixMountMonitor.
2134 *
2135 * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
2136 */
2137GUnixMountMonitor *
2138g_unix_mount_monitor_new (void)
2139{
2140 return g_unix_mount_monitor_get ();
2141}
2142
2143/* GUnixMount {{{1 */
2144/**
2145 * g_unix_mount_free:
2146 * @mount_entry: a #GUnixMountEntry.
2147 *
2148 * Frees a unix mount.
2149 */
2150void
2151g_unix_mount_free (GUnixMountEntry *mount_entry)
2152{
2153 g_return_if_fail (mount_entry != NULL);
2154
2155 g_free (mem: mount_entry->mount_path);
2156 g_free (mem: mount_entry->device_path);
2157 g_free (mem: mount_entry->root_path);
2158 g_free (mem: mount_entry->filesystem_type);
2159 g_free (mem: mount_entry->options);
2160 g_free (mem: mount_entry);
2161}
2162
2163/**
2164 * g_unix_mount_copy:
2165 * @mount_entry: a #GUnixMountEntry.
2166 *
2167 * Makes a copy of @mount_entry.
2168 *
2169 * Returns: (transfer full): a new #GUnixMountEntry
2170 *
2171 * Since: 2.54
2172 */
2173GUnixMountEntry *
2174g_unix_mount_copy (GUnixMountEntry *mount_entry)
2175{
2176 GUnixMountEntry *copy;
2177
2178 g_return_val_if_fail (mount_entry != NULL, NULL);
2179
2180 copy = g_new0 (GUnixMountEntry, 1);
2181 copy->mount_path = g_strdup (str: mount_entry->mount_path);
2182 copy->device_path = g_strdup (str: mount_entry->device_path);
2183 copy->root_path = g_strdup (str: mount_entry->root_path);
2184 copy->filesystem_type = g_strdup (str: mount_entry->filesystem_type);
2185 copy->options = g_strdup (str: mount_entry->options);
2186 copy->is_read_only = mount_entry->is_read_only;
2187 copy->is_system_internal = mount_entry->is_system_internal;
2188
2189 return copy;
2190}
2191
2192/**
2193 * g_unix_mount_point_free:
2194 * @mount_point: unix mount point to free.
2195 *
2196 * Frees a unix mount point.
2197 */
2198void
2199g_unix_mount_point_free (GUnixMountPoint *mount_point)
2200{
2201 g_return_if_fail (mount_point != NULL);
2202
2203 g_free (mem: mount_point->mount_path);
2204 g_free (mem: mount_point->device_path);
2205 g_free (mem: mount_point->filesystem_type);
2206 g_free (mem: mount_point->options);
2207 g_free (mem: mount_point);
2208}
2209
2210/**
2211 * g_unix_mount_point_copy:
2212 * @mount_point: a #GUnixMountPoint.
2213 *
2214 * Makes a copy of @mount_point.
2215 *
2216 * Returns: (transfer full): a new #GUnixMountPoint
2217 *
2218 * Since: 2.54
2219 */
2220GUnixMountPoint*
2221g_unix_mount_point_copy (GUnixMountPoint *mount_point)
2222{
2223 GUnixMountPoint *copy;
2224
2225 g_return_val_if_fail (mount_point != NULL, NULL);
2226
2227 copy = g_new0 (GUnixMountPoint, 1);
2228 copy->mount_path = g_strdup (str: mount_point->mount_path);
2229 copy->device_path = g_strdup (str: mount_point->device_path);
2230 copy->filesystem_type = g_strdup (str: mount_point->filesystem_type);
2231 copy->options = g_strdup (str: mount_point->options);
2232 copy->is_read_only = mount_point->is_read_only;
2233 copy->is_user_mountable = mount_point->is_user_mountable;
2234 copy->is_loopback = mount_point->is_loopback;
2235
2236 return copy;
2237}
2238
2239/**
2240 * g_unix_mount_compare:
2241 * @mount1: first #GUnixMountEntry to compare.
2242 * @mount2: second #GUnixMountEntry to compare.
2243 *
2244 * Compares two unix mounts.
2245 *
2246 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2247 * or less than @mount2, respectively.
2248 */
2249gint
2250g_unix_mount_compare (GUnixMountEntry *mount1,
2251 GUnixMountEntry *mount2)
2252{
2253 int res;
2254
2255 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2256
2257 res = g_strcmp0 (str1: mount1->mount_path, str2: mount2->mount_path);
2258 if (res != 0)
2259 return res;
2260
2261 res = g_strcmp0 (str1: mount1->device_path, str2: mount2->device_path);
2262 if (res != 0)
2263 return res;
2264
2265 res = g_strcmp0 (str1: mount1->root_path, str2: mount2->root_path);
2266 if (res != 0)
2267 return res;
2268
2269 res = g_strcmp0 (str1: mount1->filesystem_type, str2: mount2->filesystem_type);
2270 if (res != 0)
2271 return res;
2272
2273 res = g_strcmp0 (str1: mount1->options, str2: mount2->options);
2274 if (res != 0)
2275 return res;
2276
2277 res = mount1->is_read_only - mount2->is_read_only;
2278 if (res != 0)
2279 return res;
2280
2281 return 0;
2282}
2283
2284/**
2285 * g_unix_mount_get_mount_path:
2286 * @mount_entry: input #GUnixMountEntry to get the mount path for.
2287 *
2288 * Gets the mount path for a unix mount.
2289 *
2290 * Returns: (type filename): the mount path for @mount_entry.
2291 */
2292const gchar *
2293g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
2294{
2295 g_return_val_if_fail (mount_entry != NULL, NULL);
2296
2297 return mount_entry->mount_path;
2298}
2299
2300/**
2301 * g_unix_mount_get_device_path:
2302 * @mount_entry: a #GUnixMount.
2303 *
2304 * Gets the device path for a unix mount.
2305 *
2306 * Returns: (type filename): a string containing the device path.
2307 */
2308const gchar *
2309g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
2310{
2311 g_return_val_if_fail (mount_entry != NULL, NULL);
2312
2313 return mount_entry->device_path;
2314}
2315
2316/**
2317 * g_unix_mount_get_root_path:
2318 * @mount_entry: a #GUnixMountEntry.
2319 *
2320 * Gets the root of the mount within the filesystem. This is useful e.g. for
2321 * mounts created by bind operation, or btrfs subvolumes.
2322 *
2323 * For example, the root path is equal to "/" for mount created by
2324 * "mount /dev/sda1 /mnt/foo" and "/bar" for
2325 * "mount --bind /mnt/foo/bar /mnt/bar".
2326 *
2327 * Returns: (nullable): a string containing the root, or %NULL if not supported.
2328 *
2329 * Since: 2.60
2330 */
2331const gchar *
2332g_unix_mount_get_root_path (GUnixMountEntry *mount_entry)
2333{
2334 g_return_val_if_fail (mount_entry != NULL, NULL);
2335
2336 return mount_entry->root_path;
2337}
2338
2339/**
2340 * g_unix_mount_get_fs_type:
2341 * @mount_entry: a #GUnixMount.
2342 *
2343 * Gets the filesystem type for the unix mount.
2344 *
2345 * Returns: a string containing the file system type.
2346 */
2347const gchar *
2348g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
2349{
2350 g_return_val_if_fail (mount_entry != NULL, NULL);
2351
2352 return mount_entry->filesystem_type;
2353}
2354
2355/**
2356 * g_unix_mount_get_options:
2357 * @mount_entry: a #GUnixMountEntry.
2358 *
2359 * Gets a comma-separated list of mount options for the unix mount. For example,
2360 * `rw,relatime,seclabel,data=ordered`.
2361 *
2362 * This is similar to g_unix_mount_point_get_options(), but it takes
2363 * a #GUnixMountEntry as an argument.
2364 *
2365 * Returns: (nullable): a string containing the options, or %NULL if not
2366 * available.
2367 *
2368 * Since: 2.58
2369 */
2370const gchar *
2371g_unix_mount_get_options (GUnixMountEntry *mount_entry)
2372{
2373 g_return_val_if_fail (mount_entry != NULL, NULL);
2374
2375 return mount_entry->options;
2376}
2377
2378/**
2379 * g_unix_mount_is_readonly:
2380 * @mount_entry: a #GUnixMount.
2381 *
2382 * Checks if a unix mount is mounted read only.
2383 *
2384 * Returns: %TRUE if @mount_entry is read only.
2385 */
2386gboolean
2387g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
2388{
2389 g_return_val_if_fail (mount_entry != NULL, FALSE);
2390
2391 return mount_entry->is_read_only;
2392}
2393
2394/**
2395 * g_unix_mount_is_system_internal:
2396 * @mount_entry: a #GUnixMount.
2397 *
2398 * Checks if a Unix mount is a system mount. This is the Boolean OR of
2399 * g_unix_is_system_fs_type(), g_unix_is_system_device_path() and
2400 * g_unix_is_mount_path_system_internal() on @mount_entry’s properties.
2401 *
2402 * The definition of what a ‘system’ mount entry is may change over time as new
2403 * file system types and device paths are ignored.
2404 *
2405 * Returns: %TRUE if the unix mount is for a system path.
2406 */
2407gboolean
2408g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
2409{
2410 g_return_val_if_fail (mount_entry != NULL, FALSE);
2411
2412 return mount_entry->is_system_internal;
2413}
2414
2415/* GUnixMountPoint {{{1 */
2416/**
2417 * g_unix_mount_point_compare:
2418 * @mount1: a #GUnixMount.
2419 * @mount2: a #GUnixMount.
2420 *
2421 * Compares two unix mount points.
2422 *
2423 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2424 * or less than @mount2, respectively.
2425 */
2426gint
2427g_unix_mount_point_compare (GUnixMountPoint *mount1,
2428 GUnixMountPoint *mount2)
2429{
2430 int res;
2431
2432 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2433
2434 res = g_strcmp0 (str1: mount1->mount_path, str2: mount2->mount_path);
2435 if (res != 0)
2436 return res;
2437
2438 res = g_strcmp0 (str1: mount1->device_path, str2: mount2->device_path);
2439 if (res != 0)
2440 return res;
2441
2442 res = g_strcmp0 (str1: mount1->filesystem_type, str2: mount2->filesystem_type);
2443 if (res != 0)
2444 return res;
2445
2446 res = g_strcmp0 (str1: mount1->options, str2: mount2->options);
2447 if (res != 0)
2448 return res;
2449
2450 res = mount1->is_read_only - mount2->is_read_only;
2451 if (res != 0)
2452 return res;
2453
2454 res = mount1->is_user_mountable - mount2->is_user_mountable;
2455 if (res != 0)
2456 return res;
2457
2458 res = mount1->is_loopback - mount2->is_loopback;
2459 if (res != 0)
2460 return res;
2461
2462 return 0;
2463}
2464
2465/**
2466 * g_unix_mount_point_get_mount_path:
2467 * @mount_point: a #GUnixMountPoint.
2468 *
2469 * Gets the mount path for a unix mount point.
2470 *
2471 * Returns: (type filename): a string containing the mount path.
2472 */
2473const gchar *
2474g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2475{
2476 g_return_val_if_fail (mount_point != NULL, NULL);
2477
2478 return mount_point->mount_path;
2479}
2480
2481/**
2482 * g_unix_mount_point_get_device_path:
2483 * @mount_point: a #GUnixMountPoint.
2484 *
2485 * Gets the device path for a unix mount point.
2486 *
2487 * Returns: (type filename): a string containing the device path.
2488 */
2489const gchar *
2490g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2491{
2492 g_return_val_if_fail (mount_point != NULL, NULL);
2493
2494 return mount_point->device_path;
2495}
2496
2497/**
2498 * g_unix_mount_point_get_fs_type:
2499 * @mount_point: a #GUnixMountPoint.
2500 *
2501 * Gets the file system type for the mount point.
2502 *
2503 * Returns: a string containing the file system type.
2504 */
2505const gchar *
2506g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2507{
2508 g_return_val_if_fail (mount_point != NULL, NULL);
2509
2510 return mount_point->filesystem_type;
2511}
2512
2513/**
2514 * g_unix_mount_point_get_options:
2515 * @mount_point: a #GUnixMountPoint.
2516 *
2517 * Gets the options for the mount point.
2518 *
2519 * Returns: (nullable): a string containing the options.
2520 *
2521 * Since: 2.32
2522 */
2523const gchar *
2524g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2525{
2526 g_return_val_if_fail (mount_point != NULL, NULL);
2527
2528 return mount_point->options;
2529}
2530
2531/**
2532 * g_unix_mount_point_is_readonly:
2533 * @mount_point: a #GUnixMountPoint.
2534 *
2535 * Checks if a unix mount point is read only.
2536 *
2537 * Returns: %TRUE if a mount point is read only.
2538 */
2539gboolean
2540g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2541{
2542 g_return_val_if_fail (mount_point != NULL, FALSE);
2543
2544 return mount_point->is_read_only;
2545}
2546
2547/**
2548 * g_unix_mount_point_is_user_mountable:
2549 * @mount_point: a #GUnixMountPoint.
2550 *
2551 * Checks if a unix mount point is mountable by the user.
2552 *
2553 * Returns: %TRUE if the mount point is user mountable.
2554 */
2555gboolean
2556g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2557{
2558 g_return_val_if_fail (mount_point != NULL, FALSE);
2559
2560 return mount_point->is_user_mountable;
2561}
2562
2563/**
2564 * g_unix_mount_point_is_loopback:
2565 * @mount_point: a #GUnixMountPoint.
2566 *
2567 * Checks if a unix mount point is a loopback device.
2568 *
2569 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
2570 */
2571gboolean
2572g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2573{
2574 g_return_val_if_fail (mount_point != NULL, FALSE);
2575
2576 return mount_point->is_loopback;
2577}
2578
2579static GUnixMountType
2580guess_mount_type (const char *mount_path,
2581 const char *device_path,
2582 const char *filesystem_type)
2583{
2584 GUnixMountType type;
2585 char *basename;
2586
2587 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2588
2589 if ((strcmp (s1: filesystem_type, s2: "udf") == 0) ||
2590 (strcmp (s1: filesystem_type, s2: "iso9660") == 0) ||
2591 (strcmp (s1: filesystem_type, s2: "cd9660") == 0))
2592 type = G_UNIX_MOUNT_TYPE_CDROM;
2593 else if ((strcmp (s1: filesystem_type, s2: "nfs") == 0) ||
2594 (strcmp (s1: filesystem_type, s2: "nfs4") == 0))
2595 type = G_UNIX_MOUNT_TYPE_NFS;
2596 else if (g_str_has_prefix (str: device_path, prefix: "/vol/dev/diskette/") ||
2597 g_str_has_prefix (str: device_path, prefix: "/dev/fd") ||
2598 g_str_has_prefix (str: device_path, prefix: "/dev/floppy"))
2599 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2600 else if (g_str_has_prefix (str: device_path, prefix: "/dev/cdrom") ||
2601 g_str_has_prefix (str: device_path, prefix: "/dev/acd") ||
2602 g_str_has_prefix (str: device_path, prefix: "/dev/cd"))
2603 type = G_UNIX_MOUNT_TYPE_CDROM;
2604 else if (g_str_has_prefix (str: device_path, prefix: "/vol/"))
2605 {
2606 const char *name = mount_path + strlen (s: "/");
2607
2608 if (g_str_has_prefix (str: name, prefix: "cdrom"))
2609 type = G_UNIX_MOUNT_TYPE_CDROM;
2610 else if (g_str_has_prefix (str: name, prefix: "floppy") ||
2611 g_str_has_prefix (str: device_path, prefix: "/vol/dev/diskette/"))
2612 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2613 else if (g_str_has_prefix (str: name, prefix: "rmdisk"))
2614 type = G_UNIX_MOUNT_TYPE_ZIP;
2615 else if (g_str_has_prefix (str: name, prefix: "jaz"))
2616 type = G_UNIX_MOUNT_TYPE_JAZ;
2617 else if (g_str_has_prefix (str: name, prefix: "memstick"))
2618 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2619 }
2620 else
2621 {
2622 basename = g_path_get_basename (file_name: mount_path);
2623
2624 if (g_str_has_prefix (str: basename, prefix: "cdr") ||
2625 g_str_has_prefix (str: basename, prefix: "cdwriter") ||
2626 g_str_has_prefix (str: basename, prefix: "burn") ||
2627 g_str_has_prefix (str: basename, prefix: "dvdr"))
2628 type = G_UNIX_MOUNT_TYPE_CDROM;
2629 else if (g_str_has_prefix (str: basename, prefix: "floppy"))
2630 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2631 else if (g_str_has_prefix (str: basename, prefix: "zip"))
2632 type = G_UNIX_MOUNT_TYPE_ZIP;
2633 else if (g_str_has_prefix (str: basename, prefix: "jaz"))
2634 type = G_UNIX_MOUNT_TYPE_JAZ;
2635 else if (g_str_has_prefix (str: basename, prefix: "camera"))
2636 type = G_UNIX_MOUNT_TYPE_CAMERA;
2637 else if (g_str_has_prefix (str: basename, prefix: "memstick") ||
2638 g_str_has_prefix (str: basename, prefix: "memory_stick") ||
2639 g_str_has_prefix (str: basename, prefix: "ram"))
2640 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2641 else if (g_str_has_prefix (str: basename, prefix: "compact_flash"))
2642 type = G_UNIX_MOUNT_TYPE_CF;
2643 else if (g_str_has_prefix (str: basename, prefix: "smart_media"))
2644 type = G_UNIX_MOUNT_TYPE_SM;
2645 else if (g_str_has_prefix (str: basename, prefix: "sd_mmc"))
2646 type = G_UNIX_MOUNT_TYPE_SDMMC;
2647 else if (g_str_has_prefix (str: basename, prefix: "ipod"))
2648 type = G_UNIX_MOUNT_TYPE_IPOD;
2649
2650 g_free (mem: basename);
2651 }
2652
2653 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2654 type = G_UNIX_MOUNT_TYPE_HD;
2655
2656 return type;
2657}
2658
2659/**
2660 * g_unix_mount_guess_type:
2661 * @mount_entry: a #GUnixMount.
2662 *
2663 * Guesses the type of a unix mount. If the mount type cannot be
2664 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2665 *
2666 * Returns: a #GUnixMountType.
2667 */
2668static GUnixMountType
2669g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2670{
2671 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2672 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2673 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2674 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2675
2676 return guess_mount_type (mount_path: mount_entry->mount_path,
2677 device_path: mount_entry->device_path,
2678 filesystem_type: mount_entry->filesystem_type);
2679}
2680
2681/**
2682 * g_unix_mount_point_guess_type:
2683 * @mount_point: a #GUnixMountPoint.
2684 *
2685 * Guesses the type of a unix mount point.
2686 * If the mount type cannot be determined,
2687 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2688 *
2689 * Returns: a #GUnixMountType.
2690 */
2691static GUnixMountType
2692g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2693{
2694 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2695 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2696 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2697 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2698
2699 return guess_mount_type (mount_path: mount_point->mount_path,
2700 device_path: mount_point->device_path,
2701 filesystem_type: mount_point->filesystem_type);
2702}
2703
2704static const char *
2705type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2706{
2707 const char *icon_name;
2708
2709 switch (type)
2710 {
2711 case G_UNIX_MOUNT_TYPE_HD:
2712 if (is_mount_point)
2713 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2714 else
2715 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2716 break;
2717 case G_UNIX_MOUNT_TYPE_FLOPPY:
2718 case G_UNIX_MOUNT_TYPE_ZIP:
2719 case G_UNIX_MOUNT_TYPE_JAZ:
2720 if (is_mount_point)
2721 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2722 else
2723 icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2724 break;
2725 case G_UNIX_MOUNT_TYPE_CDROM:
2726 if (is_mount_point)
2727 icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2728 else
2729 icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2730 break;
2731 case G_UNIX_MOUNT_TYPE_NFS:
2732 icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2733 break;
2734 case G_UNIX_MOUNT_TYPE_MEMSTICK:
2735 if (is_mount_point)
2736 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2737 else
2738 icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2739 break;
2740 case G_UNIX_MOUNT_TYPE_CAMERA:
2741 if (is_mount_point)
2742 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2743 else
2744 icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2745 break;
2746 case G_UNIX_MOUNT_TYPE_IPOD:
2747 if (is_mount_point)
2748 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2749 else
2750 icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2751 break;
2752 case G_UNIX_MOUNT_TYPE_UNKNOWN:
2753 default:
2754 if (is_mount_point)
2755 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2756 else
2757 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2758 break;
2759 }
2760
2761 return icon_name;
2762}
2763
2764/**
2765 * g_unix_mount_guess_name:
2766 * @mount_entry: a #GUnixMountEntry
2767 *
2768 * Guesses the name of a Unix mount.
2769 * The result is a translated string.
2770 *
2771 * Returns: A newly allocated string that must
2772 * be freed with g_free()
2773 */
2774gchar *
2775g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2776{
2777 char *name;
2778
2779 if (strcmp (s1: mount_entry->mount_path, s2: "/") == 0)
2780 name = g_strdup (_("Filesystem root"));
2781 else
2782 name = g_filename_display_basename (filename: mount_entry->mount_path);
2783
2784 return name;
2785}
2786
2787/**
2788 * g_unix_mount_guess_icon:
2789 * @mount_entry: a #GUnixMountEntry
2790 *
2791 * Guesses the icon of a Unix mount.
2792 *
2793 * Returns: (transfer full): a #GIcon
2794 */
2795GIcon *
2796g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2797{
2798 return g_themed_icon_new_with_default_fallbacks (iconname: type_to_icon (type: g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2799}
2800
2801/**
2802 * g_unix_mount_guess_symbolic_icon:
2803 * @mount_entry: a #GUnixMountEntry
2804 *
2805 * Guesses the symbolic icon of a Unix mount.
2806 *
2807 * Returns: (transfer full): a #GIcon
2808 *
2809 * Since: 2.34
2810 */
2811GIcon *
2812g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2813{
2814 return g_themed_icon_new_with_default_fallbacks (iconname: type_to_icon (type: g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2815}
2816
2817/**
2818 * g_unix_mount_point_guess_name:
2819 * @mount_point: a #GUnixMountPoint
2820 *
2821 * Guesses the name of a Unix mount point.
2822 * The result is a translated string.
2823 *
2824 * Returns: A newly allocated string that must
2825 * be freed with g_free()
2826 */
2827gchar *
2828g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2829{
2830 char *name;
2831
2832 if (strcmp (s1: mount_point->mount_path, s2: "/") == 0)
2833 name = g_strdup (_("Filesystem root"));
2834 else
2835 name = g_filename_display_basename (filename: mount_point->mount_path);
2836
2837 return name;
2838}
2839
2840/**
2841 * g_unix_mount_point_guess_icon:
2842 * @mount_point: a #GUnixMountPoint
2843 *
2844 * Guesses the icon of a Unix mount point.
2845 *
2846 * Returns: (transfer full): a #GIcon
2847 */
2848GIcon *
2849g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2850{
2851 return g_themed_icon_new_with_default_fallbacks (iconname: type_to_icon (type: g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2852}
2853
2854/**
2855 * g_unix_mount_point_guess_symbolic_icon:
2856 * @mount_point: a #GUnixMountPoint
2857 *
2858 * Guesses the symbolic icon of a Unix mount point.
2859 *
2860 * Returns: (transfer full): a #GIcon
2861 *
2862 * Since: 2.34
2863 */
2864GIcon *
2865g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2866{
2867 return g_themed_icon_new_with_default_fallbacks (iconname: type_to_icon (type: g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2868}
2869
2870/**
2871 * g_unix_mount_guess_can_eject:
2872 * @mount_entry: a #GUnixMountEntry
2873 *
2874 * Guesses whether a Unix mount can be ejected.
2875 *
2876 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2877 */
2878gboolean
2879g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2880{
2881 GUnixMountType guessed_type;
2882
2883 guessed_type = g_unix_mount_guess_type (mount_entry);
2884 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2885 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2886 return TRUE;
2887
2888 return FALSE;
2889}
2890
2891/**
2892 * g_unix_mount_guess_should_display:
2893 * @mount_entry: a #GUnixMountEntry
2894 *
2895 * Guesses whether a Unix mount should be displayed in the UI.
2896 *
2897 * Returns: %TRUE if @mount_entry is deemed to be displayable.
2898 */
2899gboolean
2900g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2901{
2902 const char *mount_path;
2903 const gchar *user_name;
2904 gsize user_name_len;
2905
2906 /* Never display internal mountpoints */
2907 if (g_unix_mount_is_system_internal (mount_entry))
2908 return FALSE;
2909
2910 /* Only display things in /media (which are generally user mountable)
2911 and home dir (fuse stuff) and /run/media/$USER */
2912 mount_path = mount_entry->mount_path;
2913 if (mount_path != NULL)
2914 {
2915 const gboolean running_as_root = (getuid () == 0);
2916 gboolean is_in_runtime_dir = FALSE;
2917
2918 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2919 if (g_strstr_len (haystack: mount_path, haystack_len: -1, needle: "/.") != NULL)
2920 return FALSE;
2921
2922 /* Check /run/media/$USER/. If running as root, display any mounts below
2923 * /run/media/. */
2924 if (running_as_root)
2925 {
2926 if (strncmp (s1: mount_path, s2: "/run/media/", n: strlen (s: "/run/media/")) == 0)
2927 is_in_runtime_dir = TRUE;
2928 }
2929 else
2930 {
2931 user_name = g_get_user_name ();
2932 user_name_len = strlen (s: user_name);
2933 if (strncmp (s1: mount_path, s2: "/run/media/", n: strlen (s: "/run/media/")) == 0 &&
2934 strncmp (s1: mount_path + strlen (s: "/run/media/"), s2: user_name, n: user_name_len) == 0 &&
2935 mount_path[strlen (s: "/run/media/") + user_name_len] == '/')
2936 is_in_runtime_dir = TRUE;
2937 }
2938
2939 if (is_in_runtime_dir || g_str_has_prefix (str: mount_path, prefix: "/media/"))
2940 {
2941 char *path;
2942 /* Avoid displaying mounts that are not accessible to the user.
2943 *
2944 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2945 * want to avoid g_access() for mount points which can potentially
2946 * block or fail stat()'ing, such as network mounts.
2947 */
2948 path = g_path_get_dirname (file_name: mount_path);
2949 if (g_str_has_prefix (str: path, prefix: "/media/"))
2950 {
2951 if (g_access (filename: path, R_OK|X_OK) != 0)
2952 {
2953 g_free (mem: path);
2954 return FALSE;
2955 }
2956 }
2957 g_free (mem: path);
2958
2959 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2960 {
2961 struct stat st;
2962 if (g_stat (file: mount_entry->device_path, buf: &st) == 0 &&
2963 S_ISBLK(st.st_mode) &&
2964 g_access (filename: mount_path, R_OK|X_OK) != 0)
2965 return FALSE;
2966 }
2967 return TRUE;
2968 }
2969
2970 if (g_str_has_prefix (str: mount_path, prefix: g_get_home_dir ()) &&
2971 mount_path[strlen (s: g_get_home_dir())] == G_DIR_SEPARATOR)
2972 return TRUE;
2973 }
2974
2975 return FALSE;
2976}
2977
2978/**
2979 * g_unix_mount_point_guess_can_eject:
2980 * @mount_point: a #GUnixMountPoint
2981 *
2982 * Guesses whether a Unix mount point can be ejected.
2983 *
2984 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2985 */
2986gboolean
2987g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2988{
2989 GUnixMountType guessed_type;
2990
2991 guessed_type = g_unix_mount_point_guess_type (mount_point);
2992 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2993 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2994 return TRUE;
2995
2996 return FALSE;
2997}
2998
2999/* Utility functions {{{1 */
3000
3001#ifdef HAVE_MNTENT_H
3002/* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
3003static void
3004_canonicalize_filename (gchar *filename)
3005{
3006 gchar *p, *q;
3007 gboolean last_was_slash = FALSE;
3008
3009 p = filename;
3010 q = filename;
3011
3012 while (*p)
3013 {
3014 if (*p == G_DIR_SEPARATOR)
3015 {
3016 if (!last_was_slash)
3017 *q++ = G_DIR_SEPARATOR;
3018
3019 last_was_slash = TRUE;
3020 }
3021 else
3022 {
3023 if (last_was_slash && *p == '.')
3024 {
3025 if (*(p + 1) == G_DIR_SEPARATOR ||
3026 *(p + 1) == '\0')
3027 {
3028 if (*(p + 1) == '\0')
3029 break;
3030
3031 p += 1;
3032 }
3033 else if (*(p + 1) == '.' &&
3034 (*(p + 2) == G_DIR_SEPARATOR ||
3035 *(p + 2) == '\0'))
3036 {
3037 if (q > filename + 1)
3038 {
3039 q--;
3040 while (q > filename + 1 &&
3041 *(q - 1) != G_DIR_SEPARATOR)
3042 q--;
3043 }
3044
3045 if (*(p + 2) == '\0')
3046 break;
3047
3048 p += 2;
3049 }
3050 else
3051 {
3052 *q++ = *p;
3053 last_was_slash = FALSE;
3054 }
3055 }
3056 else
3057 {
3058 *q++ = *p;
3059 last_was_slash = FALSE;
3060 }
3061 }
3062
3063 p++;
3064 }
3065
3066 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
3067 q--;
3068
3069 *q = '\0';
3070}
3071
3072static char *
3073_resolve_symlink (const char *file)
3074{
3075 GError *error;
3076 char *dir;
3077 char *link;
3078 char *f;
3079 char *f1;
3080
3081 f = g_strdup (str: file);
3082
3083 while (g_file_test (filename: f, test: G_FILE_TEST_IS_SYMLINK))
3084 {
3085 link = g_file_read_link (filename: f, error: &error);
3086 if (link == NULL)
3087 {
3088 g_error_free (error);
3089 g_free (mem: f);
3090 f = NULL;
3091 goto out;
3092 }
3093
3094 dir = g_path_get_dirname (file_name: f);
3095 f1 = g_strdup_printf (format: "%s/%s", dir, link);
3096 g_free (mem: dir);
3097 g_free (mem: link);
3098 g_free (mem: f);
3099 f = f1;
3100 }
3101
3102 out:
3103 if (f != NULL)
3104 _canonicalize_filename (filename: f);
3105 return f;
3106}
3107
3108static const char *
3109_resolve_dev_root (void)
3110{
3111 static gboolean have_real_dev_root = FALSE;
3112 static char real_dev_root[256];
3113 struct stat statbuf;
3114
3115 /* see if it's cached already */
3116 if (have_real_dev_root)
3117 goto found;
3118
3119 /* otherwise we're going to find it right away.. */
3120 have_real_dev_root = TRUE;
3121
3122 if (stat (file: "/dev/root", buf: &statbuf) == 0)
3123 {
3124 if (! S_ISLNK (statbuf.st_mode))
3125 {
3126 dev_t root_dev = statbuf.st_dev;
3127 FILE *f;
3128
3129 /* see if device with similar major:minor as /dev/root is mention
3130 * in /etc/mtab (it usually is)
3131 */
3132 f = fopen (filename: "/etc/mtab", modes: "r");
3133 if (f != NULL)
3134 {
3135 struct mntent *entp;
3136#ifdef HAVE_GETMNTENT_R
3137 struct mntent ent;
3138 char buf[1024];
3139 while ((entp = getmntent_r (stream: f, result: &ent, buffer: buf, bufsize: sizeof (buf))) != NULL)
3140 {
3141#else
3142 G_LOCK (getmntent);
3143 while ((entp = getmntent (f)) != NULL)
3144 {
3145#endif
3146 if (stat (file: entp->mnt_fsname, buf: &statbuf) == 0 &&
3147 statbuf.st_dev == root_dev)
3148 {
3149 strncpy (dest: real_dev_root, src: entp->mnt_fsname, n: sizeof (real_dev_root) - 1);
3150 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
3151 fclose (stream: f);
3152 goto found;
3153 }
3154 }
3155
3156 endmntent (stream: f);
3157
3158#ifndef HAVE_GETMNTENT_R
3159 G_UNLOCK (getmntent);
3160#endif
3161 }
3162
3163 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
3164
3165 }
3166 else
3167 {
3168 char *resolved;
3169 resolved = _resolve_symlink (file: "/dev/root");
3170 if (resolved != NULL)
3171 {
3172 strncpy (dest: real_dev_root, src: resolved, n: sizeof (real_dev_root) - 1);
3173 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
3174 g_free (mem: resolved);
3175 goto found;
3176 }
3177 }
3178 }
3179
3180 /* bah sucks.. */
3181 strcpy (dest: real_dev_root, src: "/dev/root");
3182
3183found:
3184 return real_dev_root;
3185}
3186#endif
3187
3188/* Epilogue {{{1 */
3189/* vim:set foldmethod=marker: */
3190

source code of gtk/subprojects/glib/gio/gunixmounts.c