cleanup
[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   g_list_free_full (monitor->mount_poller_mounts, (GDestroyNotify)g_unix_mount_free);
1295
1296   the_mount_monitor = NULL;
1297
1298   G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1299 }
1300
1301
1302 static void
1303 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1304 {
1305   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1306
1307   gobject_class->finalize = g_unix_mount_monitor_finalize;
1308  
1309   /**
1310    * GUnixMountMonitor::mounts-changed:
1311    * @monitor: the object on which the signal is emitted
1312    * 
1313    * Emitted when the unix mounts have changed.
1314    */ 
1315   signals[MOUNTS_CHANGED] =
1316     g_signal_new ("mounts-changed",
1317                   G_TYPE_FROM_CLASS (klass),
1318                   G_SIGNAL_RUN_LAST,
1319                   0,
1320                   NULL, NULL,
1321                   g_cclosure_marshal_VOID__VOID,
1322                   G_TYPE_NONE, 0);
1323
1324   /**
1325    * GUnixMountMonitor::mountpoints-changed:
1326    * @monitor: the object on which the signal is emitted
1327    * 
1328    * Emitted when the unix mount points have changed.
1329    */
1330   signals[MOUNTPOINTS_CHANGED] =
1331     g_signal_new ("mountpoints-changed",
1332                   G_TYPE_FROM_CLASS (klass),
1333                   G_SIGNAL_RUN_LAST,
1334                   0,
1335                   NULL, NULL,
1336                   g_cclosure_marshal_VOID__VOID,
1337                   G_TYPE_NONE, 0);
1338 }
1339
1340 static void
1341 fstab_file_changed (GFileMonitor      *monitor,
1342                     GFile             *file,
1343                     GFile             *other_file,
1344                     GFileMonitorEvent  event_type,
1345                     gpointer           user_data)
1346 {
1347   GUnixMountMonitor *mount_monitor;
1348
1349   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1350       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1351       event_type != G_FILE_MONITOR_EVENT_DELETED)
1352     return;
1353
1354   mount_monitor = user_data;
1355   g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
1356 }
1357
1358 static void
1359 mtab_file_changed (GFileMonitor      *monitor,
1360                    GFile             *file,
1361                    GFile             *other_file,
1362                    GFileMonitorEvent  event_type,
1363                    gpointer           user_data)
1364 {
1365   GUnixMountMonitor *mount_monitor;
1366
1367   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1368       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1369       event_type != G_FILE_MONITOR_EVENT_DELETED)
1370     return;
1371   
1372   mount_monitor = user_data;
1373   g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1374 }
1375
1376 static gboolean
1377 proc_mounts_changed (GIOChannel   *channel,
1378                      GIOCondition  cond,
1379                      gpointer      user_data)
1380 {
1381   GUnixMountMonitor *mount_monitor = G_UNIX_MOUNT_MONITOR (user_data);
1382   if (cond & G_IO_ERR)
1383     g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1384   return TRUE;
1385 }
1386
1387 static gboolean
1388 mount_change_poller (gpointer user_data)
1389 {
1390   GUnixMountMonitor *mount_monitor;
1391   GList *current_mounts, *new_it, *old_it;
1392   gboolean has_changed = FALSE;
1393
1394   mount_monitor = user_data;
1395   current_mounts = _g_get_unix_mounts ();
1396
1397   for ( new_it = current_mounts, old_it = mount_monitor->mount_poller_mounts;
1398         new_it != NULL && old_it != NULL;
1399         new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1400     {
1401       if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1402         {
1403           has_changed = TRUE;
1404           break;
1405         }
1406     }
1407   if (!(new_it == NULL && old_it == NULL))
1408     has_changed = TRUE;
1409
1410   g_list_free_full (mount_monitor->mount_poller_mounts,
1411                     (GDestroyNotify)g_unix_mount_free);
1412
1413   mount_monitor->mount_poller_mounts = current_mounts;
1414
1415   if (has_changed)
1416     {
1417       mount_poller_time = (guint64)g_get_monotonic_time ();
1418       g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1419     }
1420
1421   return TRUE;
1422 }
1423
1424 static void
1425 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1426 {
1427   GFile *file;
1428     
1429   if (get_fstab_file () != NULL)
1430     {
1431       file = g_file_new_for_path (get_fstab_file ());
1432       monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1433       g_object_unref (file);
1434       
1435       g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
1436     }
1437   
1438   if (get_mtab_monitor_file () != NULL)
1439     {
1440       const gchar *mtab_path;
1441
1442       mtab_path = get_mtab_monitor_file ();
1443       /* /proc/mounts monitoring is special - can't just use GFileMonitor.
1444        * See 'man proc' for more details.
1445        */
1446       if (g_strcmp0 (mtab_path, "/proc/mounts") == 0)
1447         {
1448           GIOChannel *proc_mounts_channel;
1449           GError *error = NULL;
1450           proc_mounts_channel = g_io_channel_new_file ("/proc/mounts", "r", &error);
1451           if (proc_mounts_channel == NULL)
1452             {
1453               g_warning ("Error creating IO channel for /proc/mounts: %s (%s, %d)",
1454                          error->message, g_quark_to_string (error->domain), error->code);
1455               g_error_free (error);
1456             }
1457           else
1458             {
1459               monitor->proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1460               g_source_set_callback (monitor->proc_mounts_watch_source,
1461                                      (GSourceFunc) proc_mounts_changed,
1462                                      monitor,
1463                                      NULL);
1464               g_source_attach (monitor->proc_mounts_watch_source,
1465                                g_main_context_get_thread_default ());
1466               g_source_unref (monitor->proc_mounts_watch_source);
1467               g_io_channel_unref (proc_mounts_channel);
1468             }
1469         }
1470       else
1471         {
1472           file = g_file_new_for_path (mtab_path);
1473           monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1474           g_object_unref (file);
1475           g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
1476         }
1477     }
1478   else
1479     {
1480       monitor->proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1481       monitor->mount_poller_mounts = _g_get_unix_mounts ();
1482       mount_poller_time = (guint64)g_get_monotonic_time ();
1483       g_source_set_callback (monitor->proc_mounts_watch_source,
1484                              (GSourceFunc)mount_change_poller,
1485                              monitor, NULL);
1486       g_source_attach (monitor->proc_mounts_watch_source,
1487                        g_main_context_get_thread_default ());
1488       g_source_unref (monitor->proc_mounts_watch_source);
1489     }
1490 }
1491
1492 /**
1493  * g_unix_mount_monitor_set_rate_limit:
1494  * @mount_monitor: a #GUnixMountMonitor
1495  * @limit_msec: a integer with the limit in milliseconds to
1496  *     poll for changes.
1497  *
1498  * Sets the rate limit to which the @mount_monitor will report
1499  * consecutive change events to the mount and mount point entry files.
1500  *
1501  * Since: 2.18
1502  */
1503 void
1504 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1505                                      gint               limit_msec)
1506 {
1507   g_return_if_fail (G_IS_UNIX_MOUNT_MONITOR (mount_monitor));
1508
1509   if (mount_monitor->fstab_monitor != NULL)
1510     g_file_monitor_set_rate_limit (mount_monitor->fstab_monitor, limit_msec);
1511
1512   if (mount_monitor->mtab_monitor != NULL)
1513     g_file_monitor_set_rate_limit (mount_monitor->mtab_monitor, limit_msec);
1514 }
1515
1516 /**
1517  * g_unix_mount_monitor_new:
1518  * 
1519  * Gets a new #GUnixMountMonitor. The default rate limit for which the
1520  * monitor will report consecutive changes for the mount and mount
1521  * point entry files is the default for a #GFileMonitor. Use
1522  * g_unix_mount_monitor_set_rate_limit() to change this.
1523  * 
1524  * Returns: a #GUnixMountMonitor. 
1525  */
1526 GUnixMountMonitor *
1527 g_unix_mount_monitor_new (void)
1528 {
1529   if (the_mount_monitor == NULL)
1530     {
1531       the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
1532       return the_mount_monitor;
1533     }
1534   
1535   return g_object_ref (the_mount_monitor);
1536 }
1537
1538 /**
1539  * g_unix_mount_free:
1540  * @mount_entry: a #GUnixMountEntry.
1541  * 
1542  * Frees a unix mount.
1543  */
1544 void
1545 g_unix_mount_free (GUnixMountEntry *mount_entry)
1546 {
1547   g_return_if_fail (mount_entry != NULL);
1548
1549   g_free (mount_entry->mount_path);
1550   g_free (mount_entry->device_path);
1551   g_free (mount_entry->filesystem_type);
1552   g_free (mount_entry);
1553 }
1554
1555 /**
1556  * g_unix_mount_point_free:
1557  * @mount_point: unix mount point to free.
1558  * 
1559  * Frees a unix mount point.
1560  */
1561 void
1562 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1563 {
1564   g_return_if_fail (mount_point != NULL);
1565
1566   g_free (mount_point->mount_path);
1567   g_free (mount_point->device_path);
1568   g_free (mount_point->filesystem_type);
1569   g_free (mount_point->options);
1570   g_free (mount_point);
1571 }
1572
1573 /**
1574  * g_unix_mount_compare:
1575  * @mount1: first #GUnixMountEntry to compare.
1576  * @mount2: second #GUnixMountEntry to compare.
1577  * 
1578  * Compares two unix mounts.
1579  * 
1580  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1581  * or less than @mount2, respectively. 
1582  */
1583 gint
1584 g_unix_mount_compare (GUnixMountEntry *mount1,
1585                       GUnixMountEntry *mount2)
1586 {
1587   int res;
1588
1589   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1590   
1591   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1592   if (res != 0)
1593     return res;
1594         
1595   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1596   if (res != 0)
1597     return res;
1598         
1599   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1600   if (res != 0)
1601     return res;
1602
1603   res =  mount1->is_read_only - mount2->is_read_only;
1604   if (res != 0)
1605     return res;
1606   
1607   return 0;
1608 }
1609
1610 /**
1611  * g_unix_mount_get_mount_path:
1612  * @mount_entry: input #GUnixMountEntry to get the mount path for.
1613  * 
1614  * Gets the mount path for a unix mount.
1615  * 
1616  * Returns: the mount path for @mount_entry.
1617  */
1618 const gchar *
1619 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1620 {
1621   g_return_val_if_fail (mount_entry != NULL, NULL);
1622
1623   return mount_entry->mount_path;
1624 }
1625
1626 /**
1627  * g_unix_mount_get_device_path:
1628  * @mount_entry: a #GUnixMount.
1629  * 
1630  * Gets the device path for a unix mount.
1631  * 
1632  * Returns: a string containing the device path.
1633  */
1634 const gchar *
1635 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1636 {
1637   g_return_val_if_fail (mount_entry != NULL, NULL);
1638
1639   return mount_entry->device_path;
1640 }
1641
1642 /**
1643  * g_unix_mount_get_fs_type:
1644  * @mount_entry: a #GUnixMount.
1645  * 
1646  * Gets the filesystem type for the unix mount.
1647  * 
1648  * Returns: a string containing the file system type.
1649  */
1650 const gchar *
1651 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1652 {
1653   g_return_val_if_fail (mount_entry != NULL, NULL);
1654
1655   return mount_entry->filesystem_type;
1656 }
1657
1658 /**
1659  * g_unix_mount_is_readonly:
1660  * @mount_entry: a #GUnixMount.
1661  * 
1662  * Checks if a unix mount is mounted read only.
1663  * 
1664  * Returns: %TRUE if @mount_entry is read only.
1665  */
1666 gboolean
1667 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1668 {
1669   g_return_val_if_fail (mount_entry != NULL, FALSE);
1670
1671   return mount_entry->is_read_only;
1672 }
1673
1674 /**
1675  * g_unix_mount_is_system_internal:
1676  * @mount_entry: a #GUnixMount.
1677  * 
1678  * Checks if a unix mount is a system path.
1679  * 
1680  * Returns: %TRUE if the unix mount is for a system path.
1681  */
1682 gboolean
1683 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1684 {
1685   g_return_val_if_fail (mount_entry != NULL, FALSE);
1686
1687   return mount_entry->is_system_internal;
1688 }
1689
1690 /**
1691  * g_unix_mount_point_compare:
1692  * @mount1: a #GUnixMount.
1693  * @mount2: a #GUnixMount.
1694  * 
1695  * Compares two unix mount points.
1696  * 
1697  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1698  * or less than @mount2, respectively.
1699  */
1700 gint
1701 g_unix_mount_point_compare (GUnixMountPoint *mount1,
1702                             GUnixMountPoint *mount2)
1703 {
1704   int res;
1705
1706   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1707
1708   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1709   if (res != 0) 
1710     return res;
1711         
1712   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1713   if (res != 0) 
1714     return res;
1715         
1716   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1717   if (res != 0) 
1718     return res;
1719
1720   res = g_strcmp0 (mount1->options, mount2->options);
1721   if (res != 0) 
1722     return res;
1723
1724   res =  mount1->is_read_only - mount2->is_read_only;
1725   if (res != 0) 
1726     return res;
1727
1728   res = mount1->is_user_mountable - mount2->is_user_mountable;
1729   if (res != 0) 
1730     return res;
1731
1732   res = mount1->is_loopback - mount2->is_loopback;
1733   if (res != 0)
1734     return res;
1735   
1736   return 0;
1737 }
1738
1739 /**
1740  * g_unix_mount_point_get_mount_path:
1741  * @mount_point: a #GUnixMountPoint.
1742  * 
1743  * Gets the mount path for a unix mount point.
1744  * 
1745  * Returns: a string containing the mount path.
1746  */
1747 const gchar *
1748 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
1749 {
1750   g_return_val_if_fail (mount_point != NULL, NULL);
1751
1752   return mount_point->mount_path;
1753 }
1754
1755 /**
1756  * g_unix_mount_point_get_device_path:
1757  * @mount_point: a #GUnixMountPoint.
1758  * 
1759  * Gets the device path for a unix mount point.
1760  * 
1761  * Returns: a string containing the device path.
1762  */
1763 const gchar *
1764 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
1765 {
1766   g_return_val_if_fail (mount_point != NULL, NULL);
1767
1768   return mount_point->device_path;
1769 }
1770
1771 /**
1772  * g_unix_mount_point_get_fs_type:
1773  * @mount_point: a #GUnixMountPoint.
1774  * 
1775  * Gets the file system type for the mount point.
1776  * 
1777  * Returns: a string containing the file system type.
1778  */
1779 const gchar *
1780 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
1781 {
1782   g_return_val_if_fail (mount_point != NULL, NULL);
1783
1784   return mount_point->filesystem_type;
1785 }
1786
1787 /**
1788  * g_unix_mount_point_get_options:
1789  * @mount_point: a #GUnixMountPoint.
1790  * 
1791  * Gets the options for the mount point.
1792  * 
1793  * Returns: a string containing the options.
1794  *
1795  * Since: 2.32
1796  */
1797 const gchar *
1798 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
1799 {
1800   g_return_val_if_fail (mount_point != NULL, NULL);
1801
1802   return mount_point->options;
1803 }
1804
1805 /**
1806  * g_unix_mount_point_is_readonly:
1807  * @mount_point: a #GUnixMountPoint.
1808  * 
1809  * Checks if a unix mount point is read only.
1810  * 
1811  * Returns: %TRUE if a mount point is read only.
1812  */
1813 gboolean
1814 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
1815 {
1816   g_return_val_if_fail (mount_point != NULL, FALSE);
1817
1818   return mount_point->is_read_only;
1819 }
1820
1821 /**
1822  * g_unix_mount_point_is_user_mountable:
1823  * @mount_point: a #GUnixMountPoint.
1824  * 
1825  * Checks if a unix mount point is mountable by the user.
1826  * 
1827  * Returns: %TRUE if the mount point is user mountable.
1828  */
1829 gboolean
1830 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
1831 {
1832   g_return_val_if_fail (mount_point != NULL, FALSE);
1833
1834   return mount_point->is_user_mountable;
1835 }
1836
1837 /**
1838  * g_unix_mount_point_is_loopback:
1839  * @mount_point: a #GUnixMountPoint.
1840  * 
1841  * Checks if a unix mount point is a loopback device.
1842  * 
1843  * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise. 
1844  */
1845 gboolean
1846 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
1847 {
1848   g_return_val_if_fail (mount_point != NULL, FALSE);
1849
1850   return mount_point->is_loopback;
1851 }
1852
1853 static GUnixMountType
1854 guess_mount_type (const char *mount_path,
1855                   const char *device_path,
1856                   const char *filesystem_type)
1857 {
1858   GUnixMountType type;
1859   char *basename;
1860
1861   type = G_UNIX_MOUNT_TYPE_UNKNOWN;
1862   
1863   if ((strcmp (filesystem_type, "udf") == 0) ||
1864       (strcmp (filesystem_type, "iso9660") == 0) ||
1865       (strcmp (filesystem_type, "cd9660") == 0))
1866     type = G_UNIX_MOUNT_TYPE_CDROM;
1867   else if ((strcmp (filesystem_type, "nfs") == 0) ||
1868            (strcmp (filesystem_type, "nfs4") == 0))
1869     type = G_UNIX_MOUNT_TYPE_NFS;
1870   else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
1871            g_str_has_prefix (device_path, "/dev/fd") ||
1872            g_str_has_prefix (device_path, "/dev/floppy"))
1873     type = G_UNIX_MOUNT_TYPE_FLOPPY;
1874   else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
1875            g_str_has_prefix (device_path, "/dev/acd") ||
1876            g_str_has_prefix (device_path, "/dev/cd"))
1877     type = G_UNIX_MOUNT_TYPE_CDROM;
1878   else if (g_str_has_prefix (device_path, "/vol/"))
1879     {
1880       const char *name = mount_path + strlen ("/");
1881       
1882       if (g_str_has_prefix (name, "cdrom"))
1883         type = G_UNIX_MOUNT_TYPE_CDROM;
1884       else if (g_str_has_prefix (name, "floppy") ||
1885                g_str_has_prefix (device_path, "/vol/dev/diskette/")) 
1886         type = G_UNIX_MOUNT_TYPE_FLOPPY;
1887       else if (g_str_has_prefix (name, "rmdisk")) 
1888         type = G_UNIX_MOUNT_TYPE_ZIP;
1889       else if (g_str_has_prefix (name, "jaz"))
1890         type = G_UNIX_MOUNT_TYPE_JAZ;
1891       else if (g_str_has_prefix (name, "memstick"))
1892         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1893     }
1894   else
1895     {
1896       basename = g_path_get_basename (mount_path);
1897       
1898       if (g_str_has_prefix (basename, "cdr") ||
1899           g_str_has_prefix (basename, "cdwriter") ||
1900           g_str_has_prefix (basename, "burn") ||
1901           g_str_has_prefix (basename, "dvdr"))
1902         type = G_UNIX_MOUNT_TYPE_CDROM;
1903       else if (g_str_has_prefix (basename, "floppy"))
1904         type = G_UNIX_MOUNT_TYPE_FLOPPY;
1905       else if (g_str_has_prefix (basename, "zip"))
1906         type = G_UNIX_MOUNT_TYPE_ZIP;
1907       else if (g_str_has_prefix (basename, "jaz"))
1908         type = G_UNIX_MOUNT_TYPE_JAZ;
1909       else if (g_str_has_prefix (basename, "camera"))
1910         type = G_UNIX_MOUNT_TYPE_CAMERA;
1911       else if (g_str_has_prefix (basename, "memstick") ||
1912                g_str_has_prefix (basename, "memory_stick") ||
1913                g_str_has_prefix (basename, "ram"))
1914         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1915       else if (g_str_has_prefix (basename, "compact_flash"))
1916         type = G_UNIX_MOUNT_TYPE_CF;
1917       else if (g_str_has_prefix (basename, "smart_media"))
1918         type = G_UNIX_MOUNT_TYPE_SM;
1919       else if (g_str_has_prefix (basename, "sd_mmc"))
1920         type = G_UNIX_MOUNT_TYPE_SDMMC;
1921       else if (g_str_has_prefix (basename, "ipod"))
1922         type = G_UNIX_MOUNT_TYPE_IPOD;
1923       
1924       g_free (basename);
1925     }
1926   
1927   if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
1928     type = G_UNIX_MOUNT_TYPE_HD;
1929   
1930   return type;
1931 }
1932
1933 /**
1934  * g_unix_mount_guess_type:
1935  * @mount_entry: a #GUnixMount.
1936  * 
1937  * Guesses the type of a unix mount. If the mount type cannot be 
1938  * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1939  * 
1940  * Returns: a #GUnixMountType. 
1941  */
1942 static GUnixMountType
1943 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
1944 {
1945   g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1946   g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1947   g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1948   g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1949
1950   return guess_mount_type (mount_entry->mount_path,
1951                            mount_entry->device_path,
1952                            mount_entry->filesystem_type);
1953 }
1954
1955 /**
1956  * g_unix_mount_point_guess_type:
1957  * @mount_point: a #GUnixMountPoint.
1958  * 
1959  * Guesses the type of a unix mount point. 
1960  * If the mount type cannot be determined, 
1961  * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1962  * 
1963  * Returns: a #GUnixMountType.
1964  */
1965 static GUnixMountType
1966 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
1967 {
1968   g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1969   g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1970   g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1971   g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1972
1973   return guess_mount_type (mount_point->mount_path,
1974                            mount_point->device_path,
1975                            mount_point->filesystem_type);
1976 }
1977
1978 static const char *
1979 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
1980 {
1981   const char *icon_name;
1982   
1983   switch (type)
1984     {
1985     case G_UNIX_MOUNT_TYPE_HD:
1986       if (is_mount_point)
1987         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1988       else
1989         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
1990       break;
1991     case G_UNIX_MOUNT_TYPE_FLOPPY:
1992     case G_UNIX_MOUNT_TYPE_ZIP:
1993     case G_UNIX_MOUNT_TYPE_JAZ:
1994       if (is_mount_point)
1995         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1996       else
1997         icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
1998       break;
1999     case G_UNIX_MOUNT_TYPE_CDROM:
2000       if (is_mount_point)
2001         icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2002       else
2003         icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2004       break;
2005     case G_UNIX_MOUNT_TYPE_NFS:
2006         icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2007       break;
2008     case G_UNIX_MOUNT_TYPE_MEMSTICK:
2009       if (is_mount_point)
2010         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2011       else
2012         icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2013       break;
2014     case G_UNIX_MOUNT_TYPE_CAMERA:
2015       if (is_mount_point)
2016         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2017       else
2018         icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2019       break;
2020     case G_UNIX_MOUNT_TYPE_IPOD:
2021       if (is_mount_point)
2022         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2023       else
2024         icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2025       break;
2026     case G_UNIX_MOUNT_TYPE_UNKNOWN:
2027     default:
2028       if (is_mount_point)
2029         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2030       else
2031         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2032       break;
2033     }
2034
2035   return icon_name;
2036 }
2037
2038 /**
2039  * g_unix_mount_guess_name:
2040  * @mount_entry: a #GUnixMountEntry
2041  * 
2042  * Guesses the name of a Unix mount. 
2043  * The result is a translated string.
2044  *
2045  * Returns: A newly allocated string that must
2046  *     be freed with g_free()
2047  */
2048 gchar *
2049 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2050 {
2051   char *name;
2052
2053   if (strcmp (mount_entry->mount_path, "/") == 0)
2054     name = g_strdup (_("Filesystem root"));
2055   else
2056     name = g_filename_display_basename (mount_entry->mount_path);
2057
2058   return name;
2059 }
2060
2061 /**
2062  * g_unix_mount_guess_icon:
2063  * @mount_entry: a #GUnixMountEntry
2064  * 
2065  * Guesses the icon of a Unix mount. 
2066  *
2067  * Returns: (transfer full): a #GIcon
2068  */
2069 GIcon *
2070 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2071 {
2072   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2073 }
2074
2075 /**
2076  * g_unix_mount_guess_symbolic_icon:
2077  * @mount_entry: a #GUnixMountEntry
2078  *
2079  * Guesses the symbolic icon of a Unix mount.
2080  *
2081  * Returns: (transfer full): a #GIcon
2082  *
2083  * Since: 2.34
2084  */
2085 GIcon *
2086 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2087 {
2088   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2089 }
2090
2091 /**
2092  * g_unix_mount_point_guess_name:
2093  * @mount_point: a #GUnixMountPoint
2094  * 
2095  * Guesses the name of a Unix mount point. 
2096  * The result is a translated string.
2097  *
2098  * Returns: A newly allocated string that must 
2099  *     be freed with g_free()
2100  */
2101 gchar *
2102 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2103 {
2104   char *name;
2105
2106   if (strcmp (mount_point->mount_path, "/") == 0)
2107     name = g_strdup (_("Filesystem root"));
2108   else
2109     name = g_filename_display_basename (mount_point->mount_path);
2110
2111   return name;
2112 }
2113
2114 /**
2115  * g_unix_mount_point_guess_icon:
2116  * @mount_point: a #GUnixMountPoint
2117  * 
2118  * Guesses the icon of a Unix mount point. 
2119  *
2120  * Returns: (transfer full): a #GIcon
2121  */
2122 GIcon *
2123 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2124 {
2125   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2126 }
2127
2128 /**
2129  * g_unix_mount_point_guess_symbolic_icon:
2130  * @mount_point: a #GUnixMountPoint
2131  *
2132  * Guesses the symbolic icon of a Unix mount point.
2133  *
2134  * Returns: (transfer full): a #GIcon
2135  *
2136  * Since: 2.34
2137  */
2138 GIcon *
2139 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2140 {
2141   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2142 }
2143
2144 /**
2145  * g_unix_mount_guess_can_eject:
2146  * @mount_entry: a #GUnixMountEntry
2147  * 
2148  * Guesses whether a Unix mount can be ejected.
2149  *
2150  * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2151  */
2152 gboolean
2153 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2154 {
2155   GUnixMountType guessed_type;
2156
2157   guessed_type = g_unix_mount_guess_type (mount_entry);
2158   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2159       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2160     return TRUE;
2161
2162   return FALSE;
2163 }
2164
2165 /**
2166  * g_unix_mount_guess_should_display:
2167  * @mount_entry: a #GUnixMountEntry
2168  * 
2169  * Guesses whether a Unix mount should be displayed in the UI.
2170  *
2171  * Returns: %TRUE if @mount_entry is deemed to be displayable.
2172  */
2173 gboolean
2174 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2175 {
2176   const char *mount_path;
2177   const gchar *user_name;
2178   gsize user_name_len;
2179
2180   /* Never display internal mountpoints */
2181   if (g_unix_mount_is_system_internal (mount_entry))
2182     return FALSE;
2183   
2184   /* Only display things in /media (which are generally user mountable)
2185      and home dir (fuse stuff) and /run/media/$USER */
2186   mount_path = mount_entry->mount_path;
2187   if (mount_path != NULL)
2188     {
2189       gboolean is_in_runtime_dir = FALSE;
2190       /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2191       if (g_strstr_len (mount_path, -1, "/.") != NULL)
2192         return FALSE;
2193
2194       /* Check /run/media/$USER/ */
2195       user_name = g_get_user_name ();
2196       user_name_len = strlen (user_name);
2197       if (strncmp (mount_path, "/run/media/", sizeof ("/run/media/") - 1) == 0 &&
2198           strncmp (mount_path + sizeof ("/run/media/") - 1, user_name, user_name_len) == 0 &&
2199           mount_path[sizeof ("/run/media/") - 1 + user_name_len] == '/')
2200         is_in_runtime_dir = TRUE;
2201
2202       if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2203         {
2204           char *path;
2205           /* Avoid displaying mounts that are not accessible to the user.
2206            *
2207            * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2208            * want to avoid g_access() for mount points which can potentially
2209            * block or fail stat()'ing, such as network mounts.
2210            */
2211           path = g_path_get_dirname (mount_path);
2212           if (g_str_has_prefix (path, "/media/"))
2213             {
2214               if (g_access (path, R_OK|X_OK) != 0) 
2215                 {
2216                   g_free (path);
2217                   return FALSE;
2218                 }
2219             }
2220           g_free (path);
2221
2222           if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2223            {
2224              struct stat st;
2225              if (g_stat (mount_entry->device_path, &st) == 0 &&
2226                  S_ISBLK(st.st_mode) &&
2227                  g_access (mount_path, R_OK|X_OK) != 0)
2228                return FALSE;
2229            }
2230           return TRUE;
2231         }
2232       
2233       if (g_str_has_prefix (mount_path, g_get_home_dir ()) && 
2234           mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2235         return TRUE;
2236     }
2237   
2238   return FALSE;
2239 }
2240
2241 /**
2242  * g_unix_mount_point_guess_can_eject:
2243  * @mount_point: a #GUnixMountPoint
2244  * 
2245  * Guesses whether a Unix mount point can be ejected.
2246  *
2247  * Returns: %TRUE if @mount_point is deemed to be ejectable.
2248  */
2249 gboolean
2250 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2251 {
2252   GUnixMountType guessed_type;
2253
2254   guessed_type = g_unix_mount_point_guess_type (mount_point);
2255   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2256       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2257     return TRUE;
2258
2259   return FALSE;
2260 }
2261
2262 #ifdef HAVE_MNTENT_H
2263 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2264 static void
2265 _canonicalize_filename (gchar *filename)
2266 {
2267   gchar *p, *q;
2268   gboolean last_was_slash = FALSE;
2269   
2270   p = filename;
2271   q = filename;
2272   
2273   while (*p)
2274     {
2275       if (*p == G_DIR_SEPARATOR)
2276         {
2277           if (!last_was_slash)
2278             *q++ = G_DIR_SEPARATOR;
2279           
2280           last_was_slash = TRUE;
2281         }
2282       else
2283         {
2284           if (last_was_slash && *p == '.')
2285             {
2286               if (*(p + 1) == G_DIR_SEPARATOR ||
2287                   *(p + 1) == '\0')
2288                 {
2289                   if (*(p + 1) == '\0')
2290                     break;
2291                   
2292                   p += 1;
2293                 }
2294               else if (*(p + 1) == '.' &&
2295                        (*(p + 2) == G_DIR_SEPARATOR ||
2296                         *(p + 2) == '\0'))
2297                 {
2298                   if (q > filename + 1)
2299                     {
2300                       q--;
2301                       while (q > filename + 1 &&
2302                              *(q - 1) != G_DIR_SEPARATOR)
2303                         q--;
2304                     }
2305                   
2306                   if (*(p + 2) == '\0')
2307                     break;
2308                   
2309                   p += 2;
2310                 }
2311               else
2312                 {
2313                   *q++ = *p;
2314                   last_was_slash = FALSE;
2315                 }
2316             }
2317           else
2318             {
2319               *q++ = *p;
2320               last_was_slash = FALSE;
2321             }
2322         }
2323       
2324       p++;
2325     }
2326   
2327   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2328     q--;
2329   
2330   *q = '\0';
2331 }
2332
2333 static char *
2334 _resolve_symlink (const char *file)
2335 {
2336   GError *error;
2337   char *dir;
2338   char *link;
2339   char *f;
2340   char *f1;
2341   
2342   f = g_strdup (file);
2343   
2344   while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) 
2345     {
2346       link = g_file_read_link (f, &error);
2347       if (link == NULL) 
2348         {
2349           g_error_free (error);
2350           g_free (f);
2351           f = NULL;
2352           goto out;
2353         }
2354     
2355       dir = g_path_get_dirname (f);
2356       f1 = g_strdup_printf ("%s/%s", dir, link);
2357       g_free (dir);
2358       g_free (link);
2359       g_free (f);
2360       f = f1;
2361     }
2362   
2363  out:
2364   if (f != NULL)
2365     _canonicalize_filename (f);
2366   return f;
2367 }
2368
2369 static const char *
2370 _resolve_dev_root (void)
2371 {
2372   static gboolean have_real_dev_root = FALSE;
2373   static char real_dev_root[256];
2374   struct stat statbuf;
2375   
2376   /* see if it's cached already */
2377   if (have_real_dev_root)
2378     goto found;
2379   
2380   /* otherwise we're going to find it right away.. */
2381   have_real_dev_root = TRUE;
2382   
2383   if (stat ("/dev/root", &statbuf) == 0) 
2384     {
2385       if (! S_ISLNK (statbuf.st_mode)) 
2386         {
2387           dev_t root_dev = statbuf.st_dev;
2388           FILE *f;
2389       
2390           /* see if device with similar major:minor as /dev/root is mention
2391            * in /etc/mtab (it usually is) 
2392            */
2393           f = fopen ("/etc/mtab", "r");
2394           if (f != NULL) 
2395             {
2396               struct mntent *entp;
2397 #ifdef HAVE_GETMNTENT_R        
2398               struct mntent ent;
2399               char buf[1024];
2400               while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL) 
2401                 {
2402 #else
2403               G_LOCK (getmntent);
2404               while ((entp = getmntent (f)) != NULL) 
2405                 { 
2406 #endif          
2407                   if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2408                       statbuf.st_dev == root_dev) 
2409                     {
2410                       strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2411                       real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2412                       fclose (f);
2413                       goto found;
2414                     }
2415                 }
2416
2417               endmntent (f);
2418
2419 #ifndef HAVE_GETMNTENT_R
2420               G_UNLOCK (getmntent);
2421 #endif
2422             }                                        
2423       
2424           /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2425       
2426         } 
2427        else 
2428         {
2429           char *resolved;
2430           resolved = _resolve_symlink ("/dev/root");
2431           if (resolved != NULL)
2432             {
2433               strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2434               real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2435               g_free (resolved);
2436               goto found;
2437             }
2438         }
2439     }
2440   
2441   /* bah sucks.. */
2442   strcpy (real_dev_root, "/dev/root");
2443   
2444 found:
2445   return real_dev_root;
2446 }
2447 #endif