Home | History | Annotate | Download | only in gio
      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 (at) 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 #include "gioalias.h"
     60 
     61 static const char *_resolve_dev_root (void);
     62 
     63 /**
     64  * SECTION:gunixmounts
     65  * @include: gio/gunixmounts.h
     66  * @short_description: Unix Mounts
     67  *
     68  * Routines for managing mounted UNIX mount points and paths.
     69  *
     70  * Note that <filename>&lt;gio/gunixmounts.h&gt;</filename> belongs to the
     71  * UNIX-specific GIO interfaces, thus you have to use the
     72  * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
     73  */
     74 
     75 /*
     76  * GUnixMountType:
     77  * @G_UNIX_MOUNT_TYPE_UNKNOWN: Unknown UNIX mount type.
     78  * @G_UNIX_MOUNT_TYPE_FLOPPY: Floppy disk UNIX mount type.
     79  * @G_UNIX_MOUNT_TYPE_CDROM: CDROM UNIX mount type.
     80  * @G_UNIX_MOUNT_TYPE_NFS: Network File System (NFS) UNIX mount type.
     81  * @G_UNIX_MOUNT_TYPE_ZIP: ZIP UNIX mount type.
     82  * @G_UNIX_MOUNT_TYPE_JAZ: JAZZ UNIX mount type.
     83  * @G_UNIX_MOUNT_TYPE_MEMSTICK: Memory Stick UNIX mount type.
     84  * @G_UNIX_MOUNT_TYPE_CF: Compact Flash UNIX mount type.
     85  * @G_UNIX_MOUNT_TYPE_SM: Smart Media UNIX mount type.
     86  * @G_UNIX_MOUNT_TYPE_SDMMC: SD/MMC UNIX mount type.
     87  * @G_UNIX_MOUNT_TYPE_IPOD: iPod UNIX mount type.
     88  * @G_UNIX_MOUNT_TYPE_CAMERA: Digital camera UNIX mount type.
     89  * @G_UNIX_MOUNT_TYPE_HD: Hard drive UNIX mount type.
     90  *
     91  * Types of UNIX mounts.
     92  **/
     93 typedef enum {
     94   G_UNIX_MOUNT_TYPE_UNKNOWN,
     95   G_UNIX_MOUNT_TYPE_FLOPPY,
     96   G_UNIX_MOUNT_TYPE_CDROM,
     97   G_UNIX_MOUNT_TYPE_NFS,
     98   G_UNIX_MOUNT_TYPE_ZIP,
     99   G_UNIX_MOUNT_TYPE_JAZ,
    100   G_UNIX_MOUNT_TYPE_MEMSTICK,
    101   G_UNIX_MOUNT_TYPE_CF,
    102   G_UNIX_MOUNT_TYPE_SM,
    103   G_UNIX_MOUNT_TYPE_SDMMC,
    104   G_UNIX_MOUNT_TYPE_IPOD,
    105   G_UNIX_MOUNT_TYPE_CAMERA,
    106   G_UNIX_MOUNT_TYPE_HD
    107 } GUnixMountType;
    108 
    109 struct _GUnixMountEntry {
    110   char *mount_path;
    111   char *device_path;
    112   char *filesystem_type;
    113   gboolean is_read_only;
    114   gboolean is_system_internal;
    115 };
    116 
    117 struct _GUnixMountPoint {
    118   char *mount_path;
    119   char *device_path;
    120   char *filesystem_type;
    121   gboolean is_read_only;
    122   gboolean is_user_mountable;
    123   gboolean is_loopback;
    124 };
    125 
    126 enum {
    127   MOUNTS_CHANGED,
    128   MOUNTPOINTS_CHANGED,
    129   LAST_SIGNAL
    130 };
    131 
    132 static guint signals[LAST_SIGNAL];
    133 
    134 struct _GUnixMountMonitor {
    135   GObject parent;
    136 
    137   GFileMonitor *fstab_monitor;
    138   GFileMonitor *mtab_monitor;
    139 };
    140 
    141 struct _GUnixMountMonitorClass {
    142   GObjectClass parent_class;
    143 };
    144 
    145 static GUnixMountMonitor *the_mount_monitor = NULL;
    146 
    147 static GList *_g_get_unix_mounts (void);
    148 static GList *_g_get_unix_mount_points (void);
    149 
    150 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
    151 
    152 #define MOUNT_POLL_INTERVAL 4000
    153 
    154 #ifdef HAVE_SYS_MNTTAB_H
    155 #define MNTOPT_RO	"ro"
    156 #endif
    157 
    158 #ifdef HAVE_MNTENT_H
    159 #include <mntent.h>
    160 #elif defined (HAVE_SYS_MNTTAB_H)
    161 #include <sys/mnttab.h>
    162 #endif
    163 
    164 #ifdef HAVE_SYS_VFSTAB_H
    165 #include <sys/vfstab.h>
    166 #endif
    167 
    168 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
    169 #include <sys/mntctl.h>
    170 #include <sys/vfs.h>
    171 #include <sys/vmount.h>
    172 #include <fshelp.h>
    173 #endif
    174 
    175 #if defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
    176 #include <sys/ucred.h>
    177 #include <sys/mount.h>
    178 #include <fstab.h>
    179 #ifdef HAVE_SYS_SYSCTL_H
    180 #include <sys/sysctl.h>
    181 #endif
    182 #endif
    183 
    184 #ifndef HAVE_SETMNTENT
    185 #define setmntent(f,m) fopen(f,m)
    186 #endif
    187 #ifndef HAVE_ENDMNTENT
    188 #define endmntent(f) fclose(f)
    189 #endif
    190 
    191 static gboolean
    192 is_in (const char *value, const char *set[])
    193 {
    194   int i;
    195   for (i = 0; set[i] != NULL; i++)
    196     {
    197       if (strcmp (set[i], value) == 0)
    198 	return TRUE;
    199     }
    200   return FALSE;
    201 }
    202 
    203 /**
    204  * g_unix_is_mount_path_system_internal:
    205  * @mount_path: a mount path, e.g. <filename>/media/disk</filename>
    206  *    or <filename>/usr</filename>
    207  *
    208  * Determines if @mount_path is considered an implementation of the
    209  * OS. This is primarily used for hiding mountable and mounted volumes
    210  * that only are used in the OS and has little to no relevance to the
    211  * casual user.
    212  *
    213  * Returns: %TRUE if @mount_path is considered an implementation detail
    214  *     of the OS.
    215  **/
    216 gboolean
    217 g_unix_is_mount_path_system_internal (const char *mount_path)
    218 {
    219   const char *ignore_mountpoints[] = {
    220     /* Includes all FHS 2.3 toplevel dirs and other specilized
    221      * directories that we want to hide from the user.
    222      */
    223     "/",              /* we already have "Filesystem root" in Nautilus */
    224     "/bin",
    225     "/boot",
    226     "/dev",
    227     "/etc",
    228     "/home",
    229     "/lib",
    230     "/lib64",
    231     "/media",
    232     "/mnt",
    233     "/opt",
    234     "/root",
    235     "/sbin",
    236     "/srv",
    237     "/tmp",
    238     "/usr",
    239     "/usr/local",
    240     "/var",
    241     "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
    242     "/var/tmp",       /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
    243     "/proc",
    244     "/sbin",
    245     "/net",
    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 (strstr (mount_path, "/.gvfs") != NULL)
    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:
   1079  * @time_read: guint64 to contain a timestamp.
   1080  *
   1081  * Gets a #GList of strings 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: a #GList of the UNIX mounts.
   1087  **/
   1088 GList *
   1089 g_unix_mounts_get (guint64 *time_read)
   1090 {
   1091   if (time_read)
   1092     *time_read = get_mounts_timestamp ();
   1093 
   1094   return _g_get_unix_mounts ();
   1095 }
   1096 
   1097 /**
   1098  * g_unix_mount_at:
   1099  * @mount_path: path for a possible unix mount.
   1100  * @time_read: guint64 to contain a timestamp.
   1101  *
   1102  * Gets a #GUnixMountEntry for a given mount path. If @time_read
   1103  * is set, it will be filled with a unix timestamp for checking
   1104  * if the mounts have changed since with g_unix_mounts_changed_since().
   1105  *
   1106  * Returns: a #GUnixMount.
   1107  **/
   1108 GUnixMountEntry *
   1109 g_unix_mount_at (const char *mount_path,
   1110 		 guint64    *time_read)
   1111 {
   1112   GList *mounts, *l;
   1113   GUnixMountEntry *mount_entry, *found;
   1114 
   1115   mounts = g_unix_mounts_get (time_read);
   1116 
   1117   found = NULL;
   1118   for (l = mounts; l != NULL; l = l->next)
   1119     {
   1120       mount_entry = l->data;
   1121 
   1122       if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
   1123 	found = mount_entry;
   1124       else
   1125 	g_unix_mount_free (mount_entry);
   1126     }
   1127   g_list_free (mounts);
   1128 
   1129   return found;
   1130 }
   1131 
   1132 /**
   1133  * g_unix_mount_points_get:
   1134  * @time_read: guint64 to contain a timestamp.
   1135  *
   1136  * Gets a #GList of strings containing the unix mount points.
   1137  * If @time_read is set, it will be filled with the mount timestamp,
   1138  * allowing for checking if the mounts have changed with
   1139  * g_unix_mounts_points_changed_since().
   1140  *
   1141  * Returns: a #GList of the UNIX mountpoints.
   1142  **/
   1143 GList *
   1144 g_unix_mount_points_get (guint64 *time_read)
   1145 {
   1146   if (time_read)
   1147     *time_read = get_mount_points_timestamp ();
   1148 
   1149   return _g_get_unix_mount_points ();
   1150 }
   1151 
   1152 /**
   1153  * g_unix_mounts_changed_since:
   1154  * @time: guint64 to contain a timestamp.
   1155  *
   1156  * Checks if the unix mounts have changed since a given unix time.
   1157  *
   1158  * Returns: %TRUE if the mounts have changed since @time.
   1159  **/
   1160 gboolean
   1161 g_unix_mounts_changed_since (guint64 time)
   1162 {
   1163   return get_mounts_timestamp () != time;
   1164 }
   1165 
   1166 /**
   1167  * g_unix_mount_points_changed_since:
   1168  * @time: guint64 to contain a timestamp.
   1169  *
   1170  * Checks if the unix mount points have changed since a given unix time.
   1171  *
   1172  * Returns: %TRUE if the mount points have changed since @time.
   1173  **/
   1174 gboolean
   1175 g_unix_mount_points_changed_since (guint64 time)
   1176 {
   1177   return get_mount_points_timestamp () != time;
   1178 }
   1179 
   1180 static void
   1181 g_unix_mount_monitor_finalize (GObject *object)
   1182 {
   1183   GUnixMountMonitor *monitor;
   1184 
   1185   monitor = G_UNIX_MOUNT_MONITOR (object);
   1186 
   1187   if (monitor->fstab_monitor)
   1188     {
   1189       g_file_monitor_cancel (monitor->fstab_monitor);
   1190       g_object_unref (monitor->fstab_monitor);
   1191     }
   1192 
   1193   if (monitor->mtab_monitor)
   1194     {
   1195       g_file_monitor_cancel (monitor->mtab_monitor);
   1196       g_object_unref (monitor->mtab_monitor);
   1197     }
   1198 
   1199   the_mount_monitor = NULL;
   1200 
   1201   G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
   1202 }
   1203 
   1204 
   1205 static void
   1206 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
   1207 {
   1208   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   1209 
   1210   gobject_class->finalize = g_unix_mount_monitor_finalize;
   1211 
   1212   /**
   1213    * GUnixMountMonitor::mounts-changed:
   1214    * @monitor: the object on which the signal is emitted
   1215    *
   1216    * Emitted when the unix mounts have changed.
   1217    */
   1218   signals[MOUNTS_CHANGED] =
   1219     g_signal_new ("mounts-changed",
   1220 		  G_TYPE_FROM_CLASS (klass),
   1221 		  G_SIGNAL_RUN_LAST,
   1222 		  0,
   1223 		  NULL, NULL,
   1224 		  g_cclosure_marshal_VOID__VOID,
   1225 		  G_TYPE_NONE, 0);
   1226 
   1227   /**
   1228    * GUnixMountMonitor::mountpoints-changed:
   1229    * @monitor: the object on which the signal is emitted
   1230    *
   1231    * Emitted when the unix mount points have changed.
   1232    */
   1233   signals[MOUNTPOINTS_CHANGED] =
   1234     g_signal_new ("mountpoints-changed",
   1235 		  G_TYPE_FROM_CLASS (klass),
   1236 		  G_SIGNAL_RUN_LAST,
   1237 		  0,
   1238 		  NULL, NULL,
   1239 		  g_cclosure_marshal_VOID__VOID,
   1240 		  G_TYPE_NONE, 0);
   1241 }
   1242 
   1243 static void
   1244 fstab_file_changed (GFileMonitor      *monitor,
   1245 		    GFile             *file,
   1246 		    GFile             *other_file,
   1247 		    GFileMonitorEvent  event_type,
   1248 		    gpointer           user_data)
   1249 {
   1250   GUnixMountMonitor *mount_monitor;
   1251 
   1252   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
   1253       event_type != G_FILE_MONITOR_EVENT_CREATED &&
   1254       event_type != G_FILE_MONITOR_EVENT_DELETED)
   1255     return;
   1256 
   1257   mount_monitor = user_data;
   1258   g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
   1259 }
   1260 
   1261 static void
   1262 mtab_file_changed (GFileMonitor      *monitor,
   1263 		   GFile             *file,
   1264 		   GFile             *other_file,
   1265 		   GFileMonitorEvent  event_type,
   1266 		   gpointer           user_data)
   1267 {
   1268   GUnixMountMonitor *mount_monitor;
   1269 
   1270   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
   1271       event_type != G_FILE_MONITOR_EVENT_CREATED &&
   1272       event_type != G_FILE_MONITOR_EVENT_DELETED)
   1273     return;
   1274 
   1275   mount_monitor = user_data;
   1276   g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
   1277 }
   1278 
   1279 static void
   1280 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
   1281 {
   1282   GFile *file;
   1283 
   1284   if (get_fstab_file () != NULL)
   1285     {
   1286       file = g_file_new_for_path (get_fstab_file ());
   1287       monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
   1288       g_object_unref (file);
   1289 
   1290       g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
   1291     }
   1292 
   1293   if (get_mtab_monitor_file () != NULL)
   1294     {
   1295       file = g_file_new_for_path (get_mtab_monitor_file ());
   1296       monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
   1297       g_object_unref (file);
   1298 
   1299       g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
   1300     }
   1301 }
   1302 
   1303 /**
   1304  * g_unix_mount_monitor_set_rate_limit:
   1305  * @mount_monitor: a #GUnixMountMonitor
   1306  * @limit_msec: a integer with the limit in milliseconds to
   1307  *     poll for changes.
   1308  *
   1309  * Sets the rate limit to which the @mount_monitor will report
   1310  * consecutive change events to the mount and mount point entry files.
   1311  *
   1312  * Since: 2.18
   1313  */
   1314 void
   1315 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
   1316                                      gint               limit_msec)
   1317 {
   1318   g_return_if_fail (G_IS_UNIX_MOUNT_MONITOR (mount_monitor));
   1319 
   1320   if (mount_monitor->fstab_monitor != NULL)
   1321     g_file_monitor_set_rate_limit (mount_monitor->fstab_monitor, limit_msec);
   1322 
   1323   if (mount_monitor->mtab_monitor != NULL)
   1324     g_file_monitor_set_rate_limit (mount_monitor->mtab_monitor, limit_msec);
   1325 }
   1326 
   1327 /**
   1328  * g_unix_mount_monitor_new:
   1329  *
   1330  * Gets a new #GUnixMountMonitor. The default rate limit for which the
   1331  * monitor will report consecutive changes for the mount and mount
   1332  * point entry files is the default for a #GFileMonitor. Use
   1333  * g_unix_mount_monitor_set_rate_limit() to change this.
   1334  *
   1335  * Returns: a #GUnixMountMonitor.
   1336  */
   1337 GUnixMountMonitor *
   1338 g_unix_mount_monitor_new (void)
   1339 {
   1340   if (the_mount_monitor == NULL)
   1341     {
   1342       the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
   1343       return the_mount_monitor;
   1344     }
   1345 
   1346   return g_object_ref (the_mount_monitor);
   1347 }
   1348 
   1349 /**
   1350  * g_unix_mount_free:
   1351  * @mount_entry: a #GUnixMount.
   1352  *
   1353  * Frees a unix mount.
   1354  */
   1355 void
   1356 g_unix_mount_free (GUnixMountEntry *mount_entry)
   1357 {
   1358   g_return_if_fail (mount_entry != NULL);
   1359 
   1360   g_free (mount_entry->mount_path);
   1361   g_free (mount_entry->device_path);
   1362   g_free (mount_entry->filesystem_type);
   1363   g_free (mount_entry);
   1364 }
   1365 
   1366 /**
   1367  * g_unix_mount_point_free:
   1368  * @mount_point: unix mount point to free.
   1369  *
   1370  * Frees a unix mount point.
   1371  */
   1372 void
   1373 g_unix_mount_point_free (GUnixMountPoint *mount_point)
   1374 {
   1375   g_return_if_fail (mount_point != NULL);
   1376 
   1377   g_free (mount_point->mount_path);
   1378   g_free (mount_point->device_path);
   1379   g_free (mount_point->filesystem_type);
   1380   g_free (mount_point);
   1381 }
   1382 
   1383 /**
   1384  * g_unix_mount_compare:
   1385  * @mount1: first #GUnixMountEntry to compare.
   1386  * @mount2: second #GUnixMountEntry to compare.
   1387  *
   1388  * Compares two unix mounts.
   1389  *
   1390  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
   1391  * or less than @mount2, respectively.
   1392  */
   1393 gint
   1394 g_unix_mount_compare (GUnixMountEntry *mount1,
   1395 		      GUnixMountEntry *mount2)
   1396 {
   1397   int res;
   1398 
   1399   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
   1400 
   1401   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
   1402   if (res != 0)
   1403     return res;
   1404 
   1405   res = g_strcmp0 (mount1->device_path, mount2->device_path);
   1406   if (res != 0)
   1407     return res;
   1408 
   1409   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
   1410   if (res != 0)
   1411     return res;
   1412 
   1413   res =  mount1->is_read_only - mount2->is_read_only;
   1414   if (res != 0)
   1415     return res;
   1416 
   1417   return 0;
   1418 }
   1419 
   1420 /**
   1421  * g_unix_mount_get_mount_path:
   1422  * @mount_entry: input #GUnixMountEntry to get the mount path for.
   1423  *
   1424  * Gets the mount path for a unix mount.
   1425  *
   1426  * Returns: the mount path for @mount_entry.
   1427  */
   1428 const gchar *
   1429 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
   1430 {
   1431   g_return_val_if_fail (mount_entry != NULL, NULL);
   1432 
   1433   return mount_entry->mount_path;
   1434 }
   1435 
   1436 /**
   1437  * g_unix_mount_get_device_path:
   1438  * @mount_entry: a #GUnixMount.
   1439  *
   1440  * Gets the device path for a unix mount.
   1441  *
   1442  * Returns: a string containing the device path.
   1443  */
   1444 const gchar *
   1445 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
   1446 {
   1447   g_return_val_if_fail (mount_entry != NULL, NULL);
   1448 
   1449   return mount_entry->device_path;
   1450 }
   1451 
   1452 /**
   1453  * g_unix_mount_get_fs_type:
   1454  * @mount_entry: a #GUnixMount.
   1455  *
   1456  * Gets the filesystem type for the unix mount.
   1457  *
   1458  * Returns: a string containing the file system type.
   1459  */
   1460 const gchar *
   1461 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
   1462 {
   1463   g_return_val_if_fail (mount_entry != NULL, NULL);
   1464 
   1465   return mount_entry->filesystem_type;
   1466 }
   1467 
   1468 /**
   1469  * g_unix_mount_is_readonly:
   1470  * @mount_entry: a #GUnixMount.
   1471  *
   1472  * Checks if a unix mount is mounted read only.
   1473  *
   1474  * Returns: %TRUE if @mount_entry is read only.
   1475  */
   1476 gboolean
   1477 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
   1478 {
   1479   g_return_val_if_fail (mount_entry != NULL, FALSE);
   1480 
   1481   return mount_entry->is_read_only;
   1482 }
   1483 
   1484 /**
   1485  * g_unix_mount_is_system_internal:
   1486  * @mount_entry: a #GUnixMount.
   1487  *
   1488  * Checks if a unix mount is a system path.
   1489  *
   1490  * Returns: %TRUE if the unix mount is for a system path.
   1491  */
   1492 gboolean
   1493 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
   1494 {
   1495   g_return_val_if_fail (mount_entry != NULL, FALSE);
   1496 
   1497   return mount_entry->is_system_internal;
   1498 }
   1499 
   1500 /**
   1501  * g_unix_mount_point_compare:
   1502  * @mount1: a #GUnixMount.
   1503  * @mount2: a #GUnixMount.
   1504  *
   1505  * Compares two unix mount points.
   1506  *
   1507  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
   1508  * or less than @mount2, respectively.
   1509  */
   1510 gint
   1511 g_unix_mount_point_compare (GUnixMountPoint *mount1,
   1512 			    GUnixMountPoint *mount2)
   1513 {
   1514   int res;
   1515 
   1516   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
   1517 
   1518   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
   1519   if (res != 0)
   1520     return res;
   1521 
   1522   res = g_strcmp0 (mount1->device_path, mount2->device_path);
   1523   if (res != 0)
   1524     return res;
   1525 
   1526   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
   1527   if (res != 0)
   1528     return res;
   1529 
   1530   res =  mount1->is_read_only - mount2->is_read_only;
   1531   if (res != 0)
   1532     return res;
   1533 
   1534   res = mount1->is_user_mountable - mount2->is_user_mountable;
   1535   if (res != 0)
   1536     return res;
   1537 
   1538   res = mount1->is_loopback - mount2->is_loopback;
   1539   if (res != 0)
   1540     return res;
   1541 
   1542   return 0;
   1543 }
   1544 
   1545 /**
   1546  * g_unix_mount_point_get_mount_path:
   1547  * @mount_point: a #GUnixMountPoint.
   1548  *
   1549  * Gets the mount path for a unix mount point.
   1550  *
   1551  * Returns: a string containing the mount path.
   1552  */
   1553 const gchar *
   1554 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
   1555 {
   1556   g_return_val_if_fail (mount_point != NULL, NULL);
   1557 
   1558   return mount_point->mount_path;
   1559 }
   1560 
   1561 /**
   1562  * g_unix_mount_point_get_device_path:
   1563  * @mount_point: a #GUnixMountPoint.
   1564  *
   1565  * Gets the device path for a unix mount point.
   1566  *
   1567  * Returns: a string containing the device path.
   1568  */
   1569 const gchar *
   1570 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
   1571 {
   1572   g_return_val_if_fail (mount_point != NULL, NULL);
   1573 
   1574   return mount_point->device_path;
   1575 }
   1576 
   1577 /**
   1578  * g_unix_mount_point_get_fs_type:
   1579  * @mount_point: a #GUnixMountPoint.
   1580  *
   1581  * Gets the file system type for the mount point.
   1582  *
   1583  * Returns: a string containing the file system type.
   1584  */
   1585 const gchar *
   1586 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
   1587 {
   1588   g_return_val_if_fail (mount_point != NULL, NULL);
   1589 
   1590   return mount_point->filesystem_type;
   1591 }
   1592 
   1593 /**
   1594  * g_unix_mount_point_is_readonly:
   1595  * @mount_point: a #GUnixMountPoint.
   1596  *
   1597  * Checks if a unix mount point is read only.
   1598  *
   1599  * Returns: %TRUE if a mount point is read only.
   1600  */
   1601 gboolean
   1602 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
   1603 {
   1604   g_return_val_if_fail (mount_point != NULL, FALSE);
   1605 
   1606   return mount_point->is_read_only;
   1607 }
   1608 
   1609 /**
   1610  * g_unix_mount_point_is_user_mountable:
   1611  * @mount_point: a #GUnixMountPoint.
   1612  *
   1613  * Checks if a unix mount point is mountable by the user.
   1614  *
   1615  * Returns: %TRUE if the mount point is user mountable.
   1616  */
   1617 gboolean
   1618 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
   1619 {
   1620   g_return_val_if_fail (mount_point != NULL, FALSE);
   1621 
   1622   return mount_point->is_user_mountable;
   1623 }
   1624 
   1625 /**
   1626  * g_unix_mount_point_is_loopback:
   1627  * @mount_point: a #GUnixMountPoint.
   1628  *
   1629  * Checks if a unix mount point is a loopback device.
   1630  *
   1631  * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
   1632  */
   1633 gboolean
   1634 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
   1635 {
   1636   g_return_val_if_fail (mount_point != NULL, FALSE);
   1637 
   1638   return mount_point->is_loopback;
   1639 }
   1640 
   1641 static GUnixMountType
   1642 guess_mount_type (const char *mount_path,
   1643 		  const char *device_path,
   1644 		  const char *filesystem_type)
   1645 {
   1646   GUnixMountType type;
   1647   char *basename;
   1648 
   1649   type = G_UNIX_MOUNT_TYPE_UNKNOWN;
   1650 
   1651   if ((strcmp (filesystem_type, "udf") == 0) ||
   1652       (strcmp (filesystem_type, "iso9660") == 0) ||
   1653       (strcmp (filesystem_type, "cd9660") == 0))
   1654     type = G_UNIX_MOUNT_TYPE_CDROM;
   1655   else if ((strcmp (filesystem_type, "nfs") == 0) ||
   1656            (strcmp (filesystem_type, "nfs4") == 0))
   1657     type = G_UNIX_MOUNT_TYPE_NFS;
   1658   else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
   1659 	   g_str_has_prefix (device_path, "/dev/fd") ||
   1660 	   g_str_has_prefix (device_path, "/dev/floppy"))
   1661     type = G_UNIX_MOUNT_TYPE_FLOPPY;
   1662   else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
   1663 	   g_str_has_prefix (device_path, "/dev/acd") ||
   1664 	   g_str_has_prefix (device_path, "/dev/cd"))
   1665     type = G_UNIX_MOUNT_TYPE_CDROM;
   1666   else if (g_str_has_prefix (device_path, "/vol/"))
   1667     {
   1668       const char *name = mount_path + strlen ("/");
   1669 
   1670       if (g_str_has_prefix (name, "cdrom"))
   1671 	type = G_UNIX_MOUNT_TYPE_CDROM;
   1672       else if (g_str_has_prefix (name, "floppy") ||
   1673 	       g_str_has_prefix (device_path, "/vol/dev/diskette/"))
   1674 	type = G_UNIX_MOUNT_TYPE_FLOPPY;
   1675       else if (g_str_has_prefix (name, "rmdisk"))
   1676 	type = G_UNIX_MOUNT_TYPE_ZIP;
   1677       else if (g_str_has_prefix (name, "jaz"))
   1678 	type = G_UNIX_MOUNT_TYPE_JAZ;
   1679       else if (g_str_has_prefix (name, "memstick"))
   1680 	type = G_UNIX_MOUNT_TYPE_MEMSTICK;
   1681     }
   1682   else
   1683     {
   1684       basename = g_path_get_basename (mount_path);
   1685 
   1686       if (g_str_has_prefix (basename, "cdr") ||
   1687 	  g_str_has_prefix (basename, "cdwriter") ||
   1688 	  g_str_has_prefix (basename, "burn") ||
   1689 	  g_str_has_prefix (basename, "dvdr"))
   1690 	type = G_UNIX_MOUNT_TYPE_CDROM;
   1691       else if (g_str_has_prefix (basename, "floppy"))
   1692 	type = G_UNIX_MOUNT_TYPE_FLOPPY;
   1693       else if (g_str_has_prefix (basename, "zip"))
   1694 	type = G_UNIX_MOUNT_TYPE_ZIP;
   1695       else if (g_str_has_prefix (basename, "jaz"))
   1696 	type = G_UNIX_MOUNT_TYPE_JAZ;
   1697       else if (g_str_has_prefix (basename, "camera"))
   1698 	type = G_UNIX_MOUNT_TYPE_CAMERA;
   1699       else if (g_str_has_prefix (basename, "memstick") ||
   1700 	       g_str_has_prefix (basename, "memory_stick") ||
   1701 	       g_str_has_prefix (basename, "ram"))
   1702 	type = G_UNIX_MOUNT_TYPE_MEMSTICK;
   1703       else if (g_str_has_prefix (basename, "compact_flash"))
   1704 	type = G_UNIX_MOUNT_TYPE_CF;
   1705       else if (g_str_has_prefix (basename, "smart_media"))
   1706 	type = G_UNIX_MOUNT_TYPE_SM;
   1707       else if (g_str_has_prefix (basename, "sd_mmc"))
   1708 	type = G_UNIX_MOUNT_TYPE_SDMMC;
   1709       else if (g_str_has_prefix (basename, "ipod"))
   1710 	type = G_UNIX_MOUNT_TYPE_IPOD;
   1711 
   1712       g_free (basename);
   1713     }
   1714 
   1715   if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
   1716     type = G_UNIX_MOUNT_TYPE_HD;
   1717 
   1718   return type;
   1719 }
   1720 
   1721 /*
   1722  * g_unix_mount_guess_type:
   1723  * @mount_entry: a #GUnixMount.
   1724  *
   1725  * Guesses the type of a unix mount. If the mount type cannot be
   1726  * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
   1727  *
   1728  * Returns: a #GUnixMountType.
   1729  */
   1730 static GUnixMountType
   1731 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
   1732 {
   1733   g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
   1734   g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
   1735   g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
   1736   g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
   1737 
   1738   return guess_mount_type (mount_entry->mount_path,
   1739 			   mount_entry->device_path,
   1740 			   mount_entry->filesystem_type);
   1741 }
   1742 
   1743 /*
   1744  * g_unix_mount_point_guess_type:
   1745  * @mount_point: a #GUnixMountPoint.
   1746  *
   1747  * Guesses the type of a unix mount point.
   1748  * If the mount type cannot be determined,
   1749  * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
   1750  *
   1751  * Returns: a #GUnixMountType.
   1752  */
   1753 static GUnixMountType
   1754 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
   1755 {
   1756   g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
   1757   g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
   1758   g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
   1759   g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
   1760 
   1761   return guess_mount_type (mount_point->mount_path,
   1762 			   mount_point->device_path,
   1763 			   mount_point->filesystem_type);
   1764 }
   1765 
   1766 static const char *
   1767 type_to_icon (GUnixMountType type, gboolean is_mount_point)
   1768 {
   1769   const char *icon_name;
   1770 
   1771   switch (type)
   1772     {
   1773     case G_UNIX_MOUNT_TYPE_HD:
   1774       if (is_mount_point)
   1775         icon_name = "drive-removable-media";
   1776       else
   1777         icon_name = "drive-harddisk";
   1778       break;
   1779     case G_UNIX_MOUNT_TYPE_FLOPPY:
   1780     case G_UNIX_MOUNT_TYPE_ZIP:
   1781     case G_UNIX_MOUNT_TYPE_JAZ:
   1782       if (is_mount_point)
   1783         icon_name = "drive-removable-media";
   1784       else
   1785         icon_name = "media-floppy";
   1786       break;
   1787     case G_UNIX_MOUNT_TYPE_CDROM:
   1788       if (is_mount_point)
   1789         icon_name = "drive-optical";
   1790       else
   1791         icon_name = "media-optical";
   1792       break;
   1793     case G_UNIX_MOUNT_TYPE_NFS:
   1794       /* TODO: Would like a better icon here... */
   1795       if (is_mount_point)
   1796         icon_name = "drive-removable-media";
   1797       else
   1798         icon_name = "drive-harddisk";
   1799       break;
   1800     case G_UNIX_MOUNT_TYPE_MEMSTICK:
   1801       if (is_mount_point)
   1802         icon_name = "drive-removable-media";
   1803       else
   1804         icon_name = "media-flash";
   1805       break;
   1806     case G_UNIX_MOUNT_TYPE_CAMERA:
   1807       if (is_mount_point)
   1808         icon_name = "drive-removable-media";
   1809       else
   1810         icon_name = "camera-photo";
   1811       break;
   1812     case G_UNIX_MOUNT_TYPE_IPOD:
   1813       if (is_mount_point)
   1814         icon_name = "drive-removable-media";
   1815       else
   1816         icon_name = "multimedia-player";
   1817       break;
   1818     case G_UNIX_MOUNT_TYPE_UNKNOWN:
   1819     default:
   1820       if (is_mount_point)
   1821         icon_name = "drive-removable-media";
   1822       else
   1823         icon_name = "drive-harddisk";
   1824       break;
   1825     }
   1826 
   1827   return icon_name;
   1828 }
   1829 
   1830 /**
   1831  * g_unix_mount_guess_name:
   1832  * @mount_entry: a #GUnixMountEntry
   1833  *
   1834  * Guesses the name of a Unix mount.
   1835  * The result is a translated string.
   1836  *
   1837  * Returns: A newly allocated string that must
   1838  *     be freed with g_free()
   1839  */
   1840 gchar *
   1841 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
   1842 {
   1843   char *name;
   1844 
   1845   if (strcmp (mount_entry->mount_path, "/") == 0)
   1846     name = g_strdup (_("Filesystem root"));
   1847   else
   1848     name = g_filename_display_basename (mount_entry->mount_path);
   1849 
   1850   return name;
   1851 }
   1852 
   1853 /**
   1854  * g_unix_mount_guess_icon:
   1855  * @mount_entry: a #GUnixMountEntry
   1856  *
   1857  * Guesses the icon of a Unix mount.
   1858  *
   1859  * Returns: a #GIcon
   1860  */
   1861 GIcon *
   1862 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
   1863 {
   1864   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE));
   1865 }
   1866 
   1867 /**
   1868  * g_unix_mount_point_guess_name:
   1869  * @mount_point: a #GUnixMountPoint
   1870  *
   1871  * Guesses the name of a Unix mount point.
   1872  * The result is a translated string.
   1873  *
   1874  * Returns: A newly allocated string that must
   1875  *     be freed with g_free()
   1876  */
   1877 gchar *
   1878 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
   1879 {
   1880   char *name;
   1881 
   1882   if (strcmp (mount_point->mount_path, "/") == 0)
   1883     name = g_strdup (_("Filesystem root"));
   1884   else
   1885     name = g_filename_display_basename (mount_point->mount_path);
   1886 
   1887   return name;
   1888 }
   1889 
   1890 /**
   1891  * g_unix_mount_point_guess_icon:
   1892  * @mount_point: a #GUnixMountPoint
   1893  *
   1894  * Guesses the icon of a Unix mount point.
   1895  *
   1896  * Returns: a #GIcon
   1897  */
   1898 GIcon *
   1899 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
   1900 {
   1901   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE));
   1902 }
   1903 
   1904 /**
   1905  * g_unix_mount_guess_can_eject:
   1906  * @mount_entry: a #GUnixMountEntry
   1907  *
   1908  * Guesses whether a Unix mount can be ejected.
   1909  *
   1910  * Returns: %TRUE if @mount_entry is deemed to be ejectable.
   1911  */
   1912 gboolean
   1913 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
   1914 {
   1915   GUnixMountType guessed_type;
   1916 
   1917   guessed_type = g_unix_mount_guess_type (mount_entry);
   1918   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
   1919       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
   1920     return TRUE;
   1921 
   1922   return FALSE;
   1923 }
   1924 
   1925 /**
   1926  * g_unix_mount_guess_should_display:
   1927  * @mount_entry: a #GUnixMountEntry
   1928  *
   1929  * Guesses whether a Unix mount should be displayed in the UI.
   1930  *
   1931  * Returns: %TRUE if @mount_entry is deemed to be displayable.
   1932  */
   1933 gboolean
   1934 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
   1935 {
   1936   const char *mount_path;
   1937 
   1938   /* Never display internal mountpoints */
   1939   if (g_unix_mount_is_system_internal (mount_entry))
   1940     return FALSE;
   1941 
   1942   /* Only display things in /media (which are generally user mountable)
   1943      and home dir (fuse stuff) */
   1944   mount_path = mount_entry->mount_path;
   1945   if (mount_path != NULL)
   1946     {
   1947       if (g_str_has_prefix (mount_path, "/media/"))
   1948         {
   1949           char *path;
   1950           /* Avoid displaying mounts that are not accessible to the user.
   1951            *
   1952            * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
   1953            * want to avoid g_access() for mount points which can potentially
   1954            * block or fail stat()'ing, such as network mounts.
   1955            */
   1956           path = g_path_get_dirname (mount_path);
   1957           if (g_str_has_prefix (path, "/media/"))
   1958             {
   1959               if (g_access (path, R_OK|X_OK) != 0)
   1960                 {
   1961                   g_free (path);
   1962                   return FALSE;
   1963                 }
   1964             }
   1965           g_free (path);
   1966 
   1967           if (mount_entry->device_path && mount_entry->device_path[0] == '/')
   1968            {
   1969              struct stat st;
   1970              if (g_stat (mount_entry->device_path, &st) == 0 &&
   1971                  S_ISBLK(st.st_mode) &&
   1972                  g_access (mount_path, R_OK|X_OK) != 0)
   1973                return FALSE;
   1974            }
   1975           return TRUE;
   1976         }
   1977 
   1978       if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
   1979           mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
   1980         return TRUE;
   1981     }
   1982 
   1983   return FALSE;
   1984 }
   1985 
   1986 /**
   1987  * g_unix_mount_point_guess_can_eject:
   1988  * @mount_point: a #GUnixMountPoint
   1989  *
   1990  * Guesses whether a Unix mount point can be ejected.
   1991  *
   1992  * Returns: %TRUE if @mount_point is deemed to be ejectable.
   1993  */
   1994 gboolean
   1995 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
   1996 {
   1997   GUnixMountType guessed_type;
   1998 
   1999   guessed_type = g_unix_mount_point_guess_type (mount_point);
   2000   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
   2001       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
   2002     return TRUE;
   2003 
   2004   return FALSE;
   2005 }
   2006 
   2007 
   2008 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
   2009 static void
   2010 _canonicalize_filename (gchar *filename)
   2011 {
   2012   gchar *p, *q;
   2013   gboolean last_was_slash = FALSE;
   2014 
   2015   p = filename;
   2016   q = filename;
   2017 
   2018   while (*p)
   2019     {
   2020       if (*p == G_DIR_SEPARATOR)
   2021         {
   2022           if (!last_was_slash)
   2023             *q++ = G_DIR_SEPARATOR;
   2024 
   2025           last_was_slash = TRUE;
   2026         }
   2027       else
   2028         {
   2029           if (last_was_slash && *p == '.')
   2030             {
   2031               if (*(p + 1) == G_DIR_SEPARATOR ||
   2032                   *(p + 1) == '\0')
   2033                 {
   2034                   if (*(p + 1) == '\0')
   2035                     break;
   2036 
   2037                   p += 1;
   2038                 }
   2039               else if (*(p + 1) == '.' &&
   2040                        (*(p + 2) == G_DIR_SEPARATOR ||
   2041                         *(p + 2) == '\0'))
   2042                 {
   2043                   if (q > filename + 1)
   2044                     {
   2045                       q--;
   2046                       while (q > filename + 1 &&
   2047                              *(q - 1) != G_DIR_SEPARATOR)
   2048                         q--;
   2049                     }
   2050 
   2051                   if (*(p + 2) == '\0')
   2052                     break;
   2053 
   2054                   p += 2;
   2055                 }
   2056               else
   2057                 {
   2058                   *q++ = *p;
   2059                   last_was_slash = FALSE;
   2060                 }
   2061             }
   2062           else
   2063             {
   2064               *q++ = *p;
   2065               last_was_slash = FALSE;
   2066             }
   2067         }
   2068 
   2069       p++;
   2070     }
   2071 
   2072   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
   2073     q--;
   2074 
   2075   *q = '\0';
   2076 }
   2077 
   2078 static char *
   2079 _resolve_symlink (const char *file)
   2080 {
   2081   GError *error;
   2082   char *dir;
   2083   char *link;
   2084   char *f;
   2085   char *f1;
   2086 
   2087   f = g_strdup (file);
   2088 
   2089   while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
   2090     {
   2091       link = g_file_read_link (f, &error);
   2092       if (link == NULL)
   2093         {
   2094           g_error_free (error);
   2095           g_free (f);
   2096           f = NULL;
   2097           goto out;
   2098         }
   2099 
   2100       dir = g_path_get_dirname (f);
   2101       f1 = g_strdup_printf ("%s/%s", dir, link);
   2102       g_free (dir);
   2103       g_free (link);
   2104       g_free (f);
   2105       f = f1;
   2106     }
   2107 
   2108  out:
   2109   if (f != NULL)
   2110     _canonicalize_filename (f);
   2111   return f;
   2112 }
   2113 
   2114 #ifdef HAVE_MNTENT_H
   2115 static const char *
   2116 _resolve_dev_root (void)
   2117 {
   2118   static gboolean have_real_dev_root = FALSE;
   2119   static char real_dev_root[256];
   2120   struct stat statbuf;
   2121 
   2122   /* see if it's cached already */
   2123   if (have_real_dev_root)
   2124     goto found;
   2125 
   2126   /* otherwise we're going to find it right away.. */
   2127   have_real_dev_root = TRUE;
   2128 
   2129   if (stat ("/dev/root", &statbuf) == 0)
   2130     {
   2131       if (! S_ISLNK (statbuf.st_mode))
   2132         {
   2133           dev_t root_dev = statbuf.st_dev;
   2134           FILE *f;
   2135           char buf[1024];
   2136 
   2137           /* see if device with similar major:minor as /dev/root is mention
   2138            * in /etc/mtab (it usually is)
   2139            */
   2140           f = fopen ("/etc/mtab", "r");
   2141           if (f != NULL)
   2142             {
   2143 	      struct mntent *entp;
   2144 #ifdef HAVE_GETMNTENT_R
   2145               struct mntent ent;
   2146               while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
   2147                 {
   2148 #else
   2149 	      G_LOCK (getmntent);
   2150 	      while ((entp = getmntent (f)) != NULL)
   2151                 {
   2152 #endif
   2153                   if (stat (entp->mnt_fsname, &statbuf) == 0 &&
   2154                       statbuf.st_dev == root_dev)
   2155                     {
   2156                       strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
   2157                       real_dev_root[sizeof (real_dev_root) - 1] = '\0';
   2158                       fclose (f);
   2159                       goto found;
   2160                     }
   2161                 }
   2162 
   2163               endmntent (f);
   2164 
   2165 #ifndef HAVE_GETMNTENT_R
   2166 	      G_UNLOCK (getmntent);
   2167 #endif
   2168             }
   2169 
   2170           /* no, that didn't work.. next we could scan /dev ... but I digress.. */
   2171 
   2172         }
   2173        else
   2174         {
   2175           char *resolved;
   2176           resolved = _resolve_symlink ("/dev/root");
   2177           if (resolved != NULL)
   2178             {
   2179               strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
   2180               real_dev_root[sizeof (real_dev_root) - 1] = '\0';
   2181               g_free (resolved);
   2182               goto found;
   2183             }
   2184         }
   2185     }
   2186 
   2187   /* bah sucks.. */
   2188   strcpy (real_dev_root, "/dev/root");
   2189 
   2190 found:
   2191   return real_dev_root;
   2192 }
   2193 #endif
   2194 
   2195 #define __G_UNIX_MOUNTS_C__
   2196 #include "gioaliasdef.c"
   2197