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