gunixmounts: correctly flag hasmntopt usage
[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 #ifdef HAVE_HASMNTOPT
778       /* We ignore bind fstab entries, as we ignore bind mounts anyway */
779       if (hasmntopt (mntent, "bind"))
780         continue;
781 #endif
782
783       mount_entry = g_new0 (GUnixMountPoint, 1);
784       mount_entry->mount_path = g_strdup (mntent->mnt_dir);
785       if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
786         mount_entry->device_path = g_strdup (_resolve_dev_root ());
787       else
788         mount_entry->device_path = g_strdup (mntent->mnt_fsname);
789       mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
790       mount_entry->options = g_strdup (mntent->mnt_opts);
791       
792 #ifdef HAVE_HASMNTOPT
793       if (hasmntopt (mntent, MNTOPT_RO) != NULL)
794         mount_entry->is_read_only = TRUE;
795       
796       if (hasmntopt (mntent, "loop") != NULL)
797         mount_entry->is_loopback = TRUE;
798       
799 #endif
800       
801       if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
802 #ifdef HAVE_HASMNTOPT
803           || (hasmntopt (mntent, "user") != NULL
804               && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
805           || hasmntopt (mntent, "pamconsole") != NULL
806           || hasmntopt (mntent, "users") != NULL
807           || hasmntopt (mntent, "owner") != NULL
808 #endif
809           )
810         mount_entry->is_user_mountable = TRUE;
811       
812       return_list = g_list_prepend (return_list, mount_entry);
813     }
814   
815   endmntent (file);
816
817 #ifndef HAVE_GETMNTENT_R
818   G_UNLOCK (getmntent);
819 #endif
820   
821   return g_list_reverse (return_list);
822 }
823
824 #elif defined (HAVE_SYS_MNTTAB_H)
825
826 static GList *
827 _g_get_unix_mount_points (void)
828 {
829   struct mnttab mntent;
830   FILE *file;
831   char *read_file;
832   GUnixMountPoint *mount_entry;
833   GList *return_list;
834   
835   read_file = get_fstab_file ();
836   
837   file = setmntent (read_file, "r");
838   if (file == NULL)
839     return NULL;
840
841   return_list = NULL;
842   
843   G_LOCK (getmntent);
844   while (! getmntent (file, &mntent))
845     {
846       if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
847           (strcmp (mntent.mnt_mountp, "swap") == 0) ||
848           (strcmp (mntent.mnt_mountp, "none") == 0))
849         continue;
850       
851       mount_entry = g_new0 (GUnixMountPoint, 1);
852       
853       mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
854       mount_entry->device_path = g_strdup (mntent.mnt_special);
855       mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
856       mount_entry->options = g_strdup (mntent.mnt_mntopts);
857       
858 #ifdef HAVE_HASMNTOPT
859       if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
860         mount_entry->is_read_only = TRUE;
861       
862       if (hasmntopt (&mntent, "lofs") != NULL)
863         mount_entry->is_loopback = TRUE;
864 #endif
865       
866       if ((mntent.mnt_fstype != NULL)
867 #ifdef HAVE_HASMNTOPT
868           || (hasmntopt (&mntent, "user") != NULL
869               && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
870           || hasmntopt (&mntent, "pamconsole") != NULL
871           || hasmntopt (&mntent, "users") != NULL
872           || hasmntopt (&mntent, "owner") != NULL
873 #endif
874           )
875         mount_entry->is_user_mountable = TRUE;
876       
877       return_list = g_list_prepend (return_list, mount_entry);
878     }
879   
880   endmntent (file);
881   G_UNLOCK (getmntent);
882   
883   return g_list_reverse (return_list);
884 }
885 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
886
887 /* functions to parse /etc/filesystems on aix */
888
889 /* read character, ignoring comments (begin with '*', end with '\n' */
890 static int
891 aix_fs_getc (FILE *fd)
892 {
893   int c;
894   
895   while ((c = getc (fd)) == '*')
896     {
897       while (((c = getc (fd)) != '\n') && (c != EOF))
898         ;
899     }
900 }
901
902 /* eat all continuous spaces in a file */
903 static int
904 aix_fs_ignorespace (FILE *fd)
905 {
906   int c;
907   
908   while ((c = aix_fs_getc (fd)) != EOF)
909     {
910       if (!g_ascii_isspace (c))
911         {
912           ungetc (c,fd);
913           return c;
914         }
915     }
916   
917   return EOF;
918 }
919
920 /* read one word from file */
921 static int
922 aix_fs_getword (FILE *fd, 
923                 char *word)
924 {
925   int c;
926   
927   aix_fs_ignorespace (fd);
928
929   while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
930     {
931       if (c == '"')
932         {
933           while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
934             *word++ = c;
935           else
936             *word++ = c;
937         }
938     }
939   *word = 0;
940   
941   return c;
942 }
943
944 typedef struct {
945   char mnt_mount[PATH_MAX];
946   char mnt_special[PATH_MAX];
947   char mnt_fstype[16];
948   char mnt_options[128];
949 } AixMountTableEntry;
950
951 /* read mount points properties */
952 static int
953 aix_fs_get (FILE               *fd, 
954             AixMountTableEntry *prop)
955 {
956   static char word[PATH_MAX] = { 0 };
957   char value[PATH_MAX];
958   
959   /* read stanza */
960   if (word[0] == 0)
961     {
962       if (aix_fs_getword (fd, word) == EOF)
963         return EOF;
964     }
965
966   word[strlen(word) - 1] = 0;
967   strcpy (prop->mnt_mount, word);
968   
969   /* read attributes and value */
970   
971   while (aix_fs_getword (fd, word) != EOF)
972     {
973       /* test if is attribute or new stanza */
974       if (word[strlen(word) - 1] == ':')
975         return 0;
976       
977       /* read "=" */
978       aix_fs_getword (fd, value);
979       
980       /* read value */
981       aix_fs_getword (fd, value);
982       
983       if (strcmp (word, "dev") == 0)
984         strcpy (prop->mnt_special, value);
985       else if (strcmp (word, "vfs") == 0)
986         strcpy (prop->mnt_fstype, value);
987       else if (strcmp (word, "options") == 0)
988         strcpy(prop->mnt_options, value);
989     }
990   
991   return 0;
992 }
993
994 static GList *
995 _g_get_unix_mount_points (void)
996 {
997   struct mntent *mntent;
998   FILE *file;
999   char *read_file;
1000   GUnixMountPoint *mount_entry;
1001   AixMountTableEntry mntent;
1002   GList *return_list;
1003   
1004   read_file = get_fstab_file ();
1005   
1006   file = setmntent (read_file, "r");
1007   if (file == NULL)
1008     return NULL;
1009   
1010   return_list = NULL;
1011   
1012   while (!aix_fs_get (file, &mntent))
1013     {
1014       if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1015         {
1016           mount_entry = g_new0 (GUnixMountPoint, 1);
1017           
1018           mount_entry->mount_path = g_strdup (mntent.mnt_mount);
1019           mount_entry->device_path = g_strdup (mntent.mnt_special);
1020           mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
1021           mount_entry->options = g_strdup (mntent.mnt_options);
1022           mount_entry->is_read_only = TRUE;
1023           mount_entry->is_user_mountable = TRUE;
1024           
1025           return_list = g_list_prepend (return_list, mount_entry);
1026         }
1027     }
1028         
1029   endmntent (file);
1030   
1031   return g_list_reverse (return_list);
1032 }
1033
1034 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1035
1036 static GList *
1037 _g_get_unix_mount_points (void)
1038 {
1039   struct fstab *fstab = NULL;
1040   GUnixMountPoint *mount_entry;
1041   GList *return_list;
1042 #ifdef HAVE_SYS_SYSCTL_H
1043   int usermnt = 0;
1044   size_t len = sizeof(usermnt);
1045   struct stat sb;
1046 #endif
1047   
1048   if (!setfsent ())
1049     return NULL;
1050
1051   return_list = NULL;
1052   
1053 #ifdef HAVE_SYS_SYSCTL_H
1054 #if defined(HAVE_SYSCTLBYNAME)
1055   sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1056 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1057   {
1058     int mib[2];
1059     
1060     mib[0] = CTL_VFS;
1061     mib[1] = VFS_USERMOUNT;
1062     sysctl (mib, 2, &usermnt, &len, NULL, 0);
1063   }
1064 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1065   {
1066     int mib[2];
1067     
1068     mib[0] = CTL_KERN;
1069     mib[1] = KERN_USERMOUNT;
1070     sysctl (mib, 2, &usermnt, &len, NULL, 0);
1071   }
1072 #endif
1073 #endif
1074   
1075   while ((fstab = getfsent ()) != NULL)
1076     {
1077       if (strcmp (fstab->fs_vfstype, "swap") == 0)
1078         continue;
1079       
1080       mount_entry = g_new0 (GUnixMountPoint, 1);
1081       
1082       mount_entry->mount_path = g_strdup (fstab->fs_file);
1083       mount_entry->device_path = g_strdup (fstab->fs_spec);
1084       mount_entry->filesystem_type = g_strdup (fstab->fs_vfstype);
1085       mount_entry->options = g_strdup (fstab->fs_mntops);
1086       
1087       if (strcmp (fstab->fs_type, "ro") == 0)
1088         mount_entry->is_read_only = TRUE;
1089
1090 #ifdef HAVE_SYS_SYSCTL_H
1091       if (usermnt != 0)
1092         {
1093           uid_t uid = getuid ();
1094           if (stat (fstab->fs_file, &sb) == 0)
1095             {
1096               if (uid == 0 || sb.st_uid == uid)
1097                 mount_entry->is_user_mountable = TRUE;
1098             }
1099         }
1100 #endif
1101
1102       return_list = g_list_prepend (return_list, mount_entry);
1103     }
1104   
1105   endfsent ();
1106   
1107   return g_list_reverse (return_list);
1108 }
1109 #elif defined(__INTERIX)
1110 static GList *
1111 _g_get_unix_mount_points (void)
1112 {
1113   return _g_get_unix_mounts ();
1114 }
1115 #else
1116 #error No g_get_mount_table() implementation for system
1117 #endif
1118
1119 static guint64
1120 get_mounts_timestamp (void)
1121 {
1122   const char *monitor_file;
1123   struct stat buf;
1124
1125   monitor_file = get_mtab_monitor_file ();
1126   if (monitor_file)
1127     {
1128       if (stat (monitor_file, &buf) == 0)
1129         return (guint64)buf.st_mtime;
1130     }
1131   return 0;
1132 }
1133
1134 static guint64
1135 get_mount_points_timestamp (void)
1136 {
1137   const char *monitor_file;
1138   struct stat buf;
1139
1140   monitor_file = get_fstab_file ();
1141   if (monitor_file)
1142     {
1143       if (stat (monitor_file, &buf) == 0)
1144         return (guint64)buf.st_mtime;
1145     }
1146   return 0;
1147 }
1148
1149 /**
1150  * g_unix_mounts_get: (skip)
1151  * @time_read: (out) (allow-none): guint64 to contain a timestamp, or %NULL
1152  *
1153  * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1154  * If @time_read is set, it will be filled with the mount
1155  * timestamp, allowing for checking if the mounts have changed
1156  * with g_unix_mounts_changed_since().
1157  *
1158  * Returns: (element-type GUnixMountEntry) (transfer full):
1159  *     a #GList of the UNIX mounts.
1160  **/
1161 GList *
1162 g_unix_mounts_get (guint64 *time_read)
1163 {
1164   if (time_read)
1165     *time_read = get_mounts_timestamp ();
1166
1167   return _g_get_unix_mounts ();
1168 }
1169
1170 /**
1171  * g_unix_mount_at: (skip)
1172  * @mount_path: path for a possible unix mount.
1173  * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1174  * 
1175  * Gets a #GUnixMountEntry for a given mount path. If @time_read
1176  * is set, it will be filled with a unix timestamp for checking
1177  * if the mounts have changed since with g_unix_mounts_changed_since().
1178  * 
1179  * Returns: (transfer full): a #GUnixMountEntry.
1180  **/
1181 GUnixMountEntry *
1182 g_unix_mount_at (const char *mount_path,
1183                  guint64    *time_read)
1184 {
1185   GList *mounts, *l;
1186   GUnixMountEntry *mount_entry, *found;
1187   
1188   mounts = g_unix_mounts_get (time_read);
1189
1190   found = NULL;
1191   for (l = mounts; l != NULL; l = l->next)
1192     {
1193       mount_entry = l->data;
1194
1195       if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1196         found = mount_entry;
1197       else
1198         g_unix_mount_free (mount_entry);
1199     }
1200   g_list_free (mounts);
1201
1202   return found;
1203 }
1204
1205 /**
1206  * g_unix_mount_points_get: (skip)
1207  * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1208  *
1209  * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1210  * If @time_read is set, it will be filled with the mount timestamp,
1211  * allowing for checking if the mounts have changed with
1212  * g_unix_mount_points_changed_since().
1213  *
1214  * Returns: (element-type GUnixMountPoint) (transfer full):
1215  *     a #GList of the UNIX mountpoints.
1216  **/
1217 GList *
1218 g_unix_mount_points_get (guint64 *time_read)
1219 {
1220   if (time_read)
1221     *time_read = get_mount_points_timestamp ();
1222
1223   return _g_get_unix_mount_points ();
1224 }
1225
1226 /**
1227  * g_unix_mounts_changed_since:
1228  * @time: guint64 to contain a timestamp.
1229  * 
1230  * Checks if the unix mounts have changed since a given unix time.
1231  * 
1232  * Returns: %TRUE if the mounts have changed since @time. 
1233  **/
1234 gboolean
1235 g_unix_mounts_changed_since (guint64 time)
1236 {
1237   return get_mounts_timestamp () != time;
1238 }
1239
1240 /**
1241  * g_unix_mount_points_changed_since:
1242  * @time: guint64 to contain a timestamp.
1243  * 
1244  * Checks if the unix mount points have changed since a given unix time.
1245  * 
1246  * Returns: %TRUE if the mount points have changed since @time. 
1247  **/
1248 gboolean
1249 g_unix_mount_points_changed_since (guint64 time)
1250 {
1251   return get_mount_points_timestamp () != time;
1252 }
1253
1254 static void
1255 g_unix_mount_monitor_finalize (GObject *object)
1256 {
1257   GUnixMountMonitor *monitor;
1258   
1259   monitor = G_UNIX_MOUNT_MONITOR (object);
1260
1261   if (monitor->fstab_monitor)
1262     {
1263       g_file_monitor_cancel (monitor->fstab_monitor);
1264       g_object_unref (monitor->fstab_monitor);
1265     }
1266   
1267   if (monitor->proc_mounts_watch_source != NULL)
1268     g_source_destroy (monitor->proc_mounts_watch_source);
1269
1270   if (monitor->mtab_monitor)
1271     {
1272       g_file_monitor_cancel (monitor->mtab_monitor);
1273       g_object_unref (monitor->mtab_monitor);
1274     }
1275
1276   the_mount_monitor = NULL;
1277
1278   G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1279 }
1280
1281
1282 static void
1283 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1284 {
1285   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1286
1287   gobject_class->finalize = g_unix_mount_monitor_finalize;
1288  
1289   /**
1290    * GUnixMountMonitor::mounts-changed:
1291    * @monitor: the object on which the signal is emitted
1292    * 
1293    * Emitted when the unix mounts have changed.
1294    */ 
1295   signals[MOUNTS_CHANGED] =
1296     g_signal_new ("mounts-changed",
1297                   G_TYPE_FROM_CLASS (klass),
1298                   G_SIGNAL_RUN_LAST,
1299                   0,
1300                   NULL, NULL,
1301                   g_cclosure_marshal_VOID__VOID,
1302                   G_TYPE_NONE, 0);
1303
1304   /**
1305    * GUnixMountMonitor::mountpoints-changed:
1306    * @monitor: the object on which the signal is emitted
1307    * 
1308    * Emitted when the unix mount points have changed.
1309    */
1310   signals[MOUNTPOINTS_CHANGED] =
1311     g_signal_new ("mountpoints-changed",
1312                   G_TYPE_FROM_CLASS (klass),
1313                   G_SIGNAL_RUN_LAST,
1314                   0,
1315                   NULL, NULL,
1316                   g_cclosure_marshal_VOID__VOID,
1317                   G_TYPE_NONE, 0);
1318 }
1319
1320 static void
1321 fstab_file_changed (GFileMonitor      *monitor,
1322                     GFile             *file,
1323                     GFile             *other_file,
1324                     GFileMonitorEvent  event_type,
1325                     gpointer           user_data)
1326 {
1327   GUnixMountMonitor *mount_monitor;
1328
1329   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1330       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1331       event_type != G_FILE_MONITOR_EVENT_DELETED)
1332     return;
1333
1334   mount_monitor = user_data;
1335   g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
1336 }
1337
1338 static void
1339 mtab_file_changed (GFileMonitor      *monitor,
1340                    GFile             *file,
1341                    GFile             *other_file,
1342                    GFileMonitorEvent  event_type,
1343                    gpointer           user_data)
1344 {
1345   GUnixMountMonitor *mount_monitor;
1346
1347   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1348       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1349       event_type != G_FILE_MONITOR_EVENT_DELETED)
1350     return;
1351   
1352   mount_monitor = user_data;
1353   g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1354 }
1355
1356 static gboolean
1357 proc_mounts_changed (GIOChannel   *channel,
1358                      GIOCondition  cond,
1359                      gpointer      user_data)
1360 {
1361   GUnixMountMonitor *mount_monitor = G_UNIX_MOUNT_MONITOR (user_data);
1362   if (cond & G_IO_ERR)
1363     g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1364   return TRUE;
1365 }
1366
1367 static void
1368 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1369 {
1370   GFile *file;
1371     
1372   if (get_fstab_file () != NULL)
1373     {
1374       file = g_file_new_for_path (get_fstab_file ());
1375       monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1376       g_object_unref (file);
1377       
1378       g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
1379     }
1380   
1381   if (get_mtab_monitor_file () != NULL)
1382     {
1383       const gchar *mtab_path;
1384
1385       mtab_path = get_mtab_monitor_file ();
1386       /* /proc/mounts monitoring is special - can't just use GFileMonitor.
1387        * See 'man proc' for more details.
1388        */
1389       if (g_strcmp0 (mtab_path, "/proc/mounts") == 0)
1390         {
1391           GIOChannel *proc_mounts_channel;
1392           GError *error = NULL;
1393           proc_mounts_channel = g_io_channel_new_file ("/proc/mounts", "r", &error);
1394           if (proc_mounts_channel == NULL)
1395             {
1396               g_warning ("Error creating IO channel for /proc/mounts: %s (%s, %d)",
1397                          error->message, g_quark_to_string (error->domain), error->code);
1398               g_error_free (error);
1399             }
1400           else
1401             {
1402               monitor->proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1403               g_source_set_callback (monitor->proc_mounts_watch_source,
1404                                      (GSourceFunc) proc_mounts_changed,
1405                                      monitor,
1406                                      NULL);
1407               g_source_attach (monitor->proc_mounts_watch_source,
1408                                g_main_context_get_thread_default ());
1409               g_source_unref (monitor->proc_mounts_watch_source);
1410               g_io_channel_unref (proc_mounts_channel);
1411             }
1412         }
1413       else
1414         {
1415           file = g_file_new_for_path (mtab_path);
1416           monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1417           g_object_unref (file);
1418           g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
1419         }
1420     }
1421 }
1422
1423 /**
1424  * g_unix_mount_monitor_set_rate_limit:
1425  * @mount_monitor: a #GUnixMountMonitor
1426  * @limit_msec: a integer with the limit in milliseconds to
1427  *     poll for changes.
1428  *
1429  * Sets the rate limit to which the @mount_monitor will report
1430  * consecutive change events to the mount and mount point entry files.
1431  *
1432  * Since: 2.18
1433  */
1434 void
1435 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1436                                      gint               limit_msec)
1437 {
1438   g_return_if_fail (G_IS_UNIX_MOUNT_MONITOR (mount_monitor));
1439
1440   if (mount_monitor->fstab_monitor != NULL)
1441     g_file_monitor_set_rate_limit (mount_monitor->fstab_monitor, limit_msec);
1442
1443   if (mount_monitor->mtab_monitor != NULL)
1444     g_file_monitor_set_rate_limit (mount_monitor->mtab_monitor, limit_msec);
1445 }
1446
1447 /**
1448  * g_unix_mount_monitor_new:
1449  * 
1450  * Gets a new #GUnixMountMonitor. The default rate limit for which the
1451  * monitor will report consecutive changes for the mount and mount
1452  * point entry files is the default for a #GFileMonitor. Use
1453  * g_unix_mount_monitor_set_rate_limit() to change this.
1454  * 
1455  * Returns: a #GUnixMountMonitor. 
1456  */
1457 GUnixMountMonitor *
1458 g_unix_mount_monitor_new (void)
1459 {
1460   if (the_mount_monitor == NULL)
1461     {
1462       the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
1463       return the_mount_monitor;
1464     }
1465   
1466   return g_object_ref (the_mount_monitor);
1467 }
1468
1469 /**
1470  * g_unix_mount_free:
1471  * @mount_entry: a #GUnixMountEntry.
1472  * 
1473  * Frees a unix mount.
1474  */
1475 void
1476 g_unix_mount_free (GUnixMountEntry *mount_entry)
1477 {
1478   g_return_if_fail (mount_entry != NULL);
1479
1480   g_free (mount_entry->mount_path);
1481   g_free (mount_entry->device_path);
1482   g_free (mount_entry->filesystem_type);
1483   g_free (mount_entry);
1484 }
1485
1486 /**
1487  * g_unix_mount_point_free:
1488  * @mount_point: unix mount point to free.
1489  * 
1490  * Frees a unix mount point.
1491  */
1492 void
1493 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1494 {
1495   g_return_if_fail (mount_point != NULL);
1496
1497   g_free (mount_point->mount_path);
1498   g_free (mount_point->device_path);
1499   g_free (mount_point->filesystem_type);
1500   g_free (mount_point->options);
1501   g_free (mount_point);
1502 }
1503
1504 /**
1505  * g_unix_mount_compare:
1506  * @mount1: first #GUnixMountEntry to compare.
1507  * @mount2: second #GUnixMountEntry to compare.
1508  * 
1509  * Compares two unix mounts.
1510  * 
1511  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1512  * or less than @mount2, respectively. 
1513  */
1514 gint
1515 g_unix_mount_compare (GUnixMountEntry *mount1,
1516                       GUnixMountEntry *mount2)
1517 {
1518   int res;
1519
1520   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1521   
1522   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1523   if (res != 0)
1524     return res;
1525         
1526   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1527   if (res != 0)
1528     return res;
1529         
1530   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1531   if (res != 0)
1532     return res;
1533
1534   res =  mount1->is_read_only - mount2->is_read_only;
1535   if (res != 0)
1536     return res;
1537   
1538   return 0;
1539 }
1540
1541 /**
1542  * g_unix_mount_get_mount_path:
1543  * @mount_entry: input #GUnixMountEntry to get the mount path for.
1544  * 
1545  * Gets the mount path for a unix mount.
1546  * 
1547  * Returns: the mount path for @mount_entry.
1548  */
1549 const gchar *
1550 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1551 {
1552   g_return_val_if_fail (mount_entry != NULL, NULL);
1553
1554   return mount_entry->mount_path;
1555 }
1556
1557 /**
1558  * g_unix_mount_get_device_path:
1559  * @mount_entry: a #GUnixMount.
1560  * 
1561  * Gets the device path for a unix mount.
1562  * 
1563  * Returns: a string containing the device path.
1564  */
1565 const gchar *
1566 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1567 {
1568   g_return_val_if_fail (mount_entry != NULL, NULL);
1569
1570   return mount_entry->device_path;
1571 }
1572
1573 /**
1574  * g_unix_mount_get_fs_type:
1575  * @mount_entry: a #GUnixMount.
1576  * 
1577  * Gets the filesystem type for the unix mount.
1578  * 
1579  * Returns: a string containing the file system type.
1580  */
1581 const gchar *
1582 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1583 {
1584   g_return_val_if_fail (mount_entry != NULL, NULL);
1585
1586   return mount_entry->filesystem_type;
1587 }
1588
1589 /**
1590  * g_unix_mount_is_readonly:
1591  * @mount_entry: a #GUnixMount.
1592  * 
1593  * Checks if a unix mount is mounted read only.
1594  * 
1595  * Returns: %TRUE if @mount_entry is read only.
1596  */
1597 gboolean
1598 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1599 {
1600   g_return_val_if_fail (mount_entry != NULL, FALSE);
1601
1602   return mount_entry->is_read_only;
1603 }
1604
1605 /**
1606  * g_unix_mount_is_system_internal:
1607  * @mount_entry: a #GUnixMount.
1608  * 
1609  * Checks if a unix mount is a system path.
1610  * 
1611  * Returns: %TRUE if the unix mount is for a system path.
1612  */
1613 gboolean
1614 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1615 {
1616   g_return_val_if_fail (mount_entry != NULL, FALSE);
1617
1618   return mount_entry->is_system_internal;
1619 }
1620
1621 /**
1622  * g_unix_mount_point_compare:
1623  * @mount1: a #GUnixMount.
1624  * @mount2: a #GUnixMount.
1625  * 
1626  * Compares two unix mount points.
1627  * 
1628  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1629  * or less than @mount2, respectively.
1630  */
1631 gint
1632 g_unix_mount_point_compare (GUnixMountPoint *mount1,
1633                             GUnixMountPoint *mount2)
1634 {
1635   int res;
1636
1637   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1638
1639   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1640   if (res != 0) 
1641     return res;
1642         
1643   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1644   if (res != 0) 
1645     return res;
1646         
1647   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1648   if (res != 0) 
1649     return res;
1650
1651   res = g_strcmp0 (mount1->options, mount2->options);
1652   if (res != 0) 
1653     return res;
1654
1655   res =  mount1->is_read_only - mount2->is_read_only;
1656   if (res != 0) 
1657     return res;
1658
1659   res = mount1->is_user_mountable - mount2->is_user_mountable;
1660   if (res != 0) 
1661     return res;
1662
1663   res = mount1->is_loopback - mount2->is_loopback;
1664   if (res != 0)
1665     return res;
1666   
1667   return 0;
1668 }
1669
1670 /**
1671  * g_unix_mount_point_get_mount_path:
1672  * @mount_point: a #GUnixMountPoint.
1673  * 
1674  * Gets the mount path for a unix mount point.
1675  * 
1676  * Returns: a string containing the mount path.
1677  */
1678 const gchar *
1679 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
1680 {
1681   g_return_val_if_fail (mount_point != NULL, NULL);
1682
1683   return mount_point->mount_path;
1684 }
1685
1686 /**
1687  * g_unix_mount_point_get_device_path:
1688  * @mount_point: a #GUnixMountPoint.
1689  * 
1690  * Gets the device path for a unix mount point.
1691  * 
1692  * Returns: a string containing the device path.
1693  */
1694 const gchar *
1695 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
1696 {
1697   g_return_val_if_fail (mount_point != NULL, NULL);
1698
1699   return mount_point->device_path;
1700 }
1701
1702 /**
1703  * g_unix_mount_point_get_fs_type:
1704  * @mount_point: a #GUnixMountPoint.
1705  * 
1706  * Gets the file system type for the mount point.
1707  * 
1708  * Returns: a string containing the file system type.
1709  */
1710 const gchar *
1711 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
1712 {
1713   g_return_val_if_fail (mount_point != NULL, NULL);
1714
1715   return mount_point->filesystem_type;
1716 }
1717
1718 /**
1719  * g_unix_mount_point_get_options:
1720  * @mount_point: a #GUnixMountPoint.
1721  * 
1722  * Gets the options for the mount point.
1723  * 
1724  * Returns: a string containing the options.
1725  *
1726  * Since: 2.32
1727  */
1728 const gchar *
1729 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
1730 {
1731   g_return_val_if_fail (mount_point != NULL, NULL);
1732
1733   return mount_point->options;
1734 }
1735
1736 /**
1737  * g_unix_mount_point_is_readonly:
1738  * @mount_point: a #GUnixMountPoint.
1739  * 
1740  * Checks if a unix mount point is read only.
1741  * 
1742  * Returns: %TRUE if a mount point is read only.
1743  */
1744 gboolean
1745 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
1746 {
1747   g_return_val_if_fail (mount_point != NULL, FALSE);
1748
1749   return mount_point->is_read_only;
1750 }
1751
1752 /**
1753  * g_unix_mount_point_is_user_mountable:
1754  * @mount_point: a #GUnixMountPoint.
1755  * 
1756  * Checks if a unix mount point is mountable by the user.
1757  * 
1758  * Returns: %TRUE if the mount point is user mountable.
1759  */
1760 gboolean
1761 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
1762 {
1763   g_return_val_if_fail (mount_point != NULL, FALSE);
1764
1765   return mount_point->is_user_mountable;
1766 }
1767
1768 /**
1769  * g_unix_mount_point_is_loopback:
1770  * @mount_point: a #GUnixMountPoint.
1771  * 
1772  * Checks if a unix mount point is a loopback device.
1773  * 
1774  * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise. 
1775  */
1776 gboolean
1777 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
1778 {
1779   g_return_val_if_fail (mount_point != NULL, FALSE);
1780
1781   return mount_point->is_loopback;
1782 }
1783
1784 static GUnixMountType
1785 guess_mount_type (const char *mount_path,
1786                   const char *device_path,
1787                   const char *filesystem_type)
1788 {
1789   GUnixMountType type;
1790   char *basename;
1791
1792   type = G_UNIX_MOUNT_TYPE_UNKNOWN;
1793   
1794   if ((strcmp (filesystem_type, "udf") == 0) ||
1795       (strcmp (filesystem_type, "iso9660") == 0) ||
1796       (strcmp (filesystem_type, "cd9660") == 0))
1797     type = G_UNIX_MOUNT_TYPE_CDROM;
1798   else if ((strcmp (filesystem_type, "nfs") == 0) ||
1799            (strcmp (filesystem_type, "nfs4") == 0))
1800     type = G_UNIX_MOUNT_TYPE_NFS;
1801   else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
1802            g_str_has_prefix (device_path, "/dev/fd") ||
1803            g_str_has_prefix (device_path, "/dev/floppy"))
1804     type = G_UNIX_MOUNT_TYPE_FLOPPY;
1805   else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
1806            g_str_has_prefix (device_path, "/dev/acd") ||
1807            g_str_has_prefix (device_path, "/dev/cd"))
1808     type = G_UNIX_MOUNT_TYPE_CDROM;
1809   else if (g_str_has_prefix (device_path, "/vol/"))
1810     {
1811       const char *name = mount_path + strlen ("/");
1812       
1813       if (g_str_has_prefix (name, "cdrom"))
1814         type = G_UNIX_MOUNT_TYPE_CDROM;
1815       else if (g_str_has_prefix (name, "floppy") ||
1816                g_str_has_prefix (device_path, "/vol/dev/diskette/")) 
1817         type = G_UNIX_MOUNT_TYPE_FLOPPY;
1818       else if (g_str_has_prefix (name, "rmdisk")) 
1819         type = G_UNIX_MOUNT_TYPE_ZIP;
1820       else if (g_str_has_prefix (name, "jaz"))
1821         type = G_UNIX_MOUNT_TYPE_JAZ;
1822       else if (g_str_has_prefix (name, "memstick"))
1823         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1824     }
1825   else
1826     {
1827       basename = g_path_get_basename (mount_path);
1828       
1829       if (g_str_has_prefix (basename, "cdr") ||
1830           g_str_has_prefix (basename, "cdwriter") ||
1831           g_str_has_prefix (basename, "burn") ||
1832           g_str_has_prefix (basename, "dvdr"))
1833         type = G_UNIX_MOUNT_TYPE_CDROM;
1834       else if (g_str_has_prefix (basename, "floppy"))
1835         type = G_UNIX_MOUNT_TYPE_FLOPPY;
1836       else if (g_str_has_prefix (basename, "zip"))
1837         type = G_UNIX_MOUNT_TYPE_ZIP;
1838       else if (g_str_has_prefix (basename, "jaz"))
1839         type = G_UNIX_MOUNT_TYPE_JAZ;
1840       else if (g_str_has_prefix (basename, "camera"))
1841         type = G_UNIX_MOUNT_TYPE_CAMERA;
1842       else if (g_str_has_prefix (basename, "memstick") ||
1843                g_str_has_prefix (basename, "memory_stick") ||
1844                g_str_has_prefix (basename, "ram"))
1845         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1846       else if (g_str_has_prefix (basename, "compact_flash"))
1847         type = G_UNIX_MOUNT_TYPE_CF;
1848       else if (g_str_has_prefix (basename, "smart_media"))
1849         type = G_UNIX_MOUNT_TYPE_SM;
1850       else if (g_str_has_prefix (basename, "sd_mmc"))
1851         type = G_UNIX_MOUNT_TYPE_SDMMC;
1852       else if (g_str_has_prefix (basename, "ipod"))
1853         type = G_UNIX_MOUNT_TYPE_IPOD;
1854       
1855       g_free (basename);
1856     }
1857   
1858   if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
1859     type = G_UNIX_MOUNT_TYPE_HD;
1860   
1861   return type;
1862 }
1863
1864 /*
1865  * g_unix_mount_guess_type:
1866  * @mount_entry: a #GUnixMount.
1867  * 
1868  * Guesses the type of a unix mount. If the mount type cannot be 
1869  * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1870  * 
1871  * Returns: a #GUnixMountType. 
1872  */
1873 static GUnixMountType
1874 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
1875 {
1876   g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1877   g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1878   g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1879   g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1880
1881   return guess_mount_type (mount_entry->mount_path,
1882                            mount_entry->device_path,
1883                            mount_entry->filesystem_type);
1884 }
1885
1886 /*
1887  * g_unix_mount_point_guess_type:
1888  * @mount_point: a #GUnixMountPoint.
1889  * 
1890  * Guesses the type of a unix mount point. 
1891  * If the mount type cannot be determined, 
1892  * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1893  * 
1894  * Returns: a #GUnixMountType.
1895  */
1896 static GUnixMountType
1897 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
1898 {
1899   g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1900   g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1901   g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1902   g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1903
1904   return guess_mount_type (mount_point->mount_path,
1905                            mount_point->device_path,
1906                            mount_point->filesystem_type);
1907 }
1908
1909 static const char *
1910 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
1911 {
1912   const char *icon_name;
1913   
1914   switch (type)
1915     {
1916     case G_UNIX_MOUNT_TYPE_HD:
1917       if (is_mount_point)
1918         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1919       else
1920         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
1921       break;
1922     case G_UNIX_MOUNT_TYPE_FLOPPY:
1923     case G_UNIX_MOUNT_TYPE_ZIP:
1924     case G_UNIX_MOUNT_TYPE_JAZ:
1925       if (is_mount_point)
1926         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1927       else
1928         icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
1929       break;
1930     case G_UNIX_MOUNT_TYPE_CDROM:
1931       if (is_mount_point)
1932         icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
1933       else
1934         icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
1935       break;
1936     case G_UNIX_MOUNT_TYPE_NFS:
1937         icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
1938       break;
1939     case G_UNIX_MOUNT_TYPE_MEMSTICK:
1940       if (is_mount_point)
1941         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1942       else
1943         icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
1944       break;
1945     case G_UNIX_MOUNT_TYPE_CAMERA:
1946       if (is_mount_point)
1947         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1948       else
1949         icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
1950       break;
1951     case G_UNIX_MOUNT_TYPE_IPOD:
1952       if (is_mount_point)
1953         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1954       else
1955         icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
1956       break;
1957     case G_UNIX_MOUNT_TYPE_UNKNOWN:
1958     default:
1959       if (is_mount_point)
1960         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
1961       else
1962         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
1963       break;
1964     }
1965
1966   return icon_name;
1967 }
1968
1969 /**
1970  * g_unix_mount_guess_name:
1971  * @mount_entry: a #GUnixMountEntry
1972  * 
1973  * Guesses the name of a Unix mount. 
1974  * The result is a translated string.
1975  *
1976  * Returns: A newly allocated string that must
1977  *     be freed with g_free()
1978  */
1979 gchar *
1980 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
1981 {
1982   char *name;
1983
1984   if (strcmp (mount_entry->mount_path, "/") == 0)
1985     name = g_strdup (_("Filesystem root"));
1986   else
1987     name = g_filename_display_basename (mount_entry->mount_path);
1988
1989   return name;
1990 }
1991
1992 /**
1993  * g_unix_mount_guess_icon:
1994  * @mount_entry: a #GUnixMountEntry
1995  * 
1996  * Guesses the icon of a Unix mount. 
1997  *
1998  * Returns: (transfer full): a #GIcon
1999  */
2000 GIcon *
2001 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2002 {
2003   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2004 }
2005
2006 /**
2007  * g_unix_mount_guess_symbolic_icon:
2008  * @mount_entry: a #GUnixMountEntry
2009  *
2010  * Guesses the symbolic icon of a Unix mount.
2011  *
2012  * Returns: (transfer full): a #GIcon
2013  *
2014  * Since: 2.34
2015  */
2016 GIcon *
2017 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2018 {
2019   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2020 }
2021
2022 /**
2023  * g_unix_mount_point_guess_name:
2024  * @mount_point: a #GUnixMountPoint
2025  * 
2026  * Guesses the name of a Unix mount point. 
2027  * The result is a translated string.
2028  *
2029  * Returns: A newly allocated string that must 
2030  *     be freed with g_free()
2031  */
2032 gchar *
2033 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2034 {
2035   char *name;
2036
2037   if (strcmp (mount_point->mount_path, "/") == 0)
2038     name = g_strdup (_("Filesystem root"));
2039   else
2040     name = g_filename_display_basename (mount_point->mount_path);
2041
2042   return name;
2043 }
2044
2045 /**
2046  * g_unix_mount_point_guess_icon:
2047  * @mount_point: a #GUnixMountPoint
2048  * 
2049  * Guesses the icon of a Unix mount point. 
2050  *
2051  * Returns: (transfer full): a #GIcon
2052  */
2053 GIcon *
2054 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2055 {
2056   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2057 }
2058
2059 /**
2060  * g_unix_mount_point_guess_symbolic_icon:
2061  * @mount_point: a #GUnixMountPoint
2062  *
2063  * Guesses the symbolic icon of a Unix mount point.
2064  *
2065  * Returns: (transfer full): a #GIcon
2066  *
2067  * Since: 2.34
2068  */
2069 GIcon *
2070 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2071 {
2072   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2073 }
2074
2075 /**
2076  * g_unix_mount_guess_can_eject:
2077  * @mount_entry: a #GUnixMountEntry
2078  * 
2079  * Guesses whether a Unix mount can be ejected.
2080  *
2081  * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2082  */
2083 gboolean
2084 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2085 {
2086   GUnixMountType guessed_type;
2087
2088   guessed_type = g_unix_mount_guess_type (mount_entry);
2089   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2090       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2091     return TRUE;
2092
2093   return FALSE;
2094 }
2095
2096 /**
2097  * g_unix_mount_guess_should_display:
2098  * @mount_entry: a #GUnixMountEntry
2099  * 
2100  * Guesses whether a Unix mount should be displayed in the UI.
2101  *
2102  * Returns: %TRUE if @mount_entry is deemed to be displayable.
2103  */
2104 gboolean
2105 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2106 {
2107   const char *mount_path;
2108   const gchar *user_name;
2109   gsize user_name_len;
2110
2111   /* Never display internal mountpoints */
2112   if (g_unix_mount_is_system_internal (mount_entry))
2113     return FALSE;
2114   
2115   /* Only display things in /media (which are generally user mountable)
2116      and home dir (fuse stuff) and /run/media/$USER */
2117   mount_path = mount_entry->mount_path;
2118   if (mount_path != NULL)
2119     {
2120       gboolean is_in_runtime_dir = FALSE;
2121       /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2122       if (g_strstr_len (mount_path, -1, "/.") != NULL)
2123         return FALSE;
2124
2125       /* Check /run/media/$USER/ */
2126       user_name = g_get_user_name ();
2127       user_name_len = strlen (user_name);
2128       if (strncmp (mount_path, "/run/media/", sizeof ("/run/media/") - 1) == 0 &&
2129           strncmp (mount_path + sizeof ("/run/media/") - 1, user_name, user_name_len) == 0 &&
2130           mount_path[sizeof ("/run/media/") - 1 + user_name_len] == '/')
2131         is_in_runtime_dir = TRUE;
2132
2133       if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2134         {
2135           char *path;
2136           /* Avoid displaying mounts that are not accessible to the user.
2137            *
2138            * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2139            * want to avoid g_access() for mount points which can potentially
2140            * block or fail stat()'ing, such as network mounts.
2141            */
2142           path = g_path_get_dirname (mount_path);
2143           if (g_str_has_prefix (path, "/media/"))
2144             {
2145               if (g_access (path, R_OK|X_OK) != 0) 
2146                 {
2147                   g_free (path);
2148                   return FALSE;
2149                 }
2150             }
2151           g_free (path);
2152
2153           if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2154            {
2155              struct stat st;
2156              if (g_stat (mount_entry->device_path, &st) == 0 &&
2157                  S_ISBLK(st.st_mode) &&
2158                  g_access (mount_path, R_OK|X_OK) != 0)
2159                return FALSE;
2160            }
2161           return TRUE;
2162         }
2163       
2164       if (g_str_has_prefix (mount_path, g_get_home_dir ()) && 
2165           mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2166         return TRUE;
2167     }
2168   
2169   return FALSE;
2170 }
2171
2172 /**
2173  * g_unix_mount_point_guess_can_eject:
2174  * @mount_point: a #GUnixMountPoint
2175  * 
2176  * Guesses whether a Unix mount point can be ejected.
2177  *
2178  * Returns: %TRUE if @mount_point is deemed to be ejectable.
2179  */
2180 gboolean
2181 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2182 {
2183   GUnixMountType guessed_type;
2184
2185   guessed_type = g_unix_mount_point_guess_type (mount_point);
2186   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2187       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2188     return TRUE;
2189
2190   return FALSE;
2191 }
2192
2193 #ifdef HAVE_MNTENT_H
2194 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2195 static void
2196 _canonicalize_filename (gchar *filename)
2197 {
2198   gchar *p, *q;
2199   gboolean last_was_slash = FALSE;
2200   
2201   p = filename;
2202   q = filename;
2203   
2204   while (*p)
2205     {
2206       if (*p == G_DIR_SEPARATOR)
2207         {
2208           if (!last_was_slash)
2209             *q++ = G_DIR_SEPARATOR;
2210           
2211           last_was_slash = TRUE;
2212         }
2213       else
2214         {
2215           if (last_was_slash && *p == '.')
2216             {
2217               if (*(p + 1) == G_DIR_SEPARATOR ||
2218                   *(p + 1) == '\0')
2219                 {
2220                   if (*(p + 1) == '\0')
2221                     break;
2222                   
2223                   p += 1;
2224                 }
2225               else if (*(p + 1) == '.' &&
2226                        (*(p + 2) == G_DIR_SEPARATOR ||
2227                         *(p + 2) == '\0'))
2228                 {
2229                   if (q > filename + 1)
2230                     {
2231                       q--;
2232                       while (q > filename + 1 &&
2233                              *(q - 1) != G_DIR_SEPARATOR)
2234                         q--;
2235                     }
2236                   
2237                   if (*(p + 2) == '\0')
2238                     break;
2239                   
2240                   p += 2;
2241                 }
2242               else
2243                 {
2244                   *q++ = *p;
2245                   last_was_slash = FALSE;
2246                 }
2247             }
2248           else
2249             {
2250               *q++ = *p;
2251               last_was_slash = FALSE;
2252             }
2253         }
2254       
2255       p++;
2256     }
2257   
2258   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2259     q--;
2260   
2261   *q = '\0';
2262 }
2263
2264 static char *
2265 _resolve_symlink (const char *file)
2266 {
2267   GError *error;
2268   char *dir;
2269   char *link;
2270   char *f;
2271   char *f1;
2272   
2273   f = g_strdup (file);
2274   
2275   while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) 
2276     {
2277       link = g_file_read_link (f, &error);
2278       if (link == NULL) 
2279         {
2280           g_error_free (error);
2281           g_free (f);
2282           f = NULL;
2283           goto out;
2284         }
2285     
2286       dir = g_path_get_dirname (f);
2287       f1 = g_strdup_printf ("%s/%s", dir, link);
2288       g_free (dir);
2289       g_free (link);
2290       g_free (f);
2291       f = f1;
2292     }
2293   
2294  out:
2295   if (f != NULL)
2296     _canonicalize_filename (f);
2297   return f;
2298 }
2299
2300 static const char *
2301 _resolve_dev_root (void)
2302 {
2303   static gboolean have_real_dev_root = FALSE;
2304   static char real_dev_root[256];
2305   struct stat statbuf;
2306   
2307   /* see if it's cached already */
2308   if (have_real_dev_root)
2309     goto found;
2310   
2311   /* otherwise we're going to find it right away.. */
2312   have_real_dev_root = TRUE;
2313   
2314   if (stat ("/dev/root", &statbuf) == 0) 
2315     {
2316       if (! S_ISLNK (statbuf.st_mode)) 
2317         {
2318           dev_t root_dev = statbuf.st_dev;
2319           FILE *f;
2320           char buf[1024];
2321       
2322           /* see if device with similar major:minor as /dev/root is mention
2323            * in /etc/mtab (it usually is) 
2324            */
2325           f = fopen ("/etc/mtab", "r");
2326           if (f != NULL) 
2327             {
2328               struct mntent *entp;
2329 #ifdef HAVE_GETMNTENT_R        
2330               struct mntent ent;
2331               while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL) 
2332                 {
2333 #else
2334               G_LOCK (getmntent);
2335               while ((entp = getmntent (f)) != NULL) 
2336                 { 
2337 #endif          
2338                   if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2339                       statbuf.st_dev == root_dev) 
2340                     {
2341                       strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2342                       real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2343                       fclose (f);
2344                       goto found;
2345                     }
2346                 }
2347
2348               endmntent (f);
2349
2350 #ifndef HAVE_GETMNTENT_R
2351               G_UNLOCK (getmntent);
2352 #endif
2353             }                                        
2354       
2355           /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2356       
2357         } 
2358        else 
2359         {
2360           char *resolved;
2361           resolved = _resolve_symlink ("/dev/root");
2362           if (resolved != NULL)
2363             {
2364               strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2365               real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2366               g_free (resolved);
2367               goto found;
2368             }
2369         }
2370     }
2371   
2372   /* bah sucks.. */
2373   strcpy (real_dev_root, "/dev/root");
2374   
2375 found:
2376   return real_dev_root;
2377 }
2378 #endif