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