gosxappinfo: fix typo in url_escape_hostname
[glib.git] / gio / gunixmounts.c
blobb2e720bf9b7b9885b20e8c6cca210982749337ef
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 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);
152 static guint64 mount_poller_time = 0;
154 #ifdef HAVE_SYS_MNTTAB_H
155 #define MNTOPT_RO "ro"
156 #endif
158 #ifdef HAVE_MNTENT_H
159 #include <mntent.h>
160 #ifdef HAVE_LIBMOUNT
161 #include <libmount/libmount.h>
162 #endif
163 #elif defined (HAVE_SYS_MNTTAB_H)
164 #include <sys/mnttab.h>
165 #endif
167 #ifdef HAVE_SYS_VFSTAB_H
168 #include <sys/vfstab.h>
169 #endif
171 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
172 #include <sys/mntctl.h>
173 #include <sys/vfs.h>
174 #include <sys/vmount.h>
175 #include <fshelp.h>
176 #endif
178 #if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
179 #include <sys/param.h>
180 #include <sys/ucred.h>
181 #include <sys/mount.h>
182 #include <fstab.h>
183 #ifdef HAVE_SYS_SYSCTL_H
184 #include <sys/sysctl.h>
185 #endif
186 #endif
188 #ifndef HAVE_SETMNTENT
189 #define setmntent(f,m) fopen(f,m)
190 #endif
191 #ifndef HAVE_ENDMNTENT
192 #define endmntent(f) fclose(f)
193 #endif
195 static gboolean
196 is_in (const char *value, const char *set[])
198 int i;
199 for (i = 0; set[i] != NULL; i++)
201 if (strcmp (set[i], value) == 0)
202 return TRUE;
204 return FALSE;
208 * g_unix_is_mount_path_system_internal:
209 * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr`
211 * Determines if @mount_path is considered an implementation of the
212 * OS. This is primarily used for hiding mountable and mounted volumes
213 * that only are used in the OS and has little to no relevance to the
214 * casual user.
216 * Returns: %TRUE if @mount_path is considered an implementation detail
217 * of the OS.
219 gboolean
220 g_unix_is_mount_path_system_internal (const char *mount_path)
222 const char *ignore_mountpoints[] = {
223 /* Includes all FHS 2.3 toplevel dirs and other specialized
224 * directories that we want to hide from the user.
226 "/", /* we already have "Filesystem root" in Nautilus */
227 "/bin",
228 "/boot",
229 "/compat/linux/proc",
230 "/compat/linux/sys",
231 "/dev",
232 "/etc",
233 "/home",
234 "/lib",
235 "/lib64",
236 "/libexec",
237 "/live/cow",
238 "/live/image",
239 "/media",
240 "/mnt",
241 "/opt",
242 "/rescue",
243 "/root",
244 "/sbin",
245 "/srv",
246 "/tmp",
247 "/usr",
248 "/usr/X11R6",
249 "/usr/local",
250 "/usr/obj",
251 "/usr/ports",
252 "/usr/src",
253 "/usr/xobj",
254 "/var",
255 "/var/crash",
256 "/var/local",
257 "/var/log",
258 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
259 "/var/mail",
260 "/var/run",
261 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
262 "/proc",
263 "/sbin",
264 "/net",
265 "/sys",
266 NULL
269 if (is_in (mount_path, ignore_mountpoints))
270 return TRUE;
272 if (g_str_has_prefix (mount_path, "/dev/") ||
273 g_str_has_prefix (mount_path, "/proc/") ||
274 g_str_has_prefix (mount_path, "/sys/"))
275 return TRUE;
277 if (g_str_has_suffix (mount_path, "/.gvfs"))
278 return TRUE;
280 return FALSE;
283 static gboolean
284 guess_system_internal (const char *mountpoint,
285 const char *fs,
286 const char *device)
288 const char *ignore_fs[] = {
289 "auto",
290 "autofs",
291 "devfs",
292 "devpts",
293 "ecryptfs",
294 "fdescfs",
295 "kernfs",
296 "linprocfs",
297 "mfs",
298 "nullfs",
299 "proc",
300 "procfs",
301 "ptyfs",
302 "rootfs",
303 "selinuxfs",
304 "sysfs",
305 "tmpfs",
306 "usbfs",
307 "nfsd",
308 "rpc_pipefs",
309 "zfs",
310 NULL
312 const char *ignore_devices[] = {
313 "none",
314 "sunrpc",
315 "devpts",
316 "nfsd",
317 "/dev/loop",
318 "/dev/vn",
319 NULL
322 if (is_in (fs, ignore_fs))
323 return TRUE;
325 if (is_in (device, ignore_devices))
326 return TRUE;
328 if (g_unix_is_mount_path_system_internal (mountpoint))
329 return TRUE;
331 return FALSE;
334 /* GUnixMounts (ie: mtab) implementations {{{1 */
336 static GUnixMountEntry *
337 create_unix_mount_entry (const char *device_path,
338 const char *mount_path,
339 const char *filesystem_type,
340 gboolean is_read_only)
342 GUnixMountEntry *mount_entry = NULL;
344 mount_entry = g_new0 (GUnixMountEntry, 1);
345 mount_entry->device_path = g_strdup (device_path);
346 mount_entry->mount_path = g_strdup (mount_path);
347 mount_entry->filesystem_type = g_strdup (filesystem_type);
348 mount_entry->is_read_only = is_read_only;
350 mount_entry->is_system_internal =
351 guess_system_internal (mount_entry->mount_path,
352 mount_entry->filesystem_type,
353 mount_entry->device_path);
354 return mount_entry;
357 static GUnixMountPoint *
358 create_unix_mount_point (const char *device_path,
359 const char *mount_path,
360 const char *filesystem_type,
361 const char *options,
362 gboolean is_read_only,
363 gboolean is_user_mountable,
364 gboolean is_loopback)
366 GUnixMountPoint *mount_point = NULL;
368 mount_point = g_new0 (GUnixMountPoint, 1);
369 mount_point->device_path = g_strdup (device_path);
370 mount_point->mount_path = g_strdup (mount_path);
371 mount_point->filesystem_type = g_strdup (filesystem_type);
372 mount_point->options = g_strdup (options);
373 mount_point->is_read_only = is_read_only;
374 mount_point->is_user_mountable = is_user_mountable;
375 mount_point->is_loopback = is_loopback;
377 return mount_point;
380 /* mntent.h (Linux, GNU, NSS) {{{2 */
381 #ifdef HAVE_MNTENT_H
383 #ifdef HAVE_LIBMOUNT
385 /* For documentation on /proc/self/mountinfo see
386 * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
388 #define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
390 static GList *
391 _g_get_unix_mounts (void)
393 struct libmnt_table *table = NULL;
394 struct libmnt_context *ctxt = NULL;
395 struct libmnt_iter* iter = NULL;
396 struct libmnt_fs *fs = NULL;
397 GUnixMountEntry *mount_entry = NULL;
398 GList *return_list = NULL;
400 ctxt = mnt_new_context ();
401 mnt_context_get_mtab (ctxt, &table);
402 if (!table)
403 goto out;
405 iter = mnt_new_iter (MNT_ITER_FORWARD);
406 while (mnt_table_next_fs (table, iter, &fs) == 0)
408 const char *device_path = NULL;
409 char *mount_options = NULL;
410 unsigned long mount_flags = 0;
411 gboolean is_read_only = FALSE;
413 if (!mnt_table_is_fs_mounted (table, fs))
414 continue;
416 device_path = mnt_fs_get_source (fs);
417 if (g_strcmp0 (device_path, "/dev/root") == 0)
418 device_path = _resolve_dev_root ();
420 mount_options = mnt_fs_strdup_options (fs);
421 if (mount_options)
423 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
424 g_free (mount_options);
426 is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
428 mount_entry = create_unix_mount_entry (device_path,
429 mnt_fs_get_target (fs),
430 mnt_fs_get_fstype (fs),
431 is_read_only);
433 return_list = g_list_prepend (return_list, mount_entry);
435 mnt_free_iter (iter);
437 out:
438 mnt_free_context (ctxt);
440 return g_list_reverse (return_list);
443 #else
445 static char *
446 get_mtab_read_file (void)
448 #ifdef _PATH_MOUNTED
449 # ifdef __linux__
450 return "/proc/mounts";
451 # else
452 return _PATH_MOUNTED;
453 # endif
454 #else
455 return "/etc/mtab";
456 #endif
459 #ifndef HAVE_GETMNTENT_R
460 G_LOCK_DEFINE_STATIC(getmntent);
461 #endif
463 static GList *
464 _g_get_unix_mounts (void)
466 #ifdef HAVE_GETMNTENT_R
467 struct mntent ent;
468 char buf[1024];
469 #endif
470 struct mntent *mntent;
471 FILE *file;
472 char *read_file;
473 GUnixMountEntry *mount_entry;
474 GHashTable *mounts_hash;
475 GList *return_list;
477 read_file = get_mtab_read_file ();
479 file = setmntent (read_file, "r");
480 if (file == NULL)
481 return NULL;
483 return_list = NULL;
485 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
487 #ifdef HAVE_GETMNTENT_R
488 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
489 #else
490 G_LOCK (getmntent);
491 while ((mntent = getmntent (file)) != NULL)
492 #endif
494 const char *device_path = NULL;
495 gboolean is_read_only = FALSE;
497 /* ignore any mnt_fsname that is repeated and begins with a '/'
499 * We do this to avoid being fooled by --bind mounts, since
500 * these have the same device as the location they bind to.
501 * It's not an ideal solution to the problem, but it's likely that
502 * the most important mountpoint is first and the --bind ones after
503 * that aren't as important. So it should work.
505 * The '/' is to handle procfs, tmpfs and other no device mounts.
507 if (mntent->mnt_fsname != NULL &&
508 mntent->mnt_fsname[0] == '/' &&
509 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
510 continue;
512 if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
513 device_path = _resolve_dev_root ();
514 else
515 device_path = mntent->mnt_fsname;
517 #if defined (HAVE_HASMNTOPT)
518 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
519 is_read_only = TRUE;
520 #endif
522 mount_entry = create_unix_mount_entry (device_path,
523 mntent->mnt_dir,
524 mntent->mnt_type,
525 is_read_only);
527 g_hash_table_insert (mounts_hash,
528 mount_entry->device_path,
529 mount_entry->device_path);
531 return_list = g_list_prepend (return_list, mount_entry);
533 g_hash_table_destroy (mounts_hash);
535 endmntent (file);
537 #ifndef HAVE_GETMNTENT_R
538 G_UNLOCK (getmntent);
539 #endif
541 return g_list_reverse (return_list);
544 #endif /* HAVE_LIBMOUNT */
546 static char *
547 get_mtab_monitor_file (void)
549 static char *mountinfo_path = NULL;
550 #ifdef HAVE_LIBMOUNT
551 struct stat buf;
552 #endif
554 if (mountinfo_path != NULL)
555 return mountinfo_path;
557 #ifdef HAVE_LIBMOUNT
558 /* If using libmount we'll have the logic in place to read mountinfo */
559 if (stat (PROC_MOUNTINFO_PATH, &buf) == 0)
561 mountinfo_path = PROC_MOUNTINFO_PATH;
562 return mountinfo_path;
564 #endif
566 #ifdef _PATH_MOUNTED
567 # ifdef __linux__
568 mountinfo_path = "/proc/mounts";
569 # else
570 mountinfo_path = _PATH_MOUNTED;
571 # endif
572 #else
573 mountinfo_path = "/etc/mtab";
574 #endif
576 return mountinfo_path;
579 /* mnttab.h {{{2 */
580 #elif defined (HAVE_SYS_MNTTAB_H)
582 G_LOCK_DEFINE_STATIC(getmntent);
584 static char *
585 get_mtab_read_file (void)
587 #ifdef _PATH_MOUNTED
588 return _PATH_MOUNTED;
589 #else
590 return "/etc/mnttab";
591 #endif
594 static char *
595 get_mtab_monitor_file (void)
597 return get_mtab_read_file ();
600 static GList *
601 _g_get_unix_mounts (void)
603 struct mnttab mntent;
604 FILE *file;
605 char *read_file;
606 GUnixMountEntry *mount_entry;
607 GList *return_list;
609 read_file = get_mtab_read_file ();
611 file = setmntent (read_file, "r");
612 if (file == NULL)
613 return NULL;
615 return_list = NULL;
617 G_LOCK (getmntent);
618 while (! getmntent (file, &mntent))
620 gboolean is_read_only = FALSE;
622 #if defined (HAVE_HASMNTOPT)
623 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
624 is_read_only = TRUE;
625 #endif
627 mount_entry = create_unix_mount_entry (mntent.mnt_special,
628 mntent.mnt_mountp,
629 mntent.mnt_fstype,
630 is_read_only);
632 return_list = g_list_prepend (return_list, mount_entry);
635 endmntent (file);
637 G_UNLOCK (getmntent);
639 return g_list_reverse (return_list);
642 /* mntctl.h (AIX) {{{2 */
643 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
645 static char *
646 get_mtab_monitor_file (void)
648 return NULL;
651 static GList *
652 _g_get_unix_mounts (void)
654 struct vfs_ent *fs_info;
655 struct vmount *vmount_info;
656 int vmount_number;
657 unsigned int vmount_size;
658 int current;
659 GList *return_list;
661 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
663 g_warning ("Unable to know the number of mounted volumes\n");
665 return NULL;
668 vmount_info = (struct vmount*)g_malloc (vmount_size);
670 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
672 if (vmount_info->vmt_revision != VMT_REVISION)
673 g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
675 if (vmount_number < 0)
677 g_warning ("Unable to recover mounted volumes information\n");
679 g_free (vmount_info);
680 return NULL;
683 return_list = NULL;
684 while (vmount_number > 0)
686 gboolean is_read_only = FALSE;
688 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
690 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
691 is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
693 mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
694 vmt2dataptr (vmount_info, VMT_STUB),
695 fs_info == NULL ? "unknown" : fs_info->vfsent_name,
696 is_read_only);
698 return_list = g_list_prepend (return_list, mount_entry);
700 vmount_info = (struct vmount *)( (char*)vmount_info
701 + vmount_info->vmt_length);
702 vmount_number--;
705 g_free (vmount_info);
707 return g_list_reverse (return_list);
710 /* sys/mount.h {{{2 */
711 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
713 static char *
714 get_mtab_monitor_file (void)
716 return NULL;
719 static GList *
720 _g_get_unix_mounts (void)
722 #if defined(USE_STATVFS)
723 struct statvfs *mntent = NULL;
724 #elif defined(USE_STATFS)
725 struct statfs *mntent = NULL;
726 #else
727 #error statfs juggling failed
728 #endif
729 size_t bufsize;
730 int num_mounts, i;
731 GUnixMountEntry *mount_entry;
732 GList *return_list;
734 /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
735 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
736 num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
737 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
738 num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
739 #endif
740 if (num_mounts == -1)
741 return NULL;
743 bufsize = num_mounts * sizeof (*mntent);
744 mntent = g_malloc (bufsize);
745 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
746 num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
747 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
748 num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
749 #endif
750 if (num_mounts == -1)
751 return NULL;
753 return_list = NULL;
755 for (i = 0; i < num_mounts; i++)
757 gboolean is_read_only = FALSE;
759 #if defined(USE_STATVFS)
760 if (mntent[i].f_flag & ST_RDONLY)
761 #elif defined(USE_STATFS)
762 if (mntent[i].f_flags & MNT_RDONLY)
763 #else
764 #error statfs juggling failed
765 #endif
766 is_read_only = TRUE;
768 mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
769 mntent[i].f_mntonname,
770 mntent[i].f_fstypename,
771 is_read_only);
773 return_list = g_list_prepend (return_list, mount_entry);
776 g_free (mntent);
778 return g_list_reverse (return_list);
781 /* Interix {{{2 */
782 #elif defined(__INTERIX)
784 static char *
785 get_mtab_monitor_file (void)
787 return NULL;
790 static GList *
791 _g_get_unix_mounts (void)
793 DIR *dirp;
794 GList* return_list = NULL;
795 char filename[9 + NAME_MAX];
797 dirp = opendir ("/dev/fs");
798 if (!dirp)
800 g_warning ("unable to read /dev/fs!");
801 return NULL;
804 while (1)
806 struct statvfs statbuf;
807 struct dirent entry;
808 struct dirent* result;
810 if (readdir_r (dirp, &entry, &result) || result == NULL)
811 break;
813 strcpy (filename, "/dev/fs/");
814 strcat (filename, entry.d_name);
816 if (statvfs (filename, &statbuf) == 0)
818 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
820 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
821 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
822 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
824 if (statbuf.f_flag & ST_RDONLY)
825 mount_entry->is_read_only = TRUE;
827 return_list = g_list_prepend(return_list, mount_entry);
831 return_list = g_list_reverse (return_list);
833 closedir (dirp);
835 return return_list;
838 /* Common code {{{2 */
839 #else
840 #error No _g_get_unix_mounts() implementation for system
841 #endif
843 /* GUnixMountPoints (ie: fstab) implementations {{{1 */
845 /* _g_get_unix_mount_points():
846 * read the fstab.
847 * don't return swap and ignore mounts.
850 static char *
851 get_fstab_file (void)
853 #ifdef HAVE_LIBMOUNT
854 return (char *) mnt_get_fstab_path ();
855 #else
856 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
857 /* AIX */
858 return "/etc/filesystems";
859 #elif defined(_PATH_MNTTAB)
860 return _PATH_MNTTAB;
861 #elif defined(VFSTAB)
862 return VFSTAB;
863 #else
864 return "/etc/fstab";
865 #endif
866 #endif
869 /* mntent.h (Linux, GNU, NSS) {{{2 */
870 #ifdef HAVE_MNTENT_H
872 #ifdef HAVE_LIBMOUNT
874 static GList *
875 _g_get_unix_mount_points (void)
877 struct libmnt_table *table = NULL;
878 struct libmnt_context *ctxt = NULL;
879 struct libmnt_iter* iter = NULL;
880 struct libmnt_fs *fs = NULL;
881 GUnixMountPoint *mount_point = NULL;
882 GList *return_list = NULL;
884 ctxt = mnt_new_context ();
885 mnt_context_get_fstab (ctxt, &table);
886 if (!table)
887 goto out;
889 iter = mnt_new_iter (MNT_ITER_FORWARD);
890 while (mnt_table_next_fs (table, iter, &fs) == 0)
892 const char *device_path = NULL;
893 const char *mount_path = NULL;
894 const char *mount_fstype = NULL;
895 char *mount_options = NULL;
896 gboolean is_read_only = FALSE;
897 gboolean is_user_mountable = FALSE;
898 gboolean is_loopback = FALSE;
900 mount_path = mnt_fs_get_target (fs);
901 if ((strcmp (mount_path, "ignore") == 0) ||
902 (strcmp (mount_path, "swap") == 0) ||
903 (strcmp (mount_path, "none") == 0))
904 continue;
906 mount_fstype = mnt_fs_get_fstype (fs);
907 mount_options = mnt_fs_strdup_options (fs);
908 if (mount_options)
910 unsigned long mount_flags = 0;
911 unsigned long userspace_flags = 0;
913 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
914 mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
916 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
917 if (mount_flags & MS_BIND)
919 g_free (mount_options);
920 continue;
923 is_read_only = (mount_flags & MS_RDONLY) != 0;
924 is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
926 if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
927 ((userspace_flags & MNT_MS_USER) &&
928 (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
929 (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
930 (userspace_flags & MNT_MS_USERS) ||
931 (userspace_flags & MNT_MS_OWNER))
933 is_user_mountable = TRUE;
937 device_path = mnt_fs_get_source (fs);
938 if (g_strcmp0 (device_path, "/dev/root") == 0)
939 device_path = _resolve_dev_root ();
941 mount_point = create_unix_mount_point (device_path,
942 mount_path,
943 mount_fstype,
944 mount_options,
945 is_read_only,
946 is_user_mountable,
947 is_loopback);
948 if (mount_options)
949 g_free (mount_options);
951 return_list = g_list_prepend (return_list, mount_point);
953 mnt_free_iter (iter);
955 out:
956 mnt_free_context (ctxt);
958 return g_list_reverse (return_list);
961 #else
963 static GList *
964 _g_get_unix_mount_points (void)
966 #ifdef HAVE_GETMNTENT_R
967 struct mntent ent;
968 char buf[1024];
969 #endif
970 struct mntent *mntent;
971 FILE *file;
972 char *read_file;
973 GUnixMountPoint *mount_point;
974 GList *return_list;
976 read_file = get_fstab_file ();
978 file = setmntent (read_file, "r");
979 if (file == NULL)
980 return NULL;
982 return_list = NULL;
984 #ifdef HAVE_GETMNTENT_R
985 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
986 #else
987 G_LOCK (getmntent);
988 while ((mntent = getmntent (file)) != NULL)
989 #endif
991 const char *device_path = NULL;
992 gboolean is_read_only = FALSE;
993 gboolean is_user_mountable = FALSE;
994 gboolean is_loopback = FALSE;
996 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
997 (strcmp (mntent->mnt_dir, "swap") == 0) ||
998 (strcmp (mntent->mnt_dir, "none") == 0))
999 continue;
1001 #ifdef HAVE_HASMNTOPT
1002 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1003 if (hasmntopt (mntent, "bind"))
1004 continue;
1005 #endif
1007 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1008 device_path = _resolve_dev_root ();
1009 else
1010 device_path = mntent->mnt_fsname;
1012 #ifdef HAVE_HASMNTOPT
1013 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1014 is_read_only = TRUE;
1016 if (hasmntopt (mntent, "loop") != NULL)
1017 is_loopback = TRUE;
1019 #endif
1021 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1022 #ifdef HAVE_HASMNTOPT
1023 || (hasmntopt (mntent, "user") != NULL
1024 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1025 || hasmntopt (mntent, "pamconsole") != NULL
1026 || hasmntopt (mntent, "users") != NULL
1027 || hasmntopt (mntent, "owner") != NULL
1028 #endif
1030 is_user_mountable = TRUE;
1032 mount_point = create_unix_mount_point (device_path,
1033 mntent->mnt_dir,
1034 mntent->mnt_type,
1035 mntent->mnt_opts,
1036 is_read_only,
1037 is_user_mountable,
1038 is_loopback);
1040 return_list = g_list_prepend (return_list, mount_point);
1043 endmntent (file);
1045 #ifndef HAVE_GETMNTENT_R
1046 G_UNLOCK (getmntent);
1047 #endif
1049 return g_list_reverse (return_list);
1052 #endif /* HAVE_LIBMOUNT */
1054 /* mnttab.h {{{2 */
1055 #elif defined (HAVE_SYS_MNTTAB_H)
1057 static GList *
1058 _g_get_unix_mount_points (void)
1060 struct mnttab mntent;
1061 FILE *file;
1062 char *read_file;
1063 GUnixMountPoint *mount_point;
1064 GList *return_list;
1066 read_file = get_fstab_file ();
1068 file = setmntent (read_file, "r");
1069 if (file == NULL)
1070 return NULL;
1072 return_list = NULL;
1074 G_LOCK (getmntent);
1075 while (! getmntent (file, &mntent))
1077 gboolean is_read_only = FALSE;
1078 gboolean is_user_mountable = FALSE;
1079 gboolean is_loopback = FALSE;
1081 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1082 (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1083 (strcmp (mntent.mnt_mountp, "none") == 0))
1084 continue;
1086 #ifdef HAVE_HASMNTOPT
1087 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1088 is_read_only = TRUE;
1090 if (hasmntopt (&mntent, "lofs") != NULL)
1091 is_loopback = TRUE;
1092 #endif
1094 if ((mntent.mnt_fstype != NULL)
1095 #ifdef HAVE_HASMNTOPT
1096 || (hasmntopt (&mntent, "user") != NULL
1097 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1098 || hasmntopt (&mntent, "pamconsole") != NULL
1099 || hasmntopt (&mntent, "users") != NULL
1100 || hasmntopt (&mntent, "owner") != NULL
1101 #endif
1103 is_user_mountable = TRUE;
1105 mount_point = create_unix_mount_point (mntent.mnt_special,
1106 mntent.mnt_mountp,
1107 mntent.mnt_fstype,
1108 mntent.mnt_mntopts,
1109 is_read_only,
1110 is_user_mountable,
1111 is_loopback);
1113 return_list = g_list_prepend (return_list, mount_point);
1116 endmntent (file);
1117 G_UNLOCK (getmntent);
1119 return g_list_reverse (return_list);
1122 /* mntctl.h (AIX) {{{2 */
1123 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1125 /* functions to parse /etc/filesystems on aix */
1127 /* read character, ignoring comments (begin with '*', end with '\n' */
1128 static int
1129 aix_fs_getc (FILE *fd)
1131 int c;
1133 while ((c = getc (fd)) == '*')
1135 while (((c = getc (fd)) != '\n') && (c != EOF))
1140 /* eat all continuous spaces in a file */
1141 static int
1142 aix_fs_ignorespace (FILE *fd)
1144 int c;
1146 while ((c = aix_fs_getc (fd)) != EOF)
1148 if (!g_ascii_isspace (c))
1150 ungetc (c,fd);
1151 return c;
1155 return EOF;
1158 /* read one word from file */
1159 static int
1160 aix_fs_getword (FILE *fd,
1161 char *word)
1163 int c;
1165 aix_fs_ignorespace (fd);
1167 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1169 if (c == '"')
1171 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1172 *word++ = c;
1173 else
1174 *word++ = c;
1177 *word = 0;
1179 return c;
1182 typedef struct {
1183 char mnt_mount[PATH_MAX];
1184 char mnt_special[PATH_MAX];
1185 char mnt_fstype[16];
1186 char mnt_options[128];
1187 } AixMountTableEntry;
1189 /* read mount points properties */
1190 static int
1191 aix_fs_get (FILE *fd,
1192 AixMountTableEntry *prop)
1194 static char word[PATH_MAX] = { 0 };
1195 char value[PATH_MAX];
1197 /* read stanza */
1198 if (word[0] == 0)
1200 if (aix_fs_getword (fd, word) == EOF)
1201 return EOF;
1204 word[strlen(word) - 1] = 0;
1205 strcpy (prop->mnt_mount, word);
1207 /* read attributes and value */
1209 while (aix_fs_getword (fd, word) != EOF)
1211 /* test if is attribute or new stanza */
1212 if (word[strlen(word) - 1] == ':')
1213 return 0;
1215 /* read "=" */
1216 aix_fs_getword (fd, value);
1218 /* read value */
1219 aix_fs_getword (fd, value);
1221 if (strcmp (word, "dev") == 0)
1222 strcpy (prop->mnt_special, value);
1223 else if (strcmp (word, "vfs") == 0)
1224 strcpy (prop->mnt_fstype, value);
1225 else if (strcmp (word, "options") == 0)
1226 strcpy(prop->mnt_options, value);
1229 return 0;
1232 static GList *
1233 _g_get_unix_mount_points (void)
1235 struct mntent *mntent;
1236 FILE *file;
1237 char *read_file;
1238 GUnixMountPoint *mount_point;
1239 AixMountTableEntry mntent;
1240 GList *return_list;
1242 read_file = get_fstab_file ();
1244 file = setmntent (read_file, "r");
1245 if (file == NULL)
1246 return NULL;
1248 return_list = NULL;
1250 while (!aix_fs_get (file, &mntent))
1252 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1254 mount_point = create_unix_mount_point (mntent.mnt_special,
1255 mntent.mnt_mount,
1256 mntent.mnt_fstype,
1257 mntent.mnt_options,
1258 TRUE,
1259 TRUE,
1260 FALSE);
1262 return_list = g_list_prepend (return_list, mount_point);
1266 endmntent (file);
1268 return g_list_reverse (return_list);
1271 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1273 static GList *
1274 _g_get_unix_mount_points (void)
1276 struct fstab *fstab = NULL;
1277 GUnixMountPoint *mount_point;
1278 GList *return_list;
1279 #ifdef HAVE_SYS_SYSCTL_H
1280 int usermnt = 0;
1281 size_t len = sizeof(usermnt);
1282 struct stat sb;
1283 #endif
1285 if (!setfsent ())
1286 return NULL;
1288 return_list = NULL;
1290 #ifdef HAVE_SYS_SYSCTL_H
1291 #if defined(HAVE_SYSCTLBYNAME)
1292 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1293 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1295 int mib[2];
1297 mib[0] = CTL_VFS;
1298 mib[1] = VFS_USERMOUNT;
1299 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1301 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1303 int mib[2];
1305 mib[0] = CTL_KERN;
1306 mib[1] = KERN_USERMOUNT;
1307 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1309 #endif
1310 #endif
1312 while ((fstab = getfsent ()) != NULL)
1314 gboolean is_read_only = FALSE;
1315 gboolean is_user_mountable = FALSE;
1317 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1318 continue;
1320 if (strcmp (fstab->fs_type, "ro") == 0)
1321 is_read_only = TRUE;
1323 #ifdef HAVE_SYS_SYSCTL_H
1324 if (usermnt != 0)
1326 uid_t uid = getuid ();
1327 if (stat (fstab->fs_file, &sb) == 0)
1329 if (uid == 0 || sb.st_uid == uid)
1330 is_user_mountable = TRUE;
1333 #endif
1335 mount_point = create_unix_mount_point (fstab->fs_spec,
1336 fstab->fs_file,
1337 fstab->fs_vfstype,
1338 fstab->fs_mntops,
1339 is_read_only,
1340 is_user_mountable,
1341 FALSE);
1343 return_list = g_list_prepend (return_list, mount_point);
1346 endfsent ();
1348 return g_list_reverse (return_list);
1350 /* Interix {{{2 */
1351 #elif defined(__INTERIX)
1352 static GList *
1353 _g_get_unix_mount_points (void)
1355 return _g_get_unix_mounts ();
1358 /* Common code {{{2 */
1359 #else
1360 #error No g_get_mount_table() implementation for system
1361 #endif
1363 static guint64
1364 get_mounts_timestamp (void)
1366 const char *monitor_file;
1367 struct stat buf;
1369 monitor_file = get_mtab_monitor_file ();
1370 if (monitor_file)
1372 if (stat (monitor_file, &buf) == 0)
1373 return (guint64)buf.st_mtime;
1375 else
1377 return mount_poller_time;
1379 return 0;
1382 static guint64
1383 get_mount_points_timestamp (void)
1385 const char *monitor_file;
1386 struct stat buf;
1388 monitor_file = get_fstab_file ();
1389 if (monitor_file)
1391 if (stat (monitor_file, &buf) == 0)
1392 return (guint64)buf.st_mtime;
1394 return 0;
1398 * g_unix_mounts_get:
1399 * @time_read: (out) (optional): guint64 to contain a timestamp, or %NULL
1401 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1402 * If @time_read is set, it will be filled with the mount
1403 * timestamp, allowing for checking if the mounts have changed
1404 * with g_unix_mounts_changed_since().
1406 * Returns: (element-type GUnixMountEntry) (transfer full):
1407 * a #GList of the UNIX mounts.
1409 GList *
1410 g_unix_mounts_get (guint64 *time_read)
1412 if (time_read)
1413 *time_read = get_mounts_timestamp ();
1415 return _g_get_unix_mounts ();
1419 * g_unix_mount_at:
1420 * @mount_path: (type filename): path for a possible unix mount.
1421 * @time_read: (out) (optional): guint64 to contain a timestamp.
1423 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1424 * is set, it will be filled with a unix timestamp for checking
1425 * if the mounts have changed since with g_unix_mounts_changed_since().
1427 * Returns: (transfer full): a #GUnixMountEntry.
1429 GUnixMountEntry *
1430 g_unix_mount_at (const char *mount_path,
1431 guint64 *time_read)
1433 GList *mounts, *l;
1434 GUnixMountEntry *mount_entry, *found;
1436 mounts = g_unix_mounts_get (time_read);
1438 found = NULL;
1439 for (l = mounts; l != NULL; l = l->next)
1441 mount_entry = l->data;
1443 if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1444 found = mount_entry;
1445 else
1446 g_unix_mount_free (mount_entry);
1448 g_list_free (mounts);
1450 return found;
1454 * g_unix_mount_for:
1455 * @file_path: (type filename): file path on some unix mount.
1456 * @time_read: (out) (optional): guint64 to contain a timestamp.
1458 * Gets a #GUnixMountEntry for a given file path. If @time_read
1459 * is set, it will be filled with a unix timestamp for checking
1460 * if the mounts have changed since with g_unix_mounts_changed_since().
1462 * Returns: (transfer full): a #GUnixMountEntry.
1464 * Since: 2.52
1466 GUnixMountEntry *
1467 g_unix_mount_for (const char *file_path,
1468 guint64 *time_read)
1470 GUnixMountEntry *entry;
1472 g_return_val_if_fail (file_path != NULL, NULL);
1474 entry = g_unix_mount_at (file_path, time_read);
1475 if (entry == NULL)
1477 char *topdir;
1479 topdir = _g_local_file_find_topdir_for (file_path);
1480 if (topdir != NULL)
1482 entry = g_unix_mount_at (topdir, time_read);
1483 g_free (topdir);
1487 return entry;
1491 * g_unix_mount_points_get:
1492 * @time_read: (out) (optional): guint64 to contain a timestamp.
1494 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1495 * If @time_read is set, it will be filled with the mount timestamp,
1496 * allowing for checking if the mounts have changed with
1497 * g_unix_mount_points_changed_since().
1499 * Returns: (element-type GUnixMountPoint) (transfer full):
1500 * a #GList of the UNIX mountpoints.
1502 GList *
1503 g_unix_mount_points_get (guint64 *time_read)
1505 if (time_read)
1506 *time_read = get_mount_points_timestamp ();
1508 return _g_get_unix_mount_points ();
1512 * g_unix_mounts_changed_since:
1513 * @time: guint64 to contain a timestamp.
1515 * Checks if the unix mounts have changed since a given unix time.
1517 * Returns: %TRUE if the mounts have changed since @time.
1519 gboolean
1520 g_unix_mounts_changed_since (guint64 time)
1522 return get_mounts_timestamp () != time;
1526 * g_unix_mount_points_changed_since:
1527 * @time: guint64 to contain a timestamp.
1529 * Checks if the unix mount points have changed since a given unix time.
1531 * Returns: %TRUE if the mount points have changed since @time.
1533 gboolean
1534 g_unix_mount_points_changed_since (guint64 time)
1536 return get_mount_points_timestamp () != time;
1539 /* GUnixMountMonitor {{{1 */
1541 enum {
1542 MOUNTS_CHANGED,
1543 MOUNTPOINTS_CHANGED,
1544 LAST_SIGNAL
1547 static guint signals[LAST_SIGNAL];
1549 struct _GUnixMountMonitor {
1550 GObject parent;
1552 GMainContext *context;
1555 struct _GUnixMountMonitorClass {
1556 GObjectClass parent_class;
1560 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
1562 static GContextSpecificGroup mount_monitor_group;
1563 static GFileMonitor *fstab_monitor;
1564 static GFileMonitor *mtab_monitor;
1565 static GSource *proc_mounts_watch_source;
1566 static GList *mount_poller_mounts;
1568 static void
1569 fstab_file_changed (GFileMonitor *monitor,
1570 GFile *file,
1571 GFile *other_file,
1572 GFileMonitorEvent event_type,
1573 gpointer user_data)
1575 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1576 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1577 event_type != G_FILE_MONITOR_EVENT_DELETED)
1578 return;
1580 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1583 static void
1584 mtab_file_changed (GFileMonitor *monitor,
1585 GFile *file,
1586 GFile *other_file,
1587 GFileMonitorEvent event_type,
1588 gpointer user_data)
1590 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1591 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1592 event_type != G_FILE_MONITOR_EVENT_DELETED)
1593 return;
1595 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1598 static gboolean
1599 proc_mounts_changed (GIOChannel *channel,
1600 GIOCondition cond,
1601 gpointer user_data)
1603 if (cond & G_IO_ERR)
1604 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1606 return TRUE;
1609 static gboolean
1610 mount_change_poller (gpointer user_data)
1612 GList *current_mounts, *new_it, *old_it;
1613 gboolean has_changed = FALSE;
1615 current_mounts = _g_get_unix_mounts ();
1617 for ( new_it = current_mounts, old_it = mount_poller_mounts;
1618 new_it != NULL && old_it != NULL;
1619 new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1621 if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1623 has_changed = TRUE;
1624 break;
1627 if (!(new_it == NULL && old_it == NULL))
1628 has_changed = TRUE;
1630 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1632 mount_poller_mounts = current_mounts;
1634 if (has_changed)
1636 mount_poller_time = (guint64) g_get_monotonic_time ();
1637 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1640 return TRUE;
1644 static void
1645 mount_monitor_stop (void)
1647 if (fstab_monitor)
1649 g_file_monitor_cancel (fstab_monitor);
1650 g_object_unref (fstab_monitor);
1653 if (proc_mounts_watch_source != NULL)
1654 g_source_destroy (proc_mounts_watch_source);
1656 if (mtab_monitor)
1658 g_file_monitor_cancel (mtab_monitor);
1659 g_object_unref (mtab_monitor);
1662 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1665 static void
1666 mount_monitor_start (void)
1668 GFile *file;
1670 if (get_fstab_file () != NULL)
1672 file = g_file_new_for_path (get_fstab_file ());
1673 fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1674 g_object_unref (file);
1676 g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1679 if (get_mtab_monitor_file () != NULL)
1681 const gchar *mtab_path;
1683 mtab_path = get_mtab_monitor_file ();
1684 /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1685 * See 'man proc' for more details.
1687 if (g_str_has_prefix (mtab_path, "/proc/"))
1689 GIOChannel *proc_mounts_channel;
1690 GError *error = NULL;
1691 proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
1692 if (proc_mounts_channel == NULL)
1694 g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1695 error->message, g_quark_to_string (error->domain), error->code);
1696 g_error_free (error);
1698 else
1700 proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1701 g_source_set_callback (proc_mounts_watch_source,
1702 (GSourceFunc) proc_mounts_changed,
1703 NULL, NULL);
1704 g_source_attach (proc_mounts_watch_source,
1705 g_main_context_get_thread_default ());
1706 g_source_unref (proc_mounts_watch_source);
1707 g_io_channel_unref (proc_mounts_channel);
1710 else
1712 file = g_file_new_for_path (mtab_path);
1713 mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1714 g_object_unref (file);
1715 g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
1718 else
1720 proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1721 mount_poller_mounts = _g_get_unix_mounts ();
1722 mount_poller_time = (guint64)g_get_monotonic_time ();
1723 g_source_set_callback (proc_mounts_watch_source,
1724 mount_change_poller,
1725 NULL, NULL);
1726 g_source_attach (proc_mounts_watch_source,
1727 g_main_context_get_thread_default ());
1728 g_source_unref (proc_mounts_watch_source);
1732 static void
1733 g_unix_mount_monitor_finalize (GObject *object)
1735 GUnixMountMonitor *monitor;
1737 monitor = G_UNIX_MOUNT_MONITOR (object);
1739 g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
1741 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1744 static void
1745 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1747 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1749 gobject_class->finalize = g_unix_mount_monitor_finalize;
1752 * GUnixMountMonitor::mounts-changed:
1753 * @monitor: the object on which the signal is emitted
1755 * Emitted when the unix mounts have changed.
1757 signals[MOUNTS_CHANGED] =
1758 g_signal_new (I_("mounts-changed"),
1759 G_TYPE_FROM_CLASS (klass),
1760 G_SIGNAL_RUN_LAST,
1762 NULL, NULL,
1763 g_cclosure_marshal_VOID__VOID,
1764 G_TYPE_NONE, 0);
1767 * GUnixMountMonitor::mountpoints-changed:
1768 * @monitor: the object on which the signal is emitted
1770 * Emitted when the unix mount points have changed.
1772 signals[MOUNTPOINTS_CHANGED] =
1773 g_signal_new (I_("mountpoints-changed"),
1774 G_TYPE_FROM_CLASS (klass),
1775 G_SIGNAL_RUN_LAST,
1777 NULL, NULL,
1778 g_cclosure_marshal_VOID__VOID,
1779 G_TYPE_NONE, 0);
1782 static void
1783 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1788 * g_unix_mount_monitor_set_rate_limit:
1789 * @mount_monitor: a #GUnixMountMonitor
1790 * @limit_msec: a integer with the limit in milliseconds to
1791 * poll for changes.
1793 * This function does nothing.
1795 * Before 2.44, this was a partially-effective way of controlling the
1796 * rate at which events would be reported under some uncommon
1797 * circumstances. Since @mount_monitor is a singleton, it also meant
1798 * that calling this function would have side effects for other users of
1799 * the monitor.
1801 * Since: 2.18
1803 * Deprecated:2.44:This function does nothing. Don't call it.
1805 void
1806 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1807 gint limit_msec)
1812 * g_unix_mount_monitor_get:
1814 * Gets the #GUnixMountMonitor for the current thread-default main
1815 * context.
1817 * The mount monitor can be used to monitor for changes to the list of
1818 * mounted filesystems as well as the list of mount points (ie: fstab
1819 * entries).
1821 * You must only call g_object_unref() on the return value from under
1822 * the same main context as you called this function.
1824 * Returns: (transfer full): the #GUnixMountMonitor.
1826 * Since: 2.44
1828 GUnixMountMonitor *
1829 g_unix_mount_monitor_get (void)
1831 return g_context_specific_group_get (&mount_monitor_group,
1832 G_TYPE_UNIX_MOUNT_MONITOR,
1833 G_STRUCT_OFFSET(GUnixMountMonitor, context),
1834 mount_monitor_start);
1838 * g_unix_mount_monitor_new:
1840 * Deprecated alias for g_unix_mount_monitor_get().
1842 * This function was never a true constructor, which is why it was
1843 * renamed.
1845 * Returns: a #GUnixMountMonitor.
1847 * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
1849 GUnixMountMonitor *
1850 g_unix_mount_monitor_new (void)
1852 return g_unix_mount_monitor_get ();
1855 /* GUnixMount {{{1 */
1857 * g_unix_mount_free:
1858 * @mount_entry: a #GUnixMountEntry.
1860 * Frees a unix mount.
1862 void
1863 g_unix_mount_free (GUnixMountEntry *mount_entry)
1865 g_return_if_fail (mount_entry != NULL);
1867 g_free (mount_entry->mount_path);
1868 g_free (mount_entry->device_path);
1869 g_free (mount_entry->filesystem_type);
1870 g_free (mount_entry);
1874 * g_unix_mount_copy:
1875 * @mount_entry: a #GUnixMountEntry.
1877 * Makes a copy of @mount_entry.
1879 * Returns: (transfer full): a new #GUnixMountEntry
1881 * Since: 2.54
1883 GUnixMountEntry *
1884 g_unix_mount_copy (GUnixMountEntry *mount_entry)
1886 GUnixMountEntry *copy;
1888 g_return_val_if_fail (mount_entry != NULL, NULL);
1890 copy = g_new0 (GUnixMountEntry, 1);
1891 copy->mount_path = g_strdup (mount_entry->mount_path);
1892 copy->device_path = g_strdup (mount_entry->device_path);
1893 copy->filesystem_type = g_strdup (mount_entry->filesystem_type);
1894 copy->is_read_only = mount_entry->is_read_only;
1895 copy->is_system_internal = mount_entry->is_system_internal;
1897 return copy;
1901 * g_unix_mount_point_free:
1902 * @mount_point: unix mount point to free.
1904 * Frees a unix mount point.
1906 void
1907 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1909 g_return_if_fail (mount_point != NULL);
1911 g_free (mount_point->mount_path);
1912 g_free (mount_point->device_path);
1913 g_free (mount_point->filesystem_type);
1914 g_free (mount_point->options);
1915 g_free (mount_point);
1919 * g_unix_mount_point_copy:
1920 * @mount_point: a #GUnixMountPoint.
1922 * Makes a copy of @mount_point.
1924 * Returns: (transfer full): a new #GUnixMountPoint
1926 * Since: 2.54
1928 GUnixMountPoint*
1929 g_unix_mount_point_copy (GUnixMountPoint *mount_point)
1931 GUnixMountPoint *copy;
1933 g_return_val_if_fail (mount_point != NULL, NULL);
1935 copy = g_new0 (GUnixMountPoint, 1);
1936 copy->mount_path = g_strdup (mount_point->mount_path);
1937 copy->device_path = g_strdup (mount_point->device_path);
1938 copy->filesystem_type = g_strdup (mount_point->filesystem_type);
1939 copy->options = g_strdup (mount_point->options);
1940 copy->is_read_only = mount_point->is_read_only;
1941 copy->is_user_mountable = mount_point->is_user_mountable;
1942 copy->is_loopback = mount_point->is_loopback;
1944 return copy;
1948 * g_unix_mount_compare:
1949 * @mount1: first #GUnixMountEntry to compare.
1950 * @mount2: second #GUnixMountEntry to compare.
1952 * Compares two unix mounts.
1954 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1955 * or less than @mount2, respectively.
1957 gint
1958 g_unix_mount_compare (GUnixMountEntry *mount1,
1959 GUnixMountEntry *mount2)
1961 int res;
1963 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1965 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1966 if (res != 0)
1967 return res;
1969 res = g_strcmp0 (mount1->device_path, mount2->device_path);
1970 if (res != 0)
1971 return res;
1973 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1974 if (res != 0)
1975 return res;
1977 res = mount1->is_read_only - mount2->is_read_only;
1978 if (res != 0)
1979 return res;
1981 return 0;
1985 * g_unix_mount_get_mount_path:
1986 * @mount_entry: input #GUnixMountEntry to get the mount path for.
1988 * Gets the mount path for a unix mount.
1990 * Returns: (type filename): the mount path for @mount_entry.
1992 const gchar *
1993 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1995 g_return_val_if_fail (mount_entry != NULL, NULL);
1997 return mount_entry->mount_path;
2001 * g_unix_mount_get_device_path:
2002 * @mount_entry: a #GUnixMount.
2004 * Gets the device path for a unix mount.
2006 * Returns: (type filename): a string containing the device path.
2008 const gchar *
2009 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
2011 g_return_val_if_fail (mount_entry != NULL, NULL);
2013 return mount_entry->device_path;
2017 * g_unix_mount_get_fs_type:
2018 * @mount_entry: a #GUnixMount.
2020 * Gets the filesystem type for the unix mount.
2022 * Returns: a string containing the file system type.
2024 const gchar *
2025 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
2027 g_return_val_if_fail (mount_entry != NULL, NULL);
2029 return mount_entry->filesystem_type;
2033 * g_unix_mount_is_readonly:
2034 * @mount_entry: a #GUnixMount.
2036 * Checks if a unix mount is mounted read only.
2038 * Returns: %TRUE if @mount_entry is read only.
2040 gboolean
2041 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
2043 g_return_val_if_fail (mount_entry != NULL, FALSE);
2045 return mount_entry->is_read_only;
2049 * g_unix_mount_is_system_internal:
2050 * @mount_entry: a #GUnixMount.
2052 * Checks if a unix mount is a system path.
2054 * Returns: %TRUE if the unix mount is for a system path.
2056 gboolean
2057 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
2059 g_return_val_if_fail (mount_entry != NULL, FALSE);
2061 return mount_entry->is_system_internal;
2064 /* GUnixMountPoint {{{1 */
2066 * g_unix_mount_point_compare:
2067 * @mount1: a #GUnixMount.
2068 * @mount2: a #GUnixMount.
2070 * Compares two unix mount points.
2072 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2073 * or less than @mount2, respectively.
2075 gint
2076 g_unix_mount_point_compare (GUnixMountPoint *mount1,
2077 GUnixMountPoint *mount2)
2079 int res;
2081 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2083 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2084 if (res != 0)
2085 return res;
2087 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2088 if (res != 0)
2089 return res;
2091 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2092 if (res != 0)
2093 return res;
2095 res = g_strcmp0 (mount1->options, mount2->options);
2096 if (res != 0)
2097 return res;
2099 res = mount1->is_read_only - mount2->is_read_only;
2100 if (res != 0)
2101 return res;
2103 res = mount1->is_user_mountable - mount2->is_user_mountable;
2104 if (res != 0)
2105 return res;
2107 res = mount1->is_loopback - mount2->is_loopback;
2108 if (res != 0)
2109 return res;
2111 return 0;
2115 * g_unix_mount_point_get_mount_path:
2116 * @mount_point: a #GUnixMountPoint.
2118 * Gets the mount path for a unix mount point.
2120 * Returns: (type filename): a string containing the mount path.
2122 const gchar *
2123 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2125 g_return_val_if_fail (mount_point != NULL, NULL);
2127 return mount_point->mount_path;
2131 * g_unix_mount_point_get_device_path:
2132 * @mount_point: a #GUnixMountPoint.
2134 * Gets the device path for a unix mount point.
2136 * Returns: (type filename): a string containing the device path.
2138 const gchar *
2139 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2141 g_return_val_if_fail (mount_point != NULL, NULL);
2143 return mount_point->device_path;
2147 * g_unix_mount_point_get_fs_type:
2148 * @mount_point: a #GUnixMountPoint.
2150 * Gets the file system type for the mount point.
2152 * Returns: a string containing the file system type.
2154 const gchar *
2155 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2157 g_return_val_if_fail (mount_point != NULL, NULL);
2159 return mount_point->filesystem_type;
2163 * g_unix_mount_point_get_options:
2164 * @mount_point: a #GUnixMountPoint.
2166 * Gets the options for the mount point.
2168 * Returns: a string containing the options.
2170 * Since: 2.32
2172 const gchar *
2173 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2175 g_return_val_if_fail (mount_point != NULL, NULL);
2177 return mount_point->options;
2181 * g_unix_mount_point_is_readonly:
2182 * @mount_point: a #GUnixMountPoint.
2184 * Checks if a unix mount point is read only.
2186 * Returns: %TRUE if a mount point is read only.
2188 gboolean
2189 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2191 g_return_val_if_fail (mount_point != NULL, FALSE);
2193 return mount_point->is_read_only;
2197 * g_unix_mount_point_is_user_mountable:
2198 * @mount_point: a #GUnixMountPoint.
2200 * Checks if a unix mount point is mountable by the user.
2202 * Returns: %TRUE if the mount point is user mountable.
2204 gboolean
2205 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2207 g_return_val_if_fail (mount_point != NULL, FALSE);
2209 return mount_point->is_user_mountable;
2213 * g_unix_mount_point_is_loopback:
2214 * @mount_point: a #GUnixMountPoint.
2216 * Checks if a unix mount point is a loopback device.
2218 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
2220 gboolean
2221 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2223 g_return_val_if_fail (mount_point != NULL, FALSE);
2225 return mount_point->is_loopback;
2228 static GUnixMountType
2229 guess_mount_type (const char *mount_path,
2230 const char *device_path,
2231 const char *filesystem_type)
2233 GUnixMountType type;
2234 char *basename;
2236 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2238 if ((strcmp (filesystem_type, "udf") == 0) ||
2239 (strcmp (filesystem_type, "iso9660") == 0) ||
2240 (strcmp (filesystem_type, "cd9660") == 0))
2241 type = G_UNIX_MOUNT_TYPE_CDROM;
2242 else if ((strcmp (filesystem_type, "nfs") == 0) ||
2243 (strcmp (filesystem_type, "nfs4") == 0))
2244 type = G_UNIX_MOUNT_TYPE_NFS;
2245 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
2246 g_str_has_prefix (device_path, "/dev/fd") ||
2247 g_str_has_prefix (device_path, "/dev/floppy"))
2248 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2249 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
2250 g_str_has_prefix (device_path, "/dev/acd") ||
2251 g_str_has_prefix (device_path, "/dev/cd"))
2252 type = G_UNIX_MOUNT_TYPE_CDROM;
2253 else if (g_str_has_prefix (device_path, "/vol/"))
2255 const char *name = mount_path + strlen ("/");
2257 if (g_str_has_prefix (name, "cdrom"))
2258 type = G_UNIX_MOUNT_TYPE_CDROM;
2259 else if (g_str_has_prefix (name, "floppy") ||
2260 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
2261 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2262 else if (g_str_has_prefix (name, "rmdisk"))
2263 type = G_UNIX_MOUNT_TYPE_ZIP;
2264 else if (g_str_has_prefix (name, "jaz"))
2265 type = G_UNIX_MOUNT_TYPE_JAZ;
2266 else if (g_str_has_prefix (name, "memstick"))
2267 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2269 else
2271 basename = g_path_get_basename (mount_path);
2273 if (g_str_has_prefix (basename, "cdr") ||
2274 g_str_has_prefix (basename, "cdwriter") ||
2275 g_str_has_prefix (basename, "burn") ||
2276 g_str_has_prefix (basename, "dvdr"))
2277 type = G_UNIX_MOUNT_TYPE_CDROM;
2278 else if (g_str_has_prefix (basename, "floppy"))
2279 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2280 else if (g_str_has_prefix (basename, "zip"))
2281 type = G_UNIX_MOUNT_TYPE_ZIP;
2282 else if (g_str_has_prefix (basename, "jaz"))
2283 type = G_UNIX_MOUNT_TYPE_JAZ;
2284 else if (g_str_has_prefix (basename, "camera"))
2285 type = G_UNIX_MOUNT_TYPE_CAMERA;
2286 else if (g_str_has_prefix (basename, "memstick") ||
2287 g_str_has_prefix (basename, "memory_stick") ||
2288 g_str_has_prefix (basename, "ram"))
2289 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2290 else if (g_str_has_prefix (basename, "compact_flash"))
2291 type = G_UNIX_MOUNT_TYPE_CF;
2292 else if (g_str_has_prefix (basename, "smart_media"))
2293 type = G_UNIX_MOUNT_TYPE_SM;
2294 else if (g_str_has_prefix (basename, "sd_mmc"))
2295 type = G_UNIX_MOUNT_TYPE_SDMMC;
2296 else if (g_str_has_prefix (basename, "ipod"))
2297 type = G_UNIX_MOUNT_TYPE_IPOD;
2299 g_free (basename);
2302 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2303 type = G_UNIX_MOUNT_TYPE_HD;
2305 return type;
2309 * g_unix_mount_guess_type:
2310 * @mount_entry: a #GUnixMount.
2312 * Guesses the type of a unix mount. If the mount type cannot be
2313 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2315 * Returns: a #GUnixMountType.
2317 static GUnixMountType
2318 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2320 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2321 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2322 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2323 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2325 return guess_mount_type (mount_entry->mount_path,
2326 mount_entry->device_path,
2327 mount_entry->filesystem_type);
2331 * g_unix_mount_point_guess_type:
2332 * @mount_point: a #GUnixMountPoint.
2334 * Guesses the type of a unix mount point.
2335 * If the mount type cannot be determined,
2336 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2338 * Returns: a #GUnixMountType.
2340 static GUnixMountType
2341 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2343 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2344 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2345 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2346 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2348 return guess_mount_type (mount_point->mount_path,
2349 mount_point->device_path,
2350 mount_point->filesystem_type);
2353 static const char *
2354 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2356 const char *icon_name;
2358 switch (type)
2360 case G_UNIX_MOUNT_TYPE_HD:
2361 if (is_mount_point)
2362 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2363 else
2364 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2365 break;
2366 case G_UNIX_MOUNT_TYPE_FLOPPY:
2367 case G_UNIX_MOUNT_TYPE_ZIP:
2368 case G_UNIX_MOUNT_TYPE_JAZ:
2369 if (is_mount_point)
2370 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2371 else
2372 icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2373 break;
2374 case G_UNIX_MOUNT_TYPE_CDROM:
2375 if (is_mount_point)
2376 icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2377 else
2378 icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2379 break;
2380 case G_UNIX_MOUNT_TYPE_NFS:
2381 icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2382 break;
2383 case G_UNIX_MOUNT_TYPE_MEMSTICK:
2384 if (is_mount_point)
2385 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2386 else
2387 icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2388 break;
2389 case G_UNIX_MOUNT_TYPE_CAMERA:
2390 if (is_mount_point)
2391 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2392 else
2393 icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2394 break;
2395 case G_UNIX_MOUNT_TYPE_IPOD:
2396 if (is_mount_point)
2397 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2398 else
2399 icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2400 break;
2401 case G_UNIX_MOUNT_TYPE_UNKNOWN:
2402 default:
2403 if (is_mount_point)
2404 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2405 else
2406 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2407 break;
2410 return icon_name;
2414 * g_unix_mount_guess_name:
2415 * @mount_entry: a #GUnixMountEntry
2417 * Guesses the name of a Unix mount.
2418 * The result is a translated string.
2420 * Returns: A newly allocated string that must
2421 * be freed with g_free()
2423 gchar *
2424 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2426 char *name;
2428 if (strcmp (mount_entry->mount_path, "/") == 0)
2429 name = g_strdup (_("Filesystem root"));
2430 else
2431 name = g_filename_display_basename (mount_entry->mount_path);
2433 return name;
2437 * g_unix_mount_guess_icon:
2438 * @mount_entry: a #GUnixMountEntry
2440 * Guesses the icon of a Unix mount.
2442 * Returns: (transfer full): a #GIcon
2444 GIcon *
2445 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2447 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2451 * g_unix_mount_guess_symbolic_icon:
2452 * @mount_entry: a #GUnixMountEntry
2454 * Guesses the symbolic icon of a Unix mount.
2456 * Returns: (transfer full): a #GIcon
2458 * Since: 2.34
2460 GIcon *
2461 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2463 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2467 * g_unix_mount_point_guess_name:
2468 * @mount_point: a #GUnixMountPoint
2470 * Guesses the name of a Unix mount point.
2471 * The result is a translated string.
2473 * Returns: A newly allocated string that must
2474 * be freed with g_free()
2476 gchar *
2477 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2479 char *name;
2481 if (strcmp (mount_point->mount_path, "/") == 0)
2482 name = g_strdup (_("Filesystem root"));
2483 else
2484 name = g_filename_display_basename (mount_point->mount_path);
2486 return name;
2490 * g_unix_mount_point_guess_icon:
2491 * @mount_point: a #GUnixMountPoint
2493 * Guesses the icon of a Unix mount point.
2495 * Returns: (transfer full): a #GIcon
2497 GIcon *
2498 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2500 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2504 * g_unix_mount_point_guess_symbolic_icon:
2505 * @mount_point: a #GUnixMountPoint
2507 * Guesses the symbolic icon of a Unix mount point.
2509 * Returns: (transfer full): a #GIcon
2511 * Since: 2.34
2513 GIcon *
2514 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2516 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2520 * g_unix_mount_guess_can_eject:
2521 * @mount_entry: a #GUnixMountEntry
2523 * Guesses whether a Unix mount can be ejected.
2525 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2527 gboolean
2528 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2530 GUnixMountType guessed_type;
2532 guessed_type = g_unix_mount_guess_type (mount_entry);
2533 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2534 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2535 return TRUE;
2537 return FALSE;
2541 * g_unix_mount_guess_should_display:
2542 * @mount_entry: a #GUnixMountEntry
2544 * Guesses whether a Unix mount should be displayed in the UI.
2546 * Returns: %TRUE if @mount_entry is deemed to be displayable.
2548 gboolean
2549 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2551 const char *mount_path;
2552 const gchar *user_name;
2553 gsize user_name_len;
2555 /* Never display internal mountpoints */
2556 if (g_unix_mount_is_system_internal (mount_entry))
2557 return FALSE;
2559 /* Only display things in /media (which are generally user mountable)
2560 and home dir (fuse stuff) and /run/media/$USER */
2561 mount_path = mount_entry->mount_path;
2562 if (mount_path != NULL)
2564 gboolean is_in_runtime_dir = FALSE;
2565 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2566 if (g_strstr_len (mount_path, -1, "/.") != NULL)
2567 return FALSE;
2569 /* Check /run/media/$USER/ */
2570 user_name = g_get_user_name ();
2571 user_name_len = strlen (user_name);
2572 if (strncmp (mount_path, "/run/media/", sizeof ("/run/media/") - 1) == 0 &&
2573 strncmp (mount_path + sizeof ("/run/media/") - 1, user_name, user_name_len) == 0 &&
2574 mount_path[sizeof ("/run/media/") - 1 + user_name_len] == '/')
2575 is_in_runtime_dir = TRUE;
2577 if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2579 char *path;
2580 /* Avoid displaying mounts that are not accessible to the user.
2582 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2583 * want to avoid g_access() for mount points which can potentially
2584 * block or fail stat()'ing, such as network mounts.
2586 path = g_path_get_dirname (mount_path);
2587 if (g_str_has_prefix (path, "/media/"))
2589 if (g_access (path, R_OK|X_OK) != 0)
2591 g_free (path);
2592 return FALSE;
2595 g_free (path);
2597 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2599 struct stat st;
2600 if (g_stat (mount_entry->device_path, &st) == 0 &&
2601 S_ISBLK(st.st_mode) &&
2602 g_access (mount_path, R_OK|X_OK) != 0)
2603 return FALSE;
2605 return TRUE;
2608 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
2609 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2610 return TRUE;
2613 return FALSE;
2617 * g_unix_mount_point_guess_can_eject:
2618 * @mount_point: a #GUnixMountPoint
2620 * Guesses whether a Unix mount point can be ejected.
2622 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2624 gboolean
2625 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2627 GUnixMountType guessed_type;
2629 guessed_type = g_unix_mount_point_guess_type (mount_point);
2630 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2631 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2632 return TRUE;
2634 return FALSE;
2637 /* Utility functions {{{1 */
2639 #ifdef HAVE_MNTENT_H
2640 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2641 static void
2642 _canonicalize_filename (gchar *filename)
2644 gchar *p, *q;
2645 gboolean last_was_slash = FALSE;
2647 p = filename;
2648 q = filename;
2650 while (*p)
2652 if (*p == G_DIR_SEPARATOR)
2654 if (!last_was_slash)
2655 *q++ = G_DIR_SEPARATOR;
2657 last_was_slash = TRUE;
2659 else
2661 if (last_was_slash && *p == '.')
2663 if (*(p + 1) == G_DIR_SEPARATOR ||
2664 *(p + 1) == '\0')
2666 if (*(p + 1) == '\0')
2667 break;
2669 p += 1;
2671 else if (*(p + 1) == '.' &&
2672 (*(p + 2) == G_DIR_SEPARATOR ||
2673 *(p + 2) == '\0'))
2675 if (q > filename + 1)
2677 q--;
2678 while (q > filename + 1 &&
2679 *(q - 1) != G_DIR_SEPARATOR)
2680 q--;
2683 if (*(p + 2) == '\0')
2684 break;
2686 p += 2;
2688 else
2690 *q++ = *p;
2691 last_was_slash = FALSE;
2694 else
2696 *q++ = *p;
2697 last_was_slash = FALSE;
2701 p++;
2704 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2705 q--;
2707 *q = '\0';
2710 static char *
2711 _resolve_symlink (const char *file)
2713 GError *error;
2714 char *dir;
2715 char *link;
2716 char *f;
2717 char *f1;
2719 f = g_strdup (file);
2721 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
2723 link = g_file_read_link (f, &error);
2724 if (link == NULL)
2726 g_error_free (error);
2727 g_free (f);
2728 f = NULL;
2729 goto out;
2732 dir = g_path_get_dirname (f);
2733 f1 = g_strdup_printf ("%s/%s", dir, link);
2734 g_free (dir);
2735 g_free (link);
2736 g_free (f);
2737 f = f1;
2740 out:
2741 if (f != NULL)
2742 _canonicalize_filename (f);
2743 return f;
2746 static const char *
2747 _resolve_dev_root (void)
2749 static gboolean have_real_dev_root = FALSE;
2750 static char real_dev_root[256];
2751 struct stat statbuf;
2753 /* see if it's cached already */
2754 if (have_real_dev_root)
2755 goto found;
2757 /* otherwise we're going to find it right away.. */
2758 have_real_dev_root = TRUE;
2760 if (stat ("/dev/root", &statbuf) == 0)
2762 if (! S_ISLNK (statbuf.st_mode))
2764 dev_t root_dev = statbuf.st_dev;
2765 FILE *f;
2767 /* see if device with similar major:minor as /dev/root is mention
2768 * in /etc/mtab (it usually is)
2770 f = fopen ("/etc/mtab", "r");
2771 if (f != NULL)
2773 struct mntent *entp;
2774 #ifdef HAVE_GETMNTENT_R
2775 struct mntent ent;
2776 char buf[1024];
2777 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
2779 #else
2780 G_LOCK (getmntent);
2781 while ((entp = getmntent (f)) != NULL)
2783 #endif
2784 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2785 statbuf.st_dev == root_dev)
2787 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2788 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2789 fclose (f);
2790 goto found;
2794 endmntent (f);
2796 #ifndef HAVE_GETMNTENT_R
2797 G_UNLOCK (getmntent);
2798 #endif
2801 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2804 else
2806 char *resolved;
2807 resolved = _resolve_symlink ("/dev/root");
2808 if (resolved != NULL)
2810 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2811 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2812 g_free (resolved);
2813 goto found;
2818 /* bah sucks.. */
2819 strcpy (real_dev_root, "/dev/root");
2821 found:
2822 return real_dev_root;
2824 #endif
2826 /* Epilogue {{{1 */
2827 /* vim:set foldmethod=marker: */