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