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