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