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