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