docs: Rename README.in to README.md for GitLab
[glib.git] / gio / gunixmounts.c
blobf2db27e661897532b1afc7c66c716b3ebb402e5e
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
4 *
5 * Copyright (C) 2006-2007 Red Hat, Inc.
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.
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.
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/>.
20 * Author: Alexander Larsson <alexl@redhat.com>
23 /* Prologue {{{1 */
25 #include "config.h"
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>
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
62 #ifndef O_BINARY
63 #define O_BINARY 0
64 #endif
66 #include "gunixmounts.h"
67 #include "glocalfileprivate.h"
68 #include "gfile.h"
69 #include "gfilemonitor.h"
70 #include "glibintl.h"
71 #include "gthemedicon.h"
72 #include "gcontextspecificgroup.h"
75 #ifdef HAVE_MNTENT_H
76 static const char *_resolve_dev_root (void);
77 #endif
79 /**
80 * SECTION:gunixmounts
81 * @include: gio/gunixmounts.h
82 * @short_description: UNIX mounts
84 * Routines for managing mounted UNIX mount points and paths.
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.
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.
107 * Types of UNIX mounts.
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;
125 struct _GUnixMountEntry {
126 char *mount_path;
127 char *device_path;
128 char *filesystem_type;
129 char *options;
130 gboolean is_read_only;
131 gboolean is_system_internal;
134 G_DEFINE_BOXED_TYPE (GUnixMountEntry, g_unix_mount_entry,
135 g_unix_mount_copy, g_unix_mount_free)
137 struct _GUnixMountPoint {
138 char *mount_path;
139 char *device_path;
140 char *filesystem_type;
141 char *options;
142 gboolean is_read_only;
143 gboolean is_user_mountable;
144 gboolean is_loopback;
147 G_DEFINE_BOXED_TYPE (GUnixMountPoint, g_unix_mount_point,
148 g_unix_mount_point_copy, g_unix_mount_point_free)
150 static GList *_g_get_unix_mounts (void);
151 static GList *_g_get_unix_mount_points (void);
152 static gboolean proc_mounts_watch_is_running (void);
154 static guint64 mount_poller_time = 0;
156 #ifdef HAVE_SYS_MNTTAB_H
157 #define MNTOPT_RO "ro"
158 #endif
160 #ifdef HAVE_MNTENT_H
161 #include <mntent.h>
162 #ifdef HAVE_LIBMOUNT
163 #include <libmount.h>
164 #endif
165 #elif defined (HAVE_SYS_MNTTAB_H)
166 #include <sys/mnttab.h>
167 #endif
169 #ifdef HAVE_SYS_VFSTAB_H
170 #include <sys/vfstab.h>
171 #endif
173 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
174 #include <sys/mntctl.h>
175 #include <sys/vfs.h>
176 #include <sys/vmount.h>
177 #include <fshelp.h>
178 #endif
180 #if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
181 #include <sys/param.h>
182 #include <sys/ucred.h>
183 #include <sys/mount.h>
184 #include <fstab.h>
185 #ifdef HAVE_SYS_SYSCTL_H
186 #include <sys/sysctl.h>
187 #endif
188 #endif
190 #ifndef HAVE_SETMNTENT
191 #define setmntent(f,m) fopen(f,m)
192 #endif
193 #ifndef HAVE_ENDMNTENT
194 #define endmntent(f) fclose(f)
195 #endif
197 static gboolean
198 is_in (const char *value, const char *set[])
200 int i;
201 for (i = 0; set[i] != NULL; i++)
203 if (strcmp (set[i], value) == 0)
204 return TRUE;
206 return FALSE;
210 * g_unix_is_mount_path_system_internal:
211 * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr`
213 * Determines if @mount_path is considered an implementation of the
214 * OS. This is primarily used for hiding mountable and mounted volumes
215 * that only are used in the OS and has little to no relevance to the
216 * casual user.
218 * Returns: %TRUE if @mount_path is considered an implementation detail
219 * of the OS.
221 gboolean
222 g_unix_is_mount_path_system_internal (const char *mount_path)
224 const char *ignore_mountpoints[] = {
225 /* Includes all FHS 2.3 toplevel dirs and other specialized
226 * directories that we want to hide from the user.
228 "/", /* we already have "Filesystem root" in Nautilus */
229 "/bin",
230 "/boot",
231 "/compat/linux/proc",
232 "/compat/linux/sys",
233 "/dev",
234 "/etc",
235 "/home",
236 "/lib",
237 "/lib64",
238 "/libexec",
239 "/live/cow",
240 "/live/image",
241 "/media",
242 "/mnt",
243 "/opt",
244 "/rescue",
245 "/root",
246 "/sbin",
247 "/srv",
248 "/tmp",
249 "/usr",
250 "/usr/X11R6",
251 "/usr/local",
252 "/usr/obj",
253 "/usr/ports",
254 "/usr/src",
255 "/usr/xobj",
256 "/var",
257 "/var/crash",
258 "/var/local",
259 "/var/log",
260 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
261 "/var/mail",
262 "/var/run",
263 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
264 "/proc",
265 "/sbin",
266 "/net",
267 "/sys",
268 NULL
271 if (is_in (mount_path, ignore_mountpoints))
272 return TRUE;
274 if (g_str_has_prefix (mount_path, "/dev/") ||
275 g_str_has_prefix (mount_path, "/proc/") ||
276 g_str_has_prefix (mount_path, "/sys/"))
277 return TRUE;
279 if (g_str_has_suffix (mount_path, "/.gvfs"))
280 return TRUE;
282 return FALSE;
286 * g_unix_is_system_fs_type:
287 * @fs_type: a file system type, e.g. `procfs` or `tmpfs`
289 * Determines if @fs_type is considered a type of file system which is only
290 * used in implementation of the OS. This is primarily used for hiding
291 * mounted volumes that are intended as APIs for programs to read, and system
292 * administrators at a shell; rather than something that should, for example,
293 * appear in a GUI. For example, the Linux `/proc` filesystem.
295 * The list of file system types considered ‘system’ ones may change over time.
297 * Returns: %TRUE if @fs_type is considered an implementation detail of the OS.
298 * Since: 2.56
300 gboolean
301 g_unix_is_system_fs_type (const char *fs_type)
303 const char *ignore_fs[] = {
304 "adfs",
305 "afs",
306 "auto",
307 "autofs",
308 "autofs4",
309 "cgroup",
310 "cifs",
311 "configfs",
312 "cxfs",
313 "debugfs",
314 "devfs",
315 "devpts",
316 "devtmpfs",
317 "ecryptfs",
318 "fdescfs",
319 "fusectl",
320 "gfs",
321 "gfs2",
322 "gpfs",
323 "hugetlbfs",
324 "kernfs",
325 "linprocfs",
326 "linsysfs",
327 "lustre",
328 "lustre_lite",
329 "mfs",
330 "mqueue",
331 "ncpfs",
332 "nfs",
333 "nfs4",
334 "nfsd",
335 "nullfs",
336 "ocfs2",
337 "overlay",
338 "proc",
339 "procfs",
340 "pstore",
341 "ptyfs",
342 "rootfs",
343 "rpc_pipefs",
344 "securityfs",
345 "selinuxfs",
346 "smbfs",
347 "sysfs",
348 "tmpfs",
349 "usbfs",
350 "zfs",
351 NULL
354 g_return_val_if_fail (fs_type != NULL && *fs_type != '\0', FALSE);
356 return is_in (fs_type, ignore_fs);
360 * g_unix_is_system_device_path:
361 * @device_path: a device path, e.g. `/dev/loop0` or `nfsd`
363 * Determines if @device_path is considered a block device path which is only
364 * used in implementation of the OS. This is primarily used for hiding
365 * mounted volumes that are intended as APIs for programs to read, and system
366 * administrators at a shell; rather than something that should, for example,
367 * appear in a GUI. For example, the Linux `/proc` filesystem.
369 * The list of device paths considered ‘system’ ones may change over time.
371 * Returns: %TRUE if @device_path is considered an implementation detail of
372 * the OS.
373 * Since: 2.56
375 gboolean
376 g_unix_is_system_device_path (const char *device_path)
378 const char *ignore_devices[] = {
379 "none",
380 "sunrpc",
381 "devpts",
382 "nfsd",
383 "/dev/loop",
384 "/dev/vn",
385 NULL
388 g_return_val_if_fail (device_path != NULL && *device_path != '\0', FALSE);
390 return is_in (device_path, ignore_devices);
393 static gboolean
394 guess_system_internal (const char *mountpoint,
395 const char *fs,
396 const char *device)
398 if (g_unix_is_system_fs_type (fs))
399 return TRUE;
401 if (g_unix_is_system_device_path (device))
402 return TRUE;
404 if (g_unix_is_mount_path_system_internal (mountpoint))
405 return TRUE;
407 return FALSE;
410 /* GUnixMounts (ie: mtab) implementations {{{1 */
412 static GUnixMountEntry *
413 create_unix_mount_entry (const char *device_path,
414 const char *mount_path,
415 const char *filesystem_type,
416 const char *options,
417 gboolean is_read_only)
419 GUnixMountEntry *mount_entry = NULL;
421 mount_entry = g_new0 (GUnixMountEntry, 1);
422 mount_entry->device_path = g_strdup (device_path);
423 mount_entry->mount_path = g_strdup (mount_path);
424 mount_entry->filesystem_type = g_strdup (filesystem_type);
425 mount_entry->options = g_strdup (options);
426 mount_entry->is_read_only = is_read_only;
428 mount_entry->is_system_internal =
429 guess_system_internal (mount_entry->mount_path,
430 mount_entry->filesystem_type,
431 mount_entry->device_path);
432 return mount_entry;
435 static GUnixMountPoint *
436 create_unix_mount_point (const char *device_path,
437 const char *mount_path,
438 const char *filesystem_type,
439 const char *options,
440 gboolean is_read_only,
441 gboolean is_user_mountable,
442 gboolean is_loopback)
444 GUnixMountPoint *mount_point = NULL;
446 mount_point = g_new0 (GUnixMountPoint, 1);
447 mount_point->device_path = g_strdup (device_path);
448 mount_point->mount_path = g_strdup (mount_path);
449 mount_point->filesystem_type = g_strdup (filesystem_type);
450 mount_point->options = g_strdup (options);
451 mount_point->is_read_only = is_read_only;
452 mount_point->is_user_mountable = is_user_mountable;
453 mount_point->is_loopback = is_loopback;
455 return mount_point;
458 /* mntent.h (Linux, GNU, NSS) {{{2 */
459 #ifdef HAVE_MNTENT_H
461 #ifdef HAVE_LIBMOUNT
463 /* For documentation on /proc/self/mountinfo see
464 * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
466 #define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
468 static GList *
469 _g_get_unix_mounts (void)
471 struct libmnt_table *table = NULL;
472 struct libmnt_iter* iter = NULL;
473 struct libmnt_fs *fs = NULL;
474 GUnixMountEntry *mount_entry = NULL;
475 GList *return_list = NULL;
477 table = mnt_new_table ();
478 if (mnt_table_parse_mtab (table, NULL) < 0)
479 goto out;
481 iter = mnt_new_iter (MNT_ITER_FORWARD);
482 while (mnt_table_next_fs (table, iter, &fs) == 0)
484 const char *device_path = NULL;
485 char *mount_options = NULL;
486 unsigned long mount_flags = 0;
487 gboolean is_read_only = FALSE;
489 device_path = mnt_fs_get_source (fs);
490 if (g_strcmp0 (device_path, "/dev/root") == 0)
491 device_path = _resolve_dev_root ();
493 mount_options = mnt_fs_strdup_options (fs);
494 if (mount_options)
496 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
497 g_free (mount_options);
499 is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
501 mount_entry = create_unix_mount_entry (device_path,
502 mnt_fs_get_target (fs),
503 mnt_fs_get_fstype (fs),
504 mnt_fs_get_options (fs),
505 is_read_only);
507 return_list = g_list_prepend (return_list, mount_entry);
509 mnt_free_iter (iter);
511 out:
512 mnt_free_table (table);
514 return g_list_reverse (return_list);
517 #else
519 static const char *
520 get_mtab_read_file (void)
522 #ifdef _PATH_MOUNTED
523 # ifdef __linux__
524 return "/proc/mounts";
525 # else
526 return _PATH_MOUNTED;
527 # endif
528 #else
529 return "/etc/mtab";
530 #endif
533 #ifndef HAVE_GETMNTENT_R
534 G_LOCK_DEFINE_STATIC(getmntent);
535 #endif
537 static GList *
538 _g_get_unix_mounts (void)
540 #ifdef HAVE_GETMNTENT_R
541 struct mntent ent;
542 char buf[1024];
543 #endif
544 struct mntent *mntent;
545 FILE *file;
546 const char *read_file;
547 GUnixMountEntry *mount_entry;
548 GHashTable *mounts_hash;
549 GList *return_list;
551 read_file = get_mtab_read_file ();
553 file = setmntent (read_file, "r");
554 if (file == NULL)
555 return NULL;
557 return_list = NULL;
559 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
561 #ifdef HAVE_GETMNTENT_R
562 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
563 #else
564 G_LOCK (getmntent);
565 while ((mntent = getmntent (file)) != NULL)
566 #endif
568 const char *device_path = NULL;
569 gboolean is_read_only = FALSE;
571 /* ignore any mnt_fsname that is repeated and begins with a '/'
573 * We do this to avoid being fooled by --bind mounts, since
574 * these have the same device as the location they bind to.
575 * It's not an ideal solution to the problem, but it's likely that
576 * the most important mountpoint is first and the --bind ones after
577 * that aren't as important. So it should work.
579 * The '/' is to handle procfs, tmpfs and other no device mounts.
581 if (mntent->mnt_fsname != NULL &&
582 mntent->mnt_fsname[0] == '/' &&
583 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
584 continue;
586 if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
587 device_path = _resolve_dev_root ();
588 else
589 device_path = mntent->mnt_fsname;
591 #if defined (HAVE_HASMNTOPT)
592 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
593 is_read_only = TRUE;
594 #endif
596 mount_entry = create_unix_mount_entry (device_path,
597 mntent->mnt_dir,
598 mntent->mnt_type,
599 mntent->mnt_opts,
600 is_read_only);
602 g_hash_table_insert (mounts_hash,
603 mount_entry->device_path,
604 mount_entry->device_path);
606 return_list = g_list_prepend (return_list, mount_entry);
608 g_hash_table_destroy (mounts_hash);
610 endmntent (file);
612 #ifndef HAVE_GETMNTENT_R
613 G_UNLOCK (getmntent);
614 #endif
616 return g_list_reverse (return_list);
619 #endif /* HAVE_LIBMOUNT */
621 static const char *
622 get_mtab_monitor_file (void)
624 static const char *mountinfo_path = NULL;
625 #ifdef HAVE_LIBMOUNT
626 struct stat buf;
627 #endif
629 if (mountinfo_path != NULL)
630 return mountinfo_path;
632 #ifdef HAVE_LIBMOUNT
633 /* The mtab file is still used by some distros, so it has to be monitored in
634 * order to avoid races between g_unix_mounts_get and "mounts-changed" signal:
635 * https://bugzilla.gnome.org/show_bug.cgi?id=782814
637 if (mnt_has_regular_mtab (&mountinfo_path, NULL))
639 return mountinfo_path;
642 if (stat (PROC_MOUNTINFO_PATH, &buf) == 0)
644 mountinfo_path = PROC_MOUNTINFO_PATH;
645 return mountinfo_path;
647 #endif
649 #ifdef _PATH_MOUNTED
650 # ifdef __linux__
651 mountinfo_path = "/proc/mounts";
652 # else
653 mountinfo_path = _PATH_MOUNTED;
654 # endif
655 #else
656 mountinfo_path = "/etc/mtab";
657 #endif
659 return mountinfo_path;
662 /* mnttab.h {{{2 */
663 #elif defined (HAVE_SYS_MNTTAB_H)
665 G_LOCK_DEFINE_STATIC(getmntent);
667 static const char *
668 get_mtab_read_file (void)
670 #ifdef _PATH_MOUNTED
671 return _PATH_MOUNTED;
672 #else
673 return "/etc/mnttab";
674 #endif
677 static const char *
678 get_mtab_monitor_file (void)
680 return get_mtab_read_file ();
683 static GList *
684 _g_get_unix_mounts (void)
686 struct mnttab mntent;
687 FILE *file;
688 const char *read_file;
689 GUnixMountEntry *mount_entry;
690 GList *return_list;
692 read_file = get_mtab_read_file ();
694 file = setmntent (read_file, "r");
695 if (file == NULL)
696 return NULL;
698 return_list = NULL;
700 G_LOCK (getmntent);
701 while (! getmntent (file, &mntent))
703 gboolean is_read_only = FALSE;
705 #if defined (HAVE_HASMNTOPT)
706 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
707 is_read_only = TRUE;
708 #endif
710 mount_entry = create_unix_mount_entry (mntent.mnt_special,
711 mntent.mnt_mountp,
712 mntent.mnt_fstype,
713 mntent.mnt_opts,
714 is_read_only);
716 return_list = g_list_prepend (return_list, mount_entry);
719 endmntent (file);
721 G_UNLOCK (getmntent);
723 return g_list_reverse (return_list);
726 /* mntctl.h (AIX) {{{2 */
727 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
729 static const char *
730 get_mtab_monitor_file (void)
732 return NULL;
735 static GList *
736 _g_get_unix_mounts (void)
738 struct vfs_ent *fs_info;
739 struct vmount *vmount_info;
740 int vmount_number;
741 unsigned int vmount_size;
742 int current;
743 GList *return_list;
745 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
747 g_warning ("Unable to know the number of mounted volumes");
749 return NULL;
752 vmount_info = (struct vmount*)g_malloc (vmount_size);
754 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
756 if (vmount_info->vmt_revision != VMT_REVISION)
757 g_warning ("Bad vmount structure revision number, want %d, got %d", VMT_REVISION, vmount_info->vmt_revision);
759 if (vmount_number < 0)
761 g_warning ("Unable to recover mounted volumes information");
763 g_free (vmount_info);
764 return NULL;
767 return_list = NULL;
768 while (vmount_number > 0)
770 gboolean is_read_only = FALSE;
772 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
774 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
775 is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
777 mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
778 vmt2dataptr (vmount_info, VMT_STUB),
779 fs_info == NULL ? "unknown" : fs_info->vfsent_name,
780 NULL,
781 is_read_only);
783 return_list = g_list_prepend (return_list, mount_entry);
785 vmount_info = (struct vmount *)( (char*)vmount_info
786 + vmount_info->vmt_length);
787 vmount_number--;
790 g_free (vmount_info);
792 return g_list_reverse (return_list);
795 /* sys/mount.h {{{2 */
796 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
798 static const char *
799 get_mtab_monitor_file (void)
801 return NULL;
804 static GList *
805 _g_get_unix_mounts (void)
807 #if defined(USE_STATVFS)
808 struct statvfs *mntent = NULL;
809 #elif defined(USE_STATFS)
810 struct statfs *mntent = NULL;
811 #else
812 #error statfs juggling failed
813 #endif
814 size_t bufsize;
815 int num_mounts, i;
816 GUnixMountEntry *mount_entry;
817 GList *return_list;
819 /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
820 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
821 num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
822 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
823 num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
824 #endif
825 if (num_mounts == -1)
826 return NULL;
828 bufsize = num_mounts * sizeof (*mntent);
829 mntent = g_malloc (bufsize);
830 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
831 num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
832 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
833 num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
834 #endif
835 if (num_mounts == -1)
836 return NULL;
838 return_list = NULL;
840 for (i = 0; i < num_mounts; i++)
842 gboolean is_read_only = FALSE;
844 #if defined(USE_STATVFS)
845 if (mntent[i].f_flag & ST_RDONLY)
846 #elif defined(USE_STATFS)
847 if (mntent[i].f_flags & MNT_RDONLY)
848 #else
849 #error statfs juggling failed
850 #endif
851 is_read_only = TRUE;
853 mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
854 mntent[i].f_mntonname,
855 mntent[i].f_fstypename,
856 NULL,
857 is_read_only);
859 return_list = g_list_prepend (return_list, mount_entry);
862 g_free (mntent);
864 return g_list_reverse (return_list);
867 /* Interix {{{2 */
868 #elif defined(__INTERIX)
870 static const char *
871 get_mtab_monitor_file (void)
873 return NULL;
876 static GList *
877 _g_get_unix_mounts (void)
879 DIR *dirp;
880 GList* return_list = NULL;
881 char filename[9 + NAME_MAX];
883 dirp = opendir ("/dev/fs");
884 if (!dirp)
886 g_warning ("unable to read /dev/fs!");
887 return NULL;
890 while (1)
892 struct statvfs statbuf;
893 struct dirent entry;
894 struct dirent* result;
896 if (readdir_r (dirp, &entry, &result) || result == NULL)
897 break;
899 strcpy (filename, "/dev/fs/");
900 strcat (filename, entry.d_name);
902 if (statvfs (filename, &statbuf) == 0)
904 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
906 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
907 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
908 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
910 if (statbuf.f_flag & ST_RDONLY)
911 mount_entry->is_read_only = TRUE;
913 return_list = g_list_prepend(return_list, mount_entry);
917 return_list = g_list_reverse (return_list);
919 closedir (dirp);
921 return return_list;
924 /* Common code {{{2 */
925 #else
926 #error No _g_get_unix_mounts() implementation for system
927 #endif
929 /* GUnixMountPoints (ie: fstab) implementations {{{1 */
931 /* _g_get_unix_mount_points():
932 * read the fstab.
933 * don't return swap and ignore mounts.
936 static char *
937 get_fstab_file (void)
939 #ifdef HAVE_LIBMOUNT
940 return (char *) mnt_get_fstab_path ();
941 #else
942 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
943 /* AIX */
944 return "/etc/filesystems";
945 #elif defined(_PATH_MNTTAB)
946 return _PATH_MNTTAB;
947 #elif defined(VFSTAB)
948 return VFSTAB;
949 #else
950 return "/etc/fstab";
951 #endif
952 #endif
955 /* mntent.h (Linux, GNU, NSS) {{{2 */
956 #ifdef HAVE_MNTENT_H
958 #ifdef HAVE_LIBMOUNT
960 static GList *
961 _g_get_unix_mount_points (void)
963 struct libmnt_table *table = NULL;
964 struct libmnt_iter* iter = NULL;
965 struct libmnt_fs *fs = NULL;
966 GUnixMountPoint *mount_point = NULL;
967 GList *return_list = NULL;
969 table = mnt_new_table ();
970 if (mnt_table_parse_fstab (table, NULL) < 0)
971 goto out;
973 iter = mnt_new_iter (MNT_ITER_FORWARD);
974 while (mnt_table_next_fs (table, iter, &fs) == 0)
976 const char *device_path = NULL;
977 const char *mount_path = NULL;
978 const char *mount_fstype = NULL;
979 char *mount_options = NULL;
980 gboolean is_read_only = FALSE;
981 gboolean is_user_mountable = FALSE;
982 gboolean is_loopback = FALSE;
984 mount_path = mnt_fs_get_target (fs);
985 if ((strcmp (mount_path, "ignore") == 0) ||
986 (strcmp (mount_path, "swap") == 0) ||
987 (strcmp (mount_path, "none") == 0))
988 continue;
990 mount_fstype = mnt_fs_get_fstype (fs);
991 mount_options = mnt_fs_strdup_options (fs);
992 if (mount_options)
994 unsigned long mount_flags = 0;
995 unsigned long userspace_flags = 0;
997 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
998 mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
1000 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1001 if (mount_flags & MS_BIND)
1003 g_free (mount_options);
1004 continue;
1007 is_read_only = (mount_flags & MS_RDONLY) != 0;
1008 is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
1010 if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
1011 ((userspace_flags & MNT_MS_USER) &&
1012 (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
1013 (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
1014 (userspace_flags & MNT_MS_USERS) ||
1015 (userspace_flags & MNT_MS_OWNER))
1017 is_user_mountable = TRUE;
1021 device_path = mnt_fs_get_source (fs);
1022 if (g_strcmp0 (device_path, "/dev/root") == 0)
1023 device_path = _resolve_dev_root ();
1025 mount_point = create_unix_mount_point (device_path,
1026 mount_path,
1027 mount_fstype,
1028 mount_options,
1029 is_read_only,
1030 is_user_mountable,
1031 is_loopback);
1032 if (mount_options)
1033 g_free (mount_options);
1035 return_list = g_list_prepend (return_list, mount_point);
1037 mnt_free_iter (iter);
1039 out:
1040 mnt_free_table (table);
1042 return g_list_reverse (return_list);
1045 #else
1047 static GList *
1048 _g_get_unix_mount_points (void)
1050 #ifdef HAVE_GETMNTENT_R
1051 struct mntent ent;
1052 char buf[1024];
1053 #endif
1054 struct mntent *mntent;
1055 FILE *file;
1056 char *read_file;
1057 GUnixMountPoint *mount_point;
1058 GList *return_list;
1060 read_file = get_fstab_file ();
1062 file = setmntent (read_file, "r");
1063 if (file == NULL)
1064 return NULL;
1066 return_list = NULL;
1068 #ifdef HAVE_GETMNTENT_R
1069 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
1070 #else
1071 G_LOCK (getmntent);
1072 while ((mntent = getmntent (file)) != NULL)
1073 #endif
1075 const char *device_path = NULL;
1076 gboolean is_read_only = FALSE;
1077 gboolean is_user_mountable = FALSE;
1078 gboolean is_loopback = FALSE;
1080 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
1081 (strcmp (mntent->mnt_dir, "swap") == 0) ||
1082 (strcmp (mntent->mnt_dir, "none") == 0))
1083 continue;
1085 #ifdef HAVE_HASMNTOPT
1086 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1087 if (hasmntopt (mntent, "bind"))
1088 continue;
1089 #endif
1091 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1092 device_path = _resolve_dev_root ();
1093 else
1094 device_path = mntent->mnt_fsname;
1096 #ifdef HAVE_HASMNTOPT
1097 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1098 is_read_only = TRUE;
1100 if (hasmntopt (mntent, "loop") != NULL)
1101 is_loopback = TRUE;
1103 #endif
1105 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1106 #ifdef HAVE_HASMNTOPT
1107 || (hasmntopt (mntent, "user") != NULL
1108 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1109 || hasmntopt (mntent, "pamconsole") != NULL
1110 || hasmntopt (mntent, "users") != NULL
1111 || hasmntopt (mntent, "owner") != NULL
1112 #endif
1114 is_user_mountable = TRUE;
1116 mount_point = create_unix_mount_point (device_path,
1117 mntent->mnt_dir,
1118 mntent->mnt_type,
1119 mntent->mnt_opts,
1120 is_read_only,
1121 is_user_mountable,
1122 is_loopback);
1124 return_list = g_list_prepend (return_list, mount_point);
1127 endmntent (file);
1129 #ifndef HAVE_GETMNTENT_R
1130 G_UNLOCK (getmntent);
1131 #endif
1133 return g_list_reverse (return_list);
1136 #endif /* HAVE_LIBMOUNT */
1138 /* mnttab.h {{{2 */
1139 #elif defined (HAVE_SYS_MNTTAB_H)
1141 static GList *
1142 _g_get_unix_mount_points (void)
1144 struct mnttab mntent;
1145 FILE *file;
1146 char *read_file;
1147 GUnixMountPoint *mount_point;
1148 GList *return_list;
1150 read_file = get_fstab_file ();
1152 file = setmntent (read_file, "r");
1153 if (file == NULL)
1154 return NULL;
1156 return_list = NULL;
1158 G_LOCK (getmntent);
1159 while (! getmntent (file, &mntent))
1161 gboolean is_read_only = FALSE;
1162 gboolean is_user_mountable = FALSE;
1163 gboolean is_loopback = FALSE;
1165 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1166 (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1167 (strcmp (mntent.mnt_mountp, "none") == 0))
1168 continue;
1170 #ifdef HAVE_HASMNTOPT
1171 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1172 is_read_only = TRUE;
1174 if (hasmntopt (&mntent, "lofs") != NULL)
1175 is_loopback = TRUE;
1176 #endif
1178 if ((mntent.mnt_fstype != NULL)
1179 #ifdef HAVE_HASMNTOPT
1180 || (hasmntopt (&mntent, "user") != NULL
1181 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1182 || hasmntopt (&mntent, "pamconsole") != NULL
1183 || hasmntopt (&mntent, "users") != NULL
1184 || hasmntopt (&mntent, "owner") != NULL
1185 #endif
1187 is_user_mountable = TRUE;
1189 mount_point = create_unix_mount_point (mntent.mnt_special,
1190 mntent.mnt_mountp,
1191 mntent.mnt_fstype,
1192 mntent.mnt_mntopts,
1193 is_read_only,
1194 is_user_mountable,
1195 is_loopback);
1197 return_list = g_list_prepend (return_list, mount_point);
1200 endmntent (file);
1201 G_UNLOCK (getmntent);
1203 return g_list_reverse (return_list);
1206 /* mntctl.h (AIX) {{{2 */
1207 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1209 /* functions to parse /etc/filesystems on aix */
1211 /* read character, ignoring comments (begin with '*', end with '\n' */
1212 static int
1213 aix_fs_getc (FILE *fd)
1215 int c;
1217 while ((c = getc (fd)) == '*')
1219 while (((c = getc (fd)) != '\n') && (c != EOF))
1224 /* eat all continuous spaces in a file */
1225 static int
1226 aix_fs_ignorespace (FILE *fd)
1228 int c;
1230 while ((c = aix_fs_getc (fd)) != EOF)
1232 if (!g_ascii_isspace (c))
1234 ungetc (c,fd);
1235 return c;
1239 return EOF;
1242 /* read one word from file */
1243 static int
1244 aix_fs_getword (FILE *fd,
1245 char *word)
1247 int c;
1249 aix_fs_ignorespace (fd);
1251 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1253 if (c == '"')
1255 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1256 *word++ = c;
1257 else
1258 *word++ = c;
1261 *word = 0;
1263 return c;
1266 typedef struct {
1267 char mnt_mount[PATH_MAX];
1268 char mnt_special[PATH_MAX];
1269 char mnt_fstype[16];
1270 char mnt_options[128];
1271 } AixMountTableEntry;
1273 /* read mount points properties */
1274 static int
1275 aix_fs_get (FILE *fd,
1276 AixMountTableEntry *prop)
1278 static char word[PATH_MAX] = { 0 };
1279 char value[PATH_MAX];
1281 /* read stanza */
1282 if (word[0] == 0)
1284 if (aix_fs_getword (fd, word) == EOF)
1285 return EOF;
1288 word[strlen(word) - 1] = 0;
1289 strcpy (prop->mnt_mount, word);
1291 /* read attributes and value */
1293 while (aix_fs_getword (fd, word) != EOF)
1295 /* test if is attribute or new stanza */
1296 if (word[strlen(word) - 1] == ':')
1297 return 0;
1299 /* read "=" */
1300 aix_fs_getword (fd, value);
1302 /* read value */
1303 aix_fs_getword (fd, value);
1305 if (strcmp (word, "dev") == 0)
1306 strcpy (prop->mnt_special, value);
1307 else if (strcmp (word, "vfs") == 0)
1308 strcpy (prop->mnt_fstype, value);
1309 else if (strcmp (word, "options") == 0)
1310 strcpy(prop->mnt_options, value);
1313 return 0;
1316 static GList *
1317 _g_get_unix_mount_points (void)
1319 struct mntent *mntent;
1320 FILE *file;
1321 char *read_file;
1322 GUnixMountPoint *mount_point;
1323 AixMountTableEntry mntent;
1324 GList *return_list;
1326 read_file = get_fstab_file ();
1328 file = setmntent (read_file, "r");
1329 if (file == NULL)
1330 return NULL;
1332 return_list = NULL;
1334 while (!aix_fs_get (file, &mntent))
1336 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1338 mount_point = create_unix_mount_point (mntent.mnt_special,
1339 mntent.mnt_mount,
1340 mntent.mnt_fstype,
1341 mntent.mnt_options,
1342 TRUE,
1343 TRUE,
1344 FALSE);
1346 return_list = g_list_prepend (return_list, mount_point);
1350 endmntent (file);
1352 return g_list_reverse (return_list);
1355 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1357 static GList *
1358 _g_get_unix_mount_points (void)
1360 struct fstab *fstab = NULL;
1361 GUnixMountPoint *mount_point;
1362 GList *return_list;
1363 #ifdef HAVE_SYS_SYSCTL_H
1364 int usermnt = 0;
1365 struct stat sb;
1366 #endif
1368 if (!setfsent ())
1369 return NULL;
1371 return_list = NULL;
1373 #ifdef HAVE_SYS_SYSCTL_H
1374 #if defined(HAVE_SYSCTLBYNAME)
1376 size_t len = sizeof(usermnt);
1378 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1380 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1382 int mib[2];
1383 size_t len = sizeof(usermnt);
1385 mib[0] = CTL_VFS;
1386 mib[1] = VFS_USERMOUNT;
1387 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1389 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1391 int mib[2];
1392 size_t len = sizeof(usermnt);
1394 mib[0] = CTL_KERN;
1395 mib[1] = KERN_USERMOUNT;
1396 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1398 #endif
1399 #endif
1401 while ((fstab = getfsent ()) != NULL)
1403 gboolean is_read_only = FALSE;
1404 gboolean is_user_mountable = FALSE;
1406 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1407 continue;
1409 if (strcmp (fstab->fs_type, "ro") == 0)
1410 is_read_only = TRUE;
1412 #ifdef HAVE_SYS_SYSCTL_H
1413 if (usermnt != 0)
1415 uid_t uid = getuid ();
1416 if (stat (fstab->fs_file, &sb) == 0)
1418 if (uid == 0 || sb.st_uid == uid)
1419 is_user_mountable = TRUE;
1422 #endif
1424 mount_point = create_unix_mount_point (fstab->fs_spec,
1425 fstab->fs_file,
1426 fstab->fs_vfstype,
1427 fstab->fs_mntops,
1428 is_read_only,
1429 is_user_mountable,
1430 FALSE);
1432 return_list = g_list_prepend (return_list, mount_point);
1435 endfsent ();
1437 return g_list_reverse (return_list);
1439 /* Interix {{{2 */
1440 #elif defined(__INTERIX)
1441 static GList *
1442 _g_get_unix_mount_points (void)
1444 return _g_get_unix_mounts ();
1447 /* Common code {{{2 */
1448 #else
1449 #error No g_get_mount_table() implementation for system
1450 #endif
1452 static guint64
1453 get_mounts_timestamp (void)
1455 const char *monitor_file;
1456 struct stat buf;
1458 monitor_file = get_mtab_monitor_file ();
1459 /* Don't return mtime for /proc/ files */
1460 if (monitor_file && !g_str_has_prefix (monitor_file, "/proc/"))
1462 if (stat (monitor_file, &buf) == 0)
1463 return (guint64)buf.st_mtime;
1465 else if (proc_mounts_watch_is_running ())
1467 /* it's being monitored by poll, so return mount_poller_time */
1468 return mount_poller_time;
1470 else
1472 /* Case of /proc/ file not being monitored - Be on the safe side and
1473 * send a new timestamp to force g_unix_mounts_changed_since() to
1474 * return TRUE so any application caches depending on it (like eg.
1475 * the one in GIO) get invalidated and don't hold possibly outdated
1476 * data - see Bug 787731 */
1477 return (guint64) g_get_monotonic_time ();
1479 return 0;
1482 static guint64
1483 get_mount_points_timestamp (void)
1485 const char *monitor_file;
1486 struct stat buf;
1488 monitor_file = get_fstab_file ();
1489 if (monitor_file)
1491 if (stat (monitor_file, &buf) == 0)
1492 return (guint64)buf.st_mtime;
1494 return 0;
1498 * g_unix_mounts_get:
1499 * @time_read: (out) (optional): guint64 to contain a timestamp, or %NULL
1501 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1502 * If @time_read is set, it will be filled with the mount
1503 * timestamp, allowing for checking if the mounts have changed
1504 * with g_unix_mounts_changed_since().
1506 * Returns: (element-type GUnixMountEntry) (transfer full):
1507 * a #GList of the UNIX mounts.
1509 GList *
1510 g_unix_mounts_get (guint64 *time_read)
1512 if (time_read)
1513 *time_read = get_mounts_timestamp ();
1515 return _g_get_unix_mounts ();
1519 * g_unix_mount_at:
1520 * @mount_path: (type filename): path for a possible unix mount.
1521 * @time_read: (out) (optional): guint64 to contain a timestamp.
1523 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1524 * is set, it will be filled with a unix timestamp for checking
1525 * if the mounts have changed since with g_unix_mounts_changed_since().
1527 * Returns: (transfer full): a #GUnixMountEntry.
1529 GUnixMountEntry *
1530 g_unix_mount_at (const char *mount_path,
1531 guint64 *time_read)
1533 GList *mounts, *l;
1534 GUnixMountEntry *mount_entry, *found;
1536 mounts = g_unix_mounts_get (time_read);
1538 found = NULL;
1539 for (l = mounts; l != NULL; l = l->next)
1541 mount_entry = l->data;
1543 if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1544 found = mount_entry;
1545 else
1546 g_unix_mount_free (mount_entry);
1548 g_list_free (mounts);
1550 return found;
1554 * g_unix_mount_for:
1555 * @file_path: (type filename): file path on some unix mount.
1556 * @time_read: (out) (optional): guint64 to contain a timestamp.
1558 * Gets a #GUnixMountEntry for a given file path. If @time_read
1559 * is set, it will be filled with a unix timestamp for checking
1560 * if the mounts have changed since with g_unix_mounts_changed_since().
1562 * Returns: (transfer full): a #GUnixMountEntry.
1564 * Since: 2.52
1566 GUnixMountEntry *
1567 g_unix_mount_for (const char *file_path,
1568 guint64 *time_read)
1570 GUnixMountEntry *entry;
1572 g_return_val_if_fail (file_path != NULL, NULL);
1574 entry = g_unix_mount_at (file_path, time_read);
1575 if (entry == NULL)
1577 char *topdir;
1579 topdir = _g_local_file_find_topdir_for (file_path);
1580 if (topdir != NULL)
1582 entry = g_unix_mount_at (topdir, time_read);
1583 g_free (topdir);
1587 return entry;
1591 * g_unix_mount_points_get:
1592 * @time_read: (out) (optional): guint64 to contain a timestamp.
1594 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1595 * If @time_read is set, it will be filled with the mount timestamp,
1596 * allowing for checking if the mounts have changed with
1597 * g_unix_mount_points_changed_since().
1599 * Returns: (element-type GUnixMountPoint) (transfer full):
1600 * a #GList of the UNIX mountpoints.
1602 GList *
1603 g_unix_mount_points_get (guint64 *time_read)
1605 if (time_read)
1606 *time_read = get_mount_points_timestamp ();
1608 return _g_get_unix_mount_points ();
1612 * g_unix_mounts_changed_since:
1613 * @time: guint64 to contain a timestamp.
1615 * Checks if the unix mounts have changed since a given unix time.
1617 * Returns: %TRUE if the mounts have changed since @time.
1619 gboolean
1620 g_unix_mounts_changed_since (guint64 time)
1622 return get_mounts_timestamp () != time;
1626 * g_unix_mount_points_changed_since:
1627 * @time: guint64 to contain a timestamp.
1629 * Checks if the unix mount points have changed since a given unix time.
1631 * Returns: %TRUE if the mount points have changed since @time.
1633 gboolean
1634 g_unix_mount_points_changed_since (guint64 time)
1636 return get_mount_points_timestamp () != time;
1639 /* GUnixMountMonitor {{{1 */
1641 enum {
1642 MOUNTS_CHANGED,
1643 MOUNTPOINTS_CHANGED,
1644 LAST_SIGNAL
1647 static guint signals[LAST_SIGNAL];
1649 struct _GUnixMountMonitor {
1650 GObject parent;
1652 GMainContext *context;
1655 struct _GUnixMountMonitorClass {
1656 GObjectClass parent_class;
1660 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT)
1662 static GContextSpecificGroup mount_monitor_group;
1663 static GFileMonitor *fstab_monitor;
1664 static GFileMonitor *mtab_monitor;
1665 static GSource *proc_mounts_watch_source;
1666 static GList *mount_poller_mounts;
1667 static guint mtab_file_changed_id;
1669 static gboolean
1670 proc_mounts_watch_is_running (void)
1672 return proc_mounts_watch_source != NULL &&
1673 !g_source_is_destroyed (proc_mounts_watch_source);
1676 static void
1677 fstab_file_changed (GFileMonitor *monitor,
1678 GFile *file,
1679 GFile *other_file,
1680 GFileMonitorEvent event_type,
1681 gpointer user_data)
1683 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1684 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1685 event_type != G_FILE_MONITOR_EVENT_DELETED)
1686 return;
1688 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1691 static gboolean
1692 mtab_file_changed_cb (gpointer user_data)
1694 mtab_file_changed_id = 0;
1695 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1697 return G_SOURCE_REMOVE;
1700 static void
1701 mtab_file_changed (GFileMonitor *monitor,
1702 GFile *file,
1703 GFile *other_file,
1704 GFileMonitorEvent event_type,
1705 gpointer user_data)
1707 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1708 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1709 event_type != G_FILE_MONITOR_EVENT_DELETED)
1710 return;
1712 /* Skip accumulated events from file monitor which we are not able to handle
1713 * in a real time instead of emitting mounts_changed signal several times.
1714 * This should behave equally to GIOChannel based monitoring. See Bug 792235.
1716 if (mtab_file_changed_id > 0)
1717 return;
1719 mtab_file_changed_id = g_idle_add (mtab_file_changed_cb, NULL);
1722 static gboolean
1723 proc_mounts_changed (GIOChannel *channel,
1724 GIOCondition cond,
1725 gpointer user_data)
1727 if (cond & G_IO_ERR)
1729 mount_poller_time = (guint64) g_get_monotonic_time ();
1730 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1733 return TRUE;
1736 static gboolean
1737 mount_change_poller (gpointer user_data)
1739 GList *current_mounts, *new_it, *old_it;
1740 gboolean has_changed = FALSE;
1742 current_mounts = _g_get_unix_mounts ();
1744 for ( new_it = current_mounts, old_it = mount_poller_mounts;
1745 new_it != NULL && old_it != NULL;
1746 new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1748 if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1750 has_changed = TRUE;
1751 break;
1754 if (!(new_it == NULL && old_it == NULL))
1755 has_changed = TRUE;
1757 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1759 mount_poller_mounts = current_mounts;
1761 if (has_changed)
1763 mount_poller_time = (guint64) g_get_monotonic_time ();
1764 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1767 return TRUE;
1771 static void
1772 mount_monitor_stop (void)
1774 if (fstab_monitor)
1776 g_file_monitor_cancel (fstab_monitor);
1777 g_object_unref (fstab_monitor);
1780 if (proc_mounts_watch_source != NULL)
1782 g_source_destroy (proc_mounts_watch_source);
1783 proc_mounts_watch_source = NULL;
1786 if (mtab_monitor)
1788 g_file_monitor_cancel (mtab_monitor);
1789 g_object_unref (mtab_monitor);
1792 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1795 static void
1796 mount_monitor_start (void)
1798 GFile *file;
1800 if (get_fstab_file () != NULL)
1802 file = g_file_new_for_path (get_fstab_file ());
1803 fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1804 g_object_unref (file);
1806 g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1809 if (get_mtab_monitor_file () != NULL)
1811 const gchar *mtab_path;
1813 mtab_path = get_mtab_monitor_file ();
1814 /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1815 * See 'man proc' for more details.
1817 if (g_str_has_prefix (mtab_path, "/proc/"))
1819 GIOChannel *proc_mounts_channel;
1820 GError *error = NULL;
1821 proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
1822 if (proc_mounts_channel == NULL)
1824 g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1825 error->message, g_quark_to_string (error->domain), error->code);
1826 g_error_free (error);
1828 else
1830 proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1831 g_source_set_callback (proc_mounts_watch_source,
1832 (GSourceFunc) proc_mounts_changed,
1833 NULL, NULL);
1834 g_source_attach (proc_mounts_watch_source,
1835 g_main_context_get_thread_default ());
1836 g_source_unref (proc_mounts_watch_source);
1837 g_io_channel_unref (proc_mounts_channel);
1840 else
1842 file = g_file_new_for_path (mtab_path);
1843 mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1844 g_object_unref (file);
1845 g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
1848 else
1850 proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1851 mount_poller_mounts = _g_get_unix_mounts ();
1852 mount_poller_time = (guint64)g_get_monotonic_time ();
1853 g_source_set_callback (proc_mounts_watch_source,
1854 mount_change_poller,
1855 NULL, NULL);
1856 g_source_attach (proc_mounts_watch_source,
1857 g_main_context_get_thread_default ());
1858 g_source_unref (proc_mounts_watch_source);
1862 static void
1863 g_unix_mount_monitor_finalize (GObject *object)
1865 GUnixMountMonitor *monitor;
1867 monitor = G_UNIX_MOUNT_MONITOR (object);
1869 g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
1871 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1874 static void
1875 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1877 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1879 gobject_class->finalize = g_unix_mount_monitor_finalize;
1882 * GUnixMountMonitor::mounts-changed:
1883 * @monitor: the object on which the signal is emitted
1885 * Emitted when the unix mounts have changed.
1887 signals[MOUNTS_CHANGED] =
1888 g_signal_new (I_("mounts-changed"),
1889 G_TYPE_FROM_CLASS (klass),
1890 G_SIGNAL_RUN_LAST,
1892 NULL, NULL,
1893 g_cclosure_marshal_VOID__VOID,
1894 G_TYPE_NONE, 0);
1897 * GUnixMountMonitor::mountpoints-changed:
1898 * @monitor: the object on which the signal is emitted
1900 * Emitted when the unix mount points have changed.
1902 signals[MOUNTPOINTS_CHANGED] =
1903 g_signal_new (I_("mountpoints-changed"),
1904 G_TYPE_FROM_CLASS (klass),
1905 G_SIGNAL_RUN_LAST,
1907 NULL, NULL,
1908 g_cclosure_marshal_VOID__VOID,
1909 G_TYPE_NONE, 0);
1912 static void
1913 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1918 * g_unix_mount_monitor_set_rate_limit:
1919 * @mount_monitor: a #GUnixMountMonitor
1920 * @limit_msec: a integer with the limit in milliseconds to
1921 * poll for changes.
1923 * This function does nothing.
1925 * Before 2.44, this was a partially-effective way of controlling the
1926 * rate at which events would be reported under some uncommon
1927 * circumstances. Since @mount_monitor is a singleton, it also meant
1928 * that calling this function would have side effects for other users of
1929 * the monitor.
1931 * Since: 2.18
1933 * Deprecated:2.44:This function does nothing. Don't call it.
1935 void
1936 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1937 gint limit_msec)
1942 * g_unix_mount_monitor_get:
1944 * Gets the #GUnixMountMonitor for the current thread-default main
1945 * context.
1947 * The mount monitor can be used to monitor for changes to the list of
1948 * mounted filesystems as well as the list of mount points (ie: fstab
1949 * entries).
1951 * You must only call g_object_unref() on the return value from under
1952 * the same main context as you called this function.
1954 * Returns: (transfer full): the #GUnixMountMonitor.
1956 * Since: 2.44
1958 GUnixMountMonitor *
1959 g_unix_mount_monitor_get (void)
1961 return g_context_specific_group_get (&mount_monitor_group,
1962 G_TYPE_UNIX_MOUNT_MONITOR,
1963 G_STRUCT_OFFSET(GUnixMountMonitor, context),
1964 mount_monitor_start);
1968 * g_unix_mount_monitor_new:
1970 * Deprecated alias for g_unix_mount_monitor_get().
1972 * This function was never a true constructor, which is why it was
1973 * renamed.
1975 * Returns: a #GUnixMountMonitor.
1977 * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
1979 GUnixMountMonitor *
1980 g_unix_mount_monitor_new (void)
1982 return g_unix_mount_monitor_get ();
1985 /* GUnixMount {{{1 */
1987 * g_unix_mount_free:
1988 * @mount_entry: a #GUnixMountEntry.
1990 * Frees a unix mount.
1992 void
1993 g_unix_mount_free (GUnixMountEntry *mount_entry)
1995 g_return_if_fail (mount_entry != NULL);
1997 g_free (mount_entry->mount_path);
1998 g_free (mount_entry->device_path);
1999 g_free (mount_entry->filesystem_type);
2000 g_free (mount_entry->options);
2001 g_free (mount_entry);
2005 * g_unix_mount_copy:
2006 * @mount_entry: a #GUnixMountEntry.
2008 * Makes a copy of @mount_entry.
2010 * Returns: (transfer full): a new #GUnixMountEntry
2012 * Since: 2.54
2014 GUnixMountEntry *
2015 g_unix_mount_copy (GUnixMountEntry *mount_entry)
2017 GUnixMountEntry *copy;
2019 g_return_val_if_fail (mount_entry != NULL, NULL);
2021 copy = g_new0 (GUnixMountEntry, 1);
2022 copy->mount_path = g_strdup (mount_entry->mount_path);
2023 copy->device_path = g_strdup (mount_entry->device_path);
2024 copy->filesystem_type = g_strdup (mount_entry->filesystem_type);
2025 copy->options = g_strdup (mount_entry->options);
2026 copy->is_read_only = mount_entry->is_read_only;
2027 copy->is_system_internal = mount_entry->is_system_internal;
2029 return copy;
2033 * g_unix_mount_point_free:
2034 * @mount_point: unix mount point to free.
2036 * Frees a unix mount point.
2038 void
2039 g_unix_mount_point_free (GUnixMountPoint *mount_point)
2041 g_return_if_fail (mount_point != NULL);
2043 g_free (mount_point->mount_path);
2044 g_free (mount_point->device_path);
2045 g_free (mount_point->filesystem_type);
2046 g_free (mount_point->options);
2047 g_free (mount_point);
2051 * g_unix_mount_point_copy:
2052 * @mount_point: a #GUnixMountPoint.
2054 * Makes a copy of @mount_point.
2056 * Returns: (transfer full): a new #GUnixMountPoint
2058 * Since: 2.54
2060 GUnixMountPoint*
2061 g_unix_mount_point_copy (GUnixMountPoint *mount_point)
2063 GUnixMountPoint *copy;
2065 g_return_val_if_fail (mount_point != NULL, NULL);
2067 copy = g_new0 (GUnixMountPoint, 1);
2068 copy->mount_path = g_strdup (mount_point->mount_path);
2069 copy->device_path = g_strdup (mount_point->device_path);
2070 copy->filesystem_type = g_strdup (mount_point->filesystem_type);
2071 copy->options = g_strdup (mount_point->options);
2072 copy->is_read_only = mount_point->is_read_only;
2073 copy->is_user_mountable = mount_point->is_user_mountable;
2074 copy->is_loopback = mount_point->is_loopback;
2076 return copy;
2080 * g_unix_mount_compare:
2081 * @mount1: first #GUnixMountEntry to compare.
2082 * @mount2: second #GUnixMountEntry to compare.
2084 * Compares two unix mounts.
2086 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2087 * or less than @mount2, respectively.
2089 gint
2090 g_unix_mount_compare (GUnixMountEntry *mount1,
2091 GUnixMountEntry *mount2)
2093 int res;
2095 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2097 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2098 if (res != 0)
2099 return res;
2101 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2102 if (res != 0)
2103 return res;
2105 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2106 if (res != 0)
2107 return res;
2109 res = g_strcmp0 (mount1->options, mount2->options);
2110 if (res != 0)
2111 return res;
2113 res = mount1->is_read_only - mount2->is_read_only;
2114 if (res != 0)
2115 return res;
2117 return 0;
2121 * g_unix_mount_get_mount_path:
2122 * @mount_entry: input #GUnixMountEntry to get the mount path for.
2124 * Gets the mount path for a unix mount.
2126 * Returns: (type filename): the mount path for @mount_entry.
2128 const gchar *
2129 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
2131 g_return_val_if_fail (mount_entry != NULL, NULL);
2133 return mount_entry->mount_path;
2137 * g_unix_mount_get_device_path:
2138 * @mount_entry: a #GUnixMount.
2140 * Gets the device path for a unix mount.
2142 * Returns: (type filename): a string containing the device path.
2144 const gchar *
2145 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
2147 g_return_val_if_fail (mount_entry != NULL, NULL);
2149 return mount_entry->device_path;
2153 * g_unix_mount_get_fs_type:
2154 * @mount_entry: a #GUnixMount.
2156 * Gets the filesystem type for the unix mount.
2158 * Returns: a string containing the file system type.
2160 const gchar *
2161 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
2163 g_return_val_if_fail (mount_entry != NULL, NULL);
2165 return mount_entry->filesystem_type;
2169 * g_unix_mount_get_options:
2170 * @mount_entry: a #GUnixMountEntry.
2172 * Gets a comma-separated list of mount options for the unix mount. For example,
2173 * `rw,relatime,seclabel,data=ordered`.
2175 * This is similar to g_unix_mount_point_get_options(), but it takes
2176 * a #GUnixMountEntry as an argument.
2178 * Returns: (nullable): a string containing the options, or %NULL if not
2179 * available.
2181 * Since: 2.58
2183 const gchar *
2184 g_unix_mount_get_options (GUnixMountEntry *mount_entry)
2186 g_return_val_if_fail (mount_entry != NULL, NULL);
2188 return mount_entry->options;
2192 * g_unix_mount_is_readonly:
2193 * @mount_entry: a #GUnixMount.
2195 * Checks if a unix mount is mounted read only.
2197 * Returns: %TRUE if @mount_entry is read only.
2199 gboolean
2200 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
2202 g_return_val_if_fail (mount_entry != NULL, FALSE);
2204 return mount_entry->is_read_only;
2208 * g_unix_mount_is_system_internal:
2209 * @mount_entry: a #GUnixMount.
2211 * Checks if a Unix mount is a system mount. This is the Boolean OR of
2212 * g_unix_is_system_fs_type(), g_unix_is_system_device_path() and
2213 * g_unix_is_mount_path_system_internal() on @mount_entry’s properties.
2215 * The definition of what a ‘system’ mount entry is may change over time as new
2216 * file system types and device paths are ignored.
2218 * Returns: %TRUE if the unix mount is for a system path.
2220 gboolean
2221 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
2223 g_return_val_if_fail (mount_entry != NULL, FALSE);
2225 return mount_entry->is_system_internal;
2228 /* GUnixMountPoint {{{1 */
2230 * g_unix_mount_point_compare:
2231 * @mount1: a #GUnixMount.
2232 * @mount2: a #GUnixMount.
2234 * Compares two unix mount points.
2236 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2237 * or less than @mount2, respectively.
2239 gint
2240 g_unix_mount_point_compare (GUnixMountPoint *mount1,
2241 GUnixMountPoint *mount2)
2243 int res;
2245 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2247 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2248 if (res != 0)
2249 return res;
2251 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2252 if (res != 0)
2253 return res;
2255 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2256 if (res != 0)
2257 return res;
2259 res = g_strcmp0 (mount1->options, mount2->options);
2260 if (res != 0)
2261 return res;
2263 res = mount1->is_read_only - mount2->is_read_only;
2264 if (res != 0)
2265 return res;
2267 res = mount1->is_user_mountable - mount2->is_user_mountable;
2268 if (res != 0)
2269 return res;
2271 res = mount1->is_loopback - mount2->is_loopback;
2272 if (res != 0)
2273 return res;
2275 return 0;
2279 * g_unix_mount_point_get_mount_path:
2280 * @mount_point: a #GUnixMountPoint.
2282 * Gets the mount path for a unix mount point.
2284 * Returns: (type filename): a string containing the mount path.
2286 const gchar *
2287 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2289 g_return_val_if_fail (mount_point != NULL, NULL);
2291 return mount_point->mount_path;
2295 * g_unix_mount_point_get_device_path:
2296 * @mount_point: a #GUnixMountPoint.
2298 * Gets the device path for a unix mount point.
2300 * Returns: (type filename): a string containing the device path.
2302 const gchar *
2303 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2305 g_return_val_if_fail (mount_point != NULL, NULL);
2307 return mount_point->device_path;
2311 * g_unix_mount_point_get_fs_type:
2312 * @mount_point: a #GUnixMountPoint.
2314 * Gets the file system type for the mount point.
2316 * Returns: a string containing the file system type.
2318 const gchar *
2319 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2321 g_return_val_if_fail (mount_point != NULL, NULL);
2323 return mount_point->filesystem_type;
2327 * g_unix_mount_point_get_options:
2328 * @mount_point: a #GUnixMountPoint.
2330 * Gets the options for the mount point.
2332 * Returns: a string containing the options.
2334 * Since: 2.32
2336 const gchar *
2337 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2339 g_return_val_if_fail (mount_point != NULL, NULL);
2341 return mount_point->options;
2345 * g_unix_mount_point_is_readonly:
2346 * @mount_point: a #GUnixMountPoint.
2348 * Checks if a unix mount point is read only.
2350 * Returns: %TRUE if a mount point is read only.
2352 gboolean
2353 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2355 g_return_val_if_fail (mount_point != NULL, FALSE);
2357 return mount_point->is_read_only;
2361 * g_unix_mount_point_is_user_mountable:
2362 * @mount_point: a #GUnixMountPoint.
2364 * Checks if a unix mount point is mountable by the user.
2366 * Returns: %TRUE if the mount point is user mountable.
2368 gboolean
2369 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2371 g_return_val_if_fail (mount_point != NULL, FALSE);
2373 return mount_point->is_user_mountable;
2377 * g_unix_mount_point_is_loopback:
2378 * @mount_point: a #GUnixMountPoint.
2380 * Checks if a unix mount point is a loopback device.
2382 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
2384 gboolean
2385 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2387 g_return_val_if_fail (mount_point != NULL, FALSE);
2389 return mount_point->is_loopback;
2392 static GUnixMountType
2393 guess_mount_type (const char *mount_path,
2394 const char *device_path,
2395 const char *filesystem_type)
2397 GUnixMountType type;
2398 char *basename;
2400 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2402 if ((strcmp (filesystem_type, "udf") == 0) ||
2403 (strcmp (filesystem_type, "iso9660") == 0) ||
2404 (strcmp (filesystem_type, "cd9660") == 0))
2405 type = G_UNIX_MOUNT_TYPE_CDROM;
2406 else if ((strcmp (filesystem_type, "nfs") == 0) ||
2407 (strcmp (filesystem_type, "nfs4") == 0))
2408 type = G_UNIX_MOUNT_TYPE_NFS;
2409 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
2410 g_str_has_prefix (device_path, "/dev/fd") ||
2411 g_str_has_prefix (device_path, "/dev/floppy"))
2412 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2413 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
2414 g_str_has_prefix (device_path, "/dev/acd") ||
2415 g_str_has_prefix (device_path, "/dev/cd"))
2416 type = G_UNIX_MOUNT_TYPE_CDROM;
2417 else if (g_str_has_prefix (device_path, "/vol/"))
2419 const char *name = mount_path + strlen ("/");
2421 if (g_str_has_prefix (name, "cdrom"))
2422 type = G_UNIX_MOUNT_TYPE_CDROM;
2423 else if (g_str_has_prefix (name, "floppy") ||
2424 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
2425 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2426 else if (g_str_has_prefix (name, "rmdisk"))
2427 type = G_UNIX_MOUNT_TYPE_ZIP;
2428 else if (g_str_has_prefix (name, "jaz"))
2429 type = G_UNIX_MOUNT_TYPE_JAZ;
2430 else if (g_str_has_prefix (name, "memstick"))
2431 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2433 else
2435 basename = g_path_get_basename (mount_path);
2437 if (g_str_has_prefix (basename, "cdr") ||
2438 g_str_has_prefix (basename, "cdwriter") ||
2439 g_str_has_prefix (basename, "burn") ||
2440 g_str_has_prefix (basename, "dvdr"))
2441 type = G_UNIX_MOUNT_TYPE_CDROM;
2442 else if (g_str_has_prefix (basename, "floppy"))
2443 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2444 else if (g_str_has_prefix (basename, "zip"))
2445 type = G_UNIX_MOUNT_TYPE_ZIP;
2446 else if (g_str_has_prefix (basename, "jaz"))
2447 type = G_UNIX_MOUNT_TYPE_JAZ;
2448 else if (g_str_has_prefix (basename, "camera"))
2449 type = G_UNIX_MOUNT_TYPE_CAMERA;
2450 else if (g_str_has_prefix (basename, "memstick") ||
2451 g_str_has_prefix (basename, "memory_stick") ||
2452 g_str_has_prefix (basename, "ram"))
2453 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2454 else if (g_str_has_prefix (basename, "compact_flash"))
2455 type = G_UNIX_MOUNT_TYPE_CF;
2456 else if (g_str_has_prefix (basename, "smart_media"))
2457 type = G_UNIX_MOUNT_TYPE_SM;
2458 else if (g_str_has_prefix (basename, "sd_mmc"))
2459 type = G_UNIX_MOUNT_TYPE_SDMMC;
2460 else if (g_str_has_prefix (basename, "ipod"))
2461 type = G_UNIX_MOUNT_TYPE_IPOD;
2463 g_free (basename);
2466 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2467 type = G_UNIX_MOUNT_TYPE_HD;
2469 return type;
2473 * g_unix_mount_guess_type:
2474 * @mount_entry: a #GUnixMount.
2476 * Guesses the type of a unix mount. If the mount type cannot be
2477 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2479 * Returns: a #GUnixMountType.
2481 static GUnixMountType
2482 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2484 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2485 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2486 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2487 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2489 return guess_mount_type (mount_entry->mount_path,
2490 mount_entry->device_path,
2491 mount_entry->filesystem_type);
2495 * g_unix_mount_point_guess_type:
2496 * @mount_point: a #GUnixMountPoint.
2498 * Guesses the type of a unix mount point.
2499 * If the mount type cannot be determined,
2500 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2502 * Returns: a #GUnixMountType.
2504 static GUnixMountType
2505 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2507 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2508 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2509 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2510 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2512 return guess_mount_type (mount_point->mount_path,
2513 mount_point->device_path,
2514 mount_point->filesystem_type);
2517 static const char *
2518 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2520 const char *icon_name;
2522 switch (type)
2524 case G_UNIX_MOUNT_TYPE_HD:
2525 if (is_mount_point)
2526 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2527 else
2528 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2529 break;
2530 case G_UNIX_MOUNT_TYPE_FLOPPY:
2531 case G_UNIX_MOUNT_TYPE_ZIP:
2532 case G_UNIX_MOUNT_TYPE_JAZ:
2533 if (is_mount_point)
2534 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2535 else
2536 icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2537 break;
2538 case G_UNIX_MOUNT_TYPE_CDROM:
2539 if (is_mount_point)
2540 icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2541 else
2542 icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2543 break;
2544 case G_UNIX_MOUNT_TYPE_NFS:
2545 icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2546 break;
2547 case G_UNIX_MOUNT_TYPE_MEMSTICK:
2548 if (is_mount_point)
2549 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2550 else
2551 icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2552 break;
2553 case G_UNIX_MOUNT_TYPE_CAMERA:
2554 if (is_mount_point)
2555 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2556 else
2557 icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2558 break;
2559 case G_UNIX_MOUNT_TYPE_IPOD:
2560 if (is_mount_point)
2561 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2562 else
2563 icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2564 break;
2565 case G_UNIX_MOUNT_TYPE_UNKNOWN:
2566 default:
2567 if (is_mount_point)
2568 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2569 else
2570 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2571 break;
2574 return icon_name;
2578 * g_unix_mount_guess_name:
2579 * @mount_entry: a #GUnixMountEntry
2581 * Guesses the name of a Unix mount.
2582 * The result is a translated string.
2584 * Returns: A newly allocated string that must
2585 * be freed with g_free()
2587 gchar *
2588 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2590 char *name;
2592 if (strcmp (mount_entry->mount_path, "/") == 0)
2593 name = g_strdup (_("Filesystem root"));
2594 else
2595 name = g_filename_display_basename (mount_entry->mount_path);
2597 return name;
2601 * g_unix_mount_guess_icon:
2602 * @mount_entry: a #GUnixMountEntry
2604 * Guesses the icon of a Unix mount.
2606 * Returns: (transfer full): a #GIcon
2608 GIcon *
2609 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2611 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2615 * g_unix_mount_guess_symbolic_icon:
2616 * @mount_entry: a #GUnixMountEntry
2618 * Guesses the symbolic icon of a Unix mount.
2620 * Returns: (transfer full): a #GIcon
2622 * Since: 2.34
2624 GIcon *
2625 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2627 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2631 * g_unix_mount_point_guess_name:
2632 * @mount_point: a #GUnixMountPoint
2634 * Guesses the name of a Unix mount point.
2635 * The result is a translated string.
2637 * Returns: A newly allocated string that must
2638 * be freed with g_free()
2640 gchar *
2641 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2643 char *name;
2645 if (strcmp (mount_point->mount_path, "/") == 0)
2646 name = g_strdup (_("Filesystem root"));
2647 else
2648 name = g_filename_display_basename (mount_point->mount_path);
2650 return name;
2654 * g_unix_mount_point_guess_icon:
2655 * @mount_point: a #GUnixMountPoint
2657 * Guesses the icon of a Unix mount point.
2659 * Returns: (transfer full): a #GIcon
2661 GIcon *
2662 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2664 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2668 * g_unix_mount_point_guess_symbolic_icon:
2669 * @mount_point: a #GUnixMountPoint
2671 * Guesses the symbolic icon of a Unix mount point.
2673 * Returns: (transfer full): a #GIcon
2675 * Since: 2.34
2677 GIcon *
2678 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2680 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2684 * g_unix_mount_guess_can_eject:
2685 * @mount_entry: a #GUnixMountEntry
2687 * Guesses whether a Unix mount can be ejected.
2689 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2691 gboolean
2692 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2694 GUnixMountType guessed_type;
2696 guessed_type = g_unix_mount_guess_type (mount_entry);
2697 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2698 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2699 return TRUE;
2701 return FALSE;
2705 * g_unix_mount_guess_should_display:
2706 * @mount_entry: a #GUnixMountEntry
2708 * Guesses whether a Unix mount should be displayed in the UI.
2710 * Returns: %TRUE if @mount_entry is deemed to be displayable.
2712 gboolean
2713 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2715 const char *mount_path;
2716 const gchar *user_name;
2717 gsize user_name_len;
2719 /* Never display internal mountpoints */
2720 if (g_unix_mount_is_system_internal (mount_entry))
2721 return FALSE;
2723 /* Only display things in /media (which are generally user mountable)
2724 and home dir (fuse stuff) and /run/media/$USER */
2725 mount_path = mount_entry->mount_path;
2726 if (mount_path != NULL)
2728 const gboolean running_as_root = (getuid () == 0);
2729 gboolean is_in_runtime_dir = FALSE;
2731 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2732 if (g_strstr_len (mount_path, -1, "/.") != NULL)
2733 return FALSE;
2735 /* Check /run/media/$USER/. If running as root, display any mounts below
2736 * /run/media/. */
2737 if (running_as_root)
2739 if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0)
2740 is_in_runtime_dir = TRUE;
2742 else
2744 user_name = g_get_user_name ();
2745 user_name_len = strlen (user_name);
2746 if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0 &&
2747 strncmp (mount_path + strlen ("/run/media/"), user_name, user_name_len) == 0 &&
2748 mount_path[strlen ("/run/media/") + user_name_len] == '/')
2749 is_in_runtime_dir = TRUE;
2752 if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2754 char *path;
2755 /* Avoid displaying mounts that are not accessible to the user.
2757 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2758 * want to avoid g_access() for mount points which can potentially
2759 * block or fail stat()'ing, such as network mounts.
2761 path = g_path_get_dirname (mount_path);
2762 if (g_str_has_prefix (path, "/media/"))
2764 if (g_access (path, R_OK|X_OK) != 0)
2766 g_free (path);
2767 return FALSE;
2770 g_free (path);
2772 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2774 struct stat st;
2775 if (g_stat (mount_entry->device_path, &st) == 0 &&
2776 S_ISBLK(st.st_mode) &&
2777 g_access (mount_path, R_OK|X_OK) != 0)
2778 return FALSE;
2780 return TRUE;
2783 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
2784 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2785 return TRUE;
2788 return FALSE;
2792 * g_unix_mount_point_guess_can_eject:
2793 * @mount_point: a #GUnixMountPoint
2795 * Guesses whether a Unix mount point can be ejected.
2797 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2799 gboolean
2800 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2802 GUnixMountType guessed_type;
2804 guessed_type = g_unix_mount_point_guess_type (mount_point);
2805 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2806 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2807 return TRUE;
2809 return FALSE;
2812 /* Utility functions {{{1 */
2814 #ifdef HAVE_MNTENT_H
2815 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2816 static void
2817 _canonicalize_filename (gchar *filename)
2819 gchar *p, *q;
2820 gboolean last_was_slash = FALSE;
2822 p = filename;
2823 q = filename;
2825 while (*p)
2827 if (*p == G_DIR_SEPARATOR)
2829 if (!last_was_slash)
2830 *q++ = G_DIR_SEPARATOR;
2832 last_was_slash = TRUE;
2834 else
2836 if (last_was_slash && *p == '.')
2838 if (*(p + 1) == G_DIR_SEPARATOR ||
2839 *(p + 1) == '\0')
2841 if (*(p + 1) == '\0')
2842 break;
2844 p += 1;
2846 else if (*(p + 1) == '.' &&
2847 (*(p + 2) == G_DIR_SEPARATOR ||
2848 *(p + 2) == '\0'))
2850 if (q > filename + 1)
2852 q--;
2853 while (q > filename + 1 &&
2854 *(q - 1) != G_DIR_SEPARATOR)
2855 q--;
2858 if (*(p + 2) == '\0')
2859 break;
2861 p += 2;
2863 else
2865 *q++ = *p;
2866 last_was_slash = FALSE;
2869 else
2871 *q++ = *p;
2872 last_was_slash = FALSE;
2876 p++;
2879 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2880 q--;
2882 *q = '\0';
2885 static char *
2886 _resolve_symlink (const char *file)
2888 GError *error;
2889 char *dir;
2890 char *link;
2891 char *f;
2892 char *f1;
2894 f = g_strdup (file);
2896 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
2898 link = g_file_read_link (f, &error);
2899 if (link == NULL)
2901 g_error_free (error);
2902 g_free (f);
2903 f = NULL;
2904 goto out;
2907 dir = g_path_get_dirname (f);
2908 f1 = g_strdup_printf ("%s/%s", dir, link);
2909 g_free (dir);
2910 g_free (link);
2911 g_free (f);
2912 f = f1;
2915 out:
2916 if (f != NULL)
2917 _canonicalize_filename (f);
2918 return f;
2921 static const char *
2922 _resolve_dev_root (void)
2924 static gboolean have_real_dev_root = FALSE;
2925 static char real_dev_root[256];
2926 struct stat statbuf;
2928 /* see if it's cached already */
2929 if (have_real_dev_root)
2930 goto found;
2932 /* otherwise we're going to find it right away.. */
2933 have_real_dev_root = TRUE;
2935 if (stat ("/dev/root", &statbuf) == 0)
2937 if (! S_ISLNK (statbuf.st_mode))
2939 dev_t root_dev = statbuf.st_dev;
2940 FILE *f;
2942 /* see if device with similar major:minor as /dev/root is mention
2943 * in /etc/mtab (it usually is)
2945 f = fopen ("/etc/mtab", "r");
2946 if (f != NULL)
2948 struct mntent *entp;
2949 #ifdef HAVE_GETMNTENT_R
2950 struct mntent ent;
2951 char buf[1024];
2952 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
2954 #else
2955 G_LOCK (getmntent);
2956 while ((entp = getmntent (f)) != NULL)
2958 #endif
2959 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2960 statbuf.st_dev == root_dev)
2962 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2963 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2964 fclose (f);
2965 goto found;
2969 endmntent (f);
2971 #ifndef HAVE_GETMNTENT_R
2972 G_UNLOCK (getmntent);
2973 #endif
2976 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2979 else
2981 char *resolved;
2982 resolved = _resolve_symlink ("/dev/root");
2983 if (resolved != NULL)
2985 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2986 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2987 g_free (resolved);
2988 goto found;
2993 /* bah sucks.. */
2994 strcpy (real_dev_root, "/dev/root");
2996 found:
2997 return real_dev_root;
2999 #endif
3001 /* Epilogue {{{1 */
3002 /* vim:set foldmethod=marker: */