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