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