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