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