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