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