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