build: Enable -fno-strict-aliasing
[glib.git] / gio / gunixmounts.c
blobfc37129870e6041afe666ec36eeddb151926eb86
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 gboolean is_read_only;
130 gboolean is_system_internal;
133 G_DEFINE_BOXED_TYPE (GUnixMountEntry, g_unix_mount_entry,
134 g_unix_mount_copy, g_unix_mount_free)
136 struct _GUnixMountPoint {
137 char *mount_path;
138 char *device_path;
139 char *filesystem_type;
140 char *options;
141 gboolean is_read_only;
142 gboolean is_user_mountable;
143 gboolean is_loopback;
146 G_DEFINE_BOXED_TYPE (GUnixMountPoint, g_unix_mount_point,
147 g_unix_mount_point_copy, g_unix_mount_point_free)
149 static GList *_g_get_unix_mounts (void);
150 static GList *_g_get_unix_mount_points (void);
151 static gboolean proc_mounts_watch_is_running (void);
153 static guint64 mount_poller_time = 0;
155 #ifdef HAVE_SYS_MNTTAB_H
156 #define MNTOPT_RO "ro"
157 #endif
159 #ifdef HAVE_MNTENT_H
160 #include <mntent.h>
161 #ifdef HAVE_LIBMOUNT
162 #include <libmount/libmount.h>
163 #endif
164 #elif defined (HAVE_SYS_MNTTAB_H)
165 #include <sys/mnttab.h>
166 #endif
168 #ifdef HAVE_SYS_VFSTAB_H
169 #include <sys/vfstab.h>
170 #endif
172 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
173 #include <sys/mntctl.h>
174 #include <sys/vfs.h>
175 #include <sys/vmount.h>
176 #include <fshelp.h>
177 #endif
179 #if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
180 #include <sys/param.h>
181 #include <sys/ucred.h>
182 #include <sys/mount.h>
183 #include <fstab.h>
184 #ifdef HAVE_SYS_SYSCTL_H
185 #include <sys/sysctl.h>
186 #endif
187 #endif
189 #ifndef HAVE_SETMNTENT
190 #define setmntent(f,m) fopen(f,m)
191 #endif
192 #ifndef HAVE_ENDMNTENT
193 #define endmntent(f) fclose(f)
194 #endif
196 static gboolean
197 is_in (const char *value, const char *set[])
199 int i;
200 for (i = 0; set[i] != NULL; i++)
202 if (strcmp (set[i], value) == 0)
203 return TRUE;
205 return FALSE;
209 * g_unix_is_mount_path_system_internal:
210 * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr`
212 * Determines if @mount_path is considered an implementation of the
213 * OS. This is primarily used for hiding mountable and mounted volumes
214 * that only are used in the OS and has little to no relevance to the
215 * casual user.
217 * Returns: %TRUE if @mount_path is considered an implementation detail
218 * of the OS.
220 gboolean
221 g_unix_is_mount_path_system_internal (const char *mount_path)
223 const char *ignore_mountpoints[] = {
224 /* Includes all FHS 2.3 toplevel dirs and other specialized
225 * directories that we want to hide from the user.
227 "/", /* we already have "Filesystem root" in Nautilus */
228 "/bin",
229 "/boot",
230 "/compat/linux/proc",
231 "/compat/linux/sys",
232 "/dev",
233 "/etc",
234 "/home",
235 "/lib",
236 "/lib64",
237 "/libexec",
238 "/live/cow",
239 "/live/image",
240 "/media",
241 "/mnt",
242 "/opt",
243 "/rescue",
244 "/root",
245 "/sbin",
246 "/srv",
247 "/tmp",
248 "/usr",
249 "/usr/X11R6",
250 "/usr/local",
251 "/usr/obj",
252 "/usr/ports",
253 "/usr/src",
254 "/usr/xobj",
255 "/var",
256 "/var/crash",
257 "/var/local",
258 "/var/log",
259 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
260 "/var/mail",
261 "/var/run",
262 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
263 "/proc",
264 "/sbin",
265 "/net",
266 "/sys",
267 NULL
270 if (is_in (mount_path, ignore_mountpoints))
271 return TRUE;
273 if (g_str_has_prefix (mount_path, "/dev/") ||
274 g_str_has_prefix (mount_path, "/proc/") ||
275 g_str_has_prefix (mount_path, "/sys/"))
276 return TRUE;
278 if (g_str_has_suffix (mount_path, "/.gvfs"))
279 return TRUE;
281 return FALSE;
285 * g_unix_is_system_fs_type:
286 * @fs_type: a file system type, e.g. `procfs` or `tmpfs`
288 * Determines if @fs_type is considered a type of file system which is only
289 * used in implementation of the OS. This is primarily used for hiding
290 * mounted volumes that are intended as APIs for programs to read, and system
291 * administrators at a shell; rather than something that should, for example,
292 * appear in a GUI. For example, the Linux `/proc` filesystem.
294 * The list of file system types considered ‘system’ ones may change over time.
296 * Returns: %TRUE if @fs_type is considered an implementation detail of the OS.
297 * Since: 2.56
299 gboolean
300 g_unix_is_system_fs_type (const char *fs_type)
302 const char *ignore_fs[] = {
303 "adfs",
304 "afs",
305 "auto",
306 "autofs",
307 "autofs4",
308 "cgroup",
309 "cifs",
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 "nfs",
332 "nfs4",
333 "nfsd",
334 "nullfs",
335 "ocfs2",
336 "overlay",
337 "proc",
338 "procfs",
339 "pstore",
340 "ptyfs",
341 "rootfs",
342 "rpc_pipefs",
343 "securityfs",
344 "selinuxfs",
345 "smbfs",
346 "sysfs",
347 "tmpfs",
348 "usbfs",
349 "zfs",
350 NULL
353 g_return_val_if_fail (fs_type != NULL && *fs_type != '\0', FALSE);
355 return is_in (fs_type, ignore_fs);
359 * g_unix_is_system_device_path:
360 * @device_path: a device path, e.g. `/dev/loop0` or `nfsd`
362 * Determines if @device_path is considered a block device path which is only
363 * used in implementation of the OS. This is primarily used for hiding
364 * mounted volumes that are intended as APIs for programs to read, and system
365 * administrators at a shell; rather than something that should, for example,
366 * appear in a GUI. For example, the Linux `/proc` filesystem.
368 * The list of device paths considered ‘system’ ones may change over time.
370 * Returns: %TRUE if @device_path is considered an implementation detail of
371 * the OS.
372 * Since: 2.56
374 gboolean
375 g_unix_is_system_device_path (const char *device_path)
377 const char *ignore_devices[] = {
378 "none",
379 "sunrpc",
380 "devpts",
381 "nfsd",
382 "/dev/loop",
383 "/dev/vn",
384 NULL
387 g_return_val_if_fail (device_path != NULL && *device_path != '\0', FALSE);
389 return is_in (device_path, ignore_devices);
392 static gboolean
393 guess_system_internal (const char *mountpoint,
394 const char *fs,
395 const char *device)
397 if (g_unix_is_system_fs_type (fs))
398 return TRUE;
400 if (g_unix_is_system_device_path (device))
401 return TRUE;
403 if (g_unix_is_mount_path_system_internal (mountpoint))
404 return TRUE;
406 return FALSE;
409 /* GUnixMounts (ie: mtab) implementations {{{1 */
411 static GUnixMountEntry *
412 create_unix_mount_entry (const char *device_path,
413 const char *mount_path,
414 const char *filesystem_type,
415 gboolean is_read_only)
417 GUnixMountEntry *mount_entry = NULL;
419 mount_entry = g_new0 (GUnixMountEntry, 1);
420 mount_entry->device_path = g_strdup (device_path);
421 mount_entry->mount_path = g_strdup (mount_path);
422 mount_entry->filesystem_type = g_strdup (filesystem_type);
423 mount_entry->is_read_only = is_read_only;
425 mount_entry->is_system_internal =
426 guess_system_internal (mount_entry->mount_path,
427 mount_entry->filesystem_type,
428 mount_entry->device_path);
429 return mount_entry;
432 static GUnixMountPoint *
433 create_unix_mount_point (const char *device_path,
434 const char *mount_path,
435 const char *filesystem_type,
436 const char *options,
437 gboolean is_read_only,
438 gboolean is_user_mountable,
439 gboolean is_loopback)
441 GUnixMountPoint *mount_point = NULL;
443 mount_point = g_new0 (GUnixMountPoint, 1);
444 mount_point->device_path = g_strdup (device_path);
445 mount_point->mount_path = g_strdup (mount_path);
446 mount_point->filesystem_type = g_strdup (filesystem_type);
447 mount_point->options = g_strdup (options);
448 mount_point->is_read_only = is_read_only;
449 mount_point->is_user_mountable = is_user_mountable;
450 mount_point->is_loopback = is_loopback;
452 return mount_point;
455 /* mntent.h (Linux, GNU, NSS) {{{2 */
456 #ifdef HAVE_MNTENT_H
458 #ifdef HAVE_LIBMOUNT
460 /* For documentation on /proc/self/mountinfo see
461 * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
463 #define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
465 static GList *
466 _g_get_unix_mounts (void)
468 struct libmnt_table *table = NULL;
469 struct libmnt_iter* iter = NULL;
470 struct libmnt_fs *fs = NULL;
471 GUnixMountEntry *mount_entry = NULL;
472 GList *return_list = NULL;
474 table = mnt_new_table ();
475 if (mnt_table_parse_mtab (table, NULL) < 0)
476 goto out;
478 iter = mnt_new_iter (MNT_ITER_FORWARD);
479 while (mnt_table_next_fs (table, iter, &fs) == 0)
481 const char *device_path = NULL;
482 char *mount_options = NULL;
483 unsigned long mount_flags = 0;
484 gboolean is_read_only = FALSE;
486 device_path = mnt_fs_get_source (fs);
487 if (g_strcmp0 (device_path, "/dev/root") == 0)
488 device_path = _resolve_dev_root ();
490 mount_options = mnt_fs_strdup_options (fs);
491 if (mount_options)
493 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
494 g_free (mount_options);
496 is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
498 mount_entry = create_unix_mount_entry (device_path,
499 mnt_fs_get_target (fs),
500 mnt_fs_get_fstype (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 is_read_only);
597 g_hash_table_insert (mounts_hash,
598 mount_entry->device_path,
599 mount_entry->device_path);
601 return_list = g_list_prepend (return_list, mount_entry);
603 g_hash_table_destroy (mounts_hash);
605 endmntent (file);
607 #ifndef HAVE_GETMNTENT_R
608 G_UNLOCK (getmntent);
609 #endif
611 return g_list_reverse (return_list);
614 #endif /* HAVE_LIBMOUNT */
616 static const char *
617 get_mtab_monitor_file (void)
619 static const char *mountinfo_path = NULL;
620 #ifdef HAVE_LIBMOUNT
621 struct stat buf;
622 #endif
624 if (mountinfo_path != NULL)
625 return mountinfo_path;
627 #ifdef HAVE_LIBMOUNT
628 /* The mtab file is still used by some distros, so it has to be monitored in
629 * order to avoid races between g_unix_mounts_get and "mounts-changed" signal:
630 * https://bugzilla.gnome.org/show_bug.cgi?id=782814
632 if (mnt_has_regular_mtab (&mountinfo_path, NULL))
634 return mountinfo_path;
637 if (stat (PROC_MOUNTINFO_PATH, &buf) == 0)
639 mountinfo_path = PROC_MOUNTINFO_PATH;
640 return mountinfo_path;
642 #endif
644 #ifdef _PATH_MOUNTED
645 # ifdef __linux__
646 mountinfo_path = "/proc/mounts";
647 # else
648 mountinfo_path = _PATH_MOUNTED;
649 # endif
650 #else
651 mountinfo_path = "/etc/mtab";
652 #endif
654 return mountinfo_path;
657 /* mnttab.h {{{2 */
658 #elif defined (HAVE_SYS_MNTTAB_H)
660 G_LOCK_DEFINE_STATIC(getmntent);
662 static const char *
663 get_mtab_read_file (void)
665 #ifdef _PATH_MOUNTED
666 return _PATH_MOUNTED;
667 #else
668 return "/etc/mnttab";
669 #endif
672 static const char *
673 get_mtab_monitor_file (void)
675 return get_mtab_read_file ();
678 static GList *
679 _g_get_unix_mounts (void)
681 struct mnttab mntent;
682 FILE *file;
683 const char *read_file;
684 GUnixMountEntry *mount_entry;
685 GList *return_list;
687 read_file = get_mtab_read_file ();
689 file = setmntent (read_file, "r");
690 if (file == NULL)
691 return NULL;
693 return_list = NULL;
695 G_LOCK (getmntent);
696 while (! getmntent (file, &mntent))
698 gboolean is_read_only = FALSE;
700 #if defined (HAVE_HASMNTOPT)
701 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
702 is_read_only = TRUE;
703 #endif
705 mount_entry = create_unix_mount_entry (mntent.mnt_special,
706 mntent.mnt_mountp,
707 mntent.mnt_fstype,
708 is_read_only);
710 return_list = g_list_prepend (return_list, mount_entry);
713 endmntent (file);
715 G_UNLOCK (getmntent);
717 return g_list_reverse (return_list);
720 /* mntctl.h (AIX) {{{2 */
721 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
723 static const char *
724 get_mtab_monitor_file (void)
726 return NULL;
729 static GList *
730 _g_get_unix_mounts (void)
732 struct vfs_ent *fs_info;
733 struct vmount *vmount_info;
734 int vmount_number;
735 unsigned int vmount_size;
736 int current;
737 GList *return_list;
739 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
741 g_warning ("Unable to know the number of mounted volumes\n");
743 return NULL;
746 vmount_info = (struct vmount*)g_malloc (vmount_size);
748 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
750 if (vmount_info->vmt_revision != VMT_REVISION)
751 g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
753 if (vmount_number < 0)
755 g_warning ("Unable to recover mounted volumes information\n");
757 g_free (vmount_info);
758 return NULL;
761 return_list = NULL;
762 while (vmount_number > 0)
764 gboolean is_read_only = FALSE;
766 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
768 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
769 is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
771 mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
772 vmt2dataptr (vmount_info, VMT_STUB),
773 fs_info == NULL ? "unknown" : fs_info->vfsent_name,
774 is_read_only);
776 return_list = g_list_prepend (return_list, mount_entry);
778 vmount_info = (struct vmount *)( (char*)vmount_info
779 + vmount_info->vmt_length);
780 vmount_number--;
783 g_free (vmount_info);
785 return g_list_reverse (return_list);
788 /* sys/mount.h {{{2 */
789 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
791 static const char *
792 get_mtab_monitor_file (void)
794 return NULL;
797 static GList *
798 _g_get_unix_mounts (void)
800 #if defined(USE_STATVFS)
801 struct statvfs *mntent = NULL;
802 #elif defined(USE_STATFS)
803 struct statfs *mntent = NULL;
804 #else
805 #error statfs juggling failed
806 #endif
807 size_t bufsize;
808 int num_mounts, i;
809 GUnixMountEntry *mount_entry;
810 GList *return_list;
812 /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
813 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
814 num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
815 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
816 num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
817 #endif
818 if (num_mounts == -1)
819 return NULL;
821 bufsize = num_mounts * sizeof (*mntent);
822 mntent = g_malloc (bufsize);
823 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
824 num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
825 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
826 num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
827 #endif
828 if (num_mounts == -1)
829 return NULL;
831 return_list = NULL;
833 for (i = 0; i < num_mounts; i++)
835 gboolean is_read_only = FALSE;
837 #if defined(USE_STATVFS)
838 if (mntent[i].f_flag & ST_RDONLY)
839 #elif defined(USE_STATFS)
840 if (mntent[i].f_flags & MNT_RDONLY)
841 #else
842 #error statfs juggling failed
843 #endif
844 is_read_only = TRUE;
846 mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
847 mntent[i].f_mntonname,
848 mntent[i].f_fstypename,
849 is_read_only);
851 return_list = g_list_prepend (return_list, mount_entry);
854 g_free (mntent);
856 return g_list_reverse (return_list);
859 /* Interix {{{2 */
860 #elif defined(__INTERIX)
862 static const char *
863 get_mtab_monitor_file (void)
865 return NULL;
868 static GList *
869 _g_get_unix_mounts (void)
871 DIR *dirp;
872 GList* return_list = NULL;
873 char filename[9 + NAME_MAX];
875 dirp = opendir ("/dev/fs");
876 if (!dirp)
878 g_warning ("unable to read /dev/fs!");
879 return NULL;
882 while (1)
884 struct statvfs statbuf;
885 struct dirent entry;
886 struct dirent* result;
888 if (readdir_r (dirp, &entry, &result) || result == NULL)
889 break;
891 strcpy (filename, "/dev/fs/");
892 strcat (filename, entry.d_name);
894 if (statvfs (filename, &statbuf) == 0)
896 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
898 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
899 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
900 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
902 if (statbuf.f_flag & ST_RDONLY)
903 mount_entry->is_read_only = TRUE;
905 return_list = g_list_prepend(return_list, mount_entry);
909 return_list = g_list_reverse (return_list);
911 closedir (dirp);
913 return return_list;
916 /* Common code {{{2 */
917 #else
918 #error No _g_get_unix_mounts() implementation for system
919 #endif
921 /* GUnixMountPoints (ie: fstab) implementations {{{1 */
923 /* _g_get_unix_mount_points():
924 * read the fstab.
925 * don't return swap and ignore mounts.
928 static char *
929 get_fstab_file (void)
931 #ifdef HAVE_LIBMOUNT
932 return (char *) mnt_get_fstab_path ();
933 #else
934 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
935 /* AIX */
936 return "/etc/filesystems";
937 #elif defined(_PATH_MNTTAB)
938 return _PATH_MNTTAB;
939 #elif defined(VFSTAB)
940 return VFSTAB;
941 #else
942 return "/etc/fstab";
943 #endif
944 #endif
947 /* mntent.h (Linux, GNU, NSS) {{{2 */
948 #ifdef HAVE_MNTENT_H
950 #ifdef HAVE_LIBMOUNT
952 static GList *
953 _g_get_unix_mount_points (void)
955 struct libmnt_table *table = NULL;
956 struct libmnt_iter* iter = NULL;
957 struct libmnt_fs *fs = NULL;
958 GUnixMountPoint *mount_point = NULL;
959 GList *return_list = NULL;
961 table = mnt_new_table ();
962 if (mnt_table_parse_fstab (table, NULL) < 0)
963 goto out;
965 iter = mnt_new_iter (MNT_ITER_FORWARD);
966 while (mnt_table_next_fs (table, iter, &fs) == 0)
968 const char *device_path = NULL;
969 const char *mount_path = NULL;
970 const char *mount_fstype = NULL;
971 char *mount_options = NULL;
972 gboolean is_read_only = FALSE;
973 gboolean is_user_mountable = FALSE;
974 gboolean is_loopback = FALSE;
976 mount_path = mnt_fs_get_target (fs);
977 if ((strcmp (mount_path, "ignore") == 0) ||
978 (strcmp (mount_path, "swap") == 0) ||
979 (strcmp (mount_path, "none") == 0))
980 continue;
982 mount_fstype = mnt_fs_get_fstype (fs);
983 mount_options = mnt_fs_strdup_options (fs);
984 if (mount_options)
986 unsigned long mount_flags = 0;
987 unsigned long userspace_flags = 0;
989 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
990 mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
992 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
993 if (mount_flags & MS_BIND)
995 g_free (mount_options);
996 continue;
999 is_read_only = (mount_flags & MS_RDONLY) != 0;
1000 is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
1002 if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
1003 ((userspace_flags & MNT_MS_USER) &&
1004 (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
1005 (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
1006 (userspace_flags & MNT_MS_USERS) ||
1007 (userspace_flags & MNT_MS_OWNER))
1009 is_user_mountable = TRUE;
1013 device_path = mnt_fs_get_source (fs);
1014 if (g_strcmp0 (device_path, "/dev/root") == 0)
1015 device_path = _resolve_dev_root ();
1017 mount_point = create_unix_mount_point (device_path,
1018 mount_path,
1019 mount_fstype,
1020 mount_options,
1021 is_read_only,
1022 is_user_mountable,
1023 is_loopback);
1024 if (mount_options)
1025 g_free (mount_options);
1027 return_list = g_list_prepend (return_list, mount_point);
1029 mnt_free_iter (iter);
1031 out:
1032 mnt_free_table (table);
1034 return g_list_reverse (return_list);
1037 #else
1039 static GList *
1040 _g_get_unix_mount_points (void)
1042 #ifdef HAVE_GETMNTENT_R
1043 struct mntent ent;
1044 char buf[1024];
1045 #endif
1046 struct mntent *mntent;
1047 FILE *file;
1048 char *read_file;
1049 GUnixMountPoint *mount_point;
1050 GList *return_list;
1052 read_file = get_fstab_file ();
1054 file = setmntent (read_file, "r");
1055 if (file == NULL)
1056 return NULL;
1058 return_list = NULL;
1060 #ifdef HAVE_GETMNTENT_R
1061 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
1062 #else
1063 G_LOCK (getmntent);
1064 while ((mntent = getmntent (file)) != NULL)
1065 #endif
1067 const char *device_path = NULL;
1068 gboolean is_read_only = FALSE;
1069 gboolean is_user_mountable = FALSE;
1070 gboolean is_loopback = FALSE;
1072 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
1073 (strcmp (mntent->mnt_dir, "swap") == 0) ||
1074 (strcmp (mntent->mnt_dir, "none") == 0))
1075 continue;
1077 #ifdef HAVE_HASMNTOPT
1078 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1079 if (hasmntopt (mntent, "bind"))
1080 continue;
1081 #endif
1083 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1084 device_path = _resolve_dev_root ();
1085 else
1086 device_path = mntent->mnt_fsname;
1088 #ifdef HAVE_HASMNTOPT
1089 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1090 is_read_only = TRUE;
1092 if (hasmntopt (mntent, "loop") != NULL)
1093 is_loopback = TRUE;
1095 #endif
1097 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1098 #ifdef HAVE_HASMNTOPT
1099 || (hasmntopt (mntent, "user") != NULL
1100 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1101 || hasmntopt (mntent, "pamconsole") != NULL
1102 || hasmntopt (mntent, "users") != NULL
1103 || hasmntopt (mntent, "owner") != NULL
1104 #endif
1106 is_user_mountable = TRUE;
1108 mount_point = create_unix_mount_point (device_path,
1109 mntent->mnt_dir,
1110 mntent->mnt_type,
1111 mntent->mnt_opts,
1112 is_read_only,
1113 is_user_mountable,
1114 is_loopback);
1116 return_list = g_list_prepend (return_list, mount_point);
1119 endmntent (file);
1121 #ifndef HAVE_GETMNTENT_R
1122 G_UNLOCK (getmntent);
1123 #endif
1125 return g_list_reverse (return_list);
1128 #endif /* HAVE_LIBMOUNT */
1130 /* mnttab.h {{{2 */
1131 #elif defined (HAVE_SYS_MNTTAB_H)
1133 static GList *
1134 _g_get_unix_mount_points (void)
1136 struct mnttab mntent;
1137 FILE *file;
1138 char *read_file;
1139 GUnixMountPoint *mount_point;
1140 GList *return_list;
1142 read_file = get_fstab_file ();
1144 file = setmntent (read_file, "r");
1145 if (file == NULL)
1146 return NULL;
1148 return_list = NULL;
1150 G_LOCK (getmntent);
1151 while (! getmntent (file, &mntent))
1153 gboolean is_read_only = FALSE;
1154 gboolean is_user_mountable = FALSE;
1155 gboolean is_loopback = FALSE;
1157 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1158 (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1159 (strcmp (mntent.mnt_mountp, "none") == 0))
1160 continue;
1162 #ifdef HAVE_HASMNTOPT
1163 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1164 is_read_only = TRUE;
1166 if (hasmntopt (&mntent, "lofs") != NULL)
1167 is_loopback = TRUE;
1168 #endif
1170 if ((mntent.mnt_fstype != NULL)
1171 #ifdef HAVE_HASMNTOPT
1172 || (hasmntopt (&mntent, "user") != NULL
1173 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1174 || hasmntopt (&mntent, "pamconsole") != NULL
1175 || hasmntopt (&mntent, "users") != NULL
1176 || hasmntopt (&mntent, "owner") != NULL
1177 #endif
1179 is_user_mountable = TRUE;
1181 mount_point = create_unix_mount_point (mntent.mnt_special,
1182 mntent.mnt_mountp,
1183 mntent.mnt_fstype,
1184 mntent.mnt_mntopts,
1185 is_read_only,
1186 is_user_mountable,
1187 is_loopback);
1189 return_list = g_list_prepend (return_list, mount_point);
1192 endmntent (file);
1193 G_UNLOCK (getmntent);
1195 return g_list_reverse (return_list);
1198 /* mntctl.h (AIX) {{{2 */
1199 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1201 /* functions to parse /etc/filesystems on aix */
1203 /* read character, ignoring comments (begin with '*', end with '\n' */
1204 static int
1205 aix_fs_getc (FILE *fd)
1207 int c;
1209 while ((c = getc (fd)) == '*')
1211 while (((c = getc (fd)) != '\n') && (c != EOF))
1216 /* eat all continuous spaces in a file */
1217 static int
1218 aix_fs_ignorespace (FILE *fd)
1220 int c;
1222 while ((c = aix_fs_getc (fd)) != EOF)
1224 if (!g_ascii_isspace (c))
1226 ungetc (c,fd);
1227 return c;
1231 return EOF;
1234 /* read one word from file */
1235 static int
1236 aix_fs_getword (FILE *fd,
1237 char *word)
1239 int c;
1241 aix_fs_ignorespace (fd);
1243 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1245 if (c == '"')
1247 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1248 *word++ = c;
1249 else
1250 *word++ = c;
1253 *word = 0;
1255 return c;
1258 typedef struct {
1259 char mnt_mount[PATH_MAX];
1260 char mnt_special[PATH_MAX];
1261 char mnt_fstype[16];
1262 char mnt_options[128];
1263 } AixMountTableEntry;
1265 /* read mount points properties */
1266 static int
1267 aix_fs_get (FILE *fd,
1268 AixMountTableEntry *prop)
1270 static char word[PATH_MAX] = { 0 };
1271 char value[PATH_MAX];
1273 /* read stanza */
1274 if (word[0] == 0)
1276 if (aix_fs_getword (fd, word) == EOF)
1277 return EOF;
1280 word[strlen(word) - 1] = 0;
1281 strcpy (prop->mnt_mount, word);
1283 /* read attributes and value */
1285 while (aix_fs_getword (fd, word) != EOF)
1287 /* test if is attribute or new stanza */
1288 if (word[strlen(word) - 1] == ':')
1289 return 0;
1291 /* read "=" */
1292 aix_fs_getword (fd, value);
1294 /* read value */
1295 aix_fs_getword (fd, value);
1297 if (strcmp (word, "dev") == 0)
1298 strcpy (prop->mnt_special, value);
1299 else if (strcmp (word, "vfs") == 0)
1300 strcpy (prop->mnt_fstype, value);
1301 else if (strcmp (word, "options") == 0)
1302 strcpy(prop->mnt_options, value);
1305 return 0;
1308 static GList *
1309 _g_get_unix_mount_points (void)
1311 struct mntent *mntent;
1312 FILE *file;
1313 char *read_file;
1314 GUnixMountPoint *mount_point;
1315 AixMountTableEntry mntent;
1316 GList *return_list;
1318 read_file = get_fstab_file ();
1320 file = setmntent (read_file, "r");
1321 if (file == NULL)
1322 return NULL;
1324 return_list = NULL;
1326 while (!aix_fs_get (file, &mntent))
1328 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1330 mount_point = create_unix_mount_point (mntent.mnt_special,
1331 mntent.mnt_mount,
1332 mntent.mnt_fstype,
1333 mntent.mnt_options,
1334 TRUE,
1335 TRUE,
1336 FALSE);
1338 return_list = g_list_prepend (return_list, mount_point);
1342 endmntent (file);
1344 return g_list_reverse (return_list);
1347 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1349 static GList *
1350 _g_get_unix_mount_points (void)
1352 struct fstab *fstab = NULL;
1353 GUnixMountPoint *mount_point;
1354 GList *return_list;
1355 #ifdef HAVE_SYS_SYSCTL_H
1356 int usermnt = 0;
1357 struct stat sb;
1358 #endif
1360 if (!setfsent ())
1361 return NULL;
1363 return_list = NULL;
1365 #ifdef HAVE_SYS_SYSCTL_H
1366 #if defined(HAVE_SYSCTLBYNAME)
1368 size_t len = sizeof(usermnt);
1370 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1372 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1374 int mib[2];
1375 size_t len = sizeof(usermnt);
1377 mib[0] = CTL_VFS;
1378 mib[1] = VFS_USERMOUNT;
1379 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1381 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1383 int mib[2];
1384 size_t len = sizeof(usermnt);
1386 mib[0] = CTL_KERN;
1387 mib[1] = KERN_USERMOUNT;
1388 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1390 #endif
1391 #endif
1393 while ((fstab = getfsent ()) != NULL)
1395 gboolean is_read_only = FALSE;
1396 gboolean is_user_mountable = FALSE;
1398 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1399 continue;
1401 if (strcmp (fstab->fs_type, "ro") == 0)
1402 is_read_only = TRUE;
1404 #ifdef HAVE_SYS_SYSCTL_H
1405 if (usermnt != 0)
1407 uid_t uid = getuid ();
1408 if (stat (fstab->fs_file, &sb) == 0)
1410 if (uid == 0 || sb.st_uid == uid)
1411 is_user_mountable = TRUE;
1414 #endif
1416 mount_point = create_unix_mount_point (fstab->fs_spec,
1417 fstab->fs_file,
1418 fstab->fs_vfstype,
1419 fstab->fs_mntops,
1420 is_read_only,
1421 is_user_mountable,
1422 FALSE);
1424 return_list = g_list_prepend (return_list, mount_point);
1427 endfsent ();
1429 return g_list_reverse (return_list);
1431 /* Interix {{{2 */
1432 #elif defined(__INTERIX)
1433 static GList *
1434 _g_get_unix_mount_points (void)
1436 return _g_get_unix_mounts ();
1439 /* Common code {{{2 */
1440 #else
1441 #error No g_get_mount_table() implementation for system
1442 #endif
1444 static guint64
1445 get_mounts_timestamp (void)
1447 const char *monitor_file;
1448 struct stat buf;
1450 monitor_file = get_mtab_monitor_file ();
1451 /* Don't return mtime for /proc/ files */
1452 if (monitor_file && !g_str_has_prefix (monitor_file, "/proc/"))
1454 if (stat (monitor_file, &buf) == 0)
1455 return (guint64)buf.st_mtime;
1457 else if (proc_mounts_watch_is_running ())
1459 /* it's being monitored by poll, so return mount_poller_time */
1460 return mount_poller_time;
1462 else
1464 /* Case of /proc/ file not being monitored - Be on the safe side and
1465 * send a new timestamp to force g_unix_mounts_changed_since() to
1466 * return TRUE so any application caches depending on it (like eg.
1467 * the one in GIO) get invalidated and don't hold possibly outdated
1468 * data - see Bug 787731 */
1469 return (guint64) g_get_monotonic_time ();
1471 return 0;
1474 static guint64
1475 get_mount_points_timestamp (void)
1477 const char *monitor_file;
1478 struct stat buf;
1480 monitor_file = get_fstab_file ();
1481 if (monitor_file)
1483 if (stat (monitor_file, &buf) == 0)
1484 return (guint64)buf.st_mtime;
1486 return 0;
1490 * g_unix_mounts_get:
1491 * @time_read: (out) (optional): guint64 to contain a timestamp, or %NULL
1493 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1494 * If @time_read is set, it will be filled with the mount
1495 * timestamp, allowing for checking if the mounts have changed
1496 * with g_unix_mounts_changed_since().
1498 * Returns: (element-type GUnixMountEntry) (transfer full):
1499 * a #GList of the UNIX mounts.
1501 GList *
1502 g_unix_mounts_get (guint64 *time_read)
1504 if (time_read)
1505 *time_read = get_mounts_timestamp ();
1507 return _g_get_unix_mounts ();
1511 * g_unix_mount_at:
1512 * @mount_path: (type filename): path for a possible unix mount.
1513 * @time_read: (out) (optional): guint64 to contain a timestamp.
1515 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1516 * is set, it will be filled with a unix timestamp for checking
1517 * if the mounts have changed since with g_unix_mounts_changed_since().
1519 * Returns: (transfer full): a #GUnixMountEntry.
1521 GUnixMountEntry *
1522 g_unix_mount_at (const char *mount_path,
1523 guint64 *time_read)
1525 GList *mounts, *l;
1526 GUnixMountEntry *mount_entry, *found;
1528 mounts = g_unix_mounts_get (time_read);
1530 found = NULL;
1531 for (l = mounts; l != NULL; l = l->next)
1533 mount_entry = l->data;
1535 if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1536 found = mount_entry;
1537 else
1538 g_unix_mount_free (mount_entry);
1540 g_list_free (mounts);
1542 return found;
1546 * g_unix_mount_for:
1547 * @file_path: (type filename): file path on some unix mount.
1548 * @time_read: (out) (optional): guint64 to contain a timestamp.
1550 * Gets a #GUnixMountEntry for a given file path. If @time_read
1551 * is set, it will be filled with a unix timestamp for checking
1552 * if the mounts have changed since with g_unix_mounts_changed_since().
1554 * Returns: (transfer full): a #GUnixMountEntry.
1556 * Since: 2.52
1558 GUnixMountEntry *
1559 g_unix_mount_for (const char *file_path,
1560 guint64 *time_read)
1562 GUnixMountEntry *entry;
1564 g_return_val_if_fail (file_path != NULL, NULL);
1566 entry = g_unix_mount_at (file_path, time_read);
1567 if (entry == NULL)
1569 char *topdir;
1571 topdir = _g_local_file_find_topdir_for (file_path);
1572 if (topdir != NULL)
1574 entry = g_unix_mount_at (topdir, time_read);
1575 g_free (topdir);
1579 return entry;
1583 * g_unix_mount_points_get:
1584 * @time_read: (out) (optional): guint64 to contain a timestamp.
1586 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1587 * If @time_read is set, it will be filled with the mount timestamp,
1588 * allowing for checking if the mounts have changed with
1589 * g_unix_mount_points_changed_since().
1591 * Returns: (element-type GUnixMountPoint) (transfer full):
1592 * a #GList of the UNIX mountpoints.
1594 GList *
1595 g_unix_mount_points_get (guint64 *time_read)
1597 if (time_read)
1598 *time_read = get_mount_points_timestamp ();
1600 return _g_get_unix_mount_points ();
1604 * g_unix_mounts_changed_since:
1605 * @time: guint64 to contain a timestamp.
1607 * Checks if the unix mounts have changed since a given unix time.
1609 * Returns: %TRUE if the mounts have changed since @time.
1611 gboolean
1612 g_unix_mounts_changed_since (guint64 time)
1614 return get_mounts_timestamp () != time;
1618 * g_unix_mount_points_changed_since:
1619 * @time: guint64 to contain a timestamp.
1621 * Checks if the unix mount points have changed since a given unix time.
1623 * Returns: %TRUE if the mount points have changed since @time.
1625 gboolean
1626 g_unix_mount_points_changed_since (guint64 time)
1628 return get_mount_points_timestamp () != time;
1631 /* GUnixMountMonitor {{{1 */
1633 enum {
1634 MOUNTS_CHANGED,
1635 MOUNTPOINTS_CHANGED,
1636 LAST_SIGNAL
1639 static guint signals[LAST_SIGNAL];
1641 struct _GUnixMountMonitor {
1642 GObject parent;
1644 GMainContext *context;
1647 struct _GUnixMountMonitorClass {
1648 GObjectClass parent_class;
1652 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT)
1654 static GContextSpecificGroup mount_monitor_group;
1655 static GFileMonitor *fstab_monitor;
1656 static GFileMonitor *mtab_monitor;
1657 static GSource *proc_mounts_watch_source;
1658 static GList *mount_poller_mounts;
1660 static gboolean
1661 proc_mounts_watch_is_running (void)
1663 return proc_mounts_watch_source != NULL &&
1664 !g_source_is_destroyed (proc_mounts_watch_source);
1667 static void
1668 fstab_file_changed (GFileMonitor *monitor,
1669 GFile *file,
1670 GFile *other_file,
1671 GFileMonitorEvent event_type,
1672 gpointer user_data)
1674 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1675 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1676 event_type != G_FILE_MONITOR_EVENT_DELETED)
1677 return;
1679 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1682 static void
1683 mtab_file_changed (GFileMonitor *monitor,
1684 GFile *file,
1685 GFile *other_file,
1686 GFileMonitorEvent event_type,
1687 gpointer user_data)
1689 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1690 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1691 event_type != G_FILE_MONITOR_EVENT_DELETED)
1692 return;
1694 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1697 static gboolean
1698 proc_mounts_changed (GIOChannel *channel,
1699 GIOCondition cond,
1700 gpointer user_data)
1702 if (cond & G_IO_ERR)
1704 mount_poller_time = (guint64) g_get_monotonic_time ();
1705 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1708 return TRUE;
1711 static gboolean
1712 mount_change_poller (gpointer user_data)
1714 GList *current_mounts, *new_it, *old_it;
1715 gboolean has_changed = FALSE;
1717 current_mounts = _g_get_unix_mounts ();
1719 for ( new_it = current_mounts, old_it = mount_poller_mounts;
1720 new_it != NULL && old_it != NULL;
1721 new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1723 if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1725 has_changed = TRUE;
1726 break;
1729 if (!(new_it == NULL && old_it == NULL))
1730 has_changed = TRUE;
1732 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1734 mount_poller_mounts = current_mounts;
1736 if (has_changed)
1738 mount_poller_time = (guint64) g_get_monotonic_time ();
1739 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1742 return TRUE;
1746 static void
1747 mount_monitor_stop (void)
1749 if (fstab_monitor)
1751 g_file_monitor_cancel (fstab_monitor);
1752 g_object_unref (fstab_monitor);
1755 if (proc_mounts_watch_source != NULL)
1757 g_source_destroy (proc_mounts_watch_source);
1758 proc_mounts_watch_source = NULL;
1761 if (mtab_monitor)
1763 g_file_monitor_cancel (mtab_monitor);
1764 g_object_unref (mtab_monitor);
1767 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1770 static void
1771 mount_monitor_start (void)
1773 GFile *file;
1775 if (get_fstab_file () != NULL)
1777 file = g_file_new_for_path (get_fstab_file ());
1778 fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1779 g_object_unref (file);
1781 g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1784 if (get_mtab_monitor_file () != NULL)
1786 const gchar *mtab_path;
1788 mtab_path = get_mtab_monitor_file ();
1789 /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1790 * See 'man proc' for more details.
1792 if (g_str_has_prefix (mtab_path, "/proc/"))
1794 GIOChannel *proc_mounts_channel;
1795 GError *error = NULL;
1796 proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
1797 if (proc_mounts_channel == NULL)
1799 g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1800 error->message, g_quark_to_string (error->domain), error->code);
1801 g_error_free (error);
1803 else
1805 proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1806 g_source_set_callback (proc_mounts_watch_source,
1807 (GSourceFunc) proc_mounts_changed,
1808 NULL, NULL);
1809 g_source_attach (proc_mounts_watch_source,
1810 g_main_context_get_thread_default ());
1811 g_source_unref (proc_mounts_watch_source);
1812 g_io_channel_unref (proc_mounts_channel);
1815 else
1817 file = g_file_new_for_path (mtab_path);
1818 mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1819 g_object_unref (file);
1820 g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
1823 else
1825 proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1826 mount_poller_mounts = _g_get_unix_mounts ();
1827 mount_poller_time = (guint64)g_get_monotonic_time ();
1828 g_source_set_callback (proc_mounts_watch_source,
1829 mount_change_poller,
1830 NULL, NULL);
1831 g_source_attach (proc_mounts_watch_source,
1832 g_main_context_get_thread_default ());
1833 g_source_unref (proc_mounts_watch_source);
1837 static void
1838 g_unix_mount_monitor_finalize (GObject *object)
1840 GUnixMountMonitor *monitor;
1842 monitor = G_UNIX_MOUNT_MONITOR (object);
1844 g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
1846 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1849 static void
1850 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1852 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1854 gobject_class->finalize = g_unix_mount_monitor_finalize;
1857 * GUnixMountMonitor::mounts-changed:
1858 * @monitor: the object on which the signal is emitted
1860 * Emitted when the unix mounts have changed.
1862 signals[MOUNTS_CHANGED] =
1863 g_signal_new (I_("mounts-changed"),
1864 G_TYPE_FROM_CLASS (klass),
1865 G_SIGNAL_RUN_LAST,
1867 NULL, NULL,
1868 g_cclosure_marshal_VOID__VOID,
1869 G_TYPE_NONE, 0);
1872 * GUnixMountMonitor::mountpoints-changed:
1873 * @monitor: the object on which the signal is emitted
1875 * Emitted when the unix mount points have changed.
1877 signals[MOUNTPOINTS_CHANGED] =
1878 g_signal_new (I_("mountpoints-changed"),
1879 G_TYPE_FROM_CLASS (klass),
1880 G_SIGNAL_RUN_LAST,
1882 NULL, NULL,
1883 g_cclosure_marshal_VOID__VOID,
1884 G_TYPE_NONE, 0);
1887 static void
1888 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1893 * g_unix_mount_monitor_set_rate_limit:
1894 * @mount_monitor: a #GUnixMountMonitor
1895 * @limit_msec: a integer with the limit in milliseconds to
1896 * poll for changes.
1898 * This function does nothing.
1900 * Before 2.44, this was a partially-effective way of controlling the
1901 * rate at which events would be reported under some uncommon
1902 * circumstances. Since @mount_monitor is a singleton, it also meant
1903 * that calling this function would have side effects for other users of
1904 * the monitor.
1906 * Since: 2.18
1908 * Deprecated:2.44:This function does nothing. Don't call it.
1910 void
1911 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1912 gint limit_msec)
1917 * g_unix_mount_monitor_get:
1919 * Gets the #GUnixMountMonitor for the current thread-default main
1920 * context.
1922 * The mount monitor can be used to monitor for changes to the list of
1923 * mounted filesystems as well as the list of mount points (ie: fstab
1924 * entries).
1926 * You must only call g_object_unref() on the return value from under
1927 * the same main context as you called this function.
1929 * Returns: (transfer full): the #GUnixMountMonitor.
1931 * Since: 2.44
1933 GUnixMountMonitor *
1934 g_unix_mount_monitor_get (void)
1936 return g_context_specific_group_get (&mount_monitor_group,
1937 G_TYPE_UNIX_MOUNT_MONITOR,
1938 G_STRUCT_OFFSET(GUnixMountMonitor, context),
1939 mount_monitor_start);
1943 * g_unix_mount_monitor_new:
1945 * Deprecated alias for g_unix_mount_monitor_get().
1947 * This function was never a true constructor, which is why it was
1948 * renamed.
1950 * Returns: a #GUnixMountMonitor.
1952 * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
1954 GUnixMountMonitor *
1955 g_unix_mount_monitor_new (void)
1957 return g_unix_mount_monitor_get ();
1960 /* GUnixMount {{{1 */
1962 * g_unix_mount_free:
1963 * @mount_entry: a #GUnixMountEntry.
1965 * Frees a unix mount.
1967 void
1968 g_unix_mount_free (GUnixMountEntry *mount_entry)
1970 g_return_if_fail (mount_entry != NULL);
1972 g_free (mount_entry->mount_path);
1973 g_free (mount_entry->device_path);
1974 g_free (mount_entry->filesystem_type);
1975 g_free (mount_entry);
1979 * g_unix_mount_copy:
1980 * @mount_entry: a #GUnixMountEntry.
1982 * Makes a copy of @mount_entry.
1984 * Returns: (transfer full): a new #GUnixMountEntry
1986 * Since: 2.54
1988 GUnixMountEntry *
1989 g_unix_mount_copy (GUnixMountEntry *mount_entry)
1991 GUnixMountEntry *copy;
1993 g_return_val_if_fail (mount_entry != NULL, NULL);
1995 copy = g_new0 (GUnixMountEntry, 1);
1996 copy->mount_path = g_strdup (mount_entry->mount_path);
1997 copy->device_path = g_strdup (mount_entry->device_path);
1998 copy->filesystem_type = g_strdup (mount_entry->filesystem_type);
1999 copy->is_read_only = mount_entry->is_read_only;
2000 copy->is_system_internal = mount_entry->is_system_internal;
2002 return copy;
2006 * g_unix_mount_point_free:
2007 * @mount_point: unix mount point to free.
2009 * Frees a unix mount point.
2011 void
2012 g_unix_mount_point_free (GUnixMountPoint *mount_point)
2014 g_return_if_fail (mount_point != NULL);
2016 g_free (mount_point->mount_path);
2017 g_free (mount_point->device_path);
2018 g_free (mount_point->filesystem_type);
2019 g_free (mount_point->options);
2020 g_free (mount_point);
2024 * g_unix_mount_point_copy:
2025 * @mount_point: a #GUnixMountPoint.
2027 * Makes a copy of @mount_point.
2029 * Returns: (transfer full): a new #GUnixMountPoint
2031 * Since: 2.54
2033 GUnixMountPoint*
2034 g_unix_mount_point_copy (GUnixMountPoint *mount_point)
2036 GUnixMountPoint *copy;
2038 g_return_val_if_fail (mount_point != NULL, NULL);
2040 copy = g_new0 (GUnixMountPoint, 1);
2041 copy->mount_path = g_strdup (mount_point->mount_path);
2042 copy->device_path = g_strdup (mount_point->device_path);
2043 copy->filesystem_type = g_strdup (mount_point->filesystem_type);
2044 copy->options = g_strdup (mount_point->options);
2045 copy->is_read_only = mount_point->is_read_only;
2046 copy->is_user_mountable = mount_point->is_user_mountable;
2047 copy->is_loopback = mount_point->is_loopback;
2049 return copy;
2053 * g_unix_mount_compare:
2054 * @mount1: first #GUnixMountEntry to compare.
2055 * @mount2: second #GUnixMountEntry to compare.
2057 * Compares two unix mounts.
2059 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2060 * or less than @mount2, respectively.
2062 gint
2063 g_unix_mount_compare (GUnixMountEntry *mount1,
2064 GUnixMountEntry *mount2)
2066 int res;
2068 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2070 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2071 if (res != 0)
2072 return res;
2074 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2075 if (res != 0)
2076 return res;
2078 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2079 if (res != 0)
2080 return res;
2082 res = mount1->is_read_only - mount2->is_read_only;
2083 if (res != 0)
2084 return res;
2086 return 0;
2090 * g_unix_mount_get_mount_path:
2091 * @mount_entry: input #GUnixMountEntry to get the mount path for.
2093 * Gets the mount path for a unix mount.
2095 * Returns: (type filename): the mount path for @mount_entry.
2097 const gchar *
2098 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
2100 g_return_val_if_fail (mount_entry != NULL, NULL);
2102 return mount_entry->mount_path;
2106 * g_unix_mount_get_device_path:
2107 * @mount_entry: a #GUnixMount.
2109 * Gets the device path for a unix mount.
2111 * Returns: (type filename): a string containing the device path.
2113 const gchar *
2114 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
2116 g_return_val_if_fail (mount_entry != NULL, NULL);
2118 return mount_entry->device_path;
2122 * g_unix_mount_get_fs_type:
2123 * @mount_entry: a #GUnixMount.
2125 * Gets the filesystem type for the unix mount.
2127 * Returns: a string containing the file system type.
2129 const gchar *
2130 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
2132 g_return_val_if_fail (mount_entry != NULL, NULL);
2134 return mount_entry->filesystem_type;
2138 * g_unix_mount_is_readonly:
2139 * @mount_entry: a #GUnixMount.
2141 * Checks if a unix mount is mounted read only.
2143 * Returns: %TRUE if @mount_entry is read only.
2145 gboolean
2146 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
2148 g_return_val_if_fail (mount_entry != NULL, FALSE);
2150 return mount_entry->is_read_only;
2154 * g_unix_mount_is_system_internal:
2155 * @mount_entry: a #GUnixMount.
2157 * Checks if a Unix mount is a system mount. This is the Boolean OR of
2158 * g_unix_is_system_fs_type(), g_unix_is_system_device_path() and
2159 * g_unix_is_mount_path_system_internal() on @mount_entry’s properties.
2161 * The definition of what a ‘system’ mount entry is may change over time as new
2162 * file system types and device paths are ignored.
2164 * Returns: %TRUE if the unix mount is for a system path.
2166 gboolean
2167 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
2169 g_return_val_if_fail (mount_entry != NULL, FALSE);
2171 return mount_entry->is_system_internal;
2174 /* GUnixMountPoint {{{1 */
2176 * g_unix_mount_point_compare:
2177 * @mount1: a #GUnixMount.
2178 * @mount2: a #GUnixMount.
2180 * Compares two unix mount points.
2182 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2183 * or less than @mount2, respectively.
2185 gint
2186 g_unix_mount_point_compare (GUnixMountPoint *mount1,
2187 GUnixMountPoint *mount2)
2189 int res;
2191 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2193 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2194 if (res != 0)
2195 return res;
2197 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2198 if (res != 0)
2199 return res;
2201 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2202 if (res != 0)
2203 return res;
2205 res = g_strcmp0 (mount1->options, mount2->options);
2206 if (res != 0)
2207 return res;
2209 res = mount1->is_read_only - mount2->is_read_only;
2210 if (res != 0)
2211 return res;
2213 res = mount1->is_user_mountable - mount2->is_user_mountable;
2214 if (res != 0)
2215 return res;
2217 res = mount1->is_loopback - mount2->is_loopback;
2218 if (res != 0)
2219 return res;
2221 return 0;
2225 * g_unix_mount_point_get_mount_path:
2226 * @mount_point: a #GUnixMountPoint.
2228 * Gets the mount path for a unix mount point.
2230 * Returns: (type filename): a string containing the mount path.
2232 const gchar *
2233 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2235 g_return_val_if_fail (mount_point != NULL, NULL);
2237 return mount_point->mount_path;
2241 * g_unix_mount_point_get_device_path:
2242 * @mount_point: a #GUnixMountPoint.
2244 * Gets the device path for a unix mount point.
2246 * Returns: (type filename): a string containing the device path.
2248 const gchar *
2249 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2251 g_return_val_if_fail (mount_point != NULL, NULL);
2253 return mount_point->device_path;
2257 * g_unix_mount_point_get_fs_type:
2258 * @mount_point: a #GUnixMountPoint.
2260 * Gets the file system type for the mount point.
2262 * Returns: a string containing the file system type.
2264 const gchar *
2265 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2267 g_return_val_if_fail (mount_point != NULL, NULL);
2269 return mount_point->filesystem_type;
2273 * g_unix_mount_point_get_options:
2274 * @mount_point: a #GUnixMountPoint.
2276 * Gets the options for the mount point.
2278 * Returns: a string containing the options.
2280 * Since: 2.32
2282 const gchar *
2283 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2285 g_return_val_if_fail (mount_point != NULL, NULL);
2287 return mount_point->options;
2291 * g_unix_mount_point_is_readonly:
2292 * @mount_point: a #GUnixMountPoint.
2294 * Checks if a unix mount point is read only.
2296 * Returns: %TRUE if a mount point is read only.
2298 gboolean
2299 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2301 g_return_val_if_fail (mount_point != NULL, FALSE);
2303 return mount_point->is_read_only;
2307 * g_unix_mount_point_is_user_mountable:
2308 * @mount_point: a #GUnixMountPoint.
2310 * Checks if a unix mount point is mountable by the user.
2312 * Returns: %TRUE if the mount point is user mountable.
2314 gboolean
2315 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2317 g_return_val_if_fail (mount_point != NULL, FALSE);
2319 return mount_point->is_user_mountable;
2323 * g_unix_mount_point_is_loopback:
2324 * @mount_point: a #GUnixMountPoint.
2326 * Checks if a unix mount point is a loopback device.
2328 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
2330 gboolean
2331 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2333 g_return_val_if_fail (mount_point != NULL, FALSE);
2335 return mount_point->is_loopback;
2338 static GUnixMountType
2339 guess_mount_type (const char *mount_path,
2340 const char *device_path,
2341 const char *filesystem_type)
2343 GUnixMountType type;
2344 char *basename;
2346 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2348 if ((strcmp (filesystem_type, "udf") == 0) ||
2349 (strcmp (filesystem_type, "iso9660") == 0) ||
2350 (strcmp (filesystem_type, "cd9660") == 0))
2351 type = G_UNIX_MOUNT_TYPE_CDROM;
2352 else if ((strcmp (filesystem_type, "nfs") == 0) ||
2353 (strcmp (filesystem_type, "nfs4") == 0))
2354 type = G_UNIX_MOUNT_TYPE_NFS;
2355 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
2356 g_str_has_prefix (device_path, "/dev/fd") ||
2357 g_str_has_prefix (device_path, "/dev/floppy"))
2358 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2359 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
2360 g_str_has_prefix (device_path, "/dev/acd") ||
2361 g_str_has_prefix (device_path, "/dev/cd"))
2362 type = G_UNIX_MOUNT_TYPE_CDROM;
2363 else if (g_str_has_prefix (device_path, "/vol/"))
2365 const char *name = mount_path + strlen ("/");
2367 if (g_str_has_prefix (name, "cdrom"))
2368 type = G_UNIX_MOUNT_TYPE_CDROM;
2369 else if (g_str_has_prefix (name, "floppy") ||
2370 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
2371 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2372 else if (g_str_has_prefix (name, "rmdisk"))
2373 type = G_UNIX_MOUNT_TYPE_ZIP;
2374 else if (g_str_has_prefix (name, "jaz"))
2375 type = G_UNIX_MOUNT_TYPE_JAZ;
2376 else if (g_str_has_prefix (name, "memstick"))
2377 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2379 else
2381 basename = g_path_get_basename (mount_path);
2383 if (g_str_has_prefix (basename, "cdr") ||
2384 g_str_has_prefix (basename, "cdwriter") ||
2385 g_str_has_prefix (basename, "burn") ||
2386 g_str_has_prefix (basename, "dvdr"))
2387 type = G_UNIX_MOUNT_TYPE_CDROM;
2388 else if (g_str_has_prefix (basename, "floppy"))
2389 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2390 else if (g_str_has_prefix (basename, "zip"))
2391 type = G_UNIX_MOUNT_TYPE_ZIP;
2392 else if (g_str_has_prefix (basename, "jaz"))
2393 type = G_UNIX_MOUNT_TYPE_JAZ;
2394 else if (g_str_has_prefix (basename, "camera"))
2395 type = G_UNIX_MOUNT_TYPE_CAMERA;
2396 else if (g_str_has_prefix (basename, "memstick") ||
2397 g_str_has_prefix (basename, "memory_stick") ||
2398 g_str_has_prefix (basename, "ram"))
2399 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2400 else if (g_str_has_prefix (basename, "compact_flash"))
2401 type = G_UNIX_MOUNT_TYPE_CF;
2402 else if (g_str_has_prefix (basename, "smart_media"))
2403 type = G_UNIX_MOUNT_TYPE_SM;
2404 else if (g_str_has_prefix (basename, "sd_mmc"))
2405 type = G_UNIX_MOUNT_TYPE_SDMMC;
2406 else if (g_str_has_prefix (basename, "ipod"))
2407 type = G_UNIX_MOUNT_TYPE_IPOD;
2409 g_free (basename);
2412 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2413 type = G_UNIX_MOUNT_TYPE_HD;
2415 return type;
2419 * g_unix_mount_guess_type:
2420 * @mount_entry: a #GUnixMount.
2422 * Guesses the type of a unix mount. If the mount type cannot be
2423 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2425 * Returns: a #GUnixMountType.
2427 static GUnixMountType
2428 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2430 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2431 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2432 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2433 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2435 return guess_mount_type (mount_entry->mount_path,
2436 mount_entry->device_path,
2437 mount_entry->filesystem_type);
2441 * g_unix_mount_point_guess_type:
2442 * @mount_point: a #GUnixMountPoint.
2444 * Guesses the type of a unix mount point.
2445 * If the mount type cannot be determined,
2446 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2448 * Returns: a #GUnixMountType.
2450 static GUnixMountType
2451 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2453 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2454 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2455 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2456 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2458 return guess_mount_type (mount_point->mount_path,
2459 mount_point->device_path,
2460 mount_point->filesystem_type);
2463 static const char *
2464 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2466 const char *icon_name;
2468 switch (type)
2470 case G_UNIX_MOUNT_TYPE_HD:
2471 if (is_mount_point)
2472 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2473 else
2474 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2475 break;
2476 case G_UNIX_MOUNT_TYPE_FLOPPY:
2477 case G_UNIX_MOUNT_TYPE_ZIP:
2478 case G_UNIX_MOUNT_TYPE_JAZ:
2479 if (is_mount_point)
2480 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2481 else
2482 icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2483 break;
2484 case G_UNIX_MOUNT_TYPE_CDROM:
2485 if (is_mount_point)
2486 icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2487 else
2488 icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2489 break;
2490 case G_UNIX_MOUNT_TYPE_NFS:
2491 icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2492 break;
2493 case G_UNIX_MOUNT_TYPE_MEMSTICK:
2494 if (is_mount_point)
2495 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2496 else
2497 icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2498 break;
2499 case G_UNIX_MOUNT_TYPE_CAMERA:
2500 if (is_mount_point)
2501 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2502 else
2503 icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2504 break;
2505 case G_UNIX_MOUNT_TYPE_IPOD:
2506 if (is_mount_point)
2507 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2508 else
2509 icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2510 break;
2511 case G_UNIX_MOUNT_TYPE_UNKNOWN:
2512 default:
2513 if (is_mount_point)
2514 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2515 else
2516 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2517 break;
2520 return icon_name;
2524 * g_unix_mount_guess_name:
2525 * @mount_entry: a #GUnixMountEntry
2527 * Guesses the name of a Unix mount.
2528 * The result is a translated string.
2530 * Returns: A newly allocated string that must
2531 * be freed with g_free()
2533 gchar *
2534 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2536 char *name;
2538 if (strcmp (mount_entry->mount_path, "/") == 0)
2539 name = g_strdup (_("Filesystem root"));
2540 else
2541 name = g_filename_display_basename (mount_entry->mount_path);
2543 return name;
2547 * g_unix_mount_guess_icon:
2548 * @mount_entry: a #GUnixMountEntry
2550 * Guesses the icon of a Unix mount.
2552 * Returns: (transfer full): a #GIcon
2554 GIcon *
2555 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2557 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2561 * g_unix_mount_guess_symbolic_icon:
2562 * @mount_entry: a #GUnixMountEntry
2564 * Guesses the symbolic icon of a Unix mount.
2566 * Returns: (transfer full): a #GIcon
2568 * Since: 2.34
2570 GIcon *
2571 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2573 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2577 * g_unix_mount_point_guess_name:
2578 * @mount_point: a #GUnixMountPoint
2580 * Guesses the name of a Unix mount point.
2581 * The result is a translated string.
2583 * Returns: A newly allocated string that must
2584 * be freed with g_free()
2586 gchar *
2587 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2589 char *name;
2591 if (strcmp (mount_point->mount_path, "/") == 0)
2592 name = g_strdup (_("Filesystem root"));
2593 else
2594 name = g_filename_display_basename (mount_point->mount_path);
2596 return name;
2600 * g_unix_mount_point_guess_icon:
2601 * @mount_point: a #GUnixMountPoint
2603 * Guesses the icon of a Unix mount point.
2605 * Returns: (transfer full): a #GIcon
2607 GIcon *
2608 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2610 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2614 * g_unix_mount_point_guess_symbolic_icon:
2615 * @mount_point: a #GUnixMountPoint
2617 * Guesses the symbolic icon of a Unix mount point.
2619 * Returns: (transfer full): a #GIcon
2621 * Since: 2.34
2623 GIcon *
2624 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2626 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2630 * g_unix_mount_guess_can_eject:
2631 * @mount_entry: a #GUnixMountEntry
2633 * Guesses whether a Unix mount can be ejected.
2635 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2637 gboolean
2638 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2640 GUnixMountType guessed_type;
2642 guessed_type = g_unix_mount_guess_type (mount_entry);
2643 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2644 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2645 return TRUE;
2647 return FALSE;
2651 * g_unix_mount_guess_should_display:
2652 * @mount_entry: a #GUnixMountEntry
2654 * Guesses whether a Unix mount should be displayed in the UI.
2656 * Returns: %TRUE if @mount_entry is deemed to be displayable.
2658 gboolean
2659 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2661 const char *mount_path;
2662 const gchar *user_name;
2663 gsize user_name_len;
2665 /* Never display internal mountpoints */
2666 if (g_unix_mount_is_system_internal (mount_entry))
2667 return FALSE;
2669 /* Only display things in /media (which are generally user mountable)
2670 and home dir (fuse stuff) and /run/media/$USER */
2671 mount_path = mount_entry->mount_path;
2672 if (mount_path != NULL)
2674 gboolean is_in_runtime_dir = FALSE;
2675 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2676 if (g_strstr_len (mount_path, -1, "/.") != NULL)
2677 return FALSE;
2679 /* Check /run/media/$USER/ */
2680 user_name = g_get_user_name ();
2681 user_name_len = strlen (user_name);
2682 if (strncmp (mount_path, "/run/media/", sizeof ("/run/media/") - 1) == 0 &&
2683 strncmp (mount_path + sizeof ("/run/media/") - 1, user_name, user_name_len) == 0 &&
2684 mount_path[sizeof ("/run/media/") - 1 + user_name_len] == '/')
2685 is_in_runtime_dir = TRUE;
2687 if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2689 char *path;
2690 /* Avoid displaying mounts that are not accessible to the user.
2692 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2693 * want to avoid g_access() for mount points which can potentially
2694 * block or fail stat()'ing, such as network mounts.
2696 path = g_path_get_dirname (mount_path);
2697 if (g_str_has_prefix (path, "/media/"))
2699 if (g_access (path, R_OK|X_OK) != 0)
2701 g_free (path);
2702 return FALSE;
2705 g_free (path);
2707 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2709 struct stat st;
2710 if (g_stat (mount_entry->device_path, &st) == 0 &&
2711 S_ISBLK(st.st_mode) &&
2712 g_access (mount_path, R_OK|X_OK) != 0)
2713 return FALSE;
2715 return TRUE;
2718 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
2719 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2720 return TRUE;
2723 return FALSE;
2727 * g_unix_mount_point_guess_can_eject:
2728 * @mount_point: a #GUnixMountPoint
2730 * Guesses whether a Unix mount point can be ejected.
2732 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2734 gboolean
2735 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2737 GUnixMountType guessed_type;
2739 guessed_type = g_unix_mount_point_guess_type (mount_point);
2740 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2741 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2742 return TRUE;
2744 return FALSE;
2747 /* Utility functions {{{1 */
2749 #ifdef HAVE_MNTENT_H
2750 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2751 static void
2752 _canonicalize_filename (gchar *filename)
2754 gchar *p, *q;
2755 gboolean last_was_slash = FALSE;
2757 p = filename;
2758 q = filename;
2760 while (*p)
2762 if (*p == G_DIR_SEPARATOR)
2764 if (!last_was_slash)
2765 *q++ = G_DIR_SEPARATOR;
2767 last_was_slash = TRUE;
2769 else
2771 if (last_was_slash && *p == '.')
2773 if (*(p + 1) == G_DIR_SEPARATOR ||
2774 *(p + 1) == '\0')
2776 if (*(p + 1) == '\0')
2777 break;
2779 p += 1;
2781 else if (*(p + 1) == '.' &&
2782 (*(p + 2) == G_DIR_SEPARATOR ||
2783 *(p + 2) == '\0'))
2785 if (q > filename + 1)
2787 q--;
2788 while (q > filename + 1 &&
2789 *(q - 1) != G_DIR_SEPARATOR)
2790 q--;
2793 if (*(p + 2) == '\0')
2794 break;
2796 p += 2;
2798 else
2800 *q++ = *p;
2801 last_was_slash = FALSE;
2804 else
2806 *q++ = *p;
2807 last_was_slash = FALSE;
2811 p++;
2814 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2815 q--;
2817 *q = '\0';
2820 static char *
2821 _resolve_symlink (const char *file)
2823 GError *error;
2824 char *dir;
2825 char *link;
2826 char *f;
2827 char *f1;
2829 f = g_strdup (file);
2831 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
2833 link = g_file_read_link (f, &error);
2834 if (link == NULL)
2836 g_error_free (error);
2837 g_free (f);
2838 f = NULL;
2839 goto out;
2842 dir = g_path_get_dirname (f);
2843 f1 = g_strdup_printf ("%s/%s", dir, link);
2844 g_free (dir);
2845 g_free (link);
2846 g_free (f);
2847 f = f1;
2850 out:
2851 if (f != NULL)
2852 _canonicalize_filename (f);
2853 return f;
2856 static const char *
2857 _resolve_dev_root (void)
2859 static gboolean have_real_dev_root = FALSE;
2860 static char real_dev_root[256];
2861 struct stat statbuf;
2863 /* see if it's cached already */
2864 if (have_real_dev_root)
2865 goto found;
2867 /* otherwise we're going to find it right away.. */
2868 have_real_dev_root = TRUE;
2870 if (stat ("/dev/root", &statbuf) == 0)
2872 if (! S_ISLNK (statbuf.st_mode))
2874 dev_t root_dev = statbuf.st_dev;
2875 FILE *f;
2877 /* see if device with similar major:minor as /dev/root is mention
2878 * in /etc/mtab (it usually is)
2880 f = fopen ("/etc/mtab", "r");
2881 if (f != NULL)
2883 struct mntent *entp;
2884 #ifdef HAVE_GETMNTENT_R
2885 struct mntent ent;
2886 char buf[1024];
2887 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
2889 #else
2890 G_LOCK (getmntent);
2891 while ((entp = getmntent (f)) != NULL)
2893 #endif
2894 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2895 statbuf.st_dev == root_dev)
2897 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2898 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2899 fclose (f);
2900 goto found;
2904 endmntent (f);
2906 #ifndef HAVE_GETMNTENT_R
2907 G_UNLOCK (getmntent);
2908 #endif
2911 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2914 else
2916 char *resolved;
2917 resolved = _resolve_symlink ("/dev/root");
2918 if (resolved != NULL)
2920 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2921 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2922 g_free (resolved);
2923 goto found;
2928 /* bah sucks.. */
2929 strcpy (real_dev_root, "/dev/root");
2931 found:
2932 return real_dev_root;
2934 #endif
2936 /* Epilogue {{{1 */
2937 /* vim:set foldmethod=marker: */