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