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