Imported Upstream version 2.65.0
[platform/upstream/glib.git] / gio / gunixmounts.c
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 "glocalfileprivate.h"
68 #include "gfile.h"
69 #include "gfilemonitor.h"
70 #include "glibintl.h"
71 #include "gthemedicon.h"
72 #include "gcontextspecificgroup.h"
73
74
75 #ifdef HAVE_MNTENT_H
76 static 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  **/
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,
136                      g_unix_mount_copy, g_unix_mount_free)
137
138 struct _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
148 G_DEFINE_BOXED_TYPE (GUnixMountPoint, g_unix_mount_point,
149                      g_unix_mount_point_copy, g_unix_mount_point_free)
150
151 static GList *_g_get_unix_mounts (void);
152 static GList *_g_get_unix_mount_points (void);
153 static gboolean proc_mounts_watch_is_running (void);
154
155 G_LOCK_DEFINE_STATIC (proc_mounts_source);
156
157 /* Protected by proc_mounts_source lock */
158 static guint64 mount_poller_time = 0;
159 static 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
205 static gboolean
206 is_in (const char *value, const char *set[])
207 {
208   int i;
209   for (i = 0; set[i] != NULL; i++)
210     {
211       if (strcmp (set[i], 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  **/
229 gboolean
230 g_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 (mount_path, ignore_mountpoints))
280     return TRUE;
281   
282   if (g_str_has_prefix (mount_path, "/dev/") ||
283       g_str_has_prefix (mount_path, "/proc/") ||
284       g_str_has_prefix (mount_path, "/sys/"))
285     return TRUE;
286
287   if (g_str_has_suffix (mount_path, "/.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  */
308 gboolean
309 g_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 (fs_type, 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  */
378 gboolean
379 g_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 (device_path, ignore_devices);
394 }
395
396 static gboolean
397 guess_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))
403     return TRUE;
404   
405   if (g_unix_is_system_device_path (device))
406     return TRUE;
407
408   if (g_unix_is_mount_path_system_internal (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 (root, "/") != 0)
431     return TRUE;
432
433   return FALSE;
434 }
435
436 /* GUnixMounts (ie: mtab) implementations {{{1 */
437
438 static GUnixMountEntry *
439 create_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 (device_path);
450   mount_entry->mount_path = g_strdup (mount_path);
451   mount_entry->root_path = g_strdup (root_path);
452   mount_entry->filesystem_type = g_strdup (filesystem_type);
453   mount_entry->options = g_strdup (options);
454   mount_entry->is_read_only = is_read_only;
455
456   mount_entry->is_system_internal =
457     guess_system_internal (mount_entry->mount_path,
458                            mount_entry->filesystem_type,
459                            mount_entry->device_path,
460                            mount_entry->root_path);
461
462   return mount_entry;
463 }
464
465 static GUnixMountPoint *
466 create_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 (device_path);
478   mount_point->mount_path = g_strdup (mount_path);
479   mount_point->filesystem_type = g_strdup (filesystem_type);
480   mount_point->options = g_strdup (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
498 static 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 (table, NULL) < 0)
509     goto out;
510
511   iter = mnt_new_iter (MNT_ITER_FORWARD);
512   while (mnt_table_next_fs (table, iter, &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 (device_path, "/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 (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
527           g_free (mount_options);
528         }
529       is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
530
531       mount_entry = create_unix_mount_entry (device_path,
532                                              mnt_fs_get_target (fs),
533                                              mnt_fs_get_root (fs),
534                                              mnt_fs_get_fstype (fs),
535                                              mnt_fs_get_options (fs),
536                                              is_read_only);
537
538       return_list = g_list_prepend (return_list, mount_entry);
539     }
540   mnt_free_iter (iter);
541
542  out:
543   mnt_free_table (table);
544
545   return g_list_reverse (return_list);
546 }
547
548 #else
549
550 static const char *
551 get_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
565 G_LOCK_DEFINE_STATIC(getmntent);
566 #endif
567
568 static 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
653 static const char *
654 get_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 (&mountinfo_path, NULL))
670     {
671       return mountinfo_path;
672     }
673
674   if (stat (PROC_MOUNTINFO_PATH, &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
697 G_LOCK_DEFINE_STATIC(getmntent);
698
699 static const char *
700 get_mtab_read_file (void)
701 {
702 #ifdef _PATH_MOUNTED
703   return _PATH_MOUNTED;
704 #else   
705   return "/etc/mnttab";
706 #endif
707 }
708
709 static const char *
710 get_mtab_monitor_file (void)
711 {
712   return get_mtab_read_file ();
713 }
714
715 static 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
762 static const char *
763 get_mtab_monitor_file (void)
764 {
765   return NULL;
766 }
767
768 static 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
832 static const char *
833 get_mtab_monitor_file (void)
834 {
835   return NULL;
836 }
837
838 static 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
905 static const char *
906 get_mtab_monitor_file (void)
907 {
908   return NULL;
909 }
910
911 static 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 /* Common code {{{2 */
960 #else
961 #error No _g_get_unix_mounts() implementation for system
962 #endif
963
964 /* GUnixMountPoints (ie: fstab) implementations {{{1 */
965
966 /* _g_get_unix_mount_points():
967  * read the fstab.
968  * don't return swap and ignore mounts.
969  */
970
971 static char *
972 get_fstab_file (void)
973 {
974 #ifdef HAVE_LIBMOUNT
975   return (char *) mnt_get_fstab_path ();
976 #else
977 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
978   /* AIX */
979   return "/etc/filesystems";
980 #elif defined(_PATH_MNTTAB)
981   return _PATH_MNTTAB;
982 #elif defined(VFSTAB)
983   return VFSTAB;
984 #else
985   return "/etc/fstab";
986 #endif
987 #endif
988 }
989
990 /* mntent.h (Linux, GNU, NSS) {{{2 */
991 #ifdef HAVE_MNTENT_H
992
993 #ifdef HAVE_LIBMOUNT
994
995 static GList *
996 _g_get_unix_mount_points (void)
997 {
998   struct libmnt_table *table = NULL;
999   struct libmnt_iter* iter = NULL;
1000   struct libmnt_fs *fs = NULL;
1001   GUnixMountPoint *mount_point = NULL;
1002   GList *return_list = NULL;
1003
1004   table = mnt_new_table ();
1005   if (mnt_table_parse_fstab (table, NULL) < 0)
1006     goto out;
1007
1008   iter = mnt_new_iter (MNT_ITER_FORWARD);
1009   while (mnt_table_next_fs (table, iter, &fs) == 0)
1010     {
1011       const char *device_path = NULL;
1012       const char *mount_path = NULL;
1013       const char *mount_fstype = NULL;
1014       char *mount_options = NULL;
1015       gboolean is_read_only = FALSE;
1016       gboolean is_user_mountable = FALSE;
1017       gboolean is_loopback = FALSE;
1018
1019       mount_path = mnt_fs_get_target (fs);
1020       if ((strcmp (mount_path, "ignore") == 0) ||
1021           (strcmp (mount_path, "swap") == 0) ||
1022           (strcmp (mount_path, "none") == 0))
1023         continue;
1024
1025       mount_fstype = mnt_fs_get_fstype (fs);
1026       mount_options = mnt_fs_strdup_options (fs);
1027       if (mount_options)
1028         {
1029           unsigned long mount_flags = 0;
1030           unsigned long userspace_flags = 0;
1031
1032           mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
1033           mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
1034
1035           /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1036           if (mount_flags & MS_BIND)
1037             {
1038               g_free (mount_options);
1039               continue;
1040             }
1041
1042           is_read_only = (mount_flags & MS_RDONLY) != 0;
1043           is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
1044
1045           if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
1046               ((userspace_flags & MNT_MS_USER) &&
1047                (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
1048               (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
1049               (userspace_flags & MNT_MS_USERS) ||
1050               (userspace_flags & MNT_MS_OWNER))
1051             {
1052               is_user_mountable = TRUE;
1053             }
1054         }
1055
1056       device_path = mnt_fs_get_source (fs);
1057       if (g_strcmp0 (device_path, "/dev/root") == 0)
1058         device_path = _resolve_dev_root ();
1059
1060       mount_point = create_unix_mount_point (device_path,
1061                                              mount_path,
1062                                              mount_fstype,
1063                                              mount_options,
1064                                              is_read_only,
1065                                              is_user_mountable,
1066                                              is_loopback);
1067       if (mount_options)
1068         g_free (mount_options);
1069
1070       return_list = g_list_prepend (return_list, mount_point);
1071     }
1072   mnt_free_iter (iter);
1073
1074  out:
1075   mnt_free_table (table);
1076
1077   return g_list_reverse (return_list);
1078 }
1079
1080 #else
1081
1082 static GList *
1083 _g_get_unix_mount_points (void)
1084 {
1085 #ifdef HAVE_GETMNTENT_R
1086   struct mntent ent;
1087   char buf[1024];
1088 #endif
1089   struct mntent *mntent;
1090   FILE *file;
1091   char *read_file;
1092   GUnixMountPoint *mount_point;
1093   GList *return_list;
1094   
1095   read_file = get_fstab_file ();
1096   
1097   file = setmntent (read_file, "r");
1098   if (file == NULL)
1099     return NULL;
1100
1101   return_list = NULL;
1102   
1103 #ifdef HAVE_GETMNTENT_R
1104   while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
1105 #else
1106   G_LOCK (getmntent);
1107   while ((mntent = getmntent (file)) != NULL)
1108 #endif
1109     {
1110       const char *device_path = NULL;
1111       gboolean is_read_only = FALSE;
1112       gboolean is_user_mountable = FALSE;
1113       gboolean is_loopback = FALSE;
1114
1115       if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
1116           (strcmp (mntent->mnt_dir, "swap") == 0) ||
1117           (strcmp (mntent->mnt_dir, "none") == 0))
1118         continue;
1119
1120 #ifdef HAVE_HASMNTOPT
1121       /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1122       if (hasmntopt (mntent, "bind"))
1123         continue;
1124 #endif
1125
1126       if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1127         device_path = _resolve_dev_root ();
1128       else
1129         device_path = mntent->mnt_fsname;
1130
1131 #ifdef HAVE_HASMNTOPT
1132       if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1133         is_read_only = TRUE;
1134
1135       if (hasmntopt (mntent, "loop") != NULL)
1136         is_loopback = TRUE;
1137
1138 #endif
1139
1140       if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1141 #ifdef HAVE_HASMNTOPT
1142           || (hasmntopt (mntent, "user") != NULL
1143               && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1144           || hasmntopt (mntent, "pamconsole") != NULL
1145           || hasmntopt (mntent, "users") != NULL
1146           || hasmntopt (mntent, "owner") != NULL
1147 #endif
1148           )
1149         is_user_mountable = TRUE;
1150
1151       mount_point = create_unix_mount_point (device_path,
1152                                              mntent->mnt_dir,
1153                                              mntent->mnt_type,
1154                                              mntent->mnt_opts,
1155                                              is_read_only,
1156                                              is_user_mountable,
1157                                              is_loopback);
1158
1159       return_list = g_list_prepend (return_list, mount_point);
1160     }
1161   
1162   endmntent (file);
1163
1164 #ifndef HAVE_GETMNTENT_R
1165   G_UNLOCK (getmntent);
1166 #endif
1167   
1168   return g_list_reverse (return_list);
1169 }
1170
1171 #endif /* HAVE_LIBMOUNT */
1172
1173 /* mnttab.h {{{2 */
1174 #elif defined (HAVE_SYS_MNTTAB_H)
1175
1176 static GList *
1177 _g_get_unix_mount_points (void)
1178 {
1179   struct mnttab mntent;
1180   FILE *file;
1181   char *read_file;
1182   GUnixMountPoint *mount_point;
1183   GList *return_list;
1184   
1185   read_file = get_fstab_file ();
1186   
1187   file = setmntent (read_file, "r");
1188   if (file == NULL)
1189     return NULL;
1190
1191   return_list = NULL;
1192   
1193   G_LOCK (getmntent);
1194   while (! getmntent (file, &mntent))
1195     {
1196       gboolean is_read_only = FALSE;
1197       gboolean is_user_mountable = FALSE;
1198       gboolean is_loopback = FALSE;
1199
1200       if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1201           (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1202           (strcmp (mntent.mnt_mountp, "none") == 0))
1203         continue;
1204
1205 #ifdef HAVE_HASMNTOPT
1206       if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1207         is_read_only = TRUE;
1208
1209       if (hasmntopt (&mntent, "lofs") != NULL)
1210         is_loopback = TRUE;
1211 #endif
1212
1213       if ((mntent.mnt_fstype != NULL)
1214 #ifdef HAVE_HASMNTOPT
1215           || (hasmntopt (&mntent, "user") != NULL
1216               && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1217           || hasmntopt (&mntent, "pamconsole") != NULL
1218           || hasmntopt (&mntent, "users") != NULL
1219           || hasmntopt (&mntent, "owner") != NULL
1220 #endif
1221           )
1222         is_user_mountable = TRUE;
1223
1224       mount_point = create_unix_mount_point (mntent.mnt_special,
1225                                              mntent.mnt_mountp,
1226                                              mntent.mnt_fstype,
1227                                              mntent.mnt_mntopts,
1228                                              is_read_only,
1229                                              is_user_mountable,
1230                                              is_loopback);
1231
1232       return_list = g_list_prepend (return_list, mount_point);
1233     }
1234   
1235   endmntent (file);
1236   G_UNLOCK (getmntent);
1237   
1238   return g_list_reverse (return_list);
1239 }
1240
1241 /* mntctl.h (AIX) {{{2 */
1242 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1243
1244 /* functions to parse /etc/filesystems on aix */
1245
1246 /* read character, ignoring comments (begin with '*', end with '\n' */
1247 static int
1248 aix_fs_getc (FILE *fd)
1249 {
1250   int c;
1251   
1252   while ((c = getc (fd)) == '*')
1253     {
1254       while (((c = getc (fd)) != '\n') && (c != EOF))
1255         ;
1256     }
1257 }
1258
1259 /* eat all continuous spaces in a file */
1260 static int
1261 aix_fs_ignorespace (FILE *fd)
1262 {
1263   int c;
1264   
1265   while ((c = aix_fs_getc (fd)) != EOF)
1266     {
1267       if (!g_ascii_isspace (c))
1268         {
1269           ungetc (c,fd);
1270           return c;
1271         }
1272     }
1273   
1274   return EOF;
1275 }
1276
1277 /* read one word from file */
1278 static int
1279 aix_fs_getword (FILE *fd, 
1280                 char *word)
1281 {
1282   int c;
1283   
1284   aix_fs_ignorespace (fd);
1285
1286   while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1287     {
1288       if (c == '"')
1289         {
1290           while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1291             *word++ = c;
1292           else
1293             *word++ = c;
1294         }
1295     }
1296   *word = 0;
1297   
1298   return c;
1299 }
1300
1301 typedef struct {
1302   char mnt_mount[PATH_MAX];
1303   char mnt_special[PATH_MAX];
1304   char mnt_fstype[16];
1305   char mnt_options[128];
1306 } AixMountTableEntry;
1307
1308 /* read mount points properties */
1309 static int
1310 aix_fs_get (FILE               *fd, 
1311             AixMountTableEntry *prop)
1312 {
1313   static char word[PATH_MAX] = { 0 };
1314   char value[PATH_MAX];
1315   
1316   /* read stanza */
1317   if (word[0] == 0)
1318     {
1319       if (aix_fs_getword (fd, word) == EOF)
1320         return EOF;
1321     }
1322
1323   word[strlen(word) - 1] = 0;
1324   strcpy (prop->mnt_mount, word);
1325   
1326   /* read attributes and value */
1327   
1328   while (aix_fs_getword (fd, word) != EOF)
1329     {
1330       /* test if is attribute or new stanza */
1331       if (word[strlen(word) - 1] == ':')
1332         return 0;
1333       
1334       /* read "=" */
1335       aix_fs_getword (fd, value);
1336       
1337       /* read value */
1338       aix_fs_getword (fd, value);
1339       
1340       if (strcmp (word, "dev") == 0)
1341         strcpy (prop->mnt_special, value);
1342       else if (strcmp (word, "vfs") == 0)
1343         strcpy (prop->mnt_fstype, value);
1344       else if (strcmp (word, "options") == 0)
1345         strcpy(prop->mnt_options, value);
1346     }
1347   
1348   return 0;
1349 }
1350
1351 static GList *
1352 _g_get_unix_mount_points (void)
1353 {
1354   struct mntent *mntent;
1355   FILE *file;
1356   char *read_file;
1357   GUnixMountPoint *mount_point;
1358   AixMountTableEntry mntent;
1359   GList *return_list;
1360   
1361   read_file = get_fstab_file ();
1362   
1363   file = setmntent (read_file, "r");
1364   if (file == NULL)
1365     return NULL;
1366   
1367   return_list = NULL;
1368   
1369   while (!aix_fs_get (file, &mntent))
1370     {
1371       if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1372         {
1373           mount_point = create_unix_mount_point (mntent.mnt_special,
1374                                                  mntent.mnt_mount,
1375                                                  mntent.mnt_fstype,
1376                                                  mntent.mnt_options,
1377                                                  TRUE,
1378                                                  TRUE,
1379                                                  FALSE);
1380
1381           return_list = g_list_prepend (return_list, mount_point);
1382         }
1383     }
1384         
1385   endmntent (file);
1386   
1387   return g_list_reverse (return_list);
1388 }
1389
1390 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1391
1392 static GList *
1393 _g_get_unix_mount_points (void)
1394 {
1395   struct fstab *fstab = NULL;
1396   GUnixMountPoint *mount_point;
1397   GList *return_list;
1398 #ifdef HAVE_SYS_SYSCTL_H
1399   int usermnt = 0;
1400   struct stat sb;
1401 #endif
1402   
1403   if (!setfsent ())
1404     return NULL;
1405
1406   return_list = NULL;
1407   
1408 #ifdef HAVE_SYS_SYSCTL_H
1409 #if defined(HAVE_SYSCTLBYNAME)
1410   {
1411     size_t len = sizeof(usermnt);
1412
1413     sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1414   }
1415 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1416   {
1417     int mib[2];
1418     size_t len = sizeof(usermnt);
1419     
1420     mib[0] = CTL_VFS;
1421     mib[1] = VFS_USERMOUNT;
1422     sysctl (mib, 2, &usermnt, &len, NULL, 0);
1423   }
1424 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1425   {
1426     int mib[2];
1427     size_t len = sizeof(usermnt);
1428     
1429     mib[0] = CTL_KERN;
1430     mib[1] = KERN_USERMOUNT;
1431     sysctl (mib, 2, &usermnt, &len, NULL, 0);
1432   }
1433 #endif
1434 #endif
1435   
1436   while ((fstab = getfsent ()) != NULL)
1437     {
1438       gboolean is_read_only = FALSE;
1439       gboolean is_user_mountable = FALSE;
1440
1441       if (strcmp (fstab->fs_vfstype, "swap") == 0)
1442         continue;
1443
1444       if (strcmp (fstab->fs_type, "ro") == 0)
1445         is_read_only = TRUE;
1446
1447 #ifdef HAVE_SYS_SYSCTL_H
1448       if (usermnt != 0)
1449         {
1450           uid_t uid = getuid ();
1451           if (stat (fstab->fs_file, &sb) == 0)
1452             {
1453               if (uid == 0 || sb.st_uid == uid)
1454                 is_user_mountable = TRUE;
1455             }
1456         }
1457 #endif
1458
1459       mount_point = create_unix_mount_point (fstab->fs_spec,
1460                                              fstab->fs_file,
1461                                              fstab->fs_vfstype,
1462                                              fstab->fs_mntops,
1463                                              is_read_only,
1464                                              is_user_mountable,
1465                                              FALSE);
1466
1467       return_list = g_list_prepend (return_list, mount_point);
1468     }
1469   
1470   endfsent ();
1471   
1472   return g_list_reverse (return_list);
1473 }
1474 /* Interix {{{2 */
1475 #elif defined(__INTERIX)
1476 static GList *
1477 _g_get_unix_mount_points (void)
1478 {
1479   return _g_get_unix_mounts ();
1480 }
1481
1482 /* Common code {{{2 */
1483 #else
1484 #error No g_get_mount_table() implementation for system
1485 #endif
1486
1487 static guint64
1488 get_mounts_timestamp (void)
1489 {
1490   const char *monitor_file;
1491   struct stat buf;
1492   guint64 timestamp = 0;
1493
1494   G_LOCK (proc_mounts_source);
1495
1496   monitor_file = get_mtab_monitor_file ();
1497   /* Don't return mtime for /proc/ files */
1498   if (monitor_file && !g_str_has_prefix (monitor_file, "/proc/"))
1499     {
1500       if (stat (monitor_file, &buf) == 0)
1501         timestamp = buf.st_mtime;
1502     }
1503   else if (proc_mounts_watch_is_running ())
1504     {
1505       /* it's being monitored by poll, so return mount_poller_time */
1506       timestamp = mount_poller_time;
1507     }
1508   else
1509     {
1510       /* Case of /proc/ file not being monitored - Be on the safe side and
1511        * send a new timestamp to force g_unix_mounts_changed_since() to
1512        * return TRUE so any application caches depending on it (like eg.
1513        * the one in GIO) get invalidated and don't hold possibly outdated
1514        * data - see Bug 787731 */
1515      timestamp = g_get_monotonic_time ();
1516     }
1517
1518   G_UNLOCK (proc_mounts_source);
1519
1520   return timestamp;
1521 }
1522
1523 static guint64
1524 get_mount_points_timestamp (void)
1525 {
1526   const char *monitor_file;
1527   struct stat buf;
1528
1529   monitor_file = get_fstab_file ();
1530   if (monitor_file)
1531     {
1532       if (stat (monitor_file, &buf) == 0)
1533         return (guint64)buf.st_mtime;
1534     }
1535   return 0;
1536 }
1537
1538 /**
1539  * g_unix_mounts_get:
1540  * @time_read: (out) (optional): guint64 to contain a timestamp, or %NULL
1541  *
1542  * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1543  * If @time_read is set, it will be filled with the mount
1544  * timestamp, allowing for checking if the mounts have changed
1545  * with g_unix_mounts_changed_since().
1546  *
1547  * Returns: (element-type GUnixMountEntry) (transfer full):
1548  *     a #GList of the UNIX mounts.
1549  **/
1550 GList *
1551 g_unix_mounts_get (guint64 *time_read)
1552 {
1553   if (time_read)
1554     *time_read = get_mounts_timestamp ();
1555
1556   return _g_get_unix_mounts ();
1557 }
1558
1559 /**
1560  * g_unix_mount_at:
1561  * @mount_path: (type filename): path for a possible unix mount.
1562  * @time_read: (out) (optional): guint64 to contain a timestamp.
1563  * 
1564  * Gets a #GUnixMountEntry for a given mount path. If @time_read
1565  * is set, it will be filled with a unix timestamp for checking
1566  * if the mounts have changed since with g_unix_mounts_changed_since().
1567  * 
1568  * If more mounts have the same mount path, the last matching mount
1569  * is returned.
1570  *
1571  * Returns: (transfer full): a #GUnixMountEntry.
1572  **/
1573 GUnixMountEntry *
1574 g_unix_mount_at (const char *mount_path,
1575                  guint64    *time_read)
1576 {
1577   GList *mounts, *l;
1578   GUnixMountEntry *mount_entry, *found;
1579   
1580   mounts = g_unix_mounts_get (time_read);
1581
1582   found = NULL;
1583   for (l = mounts; l != NULL; l = l->next)
1584     {
1585       mount_entry = l->data;
1586
1587       if (strcmp (mount_path, mount_entry->mount_path) == 0)
1588         {
1589           if (found != NULL)
1590             g_unix_mount_free (found);
1591
1592           found = mount_entry;
1593         }
1594       else
1595         g_unix_mount_free (mount_entry);
1596     }
1597   g_list_free (mounts);
1598
1599   return found;
1600 }
1601
1602 /**
1603  * g_unix_mount_for:
1604  * @file_path: (type filename): file path on some unix mount.
1605  * @time_read: (out) (optional): guint64 to contain a timestamp.
1606  *
1607  * Gets a #GUnixMountEntry for a given file path. If @time_read
1608  * is set, it will be filled with a unix timestamp for checking
1609  * if the mounts have changed since with g_unix_mounts_changed_since().
1610  *
1611  * If more mounts have the same mount path, the last matching mount
1612  * is returned.
1613  *
1614  * Returns: (transfer full): a #GUnixMountEntry.
1615  *
1616  * Since: 2.52
1617  **/
1618 GUnixMountEntry *
1619 g_unix_mount_for (const char *file_path,
1620                   guint64    *time_read)
1621 {
1622   GUnixMountEntry *entry;
1623
1624   g_return_val_if_fail (file_path != NULL, NULL);
1625
1626   entry = g_unix_mount_at (file_path, time_read);
1627   if (entry == NULL)
1628     {
1629       char *topdir;
1630
1631       topdir = _g_local_file_find_topdir_for (file_path);
1632       if (topdir != NULL)
1633         {
1634           entry = g_unix_mount_at (topdir, time_read);
1635           g_free (topdir);
1636         }
1637     }
1638
1639   return entry;
1640 }
1641
1642 /**
1643  * g_unix_mount_points_get:
1644  * @time_read: (out) (optional): guint64 to contain a timestamp.
1645  *
1646  * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1647  * If @time_read is set, it will be filled with the mount timestamp,
1648  * allowing for checking if the mounts have changed with
1649  * g_unix_mount_points_changed_since().
1650  *
1651  * Returns: (element-type GUnixMountPoint) (transfer full):
1652  *     a #GList of the UNIX mountpoints.
1653  **/
1654 GList *
1655 g_unix_mount_points_get (guint64 *time_read)
1656 {
1657   if (time_read)
1658     *time_read = get_mount_points_timestamp ();
1659
1660   return _g_get_unix_mount_points ();
1661 }
1662
1663 /**
1664  * g_unix_mounts_changed_since:
1665  * @time: guint64 to contain a timestamp.
1666  * 
1667  * Checks if the unix mounts have changed since a given unix time.
1668  * 
1669  * Returns: %TRUE if the mounts have changed since @time. 
1670  **/
1671 gboolean
1672 g_unix_mounts_changed_since (guint64 time)
1673 {
1674   return get_mounts_timestamp () != time;
1675 }
1676
1677 /**
1678  * g_unix_mount_points_changed_since:
1679  * @time: guint64 to contain a timestamp.
1680  * 
1681  * Checks if the unix mount points have changed since a given unix time.
1682  * 
1683  * Returns: %TRUE if the mount points have changed since @time. 
1684  **/
1685 gboolean
1686 g_unix_mount_points_changed_since (guint64 time)
1687 {
1688   return get_mount_points_timestamp () != time;
1689 }
1690
1691 /* GUnixMountMonitor {{{1 */
1692
1693 enum {
1694   MOUNTS_CHANGED,
1695   MOUNTPOINTS_CHANGED,
1696   LAST_SIGNAL
1697 };
1698
1699 static guint signals[LAST_SIGNAL];
1700
1701 struct _GUnixMountMonitor {
1702   GObject parent;
1703
1704   GMainContext *context;
1705 };
1706
1707 struct _GUnixMountMonitorClass {
1708   GObjectClass parent_class;
1709 };
1710
1711
1712 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT)
1713
1714 static GContextSpecificGroup  mount_monitor_group;
1715 static GFileMonitor          *fstab_monitor;
1716 static GFileMonitor          *mtab_monitor;
1717 static GList                 *mount_poller_mounts;
1718 static guint                  mtab_file_changed_id;
1719
1720 /* Called with proc_mounts_source lock held. */
1721 static gboolean
1722 proc_mounts_watch_is_running (void)
1723 {
1724   return proc_mounts_watch_source != NULL &&
1725          !g_source_is_destroyed (proc_mounts_watch_source);
1726 }
1727
1728 static void
1729 fstab_file_changed (GFileMonitor      *monitor,
1730                     GFile             *file,
1731                     GFile             *other_file,
1732                     GFileMonitorEvent  event_type,
1733                     gpointer           user_data)
1734 {
1735   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1736       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1737       event_type != G_FILE_MONITOR_EVENT_DELETED)
1738     return;
1739
1740   g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1741 }
1742
1743 static gboolean
1744 mtab_file_changed_cb (gpointer user_data)
1745 {
1746   mtab_file_changed_id = 0;
1747   g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1748
1749   return G_SOURCE_REMOVE;
1750 }
1751
1752 static void
1753 mtab_file_changed (GFileMonitor      *monitor,
1754                    GFile             *file,
1755                    GFile             *other_file,
1756                    GFileMonitorEvent  event_type,
1757                    gpointer           user_data)
1758 {
1759   GMainContext *context;
1760   GSource *source;
1761
1762   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1763       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1764       event_type != G_FILE_MONITOR_EVENT_DELETED)
1765     return;
1766
1767   /* Skip accumulated events from file monitor which we are not able to handle
1768    * in a real time instead of emitting mounts_changed signal several times.
1769    * This should behave equally to GIOChannel based monitoring. See Bug 792235.
1770    */
1771   if (mtab_file_changed_id > 0)
1772     return;
1773
1774   context = g_main_context_get_thread_default ();
1775   if (!context)
1776     context = g_main_context_default ();
1777
1778   source = g_idle_source_new ();
1779   g_source_set_priority (source, G_PRIORITY_DEFAULT);
1780   g_source_set_callback (source, mtab_file_changed_cb, NULL, NULL);
1781   g_source_set_name (source, "[gio] mtab_file_changed_cb");
1782   g_source_attach (source, context);
1783   g_source_unref (source);
1784 }
1785
1786 static gboolean
1787 proc_mounts_changed (GIOChannel   *channel,
1788                      GIOCondition  cond,
1789                      gpointer      user_data)
1790 {
1791   if (cond & G_IO_ERR)
1792     {
1793       G_LOCK (proc_mounts_source);
1794       mount_poller_time = (guint64) g_get_monotonic_time ();
1795       G_UNLOCK (proc_mounts_source);
1796
1797       g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1798     }
1799
1800   return TRUE;
1801 }
1802
1803 static gboolean
1804 mount_change_poller (gpointer user_data)
1805 {
1806   GList *current_mounts, *new_it, *old_it;
1807   gboolean has_changed = FALSE;
1808
1809   current_mounts = _g_get_unix_mounts ();
1810
1811   for ( new_it = current_mounts, old_it = mount_poller_mounts;
1812         new_it != NULL && old_it != NULL;
1813         new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1814     {
1815       if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1816         {
1817           has_changed = TRUE;
1818           break;
1819         }
1820     }
1821   if (!(new_it == NULL && old_it == NULL))
1822     has_changed = TRUE;
1823
1824   g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1825
1826   mount_poller_mounts = current_mounts;
1827
1828   if (has_changed)
1829     {
1830       G_LOCK (proc_mounts_source);
1831       mount_poller_time = (guint64) g_get_monotonic_time ();
1832       G_UNLOCK (proc_mounts_source);
1833
1834       g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1835     }
1836
1837   return TRUE;
1838 }
1839
1840
1841 static void
1842 mount_monitor_stop (void)
1843 {
1844   if (fstab_monitor)
1845     {
1846       g_file_monitor_cancel (fstab_monitor);
1847       g_object_unref (fstab_monitor);
1848     }
1849
1850   G_LOCK (proc_mounts_source);
1851   if (proc_mounts_watch_source != NULL)
1852     {
1853       g_source_destroy (proc_mounts_watch_source);
1854       proc_mounts_watch_source = NULL;
1855     }
1856   G_UNLOCK (proc_mounts_source);
1857
1858   if (mtab_monitor)
1859     {
1860       g_file_monitor_cancel (mtab_monitor);
1861       g_object_unref (mtab_monitor);
1862     }
1863
1864   if (mtab_file_changed_id)
1865     {
1866       g_source_remove (mtab_file_changed_id);
1867       mtab_file_changed_id = 0;
1868     }
1869
1870   g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1871 }
1872
1873 static void
1874 mount_monitor_start (void)
1875 {
1876   GFile *file;
1877
1878   if (get_fstab_file () != NULL)
1879     {
1880       file = g_file_new_for_path (get_fstab_file ());
1881       fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1882       g_object_unref (file);
1883
1884       g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1885     }
1886
1887   if (get_mtab_monitor_file () != NULL)
1888     {
1889       const gchar *mtab_path;
1890
1891       mtab_path = get_mtab_monitor_file ();
1892       /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1893        * See 'man proc' for more details.
1894        */
1895       if (g_str_has_prefix (mtab_path, "/proc/"))
1896         {
1897           GIOChannel *proc_mounts_channel;
1898           GError *error = NULL;
1899           proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
1900           if (proc_mounts_channel == NULL)
1901             {
1902               g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1903                          error->message, g_quark_to_string (error->domain), error->code);
1904               g_error_free (error);
1905             }
1906           else
1907             {
1908               G_LOCK (proc_mounts_source);
1909
1910               proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1911               mount_poller_time = (guint64) g_get_monotonic_time ();
1912               g_source_set_callback (proc_mounts_watch_source,
1913                                      (GSourceFunc) proc_mounts_changed,
1914                                      NULL, NULL);
1915               g_source_attach (proc_mounts_watch_source,
1916                                g_main_context_get_thread_default ());
1917               g_source_unref (proc_mounts_watch_source);
1918               g_io_channel_unref (proc_mounts_channel);
1919
1920               G_UNLOCK (proc_mounts_source);
1921             }
1922         }
1923       else
1924         {
1925           file = g_file_new_for_path (mtab_path);
1926           mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1927           g_object_unref (file);
1928           g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
1929         }
1930     }
1931   else
1932     {
1933       G_LOCK (proc_mounts_source);
1934
1935       proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1936       mount_poller_mounts = _g_get_unix_mounts ();
1937       mount_poller_time = (guint64)g_get_monotonic_time ();
1938       g_source_set_callback (proc_mounts_watch_source,
1939                              mount_change_poller,
1940                              NULL, NULL);
1941       g_source_attach (proc_mounts_watch_source,
1942                        g_main_context_get_thread_default ());
1943       g_source_unref (proc_mounts_watch_source);
1944
1945       G_UNLOCK (proc_mounts_source);
1946     }
1947 }
1948
1949 static void
1950 g_unix_mount_monitor_finalize (GObject *object)
1951 {
1952   GUnixMountMonitor *monitor;
1953
1954   monitor = G_UNIX_MOUNT_MONITOR (object);
1955
1956   g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
1957
1958   G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1959 }
1960
1961 static void
1962 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1963 {
1964   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1965
1966   gobject_class->finalize = g_unix_mount_monitor_finalize;
1967  
1968   /**
1969    * GUnixMountMonitor::mounts-changed:
1970    * @monitor: the object on which the signal is emitted
1971    * 
1972    * Emitted when the unix mounts have changed.
1973    */ 
1974   signals[MOUNTS_CHANGED] =
1975     g_signal_new (I_("mounts-changed"),
1976                   G_TYPE_FROM_CLASS (klass),
1977                   G_SIGNAL_RUN_LAST,
1978                   0,
1979                   NULL, NULL,
1980                   NULL,
1981                   G_TYPE_NONE, 0);
1982
1983   /**
1984    * GUnixMountMonitor::mountpoints-changed:
1985    * @monitor: the object on which the signal is emitted
1986    * 
1987    * Emitted when the unix mount points have changed.
1988    */
1989   signals[MOUNTPOINTS_CHANGED] =
1990     g_signal_new (I_("mountpoints-changed"),
1991                   G_TYPE_FROM_CLASS (klass),
1992                   G_SIGNAL_RUN_LAST,
1993                   0,
1994                   NULL, NULL,
1995                   NULL,
1996                   G_TYPE_NONE, 0);
1997 }
1998
1999 static void
2000 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
2001 {
2002 }
2003
2004 /**
2005  * g_unix_mount_monitor_set_rate_limit:
2006  * @mount_monitor: a #GUnixMountMonitor
2007  * @limit_msec: a integer with the limit in milliseconds to
2008  *     poll for changes.
2009  *
2010  * This function does nothing.
2011  *
2012  * Before 2.44, this was a partially-effective way of controlling the
2013  * rate at which events would be reported under some uncommon
2014  * circumstances.  Since @mount_monitor is a singleton, it also meant
2015  * that calling this function would have side effects for other users of
2016  * the monitor.
2017  *
2018  * Since: 2.18
2019  *
2020  * Deprecated:2.44:This function does nothing.  Don't call it.
2021  */
2022 void
2023 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
2024                                      gint               limit_msec)
2025 {
2026 }
2027
2028 /**
2029  * g_unix_mount_monitor_get:
2030  *
2031  * Gets the #GUnixMountMonitor for the current thread-default main
2032  * context.
2033  *
2034  * The mount monitor can be used to monitor for changes to the list of
2035  * mounted filesystems as well as the list of mount points (ie: fstab
2036  * entries).
2037  *
2038  * You must only call g_object_unref() on the return value from under
2039  * the same main context as you called this function.
2040  *
2041  * Returns: (transfer full): the #GUnixMountMonitor.
2042  *
2043  * Since: 2.44
2044  **/
2045 GUnixMountMonitor *
2046 g_unix_mount_monitor_get (void)
2047 {
2048   return g_context_specific_group_get (&mount_monitor_group,
2049                                        G_TYPE_UNIX_MOUNT_MONITOR,
2050                                        G_STRUCT_OFFSET(GUnixMountMonitor, context),
2051                                        mount_monitor_start);
2052 }
2053
2054 /**
2055  * g_unix_mount_monitor_new:
2056  *
2057  * Deprecated alias for g_unix_mount_monitor_get().
2058  *
2059  * This function was never a true constructor, which is why it was
2060  * renamed.
2061  *
2062  * Returns: a #GUnixMountMonitor.
2063  *
2064  * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
2065  */
2066 GUnixMountMonitor *
2067 g_unix_mount_monitor_new (void)
2068 {
2069   return g_unix_mount_monitor_get ();
2070 }
2071
2072 /* GUnixMount {{{1 */
2073 /**
2074  * g_unix_mount_free:
2075  * @mount_entry: a #GUnixMountEntry.
2076  * 
2077  * Frees a unix mount.
2078  */
2079 void
2080 g_unix_mount_free (GUnixMountEntry *mount_entry)
2081 {
2082   g_return_if_fail (mount_entry != NULL);
2083
2084   g_free (mount_entry->mount_path);
2085   g_free (mount_entry->device_path);
2086   g_free (mount_entry->root_path);
2087   g_free (mount_entry->filesystem_type);
2088   g_free (mount_entry->options);
2089   g_free (mount_entry);
2090 }
2091
2092 /**
2093  * g_unix_mount_copy:
2094  * @mount_entry: a #GUnixMountEntry.
2095  *
2096  * Makes a copy of @mount_entry.
2097  *
2098  * Returns: (transfer full): a new #GUnixMountEntry
2099  *
2100  * Since: 2.54
2101  */
2102 GUnixMountEntry *
2103 g_unix_mount_copy (GUnixMountEntry *mount_entry)
2104 {
2105   GUnixMountEntry *copy;
2106
2107   g_return_val_if_fail (mount_entry != NULL, NULL);
2108
2109   copy = g_new0 (GUnixMountEntry, 1);
2110   copy->mount_path = g_strdup (mount_entry->mount_path);
2111   copy->device_path = g_strdup (mount_entry->device_path);
2112   copy->root_path = g_strdup (mount_entry->root_path);
2113   copy->filesystem_type = g_strdup (mount_entry->filesystem_type);
2114   copy->options = g_strdup (mount_entry->options);
2115   copy->is_read_only = mount_entry->is_read_only;
2116   copy->is_system_internal = mount_entry->is_system_internal;
2117
2118   return copy;
2119 }
2120
2121 /**
2122  * g_unix_mount_point_free:
2123  * @mount_point: unix mount point to free.
2124  * 
2125  * Frees a unix mount point.
2126  */
2127 void
2128 g_unix_mount_point_free (GUnixMountPoint *mount_point)
2129 {
2130   g_return_if_fail (mount_point != NULL);
2131
2132   g_free (mount_point->mount_path);
2133   g_free (mount_point->device_path);
2134   g_free (mount_point->filesystem_type);
2135   g_free (mount_point->options);
2136   g_free (mount_point);
2137 }
2138
2139 /**
2140  * g_unix_mount_point_copy:
2141  * @mount_point: a #GUnixMountPoint.
2142  *
2143  * Makes a copy of @mount_point.
2144  *
2145  * Returns: (transfer full): a new #GUnixMountPoint
2146  *
2147  * Since: 2.54
2148  */
2149 GUnixMountPoint*
2150 g_unix_mount_point_copy (GUnixMountPoint *mount_point)
2151 {
2152   GUnixMountPoint *copy;
2153
2154   g_return_val_if_fail (mount_point != NULL, NULL);
2155
2156   copy = g_new0 (GUnixMountPoint, 1);
2157   copy->mount_path = g_strdup (mount_point->mount_path);
2158   copy->device_path = g_strdup (mount_point->device_path);
2159   copy->filesystem_type = g_strdup (mount_point->filesystem_type);
2160   copy->options = g_strdup (mount_point->options);
2161   copy->is_read_only = mount_point->is_read_only;
2162   copy->is_user_mountable = mount_point->is_user_mountable;
2163   copy->is_loopback = mount_point->is_loopback;
2164
2165   return copy;
2166 }
2167
2168 /**
2169  * g_unix_mount_compare:
2170  * @mount1: first #GUnixMountEntry to compare.
2171  * @mount2: second #GUnixMountEntry to compare.
2172  * 
2173  * Compares two unix mounts.
2174  * 
2175  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2176  * or less than @mount2, respectively. 
2177  */
2178 gint
2179 g_unix_mount_compare (GUnixMountEntry *mount1,
2180                       GUnixMountEntry *mount2)
2181 {
2182   int res;
2183
2184   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2185   
2186   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2187   if (res != 0)
2188     return res;
2189         
2190   res = g_strcmp0 (mount1->device_path, mount2->device_path);
2191   if (res != 0)
2192     return res;
2193
2194   res = g_strcmp0 (mount1->root_path, mount2->root_path);
2195   if (res != 0)
2196     return res;
2197
2198   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2199   if (res != 0)
2200     return res;
2201
2202   res = g_strcmp0 (mount1->options, mount2->options);
2203   if (res != 0)
2204     return res;
2205
2206   res =  mount1->is_read_only - mount2->is_read_only;
2207   if (res != 0)
2208     return res;
2209   
2210   return 0;
2211 }
2212
2213 /**
2214  * g_unix_mount_get_mount_path:
2215  * @mount_entry: input #GUnixMountEntry to get the mount path for.
2216  * 
2217  * Gets the mount path for a unix mount.
2218  * 
2219  * Returns: (type filename): the mount path for @mount_entry.
2220  */
2221 const gchar *
2222 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
2223 {
2224   g_return_val_if_fail (mount_entry != NULL, NULL);
2225
2226   return mount_entry->mount_path;
2227 }
2228
2229 /**
2230  * g_unix_mount_get_device_path:
2231  * @mount_entry: a #GUnixMount.
2232  * 
2233  * Gets the device path for a unix mount.
2234  * 
2235  * Returns: (type filename): a string containing the device path.
2236  */
2237 const gchar *
2238 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
2239 {
2240   g_return_val_if_fail (mount_entry != NULL, NULL);
2241
2242   return mount_entry->device_path;
2243 }
2244
2245 /**
2246  * g_unix_mount_get_root_path:
2247  * @mount_entry: a #GUnixMountEntry.
2248  * 
2249  * Gets the root of the mount within the filesystem. This is useful e.g. for
2250  * mounts created by bind operation, or btrfs subvolumes.
2251  * 
2252  * For example, the root path is equal to "/" for mount created by
2253  * "mount /dev/sda1 /mnt/foo" and "/bar" for
2254  * "mount --bind /mnt/foo/bar /mnt/bar".
2255  *
2256  * Returns: (nullable): a string containing the root, or %NULL if not supported.
2257  *
2258  * Since: 2.60
2259  */
2260 const gchar *
2261 g_unix_mount_get_root_path (GUnixMountEntry *mount_entry)
2262 {
2263   g_return_val_if_fail (mount_entry != NULL, NULL);
2264
2265   return mount_entry->root_path;
2266 }
2267
2268 /**
2269  * g_unix_mount_get_fs_type:
2270  * @mount_entry: a #GUnixMount.
2271  * 
2272  * Gets the filesystem type for the unix mount.
2273  * 
2274  * Returns: a string containing the file system type.
2275  */
2276 const gchar *
2277 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
2278 {
2279   g_return_val_if_fail (mount_entry != NULL, NULL);
2280
2281   return mount_entry->filesystem_type;
2282 }
2283
2284 /**
2285  * g_unix_mount_get_options:
2286  * @mount_entry: a #GUnixMountEntry.
2287  * 
2288  * Gets a comma-separated list of mount options for the unix mount. For example,
2289  * `rw,relatime,seclabel,data=ordered`.
2290  * 
2291  * This is similar to g_unix_mount_point_get_options(), but it takes
2292  * a #GUnixMountEntry as an argument.
2293  * 
2294  * Returns: (nullable): a string containing the options, or %NULL if not
2295  * available.
2296  * 
2297  * Since: 2.58
2298  */
2299 const gchar *
2300 g_unix_mount_get_options (GUnixMountEntry *mount_entry)
2301 {
2302   g_return_val_if_fail (mount_entry != NULL, NULL);
2303
2304   return mount_entry->options;
2305 }
2306
2307 /**
2308  * g_unix_mount_is_readonly:
2309  * @mount_entry: a #GUnixMount.
2310  * 
2311  * Checks if a unix mount is mounted read only.
2312  * 
2313  * Returns: %TRUE if @mount_entry is read only.
2314  */
2315 gboolean
2316 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
2317 {
2318   g_return_val_if_fail (mount_entry != NULL, FALSE);
2319
2320   return mount_entry->is_read_only;
2321 }
2322
2323 /**
2324  * g_unix_mount_is_system_internal:
2325  * @mount_entry: a #GUnixMount.
2326  *
2327  * Checks if a Unix mount is a system mount. This is the Boolean OR of
2328  * g_unix_is_system_fs_type(), g_unix_is_system_device_path() and
2329  * g_unix_is_mount_path_system_internal() on @mount_entry’s properties.
2330  * 
2331  * The definition of what a â€˜system’ mount entry is may change over time as new
2332  * file system types and device paths are ignored.
2333  *
2334  * Returns: %TRUE if the unix mount is for a system path.
2335  */
2336 gboolean
2337 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
2338 {
2339   g_return_val_if_fail (mount_entry != NULL, FALSE);
2340
2341   return mount_entry->is_system_internal;
2342 }
2343
2344 /* GUnixMountPoint {{{1 */
2345 /**
2346  * g_unix_mount_point_compare:
2347  * @mount1: a #GUnixMount.
2348  * @mount2: a #GUnixMount.
2349  * 
2350  * Compares two unix mount points.
2351  * 
2352  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2353  * or less than @mount2, respectively.
2354  */
2355 gint
2356 g_unix_mount_point_compare (GUnixMountPoint *mount1,
2357                             GUnixMountPoint *mount2)
2358 {
2359   int res;
2360
2361   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2362
2363   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2364   if (res != 0) 
2365     return res;
2366         
2367   res = g_strcmp0 (mount1->device_path, mount2->device_path);
2368   if (res != 0) 
2369     return res;
2370         
2371   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2372   if (res != 0) 
2373     return res;
2374
2375   res = g_strcmp0 (mount1->options, mount2->options);
2376   if (res != 0) 
2377     return res;
2378
2379   res =  mount1->is_read_only - mount2->is_read_only;
2380   if (res != 0) 
2381     return res;
2382
2383   res = mount1->is_user_mountable - mount2->is_user_mountable;
2384   if (res != 0) 
2385     return res;
2386
2387   res = mount1->is_loopback - mount2->is_loopback;
2388   if (res != 0)
2389     return res;
2390   
2391   return 0;
2392 }
2393
2394 /**
2395  * g_unix_mount_point_get_mount_path:
2396  * @mount_point: a #GUnixMountPoint.
2397  * 
2398  * Gets the mount path for a unix mount point.
2399  * 
2400  * Returns: (type filename): a string containing the mount path.
2401  */
2402 const gchar *
2403 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2404 {
2405   g_return_val_if_fail (mount_point != NULL, NULL);
2406
2407   return mount_point->mount_path;
2408 }
2409
2410 /**
2411  * g_unix_mount_point_get_device_path:
2412  * @mount_point: a #GUnixMountPoint.
2413  * 
2414  * Gets the device path for a unix mount point.
2415  * 
2416  * Returns: (type filename): a string containing the device path.
2417  */
2418 const gchar *
2419 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2420 {
2421   g_return_val_if_fail (mount_point != NULL, NULL);
2422
2423   return mount_point->device_path;
2424 }
2425
2426 /**
2427  * g_unix_mount_point_get_fs_type:
2428  * @mount_point: a #GUnixMountPoint.
2429  * 
2430  * Gets the file system type for the mount point.
2431  * 
2432  * Returns: a string containing the file system type.
2433  */
2434 const gchar *
2435 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2436 {
2437   g_return_val_if_fail (mount_point != NULL, NULL);
2438
2439   return mount_point->filesystem_type;
2440 }
2441
2442 /**
2443  * g_unix_mount_point_get_options:
2444  * @mount_point: a #GUnixMountPoint.
2445  * 
2446  * Gets the options for the mount point.
2447  * 
2448  * Returns: a string containing the options.
2449  *
2450  * Since: 2.32
2451  */
2452 const gchar *
2453 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2454 {
2455   g_return_val_if_fail (mount_point != NULL, NULL);
2456
2457   return mount_point->options;
2458 }
2459
2460 /**
2461  * g_unix_mount_point_is_readonly:
2462  * @mount_point: a #GUnixMountPoint.
2463  * 
2464  * Checks if a unix mount point is read only.
2465  * 
2466  * Returns: %TRUE if a mount point is read only.
2467  */
2468 gboolean
2469 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2470 {
2471   g_return_val_if_fail (mount_point != NULL, FALSE);
2472
2473   return mount_point->is_read_only;
2474 }
2475
2476 /**
2477  * g_unix_mount_point_is_user_mountable:
2478  * @mount_point: a #GUnixMountPoint.
2479  * 
2480  * Checks if a unix mount point is mountable by the user.
2481  * 
2482  * Returns: %TRUE if the mount point is user mountable.
2483  */
2484 gboolean
2485 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2486 {
2487   g_return_val_if_fail (mount_point != NULL, FALSE);
2488
2489   return mount_point->is_user_mountable;
2490 }
2491
2492 /**
2493  * g_unix_mount_point_is_loopback:
2494  * @mount_point: a #GUnixMountPoint.
2495  * 
2496  * Checks if a unix mount point is a loopback device.
2497  * 
2498  * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise. 
2499  */
2500 gboolean
2501 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2502 {
2503   g_return_val_if_fail (mount_point != NULL, FALSE);
2504
2505   return mount_point->is_loopback;
2506 }
2507
2508 static GUnixMountType
2509 guess_mount_type (const char *mount_path,
2510                   const char *device_path,
2511                   const char *filesystem_type)
2512 {
2513   GUnixMountType type;
2514   char *basename;
2515
2516   type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2517   
2518   if ((strcmp (filesystem_type, "udf") == 0) ||
2519       (strcmp (filesystem_type, "iso9660") == 0) ||
2520       (strcmp (filesystem_type, "cd9660") == 0))
2521     type = G_UNIX_MOUNT_TYPE_CDROM;
2522   else if ((strcmp (filesystem_type, "nfs") == 0) ||
2523            (strcmp (filesystem_type, "nfs4") == 0))
2524     type = G_UNIX_MOUNT_TYPE_NFS;
2525   else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
2526            g_str_has_prefix (device_path, "/dev/fd") ||
2527            g_str_has_prefix (device_path, "/dev/floppy"))
2528     type = G_UNIX_MOUNT_TYPE_FLOPPY;
2529   else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
2530            g_str_has_prefix (device_path, "/dev/acd") ||
2531            g_str_has_prefix (device_path, "/dev/cd"))
2532     type = G_UNIX_MOUNT_TYPE_CDROM;
2533   else if (g_str_has_prefix (device_path, "/vol/"))
2534     {
2535       const char *name = mount_path + strlen ("/");
2536       
2537       if (g_str_has_prefix (name, "cdrom"))
2538         type = G_UNIX_MOUNT_TYPE_CDROM;
2539       else if (g_str_has_prefix (name, "floppy") ||
2540                g_str_has_prefix (device_path, "/vol/dev/diskette/")) 
2541         type = G_UNIX_MOUNT_TYPE_FLOPPY;
2542       else if (g_str_has_prefix (name, "rmdisk")) 
2543         type = G_UNIX_MOUNT_TYPE_ZIP;
2544       else if (g_str_has_prefix (name, "jaz"))
2545         type = G_UNIX_MOUNT_TYPE_JAZ;
2546       else if (g_str_has_prefix (name, "memstick"))
2547         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2548     }
2549   else
2550     {
2551       basename = g_path_get_basename (mount_path);
2552       
2553       if (g_str_has_prefix (basename, "cdr") ||
2554           g_str_has_prefix (basename, "cdwriter") ||
2555           g_str_has_prefix (basename, "burn") ||
2556           g_str_has_prefix (basename, "dvdr"))
2557         type = G_UNIX_MOUNT_TYPE_CDROM;
2558       else if (g_str_has_prefix (basename, "floppy"))
2559         type = G_UNIX_MOUNT_TYPE_FLOPPY;
2560       else if (g_str_has_prefix (basename, "zip"))
2561         type = G_UNIX_MOUNT_TYPE_ZIP;
2562       else if (g_str_has_prefix (basename, "jaz"))
2563         type = G_UNIX_MOUNT_TYPE_JAZ;
2564       else if (g_str_has_prefix (basename, "camera"))
2565         type = G_UNIX_MOUNT_TYPE_CAMERA;
2566       else if (g_str_has_prefix (basename, "memstick") ||
2567                g_str_has_prefix (basename, "memory_stick") ||
2568                g_str_has_prefix (basename, "ram"))
2569         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2570       else if (g_str_has_prefix (basename, "compact_flash"))
2571         type = G_UNIX_MOUNT_TYPE_CF;
2572       else if (g_str_has_prefix (basename, "smart_media"))
2573         type = G_UNIX_MOUNT_TYPE_SM;
2574       else if (g_str_has_prefix (basename, "sd_mmc"))
2575         type = G_UNIX_MOUNT_TYPE_SDMMC;
2576       else if (g_str_has_prefix (basename, "ipod"))
2577         type = G_UNIX_MOUNT_TYPE_IPOD;
2578       
2579       g_free (basename);
2580     }
2581   
2582   if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2583     type = G_UNIX_MOUNT_TYPE_HD;
2584   
2585   return type;
2586 }
2587
2588 /**
2589  * g_unix_mount_guess_type:
2590  * @mount_entry: a #GUnixMount.
2591  * 
2592  * Guesses the type of a unix mount. If the mount type cannot be 
2593  * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2594  * 
2595  * Returns: a #GUnixMountType. 
2596  */
2597 static GUnixMountType
2598 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2599 {
2600   g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2601   g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2602   g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2603   g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2604
2605   return guess_mount_type (mount_entry->mount_path,
2606                            mount_entry->device_path,
2607                            mount_entry->filesystem_type);
2608 }
2609
2610 /**
2611  * g_unix_mount_point_guess_type:
2612  * @mount_point: a #GUnixMountPoint.
2613  * 
2614  * Guesses the type of a unix mount point. 
2615  * If the mount type cannot be determined, 
2616  * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2617  * 
2618  * Returns: a #GUnixMountType.
2619  */
2620 static GUnixMountType
2621 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2622 {
2623   g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2624   g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2625   g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2626   g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2627
2628   return guess_mount_type (mount_point->mount_path,
2629                            mount_point->device_path,
2630                            mount_point->filesystem_type);
2631 }
2632
2633 static const char *
2634 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2635 {
2636   const char *icon_name;
2637   
2638   switch (type)
2639     {
2640     case G_UNIX_MOUNT_TYPE_HD:
2641       if (is_mount_point)
2642         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2643       else
2644         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2645       break;
2646     case G_UNIX_MOUNT_TYPE_FLOPPY:
2647     case G_UNIX_MOUNT_TYPE_ZIP:
2648     case G_UNIX_MOUNT_TYPE_JAZ:
2649       if (is_mount_point)
2650         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2651       else
2652         icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2653       break;
2654     case G_UNIX_MOUNT_TYPE_CDROM:
2655       if (is_mount_point)
2656         icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2657       else
2658         icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2659       break;
2660     case G_UNIX_MOUNT_TYPE_NFS:
2661         icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2662       break;
2663     case G_UNIX_MOUNT_TYPE_MEMSTICK:
2664       if (is_mount_point)
2665         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2666       else
2667         icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2668       break;
2669     case G_UNIX_MOUNT_TYPE_CAMERA:
2670       if (is_mount_point)
2671         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2672       else
2673         icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2674       break;
2675     case G_UNIX_MOUNT_TYPE_IPOD:
2676       if (is_mount_point)
2677         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2678       else
2679         icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2680       break;
2681     case G_UNIX_MOUNT_TYPE_UNKNOWN:
2682     default:
2683       if (is_mount_point)
2684         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2685       else
2686         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2687       break;
2688     }
2689
2690   return icon_name;
2691 }
2692
2693 /**
2694  * g_unix_mount_guess_name:
2695  * @mount_entry: a #GUnixMountEntry
2696  * 
2697  * Guesses the name of a Unix mount. 
2698  * The result is a translated string.
2699  *
2700  * Returns: A newly allocated string that must
2701  *     be freed with g_free()
2702  */
2703 gchar *
2704 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2705 {
2706   char *name;
2707
2708   if (strcmp (mount_entry->mount_path, "/") == 0)
2709     name = g_strdup (_("Filesystem root"));
2710   else
2711     name = g_filename_display_basename (mount_entry->mount_path);
2712
2713   return name;
2714 }
2715
2716 /**
2717  * g_unix_mount_guess_icon:
2718  * @mount_entry: a #GUnixMountEntry
2719  * 
2720  * Guesses the icon of a Unix mount. 
2721  *
2722  * Returns: (transfer full): a #GIcon
2723  */
2724 GIcon *
2725 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2726 {
2727   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2728 }
2729
2730 /**
2731  * g_unix_mount_guess_symbolic_icon:
2732  * @mount_entry: a #GUnixMountEntry
2733  *
2734  * Guesses the symbolic icon of a Unix mount.
2735  *
2736  * Returns: (transfer full): a #GIcon
2737  *
2738  * Since: 2.34
2739  */
2740 GIcon *
2741 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2742 {
2743   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2744 }
2745
2746 /**
2747  * g_unix_mount_point_guess_name:
2748  * @mount_point: a #GUnixMountPoint
2749  * 
2750  * Guesses the name of a Unix mount point. 
2751  * The result is a translated string.
2752  *
2753  * Returns: A newly allocated string that must 
2754  *     be freed with g_free()
2755  */
2756 gchar *
2757 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2758 {
2759   char *name;
2760
2761   if (strcmp (mount_point->mount_path, "/") == 0)
2762     name = g_strdup (_("Filesystem root"));
2763   else
2764     name = g_filename_display_basename (mount_point->mount_path);
2765
2766   return name;
2767 }
2768
2769 /**
2770  * g_unix_mount_point_guess_icon:
2771  * @mount_point: a #GUnixMountPoint
2772  * 
2773  * Guesses the icon of a Unix mount point. 
2774  *
2775  * Returns: (transfer full): a #GIcon
2776  */
2777 GIcon *
2778 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2779 {
2780   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2781 }
2782
2783 /**
2784  * g_unix_mount_point_guess_symbolic_icon:
2785  * @mount_point: a #GUnixMountPoint
2786  *
2787  * Guesses the symbolic icon of a Unix mount point.
2788  *
2789  * Returns: (transfer full): a #GIcon
2790  *
2791  * Since: 2.34
2792  */
2793 GIcon *
2794 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2795 {
2796   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2797 }
2798
2799 /**
2800  * g_unix_mount_guess_can_eject:
2801  * @mount_entry: a #GUnixMountEntry
2802  * 
2803  * Guesses whether a Unix mount can be ejected.
2804  *
2805  * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2806  */
2807 gboolean
2808 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2809 {
2810   GUnixMountType guessed_type;
2811
2812   guessed_type = g_unix_mount_guess_type (mount_entry);
2813   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2814       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2815     return TRUE;
2816
2817   return FALSE;
2818 }
2819
2820 /**
2821  * g_unix_mount_guess_should_display:
2822  * @mount_entry: a #GUnixMountEntry
2823  * 
2824  * Guesses whether a Unix mount should be displayed in the UI.
2825  *
2826  * Returns: %TRUE if @mount_entry is deemed to be displayable.
2827  */
2828 gboolean
2829 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2830 {
2831   const char *mount_path;
2832   const gchar *user_name;
2833   gsize user_name_len;
2834
2835   /* Never display internal mountpoints */
2836   if (g_unix_mount_is_system_internal (mount_entry))
2837     return FALSE;
2838   
2839   /* Only display things in /media (which are generally user mountable)
2840      and home dir (fuse stuff) and /run/media/$USER */
2841   mount_path = mount_entry->mount_path;
2842   if (mount_path != NULL)
2843     {
2844       const gboolean running_as_root = (getuid () == 0);
2845       gboolean is_in_runtime_dir = FALSE;
2846
2847       /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2848       if (g_strstr_len (mount_path, -1, "/.") != NULL)
2849         return FALSE;
2850
2851       /* Check /run/media/$USER/. If running as root, display any mounts below
2852        * /run/media/. */
2853       if (running_as_root)
2854         {
2855           if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0)
2856             is_in_runtime_dir = TRUE;
2857         }
2858       else
2859         {
2860           user_name = g_get_user_name ();
2861           user_name_len = strlen (user_name);
2862           if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0 &&
2863               strncmp (mount_path + strlen ("/run/media/"), user_name, user_name_len) == 0 &&
2864               mount_path[strlen ("/run/media/") + user_name_len] == '/')
2865             is_in_runtime_dir = TRUE;
2866         }
2867
2868       if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2869         {
2870           char *path;
2871           /* Avoid displaying mounts that are not accessible to the user.
2872            *
2873            * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2874            * want to avoid g_access() for mount points which can potentially
2875            * block or fail stat()'ing, such as network mounts.
2876            */
2877           path = g_path_get_dirname (mount_path);
2878           if (g_str_has_prefix (path, "/media/"))
2879             {
2880               if (g_access (path, R_OK|X_OK) != 0) 
2881                 {
2882                   g_free (path);
2883                   return FALSE;
2884                 }
2885             }
2886           g_free (path);
2887
2888           if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2889            {
2890              struct stat st;
2891              if (g_stat (mount_entry->device_path, &st) == 0 &&
2892                  S_ISBLK(st.st_mode) &&
2893                  g_access (mount_path, R_OK|X_OK) != 0)
2894                return FALSE;
2895            }
2896           return TRUE;
2897         }
2898       
2899       if (g_str_has_prefix (mount_path, g_get_home_dir ()) && 
2900           mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2901         return TRUE;
2902     }
2903   
2904   return FALSE;
2905 }
2906
2907 /**
2908  * g_unix_mount_point_guess_can_eject:
2909  * @mount_point: a #GUnixMountPoint
2910  * 
2911  * Guesses whether a Unix mount point can be ejected.
2912  *
2913  * Returns: %TRUE if @mount_point is deemed to be ejectable.
2914  */
2915 gboolean
2916 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2917 {
2918   GUnixMountType guessed_type;
2919
2920   guessed_type = g_unix_mount_point_guess_type (mount_point);
2921   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2922       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2923     return TRUE;
2924
2925   return FALSE;
2926 }
2927
2928 /* Utility functions {{{1 */
2929
2930 #ifdef HAVE_MNTENT_H
2931 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2932 static void
2933 _canonicalize_filename (gchar *filename)
2934 {
2935   gchar *p, *q;
2936   gboolean last_was_slash = FALSE;
2937   
2938   p = filename;
2939   q = filename;
2940   
2941   while (*p)
2942     {
2943       if (*p == G_DIR_SEPARATOR)
2944         {
2945           if (!last_was_slash)
2946             *q++ = G_DIR_SEPARATOR;
2947           
2948           last_was_slash = TRUE;
2949         }
2950       else
2951         {
2952           if (last_was_slash && *p == '.')
2953             {
2954               if (*(p + 1) == G_DIR_SEPARATOR ||
2955                   *(p + 1) == '\0')
2956                 {
2957                   if (*(p + 1) == '\0')
2958                     break;
2959                   
2960                   p += 1;
2961                 }
2962               else if (*(p + 1) == '.' &&
2963                        (*(p + 2) == G_DIR_SEPARATOR ||
2964                         *(p + 2) == '\0'))
2965                 {
2966                   if (q > filename + 1)
2967                     {
2968                       q--;
2969                       while (q > filename + 1 &&
2970                              *(q - 1) != G_DIR_SEPARATOR)
2971                         q--;
2972                     }
2973                   
2974                   if (*(p + 2) == '\0')
2975                     break;
2976                   
2977                   p += 2;
2978                 }
2979               else
2980                 {
2981                   *q++ = *p;
2982                   last_was_slash = FALSE;
2983                 }
2984             }
2985           else
2986             {
2987               *q++ = *p;
2988               last_was_slash = FALSE;
2989             }
2990         }
2991       
2992       p++;
2993     }
2994   
2995   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2996     q--;
2997   
2998   *q = '\0';
2999 }
3000
3001 static char *
3002 _resolve_symlink (const char *file)
3003 {
3004   GError *error;
3005   char *dir;
3006   char *link;
3007   char *f;
3008   char *f1;
3009   
3010   f = g_strdup (file);
3011   
3012   while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) 
3013     {
3014       link = g_file_read_link (f, &error);
3015       if (link == NULL) 
3016         {
3017           g_error_free (error);
3018           g_free (f);
3019           f = NULL;
3020           goto out;
3021         }
3022     
3023       dir = g_path_get_dirname (f);
3024       f1 = g_strdup_printf ("%s/%s", dir, link);
3025       g_free (dir);
3026       g_free (link);
3027       g_free (f);
3028       f = f1;
3029     }
3030   
3031  out:
3032   if (f != NULL)
3033     _canonicalize_filename (f);
3034   return f;
3035 }
3036
3037 static const char *
3038 _resolve_dev_root (void)
3039 {
3040   static gboolean have_real_dev_root = FALSE;
3041   static char real_dev_root[256];
3042   struct stat statbuf;
3043   
3044   /* see if it's cached already */
3045   if (have_real_dev_root)
3046     goto found;
3047   
3048   /* otherwise we're going to find it right away.. */
3049   have_real_dev_root = TRUE;
3050   
3051   if (stat ("/dev/root", &statbuf) == 0) 
3052     {
3053       if (! S_ISLNK (statbuf.st_mode)) 
3054         {
3055           dev_t root_dev = statbuf.st_dev;
3056           FILE *f;
3057       
3058           /* see if device with similar major:minor as /dev/root is mention
3059            * in /etc/mtab (it usually is) 
3060            */
3061           f = fopen ("/etc/mtab", "r");
3062           if (f != NULL) 
3063             {
3064               struct mntent *entp;
3065 #ifdef HAVE_GETMNTENT_R        
3066               struct mntent ent;
3067               char buf[1024];
3068               while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL) 
3069                 {
3070 #else
3071               G_LOCK (getmntent);
3072               while ((entp = getmntent (f)) != NULL) 
3073                 { 
3074 #endif          
3075                   if (stat (entp->mnt_fsname, &statbuf) == 0 &&
3076                       statbuf.st_dev == root_dev) 
3077                     {
3078                       strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
3079                       real_dev_root[sizeof (real_dev_root) - 1] = '\0';
3080                       fclose (f);
3081                       goto found;
3082                     }
3083                 }
3084
3085               endmntent (f);
3086
3087 #ifndef HAVE_GETMNTENT_R
3088               G_UNLOCK (getmntent);
3089 #endif
3090             }                                        
3091       
3092           /* no, that didn't work.. next we could scan /dev ... but I digress.. */
3093       
3094         } 
3095        else 
3096         {
3097           char *resolved;
3098           resolved = _resolve_symlink ("/dev/root");
3099           if (resolved != NULL)
3100             {
3101               strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
3102               real_dev_root[sizeof (real_dev_root) - 1] = '\0';
3103               g_free (resolved);
3104               goto found;
3105             }
3106         }
3107     }
3108   
3109   /* bah sucks.. */
3110   strcpy (real_dev_root, "/dev/root");
3111   
3112 found:
3113   return real_dev_root;
3114 }
3115 #endif
3116
3117 /* Epilogue {{{1 */
3118 /* vim:set foldmethod=marker: */