Add some more cases to the app-id unit tests
[glib.git] / gio / gunixmounts.c
blob0c73a5689dba3de2a9bbcf6d4449aad8684e3969
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 struct _GUnixMountPoint {
134 char *mount_path;
135 char *device_path;
136 char *filesystem_type;
137 char *options;
138 gboolean is_read_only;
139 gboolean is_user_mountable;
140 gboolean is_loopback;
143 static GList *_g_get_unix_mounts (void);
144 static GList *_g_get_unix_mount_points (void);
146 static guint64 mount_poller_time = 0;
148 #ifdef HAVE_SYS_MNTTAB_H
149 #define MNTOPT_RO "ro"
150 #endif
152 #ifdef HAVE_MNTENT_H
153 #include <mntent.h>
154 #ifdef HAVE_LIBMOUNT
155 #include <libmount/libmount.h>
156 #endif
157 #elif defined (HAVE_SYS_MNTTAB_H)
158 #include <sys/mnttab.h>
159 #endif
161 #ifdef HAVE_SYS_VFSTAB_H
162 #include <sys/vfstab.h>
163 #endif
165 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
166 #include <sys/mntctl.h>
167 #include <sys/vfs.h>
168 #include <sys/vmount.h>
169 #include <fshelp.h>
170 #endif
172 #if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
173 #include <sys/param.h>
174 #include <sys/ucred.h>
175 #include <sys/mount.h>
176 #include <fstab.h>
177 #ifdef HAVE_SYS_SYSCTL_H
178 #include <sys/sysctl.h>
179 #endif
180 #endif
182 #ifndef HAVE_SETMNTENT
183 #define setmntent(f,m) fopen(f,m)
184 #endif
185 #ifndef HAVE_ENDMNTENT
186 #define endmntent(f) fclose(f)
187 #endif
189 static gboolean
190 is_in (const char *value, const char *set[])
192 int i;
193 for (i = 0; set[i] != NULL; i++)
195 if (strcmp (set[i], value) == 0)
196 return TRUE;
198 return FALSE;
202 * g_unix_is_mount_path_system_internal:
203 * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr`
205 * Determines if @mount_path is considered an implementation of the
206 * OS. This is primarily used for hiding mountable and mounted volumes
207 * that only are used in the OS and has little to no relevance to the
208 * casual user.
210 * Returns: %TRUE if @mount_path is considered an implementation detail
211 * of the OS.
213 gboolean
214 g_unix_is_mount_path_system_internal (const char *mount_path)
216 const char *ignore_mountpoints[] = {
217 /* Includes all FHS 2.3 toplevel dirs and other specialized
218 * directories that we want to hide from the user.
220 "/", /* we already have "Filesystem root" in Nautilus */
221 "/bin",
222 "/boot",
223 "/compat/linux/proc",
224 "/compat/linux/sys",
225 "/dev",
226 "/etc",
227 "/home",
228 "/lib",
229 "/lib64",
230 "/libexec",
231 "/live/cow",
232 "/live/image",
233 "/media",
234 "/mnt",
235 "/opt",
236 "/rescue",
237 "/root",
238 "/sbin",
239 "/srv",
240 "/tmp",
241 "/usr",
242 "/usr/X11R6",
243 "/usr/local",
244 "/usr/obj",
245 "/usr/ports",
246 "/usr/src",
247 "/usr/xobj",
248 "/var",
249 "/var/crash",
250 "/var/local",
251 "/var/log",
252 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
253 "/var/mail",
254 "/var/run",
255 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
256 "/proc",
257 "/sbin",
258 "/net",
259 "/sys",
260 NULL
263 if (is_in (mount_path, ignore_mountpoints))
264 return TRUE;
266 if (g_str_has_prefix (mount_path, "/dev/") ||
267 g_str_has_prefix (mount_path, "/proc/") ||
268 g_str_has_prefix (mount_path, "/sys/"))
269 return TRUE;
271 if (g_str_has_suffix (mount_path, "/.gvfs"))
272 return TRUE;
274 return FALSE;
277 static gboolean
278 guess_system_internal (const char *mountpoint,
279 const char *fs,
280 const char *device)
282 const char *ignore_fs[] = {
283 "auto",
284 "autofs",
285 "devfs",
286 "devpts",
287 "ecryptfs",
288 "fdescfs",
289 "kernfs",
290 "linprocfs",
291 "mfs",
292 "nullfs",
293 "proc",
294 "procfs",
295 "ptyfs",
296 "rootfs",
297 "selinuxfs",
298 "sysfs",
299 "tmpfs",
300 "usbfs",
301 "nfsd",
302 "rpc_pipefs",
303 "zfs",
304 NULL
306 const char *ignore_devices[] = {
307 "none",
308 "sunrpc",
309 "devpts",
310 "nfsd",
311 "/dev/loop",
312 "/dev/vn",
313 NULL
316 if (is_in (fs, ignore_fs))
317 return TRUE;
319 if (is_in (device, ignore_devices))
320 return TRUE;
322 if (g_unix_is_mount_path_system_internal (mountpoint))
323 return TRUE;
325 return FALSE;
328 /* GUnixMounts (ie: mtab) implementations {{{1 */
330 static GUnixMountEntry *
331 create_unix_mount_entry (const char *device_path,
332 const char *mount_path,
333 const char *filesystem_type,
334 gboolean is_read_only)
336 GUnixMountEntry *mount_entry = NULL;
338 mount_entry = g_new0 (GUnixMountEntry, 1);
339 mount_entry->device_path = g_strdup (device_path);
340 mount_entry->mount_path = g_strdup (mount_path);
341 mount_entry->filesystem_type = g_strdup (filesystem_type);
342 mount_entry->is_read_only = is_read_only;
344 mount_entry->is_system_internal =
345 guess_system_internal (mount_entry->mount_path,
346 mount_entry->filesystem_type,
347 mount_entry->device_path);
348 return mount_entry;
351 static GUnixMountPoint *
352 create_unix_mount_point (const char *device_path,
353 const char *mount_path,
354 const char *filesystem_type,
355 const char *options,
356 gboolean is_read_only,
357 gboolean is_user_mountable,
358 gboolean is_loopback)
360 GUnixMountPoint *mount_point = NULL;
362 mount_point = g_new0 (GUnixMountPoint, 1);
363 mount_point->device_path = g_strdup (device_path);
364 mount_point->mount_path = g_strdup (mount_path);
365 mount_point->filesystem_type = g_strdup (filesystem_type);
366 mount_point->options = g_strdup (options);
367 mount_point->is_read_only = is_read_only;
368 mount_point->is_user_mountable = is_user_mountable;
369 mount_point->is_loopback = is_loopback;
371 return mount_point;
374 /* mntent.h (Linux, GNU, NSS) {{{2 */
375 #ifdef HAVE_MNTENT_H
377 #ifdef HAVE_LIBMOUNT
379 /* For documentation on /proc/self/mountinfo see
380 * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
382 #define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
384 static GList *
385 _g_get_unix_mounts (void)
387 struct libmnt_table *table = NULL;
388 struct libmnt_context *ctxt = NULL;
389 struct libmnt_iter* iter = NULL;
390 struct libmnt_fs *fs = NULL;
391 GUnixMountEntry *mount_entry = NULL;
392 GList *return_list = NULL;
394 ctxt = mnt_new_context ();
395 mnt_context_get_mtab (ctxt, &table);
396 if (!table)
397 goto out;
399 iter = mnt_new_iter (MNT_ITER_FORWARD);
400 while (mnt_table_next_fs (table, iter, &fs) == 0)
402 const char *device_path = NULL;
403 char *mount_options = NULL;
404 unsigned long mount_flags = 0;
405 gboolean is_read_only = FALSE;
407 if (!mnt_table_is_fs_mounted (table, fs))
408 continue;
410 device_path = mnt_fs_get_source (fs);
411 if (g_strcmp0 (device_path, "/dev/root") == 0)
412 device_path = _resolve_dev_root ();
414 mount_options = mnt_fs_strdup_options (fs);
415 if (mount_options)
417 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
418 g_free (mount_options);
420 is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
422 mount_entry = create_unix_mount_entry (device_path,
423 mnt_fs_get_target (fs),
424 mnt_fs_get_fstype (fs),
425 is_read_only);
427 return_list = g_list_prepend (return_list, mount_entry);
429 mnt_free_iter (iter);
431 out:
432 mnt_free_context (ctxt);
434 return g_list_reverse (return_list);
437 #else
439 static char *
440 get_mtab_read_file (void)
442 #ifdef _PATH_MOUNTED
443 # ifdef __linux__
444 return "/proc/mounts";
445 # else
446 return _PATH_MOUNTED;
447 # endif
448 #else
449 return "/etc/mtab";
450 #endif
453 #ifndef HAVE_GETMNTENT_R
454 G_LOCK_DEFINE_STATIC(getmntent);
455 #endif
457 static GList *
458 _g_get_unix_mounts (void)
460 #ifdef HAVE_GETMNTENT_R
461 struct mntent ent;
462 char buf[1024];
463 #endif
464 struct mntent *mntent;
465 FILE *file;
466 char *read_file;
467 GUnixMountEntry *mount_entry;
468 GHashTable *mounts_hash;
469 GList *return_list;
471 read_file = get_mtab_read_file ();
473 file = setmntent (read_file, "r");
474 if (file == NULL)
475 return NULL;
477 return_list = NULL;
479 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
481 #ifdef HAVE_GETMNTENT_R
482 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
483 #else
484 G_LOCK (getmntent);
485 while ((mntent = getmntent (file)) != NULL)
486 #endif
488 const char *device_path = NULL;
489 gboolean is_read_only = FALSE;
491 /* ignore any mnt_fsname that is repeated and begins with a '/'
493 * We do this to avoid being fooled by --bind mounts, since
494 * these have the same device as the location they bind to.
495 * It's not an ideal solution to the problem, but it's likely that
496 * the most important mountpoint is first and the --bind ones after
497 * that aren't as important. So it should work.
499 * The '/' is to handle procfs, tmpfs and other no device mounts.
501 if (mntent->mnt_fsname != NULL &&
502 mntent->mnt_fsname[0] == '/' &&
503 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
504 continue;
506 if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
507 device_path = _resolve_dev_root ();
508 else
509 device_path = mntent->mnt_fsname;
511 #if defined (HAVE_HASMNTOPT)
512 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
513 is_read_only = TRUE;
514 #endif
516 mount_entry = create_unix_mount_entry (device_path,
517 mntent->mnt_dir,
518 mntent->mnt_type,
519 is_read_only);
521 g_hash_table_insert (mounts_hash,
522 mount_entry->device_path,
523 mount_entry->device_path);
525 return_list = g_list_prepend (return_list, mount_entry);
527 g_hash_table_destroy (mounts_hash);
529 endmntent (file);
531 #ifndef HAVE_GETMNTENT_R
532 G_UNLOCK (getmntent);
533 #endif
535 return g_list_reverse (return_list);
538 #endif /* HAVE_LIBMOUNT */
540 static char *
541 get_mtab_monitor_file (void)
543 static char *mountinfo_path = NULL;
544 #ifdef HAVE_LIBMOUNT
545 struct stat buf;
546 #endif
548 if (mountinfo_path != NULL)
549 return mountinfo_path;
551 #ifdef HAVE_LIBMOUNT
552 /* If using libmount we'll have the logic in place to read mountinfo */
553 if (stat (PROC_MOUNTINFO_PATH, &buf) == 0)
555 mountinfo_path = PROC_MOUNTINFO_PATH;
556 return mountinfo_path;
558 #endif
560 #ifdef _PATH_MOUNTED
561 # ifdef __linux__
562 mountinfo_path = "/proc/mounts";
563 # else
564 mountinfo_path = _PATH_MOUNTED;
565 # endif
566 #else
567 mountinfo_path = "/etc/mtab";
568 #endif
570 return mountinfo_path;
573 /* mnttab.h {{{2 */
574 #elif defined (HAVE_SYS_MNTTAB_H)
576 G_LOCK_DEFINE_STATIC(getmntent);
578 static char *
579 get_mtab_read_file (void)
581 #ifdef _PATH_MOUNTED
582 return _PATH_MOUNTED;
583 #else
584 return "/etc/mnttab";
585 #endif
588 static char *
589 get_mtab_monitor_file (void)
591 return get_mtab_read_file ();
594 static GList *
595 _g_get_unix_mounts (void)
597 struct mnttab mntent;
598 FILE *file;
599 char *read_file;
600 GUnixMountEntry *mount_entry;
601 GList *return_list;
603 read_file = get_mtab_read_file ();
605 file = setmntent (read_file, "r");
606 if (file == NULL)
607 return NULL;
609 return_list = NULL;
611 G_LOCK (getmntent);
612 while (! getmntent (file, &mntent))
614 gboolean is_read_only = FALSE;
616 #if defined (HAVE_HASMNTOPT)
617 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
618 is_read_only = TRUE;
619 #endif
621 mount_entry = create_unix_mount_entry (mntent.mnt_special,
622 mntent.mnt_mountp,
623 mntent.mnt_fstype,
624 is_read_only);
626 return_list = g_list_prepend (return_list, mount_entry);
629 endmntent (file);
631 G_UNLOCK (getmntent);
633 return g_list_reverse (return_list);
636 /* mntctl.h (AIX) {{{2 */
637 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
639 static char *
640 get_mtab_monitor_file (void)
642 return NULL;
645 static GList *
646 _g_get_unix_mounts (void)
648 struct vfs_ent *fs_info;
649 struct vmount *vmount_info;
650 int vmount_number;
651 unsigned int vmount_size;
652 int current;
653 GList *return_list;
655 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
657 g_warning ("Unable to know the number of mounted volumes\n");
659 return NULL;
662 vmount_info = (struct vmount*)g_malloc (vmount_size);
664 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
666 if (vmount_info->vmt_revision != VMT_REVISION)
667 g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
669 if (vmount_number < 0)
671 g_warning ("Unable to recover mounted volumes information\n");
673 g_free (vmount_info);
674 return NULL;
677 return_list = NULL;
678 while (vmount_number > 0)
680 gboolean is_read_only = FALSE;
682 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
684 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
685 is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
687 mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
688 vmt2dataptr (vmount_info, VMT_STUB),
689 fs_info == NULL ? "unknown" : fs_info->vfsent_name,
690 is_read_only);
692 return_list = g_list_prepend (return_list, mount_entry);
694 vmount_info = (struct vmount *)( (char*)vmount_info
695 + vmount_info->vmt_length);
696 vmount_number--;
699 g_free (vmount_info);
701 return g_list_reverse (return_list);
704 /* sys/mount.h {{{2 */
705 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
707 static char *
708 get_mtab_monitor_file (void)
710 return NULL;
713 static GList *
714 _g_get_unix_mounts (void)
716 #if defined(USE_STATVFS)
717 struct statvfs *mntent = NULL;
718 #elif defined(USE_STATFS)
719 struct statfs *mntent = NULL;
720 #else
721 #error statfs juggling failed
722 #endif
723 size_t bufsize;
724 int num_mounts, i;
725 GUnixMountEntry *mount_entry;
726 GList *return_list;
728 /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
729 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
730 num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
731 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
732 num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
733 #endif
734 if (num_mounts == -1)
735 return NULL;
737 bufsize = num_mounts * sizeof (*mntent);
738 mntent = g_malloc (bufsize);
739 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
740 num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
741 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
742 num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
743 #endif
744 if (num_mounts == -1)
745 return NULL;
747 return_list = NULL;
749 for (i = 0; i < num_mounts; i++)
751 gboolean is_read_only = FALSE;
753 #if defined(USE_STATVFS)
754 if (mntent[i].f_flag & ST_RDONLY)
755 #elif defined(USE_STATFS)
756 if (mntent[i].f_flags & MNT_RDONLY)
757 #else
758 #error statfs juggling failed
759 #endif
760 is_read_only = TRUE;
762 mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
763 mntent[i].f_mntonname,
764 mntent[i].f_fstypename,
765 is_read_only);
767 return_list = g_list_prepend (return_list, mount_entry);
770 g_free (mntent);
772 return g_list_reverse (return_list);
775 /* Interix {{{2 */
776 #elif defined(__INTERIX)
778 static char *
779 get_mtab_monitor_file (void)
781 return NULL;
784 static GList *
785 _g_get_unix_mounts (void)
787 DIR *dirp;
788 GList* return_list = NULL;
789 char filename[9 + NAME_MAX];
791 dirp = opendir ("/dev/fs");
792 if (!dirp)
794 g_warning ("unable to read /dev/fs!");
795 return NULL;
798 while (1)
800 struct statvfs statbuf;
801 struct dirent entry;
802 struct dirent* result;
804 if (readdir_r (dirp, &entry, &result) || result == NULL)
805 break;
807 strcpy (filename, "/dev/fs/");
808 strcat (filename, entry.d_name);
810 if (statvfs (filename, &statbuf) == 0)
812 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
814 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
815 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
816 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
818 if (statbuf.f_flag & ST_RDONLY)
819 mount_entry->is_read_only = TRUE;
821 return_list = g_list_prepend(return_list, mount_entry);
825 return_list = g_list_reverse (return_list);
827 closedir (dirp);
829 return return_list;
832 /* Common code {{{2 */
833 #else
834 #error No _g_get_unix_mounts() implementation for system
835 #endif
837 /* GUnixMountPoints (ie: fstab) implementations {{{1 */
839 /* _g_get_unix_mount_points():
840 * read the fstab.
841 * don't return swap and ignore mounts.
844 static char *
845 get_fstab_file (void)
847 #ifdef HAVE_LIBMOUNT
848 return (char *) mnt_get_fstab_path ();
849 #else
850 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
851 /* AIX */
852 return "/etc/filesystems";
853 #elif defined(_PATH_MNTTAB)
854 return _PATH_MNTTAB;
855 #elif defined(VFSTAB)
856 return VFSTAB;
857 #else
858 return "/etc/fstab";
859 #endif
860 #endif
863 /* mntent.h (Linux, GNU, NSS) {{{2 */
864 #ifdef HAVE_MNTENT_H
866 #ifdef HAVE_LIBMOUNT
868 static GList *
869 _g_get_unix_mount_points (void)
871 struct libmnt_table *table = NULL;
872 struct libmnt_context *ctxt = NULL;
873 struct libmnt_iter* iter = NULL;
874 struct libmnt_fs *fs = NULL;
875 GUnixMountPoint *mount_point = NULL;
876 GList *return_list = NULL;
878 ctxt = mnt_new_context ();
879 mnt_context_get_fstab (ctxt, &table);
880 if (!table)
881 goto out;
883 iter = mnt_new_iter (MNT_ITER_FORWARD);
884 while (mnt_table_next_fs (table, iter, &fs) == 0)
886 const char *device_path = NULL;
887 const char *mount_path = NULL;
888 const char *mount_fstype = NULL;
889 char *mount_options = NULL;
890 gboolean is_read_only = FALSE;
891 gboolean is_user_mountable = FALSE;
892 gboolean is_loopback = FALSE;
894 mount_path = mnt_fs_get_target (fs);
895 if ((strcmp (mount_path, "ignore") == 0) ||
896 (strcmp (mount_path, "swap") == 0) ||
897 (strcmp (mount_path, "none") == 0))
898 continue;
900 mount_fstype = mnt_fs_get_fstype (fs);
901 mount_options = mnt_fs_strdup_options (fs);
902 if (mount_options)
904 unsigned long mount_flags = 0;
905 unsigned long userspace_flags = 0;
907 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
908 mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
910 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
911 if (mount_flags & MS_BIND)
913 g_free (mount_options);
914 continue;
917 is_read_only = (mount_flags & MS_RDONLY) != 0;
918 is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
920 if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
921 ((userspace_flags & MNT_MS_USER) &&
922 (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
923 (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
924 (userspace_flags & MNT_MS_USERS) ||
925 (userspace_flags & MNT_MS_OWNER))
927 is_user_mountable = TRUE;
931 device_path = mnt_fs_get_source (fs);
932 if (g_strcmp0 (device_path, "/dev/root") == 0)
933 device_path = _resolve_dev_root ();
935 mount_point = create_unix_mount_point (device_path,
936 mount_path,
937 mount_fstype,
938 mount_options,
939 is_read_only,
940 is_user_mountable,
941 is_loopback);
942 if (mount_options)
943 g_free (mount_options);
945 return_list = g_list_prepend (return_list, mount_point);
947 mnt_free_iter (iter);
949 out:
950 mnt_free_context (ctxt);
952 return g_list_reverse (return_list);
955 #else
957 static GList *
958 _g_get_unix_mount_points (void)
960 #ifdef HAVE_GETMNTENT_R
961 struct mntent ent;
962 char buf[1024];
963 #endif
964 struct mntent *mntent;
965 FILE *file;
966 char *read_file;
967 GUnixMountPoint *mount_point;
968 GList *return_list;
970 read_file = get_fstab_file ();
972 file = setmntent (read_file, "r");
973 if (file == NULL)
974 return NULL;
976 return_list = NULL;
978 #ifdef HAVE_GETMNTENT_R
979 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
980 #else
981 G_LOCK (getmntent);
982 while ((mntent = getmntent (file)) != NULL)
983 #endif
985 const char *device_path = NULL;
986 gboolean is_read_only = FALSE;
987 gboolean is_user_mountable = FALSE;
988 gboolean is_loopback = FALSE;
990 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
991 (strcmp (mntent->mnt_dir, "swap") == 0) ||
992 (strcmp (mntent->mnt_dir, "none") == 0))
993 continue;
995 #ifdef HAVE_HASMNTOPT
996 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
997 if (hasmntopt (mntent, "bind"))
998 continue;
999 #endif
1001 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1002 device_path = _resolve_dev_root ();
1003 else
1004 device_path = mntent->mnt_fsname;
1006 #ifdef HAVE_HASMNTOPT
1007 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1008 is_read_only = TRUE;
1010 if (hasmntopt (mntent, "loop") != NULL)
1011 is_loopback = TRUE;
1013 #endif
1015 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1016 #ifdef HAVE_HASMNTOPT
1017 || (hasmntopt (mntent, "user") != NULL
1018 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1019 || hasmntopt (mntent, "pamconsole") != NULL
1020 || hasmntopt (mntent, "users") != NULL
1021 || hasmntopt (mntent, "owner") != NULL
1022 #endif
1024 is_user_mountable = TRUE;
1026 mount_point = create_unix_mount_point (device_path,
1027 mntent->mnt_dir,
1028 mntent->mnt_type,
1029 mntent->mnt_opts,
1030 is_read_only,
1031 is_user_mountable,
1032 is_loopback);
1034 return_list = g_list_prepend (return_list, mount_point);
1037 endmntent (file);
1039 #ifndef HAVE_GETMNTENT_R
1040 G_UNLOCK (getmntent);
1041 #endif
1043 return g_list_reverse (return_list);
1046 #endif /* HAVE_LIBMOUNT */
1048 /* mnttab.h {{{2 */
1049 #elif defined (HAVE_SYS_MNTTAB_H)
1051 static GList *
1052 _g_get_unix_mount_points (void)
1054 struct mnttab mntent;
1055 FILE *file;
1056 char *read_file;
1057 GUnixMountPoint *mount_point;
1058 GList *return_list;
1060 read_file = get_fstab_file ();
1062 file = setmntent (read_file, "r");
1063 if (file == NULL)
1064 return NULL;
1066 return_list = NULL;
1068 G_LOCK (getmntent);
1069 while (! getmntent (file, &mntent))
1071 gboolean is_read_only = FALSE;
1072 gboolean is_user_mountable = FALSE;
1073 gboolean is_loopback = FALSE;
1075 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1076 (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1077 (strcmp (mntent.mnt_mountp, "none") == 0))
1078 continue;
1080 #ifdef HAVE_HASMNTOPT
1081 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1082 is_read_only = TRUE;
1084 if (hasmntopt (&mntent, "lofs") != NULL)
1085 is_loopback = TRUE;
1086 #endif
1088 if ((mntent.mnt_fstype != NULL)
1089 #ifdef HAVE_HASMNTOPT
1090 || (hasmntopt (&mntent, "user") != NULL
1091 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1092 || hasmntopt (&mntent, "pamconsole") != NULL
1093 || hasmntopt (&mntent, "users") != NULL
1094 || hasmntopt (&mntent, "owner") != NULL
1095 #endif
1097 is_user_mountable = TRUE;
1099 mount_point = create_unix_mount_point (mntent.mnt_special,
1100 mntent.mnt_mountp,
1101 mntent.mnt_fstype,
1102 mntent.mnt_mntopts,
1103 is_read_only,
1104 is_user_mountable,
1105 is_loopback);
1107 return_list = g_list_prepend (return_list, mount_point);
1110 endmntent (file);
1111 G_UNLOCK (getmntent);
1113 return g_list_reverse (return_list);
1116 /* mntctl.h (AIX) {{{2 */
1117 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1119 /* functions to parse /etc/filesystems on aix */
1121 /* read character, ignoring comments (begin with '*', end with '\n' */
1122 static int
1123 aix_fs_getc (FILE *fd)
1125 int c;
1127 while ((c = getc (fd)) == '*')
1129 while (((c = getc (fd)) != '\n') && (c != EOF))
1134 /* eat all continuous spaces in a file */
1135 static int
1136 aix_fs_ignorespace (FILE *fd)
1138 int c;
1140 while ((c = aix_fs_getc (fd)) != EOF)
1142 if (!g_ascii_isspace (c))
1144 ungetc (c,fd);
1145 return c;
1149 return EOF;
1152 /* read one word from file */
1153 static int
1154 aix_fs_getword (FILE *fd,
1155 char *word)
1157 int c;
1159 aix_fs_ignorespace (fd);
1161 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1163 if (c == '"')
1165 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1166 *word++ = c;
1167 else
1168 *word++ = c;
1171 *word = 0;
1173 return c;
1176 typedef struct {
1177 char mnt_mount[PATH_MAX];
1178 char mnt_special[PATH_MAX];
1179 char mnt_fstype[16];
1180 char mnt_options[128];
1181 } AixMountTableEntry;
1183 /* read mount points properties */
1184 static int
1185 aix_fs_get (FILE *fd,
1186 AixMountTableEntry *prop)
1188 static char word[PATH_MAX] = { 0 };
1189 char value[PATH_MAX];
1191 /* read stanza */
1192 if (word[0] == 0)
1194 if (aix_fs_getword (fd, word) == EOF)
1195 return EOF;
1198 word[strlen(word) - 1] = 0;
1199 strcpy (prop->mnt_mount, word);
1201 /* read attributes and value */
1203 while (aix_fs_getword (fd, word) != EOF)
1205 /* test if is attribute or new stanza */
1206 if (word[strlen(word) - 1] == ':')
1207 return 0;
1209 /* read "=" */
1210 aix_fs_getword (fd, value);
1212 /* read value */
1213 aix_fs_getword (fd, value);
1215 if (strcmp (word, "dev") == 0)
1216 strcpy (prop->mnt_special, value);
1217 else if (strcmp (word, "vfs") == 0)
1218 strcpy (prop->mnt_fstype, value);
1219 else if (strcmp (word, "options") == 0)
1220 strcpy(prop->mnt_options, value);
1223 return 0;
1226 static GList *
1227 _g_get_unix_mount_points (void)
1229 struct mntent *mntent;
1230 FILE *file;
1231 char *read_file;
1232 GUnixMountPoint *mount_point;
1233 AixMountTableEntry mntent;
1234 GList *return_list;
1236 read_file = get_fstab_file ();
1238 file = setmntent (read_file, "r");
1239 if (file == NULL)
1240 return NULL;
1242 return_list = NULL;
1244 while (!aix_fs_get (file, &mntent))
1246 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1248 mount_point = create_unix_mount_point (mntent.mnt_special,
1249 mntent.mnt_mount,
1250 mntent.mnt_fstype,
1251 mntent.mnt_options,
1252 TRUE,
1253 TRUE,
1254 FALSE);
1256 return_list = g_list_prepend (return_list, mount_point);
1260 endmntent (file);
1262 return g_list_reverse (return_list);
1265 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1267 static GList *
1268 _g_get_unix_mount_points (void)
1270 struct fstab *fstab = NULL;
1271 GUnixMountPoint *mount_point;
1272 GList *return_list;
1273 #ifdef HAVE_SYS_SYSCTL_H
1274 int usermnt = 0;
1275 size_t len = sizeof(usermnt);
1276 struct stat sb;
1277 #endif
1279 if (!setfsent ())
1280 return NULL;
1282 return_list = NULL;
1284 #ifdef HAVE_SYS_SYSCTL_H
1285 #if defined(HAVE_SYSCTLBYNAME)
1286 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1287 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1289 int mib[2];
1291 mib[0] = CTL_VFS;
1292 mib[1] = VFS_USERMOUNT;
1293 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1295 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1297 int mib[2];
1299 mib[0] = CTL_KERN;
1300 mib[1] = KERN_USERMOUNT;
1301 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1303 #endif
1304 #endif
1306 while ((fstab = getfsent ()) != NULL)
1308 gboolean is_read_only = FALSE;
1309 gboolean is_user_mountable = FALSE;
1311 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1312 continue;
1314 if (strcmp (fstab->fs_type, "ro") == 0)
1315 is_read_only = TRUE;
1317 #ifdef HAVE_SYS_SYSCTL_H
1318 if (usermnt != 0)
1320 uid_t uid = getuid ();
1321 if (stat (fstab->fs_file, &sb) == 0)
1323 if (uid == 0 || sb.st_uid == uid)
1324 is_user_mountable = TRUE;
1327 #endif
1329 mount_point = create_unix_mount_point (fstab->fs_spec,
1330 fstab->fs_file,
1331 fstab->fs_vfstype,
1332 fstab->fs_mntops,
1333 is_read_only,
1334 is_user_mountable,
1335 FALSE);
1337 return_list = g_list_prepend (return_list, mount_point);
1340 endfsent ();
1342 return g_list_reverse (return_list);
1344 /* Interix {{{2 */
1345 #elif defined(__INTERIX)
1346 static GList *
1347 _g_get_unix_mount_points (void)
1349 return _g_get_unix_mounts ();
1352 /* Common code {{{2 */
1353 #else
1354 #error No g_get_mount_table() implementation for system
1355 #endif
1357 static guint64
1358 get_mounts_timestamp (void)
1360 const char *monitor_file;
1361 struct stat buf;
1363 monitor_file = get_mtab_monitor_file ();
1364 if (monitor_file)
1366 if (stat (monitor_file, &buf) == 0)
1367 return (guint64)buf.st_mtime;
1369 else
1371 return mount_poller_time;
1373 return 0;
1376 static guint64
1377 get_mount_points_timestamp (void)
1379 const char *monitor_file;
1380 struct stat buf;
1382 monitor_file = get_fstab_file ();
1383 if (monitor_file)
1385 if (stat (monitor_file, &buf) == 0)
1386 return (guint64)buf.st_mtime;
1388 return 0;
1392 * g_unix_mounts_get: (skip)
1393 * @time_read: (out) (optional): guint64 to contain a timestamp, or %NULL
1395 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1396 * If @time_read is set, it will be filled with the mount
1397 * timestamp, allowing for checking if the mounts have changed
1398 * with g_unix_mounts_changed_since().
1400 * Returns: (element-type GUnixMountEntry) (transfer full):
1401 * a #GList of the UNIX mounts.
1403 GList *
1404 g_unix_mounts_get (guint64 *time_read)
1406 if (time_read)
1407 *time_read = get_mounts_timestamp ();
1409 return _g_get_unix_mounts ();
1413 * g_unix_mount_at: (skip)
1414 * @mount_path: path for a possible unix mount.
1415 * @time_read: (out) (optional): guint64 to contain a timestamp.
1417 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1418 * is set, it will be filled with a unix timestamp for checking
1419 * if the mounts have changed since with g_unix_mounts_changed_since().
1421 * Returns: (transfer full): a #GUnixMountEntry.
1423 GUnixMountEntry *
1424 g_unix_mount_at (const char *mount_path,
1425 guint64 *time_read)
1427 GList *mounts, *l;
1428 GUnixMountEntry *mount_entry, *found;
1430 mounts = g_unix_mounts_get (time_read);
1432 found = NULL;
1433 for (l = mounts; l != NULL; l = l->next)
1435 mount_entry = l->data;
1437 if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1438 found = mount_entry;
1439 else
1440 g_unix_mount_free (mount_entry);
1442 g_list_free (mounts);
1444 return found;
1448 * g_unix_mount_for: (skip)
1449 * @file_path: file path on some unix mount.
1450 * @time_read: (out) (optional): guint64 to contain a timestamp.
1452 * Gets a #GUnixMountEntry for a given file path. If @time_read
1453 * is set, it will be filled with a unix timestamp for checking
1454 * if the mounts have changed since with g_unix_mounts_changed_since().
1456 * Returns: (transfer full): a #GUnixMountEntry.
1458 * Since: 2.52
1460 GUnixMountEntry *
1461 g_unix_mount_for (const char *file_path,
1462 guint64 *time_read)
1464 GUnixMountEntry *entry;
1466 g_return_val_if_fail (file_path != NULL, NULL);
1468 entry = g_unix_mount_at (file_path, time_read);
1469 if (entry == NULL)
1471 char *topdir;
1473 topdir = _g_local_file_find_topdir_for (file_path);
1474 if (topdir != NULL)
1476 entry = g_unix_mount_at (topdir, time_read);
1477 g_free (topdir);
1481 return entry;
1485 * g_unix_mount_points_get: (skip)
1486 * @time_read: (out) (optional): guint64 to contain a timestamp.
1488 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1489 * If @time_read is set, it will be filled with the mount timestamp,
1490 * allowing for checking if the mounts have changed with
1491 * g_unix_mount_points_changed_since().
1493 * Returns: (element-type GUnixMountPoint) (transfer full):
1494 * a #GList of the UNIX mountpoints.
1496 GList *
1497 g_unix_mount_points_get (guint64 *time_read)
1499 if (time_read)
1500 *time_read = get_mount_points_timestamp ();
1502 return _g_get_unix_mount_points ();
1506 * g_unix_mounts_changed_since:
1507 * @time: guint64 to contain a timestamp.
1509 * Checks if the unix mounts have changed since a given unix time.
1511 * Returns: %TRUE if the mounts have changed since @time.
1513 gboolean
1514 g_unix_mounts_changed_since (guint64 time)
1516 return get_mounts_timestamp () != time;
1520 * g_unix_mount_points_changed_since:
1521 * @time: guint64 to contain a timestamp.
1523 * Checks if the unix mount points have changed since a given unix time.
1525 * Returns: %TRUE if the mount points have changed since @time.
1527 gboolean
1528 g_unix_mount_points_changed_since (guint64 time)
1530 return get_mount_points_timestamp () != time;
1533 /* GUnixMountMonitor {{{1 */
1535 enum {
1536 MOUNTS_CHANGED,
1537 MOUNTPOINTS_CHANGED,
1538 LAST_SIGNAL
1541 static guint signals[LAST_SIGNAL];
1543 struct _GUnixMountMonitor {
1544 GObject parent;
1546 GMainContext *context;
1549 struct _GUnixMountMonitorClass {
1550 GObjectClass parent_class;
1554 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
1556 static GContextSpecificGroup mount_monitor_group;
1557 static GFileMonitor *fstab_monitor;
1558 static GFileMonitor *mtab_monitor;
1559 static GSource *proc_mounts_watch_source;
1560 static GList *mount_poller_mounts;
1562 static void
1563 fstab_file_changed (GFileMonitor *monitor,
1564 GFile *file,
1565 GFile *other_file,
1566 GFileMonitorEvent event_type,
1567 gpointer user_data)
1569 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1570 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1571 event_type != G_FILE_MONITOR_EVENT_DELETED)
1572 return;
1574 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1577 static void
1578 mtab_file_changed (GFileMonitor *monitor,
1579 GFile *file,
1580 GFile *other_file,
1581 GFileMonitorEvent event_type,
1582 gpointer user_data)
1584 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1585 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1586 event_type != G_FILE_MONITOR_EVENT_DELETED)
1587 return;
1589 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1592 static gboolean
1593 proc_mounts_changed (GIOChannel *channel,
1594 GIOCondition cond,
1595 gpointer user_data)
1597 if (cond & G_IO_ERR)
1598 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1600 return TRUE;
1603 static gboolean
1604 mount_change_poller (gpointer user_data)
1606 GList *current_mounts, *new_it, *old_it;
1607 gboolean has_changed = FALSE;
1609 current_mounts = _g_get_unix_mounts ();
1611 for ( new_it = current_mounts, old_it = mount_poller_mounts;
1612 new_it != NULL && old_it != NULL;
1613 new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1615 if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1617 has_changed = TRUE;
1618 break;
1621 if (!(new_it == NULL && old_it == NULL))
1622 has_changed = TRUE;
1624 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1626 mount_poller_mounts = current_mounts;
1628 if (has_changed)
1630 mount_poller_time = (guint64) g_get_monotonic_time ();
1631 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1634 return TRUE;
1638 static void
1639 mount_monitor_stop (void)
1641 if (fstab_monitor)
1643 g_file_monitor_cancel (fstab_monitor);
1644 g_object_unref (fstab_monitor);
1647 if (proc_mounts_watch_source != NULL)
1648 g_source_destroy (proc_mounts_watch_source);
1650 if (mtab_monitor)
1652 g_file_monitor_cancel (mtab_monitor);
1653 g_object_unref (mtab_monitor);
1656 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1659 static void
1660 mount_monitor_start (void)
1662 GFile *file;
1664 if (get_fstab_file () != NULL)
1666 file = g_file_new_for_path (get_fstab_file ());
1667 fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1668 g_object_unref (file);
1670 g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1673 if (get_mtab_monitor_file () != NULL)
1675 const gchar *mtab_path;
1677 mtab_path = get_mtab_monitor_file ();
1678 /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1679 * See 'man proc' for more details.
1681 if (g_str_has_prefix (mtab_path, "/proc/"))
1683 GIOChannel *proc_mounts_channel;
1684 GError *error = NULL;
1685 proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
1686 if (proc_mounts_channel == NULL)
1688 g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1689 error->message, g_quark_to_string (error->domain), error->code);
1690 g_error_free (error);
1692 else
1694 proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1695 g_source_set_callback (proc_mounts_watch_source,
1696 (GSourceFunc) proc_mounts_changed,
1697 NULL, NULL);
1698 g_source_attach (proc_mounts_watch_source,
1699 g_main_context_get_thread_default ());
1700 g_source_unref (proc_mounts_watch_source);
1701 g_io_channel_unref (proc_mounts_channel);
1704 else
1706 file = g_file_new_for_path (mtab_path);
1707 mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1708 g_object_unref (file);
1709 g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
1712 else
1714 proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1715 mount_poller_mounts = _g_get_unix_mounts ();
1716 mount_poller_time = (guint64)g_get_monotonic_time ();
1717 g_source_set_callback (proc_mounts_watch_source,
1718 mount_change_poller,
1719 NULL, NULL);
1720 g_source_attach (proc_mounts_watch_source,
1721 g_main_context_get_thread_default ());
1722 g_source_unref (proc_mounts_watch_source);
1726 static void
1727 g_unix_mount_monitor_finalize (GObject *object)
1729 GUnixMountMonitor *monitor;
1731 monitor = G_UNIX_MOUNT_MONITOR (object);
1733 g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
1735 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1738 static void
1739 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1741 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1743 gobject_class->finalize = g_unix_mount_monitor_finalize;
1746 * GUnixMountMonitor::mounts-changed:
1747 * @monitor: the object on which the signal is emitted
1749 * Emitted when the unix mounts have changed.
1751 signals[MOUNTS_CHANGED] =
1752 g_signal_new (I_("mounts-changed"),
1753 G_TYPE_FROM_CLASS (klass),
1754 G_SIGNAL_RUN_LAST,
1756 NULL, NULL,
1757 g_cclosure_marshal_VOID__VOID,
1758 G_TYPE_NONE, 0);
1761 * GUnixMountMonitor::mountpoints-changed:
1762 * @monitor: the object on which the signal is emitted
1764 * Emitted when the unix mount points have changed.
1766 signals[MOUNTPOINTS_CHANGED] =
1767 g_signal_new (I_("mountpoints-changed"),
1768 G_TYPE_FROM_CLASS (klass),
1769 G_SIGNAL_RUN_LAST,
1771 NULL, NULL,
1772 g_cclosure_marshal_VOID__VOID,
1773 G_TYPE_NONE, 0);
1776 static void
1777 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1782 * g_unix_mount_monitor_set_rate_limit:
1783 * @mount_monitor: a #GUnixMountMonitor
1784 * @limit_msec: a integer with the limit in milliseconds to
1785 * poll for changes.
1787 * This function does nothing.
1789 * Before 2.44, this was a partially-effective way of controlling the
1790 * rate at which events would be reported under some uncommon
1791 * circumstances. Since @mount_monitor is a singleton, it also meant
1792 * that calling this function would have side effects for other users of
1793 * the monitor.
1795 * Since: 2.18
1797 * Deprecated:2.44:This function does nothing. Don't call it.
1799 void
1800 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1801 gint limit_msec)
1806 * g_unix_mount_monitor_get:
1808 * Gets the #GUnixMountMonitor for the current thread-default main
1809 * context.
1811 * The mount monitor can be used to monitor for changes to the list of
1812 * mounted filesystems as well as the list of mount points (ie: fstab
1813 * entries).
1815 * You must only call g_object_unref() on the return value from under
1816 * the same main context as you called this function.
1818 * Returns: (transfer full): the #GUnixMountMonitor.
1820 * Since: 2.44
1822 GUnixMountMonitor *
1823 g_unix_mount_monitor_get (void)
1825 return g_context_specific_group_get (&mount_monitor_group,
1826 G_TYPE_UNIX_MOUNT_MONITOR,
1827 G_STRUCT_OFFSET(GUnixMountMonitor, context),
1828 mount_monitor_start);
1832 * g_unix_mount_monitor_new:
1834 * Deprecated alias for g_unix_mount_monitor_get().
1836 * This function was never a true constructor, which is why it was
1837 * renamed.
1839 * Returns: a #GUnixMountMonitor.
1841 * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
1843 GUnixMountMonitor *
1844 g_unix_mount_monitor_new (void)
1846 return g_unix_mount_monitor_get ();
1849 /* GUnixMount {{{1 */
1851 * g_unix_mount_free:
1852 * @mount_entry: a #GUnixMountEntry.
1854 * Frees a unix mount.
1856 void
1857 g_unix_mount_free (GUnixMountEntry *mount_entry)
1859 g_return_if_fail (mount_entry != NULL);
1861 g_free (mount_entry->mount_path);
1862 g_free (mount_entry->device_path);
1863 g_free (mount_entry->filesystem_type);
1864 g_free (mount_entry);
1868 * g_unix_mount_point_free:
1869 * @mount_point: unix mount point to free.
1871 * Frees a unix mount point.
1873 void
1874 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1876 g_return_if_fail (mount_point != NULL);
1878 g_free (mount_point->mount_path);
1879 g_free (mount_point->device_path);
1880 g_free (mount_point->filesystem_type);
1881 g_free (mount_point->options);
1882 g_free (mount_point);
1886 * g_unix_mount_compare:
1887 * @mount1: first #GUnixMountEntry to compare.
1888 * @mount2: second #GUnixMountEntry to compare.
1890 * Compares two unix mounts.
1892 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1893 * or less than @mount2, respectively.
1895 gint
1896 g_unix_mount_compare (GUnixMountEntry *mount1,
1897 GUnixMountEntry *mount2)
1899 int res;
1901 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1903 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1904 if (res != 0)
1905 return res;
1907 res = g_strcmp0 (mount1->device_path, mount2->device_path);
1908 if (res != 0)
1909 return res;
1911 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1912 if (res != 0)
1913 return res;
1915 res = mount1->is_read_only - mount2->is_read_only;
1916 if (res != 0)
1917 return res;
1919 return 0;
1923 * g_unix_mount_get_mount_path:
1924 * @mount_entry: input #GUnixMountEntry to get the mount path for.
1926 * Gets the mount path for a unix mount.
1928 * Returns: (type filename): the mount path for @mount_entry.
1930 const gchar *
1931 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1933 g_return_val_if_fail (mount_entry != NULL, NULL);
1935 return mount_entry->mount_path;
1939 * g_unix_mount_get_device_path:
1940 * @mount_entry: a #GUnixMount.
1942 * Gets the device path for a unix mount.
1944 * Returns: (type filename): a string containing the device path.
1946 const gchar *
1947 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1949 g_return_val_if_fail (mount_entry != NULL, NULL);
1951 return mount_entry->device_path;
1955 * g_unix_mount_get_fs_type:
1956 * @mount_entry: a #GUnixMount.
1958 * Gets the filesystem type for the unix mount.
1960 * Returns: a string containing the file system type.
1962 const gchar *
1963 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1965 g_return_val_if_fail (mount_entry != NULL, NULL);
1967 return mount_entry->filesystem_type;
1971 * g_unix_mount_is_readonly:
1972 * @mount_entry: a #GUnixMount.
1974 * Checks if a unix mount is mounted read only.
1976 * Returns: %TRUE if @mount_entry is read only.
1978 gboolean
1979 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1981 g_return_val_if_fail (mount_entry != NULL, FALSE);
1983 return mount_entry->is_read_only;
1987 * g_unix_mount_is_system_internal:
1988 * @mount_entry: a #GUnixMount.
1990 * Checks if a unix mount is a system path.
1992 * Returns: %TRUE if the unix mount is for a system path.
1994 gboolean
1995 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1997 g_return_val_if_fail (mount_entry != NULL, FALSE);
1999 return mount_entry->is_system_internal;
2002 /* GUnixMountPoint {{{1 */
2004 * g_unix_mount_point_compare:
2005 * @mount1: a #GUnixMount.
2006 * @mount2: a #GUnixMount.
2008 * Compares two unix mount points.
2010 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2011 * or less than @mount2, respectively.
2013 gint
2014 g_unix_mount_point_compare (GUnixMountPoint *mount1,
2015 GUnixMountPoint *mount2)
2017 int res;
2019 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2021 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2022 if (res != 0)
2023 return res;
2025 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2026 if (res != 0)
2027 return res;
2029 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2030 if (res != 0)
2031 return res;
2033 res = g_strcmp0 (mount1->options, mount2->options);
2034 if (res != 0)
2035 return res;
2037 res = mount1->is_read_only - mount2->is_read_only;
2038 if (res != 0)
2039 return res;
2041 res = mount1->is_user_mountable - mount2->is_user_mountable;
2042 if (res != 0)
2043 return res;
2045 res = mount1->is_loopback - mount2->is_loopback;
2046 if (res != 0)
2047 return res;
2049 return 0;
2053 * g_unix_mount_point_get_mount_path:
2054 * @mount_point: a #GUnixMountPoint.
2056 * Gets the mount path for a unix mount point.
2058 * Returns: (type filename): a string containing the mount path.
2060 const gchar *
2061 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2063 g_return_val_if_fail (mount_point != NULL, NULL);
2065 return mount_point->mount_path;
2069 * g_unix_mount_point_get_device_path:
2070 * @mount_point: a #GUnixMountPoint.
2072 * Gets the device path for a unix mount point.
2074 * Returns: (type filename): a string containing the device path.
2076 const gchar *
2077 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2079 g_return_val_if_fail (mount_point != NULL, NULL);
2081 return mount_point->device_path;
2085 * g_unix_mount_point_get_fs_type:
2086 * @mount_point: a #GUnixMountPoint.
2088 * Gets the file system type for the mount point.
2090 * Returns: a string containing the file system type.
2092 const gchar *
2093 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2095 g_return_val_if_fail (mount_point != NULL, NULL);
2097 return mount_point->filesystem_type;
2101 * g_unix_mount_point_get_options:
2102 * @mount_point: a #GUnixMountPoint.
2104 * Gets the options for the mount point.
2106 * Returns: a string containing the options.
2108 * Since: 2.32
2110 const gchar *
2111 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2113 g_return_val_if_fail (mount_point != NULL, NULL);
2115 return mount_point->options;
2119 * g_unix_mount_point_is_readonly:
2120 * @mount_point: a #GUnixMountPoint.
2122 * Checks if a unix mount point is read only.
2124 * Returns: %TRUE if a mount point is read only.
2126 gboolean
2127 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2129 g_return_val_if_fail (mount_point != NULL, FALSE);
2131 return mount_point->is_read_only;
2135 * g_unix_mount_point_is_user_mountable:
2136 * @mount_point: a #GUnixMountPoint.
2138 * Checks if a unix mount point is mountable by the user.
2140 * Returns: %TRUE if the mount point is user mountable.
2142 gboolean
2143 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2145 g_return_val_if_fail (mount_point != NULL, FALSE);
2147 return mount_point->is_user_mountable;
2151 * g_unix_mount_point_is_loopback:
2152 * @mount_point: a #GUnixMountPoint.
2154 * Checks if a unix mount point is a loopback device.
2156 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
2158 gboolean
2159 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2161 g_return_val_if_fail (mount_point != NULL, FALSE);
2163 return mount_point->is_loopback;
2166 static GUnixMountType
2167 guess_mount_type (const char *mount_path,
2168 const char *device_path,
2169 const char *filesystem_type)
2171 GUnixMountType type;
2172 char *basename;
2174 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2176 if ((strcmp (filesystem_type, "udf") == 0) ||
2177 (strcmp (filesystem_type, "iso9660") == 0) ||
2178 (strcmp (filesystem_type, "cd9660") == 0))
2179 type = G_UNIX_MOUNT_TYPE_CDROM;
2180 else if ((strcmp (filesystem_type, "nfs") == 0) ||
2181 (strcmp (filesystem_type, "nfs4") == 0))
2182 type = G_UNIX_MOUNT_TYPE_NFS;
2183 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
2184 g_str_has_prefix (device_path, "/dev/fd") ||
2185 g_str_has_prefix (device_path, "/dev/floppy"))
2186 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2187 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
2188 g_str_has_prefix (device_path, "/dev/acd") ||
2189 g_str_has_prefix (device_path, "/dev/cd"))
2190 type = G_UNIX_MOUNT_TYPE_CDROM;
2191 else if (g_str_has_prefix (device_path, "/vol/"))
2193 const char *name = mount_path + strlen ("/");
2195 if (g_str_has_prefix (name, "cdrom"))
2196 type = G_UNIX_MOUNT_TYPE_CDROM;
2197 else if (g_str_has_prefix (name, "floppy") ||
2198 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
2199 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2200 else if (g_str_has_prefix (name, "rmdisk"))
2201 type = G_UNIX_MOUNT_TYPE_ZIP;
2202 else if (g_str_has_prefix (name, "jaz"))
2203 type = G_UNIX_MOUNT_TYPE_JAZ;
2204 else if (g_str_has_prefix (name, "memstick"))
2205 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2207 else
2209 basename = g_path_get_basename (mount_path);
2211 if (g_str_has_prefix (basename, "cdr") ||
2212 g_str_has_prefix (basename, "cdwriter") ||
2213 g_str_has_prefix (basename, "burn") ||
2214 g_str_has_prefix (basename, "dvdr"))
2215 type = G_UNIX_MOUNT_TYPE_CDROM;
2216 else if (g_str_has_prefix (basename, "floppy"))
2217 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2218 else if (g_str_has_prefix (basename, "zip"))
2219 type = G_UNIX_MOUNT_TYPE_ZIP;
2220 else if (g_str_has_prefix (basename, "jaz"))
2221 type = G_UNIX_MOUNT_TYPE_JAZ;
2222 else if (g_str_has_prefix (basename, "camera"))
2223 type = G_UNIX_MOUNT_TYPE_CAMERA;
2224 else if (g_str_has_prefix (basename, "memstick") ||
2225 g_str_has_prefix (basename, "memory_stick") ||
2226 g_str_has_prefix (basename, "ram"))
2227 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2228 else if (g_str_has_prefix (basename, "compact_flash"))
2229 type = G_UNIX_MOUNT_TYPE_CF;
2230 else if (g_str_has_prefix (basename, "smart_media"))
2231 type = G_UNIX_MOUNT_TYPE_SM;
2232 else if (g_str_has_prefix (basename, "sd_mmc"))
2233 type = G_UNIX_MOUNT_TYPE_SDMMC;
2234 else if (g_str_has_prefix (basename, "ipod"))
2235 type = G_UNIX_MOUNT_TYPE_IPOD;
2237 g_free (basename);
2240 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2241 type = G_UNIX_MOUNT_TYPE_HD;
2243 return type;
2247 * g_unix_mount_guess_type:
2248 * @mount_entry: a #GUnixMount.
2250 * Guesses the type of a unix mount. If the mount type cannot be
2251 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2253 * Returns: a #GUnixMountType.
2255 static GUnixMountType
2256 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2258 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2259 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2260 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2261 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2263 return guess_mount_type (mount_entry->mount_path,
2264 mount_entry->device_path,
2265 mount_entry->filesystem_type);
2269 * g_unix_mount_point_guess_type:
2270 * @mount_point: a #GUnixMountPoint.
2272 * Guesses the type of a unix mount point.
2273 * If the mount type cannot be determined,
2274 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2276 * Returns: a #GUnixMountType.
2278 static GUnixMountType
2279 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2281 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2282 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2283 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2284 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2286 return guess_mount_type (mount_point->mount_path,
2287 mount_point->device_path,
2288 mount_point->filesystem_type);
2291 static const char *
2292 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2294 const char *icon_name;
2296 switch (type)
2298 case G_UNIX_MOUNT_TYPE_HD:
2299 if (is_mount_point)
2300 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2301 else
2302 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2303 break;
2304 case G_UNIX_MOUNT_TYPE_FLOPPY:
2305 case G_UNIX_MOUNT_TYPE_ZIP:
2306 case G_UNIX_MOUNT_TYPE_JAZ:
2307 if (is_mount_point)
2308 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2309 else
2310 icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2311 break;
2312 case G_UNIX_MOUNT_TYPE_CDROM:
2313 if (is_mount_point)
2314 icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2315 else
2316 icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2317 break;
2318 case G_UNIX_MOUNT_TYPE_NFS:
2319 icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2320 break;
2321 case G_UNIX_MOUNT_TYPE_MEMSTICK:
2322 if (is_mount_point)
2323 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2324 else
2325 icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2326 break;
2327 case G_UNIX_MOUNT_TYPE_CAMERA:
2328 if (is_mount_point)
2329 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2330 else
2331 icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2332 break;
2333 case G_UNIX_MOUNT_TYPE_IPOD:
2334 if (is_mount_point)
2335 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2336 else
2337 icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2338 break;
2339 case G_UNIX_MOUNT_TYPE_UNKNOWN:
2340 default:
2341 if (is_mount_point)
2342 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2343 else
2344 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2345 break;
2348 return icon_name;
2352 * g_unix_mount_guess_name:
2353 * @mount_entry: a #GUnixMountEntry
2355 * Guesses the name of a Unix mount.
2356 * The result is a translated string.
2358 * Returns: A newly allocated string that must
2359 * be freed with g_free()
2361 gchar *
2362 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2364 char *name;
2366 if (strcmp (mount_entry->mount_path, "/") == 0)
2367 name = g_strdup (_("Filesystem root"));
2368 else
2369 name = g_filename_display_basename (mount_entry->mount_path);
2371 return name;
2375 * g_unix_mount_guess_icon:
2376 * @mount_entry: a #GUnixMountEntry
2378 * Guesses the icon of a Unix mount.
2380 * Returns: (transfer full): a #GIcon
2382 GIcon *
2383 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2385 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2389 * g_unix_mount_guess_symbolic_icon:
2390 * @mount_entry: a #GUnixMountEntry
2392 * Guesses the symbolic icon of a Unix mount.
2394 * Returns: (transfer full): a #GIcon
2396 * Since: 2.34
2398 GIcon *
2399 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2401 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2405 * g_unix_mount_point_guess_name:
2406 * @mount_point: a #GUnixMountPoint
2408 * Guesses the name of a Unix mount point.
2409 * The result is a translated string.
2411 * Returns: A newly allocated string that must
2412 * be freed with g_free()
2414 gchar *
2415 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2417 char *name;
2419 if (strcmp (mount_point->mount_path, "/") == 0)
2420 name = g_strdup (_("Filesystem root"));
2421 else
2422 name = g_filename_display_basename (mount_point->mount_path);
2424 return name;
2428 * g_unix_mount_point_guess_icon:
2429 * @mount_point: a #GUnixMountPoint
2431 * Guesses the icon of a Unix mount point.
2433 * Returns: (transfer full): a #GIcon
2435 GIcon *
2436 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2438 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2442 * g_unix_mount_point_guess_symbolic_icon:
2443 * @mount_point: a #GUnixMountPoint
2445 * Guesses the symbolic icon of a Unix mount point.
2447 * Returns: (transfer full): a #GIcon
2449 * Since: 2.34
2451 GIcon *
2452 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2454 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2458 * g_unix_mount_guess_can_eject:
2459 * @mount_entry: a #GUnixMountEntry
2461 * Guesses whether a Unix mount can be ejected.
2463 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2465 gboolean
2466 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2468 GUnixMountType guessed_type;
2470 guessed_type = g_unix_mount_guess_type (mount_entry);
2471 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2472 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2473 return TRUE;
2475 return FALSE;
2479 * g_unix_mount_guess_should_display:
2480 * @mount_entry: a #GUnixMountEntry
2482 * Guesses whether a Unix mount should be displayed in the UI.
2484 * Returns: %TRUE if @mount_entry is deemed to be displayable.
2486 gboolean
2487 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2489 const char *mount_path;
2490 const gchar *user_name;
2491 gsize user_name_len;
2493 /* Never display internal mountpoints */
2494 if (g_unix_mount_is_system_internal (mount_entry))
2495 return FALSE;
2497 /* Only display things in /media (which are generally user mountable)
2498 and home dir (fuse stuff) and /run/media/$USER */
2499 mount_path = mount_entry->mount_path;
2500 if (mount_path != NULL)
2502 gboolean is_in_runtime_dir = FALSE;
2503 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2504 if (g_strstr_len (mount_path, -1, "/.") != NULL)
2505 return FALSE;
2507 /* Check /run/media/$USER/ */
2508 user_name = g_get_user_name ();
2509 user_name_len = strlen (user_name);
2510 if (strncmp (mount_path, "/run/media/", sizeof ("/run/media/") - 1) == 0 &&
2511 strncmp (mount_path + sizeof ("/run/media/") - 1, user_name, user_name_len) == 0 &&
2512 mount_path[sizeof ("/run/media/") - 1 + user_name_len] == '/')
2513 is_in_runtime_dir = TRUE;
2515 if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2517 char *path;
2518 /* Avoid displaying mounts that are not accessible to the user.
2520 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2521 * want to avoid g_access() for mount points which can potentially
2522 * block or fail stat()'ing, such as network mounts.
2524 path = g_path_get_dirname (mount_path);
2525 if (g_str_has_prefix (path, "/media/"))
2527 if (g_access (path, R_OK|X_OK) != 0)
2529 g_free (path);
2530 return FALSE;
2533 g_free (path);
2535 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2537 struct stat st;
2538 if (g_stat (mount_entry->device_path, &st) == 0 &&
2539 S_ISBLK(st.st_mode) &&
2540 g_access (mount_path, R_OK|X_OK) != 0)
2541 return FALSE;
2543 return TRUE;
2546 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
2547 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2548 return TRUE;
2551 return FALSE;
2555 * g_unix_mount_point_guess_can_eject:
2556 * @mount_point: a #GUnixMountPoint
2558 * Guesses whether a Unix mount point can be ejected.
2560 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2562 gboolean
2563 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2565 GUnixMountType guessed_type;
2567 guessed_type = g_unix_mount_point_guess_type (mount_point);
2568 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2569 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2570 return TRUE;
2572 return FALSE;
2575 /* Utility functions {{{1 */
2577 #ifdef HAVE_MNTENT_H
2578 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2579 static void
2580 _canonicalize_filename (gchar *filename)
2582 gchar *p, *q;
2583 gboolean last_was_slash = FALSE;
2585 p = filename;
2586 q = filename;
2588 while (*p)
2590 if (*p == G_DIR_SEPARATOR)
2592 if (!last_was_slash)
2593 *q++ = G_DIR_SEPARATOR;
2595 last_was_slash = TRUE;
2597 else
2599 if (last_was_slash && *p == '.')
2601 if (*(p + 1) == G_DIR_SEPARATOR ||
2602 *(p + 1) == '\0')
2604 if (*(p + 1) == '\0')
2605 break;
2607 p += 1;
2609 else if (*(p + 1) == '.' &&
2610 (*(p + 2) == G_DIR_SEPARATOR ||
2611 *(p + 2) == '\0'))
2613 if (q > filename + 1)
2615 q--;
2616 while (q > filename + 1 &&
2617 *(q - 1) != G_DIR_SEPARATOR)
2618 q--;
2621 if (*(p + 2) == '\0')
2622 break;
2624 p += 2;
2626 else
2628 *q++ = *p;
2629 last_was_slash = FALSE;
2632 else
2634 *q++ = *p;
2635 last_was_slash = FALSE;
2639 p++;
2642 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2643 q--;
2645 *q = '\0';
2648 static char *
2649 _resolve_symlink (const char *file)
2651 GError *error;
2652 char *dir;
2653 char *link;
2654 char *f;
2655 char *f1;
2657 f = g_strdup (file);
2659 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
2661 link = g_file_read_link (f, &error);
2662 if (link == NULL)
2664 g_error_free (error);
2665 g_free (f);
2666 f = NULL;
2667 goto out;
2670 dir = g_path_get_dirname (f);
2671 f1 = g_strdup_printf ("%s/%s", dir, link);
2672 g_free (dir);
2673 g_free (link);
2674 g_free (f);
2675 f = f1;
2678 out:
2679 if (f != NULL)
2680 _canonicalize_filename (f);
2681 return f;
2684 static const char *
2685 _resolve_dev_root (void)
2687 static gboolean have_real_dev_root = FALSE;
2688 static char real_dev_root[256];
2689 struct stat statbuf;
2691 /* see if it's cached already */
2692 if (have_real_dev_root)
2693 goto found;
2695 /* otherwise we're going to find it right away.. */
2696 have_real_dev_root = TRUE;
2698 if (stat ("/dev/root", &statbuf) == 0)
2700 if (! S_ISLNK (statbuf.st_mode))
2702 dev_t root_dev = statbuf.st_dev;
2703 FILE *f;
2705 /* see if device with similar major:minor as /dev/root is mention
2706 * in /etc/mtab (it usually is)
2708 f = fopen ("/etc/mtab", "r");
2709 if (f != NULL)
2711 struct mntent *entp;
2712 #ifdef HAVE_GETMNTENT_R
2713 struct mntent ent;
2714 char buf[1024];
2715 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
2717 #else
2718 G_LOCK (getmntent);
2719 while ((entp = getmntent (f)) != NULL)
2721 #endif
2722 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2723 statbuf.st_dev == root_dev)
2725 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2726 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2727 fclose (f);
2728 goto found;
2732 endmntent (f);
2734 #ifndef HAVE_GETMNTENT_R
2735 G_UNLOCK (getmntent);
2736 #endif
2739 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2742 else
2744 char *resolved;
2745 resolved = _resolve_symlink ("/dev/root");
2746 if (resolved != NULL)
2748 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2749 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2750 g_free (resolved);
2751 goto found;
2756 /* bah sucks.. */
2757 strcpy (real_dev_root, "/dev/root");
2759 found:
2760 return real_dev_root;
2762 #endif
2764 /* Epilogue {{{1 */
2765 /* vim:set foldmethod=marker: */