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