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