Imported Upstream version 2.50.2
[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, see <http://www.gnu.org/licenses/>.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 /* Prologue {{{1 */
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 #endif
35 #ifdef HAVE_POLL
36 #include <poll.h>
37 #endif
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <sys/time.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <signal.h>
44 #include <gstdio.h>
45 #include <dirent.h>
46
47 #if HAVE_SYS_STATFS_H
48 #include <sys/statfs.h>
49 #endif
50 #if HAVE_SYS_STATVFS_H
51 #include <sys/statvfs.h>
52 #endif
53 #if HAVE_SYS_VFS_H
54 #include <sys/vfs.h>
55 #elif HAVE_SYS_MOUNT_H
56 #if HAVE_SYS_PARAM_H
57 #include <sys/param.h>
58 #endif
59 #include <sys/mount.h>
60 #endif
61
62 #ifndef O_BINARY
63 #define O_BINARY 0
64 #endif
65
66 #include "gunixmounts.h"
67 #include "gfile.h"
68 #include "gfilemonitor.h"
69 #include "glibintl.h"
70 #include "gthemedicon.h"
71 #include "gcontextspecificgroup.h"
72
73
74 #ifdef HAVE_MNTENT_H
75 static const char *_resolve_dev_root (void);
76 #endif
77
78 /**
79  * SECTION:gunixmounts
80  * @include: gio/gunixmounts.h
81  * @short_description: UNIX mounts
82  *
83  * Routines for managing mounted UNIX mount points and paths.
84  *
85  * Note that `<gio/gunixmounts.h>` belongs to the UNIX-specific GIO
86  * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
87  * file when using it.
88  */
89
90 /**
91  * GUnixMountType:
92  * @G_UNIX_MOUNT_TYPE_UNKNOWN: Unknown UNIX mount type.
93  * @G_UNIX_MOUNT_TYPE_FLOPPY: Floppy disk UNIX mount type.
94  * @G_UNIX_MOUNT_TYPE_CDROM: CDROM UNIX mount type.
95  * @G_UNIX_MOUNT_TYPE_NFS: Network File System (NFS) UNIX mount type.
96  * @G_UNIX_MOUNT_TYPE_ZIP: ZIP UNIX mount type.
97  * @G_UNIX_MOUNT_TYPE_JAZ: JAZZ UNIX mount type.
98  * @G_UNIX_MOUNT_TYPE_MEMSTICK: Memory Stick UNIX mount type.
99  * @G_UNIX_MOUNT_TYPE_CF: Compact Flash UNIX mount type.
100  * @G_UNIX_MOUNT_TYPE_SM: Smart Media UNIX mount type.
101  * @G_UNIX_MOUNT_TYPE_SDMMC: SD/MMC UNIX mount type.
102  * @G_UNIX_MOUNT_TYPE_IPOD: iPod UNIX mount type.
103  * @G_UNIX_MOUNT_TYPE_CAMERA: Digital camera UNIX mount type.
104  * @G_UNIX_MOUNT_TYPE_HD: Hard drive UNIX mount type.
105  * 
106  * Types of UNIX mounts.
107  **/
108 typedef enum {
109   G_UNIX_MOUNT_TYPE_UNKNOWN,
110   G_UNIX_MOUNT_TYPE_FLOPPY,
111   G_UNIX_MOUNT_TYPE_CDROM,
112   G_UNIX_MOUNT_TYPE_NFS,
113   G_UNIX_MOUNT_TYPE_ZIP,
114   G_UNIX_MOUNT_TYPE_JAZ,
115   G_UNIX_MOUNT_TYPE_MEMSTICK,
116   G_UNIX_MOUNT_TYPE_CF,
117   G_UNIX_MOUNT_TYPE_SM,
118   G_UNIX_MOUNT_TYPE_SDMMC,
119   G_UNIX_MOUNT_TYPE_IPOD,
120   G_UNIX_MOUNT_TYPE_CAMERA,
121   G_UNIX_MOUNT_TYPE_HD
122 } GUnixMountType;
123
124 struct _GUnixMountEntry {
125   char *mount_path;
126   char *device_path;
127   char *filesystem_type;
128   gboolean is_read_only;
129   gboolean is_system_internal;
130 };
131
132 struct _GUnixMountPoint {
133   char *mount_path;
134   char *device_path;
135   char *filesystem_type;
136   char *options;
137   gboolean is_read_only;
138   gboolean is_user_mountable;
139   gboolean is_loopback;
140 };
141
142 static GList *_g_get_unix_mounts (void);
143 static GList *_g_get_unix_mount_points (void);
144
145 static guint64 mount_poller_time = 0;
146
147 #ifdef HAVE_SYS_MNTTAB_H
148 #define MNTOPT_RO       "ro"
149 #endif
150
151 #ifdef HAVE_MNTENT_H
152 #include <mntent.h>
153 #ifdef HAVE_LIBMOUNT
154 #include <libmount/libmount.h>
155 #endif
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_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
172 #include <sys/param.h>
173 #include <sys/ucred.h>
174 #include <sys/mount.h>
175 #include <fstab.h>
176 #ifdef HAVE_SYS_SYSCTL_H
177 #include <sys/sysctl.h>
178 #endif
179 #endif
180
181 #ifndef HAVE_SETMNTENT
182 #define setmntent(f,m) fopen(f,m)
183 #endif
184 #ifndef HAVE_ENDMNTENT
185 #define endmntent(f) fclose(f)
186 #endif
187
188 static gboolean
189 is_in (const char *value, const char *set[])
190 {
191   int i;
192   for (i = 0; set[i] != NULL; i++)
193     {
194       if (strcmp (set[i], value) == 0)
195         return TRUE;
196     }
197   return FALSE;
198 }
199
200 /**
201  * g_unix_is_mount_path_system_internal:
202  * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr`
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 specialized
217      * directories that we want to hide from the user.
218      */
219     "/",              /* we already have "Filesystem root" in Nautilus */ 
220     "/bin",
221     "/boot",
222     "/compat/linux/proc",
223     "/compat/linux/sys",
224     "/dev",
225     "/etc",
226     "/home",
227     "/lib",
228     "/lib64",
229     "/libexec",
230     "/live/cow",
231     "/live/image",
232     "/media",
233     "/mnt",
234     "/opt",
235     "/rescue",
236     "/root",
237     "/sbin",
238     "/srv",
239     "/tmp",
240     "/usr",
241     "/usr/X11R6",
242     "/usr/local",
243     "/usr/obj",
244     "/usr/ports",
245     "/usr/src",
246     "/usr/xobj",
247     "/var",
248     "/var/crash",
249     "/var/local",
250     "/var/log",
251     "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
252     "/var/mail",
253     "/var/run",
254     "/var/tmp",       /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
255     "/proc",
256     "/sbin",
257     "/net",
258     "/sys",
259     NULL
260   };
261
262   if (is_in (mount_path, ignore_mountpoints))
263     return TRUE;
264   
265   if (g_str_has_prefix (mount_path, "/dev/") ||
266       g_str_has_prefix (mount_path, "/proc/") ||
267       g_str_has_prefix (mount_path, "/sys/"))
268     return TRUE;
269
270   if (g_str_has_suffix (mount_path, "/.gvfs"))
271     return TRUE;
272
273   return FALSE;
274 }
275
276 static gboolean
277 guess_system_internal (const char *mountpoint,
278                        const char *fs,
279                        const char *device)
280 {
281   const char *ignore_fs[] = {
282     "auto",
283     "autofs",
284     "devfs",
285     "devpts",
286     "ecryptfs",
287     "fdescfs",
288     "kernfs",
289     "linprocfs",
290     "mfs",
291     "nullfs",
292     "proc",
293     "procfs",
294     "ptyfs",
295     "rootfs",
296     "selinuxfs",
297     "sysfs",
298     "tmpfs",
299     "usbfs",
300     "nfsd",
301     "rpc_pipefs",
302     "zfs",
303     NULL
304   };
305   const char *ignore_devices[] = {
306     "none",
307     "sunrpc",
308     "devpts",
309     "nfsd",
310     "/dev/loop",
311     "/dev/vn",
312     NULL
313   };
314   
315   if (is_in (fs, ignore_fs))
316     return TRUE;
317   
318   if (is_in (device, ignore_devices))
319     return TRUE;
320
321   if (g_unix_is_mount_path_system_internal (mountpoint))
322     return TRUE;
323   
324   return FALSE;
325 }
326
327 /* GUnixMounts (ie: mtab) implementations {{{1 */
328
329 static GUnixMountEntry *
330 create_unix_mount_entry (const char *device_path,
331                          const char *mount_path,
332                          const char *filesystem_type,
333                          gboolean    is_read_only)
334 {
335   GUnixMountEntry *mount_entry = NULL;
336
337   mount_entry = g_new0 (GUnixMountEntry, 1);
338   mount_entry->device_path = g_strdup (device_path);
339   mount_entry->mount_path = g_strdup (mount_path);
340   mount_entry->filesystem_type = g_strdup (filesystem_type);
341   mount_entry->is_read_only = is_read_only;
342
343   mount_entry->is_system_internal =
344     guess_system_internal (mount_entry->mount_path,
345                            mount_entry->filesystem_type,
346                            mount_entry->device_path);
347   return mount_entry;
348 }
349
350 static GUnixMountPoint *
351 create_unix_mount_point (const char *device_path,
352                          const char *mount_path,
353                          const char *filesystem_type,
354                          const char *options,
355                          gboolean    is_read_only,
356                          gboolean    is_user_mountable,
357                          gboolean    is_loopback)
358 {
359   GUnixMountPoint *mount_point = NULL;
360
361   mount_point = g_new0 (GUnixMountPoint, 1);
362   mount_point->device_path = g_strdup (device_path);
363   mount_point->mount_path = g_strdup (mount_path);
364   mount_point->filesystem_type = g_strdup (filesystem_type);
365   mount_point->options = g_strdup (options);
366   mount_point->is_read_only = is_read_only;
367   mount_point->is_user_mountable = is_user_mountable;
368   mount_point->is_loopback = is_loopback;
369
370   return mount_point;
371 }
372
373 /* mntent.h (Linux, GNU, NSS) {{{2 */
374 #ifdef HAVE_MNTENT_H
375
376 #ifdef HAVE_LIBMOUNT
377
378 /* For documentation on /proc/self/mountinfo see
379  * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
380  */
381 #define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
382
383 static GList *
384 _g_get_unix_mounts (void)
385 {
386   struct libmnt_table *table = NULL;
387   struct libmnt_context *ctxt = NULL;
388   struct libmnt_iter* iter = NULL;
389   struct libmnt_fs *fs = NULL;
390   GUnixMountEntry *mount_entry = NULL;
391   GList *return_list = NULL;
392
393   ctxt = mnt_new_context ();
394   mnt_context_get_mtab (ctxt, &table);
395   if (!table)
396     goto out;
397
398   iter = mnt_new_iter (MNT_ITER_FORWARD);
399   while (mnt_table_next_fs (table, iter, &fs) == 0)
400     {
401       const char *device_path = NULL;
402       char *mount_options = NULL;
403       unsigned long mount_flags = 0;
404       gboolean is_read_only = FALSE;
405
406       if (!mnt_table_is_fs_mounted (table, fs))
407         continue;
408
409       device_path = mnt_fs_get_source (fs);
410       if (g_strcmp0 (device_path, "/dev/root") == 0)
411         device_path = _resolve_dev_root ();
412
413       mount_options = mnt_fs_strdup_options (fs);
414       if (mount_options)
415         {
416           mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
417           g_free (mount_options);
418         }
419       is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
420
421       mount_entry = create_unix_mount_entry (device_path,
422                                              mnt_fs_get_target (fs),
423                                              mnt_fs_get_fstype (fs),
424                                              is_read_only);
425
426       return_list = g_list_prepend (return_list, mount_entry);
427     }
428   mnt_free_iter (iter);
429
430  out:
431   mnt_free_context (ctxt);
432
433   return g_list_reverse (return_list);
434 }
435
436 #else
437
438 static char *
439 get_mtab_read_file (void)
440 {
441 #ifdef _PATH_MOUNTED
442 # ifdef __linux__
443   return "/proc/mounts";
444 # else
445   return _PATH_MOUNTED;
446 # endif
447 #else
448   return "/etc/mtab";
449 #endif
450 }
451
452 #ifndef HAVE_GETMNTENT_R
453 G_LOCK_DEFINE_STATIC(getmntent);
454 #endif
455
456 static GList *
457 _g_get_unix_mounts (void)
458 {
459 #ifdef HAVE_GETMNTENT_R
460   struct mntent ent;
461   char buf[1024];
462 #endif
463   struct mntent *mntent;
464   FILE *file;
465   char *read_file;
466   GUnixMountEntry *mount_entry;
467   GHashTable *mounts_hash;
468   GList *return_list;
469   
470   read_file = get_mtab_read_file ();
471
472   file = setmntent (read_file, "r");
473   if (file == NULL)
474     return NULL;
475
476   return_list = NULL;
477   
478   mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
479   
480 #ifdef HAVE_GETMNTENT_R
481   while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
482 #else
483   G_LOCK (getmntent);
484   while ((mntent = getmntent (file)) != NULL)
485 #endif
486     {
487       const char *device_path = NULL;
488       gboolean is_read_only = FALSE;
489
490       /* ignore any mnt_fsname that is repeated and begins with a '/'
491        *
492        * We do this to avoid being fooled by --bind mounts, since
493        * these have the same device as the location they bind to.
494        * It's not an ideal solution to the problem, but it's likely that
495        * the most important mountpoint is first and the --bind ones after
496        * that aren't as important. So it should work.
497        *
498        * The '/' is to handle procfs, tmpfs and other no device mounts.
499        */
500       if (mntent->mnt_fsname != NULL &&
501           mntent->mnt_fsname[0] == '/' &&
502           g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
503         continue;
504
505       if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
506         device_path = _resolve_dev_root ();
507       else
508         device_path = mntent->mnt_fsname;
509
510 #if defined (HAVE_HASMNTOPT)
511       if (hasmntopt (mntent, MNTOPT_RO) != NULL)
512         is_read_only = TRUE;
513 #endif
514
515       mount_entry = create_unix_mount_entry (device_path,
516                                              mntent->mnt_dir,
517                                              mntent->mnt_type,
518                                              is_read_only);
519
520       g_hash_table_insert (mounts_hash,
521                            mount_entry->device_path,
522                            mount_entry->device_path);
523
524       return_list = g_list_prepend (return_list, mount_entry);
525     }
526   g_hash_table_destroy (mounts_hash);
527   
528   endmntent (file);
529
530 #ifndef HAVE_GETMNTENT_R
531   G_UNLOCK (getmntent);
532 #endif
533   
534   return g_list_reverse (return_list);
535 }
536
537 #endif /* HAVE_LIBMOUNT */
538
539 static char *
540 get_mtab_monitor_file (void)
541 {
542   static char *mountinfo_path = NULL;
543 #ifdef HAVE_LIBMOUNT
544   struct stat buf;
545 #endif
546
547   if (mountinfo_path != NULL)
548     return mountinfo_path;
549
550 #ifdef HAVE_LIBMOUNT
551   /* If using libmount we'll have the logic in place to read mountinfo */
552   if (stat (PROC_MOUNTINFO_PATH, &buf) == 0)
553     {
554       mountinfo_path = PROC_MOUNTINFO_PATH;
555       return mountinfo_path;
556     }
557 #endif
558
559 #ifdef _PATH_MOUNTED
560 # ifdef __linux__
561   mountinfo_path = "/proc/mounts";
562 # else
563   mountinfo_path = _PATH_MOUNTED;
564 # endif
565 #else
566   mountinfo_path = "/etc/mtab";
567 #endif
568
569   return mountinfo_path;
570 }
571
572 /* mnttab.h {{{2 */
573 #elif defined (HAVE_SYS_MNTTAB_H)
574
575 G_LOCK_DEFINE_STATIC(getmntent);
576
577 static char *
578 get_mtab_read_file (void)
579 {
580 #ifdef _PATH_MOUNTED
581   return _PATH_MOUNTED;
582 #else   
583   return "/etc/mnttab";
584 #endif
585 }
586
587 static char *
588 get_mtab_monitor_file (void)
589 {
590   return get_mtab_read_file ();
591 }
592
593 static GList *
594 _g_get_unix_mounts (void)
595 {
596   struct mnttab mntent;
597   FILE *file;
598   char *read_file;
599   GUnixMountEntry *mount_entry;
600   GList *return_list;
601   
602   read_file = get_mtab_read_file ();
603   
604   file = setmntent (read_file, "r");
605   if (file == NULL)
606     return NULL;
607   
608   return_list = NULL;
609   
610   G_LOCK (getmntent);
611   while (! getmntent (file, &mntent))
612     {
613       gboolean is_read_only = FALSE;
614
615 #if defined (HAVE_HASMNTOPT)
616       if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
617         is_read_only = TRUE;
618 #endif
619
620       mount_entry = create_unix_mount_entry (mntent.mnt_special,
621                                              mntent.mnt_mountp,
622                                              mntent.mnt_fstype,
623                                              is_read_only);
624
625       return_list = g_list_prepend (return_list, mount_entry);
626     }
627   
628   endmntent (file);
629   
630   G_UNLOCK (getmntent);
631   
632   return g_list_reverse (return_list);
633 }
634
635 /* mntctl.h (AIX) {{{2 */
636 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
637
638 static char *
639 get_mtab_monitor_file (void)
640 {
641   return NULL;
642 }
643
644 static GList *
645 _g_get_unix_mounts (void)
646 {
647   struct vfs_ent *fs_info;
648   struct vmount *vmount_info;
649   int vmount_number;
650   unsigned int vmount_size;
651   int current;
652   GList *return_list;
653   
654   if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
655     {
656       g_warning ("Unable to know the number of mounted volumes\n");
657       
658       return NULL;
659     }
660
661   vmount_info = (struct vmount*)g_malloc (vmount_size);
662
663   vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
664   
665   if (vmount_info->vmt_revision != VMT_REVISION)
666     g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
667
668   if (vmount_number < 0)
669     {
670       g_warning ("Unable to recover mounted volumes information\n");
671       
672       g_free (vmount_info);
673       return NULL;
674     }
675   
676   return_list = NULL;
677   while (vmount_number > 0)
678     {
679       gboolean is_read_only = FALSE;
680
681       fs_info = getvfsbytype (vmount_info->vmt_gfstype);
682
683       /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
684       is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
685
686       mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
687                                              vmt2dataptr (vmount_info, VMT_STUB),
688                                              fs_info == NULL ? "unknown" : fs_info->vfsent_name,
689                                              is_read_only);
690
691       return_list = g_list_prepend (return_list, mount_entry);
692       
693       vmount_info = (struct vmount *)( (char*)vmount_info 
694                                        + vmount_info->vmt_length);
695       vmount_number--;
696     }
697   
698   g_free (vmount_info);
699   
700   return g_list_reverse (return_list);
701 }
702
703 /* sys/mount.h {{{2 */
704 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
705
706 static char *
707 get_mtab_monitor_file (void)
708 {
709   return NULL;
710 }
711
712 static GList *
713 _g_get_unix_mounts (void)
714 {
715 #if defined(USE_STATVFS)
716   struct statvfs *mntent = NULL;
717 #elif defined(USE_STATFS)
718   struct statfs *mntent = NULL;
719 #else
720   #error statfs juggling failed
721 #endif
722   size_t bufsize;
723   int num_mounts, i;
724   GUnixMountEntry *mount_entry;
725   GList *return_list;
726   
727   /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
728 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
729   num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
730 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
731   num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
732 #endif
733   if (num_mounts == -1)
734     return NULL;
735
736   bufsize = num_mounts * sizeof (*mntent);
737   mntent = g_malloc (bufsize);
738 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
739   num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
740 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
741   num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
742 #endif
743   if (num_mounts == -1)
744     return NULL;
745   
746   return_list = NULL;
747   
748   for (i = 0; i < num_mounts; i++)
749     {
750       gboolean is_read_only = FALSE;
751
752 #if defined(USE_STATVFS)
753       if (mntent[i].f_flag & ST_RDONLY)
754 #elif defined(USE_STATFS)
755       if (mntent[i].f_flags & MNT_RDONLY)
756 #else
757       #error statfs juggling failed
758 #endif
759         is_read_only = TRUE;
760
761       mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
762                                              mntent[i].f_mntonname,
763                                              mntent[i].f_fstypename,
764                                              is_read_only);
765
766       return_list = g_list_prepend (return_list, mount_entry);
767     }
768
769   g_free (mntent);
770   
771   return g_list_reverse (return_list);
772 }
773
774 /* Interix {{{2 */
775 #elif defined(__INTERIX)
776
777 static char *
778 get_mtab_monitor_file (void)
779 {
780   return NULL;
781 }
782
783 static GList *
784 _g_get_unix_mounts (void)
785 {
786   DIR *dirp;
787   GList* return_list = NULL;
788   char filename[9 + NAME_MAX];
789
790   dirp = opendir ("/dev/fs");
791   if (!dirp)
792     {
793       g_warning ("unable to read /dev/fs!");
794       return NULL;
795     }
796
797   while (1)
798     {
799       struct statvfs statbuf;
800       struct dirent entry;
801       struct dirent* result;
802       
803       if (readdir_r (dirp, &entry, &result) || result == NULL)
804         break;
805       
806       strcpy (filename, "/dev/fs/");
807       strcat (filename, entry.d_name);
808       
809       if (statvfs (filename, &statbuf) == 0)
810         {
811           GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
812           
813           mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
814           mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
815           mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
816           
817           if (statbuf.f_flag & ST_RDONLY)
818             mount_entry->is_read_only = TRUE;
819           
820           return_list = g_list_prepend(return_list, mount_entry);
821         }
822     }
823   
824   return_list = g_list_reverse (return_list);
825   
826   closedir (dirp);
827
828   return return_list;
829 }
830
831 /* Common code {{{2 */
832 #else
833 #error No _g_get_unix_mounts() implementation for system
834 #endif
835
836 /* GUnixMountPoints (ie: fstab) implementations {{{1 */
837
838 /* _g_get_unix_mount_points():
839  * read the fstab.
840  * don't return swap and ignore mounts.
841  */
842
843 static char *
844 get_fstab_file (void)
845 {
846 #ifdef HAVE_LIBMOUNT
847   return (char *) mnt_get_fstab_path ();
848 #else
849 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
850   /* AIX */
851   return "/etc/filesystems";
852 #elif defined(_PATH_MNTTAB)
853   return _PATH_MNTTAB;
854 #elif defined(VFSTAB)
855   return VFSTAB;
856 #else
857   return "/etc/fstab";
858 #endif
859 #endif
860 }
861
862 /* mntent.h (Linux, GNU, NSS) {{{2 */
863 #ifdef HAVE_MNTENT_H
864
865 #ifdef HAVE_LIBMOUNT
866
867 static GList *
868 _g_get_unix_mount_points (void)
869 {
870   struct libmnt_table *table = NULL;
871   struct libmnt_context *ctxt = NULL;
872   struct libmnt_iter* iter = NULL;
873   struct libmnt_fs *fs = NULL;
874   GUnixMountPoint *mount_point = NULL;
875   GList *return_list = NULL;
876
877   ctxt = mnt_new_context ();
878   mnt_context_get_fstab (ctxt, &table);
879   if (!table)
880     goto out;
881
882   iter = mnt_new_iter (MNT_ITER_FORWARD);
883   while (mnt_table_next_fs (table, iter, &fs) == 0)
884     {
885       const char *device_path = NULL;
886       const char *mount_path = NULL;
887       const char *mount_fstype = NULL;
888       char *mount_options = NULL;
889       gboolean is_read_only = FALSE;
890       gboolean is_user_mountable = FALSE;
891       gboolean is_loopback = FALSE;
892
893       mount_path = mnt_fs_get_target (fs);
894       if ((strcmp (mount_path, "ignore") == 0) ||
895           (strcmp (mount_path, "swap") == 0) ||
896           (strcmp (mount_path, "none") == 0))
897         continue;
898
899       mount_fstype = mnt_fs_get_fstype (fs);
900       mount_options = mnt_fs_strdup_options (fs);
901       if (mount_options)
902         {
903           unsigned long mount_flags = 0;
904           unsigned long userspace_flags = 0;
905
906           mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
907           mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
908
909           /* We ignore bind fstab entries, as we ignore bind mounts anyway */
910           if (mount_flags & MS_BIND)
911             {
912               g_free (mount_options);
913               continue;
914             }
915
916           is_read_only = (mount_flags & MS_RDONLY) != 0;
917           is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
918
919           if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
920               ((userspace_flags & MNT_MS_USER) &&
921                (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
922               (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
923               (userspace_flags & MNT_MS_USERS) ||
924               (userspace_flags & MNT_MS_OWNER))
925             {
926               is_user_mountable = TRUE;
927             }
928         }
929
930       device_path = mnt_fs_get_source (fs);
931       if (g_strcmp0 (device_path, "/dev/root") == 0)
932         device_path = _resolve_dev_root ();
933
934       mount_point = create_unix_mount_point (device_path,
935                                              mount_path,
936                                              mount_fstype,
937                                              mount_options,
938                                              is_read_only,
939                                              is_user_mountable,
940                                              is_loopback);
941       if (mount_options)
942         g_free (mount_options);
943
944       return_list = g_list_prepend (return_list, mount_point);
945     }
946   mnt_free_iter (iter);
947
948  out:
949   mnt_free_context (ctxt);
950
951   return g_list_reverse (return_list);
952 }
953
954 #else
955
956 static GList *
957 _g_get_unix_mount_points (void)
958 {
959 #ifdef HAVE_GETMNTENT_R
960   struct mntent ent;
961   char buf[1024];
962 #endif
963   struct mntent *mntent;
964   FILE *file;
965   char *read_file;
966   GUnixMountPoint *mount_point;
967   GList *return_list;
968   
969   read_file = get_fstab_file ();
970   
971   file = setmntent (read_file, "r");
972   if (file == NULL)
973     return NULL;
974
975   return_list = NULL;
976   
977 #ifdef HAVE_GETMNTENT_R
978   while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
979 #else
980   G_LOCK (getmntent);
981   while ((mntent = getmntent (file)) != NULL)
982 #endif
983     {
984       const char *device_path = NULL;
985       gboolean is_read_only = FALSE;
986       gboolean is_user_mountable = FALSE;
987       gboolean is_loopback = FALSE;
988
989       if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
990           (strcmp (mntent->mnt_dir, "swap") == 0) ||
991           (strcmp (mntent->mnt_dir, "none") == 0))
992         continue;
993
994 #ifdef HAVE_HASMNTOPT
995       /* We ignore bind fstab entries, as we ignore bind mounts anyway */
996       if (hasmntopt (mntent, "bind"))
997         continue;
998 #endif
999
1000       if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1001         device_path = _resolve_dev_root ();
1002       else
1003         device_path = mntent->mnt_fsname;
1004
1005 #ifdef HAVE_HASMNTOPT
1006       if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1007         is_read_only = TRUE;
1008
1009       if (hasmntopt (mntent, "loop") != NULL)
1010         is_loopback = TRUE;
1011
1012 #endif
1013
1014       if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1015 #ifdef HAVE_HASMNTOPT
1016           || (hasmntopt (mntent, "user") != NULL
1017               && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1018           || hasmntopt (mntent, "pamconsole") != NULL
1019           || hasmntopt (mntent, "users") != NULL
1020           || hasmntopt (mntent, "owner") != NULL
1021 #endif
1022           )
1023         is_user_mountable = TRUE;
1024
1025       mount_point = create_unix_mount_point (device_path,
1026                                              mntent->mnt_dir,
1027                                              mntent->mnt_type,
1028                                              mntent->mnt_opts,
1029                                              is_read_only,
1030                                              is_user_mountable,
1031                                              is_loopback);
1032
1033       return_list = g_list_prepend (return_list, mount_point);
1034     }
1035   
1036   endmntent (file);
1037
1038 #ifndef HAVE_GETMNTENT_R
1039   G_UNLOCK (getmntent);
1040 #endif
1041   
1042   return g_list_reverse (return_list);
1043 }
1044
1045 #endif /* HAVE_LIBMOUNT */
1046
1047 /* mnttab.h {{{2 */
1048 #elif defined (HAVE_SYS_MNTTAB_H)
1049
1050 static GList *
1051 _g_get_unix_mount_points (void)
1052 {
1053   struct mnttab mntent;
1054   FILE *file;
1055   char *read_file;
1056   GUnixMountPoint *mount_point;
1057   GList *return_list;
1058   
1059   read_file = get_fstab_file ();
1060   
1061   file = setmntent (read_file, "r");
1062   if (file == NULL)
1063     return NULL;
1064
1065   return_list = NULL;
1066   
1067   G_LOCK (getmntent);
1068   while (! getmntent (file, &mntent))
1069     {
1070       gboolean is_read_only = FALSE;
1071       gboolean is_user_mountable = FALSE;
1072       gboolean is_loopback = FALSE;
1073
1074       if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1075           (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1076           (strcmp (mntent.mnt_mountp, "none") == 0))
1077         continue;
1078
1079 #ifdef HAVE_HASMNTOPT
1080       if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1081         is_read_only = TRUE;
1082
1083       if (hasmntopt (&mntent, "lofs") != NULL)
1084         is_loopback = TRUE;
1085 #endif
1086
1087       if ((mntent.mnt_fstype != NULL)
1088 #ifdef HAVE_HASMNTOPT
1089           || (hasmntopt (&mntent, "user") != NULL
1090               && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1091           || hasmntopt (&mntent, "pamconsole") != NULL
1092           || hasmntopt (&mntent, "users") != NULL
1093           || hasmntopt (&mntent, "owner") != NULL
1094 #endif
1095           )
1096         is_user_mountable = TRUE;
1097
1098       mount_point = create_unix_mount_point (mntent.mnt_special,
1099                                              mntent.mnt_mountp,
1100                                              mntent.mnt_fstype,
1101                                              mntent.mnt_mntopts,
1102                                              is_read_only,
1103                                              is_user_mountable,
1104                                              is_loopback);
1105
1106       return_list = g_list_prepend (return_list, mount_point);
1107     }
1108   
1109   endmntent (file);
1110   G_UNLOCK (getmntent);
1111   
1112   return g_list_reverse (return_list);
1113 }
1114
1115 /* mntctl.h (AIX) {{{2 */
1116 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1117
1118 /* functions to parse /etc/filesystems on aix */
1119
1120 /* read character, ignoring comments (begin with '*', end with '\n' */
1121 static int
1122 aix_fs_getc (FILE *fd)
1123 {
1124   int c;
1125   
1126   while ((c = getc (fd)) == '*')
1127     {
1128       while (((c = getc (fd)) != '\n') && (c != EOF))
1129         ;
1130     }
1131 }
1132
1133 /* eat all continuous spaces in a file */
1134 static int
1135 aix_fs_ignorespace (FILE *fd)
1136 {
1137   int c;
1138   
1139   while ((c = aix_fs_getc (fd)) != EOF)
1140     {
1141       if (!g_ascii_isspace (c))
1142         {
1143           ungetc (c,fd);
1144           return c;
1145         }
1146     }
1147   
1148   return EOF;
1149 }
1150
1151 /* read one word from file */
1152 static int
1153 aix_fs_getword (FILE *fd, 
1154                 char *word)
1155 {
1156   int c;
1157   
1158   aix_fs_ignorespace (fd);
1159
1160   while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1161     {
1162       if (c == '"')
1163         {
1164           while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1165             *word++ = c;
1166           else
1167             *word++ = c;
1168         }
1169     }
1170   *word = 0;
1171   
1172   return c;
1173 }
1174
1175 typedef struct {
1176   char mnt_mount[PATH_MAX];
1177   char mnt_special[PATH_MAX];
1178   char mnt_fstype[16];
1179   char mnt_options[128];
1180 } AixMountTableEntry;
1181
1182 /* read mount points properties */
1183 static int
1184 aix_fs_get (FILE               *fd, 
1185             AixMountTableEntry *prop)
1186 {
1187   static char word[PATH_MAX] = { 0 };
1188   char value[PATH_MAX];
1189   
1190   /* read stanza */
1191   if (word[0] == 0)
1192     {
1193       if (aix_fs_getword (fd, word) == EOF)
1194         return EOF;
1195     }
1196
1197   word[strlen(word) - 1] = 0;
1198   strcpy (prop->mnt_mount, word);
1199   
1200   /* read attributes and value */
1201   
1202   while (aix_fs_getword (fd, word) != EOF)
1203     {
1204       /* test if is attribute or new stanza */
1205       if (word[strlen(word) - 1] == ':')
1206         return 0;
1207       
1208       /* read "=" */
1209       aix_fs_getword (fd, value);
1210       
1211       /* read value */
1212       aix_fs_getword (fd, value);
1213       
1214       if (strcmp (word, "dev") == 0)
1215         strcpy (prop->mnt_special, value);
1216       else if (strcmp (word, "vfs") == 0)
1217         strcpy (prop->mnt_fstype, value);
1218       else if (strcmp (word, "options") == 0)
1219         strcpy(prop->mnt_options, value);
1220     }
1221   
1222   return 0;
1223 }
1224
1225 static GList *
1226 _g_get_unix_mount_points (void)
1227 {
1228   struct mntent *mntent;
1229   FILE *file;
1230   char *read_file;
1231   GUnixMountPoint *mount_point;
1232   AixMountTableEntry mntent;
1233   GList *return_list;
1234   
1235   read_file = get_fstab_file ();
1236   
1237   file = setmntent (read_file, "r");
1238   if (file == NULL)
1239     return NULL;
1240   
1241   return_list = NULL;
1242   
1243   while (!aix_fs_get (file, &mntent))
1244     {
1245       if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1246         {
1247           mount_point = create_unix_mount_point (mntent.mnt_special,
1248                                                  mntent.mnt_mount,
1249                                                  mntent.mnt_fstype,
1250                                                  mntent.mnt_options,
1251                                                  TRUE,
1252                                                  TRUE,
1253                                                  FALSE);
1254
1255           return_list = g_list_prepend (return_list, mount_point);
1256         }
1257     }
1258         
1259   endmntent (file);
1260   
1261   return g_list_reverse (return_list);
1262 }
1263
1264 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1265
1266 static GList *
1267 _g_get_unix_mount_points (void)
1268 {
1269   struct fstab *fstab = NULL;
1270   GUnixMountPoint *mount_point;
1271   GList *return_list;
1272 #ifdef HAVE_SYS_SYSCTL_H
1273   int usermnt = 0;
1274   size_t len = sizeof(usermnt);
1275   struct stat sb;
1276 #endif
1277   
1278   if (!setfsent ())
1279     return NULL;
1280
1281   return_list = NULL;
1282   
1283 #ifdef HAVE_SYS_SYSCTL_H
1284 #if defined(HAVE_SYSCTLBYNAME)
1285   sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1286 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1287   {
1288     int mib[2];
1289     
1290     mib[0] = CTL_VFS;
1291     mib[1] = VFS_USERMOUNT;
1292     sysctl (mib, 2, &usermnt, &len, NULL, 0);
1293   }
1294 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1295   {
1296     int mib[2];
1297     
1298     mib[0] = CTL_KERN;
1299     mib[1] = KERN_USERMOUNT;
1300     sysctl (mib, 2, &usermnt, &len, NULL, 0);
1301   }
1302 #endif
1303 #endif
1304   
1305   while ((fstab = getfsent ()) != NULL)
1306     {
1307       gboolean is_read_only = FALSE;
1308       gboolean is_user_mountable = FALSE;
1309
1310       if (strcmp (fstab->fs_vfstype, "swap") == 0)
1311         continue;
1312
1313       if (strcmp (fstab->fs_type, "ro") == 0)
1314         is_read_only = TRUE;
1315
1316 #ifdef HAVE_SYS_SYSCTL_H
1317       if (usermnt != 0)
1318         {
1319           uid_t uid = getuid ();
1320           if (stat (fstab->fs_file, &sb) == 0)
1321             {
1322               if (uid == 0 || sb.st_uid == uid)
1323                 is_user_mountable = TRUE;
1324             }
1325         }
1326 #endif
1327
1328       mount_point = create_unix_mount_point (fstab->fs_spec,
1329                                              fstab->fs_file,
1330                                              fstab->fs_vfstype,
1331                                              fstab->fs_mntops,
1332                                              is_read_only,
1333                                              is_user_mountable,
1334                                              FALSE);
1335
1336       return_list = g_list_prepend (return_list, mount_point);
1337     }
1338   
1339   endfsent ();
1340   
1341   return g_list_reverse (return_list);
1342 }
1343 /* Interix {{{2 */
1344 #elif defined(__INTERIX)
1345 static GList *
1346 _g_get_unix_mount_points (void)
1347 {
1348   return _g_get_unix_mounts ();
1349 }
1350
1351 /* Common code {{{2 */
1352 #else
1353 #error No g_get_mount_table() implementation for system
1354 #endif
1355
1356 static guint64
1357 get_mounts_timestamp (void)
1358 {
1359   const char *monitor_file;
1360   struct stat buf;
1361
1362   monitor_file = get_mtab_monitor_file ();
1363   if (monitor_file)
1364     {
1365       if (stat (monitor_file, &buf) == 0)
1366         return (guint64)buf.st_mtime;
1367     }
1368   else
1369     {
1370       return mount_poller_time;
1371     }
1372   return 0;
1373 }
1374
1375 static guint64
1376 get_mount_points_timestamp (void)
1377 {
1378   const char *monitor_file;
1379   struct stat buf;
1380
1381   monitor_file = get_fstab_file ();
1382   if (monitor_file)
1383     {
1384       if (stat (monitor_file, &buf) == 0)
1385         return (guint64)buf.st_mtime;
1386     }
1387   return 0;
1388 }
1389
1390 /**
1391  * g_unix_mounts_get: (skip)
1392  * @time_read: (out) (allow-none): guint64 to contain a timestamp, or %NULL
1393  *
1394  * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1395  * If @time_read is set, it will be filled with the mount
1396  * timestamp, allowing for checking if the mounts have changed
1397  * with g_unix_mounts_changed_since().
1398  *
1399  * Returns: (element-type GUnixMountEntry) (transfer full):
1400  *     a #GList of the UNIX mounts.
1401  **/
1402 GList *
1403 g_unix_mounts_get (guint64 *time_read)
1404 {
1405   if (time_read)
1406     *time_read = get_mounts_timestamp ();
1407
1408   return _g_get_unix_mounts ();
1409 }
1410
1411 /**
1412  * g_unix_mount_at: (skip)
1413  * @mount_path: path for a possible unix mount.
1414  * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1415  * 
1416  * Gets a #GUnixMountEntry for a given mount path. If @time_read
1417  * is set, it will be filled with a unix timestamp for checking
1418  * if the mounts have changed since with g_unix_mounts_changed_since().
1419  * 
1420  * Returns: (transfer full): a #GUnixMountEntry.
1421  **/
1422 GUnixMountEntry *
1423 g_unix_mount_at (const char *mount_path,
1424                  guint64    *time_read)
1425 {
1426   GList *mounts, *l;
1427   GUnixMountEntry *mount_entry, *found;
1428   
1429   mounts = g_unix_mounts_get (time_read);
1430
1431   found = NULL;
1432   for (l = mounts; l != NULL; l = l->next)
1433     {
1434       mount_entry = l->data;
1435
1436       if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1437         found = mount_entry;
1438       else
1439         g_unix_mount_free (mount_entry);
1440     }
1441   g_list_free (mounts);
1442
1443   return found;
1444 }
1445
1446 /**
1447  * g_unix_mount_points_get: (skip)
1448  * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1449  *
1450  * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1451  * If @time_read is set, it will be filled with the mount timestamp,
1452  * allowing for checking if the mounts have changed with
1453  * g_unix_mount_points_changed_since().
1454  *
1455  * Returns: (element-type GUnixMountPoint) (transfer full):
1456  *     a #GList of the UNIX mountpoints.
1457  **/
1458 GList *
1459 g_unix_mount_points_get (guint64 *time_read)
1460 {
1461   if (time_read)
1462     *time_read = get_mount_points_timestamp ();
1463
1464   return _g_get_unix_mount_points ();
1465 }
1466
1467 /**
1468  * g_unix_mounts_changed_since:
1469  * @time: guint64 to contain a timestamp.
1470  * 
1471  * Checks if the unix mounts have changed since a given unix time.
1472  * 
1473  * Returns: %TRUE if the mounts have changed since @time. 
1474  **/
1475 gboolean
1476 g_unix_mounts_changed_since (guint64 time)
1477 {
1478   return get_mounts_timestamp () != time;
1479 }
1480
1481 /**
1482  * g_unix_mount_points_changed_since:
1483  * @time: guint64 to contain a timestamp.
1484  * 
1485  * Checks if the unix mount points have changed since a given unix time.
1486  * 
1487  * Returns: %TRUE if the mount points have changed since @time. 
1488  **/
1489 gboolean
1490 g_unix_mount_points_changed_since (guint64 time)
1491 {
1492   return get_mount_points_timestamp () != time;
1493 }
1494
1495 /* GUnixMountMonitor {{{1 */
1496
1497 enum {
1498   MOUNTS_CHANGED,
1499   MOUNTPOINTS_CHANGED,
1500   LAST_SIGNAL
1501 };
1502
1503 static guint signals[LAST_SIGNAL];
1504
1505 struct _GUnixMountMonitor {
1506   GObject parent;
1507
1508   GMainContext *context;
1509 };
1510
1511 struct _GUnixMountMonitorClass {
1512   GObjectClass parent_class;
1513 };
1514
1515
1516 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
1517
1518 static GContextSpecificGroup  mount_monitor_group;
1519 static GFileMonitor          *fstab_monitor;
1520 static GFileMonitor          *mtab_monitor;
1521 static GSource               *proc_mounts_watch_source;
1522 static GList                 *mount_poller_mounts;
1523
1524 static void
1525 fstab_file_changed (GFileMonitor      *monitor,
1526                     GFile             *file,
1527                     GFile             *other_file,
1528                     GFileMonitorEvent  event_type,
1529                     gpointer           user_data)
1530 {
1531   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1532       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1533       event_type != G_FILE_MONITOR_EVENT_DELETED)
1534     return;
1535
1536   g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1537 }
1538
1539 static void
1540 mtab_file_changed (GFileMonitor      *monitor,
1541                    GFile             *file,
1542                    GFile             *other_file,
1543                    GFileMonitorEvent  event_type,
1544                    gpointer           user_data)
1545 {
1546   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1547       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1548       event_type != G_FILE_MONITOR_EVENT_DELETED)
1549     return;
1550
1551   g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1552 }
1553
1554 static gboolean
1555 proc_mounts_changed (GIOChannel   *channel,
1556                      GIOCondition  cond,
1557                      gpointer      user_data)
1558 {
1559   if (cond & G_IO_ERR)
1560     g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1561
1562   return TRUE;
1563 }
1564
1565 static gboolean
1566 mount_change_poller (gpointer user_data)
1567 {
1568   GList *current_mounts, *new_it, *old_it;
1569   gboolean has_changed = FALSE;
1570
1571   current_mounts = _g_get_unix_mounts ();
1572
1573   for ( new_it = current_mounts, old_it = mount_poller_mounts;
1574         new_it != NULL && old_it != NULL;
1575         new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1576     {
1577       if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1578         {
1579           has_changed = TRUE;
1580           break;
1581         }
1582     }
1583   if (!(new_it == NULL && old_it == NULL))
1584     has_changed = TRUE;
1585
1586   g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1587
1588   mount_poller_mounts = current_mounts;
1589
1590   if (has_changed)
1591     {
1592       mount_poller_time = (guint64) g_get_monotonic_time ();
1593       g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1594     }
1595
1596   return TRUE;
1597 }
1598
1599
1600 static void
1601 mount_monitor_stop (void)
1602 {
1603   if (fstab_monitor)
1604     {
1605       g_file_monitor_cancel (fstab_monitor);
1606       g_object_unref (fstab_monitor);
1607     }
1608
1609   if (proc_mounts_watch_source != NULL)
1610     g_source_destroy (proc_mounts_watch_source);
1611
1612   if (mtab_monitor)
1613     {
1614       g_file_monitor_cancel (mtab_monitor);
1615       g_object_unref (mtab_monitor);
1616     }
1617
1618   g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1619 }
1620
1621 static void
1622 mount_monitor_start (void)
1623 {
1624   GFile *file;
1625
1626   if (get_fstab_file () != NULL)
1627     {
1628       file = g_file_new_for_path (get_fstab_file ());
1629       fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1630       g_object_unref (file);
1631
1632       g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1633     }
1634
1635   if (get_mtab_monitor_file () != NULL)
1636     {
1637       const gchar *mtab_path;
1638
1639       mtab_path = get_mtab_monitor_file ();
1640       /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1641        * See 'man proc' for more details.
1642        */
1643       if (g_str_has_prefix (mtab_path, "/proc/"))
1644         {
1645           GIOChannel *proc_mounts_channel;
1646           GError *error = NULL;
1647           proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
1648           if (proc_mounts_channel == NULL)
1649             {
1650               g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1651                          error->message, g_quark_to_string (error->domain), error->code);
1652               g_error_free (error);
1653             }
1654           else
1655             {
1656               proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1657               g_source_set_callback (proc_mounts_watch_source,
1658                                      (GSourceFunc) proc_mounts_changed,
1659                                      NULL, NULL);
1660               g_source_attach (proc_mounts_watch_source,
1661                                g_main_context_get_thread_default ());
1662               g_source_unref (proc_mounts_watch_source);
1663               g_io_channel_unref (proc_mounts_channel);
1664             }
1665         }
1666       else
1667         {
1668           file = g_file_new_for_path (mtab_path);
1669           mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1670           g_object_unref (file);
1671           g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
1672         }
1673     }
1674   else
1675     {
1676       proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1677       mount_poller_mounts = _g_get_unix_mounts ();
1678       mount_poller_time = (guint64)g_get_monotonic_time ();
1679       g_source_set_callback (proc_mounts_watch_source,
1680                              mount_change_poller,
1681                              NULL, NULL);
1682       g_source_attach (proc_mounts_watch_source,
1683                        g_main_context_get_thread_default ());
1684       g_source_unref (proc_mounts_watch_source);
1685     }
1686 }
1687
1688 static void
1689 g_unix_mount_monitor_finalize (GObject *object)
1690 {
1691   GUnixMountMonitor *monitor;
1692
1693   monitor = G_UNIX_MOUNT_MONITOR (object);
1694
1695   g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
1696
1697   G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1698 }
1699
1700 static void
1701 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1702 {
1703   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1704
1705   gobject_class->finalize = g_unix_mount_monitor_finalize;
1706  
1707   /**
1708    * GUnixMountMonitor::mounts-changed:
1709    * @monitor: the object on which the signal is emitted
1710    * 
1711    * Emitted when the unix mounts have changed.
1712    */ 
1713   signals[MOUNTS_CHANGED] =
1714     g_signal_new (I_("mounts-changed"),
1715                   G_TYPE_FROM_CLASS (klass),
1716                   G_SIGNAL_RUN_LAST,
1717                   0,
1718                   NULL, NULL,
1719                   g_cclosure_marshal_VOID__VOID,
1720                   G_TYPE_NONE, 0);
1721
1722   /**
1723    * GUnixMountMonitor::mountpoints-changed:
1724    * @monitor: the object on which the signal is emitted
1725    * 
1726    * Emitted when the unix mount points have changed.
1727    */
1728   signals[MOUNTPOINTS_CHANGED] =
1729     g_signal_new (I_("mountpoints-changed"),
1730                   G_TYPE_FROM_CLASS (klass),
1731                   G_SIGNAL_RUN_LAST,
1732                   0,
1733                   NULL, NULL,
1734                   g_cclosure_marshal_VOID__VOID,
1735                   G_TYPE_NONE, 0);
1736 }
1737
1738 static void
1739 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1740 {
1741 }
1742
1743 /**
1744  * g_unix_mount_monitor_set_rate_limit:
1745  * @mount_monitor: a #GUnixMountMonitor
1746  * @limit_msec: a integer with the limit in milliseconds to
1747  *     poll for changes.
1748  *
1749  * This function does nothing.
1750  *
1751  * Before 2.44, this was a partially-effective way of controlling the
1752  * rate at which events would be reported under some uncommon
1753  * circumstances.  Since @mount_monitor is a singleton, it also meant
1754  * that calling this function would have side effects for other users of
1755  * the monitor.
1756  *
1757  * Since: 2.18
1758  *
1759  * Deprecated:2.44:This function does nothing.  Don't call it.
1760  */
1761 void
1762 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1763                                      gint               limit_msec)
1764 {
1765 }
1766
1767 /**
1768  * g_unix_mount_monitor_get:
1769  *
1770  * Gets the #GUnixMountMonitor for the current thread-default main
1771  * context.
1772  *
1773  * The mount monitor can be used to monitor for changes to the list of
1774  * mounted filesystems as well as the list of mount points (ie: fstab
1775  * entries).
1776  *
1777  * You must only call g_object_unref() on the return value from under
1778  * the same main context as you called this function.
1779  *
1780  * Returns: (transfer full): the #GUnixMountMonitor.
1781  *
1782  * Since: 2.44
1783  **/
1784 GUnixMountMonitor *
1785 g_unix_mount_monitor_get (void)
1786 {
1787   return g_context_specific_group_get (&mount_monitor_group,
1788                                        G_TYPE_UNIX_MOUNT_MONITOR,
1789                                        G_STRUCT_OFFSET(GUnixMountMonitor, context),
1790                                        mount_monitor_start);
1791 }
1792
1793 /**
1794  * g_unix_mount_monitor_new:
1795  *
1796  * Deprecated alias for g_unix_mount_monitor_get().
1797  *
1798  * This function was never a true constructor, which is why it was
1799  * renamed.
1800  *
1801  * Returns: a #GUnixMountMonitor.
1802  *
1803  * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
1804  */
1805 GUnixMountMonitor *
1806 g_unix_mount_monitor_new (void)
1807 {
1808   return g_unix_mount_monitor_get ();
1809 }
1810
1811 /* GUnixMount {{{1 */
1812 /**
1813  * g_unix_mount_free:
1814  * @mount_entry: a #GUnixMountEntry.
1815  * 
1816  * Frees a unix mount.
1817  */
1818 void
1819 g_unix_mount_free (GUnixMountEntry *mount_entry)
1820 {
1821   g_return_if_fail (mount_entry != NULL);
1822
1823   g_free (mount_entry->mount_path);
1824   g_free (mount_entry->device_path);
1825   g_free (mount_entry->filesystem_type);
1826   g_free (mount_entry);
1827 }
1828
1829 /**
1830  * g_unix_mount_point_free:
1831  * @mount_point: unix mount point to free.
1832  * 
1833  * Frees a unix mount point.
1834  */
1835 void
1836 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1837 {
1838   g_return_if_fail (mount_point != NULL);
1839
1840   g_free (mount_point->mount_path);
1841   g_free (mount_point->device_path);
1842   g_free (mount_point->filesystem_type);
1843   g_free (mount_point->options);
1844   g_free (mount_point);
1845 }
1846
1847 /**
1848  * g_unix_mount_compare:
1849  * @mount1: first #GUnixMountEntry to compare.
1850  * @mount2: second #GUnixMountEntry to compare.
1851  * 
1852  * Compares two unix mounts.
1853  * 
1854  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1855  * or less than @mount2, respectively. 
1856  */
1857 gint
1858 g_unix_mount_compare (GUnixMountEntry *mount1,
1859                       GUnixMountEntry *mount2)
1860 {
1861   int res;
1862
1863   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1864   
1865   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1866   if (res != 0)
1867     return res;
1868         
1869   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1870   if (res != 0)
1871     return res;
1872         
1873   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1874   if (res != 0)
1875     return res;
1876
1877   res =  mount1->is_read_only - mount2->is_read_only;
1878   if (res != 0)
1879     return res;
1880   
1881   return 0;
1882 }
1883
1884 /**
1885  * g_unix_mount_get_mount_path:
1886  * @mount_entry: input #GUnixMountEntry to get the mount path for.
1887  * 
1888  * Gets the mount path for a unix mount.
1889  * 
1890  * Returns: (type filename): the mount path for @mount_entry.
1891  */
1892 const gchar *
1893 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1894 {
1895   g_return_val_if_fail (mount_entry != NULL, NULL);
1896
1897   return mount_entry->mount_path;
1898 }
1899
1900 /**
1901  * g_unix_mount_get_device_path:
1902  * @mount_entry: a #GUnixMount.
1903  * 
1904  * Gets the device path for a unix mount.
1905  * 
1906  * Returns: (type filename): a string containing the device path.
1907  */
1908 const gchar *
1909 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1910 {
1911   g_return_val_if_fail (mount_entry != NULL, NULL);
1912
1913   return mount_entry->device_path;
1914 }
1915
1916 /**
1917  * g_unix_mount_get_fs_type:
1918  * @mount_entry: a #GUnixMount.
1919  * 
1920  * Gets the filesystem type for the unix mount.
1921  * 
1922  * Returns: a string containing the file system type.
1923  */
1924 const gchar *
1925 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1926 {
1927   g_return_val_if_fail (mount_entry != NULL, NULL);
1928
1929   return mount_entry->filesystem_type;
1930 }
1931
1932 /**
1933  * g_unix_mount_is_readonly:
1934  * @mount_entry: a #GUnixMount.
1935  * 
1936  * Checks if a unix mount is mounted read only.
1937  * 
1938  * Returns: %TRUE if @mount_entry is read only.
1939  */
1940 gboolean
1941 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1942 {
1943   g_return_val_if_fail (mount_entry != NULL, FALSE);
1944
1945   return mount_entry->is_read_only;
1946 }
1947
1948 /**
1949  * g_unix_mount_is_system_internal:
1950  * @mount_entry: a #GUnixMount.
1951  * 
1952  * Checks if a unix mount is a system path.
1953  * 
1954  * Returns: %TRUE if the unix mount is for a system path.
1955  */
1956 gboolean
1957 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1958 {
1959   g_return_val_if_fail (mount_entry != NULL, FALSE);
1960
1961   return mount_entry->is_system_internal;
1962 }
1963
1964 /* GUnixMountPoint {{{1 */
1965 /**
1966  * g_unix_mount_point_compare:
1967  * @mount1: a #GUnixMount.
1968  * @mount2: a #GUnixMount.
1969  * 
1970  * Compares two unix mount points.
1971  * 
1972  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1973  * or less than @mount2, respectively.
1974  */
1975 gint
1976 g_unix_mount_point_compare (GUnixMountPoint *mount1,
1977                             GUnixMountPoint *mount2)
1978 {
1979   int res;
1980
1981   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1982
1983   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1984   if (res != 0) 
1985     return res;
1986         
1987   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1988   if (res != 0) 
1989     return res;
1990         
1991   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1992   if (res != 0) 
1993     return res;
1994
1995   res = g_strcmp0 (mount1->options, mount2->options);
1996   if (res != 0) 
1997     return res;
1998
1999   res =  mount1->is_read_only - mount2->is_read_only;
2000   if (res != 0) 
2001     return res;
2002
2003   res = mount1->is_user_mountable - mount2->is_user_mountable;
2004   if (res != 0) 
2005     return res;
2006
2007   res = mount1->is_loopback - mount2->is_loopback;
2008   if (res != 0)
2009     return res;
2010   
2011   return 0;
2012 }
2013
2014 /**
2015  * g_unix_mount_point_get_mount_path:
2016  * @mount_point: a #GUnixMountPoint.
2017  * 
2018  * Gets the mount path for a unix mount point.
2019  * 
2020  * Returns: (type filename): a string containing the mount path.
2021  */
2022 const gchar *
2023 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2024 {
2025   g_return_val_if_fail (mount_point != NULL, NULL);
2026
2027   return mount_point->mount_path;
2028 }
2029
2030 /**
2031  * g_unix_mount_point_get_device_path:
2032  * @mount_point: a #GUnixMountPoint.
2033  * 
2034  * Gets the device path for a unix mount point.
2035  * 
2036  * Returns: (type filename): a string containing the device path.
2037  */
2038 const gchar *
2039 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2040 {
2041   g_return_val_if_fail (mount_point != NULL, NULL);
2042
2043   return mount_point->device_path;
2044 }
2045
2046 /**
2047  * g_unix_mount_point_get_fs_type:
2048  * @mount_point: a #GUnixMountPoint.
2049  * 
2050  * Gets the file system type for the mount point.
2051  * 
2052  * Returns: a string containing the file system type.
2053  */
2054 const gchar *
2055 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2056 {
2057   g_return_val_if_fail (mount_point != NULL, NULL);
2058
2059   return mount_point->filesystem_type;
2060 }
2061
2062 /**
2063  * g_unix_mount_point_get_options:
2064  * @mount_point: a #GUnixMountPoint.
2065  * 
2066  * Gets the options for the mount point.
2067  * 
2068  * Returns: a string containing the options.
2069  *
2070  * Since: 2.32
2071  */
2072 const gchar *
2073 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2074 {
2075   g_return_val_if_fail (mount_point != NULL, NULL);
2076
2077   return mount_point->options;
2078 }
2079
2080 /**
2081  * g_unix_mount_point_is_readonly:
2082  * @mount_point: a #GUnixMountPoint.
2083  * 
2084  * Checks if a unix mount point is read only.
2085  * 
2086  * Returns: %TRUE if a mount point is read only.
2087  */
2088 gboolean
2089 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2090 {
2091   g_return_val_if_fail (mount_point != NULL, FALSE);
2092
2093   return mount_point->is_read_only;
2094 }
2095
2096 /**
2097  * g_unix_mount_point_is_user_mountable:
2098  * @mount_point: a #GUnixMountPoint.
2099  * 
2100  * Checks if a unix mount point is mountable by the user.
2101  * 
2102  * Returns: %TRUE if the mount point is user mountable.
2103  */
2104 gboolean
2105 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2106 {
2107   g_return_val_if_fail (mount_point != NULL, FALSE);
2108
2109   return mount_point->is_user_mountable;
2110 }
2111
2112 /**
2113  * g_unix_mount_point_is_loopback:
2114  * @mount_point: a #GUnixMountPoint.
2115  * 
2116  * Checks if a unix mount point is a loopback device.
2117  * 
2118  * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise. 
2119  */
2120 gboolean
2121 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2122 {
2123   g_return_val_if_fail (mount_point != NULL, FALSE);
2124
2125   return mount_point->is_loopback;
2126 }
2127
2128 static GUnixMountType
2129 guess_mount_type (const char *mount_path,
2130                   const char *device_path,
2131                   const char *filesystem_type)
2132 {
2133   GUnixMountType type;
2134   char *basename;
2135
2136   type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2137   
2138   if ((strcmp (filesystem_type, "udf") == 0) ||
2139       (strcmp (filesystem_type, "iso9660") == 0) ||
2140       (strcmp (filesystem_type, "cd9660") == 0))
2141     type = G_UNIX_MOUNT_TYPE_CDROM;
2142   else if ((strcmp (filesystem_type, "nfs") == 0) ||
2143            (strcmp (filesystem_type, "nfs4") == 0))
2144     type = G_UNIX_MOUNT_TYPE_NFS;
2145   else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
2146            g_str_has_prefix (device_path, "/dev/fd") ||
2147            g_str_has_prefix (device_path, "/dev/floppy"))
2148     type = G_UNIX_MOUNT_TYPE_FLOPPY;
2149   else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
2150            g_str_has_prefix (device_path, "/dev/acd") ||
2151            g_str_has_prefix (device_path, "/dev/cd"))
2152     type = G_UNIX_MOUNT_TYPE_CDROM;
2153   else if (g_str_has_prefix (device_path, "/vol/"))
2154     {
2155       const char *name = mount_path + strlen ("/");
2156       
2157       if (g_str_has_prefix (name, "cdrom"))
2158         type = G_UNIX_MOUNT_TYPE_CDROM;
2159       else if (g_str_has_prefix (name, "floppy") ||
2160                g_str_has_prefix (device_path, "/vol/dev/diskette/")) 
2161         type = G_UNIX_MOUNT_TYPE_FLOPPY;
2162       else if (g_str_has_prefix (name, "rmdisk")) 
2163         type = G_UNIX_MOUNT_TYPE_ZIP;
2164       else if (g_str_has_prefix (name, "jaz"))
2165         type = G_UNIX_MOUNT_TYPE_JAZ;
2166       else if (g_str_has_prefix (name, "memstick"))
2167         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2168     }
2169   else
2170     {
2171       basename = g_path_get_basename (mount_path);
2172       
2173       if (g_str_has_prefix (basename, "cdr") ||
2174           g_str_has_prefix (basename, "cdwriter") ||
2175           g_str_has_prefix (basename, "burn") ||
2176           g_str_has_prefix (basename, "dvdr"))
2177         type = G_UNIX_MOUNT_TYPE_CDROM;
2178       else if (g_str_has_prefix (basename, "floppy"))
2179         type = G_UNIX_MOUNT_TYPE_FLOPPY;
2180       else if (g_str_has_prefix (basename, "zip"))
2181         type = G_UNIX_MOUNT_TYPE_ZIP;
2182       else if (g_str_has_prefix (basename, "jaz"))
2183         type = G_UNIX_MOUNT_TYPE_JAZ;
2184       else if (g_str_has_prefix (basename, "camera"))
2185         type = G_UNIX_MOUNT_TYPE_CAMERA;
2186       else if (g_str_has_prefix (basename, "memstick") ||
2187                g_str_has_prefix (basename, "memory_stick") ||
2188                g_str_has_prefix (basename, "ram"))
2189         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2190       else if (g_str_has_prefix (basename, "compact_flash"))
2191         type = G_UNIX_MOUNT_TYPE_CF;
2192       else if (g_str_has_prefix (basename, "smart_media"))
2193         type = G_UNIX_MOUNT_TYPE_SM;
2194       else if (g_str_has_prefix (basename, "sd_mmc"))
2195         type = G_UNIX_MOUNT_TYPE_SDMMC;
2196       else if (g_str_has_prefix (basename, "ipod"))
2197         type = G_UNIX_MOUNT_TYPE_IPOD;
2198       
2199       g_free (basename);
2200     }
2201   
2202   if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2203     type = G_UNIX_MOUNT_TYPE_HD;
2204   
2205   return type;
2206 }
2207
2208 /**
2209  * g_unix_mount_guess_type:
2210  * @mount_entry: a #GUnixMount.
2211  * 
2212  * Guesses the type of a unix mount. If the mount type cannot be 
2213  * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2214  * 
2215  * Returns: a #GUnixMountType. 
2216  */
2217 static GUnixMountType
2218 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2219 {
2220   g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2221   g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2222   g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2223   g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2224
2225   return guess_mount_type (mount_entry->mount_path,
2226                            mount_entry->device_path,
2227                            mount_entry->filesystem_type);
2228 }
2229
2230 /**
2231  * g_unix_mount_point_guess_type:
2232  * @mount_point: a #GUnixMountPoint.
2233  * 
2234  * Guesses the type of a unix mount point. 
2235  * If the mount type cannot be determined, 
2236  * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2237  * 
2238  * Returns: a #GUnixMountType.
2239  */
2240 static GUnixMountType
2241 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2242 {
2243   g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2244   g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2245   g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2246   g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2247
2248   return guess_mount_type (mount_point->mount_path,
2249                            mount_point->device_path,
2250                            mount_point->filesystem_type);
2251 }
2252
2253 static const char *
2254 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2255 {
2256   const char *icon_name;
2257   
2258   switch (type)
2259     {
2260     case G_UNIX_MOUNT_TYPE_HD:
2261       if (is_mount_point)
2262         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2263       else
2264         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2265       break;
2266     case G_UNIX_MOUNT_TYPE_FLOPPY:
2267     case G_UNIX_MOUNT_TYPE_ZIP:
2268     case G_UNIX_MOUNT_TYPE_JAZ:
2269       if (is_mount_point)
2270         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2271       else
2272         icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2273       break;
2274     case G_UNIX_MOUNT_TYPE_CDROM:
2275       if (is_mount_point)
2276         icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2277       else
2278         icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2279       break;
2280     case G_UNIX_MOUNT_TYPE_NFS:
2281         icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2282       break;
2283     case G_UNIX_MOUNT_TYPE_MEMSTICK:
2284       if (is_mount_point)
2285         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2286       else
2287         icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2288       break;
2289     case G_UNIX_MOUNT_TYPE_CAMERA:
2290       if (is_mount_point)
2291         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2292       else
2293         icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2294       break;
2295     case G_UNIX_MOUNT_TYPE_IPOD:
2296       if (is_mount_point)
2297         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2298       else
2299         icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2300       break;
2301     case G_UNIX_MOUNT_TYPE_UNKNOWN:
2302     default:
2303       if (is_mount_point)
2304         icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2305       else
2306         icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2307       break;
2308     }
2309
2310   return icon_name;
2311 }
2312
2313 /**
2314  * g_unix_mount_guess_name:
2315  * @mount_entry: a #GUnixMountEntry
2316  * 
2317  * Guesses the name of a Unix mount. 
2318  * The result is a translated string.
2319  *
2320  * Returns: A newly allocated string that must
2321  *     be freed with g_free()
2322  */
2323 gchar *
2324 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2325 {
2326   char *name;
2327
2328   if (strcmp (mount_entry->mount_path, "/") == 0)
2329     name = g_strdup (_("Filesystem root"));
2330   else
2331     name = g_filename_display_basename (mount_entry->mount_path);
2332
2333   return name;
2334 }
2335
2336 /**
2337  * g_unix_mount_guess_icon:
2338  * @mount_entry: a #GUnixMountEntry
2339  * 
2340  * Guesses the icon of a Unix mount. 
2341  *
2342  * Returns: (transfer full): a #GIcon
2343  */
2344 GIcon *
2345 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2346 {
2347   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2348 }
2349
2350 /**
2351  * g_unix_mount_guess_symbolic_icon:
2352  * @mount_entry: a #GUnixMountEntry
2353  *
2354  * Guesses the symbolic icon of a Unix mount.
2355  *
2356  * Returns: (transfer full): a #GIcon
2357  *
2358  * Since: 2.34
2359  */
2360 GIcon *
2361 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2362 {
2363   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2364 }
2365
2366 /**
2367  * g_unix_mount_point_guess_name:
2368  * @mount_point: a #GUnixMountPoint
2369  * 
2370  * Guesses the name of a Unix mount point. 
2371  * The result is a translated string.
2372  *
2373  * Returns: A newly allocated string that must 
2374  *     be freed with g_free()
2375  */
2376 gchar *
2377 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2378 {
2379   char *name;
2380
2381   if (strcmp (mount_point->mount_path, "/") == 0)
2382     name = g_strdup (_("Filesystem root"));
2383   else
2384     name = g_filename_display_basename (mount_point->mount_path);
2385
2386   return name;
2387 }
2388
2389 /**
2390  * g_unix_mount_point_guess_icon:
2391  * @mount_point: a #GUnixMountPoint
2392  * 
2393  * Guesses the icon of a Unix mount point. 
2394  *
2395  * Returns: (transfer full): a #GIcon
2396  */
2397 GIcon *
2398 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2399 {
2400   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2401 }
2402
2403 /**
2404  * g_unix_mount_point_guess_symbolic_icon:
2405  * @mount_point: a #GUnixMountPoint
2406  *
2407  * Guesses the symbolic icon of a Unix mount point.
2408  *
2409  * Returns: (transfer full): a #GIcon
2410  *
2411  * Since: 2.34
2412  */
2413 GIcon *
2414 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2415 {
2416   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2417 }
2418
2419 /**
2420  * g_unix_mount_guess_can_eject:
2421  * @mount_entry: a #GUnixMountEntry
2422  * 
2423  * Guesses whether a Unix mount can be ejected.
2424  *
2425  * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2426  */
2427 gboolean
2428 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2429 {
2430   GUnixMountType guessed_type;
2431
2432   guessed_type = g_unix_mount_guess_type (mount_entry);
2433   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2434       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2435     return TRUE;
2436
2437   return FALSE;
2438 }
2439
2440 /**
2441  * g_unix_mount_guess_should_display:
2442  * @mount_entry: a #GUnixMountEntry
2443  * 
2444  * Guesses whether a Unix mount should be displayed in the UI.
2445  *
2446  * Returns: %TRUE if @mount_entry is deemed to be displayable.
2447  */
2448 gboolean
2449 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2450 {
2451   const char *mount_path;
2452   const gchar *user_name;
2453   gsize user_name_len;
2454
2455   /* Never display internal mountpoints */
2456   if (g_unix_mount_is_system_internal (mount_entry))
2457     return FALSE;
2458   
2459   /* Only display things in /media (which are generally user mountable)
2460      and home dir (fuse stuff) and /run/media/$USER */
2461   mount_path = mount_entry->mount_path;
2462   if (mount_path != NULL)
2463     {
2464       gboolean is_in_runtime_dir = FALSE;
2465       /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2466       if (g_strstr_len (mount_path, -1, "/.") != NULL)
2467         return FALSE;
2468
2469       /* Check /run/media/$USER/ */
2470       user_name = g_get_user_name ();
2471       user_name_len = strlen (user_name);
2472       if (strncmp (mount_path, "/run/media/", sizeof ("/run/media/") - 1) == 0 &&
2473           strncmp (mount_path + sizeof ("/run/media/") - 1, user_name, user_name_len) == 0 &&
2474           mount_path[sizeof ("/run/media/") - 1 + user_name_len] == '/')
2475         is_in_runtime_dir = TRUE;
2476
2477       if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2478         {
2479           char *path;
2480           /* Avoid displaying mounts that are not accessible to the user.
2481            *
2482            * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2483            * want to avoid g_access() for mount points which can potentially
2484            * block or fail stat()'ing, such as network mounts.
2485            */
2486           path = g_path_get_dirname (mount_path);
2487           if (g_str_has_prefix (path, "/media/"))
2488             {
2489               if (g_access (path, R_OK|X_OK) != 0) 
2490                 {
2491                   g_free (path);
2492                   return FALSE;
2493                 }
2494             }
2495           g_free (path);
2496
2497           if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2498            {
2499              struct stat st;
2500              if (g_stat (mount_entry->device_path, &st) == 0 &&
2501                  S_ISBLK(st.st_mode) &&
2502                  g_access (mount_path, R_OK|X_OK) != 0)
2503                return FALSE;
2504            }
2505           return TRUE;
2506         }
2507       
2508       if (g_str_has_prefix (mount_path, g_get_home_dir ()) && 
2509           mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2510         return TRUE;
2511     }
2512   
2513   return FALSE;
2514 }
2515
2516 /**
2517  * g_unix_mount_point_guess_can_eject:
2518  * @mount_point: a #GUnixMountPoint
2519  * 
2520  * Guesses whether a Unix mount point can be ejected.
2521  *
2522  * Returns: %TRUE if @mount_point is deemed to be ejectable.
2523  */
2524 gboolean
2525 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2526 {
2527   GUnixMountType guessed_type;
2528
2529   guessed_type = g_unix_mount_point_guess_type (mount_point);
2530   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2531       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2532     return TRUE;
2533
2534   return FALSE;
2535 }
2536
2537 /* Utility functions {{{1 */
2538
2539 #ifdef HAVE_MNTENT_H
2540 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2541 static void
2542 _canonicalize_filename (gchar *filename)
2543 {
2544   gchar *p, *q;
2545   gboolean last_was_slash = FALSE;
2546   
2547   p = filename;
2548   q = filename;
2549   
2550   while (*p)
2551     {
2552       if (*p == G_DIR_SEPARATOR)
2553         {
2554           if (!last_was_slash)
2555             *q++ = G_DIR_SEPARATOR;
2556           
2557           last_was_slash = TRUE;
2558         }
2559       else
2560         {
2561           if (last_was_slash && *p == '.')
2562             {
2563               if (*(p + 1) == G_DIR_SEPARATOR ||
2564                   *(p + 1) == '\0')
2565                 {
2566                   if (*(p + 1) == '\0')
2567                     break;
2568                   
2569                   p += 1;
2570                 }
2571               else if (*(p + 1) == '.' &&
2572                        (*(p + 2) == G_DIR_SEPARATOR ||
2573                         *(p + 2) == '\0'))
2574                 {
2575                   if (q > filename + 1)
2576                     {
2577                       q--;
2578                       while (q > filename + 1 &&
2579                              *(q - 1) != G_DIR_SEPARATOR)
2580                         q--;
2581                     }
2582                   
2583                   if (*(p + 2) == '\0')
2584                     break;
2585                   
2586                   p += 2;
2587                 }
2588               else
2589                 {
2590                   *q++ = *p;
2591                   last_was_slash = FALSE;
2592                 }
2593             }
2594           else
2595             {
2596               *q++ = *p;
2597               last_was_slash = FALSE;
2598             }
2599         }
2600       
2601       p++;
2602     }
2603   
2604   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2605     q--;
2606   
2607   *q = '\0';
2608 }
2609
2610 static char *
2611 _resolve_symlink (const char *file)
2612 {
2613   GError *error;
2614   char *dir;
2615   char *link;
2616   char *f;
2617   char *f1;
2618   
2619   f = g_strdup (file);
2620   
2621   while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) 
2622     {
2623       link = g_file_read_link (f, &error);
2624       if (link == NULL) 
2625         {
2626           g_error_free (error);
2627           g_free (f);
2628           f = NULL;
2629           goto out;
2630         }
2631     
2632       dir = g_path_get_dirname (f);
2633       f1 = g_strdup_printf ("%s/%s", dir, link);
2634       g_free (dir);
2635       g_free (link);
2636       g_free (f);
2637       f = f1;
2638     }
2639   
2640  out:
2641   if (f != NULL)
2642     _canonicalize_filename (f);
2643   return f;
2644 }
2645
2646 static const char *
2647 _resolve_dev_root (void)
2648 {
2649   static gboolean have_real_dev_root = FALSE;
2650   static char real_dev_root[256];
2651   struct stat statbuf;
2652   
2653   /* see if it's cached already */
2654   if (have_real_dev_root)
2655     goto found;
2656   
2657   /* otherwise we're going to find it right away.. */
2658   have_real_dev_root = TRUE;
2659   
2660   if (stat ("/dev/root", &statbuf) == 0) 
2661     {
2662       if (! S_ISLNK (statbuf.st_mode)) 
2663         {
2664           dev_t root_dev = statbuf.st_dev;
2665           FILE *f;
2666       
2667           /* see if device with similar major:minor as /dev/root is mention
2668            * in /etc/mtab (it usually is) 
2669            */
2670           f = fopen ("/etc/mtab", "r");
2671           if (f != NULL) 
2672             {
2673               struct mntent *entp;
2674 #ifdef HAVE_GETMNTENT_R        
2675               struct mntent ent;
2676               char buf[1024];
2677               while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL) 
2678                 {
2679 #else
2680               G_LOCK (getmntent);
2681               while ((entp = getmntent (f)) != NULL) 
2682                 { 
2683 #endif          
2684                   if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2685                       statbuf.st_dev == root_dev) 
2686                     {
2687                       strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2688                       real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2689                       fclose (f);
2690                       goto found;
2691                     }
2692                 }
2693
2694               endmntent (f);
2695
2696 #ifndef HAVE_GETMNTENT_R
2697               G_UNLOCK (getmntent);
2698 #endif
2699             }                                        
2700       
2701           /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2702       
2703         } 
2704        else 
2705         {
2706           char *resolved;
2707           resolved = _resolve_symlink ("/dev/root");
2708           if (resolved != NULL)
2709             {
2710               strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2711               real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2712               g_free (resolved);
2713               goto found;
2714             }
2715         }
2716     }
2717   
2718   /* bah sucks.. */
2719   strcpy (real_dev_root, "/dev/root");
2720   
2721 found:
2722   return real_dev_root;
2723 }
2724 #endif
2725
2726 /* Epilogue {{{1 */
2727 /* vim:set foldmethod=marker: */