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