Updated FSF's address
[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 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 #include "config.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28 #ifndef HAVE_SYSCTLBYNAME
29 #ifdef HAVE_SYS_PARAM_H
30 #include <sys/param.h>
31 #endif
32 #endif
33 #ifdef HAVE_POLL
34 #include <poll.h>
35 #endif
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <sys/time.h>
39 #include <errno.h>
40 #include <string.h>
41 #include <signal.h>
42 #include <gstdio.h>
43 #include <dirent.h>
44
45 #if HAVE_SYS_STATFS_H
46 #include <sys/statfs.h>
47 #endif
48 #if HAVE_SYS_STATVFS_H
49 #include <sys/statvfs.h>
50 #endif
51 #if HAVE_SYS_VFS_H
52 #include <sys/vfs.h>
53 #elif HAVE_SYS_MOUNT_H
54 #if HAVE_SYS_PARAM_H
55 #include <sys/param.h>
56 #endif
57 #include <sys/mount.h>
58 #endif
59
60 #ifndef O_BINARY
61 #define O_BINARY 0
62 #endif
63
64 #include "gunixmounts.h"
65 #include "gfile.h"
66 #include "gfilemonitor.h"
67 #include "glibintl.h"
68 #include "gthemedicon.h"
69
70
71 #ifdef HAVE_MNTENT_H
72 static const char *_resolve_dev_root (void);
73 #endif
74
75 /**
76  * SECTION:gunixmounts
77  * @include: gio/gunixmounts.h
78  * @short_description: UNIX mounts
79  *
80  * Routines for managing mounted UNIX mount points and paths.
81  *
82  * Note that <filename>&lt;gio/gunixmounts.h&gt;</filename> belongs to the
83  * UNIX-specific GIO interfaces, thus you have to use the
84  * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
85  */
86
87 /*
88  * GUnixMountType:
89  * @G_UNIX_MOUNT_TYPE_UNKNOWN: Unknown UNIX mount type.
90  * @G_UNIX_MOUNT_TYPE_FLOPPY: Floppy disk UNIX mount type.
91  * @G_UNIX_MOUNT_TYPE_CDROM: CDROM UNIX mount type.
92  * @G_UNIX_MOUNT_TYPE_NFS: Network File System (NFS) UNIX mount type.
93  * @G_UNIX_MOUNT_TYPE_ZIP: ZIP UNIX mount type.
94  * @G_UNIX_MOUNT_TYPE_JAZ: JAZZ UNIX mount type.
95  * @G_UNIX_MOUNT_TYPE_MEMSTICK: Memory Stick UNIX mount type.
96  * @G_UNIX_MOUNT_TYPE_CF: Compact Flash UNIX mount type.
97  * @G_UNIX_MOUNT_TYPE_SM: Smart Media UNIX mount type.
98  * @G_UNIX_MOUNT_TYPE_SDMMC: SD/MMC UNIX mount type.
99  * @G_UNIX_MOUNT_TYPE_IPOD: iPod UNIX mount type.
100  * @G_UNIX_MOUNT_TYPE_CAMERA: Digital camera UNIX mount type.
101  * @G_UNIX_MOUNT_TYPE_HD: Hard drive UNIX mount type.
102  * 
103  * Types of UNIX mounts.
104  **/
105 typedef enum {
106   G_UNIX_MOUNT_TYPE_UNKNOWN,
107   G_UNIX_MOUNT_TYPE_FLOPPY,
108   G_UNIX_MOUNT_TYPE_CDROM,
109   G_UNIX_MOUNT_TYPE_NFS,
110   G_UNIX_MOUNT_TYPE_ZIP,
111   G_UNIX_MOUNT_TYPE_JAZ,
112   G_UNIX_MOUNT_TYPE_MEMSTICK,
113   G_UNIX_MOUNT_TYPE_CF,
114   G_UNIX_MOUNT_TYPE_SM,
115   G_UNIX_MOUNT_TYPE_SDMMC,
116   G_UNIX_MOUNT_TYPE_IPOD,
117   G_UNIX_MOUNT_TYPE_CAMERA,
118   G_UNIX_MOUNT_TYPE_HD
119 } GUnixMountType;
120
121 struct _GUnixMountEntry {
122   char *mount_path;
123   char *device_path;
124   char *filesystem_type;
125   gboolean is_read_only;
126   gboolean is_system_internal;
127 };
128
129 struct _GUnixMountPoint {
130   char *mount_path;
131   char *device_path;
132   char *filesystem_type;
133   char *options;
134   gboolean is_read_only;
135   gboolean is_user_mountable;
136   gboolean is_loopback;
137 };
138
139 enum {
140   MOUNTS_CHANGED,
141   MOUNTPOINTS_CHANGED,
142   LAST_SIGNAL
143 };
144
145 static guint signals[LAST_SIGNAL];
146
147 struct _GUnixMountMonitor {
148   GObject parent;
149
150   GFileMonitor *fstab_monitor;
151   GFileMonitor *mtab_monitor;
152
153   GSource *proc_mounts_watch_source;
154 };
155
156 struct _GUnixMountMonitorClass {
157   GObjectClass parent_class;
158 };
159   
160 static GUnixMountMonitor *the_mount_monitor = NULL;
161
162 static GList *_g_get_unix_mounts (void);
163 static GList *_g_get_unix_mount_points (void);
164
165 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
166
167 #define MOUNT_POLL_INTERVAL 4000
168
169 #ifdef HAVE_SYS_MNTTAB_H
170 #define MNTOPT_RO       "ro"
171 #endif
172
173 #ifdef HAVE_MNTENT_H
174 #include <mntent.h>
175 #elif defined (HAVE_SYS_MNTTAB_H)
176 #include <sys/mnttab.h>
177 #endif
178
179 #ifdef HAVE_SYS_VFSTAB_H
180 #include <sys/vfstab.h>
181 #endif
182
183 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
184 #include <sys/mntctl.h>
185 #include <sys/vfs.h>
186 #include <sys/vmount.h>
187 #include <fshelp.h>
188 #endif
189
190 #if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
191 #include <sys/ucred.h>
192 #include <sys/mount.h>
193 #include <fstab.h>
194 #ifdef HAVE_SYS_SYSCTL_H
195 #include <sys/sysctl.h>
196 #endif
197 #endif
198
199 #ifndef HAVE_SETMNTENT
200 #define setmntent(f,m) fopen(f,m)
201 #endif
202 #ifndef HAVE_ENDMNTENT
203 #define endmntent(f) fclose(f)
204 #endif
205
206 static gboolean
207 is_in (const char *value, const char *set[])
208 {
209   int i;
210   for (i = 0; set[i] != NULL; i++)
211     {
212       if (strcmp (set[i], value) == 0)
213         return TRUE;
214     }
215   return FALSE;
216 }
217
218 /**
219  * g_unix_is_mount_path_system_internal:
220  * @mount_path: a mount path, e.g. <filename>/media/disk</filename> 
221  *    or <filename>/usr</filename>
222  *
223  * Determines if @mount_path is considered an implementation of the
224  * OS. This is primarily used for hiding mountable and mounted volumes
225  * that only are used in the OS and has little to no relevance to the
226  * casual user.
227  *
228  * Returns: %TRUE if @mount_path is considered an implementation detail 
229  *     of the OS.
230  **/
231 gboolean
232 g_unix_is_mount_path_system_internal (const char *mount_path)
233 {
234   const char *ignore_mountpoints[] = {
235     /* Includes all FHS 2.3 toplevel dirs and other specilized
236      * directories that we want to hide from the user.
237      */
238     "/",              /* we already have "Filesystem root" in Nautilus */ 
239     "/bin",
240     "/boot",
241     "/dev",
242     "/etc",
243     "/home",
244     "/lib",
245     "/lib64",
246     "/live/cow",
247     "/live/image",
248     "/media",
249     "/mnt",
250     "/opt",
251     "/root",
252     "/sbin",
253     "/srv",
254     "/tmp",
255     "/usr",
256     "/usr/local",
257     "/var",
258     "/var/crash",
259     "/var/local",
260     "/var/log",
261     "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
262     "/var/mail",
263     "/var/run",
264     "/var/tmp",       /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
265     "/proc",
266     "/sbin",
267     "/net",
268     "/sys",
269     NULL
270   };
271
272   if (is_in (mount_path, ignore_mountpoints))
273     return TRUE;
274   
275   if (g_str_has_prefix (mount_path, "/dev/") ||
276       g_str_has_prefix (mount_path, "/proc/") ||
277       g_str_has_prefix (mount_path, "/sys/"))
278     return TRUE;
279
280   if (g_str_has_suffix (mount_path, "/.gvfs"))
281     return TRUE;
282
283   return FALSE;
284 }
285
286 static gboolean
287 guess_system_internal (const char *mountpoint,
288                        const char *fs,
289                        const char *device)
290 {
291   const char *ignore_fs[] = {
292     "auto",
293     "autofs",
294     "devfs",
295     "devpts",
296     "ecryptfs",
297     "kernfs",
298     "linprocfs",
299     "proc",
300     "procfs",
301     "ptyfs",
302     "rootfs",
303     "selinuxfs",
304     "sysfs",
305     "tmpfs",
306     "usbfs",
307     "nfsd",
308     "rpc_pipefs",
309     "zfs",
310     NULL
311   };
312   const char *ignore_devices[] = {
313     "none",
314     "sunrpc",
315     "devpts",
316     "nfsd",
317     "/dev/loop",
318     "/dev/vn",
319     NULL
320   };
321   
322   if (is_in (fs, ignore_fs))
323     return TRUE;
324   
325   if (is_in (device, ignore_devices))
326     return TRUE;
327
328   if (g_unix_is_mount_path_system_internal (mountpoint))
329     return TRUE;
330   
331   return FALSE;
332 }
333
334 #ifdef HAVE_MNTENT_H
335
336 static char *
337 get_mtab_read_file (void)
338 {
339 #ifdef _PATH_MOUNTED
340 # ifdef __linux__
341   return "/proc/mounts";
342 # else
343   return _PATH_MOUNTED;
344 # endif
345 #else   
346   return "/etc/mtab";
347 #endif
348 }
349
350 static char *
351 get_mtab_monitor_file (void)
352 {
353 #ifdef _PATH_MOUNTED
354 # ifdef __linux__
355   return "/proc/mounts";
356 # else
357   return _PATH_MOUNTED;
358 # endif
359 #else   
360   return "/etc/mtab";
361 #endif
362 }
363
364 #ifndef HAVE_GETMNTENT_R
365 G_LOCK_DEFINE_STATIC(getmntent);
366 #endif
367
368 static GList *
369 _g_get_unix_mounts (void)
370 {
371 #ifdef HAVE_GETMNTENT_R
372   struct mntent ent;
373   char buf[1024];
374 #endif
375   struct mntent *mntent;
376   FILE *file;
377   char *read_file;
378   GUnixMountEntry *mount_entry;
379   GHashTable *mounts_hash;
380   GList *return_list;
381   
382   read_file = get_mtab_read_file ();
383
384   file = setmntent (read_file, "r");
385   if (file == NULL)
386     return NULL;
387
388   return_list = NULL;
389   
390   mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
391   
392 #ifdef HAVE_GETMNTENT_R
393   while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
394 #else
395   G_LOCK (getmntent);
396   while ((mntent = getmntent (file)) != NULL)
397 #endif
398     {
399       /* ignore any mnt_fsname that is repeated and begins with a '/'
400        *
401        * We do this to avoid being fooled by --bind mounts, since
402        * these have the same device as the location they bind to.
403        * It's not an ideal solution to the problem, but it's likely that
404        * the most important mountpoint is first and the --bind ones after
405        * that aren't as important. So it should work.
406        *
407        * The '/' is to handle procfs, tmpfs and other no device mounts.
408        */
409       if (mntent->mnt_fsname != NULL &&
410           mntent->mnt_fsname[0] == '/' &&
411           g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
412         continue;
413       
414       mount_entry = g_new0 (GUnixMountEntry, 1);
415       mount_entry->mount_path = g_strdup (mntent->mnt_dir);
416       if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
417         mount_entry->device_path = g_strdup (_resolve_dev_root ());
418       else
419         mount_entry->device_path = g_strdup (mntent->mnt_fsname);
420       mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
421       
422 #if defined (HAVE_HASMNTOPT)
423       if (hasmntopt (mntent, MNTOPT_RO) != NULL)
424         mount_entry->is_read_only = TRUE;
425 #endif
426       
427       mount_entry->is_system_internal =
428         guess_system_internal (mount_entry->mount_path,
429                                mount_entry->filesystem_type,
430                                mount_entry->device_path);
431       
432       g_hash_table_insert (mounts_hash,
433                            mount_entry->device_path,
434                            mount_entry->device_path);
435       
436       return_list = g_list_prepend (return_list, mount_entry);
437     }
438   g_hash_table_destroy (mounts_hash);
439   
440   endmntent (file);
441
442 #ifndef HAVE_GETMNTENT_R
443   G_UNLOCK (getmntent);
444 #endif
445   
446   return g_list_reverse (return_list);
447 }
448
449 #elif defined (HAVE_SYS_MNTTAB_H)
450
451 G_LOCK_DEFINE_STATIC(getmntent);
452
453 static char *
454 get_mtab_read_file (void)
455 {
456 #ifdef _PATH_MOUNTED
457   return _PATH_MOUNTED;
458 #else   
459   return "/etc/mnttab";
460 #endif
461 }
462
463 static char *
464 get_mtab_monitor_file (void)
465 {
466   return get_mtab_read_file ();
467 }
468
469 static GList *
470 _g_get_unix_mounts (void)
471 {
472   struct mnttab mntent;
473   FILE *file;
474   char *read_file;
475   GUnixMountEntry *mount_entry;
476   GList *return_list;
477   
478   read_file = get_mtab_read_file ();
479   
480   file = setmntent (read_file, "r");
481   if (file == NULL)
482     return NULL;
483   
484   return_list = NULL;
485   
486   G_LOCK (getmntent);
487   while (! getmntent (file, &mntent))
488     {
489       mount_entry = g_new0 (GUnixMountEntry, 1);
490       
491       mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
492       mount_entry->device_path = g_strdup (mntent.mnt_special);
493       mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
494       
495 #if defined (HAVE_HASMNTOPT)
496       if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
497         mount_entry->is_read_only = TRUE;
498 #endif
499
500       mount_entry->is_system_internal =
501         guess_system_internal (mount_entry->mount_path,
502                                mount_entry->filesystem_type,
503                                mount_entry->device_path);
504       
505       return_list = g_list_prepend (return_list, mount_entry);
506     }
507   
508   endmntent (file);
509   
510   G_UNLOCK (getmntent);
511   
512   return g_list_reverse (return_list);
513 }
514
515 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
516
517 static char *
518 get_mtab_monitor_file (void)
519 {
520   return NULL;
521 }
522
523 static GList *
524 _g_get_unix_mounts (void)
525 {
526   struct vfs_ent *fs_info;
527   struct vmount *vmount_info;
528   int vmount_number;
529   unsigned int vmount_size;
530   int current;
531   GList *return_list;
532   
533   if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
534     {
535       g_warning ("Unable to know the number of mounted volumes\n");
536       
537       return NULL;
538     }
539
540   vmount_info = (struct vmount*)g_malloc (vmount_size);
541
542   vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
543   
544   if (vmount_info->vmt_revision != VMT_REVISION)
545     g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
546
547   if (vmount_number < 0)
548     {
549       g_warning ("Unable to recover mounted volumes information\n");
550       
551       g_free (vmount_info);
552       return NULL;
553     }
554   
555   return_list = NULL;
556   while (vmount_number > 0)
557     {
558       mount_entry = g_new0 (GUnixMountEntry, 1);
559       
560       mount_entry->device_path = g_strdup (vmt2dataptr (vmount_info, VMT_OBJECT));
561       mount_entry->mount_path = g_strdup (vmt2dataptr (vmount_info, VMT_STUB));
562       /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
563       mount_entry->is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
564
565       fs_info = getvfsbytype (vmount_info->vmt_gfstype);
566       
567       if (fs_info == NULL)
568         mount_entry->filesystem_type = g_strdup ("unknown");
569       else
570         mount_entry->filesystem_type = g_strdup (fs_info->vfsent_name);
571
572       mount_entry->is_system_internal =
573         guess_system_internal (mount_entry->mount_path,
574                                mount_entry->filesystem_type,
575                                mount_entry->device_path);
576       
577       return_list = g_list_prepend (return_list, mount_entry);
578       
579       vmount_info = (struct vmount *)( (char*)vmount_info 
580                                        + vmount_info->vmt_length);
581       vmount_number--;
582     }
583   
584   g_free (vmount_info);
585   
586   return g_list_reverse (return_list);
587 }
588
589 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
590
591 static char *
592 get_mtab_monitor_file (void)
593 {
594   return NULL;
595 }
596
597 static GList *
598 _g_get_unix_mounts (void)
599 {
600 #if defined(HAVE_GETVFSSTAT)
601   struct statvfs *mntent = NULL;
602 #elif defined(HAVE_GETFSSTAT)
603   struct statfs *mntent = NULL;
604 #else
605   #error statfs juggling failed
606 #endif
607   size_t bufsize;
608   int num_mounts, i;
609   GUnixMountEntry *mount_entry;
610   GList *return_list;
611   
612   /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
613 #if defined(HAVE_GETVFSSTAT)
614   num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
615 #elif defined(HAVE_GETFSSTAT)
616   num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
617 #endif
618   if (num_mounts == -1)
619     return NULL;
620
621   bufsize = num_mounts * sizeof (*mntent);
622   mntent = g_malloc (bufsize);
623 #if defined(HAVE_GETVFSSTAT)
624   num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
625 #elif defined(HAVE_GETFSSTAT)
626   num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
627 #endif
628   if (num_mounts == -1)
629     return NULL;
630   
631   return_list = NULL;
632   
633   for (i = 0; i < num_mounts; i++)
634     {
635       mount_entry = g_new0 (GUnixMountEntry, 1);
636       
637       mount_entry->mount_path = g_strdup (mntent[i].f_mntonname);
638       mount_entry->device_path = g_strdup (mntent[i].f_mntfromname);
639       mount_entry->filesystem_type = g_strdup (mntent[i].f_fstypename);
640 #if defined(HAVE_GETVFSSTAT)
641       if (mntent[i].f_flag & ST_RDONLY)
642 #elif defined(HAVE_GETFSSTAT)
643       if (mntent[i].f_flags & MNT_RDONLY)
644 #endif
645         mount_entry->is_read_only = TRUE;
646
647       mount_entry->is_system_internal =
648         guess_system_internal (mount_entry->mount_path,
649                                mount_entry->filesystem_type,
650                                mount_entry->device_path);
651       
652       return_list = g_list_prepend (return_list, mount_entry);
653     }
654
655   g_free (mntent);
656   
657   return g_list_reverse (return_list);
658 }
659 #elif defined(__INTERIX)
660
661 static char *
662 get_mtab_monitor_file (void)
663 {
664   return NULL;
665 }
666
667 static GList *
668 _g_get_unix_mounts (void)
669 {
670   DIR *dirp;
671   GList* return_list = NULL;
672   char filename[9 + NAME_MAX];
673
674   dirp = opendir ("/dev/fs");
675   if (!dirp)
676     {
677       g_warning ("unable to read /dev/fs!");
678       return NULL;
679     }
680
681   while (1)
682     {
683       struct statvfs statbuf;
684       struct dirent entry;
685       struct dirent* result;
686       
687       if (readdir_r (dirp, &entry, &result) || result == NULL)
688         break;
689       
690       strcpy (filename, "/dev/fs/");
691       strcat (filename, entry.d_name);
692       
693       if (statvfs (filename, &statbuf) == 0)
694         {
695           GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
696           
697           mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
698           mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
699           mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
700           
701           if (statbuf.f_flag & ST_RDONLY)
702             mount_entry->is_read_only = TRUE;
703           
704           return_list = g_list_prepend(return_list, mount_entry);
705         }
706     }
707   
708   return_list = g_list_reverse (return_list);
709   
710   closedir (dirp);
711
712   return return_list;
713 }
714 #else
715 #error No _g_get_unix_mounts() implementation for system
716 #endif
717
718 /* _g_get_unix_mount_points():
719  * read the fstab.
720  * don't return swap and ignore mounts.
721  */
722
723 static char *
724 get_fstab_file (void)
725 {
726 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
727   /* AIX */
728   return "/etc/filesystems";
729 #elif defined(_PATH_MNTTAB)
730   return _PATH_MNTTAB;
731 #elif defined(VFSTAB)
732   return VFSTAB;
733 #else
734   return "/etc/fstab";
735 #endif
736 }
737
738 #ifdef HAVE_MNTENT_H
739 static GList *
740 _g_get_unix_mount_points (void)
741 {
742 #ifdef HAVE_GETMNTENT_R
743   struct mntent ent;
744   char buf[1024];
745 #endif
746   struct mntent *mntent;
747   FILE *file;
748   char *read_file;
749   GUnixMountPoint *mount_entry;
750   GList *return_list;
751   
752   read_file = get_fstab_file ();
753   
754   file = setmntent (read_file, "r");
755   if (file == NULL)
756     return NULL;
757
758   return_list = NULL;
759   
760 #ifdef HAVE_GETMNTENT_R
761   while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
762 #else
763   G_LOCK (getmntent);
764   while ((mntent = getmntent (file)) != NULL)
765 #endif
766     {
767       if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
768           (strcmp (mntent->mnt_dir, "swap") == 0) ||
769           (strcmp (mntent->mnt_dir, "none") == 0))
770         continue;
771
772 #ifdef HAVE_HASMNTOPT
773       /* We ignore bind fstab entries, as we ignore bind mounts anyway */
774       if (hasmntopt (mntent, "bind"))
775         continue;
776 #endif
777
778       mount_entry = g_new0 (GUnixMountPoint, 1);
779       mount_entry->mount_path = g_strdup (mntent->mnt_dir);
780       if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
781         mount_entry->device_path = g_strdup (_resolve_dev_root ());
782       else
783         mount_entry->device_path = g_strdup (mntent->mnt_fsname);
784       mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
785       mount_entry->options = g_strdup (mntent->mnt_opts);
786       
787 #ifdef HAVE_HASMNTOPT
788       if (hasmntopt (mntent, MNTOPT_RO) != NULL)
789         mount_entry->is_read_only = TRUE;
790       
791       if (hasmntopt (mntent, "loop") != NULL)
792         mount_entry->is_loopback = TRUE;
793       
794 #endif
795       
796       if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
797 #ifdef HAVE_HASMNTOPT
798           || (hasmntopt (mntent, "user") != NULL
799               && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
800           || hasmntopt (mntent, "pamconsole") != NULL
801           || hasmntopt (mntent, "users") != NULL
802           || hasmntopt (mntent, "owner") != NULL
803 #endif
804           )
805         mount_entry->is_user_mountable = TRUE;
806       
807       return_list = g_list_prepend (return_list, mount_entry);
808     }
809   
810   endmntent (file);
811
812 #ifndef HAVE_GETMNTENT_R
813   G_UNLOCK (getmntent);
814 #endif
815   
816   return g_list_reverse (return_list);
817 }
818
819 #elif defined (HAVE_SYS_MNTTAB_H)
820
821 static GList *
822 _g_get_unix_mount_points (void)
823 {
824   struct mnttab mntent;
825   FILE *file;
826   char *read_file;
827   GUnixMountPoint *mount_entry;
828   GList *return_list;
829   
830   read_file = get_fstab_file ();
831   
832   file = setmntent (read_file, "r");
833   if (file == NULL)
834     return NULL;
835
836   return_list = NULL;
837   
838   G_LOCK (getmntent);
839   while (! getmntent (file, &mntent))
840     {
841       if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
842           (strcmp (mntent.mnt_mountp, "swap") == 0) ||
843           (strcmp (mntent.mnt_mountp, "none") == 0))
844         continue;
845       
846       mount_entry = g_new0 (GUnixMountPoint, 1);
847       
848       mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
849       mount_entry->device_path = g_strdup (mntent.mnt_special);
850       mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
851       mount_entry->options = g_strdup (mntent.mnt_mntopts);
852       
853 #ifdef HAVE_HASMNTOPT
854       if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
855         mount_entry->is_read_only = TRUE;
856       
857       if (hasmntopt (&mntent, "lofs") != NULL)
858         mount_entry->is_loopback = TRUE;
859 #endif
860       
861       if ((mntent.mnt_fstype != NULL)
862 #ifdef HAVE_HASMNTOPT
863           || (hasmntopt (&mntent, "user") != NULL
864               && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
865           || hasmntopt (&mntent, "pamconsole") != NULL
866           || hasmntopt (&mntent, "users") != NULL
867           || hasmntopt (&mntent, "owner") != NULL
868 #endif
869           )
870         mount_entry->is_user_mountable = TRUE;
871       
872       return_list = g_list_prepend (return_list, mount_entry);
873     }
874   
875   endmntent (file);
876   G_UNLOCK (getmntent);
877   
878   return g_list_reverse (return_list);
879 }
880 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
881
882 /* functions to parse /etc/filesystems on aix */
883
884 /* read character, ignoring comments (begin with '*', end with '\n' */
885 static int
886 aix_fs_getc (FILE *fd)
887 {
888   int c;
889   
890   while ((c = getc (fd)) == '*')
891     {
892       while (((c = getc (fd)) != '\n') && (c != EOF))
893         ;
894     }
895 }
896
897 /* eat all continuous spaces in a file */
898 static int
899 aix_fs_ignorespace (FILE *fd)
900 {
901   int c;
902   
903   while ((c = aix_fs_getc (fd)) != EOF)
904     {
905       if (!g_ascii_isspace (c))
906         {
907           ungetc (c,fd);
908           return c;
909         }
910     }
911   
912   return EOF;
913 }
914
915 /* read one word from file */
916 static int
917 aix_fs_getword (FILE *fd, 
918                 char *word)
919 {
920   int c;
921   
922   aix_fs_ignorespace (fd);
923
924   while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
925     {
926       if (c == '"')
927         {
928           while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
929             *word++ = c;
930           else
931             *word++ = c;
932         }
933     }
934   *word = 0;
935   
936   return c;
937 }
938
939 typedef struct {
940   char mnt_mount[PATH_MAX];
941   char mnt_special[PATH_MAX];
942   char mnt_fstype[16];
943   char mnt_options[128];
944 } AixMountTableEntry;
945
946 /* read mount points properties */
947 static int
948 aix_fs_get (FILE               *fd, 
949             AixMountTableEntry *prop)
950 {
951   static char word[PATH_MAX] = { 0 };
952   char value[PATH_MAX];
953   
954   /* read stanza */
955   if (word[0] == 0)
956     {
957       if (aix_fs_getword (fd, word) == EOF)
958         return EOF;
959     }
960
961   word[strlen(word) - 1] = 0;
962   strcpy (prop->mnt_mount, word);
963   
964   /* read attributes and value */
965   
966   while (aix_fs_getword (fd, word) != EOF)
967     {
968       /* test if is attribute or new stanza */
969       if (word[strlen(word) - 1] == ':')
970         return 0;
971       
972       /* read "=" */
973       aix_fs_getword (fd, value);
974       
975       /* read value */
976       aix_fs_getword (fd, value);
977       
978       if (strcmp (word, "dev") == 0)
979         strcpy (prop->mnt_special, value);
980       else if (strcmp (word, "vfs") == 0)
981         strcpy (prop->mnt_fstype, value);
982       else if (strcmp (word, "options") == 0)
983         strcpy(prop->mnt_options, value);
984     }
985   
986   return 0;
987 }
988
989 static GList *
990 _g_get_unix_mount_points (void)
991 {
992   struct mntent *mntent;
993   FILE *file;
994   char *read_file;
995   GUnixMountPoint *mount_entry;
996   AixMountTableEntry mntent;
997   GList *return_list;
998   
999   read_file = get_fstab_file ();
1000   
1001   file = setmntent (read_file, "r");
1002   if (file == NULL)
1003     return NULL;
1004   
1005   return_list = NULL;
1006   
1007   while (!aix_fs_get (file, &mntent))
1008     {
1009       if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1010         {
1011           mount_entry = g_new0 (GUnixMountPoint, 1);
1012           
1013           mount_entry->mount_path = g_strdup (mntent.mnt_mount);
1014           mount_entry->device_path = g_strdup (mntent.mnt_special);
1015           mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
1016           mount_entry->options = g_strdup (mntent.mnt_options);
1017           mount_entry->is_read_only = TRUE;
1018           mount_entry->is_user_mountable = TRUE;
1019           
1020           return_list = g_list_prepend (return_list, mount_entry);
1021         }
1022     }
1023         
1024   endmntent (file);
1025   
1026   return g_list_reverse (return_list);
1027 }
1028
1029 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1030
1031 static GList *
1032 _g_get_unix_mount_points (void)
1033 {
1034   struct fstab *fstab = NULL;
1035   GUnixMountPoint *mount_entry;
1036   GList *return_list;
1037 #ifdef HAVE_SYS_SYSCTL_H
1038   int usermnt = 0;
1039   size_t len = sizeof(usermnt);
1040   struct stat sb;
1041 #endif
1042   
1043   if (!setfsent ())
1044     return NULL;
1045
1046   return_list = NULL;
1047   
1048 #ifdef HAVE_SYS_SYSCTL_H
1049 #if defined(HAVE_SYSCTLBYNAME)
1050   sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1051 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1052   {
1053     int mib[2];
1054     
1055     mib[0] = CTL_VFS;
1056     mib[1] = VFS_USERMOUNT;
1057     sysctl (mib, 2, &usermnt, &len, NULL, 0);
1058   }
1059 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1060   {
1061     int mib[2];
1062     
1063     mib[0] = CTL_KERN;
1064     mib[1] = KERN_USERMOUNT;
1065     sysctl (mib, 2, &usermnt, &len, NULL, 0);
1066   }
1067 #endif
1068 #endif
1069   
1070   while ((fstab = getfsent ()) != NULL)
1071     {
1072       if (strcmp (fstab->fs_vfstype, "swap") == 0)
1073         continue;
1074       
1075       mount_entry = g_new0 (GUnixMountPoint, 1);
1076       
1077       mount_entry->mount_path = g_strdup (fstab->fs_file);
1078       mount_entry->device_path = g_strdup (fstab->fs_spec);
1079       mount_entry->filesystem_type = g_strdup (fstab->fs_vfstype);
1080       mount_entry->options = g_strdup (fstab->fs_mntops);
1081       
1082       if (strcmp (fstab->fs_type, "ro") == 0)
1083         mount_entry->is_read_only = TRUE;
1084
1085 #ifdef HAVE_SYS_SYSCTL_H
1086       if (usermnt != 0)
1087         {
1088           uid_t uid = getuid ();
1089           if (stat (fstab->fs_file, &sb) == 0)
1090             {
1091               if (uid == 0 || sb.st_uid == uid)
1092                 mount_entry->is_user_mountable = TRUE;
1093             }
1094         }
1095 #endif
1096
1097       return_list = g_list_prepend (return_list, mount_entry);
1098     }
1099   
1100   endfsent ();
1101   
1102   return g_list_reverse (return_list);
1103 }
1104 #elif defined(__INTERIX)
1105 static GList *
1106 _g_get_unix_mount_points (void)
1107 {
1108   return _g_get_unix_mounts ();
1109 }
1110 #else
1111 #error No g_get_mount_table() implementation for system
1112 #endif
1113
1114 static guint64
1115 get_mounts_timestamp (void)
1116 {
1117   const char *monitor_file;
1118   struct stat buf;
1119
1120   monitor_file = get_mtab_monitor_file ();
1121   if (monitor_file)
1122     {
1123       if (stat (monitor_file, &buf) == 0)
1124         return (guint64)buf.st_mtime;
1125     }
1126   return 0;
1127 }
1128
1129 static guint64
1130 get_mount_points_timestamp (void)
1131 {
1132   const char *monitor_file;
1133   struct stat buf;
1134
1135   monitor_file = get_fstab_file ();
1136   if (monitor_file)
1137     {
1138       if (stat (monitor_file, &buf) == 0)
1139         return (guint64)buf.st_mtime;
1140     }
1141   return 0;
1142 }
1143
1144 /**
1145  * g_unix_mounts_get: (skip)
1146  * @time_read: (out) (allow-none): guint64 to contain a timestamp, or %NULL
1147  *
1148  * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1149  * If @time_read is set, it will be filled with the mount
1150  * timestamp, allowing for checking if the mounts have changed
1151  * with g_unix_mounts_changed_since().
1152  *
1153  * Returns: (element-type GUnixMountEntry) (transfer full):
1154  *     a #GList of the UNIX mounts.
1155  **/
1156 GList *
1157 g_unix_mounts_get (guint64 *time_read)
1158 {
1159   if (time_read)
1160     *time_read = get_mounts_timestamp ();
1161
1162   return _g_get_unix_mounts ();
1163 }
1164
1165 /**
1166  * g_unix_mount_at: (skip)
1167  * @mount_path: path for a possible unix mount.
1168  * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1169  * 
1170  * Gets a #GUnixMountEntry for a given mount path. If @time_read
1171  * is set, it will be filled with a unix timestamp for checking
1172  * if the mounts have changed since with g_unix_mounts_changed_since().
1173  * 
1174  * Returns: (transfer full): a #GUnixMountEntry.
1175  **/
1176 GUnixMountEntry *
1177 g_unix_mount_at (const char *mount_path,
1178                  guint64    *time_read)
1179 {
1180   GList *mounts, *l;
1181   GUnixMountEntry *mount_entry, *found;
1182   
1183   mounts = g_unix_mounts_get (time_read);
1184
1185   found = NULL;
1186   for (l = mounts; l != NULL; l = l->next)
1187     {
1188       mount_entry = l->data;
1189
1190       if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1191         found = mount_entry;
1192       else
1193         g_unix_mount_free (mount_entry);
1194     }
1195   g_list_free (mounts);
1196
1197   return found;
1198 }
1199
1200 /**
1201  * g_unix_mount_points_get: (skip)
1202  * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1203  *
1204  * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1205  * If @time_read is set, it will be filled with the mount timestamp,
1206  * allowing for checking if the mounts have changed with
1207  * g_unix_mount_points_changed_since().
1208  *
1209  * Returns: (element-type GUnixMountPoint) (transfer full):
1210  *     a #GList of the UNIX mountpoints.
1211  **/
1212 GList *
1213 g_unix_mount_points_get (guint64 *time_read)
1214 {
1215   if (time_read)
1216     *time_read = get_mount_points_timestamp ();
1217
1218   return _g_get_unix_mount_points ();
1219 }
1220
1221 /**
1222  * g_unix_mounts_changed_since:
1223  * @time: guint64 to contain a timestamp.
1224  * 
1225  * Checks if the unix mounts have changed since a given unix time.
1226  * 
1227  * Returns: %TRUE if the mounts have changed since @time. 
1228  **/
1229 gboolean
1230 g_unix_mounts_changed_since (guint64 time)
1231 {
1232   return get_mounts_timestamp () != time;
1233 }
1234
1235 /**
1236  * g_unix_mount_points_changed_since:
1237  * @time: guint64 to contain a timestamp.
1238  * 
1239  * Checks if the unix mount points have changed since a given unix time.
1240  * 
1241  * Returns: %TRUE if the mount points have changed since @time. 
1242  **/
1243 gboolean
1244 g_unix_mount_points_changed_since (guint64 time)
1245 {
1246   return get_mount_points_timestamp () != time;
1247 }
1248
1249 static void
1250 g_unix_mount_monitor_finalize (GObject *object)
1251 {
1252   GUnixMountMonitor *monitor;
1253   
1254   monitor = G_UNIX_MOUNT_MONITOR (object);
1255
1256   if (monitor->fstab_monitor)
1257     {
1258       g_file_monitor_cancel (monitor->fstab_monitor);
1259       g_object_unref (monitor->fstab_monitor);
1260     }
1261   
1262   if (monitor->proc_mounts_watch_source != NULL)
1263     g_source_destroy (monitor->proc_mounts_watch_source);
1264
1265   if (monitor->mtab_monitor)
1266     {
1267       g_file_monitor_cancel (monitor->mtab_monitor);
1268       g_object_unref (monitor->mtab_monitor);
1269     }
1270
1271   the_mount_monitor = NULL;
1272
1273   G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1274 }
1275
1276
1277 static void
1278 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1279 {
1280   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1281
1282   gobject_class->finalize = g_unix_mount_monitor_finalize;
1283  
1284   /**
1285    * GUnixMountMonitor::mounts-changed:
1286    * @monitor: the object on which the signal is emitted
1287    * 
1288    * Emitted when the unix mounts have changed.
1289    */ 
1290   signals[MOUNTS_CHANGED] =
1291     g_signal_new ("mounts-changed",
1292                   G_TYPE_FROM_CLASS (klass),
1293                   G_SIGNAL_RUN_LAST,
1294                   0,
1295                   NULL, NULL,
1296                   g_cclosure_marshal_VOID__VOID,
1297                   G_TYPE_NONE, 0);
1298
1299   /**
1300    * GUnixMountMonitor::mountpoints-changed:
1301    * @monitor: the object on which the signal is emitted
1302    * 
1303    * Emitted when the unix mount points have changed.
1304    */
1305   signals[MOUNTPOINTS_CHANGED] =
1306     g_signal_new ("mountpoints-changed",
1307                   G_TYPE_FROM_CLASS (klass),
1308                   G_SIGNAL_RUN_LAST,
1309                   0,
1310                   NULL, NULL,
1311                   g_cclosure_marshal_VOID__VOID,
1312                   G_TYPE_NONE, 0);
1313 }
1314
1315 static void
1316 fstab_file_changed (GFileMonitor      *monitor,
1317                     GFile             *file,
1318                     GFile             *other_file,
1319                     GFileMonitorEvent  event_type,
1320                     gpointer           user_data)
1321 {
1322   GUnixMountMonitor *mount_monitor;
1323
1324   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1325       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1326       event_type != G_FILE_MONITOR_EVENT_DELETED)
1327     return;
1328
1329   mount_monitor = user_data;
1330   g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
1331 }
1332
1333 static void
1334 mtab_file_changed (GFileMonitor      *monitor,
1335                    GFile             *file,
1336                    GFile             *other_file,
1337                    GFileMonitorEvent  event_type,
1338                    gpointer           user_data)
1339 {
1340   GUnixMountMonitor *mount_monitor;
1341
1342   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1343       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1344       event_type != G_FILE_MONITOR_EVENT_DELETED)
1345     return;
1346   
1347   mount_monitor = user_data;
1348   g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1349 }
1350
1351 static gboolean
1352 proc_mounts_changed (GIOChannel   *channel,
1353                      GIOCondition  cond,
1354                      gpointer      user_data)
1355 {
1356   GUnixMountMonitor *mount_monitor = G_UNIX_MOUNT_MONITOR (user_data);
1357   if (cond & G_IO_ERR)
1358     g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1359   return TRUE;
1360 }
1361
1362 static void
1363 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1364 {
1365   GFile *file;
1366     
1367   if (get_fstab_file () != NULL)
1368     {
1369       file = g_file_new_for_path (get_fstab_file ());
1370       monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1371       g_object_unref (file);
1372       
1373       g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
1374     }
1375   
1376   if (get_mtab_monitor_file () != NULL)
1377     {
1378       const gchar *mtab_path;
1379
1380       mtab_path = get_mtab_monitor_file ();
1381       /* /proc/mounts monitoring is special - can't just use GFileMonitor.
1382        * See 'man proc' for more details.
1383        */
1384       if (g_strcmp0 (mtab_path, "/proc/mounts") == 0)
1385         {
1386           GIOChannel *proc_mounts_channel;
1387           GError *error = NULL;
1388           proc_mounts_channel = g_io_channel_new_file ("/proc/mounts", "r", &error);
1389           if (proc_mounts_channel == NULL)
1390             {
1391               g_warning ("Error creating IO channel for /proc/mounts: %s (%s, %d)",
1392                          error->message, g_quark_to_string (error->domain), error->code);
1393               g_error_free (error);
1394             }
1395           else
1396             {
1397               monitor->proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1398               g_source_set_callback (monitor->proc_mounts_watch_source,
1399                                      (GSourceFunc) proc_mounts_changed,
1400                                      monitor,
1401                                      NULL);
1402               g_source_attach (monitor->proc_mounts_watch_source,
1403                                g_main_context_get_thread_default ());
1404               g_source_unref (monitor->proc_mounts_watch_source);
1405               g_io_channel_unref (proc_mounts_channel);
1406             }
1407         }
1408       else
1409         {
1410           file = g_file_new_for_path (mtab_path);
1411           monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1412           g_object_unref (file);
1413           g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
1414         }
1415     }
1416 }
1417
1418 /**
1419  * g_unix_mount_monitor_set_rate_limit:
1420  * @mount_monitor: a #GUnixMountMonitor
1421  * @limit_msec: a integer with the limit in milliseconds to
1422  *     poll for changes.
1423  *
1424  * Sets the rate limit to which the @mount_monitor will report
1425  * consecutive change events to the mount and mount point entry files.
1426  *
1427  * Since: 2.18
1428  */
1429 void
1430 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1431                                      gint               limit_msec)
1432 {
1433   g_return_if_fail (G_IS_UNIX_MOUNT_MONITOR (mount_monitor));
1434
1435   if (mount_monitor->fstab_monitor != NULL)
1436     g_file_monitor_set_rate_limit (mount_monitor->fstab_monitor, limit_msec);
1437
1438   if (mount_monitor->mtab_monitor != NULL)
1439     g_file_monitor_set_rate_limit (mount_monitor->mtab_monitor, limit_msec);
1440 }
1441
1442 /**
1443  * g_unix_mount_monitor_new:
1444  * 
1445  * Gets a new #GUnixMountMonitor. The default rate limit for which the
1446  * monitor will report consecutive changes for the mount and mount
1447  * point entry files is the default for a #GFileMonitor. Use
1448  * g_unix_mount_monitor_set_rate_limit() to change this.
1449  * 
1450  * Returns: a #GUnixMountMonitor. 
1451  */
1452 GUnixMountMonitor *
1453 g_unix_mount_monitor_new (void)
1454 {
1455   if (the_mount_monitor == NULL)
1456     {
1457       the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
1458       return the_mount_monitor;
1459     }
1460   
1461   return g_object_ref (the_mount_monitor);
1462 }
1463
1464 /**
1465  * g_unix_mount_free:
1466  * @mount_entry: a #GUnixMountEntry.
1467  * 
1468  * Frees a unix mount.
1469  */
1470 void
1471 g_unix_mount_free (GUnixMountEntry *mount_entry)
1472 {
1473   g_return_if_fail (mount_entry != NULL);
1474
1475   g_free (mount_entry->mount_path);
1476   g_free (mount_entry->device_path);
1477   g_free (mount_entry->filesystem_type);
1478   g_free (mount_entry);
1479 }
1480
1481 /**
1482  * g_unix_mount_point_free:
1483  * @mount_point: unix mount point to free.
1484  * 
1485  * Frees a unix mount point.
1486  */
1487 void
1488 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1489 {
1490   g_return_if_fail (mount_point != NULL);
1491
1492   g_free (mount_point->mount_path);
1493   g_free (mount_point->device_path);
1494   g_free (mount_point->filesystem_type);
1495   g_free (mount_point->options);
1496   g_free (mount_point);
1497 }
1498
1499 /**
1500  * g_unix_mount_compare:
1501  * @mount1: first #GUnixMountEntry to compare.
1502  * @mount2: second #GUnixMountEntry to compare.
1503  * 
1504  * Compares two unix mounts.
1505  * 
1506  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1507  * or less than @mount2, respectively. 
1508  */
1509 gint
1510 g_unix_mount_compare (GUnixMountEntry *mount1,
1511                       GUnixMountEntry *mount2)
1512 {
1513   int res;
1514
1515   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1516   
1517   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1518   if (res != 0)
1519     return res;
1520         
1521   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1522   if (res != 0)
1523     return res;
1524         
1525   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1526   if (res != 0)
1527     return res;
1528
1529   res =  mount1->is_read_only - mount2->is_read_only;
1530   if (res != 0)
1531     return res;
1532   
1533   return 0;
1534 }
1535
1536 /**
1537  * g_unix_mount_get_mount_path:
1538  * @mount_entry: input #GUnixMountEntry to get the mount path for.
1539  * 
1540  * Gets the mount path for a unix mount.
1541  * 
1542  * Returns: the mount path for @mount_entry.
1543  */
1544 const gchar *
1545 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1546 {
1547   g_return_val_if_fail (mount_entry != NULL, NULL);
1548
1549   return mount_entry->mount_path;
1550 }
1551
1552 /**
1553  * g_unix_mount_get_device_path:
1554  * @mount_entry: a #GUnixMount.
1555  * 
1556  * Gets the device path for a unix mount.
1557  * 
1558  * Returns: a string containing the device path.
1559  */
1560 const gchar *
1561 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1562 {
1563   g_return_val_if_fail (mount_entry != NULL, NULL);
1564
1565   return mount_entry->device_path;
1566 }
1567
1568 /**
1569  * g_unix_mount_get_fs_type:
1570  * @mount_entry: a #GUnixMount.
1571  * 
1572  * Gets the filesystem type for the unix mount.
1573  * 
1574  * Returns: a string containing the file system type.
1575  */
1576 const gchar *
1577 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1578 {
1579   g_return_val_if_fail (mount_entry != NULL, NULL);
1580
1581   return mount_entry->filesystem_type;
1582 }
1583
1584 /**
1585  * g_unix_mount_is_readonly:
1586  * @mount_entry: a #GUnixMount.
1587  * 
1588  * Checks if a unix mount is mounted read only.
1589  * 
1590  * Returns: %TRUE if @mount_entry is read only.
1591  */
1592 gboolean
1593 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1594 {
1595   g_return_val_if_fail (mount_entry != NULL, FALSE);
1596
1597   return mount_entry->is_read_only;
1598 }
1599
1600 /**
1601  * g_unix_mount_is_system_internal:
1602  * @mount_entry: a #GUnixMount.
1603  * 
1604  * Checks if a unix mount is a system path.
1605  * 
1606  * Returns: %TRUE if the unix mount is for a system path.
1607  */
1608 gboolean
1609 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1610 {
1611   g_return_val_if_fail (mount_entry != NULL, FALSE);
1612
1613   return mount_entry->is_system_internal;
1614 }
1615
1616 /**
1617  * g_unix_mount_point_compare:
1618  * @mount1: a #GUnixMount.
1619  * @mount2: a #GUnixMount.
1620  * 
1621  * Compares two unix mount points.
1622  * 
1623  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1624  * or less than @mount2, respectively.
1625  */
1626 gint
1627 g_unix_mount_point_compare (GUnixMountPoint *mount1,
1628                             GUnixMountPoint *mount2)
1629 {
1630   int res;
1631
1632   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1633
1634   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1635   if (res != 0) 
1636     return res;
1637         
1638   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1639   if (res != 0) 
1640     return res;
1641         
1642   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1643   if (res != 0) 
1644     return res;
1645
1646   res = g_strcmp0 (mount1->options, mount2->options);
1647   if (res != 0) 
1648     return res;
1649
1650   res =  mount1->is_read_only - mount2->is_read_only;
1651   if (res != 0) 
1652     return res;
1653
1654   res = mount1->is_user_mountable - mount2->is_user_mountable;
1655   if (res != 0) 
1656     return res;
1657
1658   res = mount1->is_loopback - mount2->is_loopback;
1659   if (res != 0)
1660     return res;
1661   
1662   return 0;
1663 }
1664
1665 /**
1666  * g_unix_mount_point_get_mount_path:
1667  * @mount_point: a #GUnixMountPoint.
1668  * 
1669  * Gets the mount path for a unix mount point.
1670  * 
1671  * Returns: a string containing the mount path.
1672  */
1673 const gchar *
1674 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
1675 {
1676   g_return_val_if_fail (mount_point != NULL, NULL);
1677
1678   return mount_point->mount_path;
1679 }
1680
1681 /**
1682  * g_unix_mount_point_get_device_path:
1683  * @mount_point: a #GUnixMountPoint.
1684  * 
1685  * Gets the device path for a unix mount point.
1686  * 
1687  * Returns: a string containing the device path.
1688  */
1689 const gchar *
1690 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
1691 {
1692   g_return_val_if_fail (mount_point != NULL, NULL);
1693
1694   return mount_point->device_path;
1695 }
1696
1697 /**
1698  * g_unix_mount_point_get_fs_type:
1699  * @mount_point: a #GUnixMountPoint.
1700  * 
1701  * Gets the file system type for the mount point.
1702  * 
1703  * Returns: a string containing the file system type.
1704  */
1705 const gchar *
1706 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
1707 {
1708   g_return_val_if_fail (mount_point != NULL, NULL);
1709
1710   return mount_point->filesystem_type;
1711 }
1712
1713 /**
1714  * g_unix_mount_point_get_options:
1715  * @mount_point: a #GUnixMountPoint.
1716  * 
1717  * Gets the options for the mount point.
1718  * 
1719  * Returns: a string containing the options.
1720  *
1721  * Since: 2.32
1722  */
1723 const gchar *
1724 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
1725 {
1726   g_return_val_if_fail (mount_point != NULL, NULL);
1727
1728   return mount_point->options;
1729 }
1730
1731 /**
1732  * g_unix_mount_point_is_readonly:
1733  * @mount_point: a #GUnixMountPoint.
1734  * 
1735  * Checks if a unix mount point is read only.
1736  * 
1737  * Returns: %TRUE if a mount point is read only.
1738  */
1739 gboolean
1740 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
1741 {
1742   g_return_val_if_fail (mount_point != NULL, FALSE);
1743
1744   return mount_point->is_read_only;
1745 }
1746
1747 /**
1748  * g_unix_mount_point_is_user_mountable:
1749  * @mount_point: a #GUnixMountPoint.
1750  * 
1751  * Checks if a unix mount point is mountable by the user.
1752  * 
1753  * Returns: %TRUE if the mount point is user mountable.
1754  */
1755 gboolean
1756 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
1757 {
1758   g_return_val_if_fail (mount_point != NULL, FALSE);
1759
1760   return mount_point->is_user_mountable;
1761 }
1762
1763 /**
1764  * g_unix_mount_point_is_loopback:
1765  * @mount_point: a #GUnixMountPoint.
1766  * 
1767  * Checks if a unix mount point is a loopback device.
1768  * 
1769  * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise. 
1770  */
1771 gboolean
1772 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
1773 {
1774   g_return_val_if_fail (mount_point != NULL, FALSE);
1775
1776   return mount_point->is_loopback;
1777 }
1778
1779 static GUnixMountType
1780 guess_mount_type (const char *mount_path,
1781                   const char *device_path,
1782                   const char *filesystem_type)
1783 {
1784   GUnixMountType type;
1785   char *basename;
1786
1787   type = G_UNIX_MOUNT_TYPE_UNKNOWN;
1788   
1789   if ((strcmp (filesystem_type, "udf") == 0) ||
1790       (strcmp (filesystem_type, "iso9660") == 0) ||
1791       (strcmp (filesystem_type, "cd9660") == 0))
1792     type = G_UNIX_MOUNT_TYPE_CDROM;
1793   else if ((strcmp (filesystem_type, "nfs") == 0) ||
1794            (strcmp (filesystem_type, "nfs4") == 0))
1795     type = G_UNIX_MOUNT_TYPE_NFS;
1796   else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
1797            g_str_has_prefix (device_path, "/dev/fd") ||
1798            g_str_has_prefix (device_path, "/dev/floppy"))
1799     type = G_UNIX_MOUNT_TYPE_FLOPPY;
1800   else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
1801            g_str_has_prefix (device_path, "/dev/acd") ||
1802            g_str_has_prefix (device_path, "/dev/cd"))
1803     type = G_UNIX_MOUNT_TYPE_CDROM;
1804   else if (g_str_has_prefix (device_path, "/vol/"))
1805     {
1806       const char *name = mount_path + strlen ("/");
1807       
1808       if (g_str_has_prefix (name, "cdrom"))
1809         type = G_UNIX_MOUNT_TYPE_CDROM;
1810       else if (g_str_has_prefix (name, "floppy") ||
1811                g_str_has_prefix (device_path, "/vol/dev/diskette/")) 
1812         type = G_UNIX_MOUNT_TYPE_FLOPPY;
1813       else if (g_str_has_prefix (name, "rmdisk")) 
1814         type = G_UNIX_MOUNT_TYPE_ZIP;
1815       else if (g_str_has_prefix (name, "jaz"))
1816         type = G_UNIX_MOUNT_TYPE_JAZ;
1817       else if (g_str_has_prefix (name, "memstick"))
1818         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1819     }
1820   else
1821     {
1822       basename = g_path_get_basename (mount_path);
1823       
1824       if (g_str_has_prefix (basename, "cdr") ||
1825           g_str_has_prefix (basename, "cdwriter") ||
1826           g_str_has_prefix (basename, "burn") ||
1827           g_str_has_prefix (basename, "dvdr"))
1828         type = G_UNIX_MOUNT_TYPE_CDROM;
1829       else if (g_str_has_prefix (basename, "floppy"))
1830         type = G_UNIX_MOUNT_TYPE_FLOPPY;
1831       else if (g_str_has_prefix (basename, "zip"))
1832         type = G_UNIX_MOUNT_TYPE_ZIP;
1833       else if (g_str_has_prefix (basename, "jaz"))
1834         type = G_UNIX_MOUNT_TYPE_JAZ;
1835       else if (g_str_has_prefix (basename, "camera"))
1836         type = G_UNIX_MOUNT_TYPE_CAMERA;
1837       else if (g_str_has_prefix (basename, "memstick") ||
1838                g_str_has_prefix (basename, "memory_stick") ||
1839                g_str_has_prefix (basename, "ram"))
1840         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1841       else if (g_str_has_prefix (basename, "compact_flash"))
1842         type = G_UNIX_MOUNT_TYPE_CF;
1843       else if (g_str_has_prefix (basename, "smart_media"))
1844         type = G_UNIX_MOUNT_TYPE_SM;
1845       else if (g_str_has_prefix (basename, "sd_mmc"))
1846         type = G_UNIX_MOUNT_TYPE_SDMMC;
1847       else if (g_str_has_prefix (basename, "ipod"))
1848         type = G_UNIX_MOUNT_TYPE_IPOD;
1849       
1850       g_free (basename);
1851     }
1852   
1853   if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
1854     type = G_UNIX_MOUNT_TYPE_HD;
1855   
1856   return type;
1857 }
1858
1859 /*
1860  * g_unix_mount_guess_type:
1861  * @mount_entry: a #GUnixMount.
1862  * 
1863  * Guesses the type of a unix mount. If the mount type cannot be 
1864  * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1865  * 
1866  * Returns: a #GUnixMountType. 
1867  */
1868 static GUnixMountType
1869 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
1870 {
1871   g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1872   g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1873   g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1874   g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1875
1876   return guess_mount_type (mount_entry->mount_path,
1877                            mount_entry->device_path,
1878                            mount_entry->filesystem_type);
1879 }
1880
1881 /*
1882  * g_unix_mount_point_guess_type:
1883  * @mount_point: a #GUnixMountPoint.
1884  * 
1885  * Guesses the type of a unix mount point. 
1886  * If the mount type cannot be determined, 
1887  * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1888  * 
1889  * Returns: a #GUnixMountType.
1890  */
1891 static GUnixMountType
1892 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
1893 {
1894   g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1895   g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1896   g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1897   g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1898
1899   return guess_mount_type (mount_point->mount_path,
1900                            mount_point->device_path,
1901                            mount_point->filesystem_type);
1902 }
1903
1904 static const char *
1905 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
1906 {
1907   const char *icon_name;
1908   
1909   switch (type)
1910     {
1911     case G_UNIX_MOUNT_TYPE_HD:
1912       if (is_mount_point)
1913         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1914       else
1915         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
1916       break;
1917     case G_UNIX_MOUNT_TYPE_FLOPPY:
1918     case G_UNIX_MOUNT_TYPE_ZIP:
1919     case G_UNIX_MOUNT_TYPE_JAZ:
1920       if (is_mount_point)
1921         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1922       else
1923         icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
1924       break;
1925     case G_UNIX_MOUNT_TYPE_CDROM:
1926       if (is_mount_point)
1927         icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
1928       else
1929         icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
1930       break;
1931     case G_UNIX_MOUNT_TYPE_NFS:
1932         icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
1933       break;
1934     case G_UNIX_MOUNT_TYPE_MEMSTICK:
1935       if (is_mount_point)
1936         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1937       else
1938         icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
1939       break;
1940     case G_UNIX_MOUNT_TYPE_CAMERA:
1941       if (is_mount_point)
1942         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1943       else
1944         icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
1945       break;
1946     case G_UNIX_MOUNT_TYPE_IPOD:
1947       if (is_mount_point)
1948         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1949       else
1950         icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
1951       break;
1952     case G_UNIX_MOUNT_TYPE_UNKNOWN:
1953     default:
1954       if (is_mount_point)
1955         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1956       else
1957         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
1958       break;
1959     }
1960
1961   return icon_name;
1962 }
1963
1964 /**
1965  * g_unix_mount_guess_name:
1966  * @mount_entry: a #GUnixMountEntry
1967  * 
1968  * Guesses the name of a Unix mount. 
1969  * The result is a translated string.
1970  *
1971  * Returns: A newly allocated string that must
1972  *     be freed with g_free()
1973  */
1974 gchar *
1975 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
1976 {
1977   char *name;
1978
1979   if (strcmp (mount_entry->mount_path, "/") == 0)
1980     name = g_strdup (_("Filesystem root"));
1981   else
1982     name = g_filename_display_basename (mount_entry->mount_path);
1983
1984   return name;
1985 }
1986
1987 /**
1988  * g_unix_mount_guess_icon:
1989  * @mount_entry: a #GUnixMountEntry
1990  * 
1991  * Guesses the icon of a Unix mount. 
1992  *
1993  * Returns: (transfer full): a #GIcon
1994  */
1995 GIcon *
1996 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
1997 {
1998   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
1999 }
2000
2001 /**
2002  * g_unix_mount_guess_symbolic_icon:
2003  * @mount_entry: a #GUnixMountEntry
2004  *
2005  * Guesses the symbolic icon of a Unix mount.
2006  *
2007  * Returns: (transfer full): a #GIcon
2008  *
2009  * Since: 2.34
2010  */
2011 GIcon *
2012 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2013 {
2014   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2015 }
2016
2017 /**
2018  * g_unix_mount_point_guess_name:
2019  * @mount_point: a #GUnixMountPoint
2020  * 
2021  * Guesses the name of a Unix mount point. 
2022  * The result is a translated string.
2023  *
2024  * Returns: A newly allocated string that must 
2025  *     be freed with g_free()
2026  */
2027 gchar *
2028 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2029 {
2030   char *name;
2031
2032   if (strcmp (mount_point->mount_path, "/") == 0)
2033     name = g_strdup (_("Filesystem root"));
2034   else
2035     name = g_filename_display_basename (mount_point->mount_path);
2036
2037   return name;
2038 }
2039
2040 /**
2041  * g_unix_mount_point_guess_icon:
2042  * @mount_point: a #GUnixMountPoint
2043  * 
2044  * Guesses the icon of a Unix mount point. 
2045  *
2046  * Returns: (transfer full): a #GIcon
2047  */
2048 GIcon *
2049 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2050 {
2051   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2052 }
2053
2054 /**
2055  * g_unix_mount_point_guess_symbolic_icon:
2056  * @mount_point: a #GUnixMountPoint
2057  *
2058  * Guesses the symbolic icon of a Unix mount point.
2059  *
2060  * Returns: (transfer full): a #GIcon
2061  *
2062  * Since: 2.34
2063  */
2064 GIcon *
2065 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2066 {
2067   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2068 }
2069
2070 /**
2071  * g_unix_mount_guess_can_eject:
2072  * @mount_entry: a #GUnixMountEntry
2073  * 
2074  * Guesses whether a Unix mount can be ejected.
2075  *
2076  * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2077  */
2078 gboolean
2079 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2080 {
2081   GUnixMountType guessed_type;
2082
2083   guessed_type = g_unix_mount_guess_type (mount_entry);
2084   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2085       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2086     return TRUE;
2087
2088   return FALSE;
2089 }
2090
2091 /**
2092  * g_unix_mount_guess_should_display:
2093  * @mount_entry: a #GUnixMountEntry
2094  * 
2095  * Guesses whether a Unix mount should be displayed in the UI.
2096  *
2097  * Returns: %TRUE if @mount_entry is deemed to be displayable.
2098  */
2099 gboolean
2100 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2101 {
2102   const char *mount_path;
2103   const gchar *user_name;
2104   gsize user_name_len;
2105
2106   /* Never display internal mountpoints */
2107   if (g_unix_mount_is_system_internal (mount_entry))
2108     return FALSE;
2109   
2110   /* Only display things in /media (which are generally user mountable)
2111      and home dir (fuse stuff) and /run/media/$USER */
2112   mount_path = mount_entry->mount_path;
2113   if (mount_path != NULL)
2114     {
2115       gboolean is_in_runtime_dir = FALSE;
2116       /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2117       if (g_strstr_len (mount_path, -1, "/.") != NULL)
2118         return FALSE;
2119
2120       /* Check /run/media/$USER/ */
2121       user_name = g_get_user_name ();
2122       user_name_len = strlen (user_name);
2123       if (strncmp (mount_path, "/run/media/", sizeof ("/run/media/") - 1) == 0 &&
2124           strncmp (mount_path + sizeof ("/run/media/") - 1, user_name, user_name_len) == 0 &&
2125           mount_path[sizeof ("/run/media/") - 1 + user_name_len] == '/')
2126         is_in_runtime_dir = TRUE;
2127
2128       if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2129         {
2130           char *path;
2131           /* Avoid displaying mounts that are not accessible to the user.
2132            *
2133            * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2134            * want to avoid g_access() for mount points which can potentially
2135            * block or fail stat()'ing, such as network mounts.
2136            */
2137           path = g_path_get_dirname (mount_path);
2138           if (g_str_has_prefix (path, "/media/"))
2139             {
2140               if (g_access (path, R_OK|X_OK) != 0) 
2141                 {
2142                   g_free (path);
2143                   return FALSE;
2144                 }
2145             }
2146           g_free (path);
2147
2148           if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2149            {
2150              struct stat st;
2151              if (g_stat (mount_entry->device_path, &st) == 0 &&
2152                  S_ISBLK(st.st_mode) &&
2153                  g_access (mount_path, R_OK|X_OK) != 0)
2154                return FALSE;
2155            }
2156           return TRUE;
2157         }
2158       
2159       if (g_str_has_prefix (mount_path, g_get_home_dir ()) && 
2160           mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2161         return TRUE;
2162     }
2163   
2164   return FALSE;
2165 }
2166
2167 /**
2168  * g_unix_mount_point_guess_can_eject:
2169  * @mount_point: a #GUnixMountPoint
2170  * 
2171  * Guesses whether a Unix mount point can be ejected.
2172  *
2173  * Returns: %TRUE if @mount_point is deemed to be ejectable.
2174  */
2175 gboolean
2176 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2177 {
2178   GUnixMountType guessed_type;
2179
2180   guessed_type = g_unix_mount_point_guess_type (mount_point);
2181   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2182       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2183     return TRUE;
2184
2185   return FALSE;
2186 }
2187
2188 #ifdef HAVE_MNTENT_H
2189 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2190 static void
2191 _canonicalize_filename (gchar *filename)
2192 {
2193   gchar *p, *q;
2194   gboolean last_was_slash = FALSE;
2195   
2196   p = filename;
2197   q = filename;
2198   
2199   while (*p)
2200     {
2201       if (*p == G_DIR_SEPARATOR)
2202         {
2203           if (!last_was_slash)
2204             *q++ = G_DIR_SEPARATOR;
2205           
2206           last_was_slash = TRUE;
2207         }
2208       else
2209         {
2210           if (last_was_slash && *p == '.')
2211             {
2212               if (*(p + 1) == G_DIR_SEPARATOR ||
2213                   *(p + 1) == '\0')
2214                 {
2215                   if (*(p + 1) == '\0')
2216                     break;
2217                   
2218                   p += 1;
2219                 }
2220               else if (*(p + 1) == '.' &&
2221                        (*(p + 2) == G_DIR_SEPARATOR ||
2222                         *(p + 2) == '\0'))
2223                 {
2224                   if (q > filename + 1)
2225                     {
2226                       q--;
2227                       while (q > filename + 1 &&
2228                              *(q - 1) != G_DIR_SEPARATOR)
2229                         q--;
2230                     }
2231                   
2232                   if (*(p + 2) == '\0')
2233                     break;
2234                   
2235                   p += 2;
2236                 }
2237               else
2238                 {
2239                   *q++ = *p;
2240                   last_was_slash = FALSE;
2241                 }
2242             }
2243           else
2244             {
2245               *q++ = *p;
2246               last_was_slash = FALSE;
2247             }
2248         }
2249       
2250       p++;
2251     }
2252   
2253   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2254     q--;
2255   
2256   *q = '\0';
2257 }
2258
2259 static char *
2260 _resolve_symlink (const char *file)
2261 {
2262   GError *error;
2263   char *dir;
2264   char *link;
2265   char *f;
2266   char *f1;
2267   
2268   f = g_strdup (file);
2269   
2270   while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) 
2271     {
2272       link = g_file_read_link (f, &error);
2273       if (link == NULL) 
2274         {
2275           g_error_free (error);
2276           g_free (f);
2277           f = NULL;
2278           goto out;
2279         }
2280     
2281       dir = g_path_get_dirname (f);
2282       f1 = g_strdup_printf ("%s/%s", dir, link);
2283       g_free (dir);
2284       g_free (link);
2285       g_free (f);
2286       f = f1;
2287     }
2288   
2289  out:
2290   if (f != NULL)
2291     _canonicalize_filename (f);
2292   return f;
2293 }
2294
2295 static const char *
2296 _resolve_dev_root (void)
2297 {
2298   static gboolean have_real_dev_root = FALSE;
2299   static char real_dev_root[256];
2300   struct stat statbuf;
2301   
2302   /* see if it's cached already */
2303   if (have_real_dev_root)
2304     goto found;
2305   
2306   /* otherwise we're going to find it right away.. */
2307   have_real_dev_root = TRUE;
2308   
2309   if (stat ("/dev/root", &statbuf) == 0) 
2310     {
2311       if (! S_ISLNK (statbuf.st_mode)) 
2312         {
2313           dev_t root_dev = statbuf.st_dev;
2314           FILE *f;
2315       
2316           /* see if device with similar major:minor as /dev/root is mention
2317            * in /etc/mtab (it usually is) 
2318            */
2319           f = fopen ("/etc/mtab", "r");
2320           if (f != NULL) 
2321             {
2322               struct mntent *entp;
2323 #ifdef HAVE_GETMNTENT_R        
2324               struct mntent ent;
2325               char buf[1024];
2326               while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL) 
2327                 {
2328 #else
2329               G_LOCK (getmntent);
2330               while ((entp = getmntent (f)) != NULL) 
2331                 { 
2332 #endif          
2333                   if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2334                       statbuf.st_dev == root_dev) 
2335                     {
2336                       strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2337                       real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2338                       fclose (f);
2339                       goto found;
2340                     }
2341                 }
2342
2343               endmntent (f);
2344
2345 #ifndef HAVE_GETMNTENT_R
2346               G_UNLOCK (getmntent);
2347 #endif
2348             }                                        
2349       
2350           /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2351       
2352         } 
2353        else 
2354         {
2355           char *resolved;
2356           resolved = _resolve_symlink ("/dev/root");
2357           if (resolved != NULL)
2358             {
2359               strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2360               real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2361               g_free (resolved);
2362               goto found;
2363             }
2364         }
2365     }
2366   
2367   /* bah sucks.. */
2368   strcpy (real_dev_root, "/dev/root");
2369   
2370 found:
2371   return real_dev_root;
2372 }
2373 #endif