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