Check for overflow when allocating RcBox
[glib.git] / gio / gunixmounts.c
blob355329c19e0a850feb9d0f8d692884639570741c
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 "configfs",
311 "cxfs",
312 "debugfs",
313 "devfs",
314 "devpts",
315 "devtmpfs",
316 "ecryptfs",
317 "fdescfs",
318 "fusectl",
319 "gfs",
320 "gfs2",
321 "gpfs",
322 "hugetlbfs",
323 "kernfs",
324 "linprocfs",
325 "linsysfs",
326 "lustre",
327 "lustre_lite",
328 "mfs",
329 "mqueue",
330 "ncpfs",
331 "nfsd",
332 "nullfs",
333 "ocfs2",
334 "overlay",
335 "proc",
336 "procfs",
337 "pstore",
338 "ptyfs",
339 "rootfs",
340 "rpc_pipefs",
341 "securityfs",
342 "selinuxfs",
343 "sysfs",
344 "tmpfs",
345 "usbfs",
346 "zfs",
347 NULL
350 g_return_val_if_fail (fs_type != NULL && *fs_type != '\0', FALSE);
352 return is_in (fs_type, ignore_fs);
356 * g_unix_is_system_device_path:
357 * @device_path: a device path, e.g. `/dev/loop0` or `nfsd`
359 * Determines if @device_path is considered a block device path which is only
360 * used in implementation of the OS. This is primarily used for hiding
361 * mounted volumes that are intended as APIs for programs to read, and system
362 * administrators at a shell; rather than something that should, for example,
363 * appear in a GUI. For example, the Linux `/proc` filesystem.
365 * The list of device paths considered ‘system’ ones may change over time.
367 * Returns: %TRUE if @device_path is considered an implementation detail of
368 * the OS.
369 * Since: 2.56
371 gboolean
372 g_unix_is_system_device_path (const char *device_path)
374 const char *ignore_devices[] = {
375 "none",
376 "sunrpc",
377 "devpts",
378 "nfsd",
379 "/dev/loop",
380 "/dev/vn",
381 NULL
384 g_return_val_if_fail (device_path != NULL && *device_path != '\0', FALSE);
386 return is_in (device_path, ignore_devices);
389 static gboolean
390 guess_system_internal (const char *mountpoint,
391 const char *fs,
392 const char *device)
394 if (g_unix_is_system_fs_type (fs))
395 return TRUE;
397 if (g_unix_is_system_device_path (device))
398 return TRUE;
400 if (g_unix_is_mount_path_system_internal (mountpoint))
401 return TRUE;
403 return FALSE;
406 /* GUnixMounts (ie: mtab) implementations {{{1 */
408 static GUnixMountEntry *
409 create_unix_mount_entry (const char *device_path,
410 const char *mount_path,
411 const char *filesystem_type,
412 const char *options,
413 gboolean is_read_only)
415 GUnixMountEntry *mount_entry = NULL;
417 mount_entry = g_new0 (GUnixMountEntry, 1);
418 mount_entry->device_path = g_strdup (device_path);
419 mount_entry->mount_path = g_strdup (mount_path);
420 mount_entry->filesystem_type = g_strdup (filesystem_type);
421 mount_entry->options = g_strdup (options);
422 mount_entry->is_read_only = is_read_only;
424 mount_entry->is_system_internal =
425 guess_system_internal (mount_entry->mount_path,
426 mount_entry->filesystem_type,
427 mount_entry->device_path);
428 return mount_entry;
431 static GUnixMountPoint *
432 create_unix_mount_point (const char *device_path,
433 const char *mount_path,
434 const char *filesystem_type,
435 const char *options,
436 gboolean is_read_only,
437 gboolean is_user_mountable,
438 gboolean is_loopback)
440 GUnixMountPoint *mount_point = NULL;
442 mount_point = g_new0 (GUnixMountPoint, 1);
443 mount_point->device_path = g_strdup (device_path);
444 mount_point->mount_path = g_strdup (mount_path);
445 mount_point->filesystem_type = g_strdup (filesystem_type);
446 mount_point->options = g_strdup (options);
447 mount_point->is_read_only = is_read_only;
448 mount_point->is_user_mountable = is_user_mountable;
449 mount_point->is_loopback = is_loopback;
451 return mount_point;
454 /* mntent.h (Linux, GNU, NSS) {{{2 */
455 #ifdef HAVE_MNTENT_H
457 #ifdef HAVE_LIBMOUNT
459 /* For documentation on /proc/self/mountinfo see
460 * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
462 #define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
464 static GList *
465 _g_get_unix_mounts (void)
467 struct libmnt_table *table = NULL;
468 struct libmnt_iter* iter = NULL;
469 struct libmnt_fs *fs = NULL;
470 GUnixMountEntry *mount_entry = NULL;
471 GList *return_list = NULL;
473 table = mnt_new_table ();
474 if (mnt_table_parse_mtab (table, NULL) < 0)
475 goto out;
477 iter = mnt_new_iter (MNT_ITER_FORWARD);
478 while (mnt_table_next_fs (table, iter, &fs) == 0)
480 const char *device_path = NULL;
481 char *mount_options = NULL;
482 unsigned long mount_flags = 0;
483 gboolean is_read_only = FALSE;
485 device_path = mnt_fs_get_source (fs);
486 if (g_strcmp0 (device_path, "/dev/root") == 0)
487 device_path = _resolve_dev_root ();
489 mount_options = mnt_fs_strdup_options (fs);
490 if (mount_options)
492 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
493 g_free (mount_options);
495 is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
497 mount_entry = create_unix_mount_entry (device_path,
498 mnt_fs_get_target (fs),
499 mnt_fs_get_fstype (fs),
500 mnt_fs_get_options (fs),
501 is_read_only);
503 return_list = g_list_prepend (return_list, mount_entry);
505 mnt_free_iter (iter);
507 out:
508 mnt_free_table (table);
510 return g_list_reverse (return_list);
513 #else
515 static const char *
516 get_mtab_read_file (void)
518 #ifdef _PATH_MOUNTED
519 # ifdef __linux__
520 return "/proc/mounts";
521 # else
522 return _PATH_MOUNTED;
523 # endif
524 #else
525 return "/etc/mtab";
526 #endif
529 #ifndef HAVE_GETMNTENT_R
530 G_LOCK_DEFINE_STATIC(getmntent);
531 #endif
533 static GList *
534 _g_get_unix_mounts (void)
536 #ifdef HAVE_GETMNTENT_R
537 struct mntent ent;
538 char buf[1024];
539 #endif
540 struct mntent *mntent;
541 FILE *file;
542 const char *read_file;
543 GUnixMountEntry *mount_entry;
544 GHashTable *mounts_hash;
545 GList *return_list;
547 read_file = get_mtab_read_file ();
549 file = setmntent (read_file, "r");
550 if (file == NULL)
551 return NULL;
553 return_list = NULL;
555 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
557 #ifdef HAVE_GETMNTENT_R
558 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
559 #else
560 G_LOCK (getmntent);
561 while ((mntent = getmntent (file)) != NULL)
562 #endif
564 const char *device_path = NULL;
565 gboolean is_read_only = FALSE;
567 /* ignore any mnt_fsname that is repeated and begins with a '/'
569 * We do this to avoid being fooled by --bind mounts, since
570 * these have the same device as the location they bind to.
571 * It's not an ideal solution to the problem, but it's likely that
572 * the most important mountpoint is first and the --bind ones after
573 * that aren't as important. So it should work.
575 * The '/' is to handle procfs, tmpfs and other no device mounts.
577 if (mntent->mnt_fsname != NULL &&
578 mntent->mnt_fsname[0] == '/' &&
579 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
580 continue;
582 if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
583 device_path = _resolve_dev_root ();
584 else
585 device_path = mntent->mnt_fsname;
587 #if defined (HAVE_HASMNTOPT)
588 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
589 is_read_only = TRUE;
590 #endif
592 mount_entry = create_unix_mount_entry (device_path,
593 mntent->mnt_dir,
594 mntent->mnt_type,
595 mntent->mnt_opts,
596 is_read_only);
598 g_hash_table_insert (mounts_hash,
599 mount_entry->device_path,
600 mount_entry->device_path);
602 return_list = g_list_prepend (return_list, mount_entry);
604 g_hash_table_destroy (mounts_hash);
606 endmntent (file);
608 #ifndef HAVE_GETMNTENT_R
609 G_UNLOCK (getmntent);
610 #endif
612 return g_list_reverse (return_list);
615 #endif /* HAVE_LIBMOUNT */
617 static const char *
618 get_mtab_monitor_file (void)
620 static const char *mountinfo_path = NULL;
621 #ifdef HAVE_LIBMOUNT
622 struct stat buf;
623 #endif
625 if (mountinfo_path != NULL)
626 return mountinfo_path;
628 #ifdef HAVE_LIBMOUNT
629 /* The mtab file is still used by some distros, so it has to be monitored in
630 * order to avoid races between g_unix_mounts_get and "mounts-changed" signal:
631 * https://bugzilla.gnome.org/show_bug.cgi?id=782814
633 if (mnt_has_regular_mtab (&mountinfo_path, NULL))
635 return mountinfo_path;
638 if (stat (PROC_MOUNTINFO_PATH, &buf) == 0)
640 mountinfo_path = PROC_MOUNTINFO_PATH;
641 return mountinfo_path;
643 #endif
645 #ifdef _PATH_MOUNTED
646 # ifdef __linux__
647 mountinfo_path = "/proc/mounts";
648 # else
649 mountinfo_path = _PATH_MOUNTED;
650 # endif
651 #else
652 mountinfo_path = "/etc/mtab";
653 #endif
655 return mountinfo_path;
658 /* mnttab.h {{{2 */
659 #elif defined (HAVE_SYS_MNTTAB_H)
661 G_LOCK_DEFINE_STATIC(getmntent);
663 static const char *
664 get_mtab_read_file (void)
666 #ifdef _PATH_MOUNTED
667 return _PATH_MOUNTED;
668 #else
669 return "/etc/mnttab";
670 #endif
673 static const char *
674 get_mtab_monitor_file (void)
676 return get_mtab_read_file ();
679 static GList *
680 _g_get_unix_mounts (void)
682 struct mnttab mntent;
683 FILE *file;
684 const char *read_file;
685 GUnixMountEntry *mount_entry;
686 GList *return_list;
688 read_file = get_mtab_read_file ();
690 file = setmntent (read_file, "r");
691 if (file == NULL)
692 return NULL;
694 return_list = NULL;
696 G_LOCK (getmntent);
697 while (! getmntent (file, &mntent))
699 gboolean is_read_only = FALSE;
701 #if defined (HAVE_HASMNTOPT)
702 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
703 is_read_only = TRUE;
704 #endif
706 mount_entry = create_unix_mount_entry (mntent.mnt_special,
707 mntent.mnt_mountp,
708 mntent.mnt_fstype,
709 mntent.mnt_opts,
710 is_read_only);
712 return_list = g_list_prepend (return_list, mount_entry);
715 endmntent (file);
717 G_UNLOCK (getmntent);
719 return g_list_reverse (return_list);
722 /* mntctl.h (AIX) {{{2 */
723 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
725 static const char *
726 get_mtab_monitor_file (void)
728 return NULL;
731 static GList *
732 _g_get_unix_mounts (void)
734 struct vfs_ent *fs_info;
735 struct vmount *vmount_info;
736 int vmount_number;
737 unsigned int vmount_size;
738 int current;
739 GList *return_list;
741 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
743 g_warning ("Unable to know the number of mounted volumes");
745 return NULL;
748 vmount_info = (struct vmount*)g_malloc (vmount_size);
750 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
752 if (vmount_info->vmt_revision != VMT_REVISION)
753 g_warning ("Bad vmount structure revision number, want %d, got %d", VMT_REVISION, vmount_info->vmt_revision);
755 if (vmount_number < 0)
757 g_warning ("Unable to recover mounted volumes information");
759 g_free (vmount_info);
760 return NULL;
763 return_list = NULL;
764 while (vmount_number > 0)
766 gboolean is_read_only = FALSE;
768 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
770 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
771 is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
773 mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
774 vmt2dataptr (vmount_info, VMT_STUB),
775 fs_info == NULL ? "unknown" : fs_info->vfsent_name,
776 NULL,
777 is_read_only);
779 return_list = g_list_prepend (return_list, mount_entry);
781 vmount_info = (struct vmount *)( (char*)vmount_info
782 + vmount_info->vmt_length);
783 vmount_number--;
786 g_free (vmount_info);
788 return g_list_reverse (return_list);
791 /* sys/mount.h {{{2 */
792 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
794 static const char *
795 get_mtab_monitor_file (void)
797 return NULL;
800 static GList *
801 _g_get_unix_mounts (void)
803 #if defined(USE_STATVFS)
804 struct statvfs *mntent = NULL;
805 #elif defined(USE_STATFS)
806 struct statfs *mntent = NULL;
807 #else
808 #error statfs juggling failed
809 #endif
810 size_t bufsize;
811 int num_mounts, i;
812 GUnixMountEntry *mount_entry;
813 GList *return_list;
815 /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
816 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
817 num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
818 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
819 num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
820 #endif
821 if (num_mounts == -1)
822 return NULL;
824 bufsize = num_mounts * sizeof (*mntent);
825 mntent = g_malloc (bufsize);
826 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
827 num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
828 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
829 num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
830 #endif
831 if (num_mounts == -1)
832 return NULL;
834 return_list = NULL;
836 for (i = 0; i < num_mounts; i++)
838 gboolean is_read_only = FALSE;
840 #if defined(USE_STATVFS)
841 if (mntent[i].f_flag & ST_RDONLY)
842 #elif defined(USE_STATFS)
843 if (mntent[i].f_flags & MNT_RDONLY)
844 #else
845 #error statfs juggling failed
846 #endif
847 is_read_only = TRUE;
849 mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
850 mntent[i].f_mntonname,
851 mntent[i].f_fstypename,
852 NULL,
853 is_read_only);
855 return_list = g_list_prepend (return_list, mount_entry);
858 g_free (mntent);
860 return g_list_reverse (return_list);
863 /* Interix {{{2 */
864 #elif defined(__INTERIX)
866 static const char *
867 get_mtab_monitor_file (void)
869 return NULL;
872 static GList *
873 _g_get_unix_mounts (void)
875 DIR *dirp;
876 GList* return_list = NULL;
877 char filename[9 + NAME_MAX];
879 dirp = opendir ("/dev/fs");
880 if (!dirp)
882 g_warning ("unable to read /dev/fs!");
883 return NULL;
886 while (1)
888 struct statvfs statbuf;
889 struct dirent entry;
890 struct dirent* result;
892 if (readdir_r (dirp, &entry, &result) || result == NULL)
893 break;
895 strcpy (filename, "/dev/fs/");
896 strcat (filename, entry.d_name);
898 if (statvfs (filename, &statbuf) == 0)
900 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
902 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
903 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
904 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
906 if (statbuf.f_flag & ST_RDONLY)
907 mount_entry->is_read_only = TRUE;
909 return_list = g_list_prepend(return_list, mount_entry);
913 return_list = g_list_reverse (return_list);
915 closedir (dirp);
917 return return_list;
920 /* Common code {{{2 */
921 #else
922 #error No _g_get_unix_mounts() implementation for system
923 #endif
925 /* GUnixMountPoints (ie: fstab) implementations {{{1 */
927 /* _g_get_unix_mount_points():
928 * read the fstab.
929 * don't return swap and ignore mounts.
932 static char *
933 get_fstab_file (void)
935 #ifdef HAVE_LIBMOUNT
936 return (char *) mnt_get_fstab_path ();
937 #else
938 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
939 /* AIX */
940 return "/etc/filesystems";
941 #elif defined(_PATH_MNTTAB)
942 return _PATH_MNTTAB;
943 #elif defined(VFSTAB)
944 return VFSTAB;
945 #else
946 return "/etc/fstab";
947 #endif
948 #endif
951 /* mntent.h (Linux, GNU, NSS) {{{2 */
952 #ifdef HAVE_MNTENT_H
954 #ifdef HAVE_LIBMOUNT
956 static GList *
957 _g_get_unix_mount_points (void)
959 struct libmnt_table *table = NULL;
960 struct libmnt_iter* iter = NULL;
961 struct libmnt_fs *fs = NULL;
962 GUnixMountPoint *mount_point = NULL;
963 GList *return_list = NULL;
965 table = mnt_new_table ();
966 if (mnt_table_parse_fstab (table, NULL) < 0)
967 goto out;
969 iter = mnt_new_iter (MNT_ITER_FORWARD);
970 while (mnt_table_next_fs (table, iter, &fs) == 0)
972 const char *device_path = NULL;
973 const char *mount_path = NULL;
974 const char *mount_fstype = NULL;
975 char *mount_options = NULL;
976 gboolean is_read_only = FALSE;
977 gboolean is_user_mountable = FALSE;
978 gboolean is_loopback = FALSE;
980 mount_path = mnt_fs_get_target (fs);
981 if ((strcmp (mount_path, "ignore") == 0) ||
982 (strcmp (mount_path, "swap") == 0) ||
983 (strcmp (mount_path, "none") == 0))
984 continue;
986 mount_fstype = mnt_fs_get_fstype (fs);
987 mount_options = mnt_fs_strdup_options (fs);
988 if (mount_options)
990 unsigned long mount_flags = 0;
991 unsigned long userspace_flags = 0;
993 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
994 mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
996 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
997 if (mount_flags & MS_BIND)
999 g_free (mount_options);
1000 continue;
1003 is_read_only = (mount_flags & MS_RDONLY) != 0;
1004 is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
1006 if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
1007 ((userspace_flags & MNT_MS_USER) &&
1008 (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
1009 (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
1010 (userspace_flags & MNT_MS_USERS) ||
1011 (userspace_flags & MNT_MS_OWNER))
1013 is_user_mountable = TRUE;
1017 device_path = mnt_fs_get_source (fs);
1018 if (g_strcmp0 (device_path, "/dev/root") == 0)
1019 device_path = _resolve_dev_root ();
1021 mount_point = create_unix_mount_point (device_path,
1022 mount_path,
1023 mount_fstype,
1024 mount_options,
1025 is_read_only,
1026 is_user_mountable,
1027 is_loopback);
1028 if (mount_options)
1029 g_free (mount_options);
1031 return_list = g_list_prepend (return_list, mount_point);
1033 mnt_free_iter (iter);
1035 out:
1036 mnt_free_table (table);
1038 return g_list_reverse (return_list);
1041 #else
1043 static GList *
1044 _g_get_unix_mount_points (void)
1046 #ifdef HAVE_GETMNTENT_R
1047 struct mntent ent;
1048 char buf[1024];
1049 #endif
1050 struct mntent *mntent;
1051 FILE *file;
1052 char *read_file;
1053 GUnixMountPoint *mount_point;
1054 GList *return_list;
1056 read_file = get_fstab_file ();
1058 file = setmntent (read_file, "r");
1059 if (file == NULL)
1060 return NULL;
1062 return_list = NULL;
1064 #ifdef HAVE_GETMNTENT_R
1065 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
1066 #else
1067 G_LOCK (getmntent);
1068 while ((mntent = getmntent (file)) != NULL)
1069 #endif
1071 const char *device_path = NULL;
1072 gboolean is_read_only = FALSE;
1073 gboolean is_user_mountable = FALSE;
1074 gboolean is_loopback = FALSE;
1076 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
1077 (strcmp (mntent->mnt_dir, "swap") == 0) ||
1078 (strcmp (mntent->mnt_dir, "none") == 0))
1079 continue;
1081 #ifdef HAVE_HASMNTOPT
1082 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1083 if (hasmntopt (mntent, "bind"))
1084 continue;
1085 #endif
1087 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1088 device_path = _resolve_dev_root ();
1089 else
1090 device_path = mntent->mnt_fsname;
1092 #ifdef HAVE_HASMNTOPT
1093 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1094 is_read_only = TRUE;
1096 if (hasmntopt (mntent, "loop") != NULL)
1097 is_loopback = TRUE;
1099 #endif
1101 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1102 #ifdef HAVE_HASMNTOPT
1103 || (hasmntopt (mntent, "user") != NULL
1104 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1105 || hasmntopt (mntent, "pamconsole") != NULL
1106 || hasmntopt (mntent, "users") != NULL
1107 || hasmntopt (mntent, "owner") != NULL
1108 #endif
1110 is_user_mountable = TRUE;
1112 mount_point = create_unix_mount_point (device_path,
1113 mntent->mnt_dir,
1114 mntent->mnt_type,
1115 mntent->mnt_opts,
1116 is_read_only,
1117 is_user_mountable,
1118 is_loopback);
1120 return_list = g_list_prepend (return_list, mount_point);
1123 endmntent (file);
1125 #ifndef HAVE_GETMNTENT_R
1126 G_UNLOCK (getmntent);
1127 #endif
1129 return g_list_reverse (return_list);
1132 #endif /* HAVE_LIBMOUNT */
1134 /* mnttab.h {{{2 */
1135 #elif defined (HAVE_SYS_MNTTAB_H)
1137 static GList *
1138 _g_get_unix_mount_points (void)
1140 struct mnttab mntent;
1141 FILE *file;
1142 char *read_file;
1143 GUnixMountPoint *mount_point;
1144 GList *return_list;
1146 read_file = get_fstab_file ();
1148 file = setmntent (read_file, "r");
1149 if (file == NULL)
1150 return NULL;
1152 return_list = NULL;
1154 G_LOCK (getmntent);
1155 while (! getmntent (file, &mntent))
1157 gboolean is_read_only = FALSE;
1158 gboolean is_user_mountable = FALSE;
1159 gboolean is_loopback = FALSE;
1161 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1162 (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1163 (strcmp (mntent.mnt_mountp, "none") == 0))
1164 continue;
1166 #ifdef HAVE_HASMNTOPT
1167 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1168 is_read_only = TRUE;
1170 if (hasmntopt (&mntent, "lofs") != NULL)
1171 is_loopback = TRUE;
1172 #endif
1174 if ((mntent.mnt_fstype != NULL)
1175 #ifdef HAVE_HASMNTOPT
1176 || (hasmntopt (&mntent, "user") != NULL
1177 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1178 || hasmntopt (&mntent, "pamconsole") != NULL
1179 || hasmntopt (&mntent, "users") != NULL
1180 || hasmntopt (&mntent, "owner") != NULL
1181 #endif
1183 is_user_mountable = TRUE;
1185 mount_point = create_unix_mount_point (mntent.mnt_special,
1186 mntent.mnt_mountp,
1187 mntent.mnt_fstype,
1188 mntent.mnt_mntopts,
1189 is_read_only,
1190 is_user_mountable,
1191 is_loopback);
1193 return_list = g_list_prepend (return_list, mount_point);
1196 endmntent (file);
1197 G_UNLOCK (getmntent);
1199 return g_list_reverse (return_list);
1202 /* mntctl.h (AIX) {{{2 */
1203 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1205 /* functions to parse /etc/filesystems on aix */
1207 /* read character, ignoring comments (begin with '*', end with '\n' */
1208 static int
1209 aix_fs_getc (FILE *fd)
1211 int c;
1213 while ((c = getc (fd)) == '*')
1215 while (((c = getc (fd)) != '\n') && (c != EOF))
1220 /* eat all continuous spaces in a file */
1221 static int
1222 aix_fs_ignorespace (FILE *fd)
1224 int c;
1226 while ((c = aix_fs_getc (fd)) != EOF)
1228 if (!g_ascii_isspace (c))
1230 ungetc (c,fd);
1231 return c;
1235 return EOF;
1238 /* read one word from file */
1239 static int
1240 aix_fs_getword (FILE *fd,
1241 char *word)
1243 int c;
1245 aix_fs_ignorespace (fd);
1247 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1249 if (c == '"')
1251 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1252 *word++ = c;
1253 else
1254 *word++ = c;
1257 *word = 0;
1259 return c;
1262 typedef struct {
1263 char mnt_mount[PATH_MAX];
1264 char mnt_special[PATH_MAX];
1265 char mnt_fstype[16];
1266 char mnt_options[128];
1267 } AixMountTableEntry;
1269 /* read mount points properties */
1270 static int
1271 aix_fs_get (FILE *fd,
1272 AixMountTableEntry *prop)
1274 static char word[PATH_MAX] = { 0 };
1275 char value[PATH_MAX];
1277 /* read stanza */
1278 if (word[0] == 0)
1280 if (aix_fs_getword (fd, word) == EOF)
1281 return EOF;
1284 word[strlen(word) - 1] = 0;
1285 strcpy (prop->mnt_mount, word);
1287 /* read attributes and value */
1289 while (aix_fs_getword (fd, word) != EOF)
1291 /* test if is attribute or new stanza */
1292 if (word[strlen(word) - 1] == ':')
1293 return 0;
1295 /* read "=" */
1296 aix_fs_getword (fd, value);
1298 /* read value */
1299 aix_fs_getword (fd, value);
1301 if (strcmp (word, "dev") == 0)
1302 strcpy (prop->mnt_special, value);
1303 else if (strcmp (word, "vfs") == 0)
1304 strcpy (prop->mnt_fstype, value);
1305 else if (strcmp (word, "options") == 0)
1306 strcpy(prop->mnt_options, value);
1309 return 0;
1312 static GList *
1313 _g_get_unix_mount_points (void)
1315 struct mntent *mntent;
1316 FILE *file;
1317 char *read_file;
1318 GUnixMountPoint *mount_point;
1319 AixMountTableEntry mntent;
1320 GList *return_list;
1322 read_file = get_fstab_file ();
1324 file = setmntent (read_file, "r");
1325 if (file == NULL)
1326 return NULL;
1328 return_list = NULL;
1330 while (!aix_fs_get (file, &mntent))
1332 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1334 mount_point = create_unix_mount_point (mntent.mnt_special,
1335 mntent.mnt_mount,
1336 mntent.mnt_fstype,
1337 mntent.mnt_options,
1338 TRUE,
1339 TRUE,
1340 FALSE);
1342 return_list = g_list_prepend (return_list, mount_point);
1346 endmntent (file);
1348 return g_list_reverse (return_list);
1351 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1353 static GList *
1354 _g_get_unix_mount_points (void)
1356 struct fstab *fstab = NULL;
1357 GUnixMountPoint *mount_point;
1358 GList *return_list;
1359 #ifdef HAVE_SYS_SYSCTL_H
1360 int usermnt = 0;
1361 struct stat sb;
1362 #endif
1364 if (!setfsent ())
1365 return NULL;
1367 return_list = NULL;
1369 #ifdef HAVE_SYS_SYSCTL_H
1370 #if defined(HAVE_SYSCTLBYNAME)
1372 size_t len = sizeof(usermnt);
1374 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1376 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1378 int mib[2];
1379 size_t len = sizeof(usermnt);
1381 mib[0] = CTL_VFS;
1382 mib[1] = VFS_USERMOUNT;
1383 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1385 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1387 int mib[2];
1388 size_t len = sizeof(usermnt);
1390 mib[0] = CTL_KERN;
1391 mib[1] = KERN_USERMOUNT;
1392 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1394 #endif
1395 #endif
1397 while ((fstab = getfsent ()) != NULL)
1399 gboolean is_read_only = FALSE;
1400 gboolean is_user_mountable = FALSE;
1402 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1403 continue;
1405 if (strcmp (fstab->fs_type, "ro") == 0)
1406 is_read_only = TRUE;
1408 #ifdef HAVE_SYS_SYSCTL_H
1409 if (usermnt != 0)
1411 uid_t uid = getuid ();
1412 if (stat (fstab->fs_file, &sb) == 0)
1414 if (uid == 0 || sb.st_uid == uid)
1415 is_user_mountable = TRUE;
1418 #endif
1420 mount_point = create_unix_mount_point (fstab->fs_spec,
1421 fstab->fs_file,
1422 fstab->fs_vfstype,
1423 fstab->fs_mntops,
1424 is_read_only,
1425 is_user_mountable,
1426 FALSE);
1428 return_list = g_list_prepend (return_list, mount_point);
1431 endfsent ();
1433 return g_list_reverse (return_list);
1435 /* Interix {{{2 */
1436 #elif defined(__INTERIX)
1437 static GList *
1438 _g_get_unix_mount_points (void)
1440 return _g_get_unix_mounts ();
1443 /* Common code {{{2 */
1444 #else
1445 #error No g_get_mount_table() implementation for system
1446 #endif
1448 static guint64
1449 get_mounts_timestamp (void)
1451 const char *monitor_file;
1452 struct stat buf;
1454 monitor_file = get_mtab_monitor_file ();
1455 /* Don't return mtime for /proc/ files */
1456 if (monitor_file && !g_str_has_prefix (monitor_file, "/proc/"))
1458 if (stat (monitor_file, &buf) == 0)
1459 return (guint64)buf.st_mtime;
1461 else if (proc_mounts_watch_is_running ())
1463 /* it's being monitored by poll, so return mount_poller_time */
1464 return mount_poller_time;
1466 else
1468 /* Case of /proc/ file not being monitored - Be on the safe side and
1469 * send a new timestamp to force g_unix_mounts_changed_since() to
1470 * return TRUE so any application caches depending on it (like eg.
1471 * the one in GIO) get invalidated and don't hold possibly outdated
1472 * data - see Bug 787731 */
1473 return (guint64) g_get_monotonic_time ();
1475 return 0;
1478 static guint64
1479 get_mount_points_timestamp (void)
1481 const char *monitor_file;
1482 struct stat buf;
1484 monitor_file = get_fstab_file ();
1485 if (monitor_file)
1487 if (stat (monitor_file, &buf) == 0)
1488 return (guint64)buf.st_mtime;
1490 return 0;
1494 * g_unix_mounts_get:
1495 * @time_read: (out) (optional): guint64 to contain a timestamp, or %NULL
1497 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1498 * If @time_read is set, it will be filled with the mount
1499 * timestamp, allowing for checking if the mounts have changed
1500 * with g_unix_mounts_changed_since().
1502 * Returns: (element-type GUnixMountEntry) (transfer full):
1503 * a #GList of the UNIX mounts.
1505 GList *
1506 g_unix_mounts_get (guint64 *time_read)
1508 if (time_read)
1509 *time_read = get_mounts_timestamp ();
1511 return _g_get_unix_mounts ();
1515 * g_unix_mount_at:
1516 * @mount_path: (type filename): path for a possible unix mount.
1517 * @time_read: (out) (optional): guint64 to contain a timestamp.
1519 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1520 * is set, it will be filled with a unix timestamp for checking
1521 * if the mounts have changed since with g_unix_mounts_changed_since().
1523 * Returns: (transfer full): a #GUnixMountEntry.
1525 GUnixMountEntry *
1526 g_unix_mount_at (const char *mount_path,
1527 guint64 *time_read)
1529 GList *mounts, *l;
1530 GUnixMountEntry *mount_entry, *found;
1532 mounts = g_unix_mounts_get (time_read);
1534 found = NULL;
1535 for (l = mounts; l != NULL; l = l->next)
1537 mount_entry = l->data;
1539 if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1540 found = mount_entry;
1541 else
1542 g_unix_mount_free (mount_entry);
1544 g_list_free (mounts);
1546 return found;
1550 * g_unix_mount_for:
1551 * @file_path: (type filename): file path on some unix mount.
1552 * @time_read: (out) (optional): guint64 to contain a timestamp.
1554 * Gets a #GUnixMountEntry for a given file path. If @time_read
1555 * is set, it will be filled with a unix timestamp for checking
1556 * if the mounts have changed since with g_unix_mounts_changed_since().
1558 * Returns: (transfer full): a #GUnixMountEntry.
1560 * Since: 2.52
1562 GUnixMountEntry *
1563 g_unix_mount_for (const char *file_path,
1564 guint64 *time_read)
1566 GUnixMountEntry *entry;
1568 g_return_val_if_fail (file_path != NULL, NULL);
1570 entry = g_unix_mount_at (file_path, time_read);
1571 if (entry == NULL)
1573 char *topdir;
1575 topdir = _g_local_file_find_topdir_for (file_path);
1576 if (topdir != NULL)
1578 entry = g_unix_mount_at (topdir, time_read);
1579 g_free (topdir);
1583 return entry;
1587 * g_unix_mount_points_get:
1588 * @time_read: (out) (optional): guint64 to contain a timestamp.
1590 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1591 * If @time_read is set, it will be filled with the mount timestamp,
1592 * allowing for checking if the mounts have changed with
1593 * g_unix_mount_points_changed_since().
1595 * Returns: (element-type GUnixMountPoint) (transfer full):
1596 * a #GList of the UNIX mountpoints.
1598 GList *
1599 g_unix_mount_points_get (guint64 *time_read)
1601 if (time_read)
1602 *time_read = get_mount_points_timestamp ();
1604 return _g_get_unix_mount_points ();
1608 * g_unix_mounts_changed_since:
1609 * @time: guint64 to contain a timestamp.
1611 * Checks if the unix mounts have changed since a given unix time.
1613 * Returns: %TRUE if the mounts have changed since @time.
1615 gboolean
1616 g_unix_mounts_changed_since (guint64 time)
1618 return get_mounts_timestamp () != time;
1622 * g_unix_mount_points_changed_since:
1623 * @time: guint64 to contain a timestamp.
1625 * Checks if the unix mount points have changed since a given unix time.
1627 * Returns: %TRUE if the mount points have changed since @time.
1629 gboolean
1630 g_unix_mount_points_changed_since (guint64 time)
1632 return get_mount_points_timestamp () != time;
1635 /* GUnixMountMonitor {{{1 */
1637 enum {
1638 MOUNTS_CHANGED,
1639 MOUNTPOINTS_CHANGED,
1640 LAST_SIGNAL
1643 static guint signals[LAST_SIGNAL];
1645 struct _GUnixMountMonitor {
1646 GObject parent;
1648 GMainContext *context;
1651 struct _GUnixMountMonitorClass {
1652 GObjectClass parent_class;
1656 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT)
1658 static GContextSpecificGroup mount_monitor_group;
1659 static GFileMonitor *fstab_monitor;
1660 static GFileMonitor *mtab_monitor;
1661 static GSource *proc_mounts_watch_source;
1662 static GList *mount_poller_mounts;
1663 static guint mtab_file_changed_id;
1665 static gboolean
1666 proc_mounts_watch_is_running (void)
1668 return proc_mounts_watch_source != NULL &&
1669 !g_source_is_destroyed (proc_mounts_watch_source);
1672 static void
1673 fstab_file_changed (GFileMonitor *monitor,
1674 GFile *file,
1675 GFile *other_file,
1676 GFileMonitorEvent event_type,
1677 gpointer user_data)
1679 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1680 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1681 event_type != G_FILE_MONITOR_EVENT_DELETED)
1682 return;
1684 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1687 static gboolean
1688 mtab_file_changed_cb (gpointer user_data)
1690 mtab_file_changed_id = 0;
1691 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1693 return G_SOURCE_REMOVE;
1696 static void
1697 mtab_file_changed (GFileMonitor *monitor,
1698 GFile *file,
1699 GFile *other_file,
1700 GFileMonitorEvent event_type,
1701 gpointer user_data)
1703 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1704 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1705 event_type != G_FILE_MONITOR_EVENT_DELETED)
1706 return;
1708 /* Skip accumulated events from file monitor which we are not able to handle
1709 * in a real time instead of emitting mounts_changed signal several times.
1710 * This should behave equally to GIOChannel based monitoring. See Bug 792235.
1712 if (mtab_file_changed_id > 0)
1713 return;
1715 mtab_file_changed_id = g_idle_add (mtab_file_changed_cb, NULL);
1718 static gboolean
1719 proc_mounts_changed (GIOChannel *channel,
1720 GIOCondition cond,
1721 gpointer user_data)
1723 if (cond & G_IO_ERR)
1725 mount_poller_time = (guint64) g_get_monotonic_time ();
1726 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1729 return TRUE;
1732 static gboolean
1733 mount_change_poller (gpointer user_data)
1735 GList *current_mounts, *new_it, *old_it;
1736 gboolean has_changed = FALSE;
1738 current_mounts = _g_get_unix_mounts ();
1740 for ( new_it = current_mounts, old_it = mount_poller_mounts;
1741 new_it != NULL && old_it != NULL;
1742 new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1744 if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1746 has_changed = TRUE;
1747 break;
1750 if (!(new_it == NULL && old_it == NULL))
1751 has_changed = TRUE;
1753 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1755 mount_poller_mounts = current_mounts;
1757 if (has_changed)
1759 mount_poller_time = (guint64) g_get_monotonic_time ();
1760 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1763 return TRUE;
1767 static void
1768 mount_monitor_stop (void)
1770 if (fstab_monitor)
1772 g_file_monitor_cancel (fstab_monitor);
1773 g_object_unref (fstab_monitor);
1776 if (proc_mounts_watch_source != NULL)
1778 g_source_destroy (proc_mounts_watch_source);
1779 proc_mounts_watch_source = NULL;
1782 if (mtab_monitor)
1784 g_file_monitor_cancel (mtab_monitor);
1785 g_object_unref (mtab_monitor);
1788 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1791 static void
1792 mount_monitor_start (void)
1794 GFile *file;
1796 if (get_fstab_file () != NULL)
1798 file = g_file_new_for_path (get_fstab_file ());
1799 fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1800 g_object_unref (file);
1802 g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1805 if (get_mtab_monitor_file () != NULL)
1807 const gchar *mtab_path;
1809 mtab_path = get_mtab_monitor_file ();
1810 /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1811 * See 'man proc' for more details.
1813 if (g_str_has_prefix (mtab_path, "/proc/"))
1815 GIOChannel *proc_mounts_channel;
1816 GError *error = NULL;
1817 proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
1818 if (proc_mounts_channel == NULL)
1820 g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1821 error->message, g_quark_to_string (error->domain), error->code);
1822 g_error_free (error);
1824 else
1826 proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1827 g_source_set_callback (proc_mounts_watch_source,
1828 (GSourceFunc) proc_mounts_changed,
1829 NULL, NULL);
1830 g_source_attach (proc_mounts_watch_source,
1831 g_main_context_get_thread_default ());
1832 g_source_unref (proc_mounts_watch_source);
1833 g_io_channel_unref (proc_mounts_channel);
1836 else
1838 file = g_file_new_for_path (mtab_path);
1839 mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1840 g_object_unref (file);
1841 g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
1844 else
1846 proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1847 mount_poller_mounts = _g_get_unix_mounts ();
1848 mount_poller_time = (guint64)g_get_monotonic_time ();
1849 g_source_set_callback (proc_mounts_watch_source,
1850 mount_change_poller,
1851 NULL, NULL);
1852 g_source_attach (proc_mounts_watch_source,
1853 g_main_context_get_thread_default ());
1854 g_source_unref (proc_mounts_watch_source);
1858 static void
1859 g_unix_mount_monitor_finalize (GObject *object)
1861 GUnixMountMonitor *monitor;
1863 monitor = G_UNIX_MOUNT_MONITOR (object);
1865 g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
1867 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1870 static void
1871 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1873 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1875 gobject_class->finalize = g_unix_mount_monitor_finalize;
1878 * GUnixMountMonitor::mounts-changed:
1879 * @monitor: the object on which the signal is emitted
1881 * Emitted when the unix mounts have changed.
1883 signals[MOUNTS_CHANGED] =
1884 g_signal_new (I_("mounts-changed"),
1885 G_TYPE_FROM_CLASS (klass),
1886 G_SIGNAL_RUN_LAST,
1888 NULL, NULL,
1889 g_cclosure_marshal_VOID__VOID,
1890 G_TYPE_NONE, 0);
1893 * GUnixMountMonitor::mountpoints-changed:
1894 * @monitor: the object on which the signal is emitted
1896 * Emitted when the unix mount points have changed.
1898 signals[MOUNTPOINTS_CHANGED] =
1899 g_signal_new (I_("mountpoints-changed"),
1900 G_TYPE_FROM_CLASS (klass),
1901 G_SIGNAL_RUN_LAST,
1903 NULL, NULL,
1904 g_cclosure_marshal_VOID__VOID,
1905 G_TYPE_NONE, 0);
1908 static void
1909 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1914 * g_unix_mount_monitor_set_rate_limit:
1915 * @mount_monitor: a #GUnixMountMonitor
1916 * @limit_msec: a integer with the limit in milliseconds to
1917 * poll for changes.
1919 * This function does nothing.
1921 * Before 2.44, this was a partially-effective way of controlling the
1922 * rate at which events would be reported under some uncommon
1923 * circumstances. Since @mount_monitor is a singleton, it also meant
1924 * that calling this function would have side effects for other users of
1925 * the monitor.
1927 * Since: 2.18
1929 * Deprecated:2.44:This function does nothing. Don't call it.
1931 void
1932 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1933 gint limit_msec)
1938 * g_unix_mount_monitor_get:
1940 * Gets the #GUnixMountMonitor for the current thread-default main
1941 * context.
1943 * The mount monitor can be used to monitor for changes to the list of
1944 * mounted filesystems as well as the list of mount points (ie: fstab
1945 * entries).
1947 * You must only call g_object_unref() on the return value from under
1948 * the same main context as you called this function.
1950 * Returns: (transfer full): the #GUnixMountMonitor.
1952 * Since: 2.44
1954 GUnixMountMonitor *
1955 g_unix_mount_monitor_get (void)
1957 return g_context_specific_group_get (&mount_monitor_group,
1958 G_TYPE_UNIX_MOUNT_MONITOR,
1959 G_STRUCT_OFFSET(GUnixMountMonitor, context),
1960 mount_monitor_start);
1964 * g_unix_mount_monitor_new:
1966 * Deprecated alias for g_unix_mount_monitor_get().
1968 * This function was never a true constructor, which is why it was
1969 * renamed.
1971 * Returns: a #GUnixMountMonitor.
1973 * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
1975 GUnixMountMonitor *
1976 g_unix_mount_monitor_new (void)
1978 return g_unix_mount_monitor_get ();
1981 /* GUnixMount {{{1 */
1983 * g_unix_mount_free:
1984 * @mount_entry: a #GUnixMountEntry.
1986 * Frees a unix mount.
1988 void
1989 g_unix_mount_free (GUnixMountEntry *mount_entry)
1991 g_return_if_fail (mount_entry != NULL);
1993 g_free (mount_entry->mount_path);
1994 g_free (mount_entry->device_path);
1995 g_free (mount_entry->filesystem_type);
1996 g_free (mount_entry->options);
1997 g_free (mount_entry);
2001 * g_unix_mount_copy:
2002 * @mount_entry: a #GUnixMountEntry.
2004 * Makes a copy of @mount_entry.
2006 * Returns: (transfer full): a new #GUnixMountEntry
2008 * Since: 2.54
2010 GUnixMountEntry *
2011 g_unix_mount_copy (GUnixMountEntry *mount_entry)
2013 GUnixMountEntry *copy;
2015 g_return_val_if_fail (mount_entry != NULL, NULL);
2017 copy = g_new0 (GUnixMountEntry, 1);
2018 copy->mount_path = g_strdup (mount_entry->mount_path);
2019 copy->device_path = g_strdup (mount_entry->device_path);
2020 copy->filesystem_type = g_strdup (mount_entry->filesystem_type);
2021 copy->options = g_strdup (mount_entry->options);
2022 copy->is_read_only = mount_entry->is_read_only;
2023 copy->is_system_internal = mount_entry->is_system_internal;
2025 return copy;
2029 * g_unix_mount_point_free:
2030 * @mount_point: unix mount point to free.
2032 * Frees a unix mount point.
2034 void
2035 g_unix_mount_point_free (GUnixMountPoint *mount_point)
2037 g_return_if_fail (mount_point != NULL);
2039 g_free (mount_point->mount_path);
2040 g_free (mount_point->device_path);
2041 g_free (mount_point->filesystem_type);
2042 g_free (mount_point->options);
2043 g_free (mount_point);
2047 * g_unix_mount_point_copy:
2048 * @mount_point: a #GUnixMountPoint.
2050 * Makes a copy of @mount_point.
2052 * Returns: (transfer full): a new #GUnixMountPoint
2054 * Since: 2.54
2056 GUnixMountPoint*
2057 g_unix_mount_point_copy (GUnixMountPoint *mount_point)
2059 GUnixMountPoint *copy;
2061 g_return_val_if_fail (mount_point != NULL, NULL);
2063 copy = g_new0 (GUnixMountPoint, 1);
2064 copy->mount_path = g_strdup (mount_point->mount_path);
2065 copy->device_path = g_strdup (mount_point->device_path);
2066 copy->filesystem_type = g_strdup (mount_point->filesystem_type);
2067 copy->options = g_strdup (mount_point->options);
2068 copy->is_read_only = mount_point->is_read_only;
2069 copy->is_user_mountable = mount_point->is_user_mountable;
2070 copy->is_loopback = mount_point->is_loopback;
2072 return copy;
2076 * g_unix_mount_compare:
2077 * @mount1: first #GUnixMountEntry to compare.
2078 * @mount2: second #GUnixMountEntry to compare.
2080 * Compares two unix mounts.
2082 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2083 * or less than @mount2, respectively.
2085 gint
2086 g_unix_mount_compare (GUnixMountEntry *mount1,
2087 GUnixMountEntry *mount2)
2089 int res;
2091 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2093 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2094 if (res != 0)
2095 return res;
2097 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2098 if (res != 0)
2099 return res;
2101 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2102 if (res != 0)
2103 return res;
2105 res = g_strcmp0 (mount1->options, mount2->options);
2106 if (res != 0)
2107 return res;
2109 res = mount1->is_read_only - mount2->is_read_only;
2110 if (res != 0)
2111 return res;
2113 return 0;
2117 * g_unix_mount_get_mount_path:
2118 * @mount_entry: input #GUnixMountEntry to get the mount path for.
2120 * Gets the mount path for a unix mount.
2122 * Returns: (type filename): the mount path for @mount_entry.
2124 const gchar *
2125 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
2127 g_return_val_if_fail (mount_entry != NULL, NULL);
2129 return mount_entry->mount_path;
2133 * g_unix_mount_get_device_path:
2134 * @mount_entry: a #GUnixMount.
2136 * Gets the device path for a unix mount.
2138 * Returns: (type filename): a string containing the device path.
2140 const gchar *
2141 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
2143 g_return_val_if_fail (mount_entry != NULL, NULL);
2145 return mount_entry->device_path;
2149 * g_unix_mount_get_fs_type:
2150 * @mount_entry: a #GUnixMount.
2152 * Gets the filesystem type for the unix mount.
2154 * Returns: a string containing the file system type.
2156 const gchar *
2157 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
2159 g_return_val_if_fail (mount_entry != NULL, NULL);
2161 return mount_entry->filesystem_type;
2165 * g_unix_mount_get_options:
2166 * @mount_entry: a #GUnixMountEntry.
2168 * Gets a comma-separated list of mount options for the unix mount. For example,
2169 * `rw,relatime,seclabel,data=ordered`.
2171 * This is similar to g_unix_mount_point_get_options(), but it takes
2172 * a #GUnixMountEntry as an argument.
2174 * Returns: (nullable): a string containing the options, or %NULL if not
2175 * available.
2177 * Since: 2.58
2179 const gchar *
2180 g_unix_mount_get_options (GUnixMountEntry *mount_entry)
2182 g_return_val_if_fail (mount_entry != NULL, NULL);
2184 return mount_entry->options;
2188 * g_unix_mount_is_readonly:
2189 * @mount_entry: a #GUnixMount.
2191 * Checks if a unix mount is mounted read only.
2193 * Returns: %TRUE if @mount_entry is read only.
2195 gboolean
2196 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
2198 g_return_val_if_fail (mount_entry != NULL, FALSE);
2200 return mount_entry->is_read_only;
2204 * g_unix_mount_is_system_internal:
2205 * @mount_entry: a #GUnixMount.
2207 * Checks if a Unix mount is a system mount. This is the Boolean OR of
2208 * g_unix_is_system_fs_type(), g_unix_is_system_device_path() and
2209 * g_unix_is_mount_path_system_internal() on @mount_entry’s properties.
2211 * The definition of what a ‘system’ mount entry is may change over time as new
2212 * file system types and device paths are ignored.
2214 * Returns: %TRUE if the unix mount is for a system path.
2216 gboolean
2217 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
2219 g_return_val_if_fail (mount_entry != NULL, FALSE);
2221 return mount_entry->is_system_internal;
2224 /* GUnixMountPoint {{{1 */
2226 * g_unix_mount_point_compare:
2227 * @mount1: a #GUnixMount.
2228 * @mount2: a #GUnixMount.
2230 * Compares two unix mount points.
2232 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2233 * or less than @mount2, respectively.
2235 gint
2236 g_unix_mount_point_compare (GUnixMountPoint *mount1,
2237 GUnixMountPoint *mount2)
2239 int res;
2241 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2243 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2244 if (res != 0)
2245 return res;
2247 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2248 if (res != 0)
2249 return res;
2251 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2252 if (res != 0)
2253 return res;
2255 res = g_strcmp0 (mount1->options, mount2->options);
2256 if (res != 0)
2257 return res;
2259 res = mount1->is_read_only - mount2->is_read_only;
2260 if (res != 0)
2261 return res;
2263 res = mount1->is_user_mountable - mount2->is_user_mountable;
2264 if (res != 0)
2265 return res;
2267 res = mount1->is_loopback - mount2->is_loopback;
2268 if (res != 0)
2269 return res;
2271 return 0;
2275 * g_unix_mount_point_get_mount_path:
2276 * @mount_point: a #GUnixMountPoint.
2278 * Gets the mount path for a unix mount point.
2280 * Returns: (type filename): a string containing the mount path.
2282 const gchar *
2283 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2285 g_return_val_if_fail (mount_point != NULL, NULL);
2287 return mount_point->mount_path;
2291 * g_unix_mount_point_get_device_path:
2292 * @mount_point: a #GUnixMountPoint.
2294 * Gets the device path for a unix mount point.
2296 * Returns: (type filename): a string containing the device path.
2298 const gchar *
2299 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2301 g_return_val_if_fail (mount_point != NULL, NULL);
2303 return mount_point->device_path;
2307 * g_unix_mount_point_get_fs_type:
2308 * @mount_point: a #GUnixMountPoint.
2310 * Gets the file system type for the mount point.
2312 * Returns: a string containing the file system type.
2314 const gchar *
2315 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2317 g_return_val_if_fail (mount_point != NULL, NULL);
2319 return mount_point->filesystem_type;
2323 * g_unix_mount_point_get_options:
2324 * @mount_point: a #GUnixMountPoint.
2326 * Gets the options for the mount point.
2328 * Returns: a string containing the options.
2330 * Since: 2.32
2332 const gchar *
2333 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2335 g_return_val_if_fail (mount_point != NULL, NULL);
2337 return mount_point->options;
2341 * g_unix_mount_point_is_readonly:
2342 * @mount_point: a #GUnixMountPoint.
2344 * Checks if a unix mount point is read only.
2346 * Returns: %TRUE if a mount point is read only.
2348 gboolean
2349 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2351 g_return_val_if_fail (mount_point != NULL, FALSE);
2353 return mount_point->is_read_only;
2357 * g_unix_mount_point_is_user_mountable:
2358 * @mount_point: a #GUnixMountPoint.
2360 * Checks if a unix mount point is mountable by the user.
2362 * Returns: %TRUE if the mount point is user mountable.
2364 gboolean
2365 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2367 g_return_val_if_fail (mount_point != NULL, FALSE);
2369 return mount_point->is_user_mountable;
2373 * g_unix_mount_point_is_loopback:
2374 * @mount_point: a #GUnixMountPoint.
2376 * Checks if a unix mount point is a loopback device.
2378 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
2380 gboolean
2381 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2383 g_return_val_if_fail (mount_point != NULL, FALSE);
2385 return mount_point->is_loopback;
2388 static GUnixMountType
2389 guess_mount_type (const char *mount_path,
2390 const char *device_path,
2391 const char *filesystem_type)
2393 GUnixMountType type;
2394 char *basename;
2396 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2398 if ((strcmp (filesystem_type, "udf") == 0) ||
2399 (strcmp (filesystem_type, "iso9660") == 0) ||
2400 (strcmp (filesystem_type, "cd9660") == 0))
2401 type = G_UNIX_MOUNT_TYPE_CDROM;
2402 else if ((strcmp (filesystem_type, "nfs") == 0) ||
2403 (strcmp (filesystem_type, "nfs4") == 0))
2404 type = G_UNIX_MOUNT_TYPE_NFS;
2405 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
2406 g_str_has_prefix (device_path, "/dev/fd") ||
2407 g_str_has_prefix (device_path, "/dev/floppy"))
2408 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2409 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
2410 g_str_has_prefix (device_path, "/dev/acd") ||
2411 g_str_has_prefix (device_path, "/dev/cd"))
2412 type = G_UNIX_MOUNT_TYPE_CDROM;
2413 else if (g_str_has_prefix (device_path, "/vol/"))
2415 const char *name = mount_path + strlen ("/");
2417 if (g_str_has_prefix (name, "cdrom"))
2418 type = G_UNIX_MOUNT_TYPE_CDROM;
2419 else if (g_str_has_prefix (name, "floppy") ||
2420 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
2421 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2422 else if (g_str_has_prefix (name, "rmdisk"))
2423 type = G_UNIX_MOUNT_TYPE_ZIP;
2424 else if (g_str_has_prefix (name, "jaz"))
2425 type = G_UNIX_MOUNT_TYPE_JAZ;
2426 else if (g_str_has_prefix (name, "memstick"))
2427 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2429 else
2431 basename = g_path_get_basename (mount_path);
2433 if (g_str_has_prefix (basename, "cdr") ||
2434 g_str_has_prefix (basename, "cdwriter") ||
2435 g_str_has_prefix (basename, "burn") ||
2436 g_str_has_prefix (basename, "dvdr"))
2437 type = G_UNIX_MOUNT_TYPE_CDROM;
2438 else if (g_str_has_prefix (basename, "floppy"))
2439 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2440 else if (g_str_has_prefix (basename, "zip"))
2441 type = G_UNIX_MOUNT_TYPE_ZIP;
2442 else if (g_str_has_prefix (basename, "jaz"))
2443 type = G_UNIX_MOUNT_TYPE_JAZ;
2444 else if (g_str_has_prefix (basename, "camera"))
2445 type = G_UNIX_MOUNT_TYPE_CAMERA;
2446 else if (g_str_has_prefix (basename, "memstick") ||
2447 g_str_has_prefix (basename, "memory_stick") ||
2448 g_str_has_prefix (basename, "ram"))
2449 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2450 else if (g_str_has_prefix (basename, "compact_flash"))
2451 type = G_UNIX_MOUNT_TYPE_CF;
2452 else if (g_str_has_prefix (basename, "smart_media"))
2453 type = G_UNIX_MOUNT_TYPE_SM;
2454 else if (g_str_has_prefix (basename, "sd_mmc"))
2455 type = G_UNIX_MOUNT_TYPE_SDMMC;
2456 else if (g_str_has_prefix (basename, "ipod"))
2457 type = G_UNIX_MOUNT_TYPE_IPOD;
2459 g_free (basename);
2462 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2463 type = G_UNIX_MOUNT_TYPE_HD;
2465 return type;
2469 * g_unix_mount_guess_type:
2470 * @mount_entry: a #GUnixMount.
2472 * Guesses the type of a unix mount. If the mount type cannot be
2473 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2475 * Returns: a #GUnixMountType.
2477 static GUnixMountType
2478 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2480 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2481 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2482 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2483 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2485 return guess_mount_type (mount_entry->mount_path,
2486 mount_entry->device_path,
2487 mount_entry->filesystem_type);
2491 * g_unix_mount_point_guess_type:
2492 * @mount_point: a #GUnixMountPoint.
2494 * Guesses the type of a unix mount point.
2495 * If the mount type cannot be determined,
2496 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2498 * Returns: a #GUnixMountType.
2500 static GUnixMountType
2501 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2503 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2504 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2505 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2506 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2508 return guess_mount_type (mount_point->mount_path,
2509 mount_point->device_path,
2510 mount_point->filesystem_type);
2513 static const char *
2514 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2516 const char *icon_name;
2518 switch (type)
2520 case G_UNIX_MOUNT_TYPE_HD:
2521 if (is_mount_point)
2522 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2523 else
2524 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2525 break;
2526 case G_UNIX_MOUNT_TYPE_FLOPPY:
2527 case G_UNIX_MOUNT_TYPE_ZIP:
2528 case G_UNIX_MOUNT_TYPE_JAZ:
2529 if (is_mount_point)
2530 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2531 else
2532 icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2533 break;
2534 case G_UNIX_MOUNT_TYPE_CDROM:
2535 if (is_mount_point)
2536 icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2537 else
2538 icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2539 break;
2540 case G_UNIX_MOUNT_TYPE_NFS:
2541 icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2542 break;
2543 case G_UNIX_MOUNT_TYPE_MEMSTICK:
2544 if (is_mount_point)
2545 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2546 else
2547 icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2548 break;
2549 case G_UNIX_MOUNT_TYPE_CAMERA:
2550 if (is_mount_point)
2551 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2552 else
2553 icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2554 break;
2555 case G_UNIX_MOUNT_TYPE_IPOD:
2556 if (is_mount_point)
2557 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2558 else
2559 icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2560 break;
2561 case G_UNIX_MOUNT_TYPE_UNKNOWN:
2562 default:
2563 if (is_mount_point)
2564 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2565 else
2566 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2567 break;
2570 return icon_name;
2574 * g_unix_mount_guess_name:
2575 * @mount_entry: a #GUnixMountEntry
2577 * Guesses the name of a Unix mount.
2578 * The result is a translated string.
2580 * Returns: A newly allocated string that must
2581 * be freed with g_free()
2583 gchar *
2584 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2586 char *name;
2588 if (strcmp (mount_entry->mount_path, "/") == 0)
2589 name = g_strdup (_("Filesystem root"));
2590 else
2591 name = g_filename_display_basename (mount_entry->mount_path);
2593 return name;
2597 * g_unix_mount_guess_icon:
2598 * @mount_entry: a #GUnixMountEntry
2600 * Guesses the icon of a Unix mount.
2602 * Returns: (transfer full): a #GIcon
2604 GIcon *
2605 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2607 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2611 * g_unix_mount_guess_symbolic_icon:
2612 * @mount_entry: a #GUnixMountEntry
2614 * Guesses the symbolic icon of a Unix mount.
2616 * Returns: (transfer full): a #GIcon
2618 * Since: 2.34
2620 GIcon *
2621 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2623 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2627 * g_unix_mount_point_guess_name:
2628 * @mount_point: a #GUnixMountPoint
2630 * Guesses the name of a Unix mount point.
2631 * The result is a translated string.
2633 * Returns: A newly allocated string that must
2634 * be freed with g_free()
2636 gchar *
2637 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2639 char *name;
2641 if (strcmp (mount_point->mount_path, "/") == 0)
2642 name = g_strdup (_("Filesystem root"));
2643 else
2644 name = g_filename_display_basename (mount_point->mount_path);
2646 return name;
2650 * g_unix_mount_point_guess_icon:
2651 * @mount_point: a #GUnixMountPoint
2653 * Guesses the icon of a Unix mount point.
2655 * Returns: (transfer full): a #GIcon
2657 GIcon *
2658 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2660 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2664 * g_unix_mount_point_guess_symbolic_icon:
2665 * @mount_point: a #GUnixMountPoint
2667 * Guesses the symbolic icon of a Unix mount point.
2669 * Returns: (transfer full): a #GIcon
2671 * Since: 2.34
2673 GIcon *
2674 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2676 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2680 * g_unix_mount_guess_can_eject:
2681 * @mount_entry: a #GUnixMountEntry
2683 * Guesses whether a Unix mount can be ejected.
2685 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2687 gboolean
2688 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2690 GUnixMountType guessed_type;
2692 guessed_type = g_unix_mount_guess_type (mount_entry);
2693 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2694 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2695 return TRUE;
2697 return FALSE;
2701 * g_unix_mount_guess_should_display:
2702 * @mount_entry: a #GUnixMountEntry
2704 * Guesses whether a Unix mount should be displayed in the UI.
2706 * Returns: %TRUE if @mount_entry is deemed to be displayable.
2708 gboolean
2709 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2711 const char *mount_path;
2712 const gchar *user_name;
2713 gsize user_name_len;
2715 /* Never display internal mountpoints */
2716 if (g_unix_mount_is_system_internal (mount_entry))
2717 return FALSE;
2719 /* Only display things in /media (which are generally user mountable)
2720 and home dir (fuse stuff) and /run/media/$USER */
2721 mount_path = mount_entry->mount_path;
2722 if (mount_path != NULL)
2724 const gboolean running_as_root = (getuid () == 0);
2725 gboolean is_in_runtime_dir = FALSE;
2727 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2728 if (g_strstr_len (mount_path, -1, "/.") != NULL)
2729 return FALSE;
2731 /* Check /run/media/$USER/. If running as root, display any mounts below
2732 * /run/media/. */
2733 if (running_as_root)
2735 if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0)
2736 is_in_runtime_dir = TRUE;
2738 else
2740 user_name = g_get_user_name ();
2741 user_name_len = strlen (user_name);
2742 if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0 &&
2743 strncmp (mount_path + strlen ("/run/media/"), user_name, user_name_len) == 0 &&
2744 mount_path[strlen ("/run/media/") + user_name_len] == '/')
2745 is_in_runtime_dir = TRUE;
2748 if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2750 char *path;
2751 /* Avoid displaying mounts that are not accessible to the user.
2753 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2754 * want to avoid g_access() for mount points which can potentially
2755 * block or fail stat()'ing, such as network mounts.
2757 path = g_path_get_dirname (mount_path);
2758 if (g_str_has_prefix (path, "/media/"))
2760 if (g_access (path, R_OK|X_OK) != 0)
2762 g_free (path);
2763 return FALSE;
2766 g_free (path);
2768 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2770 struct stat st;
2771 if (g_stat (mount_entry->device_path, &st) == 0 &&
2772 S_ISBLK(st.st_mode) &&
2773 g_access (mount_path, R_OK|X_OK) != 0)
2774 return FALSE;
2776 return TRUE;
2779 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
2780 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2781 return TRUE;
2784 return FALSE;
2788 * g_unix_mount_point_guess_can_eject:
2789 * @mount_point: a #GUnixMountPoint
2791 * Guesses whether a Unix mount point can be ejected.
2793 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2795 gboolean
2796 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2798 GUnixMountType guessed_type;
2800 guessed_type = g_unix_mount_point_guess_type (mount_point);
2801 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2802 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2803 return TRUE;
2805 return FALSE;
2808 /* Utility functions {{{1 */
2810 #ifdef HAVE_MNTENT_H
2811 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2812 static void
2813 _canonicalize_filename (gchar *filename)
2815 gchar *p, *q;
2816 gboolean last_was_slash = FALSE;
2818 p = filename;
2819 q = filename;
2821 while (*p)
2823 if (*p == G_DIR_SEPARATOR)
2825 if (!last_was_slash)
2826 *q++ = G_DIR_SEPARATOR;
2828 last_was_slash = TRUE;
2830 else
2832 if (last_was_slash && *p == '.')
2834 if (*(p + 1) == G_DIR_SEPARATOR ||
2835 *(p + 1) == '\0')
2837 if (*(p + 1) == '\0')
2838 break;
2840 p += 1;
2842 else if (*(p + 1) == '.' &&
2843 (*(p + 2) == G_DIR_SEPARATOR ||
2844 *(p + 2) == '\0'))
2846 if (q > filename + 1)
2848 q--;
2849 while (q > filename + 1 &&
2850 *(q - 1) != G_DIR_SEPARATOR)
2851 q--;
2854 if (*(p + 2) == '\0')
2855 break;
2857 p += 2;
2859 else
2861 *q++ = *p;
2862 last_was_slash = FALSE;
2865 else
2867 *q++ = *p;
2868 last_was_slash = FALSE;
2872 p++;
2875 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2876 q--;
2878 *q = '\0';
2881 static char *
2882 _resolve_symlink (const char *file)
2884 GError *error;
2885 char *dir;
2886 char *link;
2887 char *f;
2888 char *f1;
2890 f = g_strdup (file);
2892 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
2894 link = g_file_read_link (f, &error);
2895 if (link == NULL)
2897 g_error_free (error);
2898 g_free (f);
2899 f = NULL;
2900 goto out;
2903 dir = g_path_get_dirname (f);
2904 f1 = g_strdup_printf ("%s/%s", dir, link);
2905 g_free (dir);
2906 g_free (link);
2907 g_free (f);
2908 f = f1;
2911 out:
2912 if (f != NULL)
2913 _canonicalize_filename (f);
2914 return f;
2917 static const char *
2918 _resolve_dev_root (void)
2920 static gboolean have_real_dev_root = FALSE;
2921 static char real_dev_root[256];
2922 struct stat statbuf;
2924 /* see if it's cached already */
2925 if (have_real_dev_root)
2926 goto found;
2928 /* otherwise we're going to find it right away.. */
2929 have_real_dev_root = TRUE;
2931 if (stat ("/dev/root", &statbuf) == 0)
2933 if (! S_ISLNK (statbuf.st_mode))
2935 dev_t root_dev = statbuf.st_dev;
2936 FILE *f;
2938 /* see if device with similar major:minor as /dev/root is mention
2939 * in /etc/mtab (it usually is)
2941 f = fopen ("/etc/mtab", "r");
2942 if (f != NULL)
2944 struct mntent *entp;
2945 #ifdef HAVE_GETMNTENT_R
2946 struct mntent ent;
2947 char buf[1024];
2948 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
2950 #else
2951 G_LOCK (getmntent);
2952 while ((entp = getmntent (f)) != NULL)
2954 #endif
2955 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2956 statbuf.st_dev == root_dev)
2958 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2959 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2960 fclose (f);
2961 goto found;
2965 endmntent (f);
2967 #ifndef HAVE_GETMNTENT_R
2968 G_UNLOCK (getmntent);
2969 #endif
2972 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2975 else
2977 char *resolved;
2978 resolved = _resolve_symlink ("/dev/root");
2979 if (resolved != NULL)
2981 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2982 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2983 g_free (resolved);
2984 goto found;
2989 /* bah sucks.. */
2990 strcpy (real_dev_root, "/dev/root");
2992 found:
2993 return real_dev_root;
2995 #endif
2997 /* Epilogue {{{1 */
2998 /* vim:set foldmethod=marker: */