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