Improve docs
[glib.git] / gio / gunixmounts.c
blob4cc87cd8c72f9fa1336f3de33026189ec96e67a1
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, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
22 * Author: Alexander Larsson <alexl@redhat.com>
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 #ifdef HAVE_SYS_POLL_H
35 #include <sys/poll.h>
36 #endif
37 #endif
38 #ifdef HAVE_POLL_H
39 #include <poll.h>
40 #endif
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <sys/time.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <signal.h>
47 #include <gstdio.h>
48 #include <dirent.h>
50 #if HAVE_SYS_STATFS_H
51 #include <sys/statfs.h>
52 #endif
53 #if HAVE_SYS_STATVFS_H
54 #include <sys/statvfs.h>
55 #endif
56 #if HAVE_SYS_VFS_H
57 #include <sys/vfs.h>
58 #elif HAVE_SYS_MOUNT_H
59 #if HAVE_SYS_PARAM_H
60 #include <sys/param.h>
61 #endif
62 #include <sys/mount.h>
63 #endif
65 #ifndef O_BINARY
66 #define O_BINARY 0
67 #endif
69 #include "gunixmounts.h"
70 #include "gfile.h"
71 #include "gfilemonitor.h"
72 #include "glibintl.h"
73 #include "gthemedicon.h"
76 #ifdef HAVE_MNTENT_H
77 static const char *_resolve_dev_root (void);
78 #endif
80 /**
81 * SECTION:gunixmounts
82 * @include: gio/gunixmounts.h
83 * @short_description: UNIX mounts
85 * Routines for managing mounted UNIX mount points and paths.
87 * Note that <filename>&lt;gio/gunixmounts.h&gt;</filename> belongs to the
88 * UNIX-specific GIO interfaces, thus you have to use the
89 * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
93 * GUnixMountType:
94 * @G_UNIX_MOUNT_TYPE_UNKNOWN: Unknown UNIX mount type.
95 * @G_UNIX_MOUNT_TYPE_FLOPPY: Floppy disk UNIX mount type.
96 * @G_UNIX_MOUNT_TYPE_CDROM: CDROM UNIX mount type.
97 * @G_UNIX_MOUNT_TYPE_NFS: Network File System (NFS) UNIX mount type.
98 * @G_UNIX_MOUNT_TYPE_ZIP: ZIP UNIX mount type.
99 * @G_UNIX_MOUNT_TYPE_JAZ: JAZZ UNIX mount type.
100 * @G_UNIX_MOUNT_TYPE_MEMSTICK: Memory Stick UNIX mount type.
101 * @G_UNIX_MOUNT_TYPE_CF: Compact Flash UNIX mount type.
102 * @G_UNIX_MOUNT_TYPE_SM: Smart Media UNIX mount type.
103 * @G_UNIX_MOUNT_TYPE_SDMMC: SD/MMC UNIX mount type.
104 * @G_UNIX_MOUNT_TYPE_IPOD: iPod UNIX mount type.
105 * @G_UNIX_MOUNT_TYPE_CAMERA: Digital camera UNIX mount type.
106 * @G_UNIX_MOUNT_TYPE_HD: Hard drive UNIX mount type.
108 * Types of UNIX mounts.
110 typedef enum {
111 G_UNIX_MOUNT_TYPE_UNKNOWN,
112 G_UNIX_MOUNT_TYPE_FLOPPY,
113 G_UNIX_MOUNT_TYPE_CDROM,
114 G_UNIX_MOUNT_TYPE_NFS,
115 G_UNIX_MOUNT_TYPE_ZIP,
116 G_UNIX_MOUNT_TYPE_JAZ,
117 G_UNIX_MOUNT_TYPE_MEMSTICK,
118 G_UNIX_MOUNT_TYPE_CF,
119 G_UNIX_MOUNT_TYPE_SM,
120 G_UNIX_MOUNT_TYPE_SDMMC,
121 G_UNIX_MOUNT_TYPE_IPOD,
122 G_UNIX_MOUNT_TYPE_CAMERA,
123 G_UNIX_MOUNT_TYPE_HD
124 } GUnixMountType;
126 struct _GUnixMountEntry {
127 char *mount_path;
128 char *device_path;
129 char *filesystem_type;
130 gboolean is_read_only;
131 gboolean is_system_internal;
134 struct _GUnixMountPoint {
135 char *mount_path;
136 char *device_path;
137 char *filesystem_type;
138 char *options;
139 gboolean is_read_only;
140 gboolean is_user_mountable;
141 gboolean is_loopback;
144 enum {
145 MOUNTS_CHANGED,
146 MOUNTPOINTS_CHANGED,
147 LAST_SIGNAL
150 static guint signals[LAST_SIGNAL];
152 struct _GUnixMountMonitor {
153 GObject parent;
155 GFileMonitor *fstab_monitor;
156 GFileMonitor *mtab_monitor;
158 GSource *proc_mounts_watch_source;
161 struct _GUnixMountMonitorClass {
162 GObjectClass parent_class;
165 static GUnixMountMonitor *the_mount_monitor = NULL;
167 static GList *_g_get_unix_mounts (void);
168 static GList *_g_get_unix_mount_points (void);
170 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
172 #define MOUNT_POLL_INTERVAL 4000
174 #ifdef HAVE_SYS_MNTTAB_H
175 #define MNTOPT_RO "ro"
176 #endif
178 #ifdef HAVE_MNTENT_H
179 #include <mntent.h>
180 #elif defined (HAVE_SYS_MNTTAB_H)
181 #include <sys/mnttab.h>
182 #endif
184 #ifdef HAVE_SYS_VFSTAB_H
185 #include <sys/vfstab.h>
186 #endif
188 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
189 #include <sys/mntctl.h>
190 #include <sys/vfs.h>
191 #include <sys/vmount.h>
192 #include <fshelp.h>
193 #endif
195 #if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
196 #include <sys/ucred.h>
197 #include <sys/mount.h>
198 #include <fstab.h>
199 #ifdef HAVE_SYS_SYSCTL_H
200 #include <sys/sysctl.h>
201 #endif
202 #endif
204 #ifndef HAVE_SETMNTENT
205 #define setmntent(f,m) fopen(f,m)
206 #endif
207 #ifndef HAVE_ENDMNTENT
208 #define endmntent(f) fclose(f)
209 #endif
211 static gboolean
212 is_in (const char *value, const char *set[])
214 int i;
215 for (i = 0; set[i] != NULL; i++)
217 if (strcmp (set[i], value) == 0)
218 return TRUE;
220 return FALSE;
224 * g_unix_is_mount_path_system_internal:
225 * @mount_path: a mount path, e.g. <filename>/media/disk</filename>
226 * or <filename>/usr</filename>
228 * Determines if @mount_path is considered an implementation of the
229 * OS. This is primarily used for hiding mountable and mounted volumes
230 * that only are used in the OS and has little to no relevance to the
231 * casual user.
233 * Returns: %TRUE if @mount_path is considered an implementation detail
234 * of the OS.
236 gboolean
237 g_unix_is_mount_path_system_internal (const char *mount_path)
239 const char *ignore_mountpoints[] = {
240 /* Includes all FHS 2.3 toplevel dirs and other specilized
241 * directories that we want to hide from the user.
243 "/", /* we already have "Filesystem root" in Nautilus */
244 "/bin",
245 "/boot",
246 "/dev",
247 "/etc",
248 "/home",
249 "/lib",
250 "/lib64",
251 "/live/cow",
252 "/live/image",
253 "/media",
254 "/mnt",
255 "/opt",
256 "/root",
257 "/sbin",
258 "/srv",
259 "/tmp",
260 "/usr",
261 "/usr/local",
262 "/var",
263 "/var/crash",
264 "/var/local",
265 "/var/log",
266 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
267 "/var/mail",
268 "/var/run",
269 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
270 "/proc",
271 "/sbin",
272 "/net",
273 "/sys",
274 NULL
277 if (is_in (mount_path, ignore_mountpoints))
278 return TRUE;
280 if (g_str_has_prefix (mount_path, "/dev/") ||
281 g_str_has_prefix (mount_path, "/proc/") ||
282 g_str_has_prefix (mount_path, "/sys/"))
283 return TRUE;
285 if (g_str_has_suffix (mount_path, "/.gvfs"))
286 return TRUE;
288 return FALSE;
291 static gboolean
292 guess_system_internal (const char *mountpoint,
293 const char *fs,
294 const char *device)
296 const char *ignore_fs[] = {
297 "auto",
298 "autofs",
299 "devfs",
300 "devpts",
301 "ecryptfs",
302 "kernfs",
303 "linprocfs",
304 "proc",
305 "procfs",
306 "ptyfs",
307 "rootfs",
308 "selinuxfs",
309 "sysfs",
310 "tmpfs",
311 "usbfs",
312 "nfsd",
313 "rpc_pipefs",
314 "zfs",
315 NULL
317 const char *ignore_devices[] = {
318 "none",
319 "sunrpc",
320 "devpts",
321 "nfsd",
322 "/dev/loop",
323 "/dev/vn",
324 NULL
327 if (is_in (fs, ignore_fs))
328 return TRUE;
330 if (is_in (device, ignore_devices))
331 return TRUE;
333 if (g_unix_is_mount_path_system_internal (mountpoint))
334 return TRUE;
336 return FALSE;
339 #ifdef HAVE_MNTENT_H
341 static char *
342 get_mtab_read_file (void)
344 #ifdef _PATH_MOUNTED
345 # ifdef __linux__
346 return "/proc/mounts";
347 # else
348 return _PATH_MOUNTED;
349 # endif
350 #else
351 return "/etc/mtab";
352 #endif
355 static char *
356 get_mtab_monitor_file (void)
358 #ifdef _PATH_MOUNTED
359 # ifdef __linux__
360 return "/proc/mounts";
361 # else
362 return _PATH_MOUNTED;
363 # endif
364 #else
365 return "/etc/mtab";
366 #endif
369 #ifndef HAVE_GETMNTENT_R
370 G_LOCK_DEFINE_STATIC(getmntent);
371 #endif
373 static GList *
374 _g_get_unix_mounts (void)
376 #ifdef HAVE_GETMNTENT_R
377 struct mntent ent;
378 char buf[1024];
379 #endif
380 struct mntent *mntent;
381 FILE *file;
382 char *read_file;
383 GUnixMountEntry *mount_entry;
384 GHashTable *mounts_hash;
385 GList *return_list;
387 read_file = get_mtab_read_file ();
389 file = setmntent (read_file, "r");
390 if (file == NULL)
391 return NULL;
393 return_list = NULL;
395 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
397 #ifdef HAVE_GETMNTENT_R
398 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
399 #else
400 G_LOCK (getmntent);
401 while ((mntent = getmntent (file)) != NULL)
402 #endif
404 /* ignore any mnt_fsname that is repeated and begins with a '/'
406 * We do this to avoid being fooled by --bind mounts, since
407 * these have the same device as the location they bind to.
408 * It's not an ideal solution to the problem, but it's likely that
409 * the most important mountpoint is first and the --bind ones after
410 * that aren't as important. So it should work.
412 * The '/' is to handle procfs, tmpfs and other no device mounts.
414 if (mntent->mnt_fsname != NULL &&
415 mntent->mnt_fsname[0] == '/' &&
416 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
417 continue;
419 mount_entry = g_new0 (GUnixMountEntry, 1);
420 mount_entry->mount_path = g_strdup (mntent->mnt_dir);
421 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
422 mount_entry->device_path = g_strdup (_resolve_dev_root ());
423 else
424 mount_entry->device_path = g_strdup (mntent->mnt_fsname);
425 mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
427 #if defined (HAVE_HASMNTOPT)
428 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
429 mount_entry->is_read_only = TRUE;
430 #endif
432 mount_entry->is_system_internal =
433 guess_system_internal (mount_entry->mount_path,
434 mount_entry->filesystem_type,
435 mount_entry->device_path);
437 g_hash_table_insert (mounts_hash,
438 mount_entry->device_path,
439 mount_entry->device_path);
441 return_list = g_list_prepend (return_list, mount_entry);
443 g_hash_table_destroy (mounts_hash);
445 endmntent (file);
447 #ifndef HAVE_GETMNTENT_R
448 G_UNLOCK (getmntent);
449 #endif
451 return g_list_reverse (return_list);
454 #elif defined (HAVE_SYS_MNTTAB_H)
456 G_LOCK_DEFINE_STATIC(getmntent);
458 static char *
459 get_mtab_read_file (void)
461 #ifdef _PATH_MOUNTED
462 return _PATH_MOUNTED;
463 #else
464 return "/etc/mnttab";
465 #endif
468 static char *
469 get_mtab_monitor_file (void)
471 return get_mtab_read_file ();
474 static GList *
475 _g_get_unix_mounts (void)
477 struct mnttab mntent;
478 FILE *file;
479 char *read_file;
480 GUnixMountEntry *mount_entry;
481 GList *return_list;
483 read_file = get_mtab_read_file ();
485 file = setmntent (read_file, "r");
486 if (file == NULL)
487 return NULL;
489 return_list = NULL;
491 G_LOCK (getmntent);
492 while (! getmntent (file, &mntent))
494 mount_entry = g_new0 (GUnixMountEntry, 1);
496 mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
497 mount_entry->device_path = g_strdup (mntent.mnt_special);
498 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
500 #if defined (HAVE_HASMNTOPT)
501 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
502 mount_entry->is_read_only = TRUE;
503 #endif
505 mount_entry->is_system_internal =
506 guess_system_internal (mount_entry->mount_path,
507 mount_entry->filesystem_type,
508 mount_entry->device_path);
510 return_list = g_list_prepend (return_list, mount_entry);
513 endmntent (file);
515 G_UNLOCK (getmntent);
517 return g_list_reverse (return_list);
520 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
522 static char *
523 get_mtab_monitor_file (void)
525 return NULL;
528 static GList *
529 _g_get_unix_mounts (void)
531 struct vfs_ent *fs_info;
532 struct vmount *vmount_info;
533 int vmount_number;
534 unsigned int vmount_size;
535 int current;
536 GList *return_list;
538 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
540 g_warning ("Unable to know the number of mounted volumes\n");
542 return NULL;
545 vmount_info = (struct vmount*)g_malloc (vmount_size);
547 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
549 if (vmount_info->vmt_revision != VMT_REVISION)
550 g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
552 if (vmount_number < 0)
554 g_warning ("Unable to recover mounted volumes information\n");
556 g_free (vmount_info);
557 return NULL;
560 return_list = NULL;
561 while (vmount_number > 0)
563 mount_entry = g_new0 (GUnixMountEntry, 1);
565 mount_entry->device_path = g_strdup (vmt2dataptr (vmount_info, VMT_OBJECT));
566 mount_entry->mount_path = g_strdup (vmt2dataptr (vmount_info, VMT_STUB));
567 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
568 mount_entry->is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
570 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
572 if (fs_info == NULL)
573 mount_entry->filesystem_type = g_strdup ("unknown");
574 else
575 mount_entry->filesystem_type = g_strdup (fs_info->vfsent_name);
577 mount_entry->is_system_internal =
578 guess_system_internal (mount_entry->mount_path,
579 mount_entry->filesystem_type,
580 mount_entry->device_path);
582 return_list = g_list_prepend (return_list, mount_entry);
584 vmount_info = (struct vmount *)( (char*)vmount_info
585 + vmount_info->vmt_length);
586 vmount_number--;
589 g_free (vmount_info);
591 return g_list_reverse (return_list);
594 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
596 static char *
597 get_mtab_monitor_file (void)
599 return NULL;
602 static GList *
603 _g_get_unix_mounts (void)
605 #if defined(HAVE_GETVFSSTAT)
606 struct statvfs *mntent = NULL;
607 #elif defined(HAVE_GETFSSTAT)
608 struct statfs *mntent = NULL;
609 #else
610 #error statfs juggling failed
611 #endif
612 size_t bufsize;
613 int num_mounts, i;
614 GUnixMountEntry *mount_entry;
615 GList *return_list;
617 /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
618 #if defined(HAVE_GETVFSSTAT)
619 num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
620 #elif defined(HAVE_GETFSSTAT)
621 num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
622 #endif
623 if (num_mounts == -1)
624 return NULL;
626 bufsize = num_mounts * sizeof (*mntent);
627 mntent = g_malloc (bufsize);
628 #if defined(HAVE_GETVFSSTAT)
629 num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
630 #elif defined(HAVE_GETFSSTAT)
631 num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
632 #endif
633 if (num_mounts == -1)
634 return NULL;
636 return_list = NULL;
638 for (i = 0; i < num_mounts; i++)
640 mount_entry = g_new0 (GUnixMountEntry, 1);
642 mount_entry->mount_path = g_strdup (mntent[i].f_mntonname);
643 mount_entry->device_path = g_strdup (mntent[i].f_mntfromname);
644 mount_entry->filesystem_type = g_strdup (mntent[i].f_fstypename);
645 #if defined(HAVE_GETVFSSTAT)
646 if (mntent[i].f_flag & ST_RDONLY)
647 #elif defined(HAVE_GETFSSTAT)
648 if (mntent[i].f_flags & MNT_RDONLY)
649 #endif
650 mount_entry->is_read_only = TRUE;
652 mount_entry->is_system_internal =
653 guess_system_internal (mount_entry->mount_path,
654 mount_entry->filesystem_type,
655 mount_entry->device_path);
657 return_list = g_list_prepend (return_list, mount_entry);
660 g_free (mntent);
662 return g_list_reverse (return_list);
664 #elif defined(__INTERIX)
666 static char *
667 get_mtab_monitor_file (void)
669 return NULL;
672 static GList *
673 _g_get_unix_mounts (void)
675 DIR *dirp;
676 GList* return_list = NULL;
677 char filename[9 + NAME_MAX];
679 dirp = opendir ("/dev/fs");
680 if (!dirp)
682 g_warning ("unable to read /dev/fs!");
683 return NULL;
686 while (1)
688 struct statvfs statbuf;
689 struct dirent entry;
690 struct dirent* result;
692 if (readdir_r (dirp, &entry, &result) || result == NULL)
693 break;
695 strcpy (filename, "/dev/fs/");
696 strcat (filename, entry.d_name);
698 if (statvfs (filename, &statbuf) == 0)
700 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
702 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
703 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
704 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
706 if (statbuf.f_flag & ST_RDONLY)
707 mount_entry->is_read_only = TRUE;
709 return_list = g_list_prepend(return_list, mount_entry);
713 return_list = g_list_reverse (return_list);
715 closedir (dirp);
717 return return_list;
719 #else
720 #error No _g_get_unix_mounts() implementation for system
721 #endif
723 /* _g_get_unix_mount_points():
724 * read the fstab.
725 * don't return swap and ignore mounts.
728 static char *
729 get_fstab_file (void)
731 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
732 /* AIX */
733 return "/etc/filesystems";
734 #elif defined(_PATH_MNTTAB)
735 return _PATH_MNTTAB;
736 #elif defined(VFSTAB)
737 return VFSTAB;
738 #else
739 return "/etc/fstab";
740 #endif
743 #ifdef HAVE_MNTENT_H
744 static GList *
745 _g_get_unix_mount_points (void)
747 #ifdef HAVE_GETMNTENT_R
748 struct mntent ent;
749 char buf[1024];
750 #endif
751 struct mntent *mntent;
752 FILE *file;
753 char *read_file;
754 GUnixMountPoint *mount_entry;
755 GList *return_list;
757 read_file = get_fstab_file ();
759 file = setmntent (read_file, "r");
760 if (file == NULL)
761 return NULL;
763 return_list = NULL;
765 #ifdef HAVE_GETMNTENT_R
766 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
767 #else
768 G_LOCK (getmntent);
769 while ((mntent = getmntent (file)) != NULL)
770 #endif
772 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
773 (strcmp (mntent->mnt_dir, "swap") == 0) ||
774 (strcmp (mntent->mnt_dir, "none") == 0))
775 continue;
777 mount_entry = g_new0 (GUnixMountPoint, 1);
778 mount_entry->mount_path = g_strdup (mntent->mnt_dir);
779 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
780 mount_entry->device_path = g_strdup (_resolve_dev_root ());
781 else
782 mount_entry->device_path = g_strdup (mntent->mnt_fsname);
783 mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
784 mount_entry->options = g_strdup (mntent->mnt_opts);
786 #ifdef HAVE_HASMNTOPT
787 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
788 mount_entry->is_read_only = TRUE;
790 if (hasmntopt (mntent, "loop") != NULL)
791 mount_entry->is_loopback = TRUE;
793 #endif
795 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
796 #ifdef HAVE_HASMNTOPT
797 || (hasmntopt (mntent, "user") != NULL
798 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
799 || hasmntopt (mntent, "pamconsole") != NULL
800 || hasmntopt (mntent, "users") != NULL
801 || hasmntopt (mntent, "owner") != NULL
802 #endif
804 mount_entry->is_user_mountable = TRUE;
806 return_list = g_list_prepend (return_list, mount_entry);
809 endmntent (file);
811 #ifndef HAVE_GETMNTENT_R
812 G_UNLOCK (getmntent);
813 #endif
815 return g_list_reverse (return_list);
818 #elif defined (HAVE_SYS_MNTTAB_H)
820 static GList *
821 _g_get_unix_mount_points (void)
823 struct mnttab mntent;
824 FILE *file;
825 char *read_file;
826 GUnixMountPoint *mount_entry;
827 GList *return_list;
829 read_file = get_fstab_file ();
831 file = setmntent (read_file, "r");
832 if (file == NULL)
833 return NULL;
835 return_list = NULL;
837 G_LOCK (getmntent);
838 while (! getmntent (file, &mntent))
840 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
841 (strcmp (mntent.mnt_mountp, "swap") == 0) ||
842 (strcmp (mntent.mnt_mountp, "none") == 0))
843 continue;
845 mount_entry = g_new0 (GUnixMountPoint, 1);
847 mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
848 mount_entry->device_path = g_strdup (mntent.mnt_special);
849 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
850 mount_entry->options = g_strdup (mntent.mnt_mntopts);
852 #ifdef HAVE_HASMNTOPT
853 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
854 mount_entry->is_read_only = TRUE;
856 if (hasmntopt (&mntent, "lofs") != NULL)
857 mount_entry->is_loopback = TRUE;
858 #endif
860 if ((mntent.mnt_fstype != NULL)
861 #ifdef HAVE_HASMNTOPT
862 || (hasmntopt (&mntent, "user") != NULL
863 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
864 || hasmntopt (&mntent, "pamconsole") != NULL
865 || hasmntopt (&mntent, "users") != NULL
866 || hasmntopt (&mntent, "owner") != NULL
867 #endif
869 mount_entry->is_user_mountable = TRUE;
871 return_list = g_list_prepend (return_list, mount_entry);
874 endmntent (file);
875 G_UNLOCK (getmntent);
877 return g_list_reverse (return_list);
879 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
881 /* functions to parse /etc/filesystems on aix */
883 /* read character, ignoring comments (begin with '*', end with '\n' */
884 static int
885 aix_fs_getc (FILE *fd)
887 int c;
889 while ((c = getc (fd)) == '*')
891 while (((c = getc (fd)) != '\n') && (c != EOF))
896 /* eat all continuous spaces in a file */
897 static int
898 aix_fs_ignorespace (FILE *fd)
900 int c;
902 while ((c = aix_fs_getc (fd)) != EOF)
904 if (!g_ascii_isspace (c))
906 ungetc (c,fd);
907 return c;
911 return EOF;
914 /* read one word from file */
915 static int
916 aix_fs_getword (FILE *fd,
917 char *word)
919 int c;
921 aix_fs_ignorespace (fd);
923 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
925 if (c == '"')
927 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
928 *word++ = c;
929 else
930 *word++ = c;
933 *word = 0;
935 return c;
938 typedef struct {
939 char mnt_mount[PATH_MAX];
940 char mnt_special[PATH_MAX];
941 char mnt_fstype[16];
942 char mnt_options[128];
943 } AixMountTableEntry;
945 /* read mount points properties */
946 static int
947 aix_fs_get (FILE *fd,
948 AixMountTableEntry *prop)
950 static char word[PATH_MAX] = { 0 };
951 char value[PATH_MAX];
953 /* read stanza */
954 if (word[0] == 0)
956 if (aix_fs_getword (fd, word) == EOF)
957 return EOF;
960 word[strlen(word) - 1] = 0;
961 strcpy (prop->mnt_mount, word);
963 /* read attributes and value */
965 while (aix_fs_getword (fd, word) != EOF)
967 /* test if is attribute or new stanza */
968 if (word[strlen(word) - 1] == ':')
969 return 0;
971 /* read "=" */
972 aix_fs_getword (fd, value);
974 /* read value */
975 aix_fs_getword (fd, value);
977 if (strcmp (word, "dev") == 0)
978 strcpy (prop->mnt_special, value);
979 else if (strcmp (word, "vfs") == 0)
980 strcpy (prop->mnt_fstype, value);
981 else if (strcmp (word, "options") == 0)
982 strcpy(prop->mnt_options, value);
985 return 0;
988 static GList *
989 _g_get_unix_mount_points (void)
991 struct mntent *mntent;
992 FILE *file;
993 char *read_file;
994 GUnixMountPoint *mount_entry;
995 AixMountTableEntry mntent;
996 GList *return_list;
998 read_file = get_fstab_file ();
1000 file = setmntent (read_file, "r");
1001 if (file == NULL)
1002 return NULL;
1004 return_list = NULL;
1006 while (!aix_fs_get (file, &mntent))
1008 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1010 mount_entry = g_new0 (GUnixMountPoint, 1);
1012 mount_entry->mount_path = g_strdup (mntent.mnt_mount);
1013 mount_entry->device_path = g_strdup (mntent.mnt_special);
1014 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
1015 mount_entry->options = g_strdup (mntent.mnt_options);
1016 mount_entry->is_read_only = TRUE;
1017 mount_entry->is_user_mountable = TRUE;
1019 return_list = g_list_prepend (return_list, mount_entry);
1023 endmntent (file);
1025 return g_list_reverse (return_list);
1028 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1030 static GList *
1031 _g_get_unix_mount_points (void)
1033 struct fstab *fstab = NULL;
1034 GUnixMountPoint *mount_entry;
1035 GList *return_list;
1036 #ifdef HAVE_SYS_SYSCTL_H
1037 int usermnt = 0;
1038 size_t len = sizeof(usermnt);
1039 struct stat sb;
1040 #endif
1042 if (!setfsent ())
1043 return NULL;
1045 return_list = NULL;
1047 #ifdef HAVE_SYS_SYSCTL_H
1048 #if defined(HAVE_SYSCTLBYNAME)
1049 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1050 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1052 int mib[2];
1054 mib[0] = CTL_VFS;
1055 mib[1] = VFS_USERMOUNT;
1056 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1058 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1060 int mib[2];
1062 mib[0] = CTL_KERN;
1063 mib[1] = KERN_USERMOUNT;
1064 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1066 #endif
1067 #endif
1069 while ((fstab = getfsent ()) != NULL)
1071 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1072 continue;
1074 mount_entry = g_new0 (GUnixMountPoint, 1);
1076 mount_entry->mount_path = g_strdup (fstab->fs_file);
1077 mount_entry->device_path = g_strdup (fstab->fs_spec);
1078 mount_entry->filesystem_type = g_strdup (fstab->fs_vfstype);
1079 mount_entry->options = g_strdup (fstab->fs_mntops);
1081 if (strcmp (fstab->fs_type, "ro") == 0)
1082 mount_entry->is_read_only = TRUE;
1084 #ifdef HAVE_SYS_SYSCTL_H
1085 if (usermnt != 0)
1087 uid_t uid = getuid ();
1088 if (stat (fstab->fs_file, &sb) == 0)
1090 if (uid == 0 || sb.st_uid == uid)
1091 mount_entry->is_user_mountable = TRUE;
1094 #endif
1096 return_list = g_list_prepend (return_list, mount_entry);
1099 endfsent ();
1101 return g_list_reverse (return_list);
1103 #elif defined(__INTERIX)
1104 static GList *
1105 _g_get_unix_mount_points (void)
1107 return _g_get_unix_mounts ();
1109 #else
1110 #error No g_get_mount_table() implementation for system
1111 #endif
1113 static guint64
1114 get_mounts_timestamp (void)
1116 const char *monitor_file;
1117 struct stat buf;
1119 monitor_file = get_mtab_monitor_file ();
1120 if (monitor_file)
1122 if (stat (monitor_file, &buf) == 0)
1123 return (guint64)buf.st_mtime;
1125 return 0;
1128 static guint64
1129 get_mount_points_timestamp (void)
1131 const char *monitor_file;
1132 struct stat buf;
1134 monitor_file = get_fstab_file ();
1135 if (monitor_file)
1137 if (stat (monitor_file, &buf) == 0)
1138 return (guint64)buf.st_mtime;
1140 return 0;
1144 * g_unix_mounts_get: (skip)
1145 * @time_read: (out) (allow-none): guint64 to contain a timestamp, or %NULL
1147 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1148 * If @time_read is set, it will be filled with the mount
1149 * timestamp, allowing for checking if the mounts have changed
1150 * with g_unix_mounts_changed_since().
1152 * Returns: (element-type GUnixMountEntry) (transfer full):
1153 * a #GList of the UNIX mounts.
1155 GList *
1156 g_unix_mounts_get (guint64 *time_read)
1158 if (time_read)
1159 *time_read = get_mounts_timestamp ();
1161 return _g_get_unix_mounts ();
1165 * g_unix_mount_at: (skip)
1166 * @mount_path: path for a possible unix mount.
1167 * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1169 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1170 * is set, it will be filled with a unix timestamp for checking
1171 * if the mounts have changed since with g_unix_mounts_changed_since().
1173 * Returns: (transfer full): a #GUnixMountEntry.
1175 GUnixMountEntry *
1176 g_unix_mount_at (const char *mount_path,
1177 guint64 *time_read)
1179 GList *mounts, *l;
1180 GUnixMountEntry *mount_entry, *found;
1182 mounts = g_unix_mounts_get (time_read);
1184 found = NULL;
1185 for (l = mounts; l != NULL; l = l->next)
1187 mount_entry = l->data;
1189 if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1190 found = mount_entry;
1191 else
1192 g_unix_mount_free (mount_entry);
1194 g_list_free (mounts);
1196 return found;
1200 * g_unix_mount_points_get: (skip)
1201 * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1203 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1204 * If @time_read is set, it will be filled with the mount timestamp,
1205 * allowing for checking if the mounts have changed with
1206 * g_unix_mount_points_changed_since().
1208 * Returns: (element-type GUnixMountPoint) (transfer full):
1209 * a #GList of the UNIX mountpoints.
1211 GList *
1212 g_unix_mount_points_get (guint64 *time_read)
1214 if (time_read)
1215 *time_read = get_mount_points_timestamp ();
1217 return _g_get_unix_mount_points ();
1221 * g_unix_mounts_changed_since:
1222 * @time: guint64 to contain a timestamp.
1224 * Checks if the unix mounts have changed since a given unix time.
1226 * Returns: %TRUE if the mounts have changed since @time.
1228 gboolean
1229 g_unix_mounts_changed_since (guint64 time)
1231 return get_mounts_timestamp () != time;
1235 * g_unix_mount_points_changed_since:
1236 * @time: guint64 to contain a timestamp.
1238 * Checks if the unix mount points have changed since a given unix time.
1240 * Returns: %TRUE if the mount points have changed since @time.
1242 gboolean
1243 g_unix_mount_points_changed_since (guint64 time)
1245 return get_mount_points_timestamp () != time;
1248 static void
1249 g_unix_mount_monitor_finalize (GObject *object)
1251 GUnixMountMonitor *monitor;
1253 monitor = G_UNIX_MOUNT_MONITOR (object);
1255 if (monitor->fstab_monitor)
1257 g_file_monitor_cancel (monitor->fstab_monitor);
1258 g_object_unref (monitor->fstab_monitor);
1261 if (monitor->proc_mounts_watch_source != NULL)
1262 g_source_destroy (monitor->proc_mounts_watch_source);
1264 if (monitor->mtab_monitor)
1266 g_file_monitor_cancel (monitor->mtab_monitor);
1267 g_object_unref (monitor->mtab_monitor);
1270 the_mount_monitor = NULL;
1272 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1276 static void
1277 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1279 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1281 gobject_class->finalize = g_unix_mount_monitor_finalize;
1284 * GUnixMountMonitor::mounts-changed:
1285 * @monitor: the object on which the signal is emitted
1287 * Emitted when the unix mounts have changed.
1289 signals[MOUNTS_CHANGED] =
1290 g_signal_new ("mounts-changed",
1291 G_TYPE_FROM_CLASS (klass),
1292 G_SIGNAL_RUN_LAST,
1294 NULL, NULL,
1295 g_cclosure_marshal_VOID__VOID,
1296 G_TYPE_NONE, 0);
1299 * GUnixMountMonitor::mountpoints-changed:
1300 * @monitor: the object on which the signal is emitted
1302 * Emitted when the unix mount points have changed.
1304 signals[MOUNTPOINTS_CHANGED] =
1305 g_signal_new ("mountpoints-changed",
1306 G_TYPE_FROM_CLASS (klass),
1307 G_SIGNAL_RUN_LAST,
1309 NULL, NULL,
1310 g_cclosure_marshal_VOID__VOID,
1311 G_TYPE_NONE, 0);
1314 static void
1315 fstab_file_changed (GFileMonitor *monitor,
1316 GFile *file,
1317 GFile *other_file,
1318 GFileMonitorEvent event_type,
1319 gpointer user_data)
1321 GUnixMountMonitor *mount_monitor;
1323 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1324 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1325 event_type != G_FILE_MONITOR_EVENT_DELETED)
1326 return;
1328 mount_monitor = user_data;
1329 g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
1332 static void
1333 mtab_file_changed (GFileMonitor *monitor,
1334 GFile *file,
1335 GFile *other_file,
1336 GFileMonitorEvent event_type,
1337 gpointer user_data)
1339 GUnixMountMonitor *mount_monitor;
1341 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1342 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1343 event_type != G_FILE_MONITOR_EVENT_DELETED)
1344 return;
1346 mount_monitor = user_data;
1347 g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1350 static gboolean
1351 proc_mounts_changed (GIOChannel *channel,
1352 GIOCondition cond,
1353 gpointer user_data)
1355 GUnixMountMonitor *mount_monitor = G_UNIX_MOUNT_MONITOR (user_data);
1356 if (cond & G_IO_ERR)
1357 g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1358 return TRUE;
1361 static void
1362 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1364 GFile *file;
1366 if (get_fstab_file () != NULL)
1368 file = g_file_new_for_path (get_fstab_file ());
1369 monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1370 g_object_unref (file);
1372 g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
1375 if (get_mtab_monitor_file () != NULL)
1377 const gchar *mtab_path;
1379 mtab_path = get_mtab_monitor_file ();
1380 /* /proc/mounts monitoring is special - can't just use GFileMonitor.
1381 * See 'man proc' for more details.
1383 if (g_strcmp0 (mtab_path, "/proc/mounts") == 0)
1385 GIOChannel *proc_mounts_channel;
1386 GError *error = NULL;
1387 proc_mounts_channel = g_io_channel_new_file ("/proc/mounts", "r", &error);
1388 if (proc_mounts_channel == NULL)
1390 g_warning ("Error creating IO channel for /proc/mounts: %s (%s, %d)",
1391 error->message, g_quark_to_string (error->domain), error->code);
1392 g_error_free (error);
1394 else
1396 monitor->proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1397 g_source_set_callback (monitor->proc_mounts_watch_source,
1398 (GSourceFunc) proc_mounts_changed,
1399 monitor,
1400 NULL);
1401 g_source_attach (monitor->proc_mounts_watch_source,
1402 g_main_context_get_thread_default ());
1403 g_source_unref (monitor->proc_mounts_watch_source);
1404 g_io_channel_unref (proc_mounts_channel);
1407 else
1409 file = g_file_new_for_path (mtab_path);
1410 monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1411 g_object_unref (file);
1412 g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
1418 * g_unix_mount_monitor_set_rate_limit:
1419 * @mount_monitor: a #GUnixMountMonitor
1420 * @limit_msec: a integer with the limit in milliseconds to
1421 * poll for changes.
1423 * Sets the rate limit to which the @mount_monitor will report
1424 * consecutive change events to the mount and mount point entry files.
1426 * Since: 2.18
1428 void
1429 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1430 gint limit_msec)
1432 g_return_if_fail (G_IS_UNIX_MOUNT_MONITOR (mount_monitor));
1434 if (mount_monitor->fstab_monitor != NULL)
1435 g_file_monitor_set_rate_limit (mount_monitor->fstab_monitor, limit_msec);
1437 if (mount_monitor->mtab_monitor != NULL)
1438 g_file_monitor_set_rate_limit (mount_monitor->mtab_monitor, limit_msec);
1442 * g_unix_mount_monitor_new:
1444 * Gets a new #GUnixMountMonitor. The default rate limit for which the
1445 * monitor will report consecutive changes for the mount and mount
1446 * point entry files is the default for a #GFileMonitor. Use
1447 * g_unix_mount_monitor_set_rate_limit() to change this.
1449 * Returns: a #GUnixMountMonitor.
1451 GUnixMountMonitor *
1452 g_unix_mount_monitor_new (void)
1454 if (the_mount_monitor == NULL)
1456 the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
1457 return the_mount_monitor;
1460 return g_object_ref (the_mount_monitor);
1464 * g_unix_mount_free:
1465 * @mount_entry: a #GUnixMount.
1467 * Frees a unix mount.
1469 void
1470 g_unix_mount_free (GUnixMountEntry *mount_entry)
1472 g_return_if_fail (mount_entry != NULL);
1474 g_free (mount_entry->mount_path);
1475 g_free (mount_entry->device_path);
1476 g_free (mount_entry->filesystem_type);
1477 g_free (mount_entry);
1481 * g_unix_mount_point_free:
1482 * @mount_point: unix mount point to free.
1484 * Frees a unix mount point.
1486 void
1487 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1489 g_return_if_fail (mount_point != NULL);
1491 g_free (mount_point->mount_path);
1492 g_free (mount_point->device_path);
1493 g_free (mount_point->filesystem_type);
1494 g_free (mount_point->options);
1495 g_free (mount_point);
1499 * g_unix_mount_compare:
1500 * @mount1: first #GUnixMountEntry to compare.
1501 * @mount2: second #GUnixMountEntry to compare.
1503 * Compares two unix mounts.
1505 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1506 * or less than @mount2, respectively.
1508 gint
1509 g_unix_mount_compare (GUnixMountEntry *mount1,
1510 GUnixMountEntry *mount2)
1512 int res;
1514 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1516 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1517 if (res != 0)
1518 return res;
1520 res = g_strcmp0 (mount1->device_path, mount2->device_path);
1521 if (res != 0)
1522 return res;
1524 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1525 if (res != 0)
1526 return res;
1528 res = mount1->is_read_only - mount2->is_read_only;
1529 if (res != 0)
1530 return res;
1532 return 0;
1536 * g_unix_mount_get_mount_path:
1537 * @mount_entry: input #GUnixMountEntry to get the mount path for.
1539 * Gets the mount path for a unix mount.
1541 * Returns: the mount path for @mount_entry.
1543 const gchar *
1544 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1546 g_return_val_if_fail (mount_entry != NULL, NULL);
1548 return mount_entry->mount_path;
1552 * g_unix_mount_get_device_path:
1553 * @mount_entry: a #GUnixMount.
1555 * Gets the device path for a unix mount.
1557 * Returns: a string containing the device path.
1559 const gchar *
1560 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1562 g_return_val_if_fail (mount_entry != NULL, NULL);
1564 return mount_entry->device_path;
1568 * g_unix_mount_get_fs_type:
1569 * @mount_entry: a #GUnixMount.
1571 * Gets the filesystem type for the unix mount.
1573 * Returns: a string containing the file system type.
1575 const gchar *
1576 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1578 g_return_val_if_fail (mount_entry != NULL, NULL);
1580 return mount_entry->filesystem_type;
1584 * g_unix_mount_is_readonly:
1585 * @mount_entry: a #GUnixMount.
1587 * Checks if a unix mount is mounted read only.
1589 * Returns: %TRUE if @mount_entry is read only.
1591 gboolean
1592 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1594 g_return_val_if_fail (mount_entry != NULL, FALSE);
1596 return mount_entry->is_read_only;
1600 * g_unix_mount_is_system_internal:
1601 * @mount_entry: a #GUnixMount.
1603 * Checks if a unix mount is a system path.
1605 * Returns: %TRUE if the unix mount is for a system path.
1607 gboolean
1608 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1610 g_return_val_if_fail (mount_entry != NULL, FALSE);
1612 return mount_entry->is_system_internal;
1616 * g_unix_mount_point_compare:
1617 * @mount1: a #GUnixMount.
1618 * @mount2: a #GUnixMount.
1620 * Compares two unix mount points.
1622 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1623 * or less than @mount2, respectively.
1625 gint
1626 g_unix_mount_point_compare (GUnixMountPoint *mount1,
1627 GUnixMountPoint *mount2)
1629 int res;
1631 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1633 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1634 if (res != 0)
1635 return res;
1637 res = g_strcmp0 (mount1->device_path, mount2->device_path);
1638 if (res != 0)
1639 return res;
1641 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1642 if (res != 0)
1643 return res;
1645 res = g_strcmp0 (mount1->options, mount2->options);
1646 if (res != 0)
1647 return res;
1649 res = mount1->is_read_only - mount2->is_read_only;
1650 if (res != 0)
1651 return res;
1653 res = mount1->is_user_mountable - mount2->is_user_mountable;
1654 if (res != 0)
1655 return res;
1657 res = mount1->is_loopback - mount2->is_loopback;
1658 if (res != 0)
1659 return res;
1661 return 0;
1665 * g_unix_mount_point_get_mount_path:
1666 * @mount_point: a #GUnixMountPoint.
1668 * Gets the mount path for a unix mount point.
1670 * Returns: a string containing the mount path.
1672 const gchar *
1673 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
1675 g_return_val_if_fail (mount_point != NULL, NULL);
1677 return mount_point->mount_path;
1681 * g_unix_mount_point_get_device_path:
1682 * @mount_point: a #GUnixMountPoint.
1684 * Gets the device path for a unix mount point.
1686 * Returns: a string containing the device path.
1688 const gchar *
1689 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
1691 g_return_val_if_fail (mount_point != NULL, NULL);
1693 return mount_point->device_path;
1697 * g_unix_mount_point_get_fs_type:
1698 * @mount_point: a #GUnixMountPoint.
1700 * Gets the file system type for the mount point.
1702 * Returns: a string containing the file system type.
1704 const gchar *
1705 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
1707 g_return_val_if_fail (mount_point != NULL, NULL);
1709 return mount_point->filesystem_type;
1713 * g_unix_mount_point_get_options:
1714 * @mount_point: a #GUnixMountPoint.
1716 * Gets the options for the mount point.
1718 * Returns: a string containing the options.
1720 * Since: 2.32
1722 const gchar *
1723 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
1725 g_return_val_if_fail (mount_point != NULL, NULL);
1727 return mount_point->options;
1731 * g_unix_mount_point_is_readonly:
1732 * @mount_point: a #GUnixMountPoint.
1734 * Checks if a unix mount point is read only.
1736 * Returns: %TRUE if a mount point is read only.
1738 gboolean
1739 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
1741 g_return_val_if_fail (mount_point != NULL, FALSE);
1743 return mount_point->is_read_only;
1747 * g_unix_mount_point_is_user_mountable:
1748 * @mount_point: a #GUnixMountPoint.
1750 * Checks if a unix mount point is mountable by the user.
1752 * Returns: %TRUE if the mount point is user mountable.
1754 gboolean
1755 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
1757 g_return_val_if_fail (mount_point != NULL, FALSE);
1759 return mount_point->is_user_mountable;
1763 * g_unix_mount_point_is_loopback:
1764 * @mount_point: a #GUnixMountPoint.
1766 * Checks if a unix mount point is a loopback device.
1768 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
1770 gboolean
1771 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
1773 g_return_val_if_fail (mount_point != NULL, FALSE);
1775 return mount_point->is_loopback;
1778 static GUnixMountType
1779 guess_mount_type (const char *mount_path,
1780 const char *device_path,
1781 const char *filesystem_type)
1783 GUnixMountType type;
1784 char *basename;
1786 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
1788 if ((strcmp (filesystem_type, "udf") == 0) ||
1789 (strcmp (filesystem_type, "iso9660") == 0) ||
1790 (strcmp (filesystem_type, "cd9660") == 0))
1791 type = G_UNIX_MOUNT_TYPE_CDROM;
1792 else if ((strcmp (filesystem_type, "nfs") == 0) ||
1793 (strcmp (filesystem_type, "nfs4") == 0))
1794 type = G_UNIX_MOUNT_TYPE_NFS;
1795 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
1796 g_str_has_prefix (device_path, "/dev/fd") ||
1797 g_str_has_prefix (device_path, "/dev/floppy"))
1798 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1799 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
1800 g_str_has_prefix (device_path, "/dev/acd") ||
1801 g_str_has_prefix (device_path, "/dev/cd"))
1802 type = G_UNIX_MOUNT_TYPE_CDROM;
1803 else if (g_str_has_prefix (device_path, "/vol/"))
1805 const char *name = mount_path + strlen ("/");
1807 if (g_str_has_prefix (name, "cdrom"))
1808 type = G_UNIX_MOUNT_TYPE_CDROM;
1809 else if (g_str_has_prefix (name, "floppy") ||
1810 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
1811 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1812 else if (g_str_has_prefix (name, "rmdisk"))
1813 type = G_UNIX_MOUNT_TYPE_ZIP;
1814 else if (g_str_has_prefix (name, "jaz"))
1815 type = G_UNIX_MOUNT_TYPE_JAZ;
1816 else if (g_str_has_prefix (name, "memstick"))
1817 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1819 else
1821 basename = g_path_get_basename (mount_path);
1823 if (g_str_has_prefix (basename, "cdr") ||
1824 g_str_has_prefix (basename, "cdwriter") ||
1825 g_str_has_prefix (basename, "burn") ||
1826 g_str_has_prefix (basename, "dvdr"))
1827 type = G_UNIX_MOUNT_TYPE_CDROM;
1828 else if (g_str_has_prefix (basename, "floppy"))
1829 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1830 else if (g_str_has_prefix (basename, "zip"))
1831 type = G_UNIX_MOUNT_TYPE_ZIP;
1832 else if (g_str_has_prefix (basename, "jaz"))
1833 type = G_UNIX_MOUNT_TYPE_JAZ;
1834 else if (g_str_has_prefix (basename, "camera"))
1835 type = G_UNIX_MOUNT_TYPE_CAMERA;
1836 else if (g_str_has_prefix (basename, "memstick") ||
1837 g_str_has_prefix (basename, "memory_stick") ||
1838 g_str_has_prefix (basename, "ram"))
1839 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1840 else if (g_str_has_prefix (basename, "compact_flash"))
1841 type = G_UNIX_MOUNT_TYPE_CF;
1842 else if (g_str_has_prefix (basename, "smart_media"))
1843 type = G_UNIX_MOUNT_TYPE_SM;
1844 else if (g_str_has_prefix (basename, "sd_mmc"))
1845 type = G_UNIX_MOUNT_TYPE_SDMMC;
1846 else if (g_str_has_prefix (basename, "ipod"))
1847 type = G_UNIX_MOUNT_TYPE_IPOD;
1849 g_free (basename);
1852 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
1853 type = G_UNIX_MOUNT_TYPE_HD;
1855 return type;
1859 * g_unix_mount_guess_type:
1860 * @mount_entry: a #GUnixMount.
1862 * Guesses the type of a unix mount. If the mount type cannot be
1863 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1865 * Returns: a #GUnixMountType.
1867 static GUnixMountType
1868 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
1870 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1871 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1872 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1873 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1875 return guess_mount_type (mount_entry->mount_path,
1876 mount_entry->device_path,
1877 mount_entry->filesystem_type);
1881 * g_unix_mount_point_guess_type:
1882 * @mount_point: a #GUnixMountPoint.
1884 * Guesses the type of a unix mount point.
1885 * If the mount type cannot be determined,
1886 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1888 * Returns: a #GUnixMountType.
1890 static GUnixMountType
1891 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
1893 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1894 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1895 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1896 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1898 return guess_mount_type (mount_point->mount_path,
1899 mount_point->device_path,
1900 mount_point->filesystem_type);
1903 static const char *
1904 type_to_icon (GUnixMountType type, gboolean is_mount_point)
1906 const char *icon_name;
1908 switch (type)
1910 case G_UNIX_MOUNT_TYPE_HD:
1911 if (is_mount_point)
1912 icon_name = "drive-removable-media";
1913 else
1914 icon_name = "drive-harddisk";
1915 break;
1916 case G_UNIX_MOUNT_TYPE_FLOPPY:
1917 case G_UNIX_MOUNT_TYPE_ZIP:
1918 case G_UNIX_MOUNT_TYPE_JAZ:
1919 if (is_mount_point)
1920 icon_name = "drive-removable-media";
1921 else
1922 icon_name = "media-floppy";
1923 break;
1924 case G_UNIX_MOUNT_TYPE_CDROM:
1925 if (is_mount_point)
1926 icon_name = "drive-optical";
1927 else
1928 icon_name = "media-optical";
1929 break;
1930 case G_UNIX_MOUNT_TYPE_NFS:
1931 /* TODO: Would like a better icon here... */
1932 if (is_mount_point)
1933 icon_name = "drive-removable-media";
1934 else
1935 icon_name = "drive-harddisk";
1936 break;
1937 case G_UNIX_MOUNT_TYPE_MEMSTICK:
1938 if (is_mount_point)
1939 icon_name = "drive-removable-media";
1940 else
1941 icon_name = "media-flash";
1942 break;
1943 case G_UNIX_MOUNT_TYPE_CAMERA:
1944 if (is_mount_point)
1945 icon_name = "drive-removable-media";
1946 else
1947 icon_name = "camera-photo";
1948 break;
1949 case G_UNIX_MOUNT_TYPE_IPOD:
1950 if (is_mount_point)
1951 icon_name = "drive-removable-media";
1952 else
1953 icon_name = "multimedia-player";
1954 break;
1955 case G_UNIX_MOUNT_TYPE_UNKNOWN:
1956 default:
1957 if (is_mount_point)
1958 icon_name = "drive-removable-media";
1959 else
1960 icon_name = "drive-harddisk";
1961 break;
1964 return icon_name;
1968 * g_unix_mount_guess_name:
1969 * @mount_entry: a #GUnixMountEntry
1971 * Guesses the name of a Unix mount.
1972 * The result is a translated string.
1974 * Returns: A newly allocated string that must
1975 * be freed with g_free()
1977 gchar *
1978 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
1980 char *name;
1982 if (strcmp (mount_entry->mount_path, "/") == 0)
1983 name = g_strdup (_("Filesystem root"));
1984 else
1985 name = g_filename_display_basename (mount_entry->mount_path);
1987 return name;
1991 * g_unix_mount_guess_icon:
1992 * @mount_entry: a #GUnixMountEntry
1994 * Guesses the icon of a Unix mount.
1996 * Returns: (transfer full): a #GIcon
1998 GIcon *
1999 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2001 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE));
2005 * g_unix_mount_point_guess_name:
2006 * @mount_point: a #GUnixMountPoint
2008 * Guesses the name of a Unix mount point.
2009 * The result is a translated string.
2011 * Returns: A newly allocated string that must
2012 * be freed with g_free()
2014 gchar *
2015 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2017 char *name;
2019 if (strcmp (mount_point->mount_path, "/") == 0)
2020 name = g_strdup (_("Filesystem root"));
2021 else
2022 name = g_filename_display_basename (mount_point->mount_path);
2024 return name;
2028 * g_unix_mount_point_guess_icon:
2029 * @mount_point: a #GUnixMountPoint
2031 * Guesses the icon of a Unix mount point.
2033 * Returns: (transfer full): a #GIcon
2035 GIcon *
2036 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2038 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE));
2042 * g_unix_mount_guess_can_eject:
2043 * @mount_entry: a #GUnixMountEntry
2045 * Guesses whether a Unix mount can be ejected.
2047 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2049 gboolean
2050 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2052 GUnixMountType guessed_type;
2054 guessed_type = g_unix_mount_guess_type (mount_entry);
2055 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2056 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2057 return TRUE;
2059 return FALSE;
2063 * g_unix_mount_guess_should_display:
2064 * @mount_entry: a #GUnixMountEntry
2066 * Guesses whether a Unix mount should be displayed in the UI.
2068 * Returns: %TRUE if @mount_entry is deemed to be displayable.
2070 gboolean
2071 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2073 const char *mount_path;
2075 /* Never display internal mountpoints */
2076 if (g_unix_mount_is_system_internal (mount_entry))
2077 return FALSE;
2079 /* Only display things in /media (which are generally user mountable)
2080 and home dir (fuse stuff) and $XDG_RUNTIME_DIR */
2081 mount_path = mount_entry->mount_path;
2082 if (mount_path != NULL)
2084 gboolean is_in_runtime_dir = FALSE;
2085 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2086 if (g_strstr_len (mount_path, -1, "/.") != NULL)
2087 return FALSE;
2089 if (g_getenv ("XDG_RUNTIME_DIR") != NULL && g_str_has_prefix (mount_path, g_get_user_runtime_dir ()))
2090 is_in_runtime_dir = TRUE;
2092 if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2094 char *path;
2095 /* Avoid displaying mounts that are not accessible to the user.
2097 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2098 * want to avoid g_access() for mount points which can potentially
2099 * block or fail stat()'ing, such as network mounts.
2101 path = g_path_get_dirname (mount_path);
2102 if (g_str_has_prefix (path, "/media/"))
2104 if (g_access (path, R_OK|X_OK) != 0)
2106 g_free (path);
2107 return FALSE;
2110 g_free (path);
2112 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2114 struct stat st;
2115 if (g_stat (mount_entry->device_path, &st) == 0 &&
2116 S_ISBLK(st.st_mode) &&
2117 g_access (mount_path, R_OK|X_OK) != 0)
2118 return FALSE;
2120 return TRUE;
2123 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
2124 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2125 return TRUE;
2128 return FALSE;
2132 * g_unix_mount_point_guess_can_eject:
2133 * @mount_point: a #GUnixMountPoint
2135 * Guesses whether a Unix mount point can be ejected.
2137 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2139 gboolean
2140 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2142 GUnixMountType guessed_type;
2144 guessed_type = g_unix_mount_point_guess_type (mount_point);
2145 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2146 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2147 return TRUE;
2149 return FALSE;
2152 #ifdef HAVE_MNTENT_H
2153 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2154 static void
2155 _canonicalize_filename (gchar *filename)
2157 gchar *p, *q;
2158 gboolean last_was_slash = FALSE;
2160 p = filename;
2161 q = filename;
2163 while (*p)
2165 if (*p == G_DIR_SEPARATOR)
2167 if (!last_was_slash)
2168 *q++ = G_DIR_SEPARATOR;
2170 last_was_slash = TRUE;
2172 else
2174 if (last_was_slash && *p == '.')
2176 if (*(p + 1) == G_DIR_SEPARATOR ||
2177 *(p + 1) == '\0')
2179 if (*(p + 1) == '\0')
2180 break;
2182 p += 1;
2184 else if (*(p + 1) == '.' &&
2185 (*(p + 2) == G_DIR_SEPARATOR ||
2186 *(p + 2) == '\0'))
2188 if (q > filename + 1)
2190 q--;
2191 while (q > filename + 1 &&
2192 *(q - 1) != G_DIR_SEPARATOR)
2193 q--;
2196 if (*(p + 2) == '\0')
2197 break;
2199 p += 2;
2201 else
2203 *q++ = *p;
2204 last_was_slash = FALSE;
2207 else
2209 *q++ = *p;
2210 last_was_slash = FALSE;
2214 p++;
2217 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2218 q--;
2220 *q = '\0';
2223 static char *
2224 _resolve_symlink (const char *file)
2226 GError *error;
2227 char *dir;
2228 char *link;
2229 char *f;
2230 char *f1;
2232 f = g_strdup (file);
2234 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
2236 link = g_file_read_link (f, &error);
2237 if (link == NULL)
2239 g_error_free (error);
2240 g_free (f);
2241 f = NULL;
2242 goto out;
2245 dir = g_path_get_dirname (f);
2246 f1 = g_strdup_printf ("%s/%s", dir, link);
2247 g_free (dir);
2248 g_free (link);
2249 g_free (f);
2250 f = f1;
2253 out:
2254 if (f != NULL)
2255 _canonicalize_filename (f);
2256 return f;
2259 static const char *
2260 _resolve_dev_root (void)
2262 static gboolean have_real_dev_root = FALSE;
2263 static char real_dev_root[256];
2264 struct stat statbuf;
2266 /* see if it's cached already */
2267 if (have_real_dev_root)
2268 goto found;
2270 /* otherwise we're going to find it right away.. */
2271 have_real_dev_root = TRUE;
2273 if (stat ("/dev/root", &statbuf) == 0)
2275 if (! S_ISLNK (statbuf.st_mode))
2277 dev_t root_dev = statbuf.st_dev;
2278 FILE *f;
2279 char buf[1024];
2281 /* see if device with similar major:minor as /dev/root is mention
2282 * in /etc/mtab (it usually is)
2284 f = fopen ("/etc/mtab", "r");
2285 if (f != NULL)
2287 struct mntent *entp;
2288 #ifdef HAVE_GETMNTENT_R
2289 struct mntent ent;
2290 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
2292 #else
2293 G_LOCK (getmntent);
2294 while ((entp = getmntent (f)) != NULL)
2296 #endif
2297 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2298 statbuf.st_dev == root_dev)
2300 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2301 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2302 fclose (f);
2303 goto found;
2307 endmntent (f);
2309 #ifndef HAVE_GETMNTENT_R
2310 G_UNLOCK (getmntent);
2311 #endif
2314 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2317 else
2319 char *resolved;
2320 resolved = _resolve_symlink ("/dev/root");
2321 if (resolved != NULL)
2323 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2324 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2325 g_free (resolved);
2326 goto found;
2331 /* bah sucks.. */
2332 strcpy (real_dev_root, "/dev/root");
2334 found:
2335 return real_dev_root;
2337 #endif