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