gmain: Fall back to pipes if kernel doesn't support EFD_CLOEXEC for eventfd()
[glib.git] / gio / gunixmounts.c
blob08eb2a0191f0ae911150b8a7648ec8412a0dc4ef
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 #if HAVE_SYS_STATVFS_H
42 #include <sys/statvfs.h>
43 #endif
44 #include <stdio.h>
45 #include <unistd.h>
46 #include <sys/time.h>
47 #include <errno.h>
48 #include <string.h>
49 #include <signal.h>
50 #include <gstdio.h>
51 #include <dirent.h>
53 #include "gunixmounts.h"
54 #include "gfile.h"
55 #include "gfilemonitor.h"
56 #include "glibintl.h"
57 #include "gthemedicon.h"
60 static const char *_resolve_dev_root (void);
62 /**
63 * SECTION:gunixmounts
64 * @include: gio/gunixmounts.h
65 * @short_description: UNIX mounts
67 * Routines for managing mounted UNIX mount points and paths.
69 * Note that <filename>&lt;gio/gunixmounts.h&gt;</filename> belongs to the
70 * UNIX-specific GIO interfaces, thus you have to use the
71 * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
75 * GUnixMountType:
76 * @G_UNIX_MOUNT_TYPE_UNKNOWN: Unknown UNIX mount type.
77 * @G_UNIX_MOUNT_TYPE_FLOPPY: Floppy disk UNIX mount type.
78 * @G_UNIX_MOUNT_TYPE_CDROM: CDROM UNIX mount type.
79 * @G_UNIX_MOUNT_TYPE_NFS: Network File System (NFS) UNIX mount type.
80 * @G_UNIX_MOUNT_TYPE_ZIP: ZIP UNIX mount type.
81 * @G_UNIX_MOUNT_TYPE_JAZ: JAZZ UNIX mount type.
82 * @G_UNIX_MOUNT_TYPE_MEMSTICK: Memory Stick UNIX mount type.
83 * @G_UNIX_MOUNT_TYPE_CF: Compact Flash UNIX mount type.
84 * @G_UNIX_MOUNT_TYPE_SM: Smart Media UNIX mount type.
85 * @G_UNIX_MOUNT_TYPE_SDMMC: SD/MMC UNIX mount type.
86 * @G_UNIX_MOUNT_TYPE_IPOD: iPod UNIX mount type.
87 * @G_UNIX_MOUNT_TYPE_CAMERA: Digital camera UNIX mount type.
88 * @G_UNIX_MOUNT_TYPE_HD: Hard drive UNIX mount type.
90 * Types of UNIX mounts.
91 **/
92 typedef enum {
93 G_UNIX_MOUNT_TYPE_UNKNOWN,
94 G_UNIX_MOUNT_TYPE_FLOPPY,
95 G_UNIX_MOUNT_TYPE_CDROM,
96 G_UNIX_MOUNT_TYPE_NFS,
97 G_UNIX_MOUNT_TYPE_ZIP,
98 G_UNIX_MOUNT_TYPE_JAZ,
99 G_UNIX_MOUNT_TYPE_MEMSTICK,
100 G_UNIX_MOUNT_TYPE_CF,
101 G_UNIX_MOUNT_TYPE_SM,
102 G_UNIX_MOUNT_TYPE_SDMMC,
103 G_UNIX_MOUNT_TYPE_IPOD,
104 G_UNIX_MOUNT_TYPE_CAMERA,
105 G_UNIX_MOUNT_TYPE_HD
106 } GUnixMountType;
108 struct _GUnixMountEntry {
109 char *mount_path;
110 char *device_path;
111 char *filesystem_type;
112 gboolean is_read_only;
113 gboolean is_system_internal;
116 struct _GUnixMountPoint {
117 char *mount_path;
118 char *device_path;
119 char *filesystem_type;
120 gboolean is_read_only;
121 gboolean is_user_mountable;
122 gboolean is_loopback;
125 enum {
126 MOUNTS_CHANGED,
127 MOUNTPOINTS_CHANGED,
128 LAST_SIGNAL
131 static guint signals[LAST_SIGNAL];
133 struct _GUnixMountMonitor {
134 GObject parent;
136 GFileMonitor *fstab_monitor;
137 GFileMonitor *mtab_monitor;
140 struct _GUnixMountMonitorClass {
141 GObjectClass parent_class;
144 static GUnixMountMonitor *the_mount_monitor = NULL;
146 static GList *_g_get_unix_mounts (void);
147 static GList *_g_get_unix_mount_points (void);
149 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
151 #define MOUNT_POLL_INTERVAL 4000
153 #ifdef HAVE_SYS_MNTTAB_H
154 #define MNTOPT_RO "ro"
155 #endif
157 #ifdef HAVE_MNTENT_H
158 #include <mntent.h>
159 #elif defined (HAVE_SYS_MNTTAB_H)
160 #include <sys/mnttab.h>
161 #endif
163 #ifdef HAVE_SYS_VFSTAB_H
164 #include <sys/vfstab.h>
165 #endif
167 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
168 #include <sys/mntctl.h>
169 #include <sys/vfs.h>
170 #include <sys/vmount.h>
171 #include <fshelp.h>
172 #endif
174 #if defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
175 #include <sys/ucred.h>
176 #include <sys/mount.h>
177 #include <fstab.h>
178 #ifdef HAVE_SYS_SYSCTL_H
179 #include <sys/sysctl.h>
180 #endif
181 #endif
183 #ifndef HAVE_SETMNTENT
184 #define setmntent(f,m) fopen(f,m)
185 #endif
186 #ifndef HAVE_ENDMNTENT
187 #define endmntent(f) fclose(f)
188 #endif
190 static gboolean
191 is_in (const char *value, const char *set[])
193 int i;
194 for (i = 0; set[i] != NULL; i++)
196 if (strcmp (set[i], value) == 0)
197 return TRUE;
199 return FALSE;
203 * g_unix_is_mount_path_system_internal:
204 * @mount_path: a mount path, e.g. <filename>/media/disk</filename>
205 * or <filename>/usr</filename>
207 * Determines if @mount_path is considered an implementation of the
208 * OS. This is primarily used for hiding mountable and mounted volumes
209 * that only are used in the OS and has little to no relevance to the
210 * casual user.
212 * Returns: %TRUE if @mount_path is considered an implementation detail
213 * of the OS.
215 gboolean
216 g_unix_is_mount_path_system_internal (const char *mount_path)
218 const char *ignore_mountpoints[] = {
219 /* Includes all FHS 2.3 toplevel dirs and other specilized
220 * directories that we want to hide from the user.
222 "/", /* we already have "Filesystem root" in Nautilus */
223 "/bin",
224 "/boot",
225 "/dev",
226 "/etc",
227 "/home",
228 "/lib",
229 "/lib64",
230 "/media",
231 "/mnt",
232 "/opt",
233 "/root",
234 "/sbin",
235 "/srv",
236 "/tmp",
237 "/usr",
238 "/usr/local",
239 "/var",
240 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
241 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
242 "/proc",
243 "/sbin",
244 "/net",
245 "/sys",
246 NULL
249 if (is_in (mount_path, ignore_mountpoints))
250 return TRUE;
252 if (g_str_has_prefix (mount_path, "/dev/") ||
253 g_str_has_prefix (mount_path, "/proc/") ||
254 g_str_has_prefix (mount_path, "/sys/"))
255 return TRUE;
257 if (g_str_has_suffix (mount_path, "/.gvfs"))
258 return TRUE;
260 return FALSE;
263 static gboolean
264 guess_system_internal (const char *mountpoint,
265 const char *fs,
266 const char *device)
268 const char *ignore_fs[] = {
269 "auto",
270 "autofs",
271 "devfs",
272 "devpts",
273 "ecryptfs",
274 "kernfs",
275 "linprocfs",
276 "proc",
277 "procfs",
278 "ptyfs",
279 "rootfs",
280 "selinuxfs",
281 "sysfs",
282 "tmpfs",
283 "usbfs",
284 "nfsd",
285 "rpc_pipefs",
286 "zfs",
287 NULL
289 const char *ignore_devices[] = {
290 "none",
291 "sunrpc",
292 "devpts",
293 "nfsd",
294 "/dev/loop",
295 "/dev/vn",
296 NULL
299 if (is_in (fs, ignore_fs))
300 return TRUE;
302 if (is_in (device, ignore_devices))
303 return TRUE;
305 if (g_unix_is_mount_path_system_internal (mountpoint))
306 return TRUE;
308 return FALSE;
311 #ifdef HAVE_MNTENT_H
313 static char *
314 get_mtab_read_file (void)
316 #ifdef _PATH_MOUNTED
317 # ifdef __linux__
318 return "/proc/mounts";
319 # else
320 return _PATH_MOUNTED;
321 # endif
322 #else
323 return "/etc/mtab";
324 #endif
327 static char *
328 get_mtab_monitor_file (void)
330 #ifdef _PATH_MOUNTED
331 return _PATH_MOUNTED;
332 #else
333 return "/etc/mtab";
334 #endif
337 #ifndef HAVE_GETMNTENT_R
338 G_LOCK_DEFINE_STATIC(getmntent);
339 #endif
341 static GList *
342 _g_get_unix_mounts (void)
344 #ifdef HAVE_GETMNTENT_R
345 struct mntent ent;
346 char buf[1024];
347 #endif
348 struct mntent *mntent;
349 FILE *file;
350 char *read_file;
351 GUnixMountEntry *mount_entry;
352 GHashTable *mounts_hash;
353 GList *return_list;
355 read_file = get_mtab_read_file ();
357 file = setmntent (read_file, "r");
358 if (file == NULL)
359 return NULL;
361 return_list = NULL;
363 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
365 #ifdef HAVE_GETMNTENT_R
366 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
367 #else
368 G_LOCK (getmntent);
369 while ((mntent = getmntent (file)) != NULL)
370 #endif
372 /* ignore any mnt_fsname that is repeated and begins with a '/'
374 * We do this to avoid being fooled by --bind mounts, since
375 * these have the same device as the location they bind to.
376 * It's not an ideal solution to the problem, but it's likely that
377 * the most important mountpoint is first and the --bind ones after
378 * that aren't as important. So it should work.
380 * The '/' is to handle procfs, tmpfs and other no device mounts.
382 if (mntent->mnt_fsname != NULL &&
383 mntent->mnt_fsname[0] == '/' &&
384 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
385 continue;
387 mount_entry = g_new0 (GUnixMountEntry, 1);
388 mount_entry->mount_path = g_strdup (mntent->mnt_dir);
389 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
390 mount_entry->device_path = g_strdup (_resolve_dev_root ());
391 else
392 mount_entry->device_path = g_strdup (mntent->mnt_fsname);
393 mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
395 #if defined (HAVE_HASMNTOPT)
396 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
397 mount_entry->is_read_only = TRUE;
398 #endif
400 mount_entry->is_system_internal =
401 guess_system_internal (mount_entry->mount_path,
402 mount_entry->filesystem_type,
403 mount_entry->device_path);
405 g_hash_table_insert (mounts_hash,
406 mount_entry->device_path,
407 mount_entry->device_path);
409 return_list = g_list_prepend (return_list, mount_entry);
411 g_hash_table_destroy (mounts_hash);
413 endmntent (file);
415 #ifndef HAVE_GETMNTENT_R
416 G_UNLOCK (getmntent);
417 #endif
419 return g_list_reverse (return_list);
422 #elif defined (HAVE_SYS_MNTTAB_H)
424 G_LOCK_DEFINE_STATIC(getmntent);
426 static char *
427 get_mtab_read_file (void)
429 #ifdef _PATH_MOUNTED
430 return _PATH_MOUNTED;
431 #else
432 return "/etc/mnttab";
433 #endif
436 static char *
437 get_mtab_monitor_file (void)
439 return get_mtab_read_file ();
442 static GList *
443 _g_get_unix_mounts (void)
445 struct mnttab mntent;
446 FILE *file;
447 char *read_file;
448 GUnixMountEntry *mount_entry;
449 GList *return_list;
451 read_file = get_mtab_read_file ();
453 file = setmntent (read_file, "r");
454 if (file == NULL)
455 return NULL;
457 return_list = NULL;
459 G_LOCK (getmntent);
460 while (! getmntent (file, &mntent))
462 mount_entry = g_new0 (GUnixMountEntry, 1);
464 mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
465 mount_entry->device_path = g_strdup (mntent.mnt_special);
466 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
468 #if defined (HAVE_HASMNTOPT)
469 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
470 mount_entry->is_read_only = TRUE;
471 #endif
473 mount_entry->is_system_internal =
474 guess_system_internal (mount_entry->mount_path,
475 mount_entry->filesystem_type,
476 mount_entry->device_path);
478 return_list = g_list_prepend (return_list, mount_entry);
481 endmntent (file);
483 G_UNLOCK (getmntent);
485 return g_list_reverse (return_list);
488 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
490 static char *
491 get_mtab_monitor_file (void)
493 return NULL;
496 static GList *
497 _g_get_unix_mounts (void)
499 struct vfs_ent *fs_info;
500 struct vmount *vmount_info;
501 int vmount_number;
502 unsigned int vmount_size;
503 int current;
504 GList *return_list;
506 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
508 g_warning ("Unable to know the number of mounted volumes\n");
510 return NULL;
513 vmount_info = (struct vmount*)g_malloc (vmount_size);
515 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
517 if (vmount_info->vmt_revision != VMT_REVISION)
518 g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
520 if (vmount_number < 0)
522 g_warning ("Unable to recover mounted volumes information\n");
524 g_free (vmount_info);
525 return NULL;
528 return_list = NULL;
529 while (vmount_number > 0)
531 mount_entry = g_new0 (GUnixMountEntry, 1);
533 mount_entry->device_path = g_strdup (vmt2dataptr (vmount_info, VMT_OBJECT));
534 mount_entry->mount_path = g_strdup (vmt2dataptr (vmount_info, VMT_STUB));
535 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
536 mount_entry->is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
538 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
540 if (fs_info == NULL)
541 mount_entry->filesystem_type = g_strdup ("unknown");
542 else
543 mount_entry->filesystem_type = g_strdup (fs_info->vfsent_name);
545 mount_entry->is_system_internal =
546 guess_system_internal (mount_entry->mount_path,
547 mount_entry->filesystem_type,
548 mount_entry->device_path);
550 return_list = g_list_prepend (return_list, mount_entry);
552 vmount_info = (struct vmount *)( (char*)vmount_info
553 + vmount_info->vmt_length);
554 vmount_number--;
557 g_free (vmount_info);
559 return g_list_reverse (return_list);
562 #elif defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
564 static char *
565 get_mtab_monitor_file (void)
567 return NULL;
570 static GList *
571 _g_get_unix_mounts (void)
573 struct statfs *mntent = NULL;
574 int num_mounts, i;
575 GUnixMountEntry *mount_entry;
576 GList *return_list;
578 /* Pass MNT_NOWAIT to avoid blocking trying to update NFS mounts. */
579 if ((num_mounts = getmntinfo (&mntent, MNT_NOWAIT)) == 0)
580 return NULL;
582 return_list = NULL;
584 for (i = 0; i < num_mounts; i++)
586 mount_entry = g_new0 (GUnixMountEntry, 1);
588 mount_entry->mount_path = g_strdup (mntent[i].f_mntonname);
589 mount_entry->device_path = g_strdup (mntent[i].f_mntfromname);
590 mount_entry->filesystem_type = g_strdup (mntent[i].f_fstypename);
591 if (mntent[i].f_flags & MNT_RDONLY)
592 mount_entry->is_read_only = TRUE;
594 mount_entry->is_system_internal =
595 guess_system_internal (mount_entry->mount_path,
596 mount_entry->filesystem_type,
597 mount_entry->device_path);
599 return_list = g_list_prepend (return_list, mount_entry);
602 return g_list_reverse (return_list);
604 #elif defined(__INTERIX)
606 static char *
607 get_mtab_monitor_file (void)
609 return NULL;
612 static GList *
613 _g_get_unix_mounts (void)
615 DIR *dirp;
616 GList* return_list = NULL;
617 char filename[9 + NAME_MAX];
619 dirp = opendir ("/dev/fs");
620 if (!dirp)
622 g_warning ("unable to read /dev/fs!");
623 return NULL;
626 while (1)
628 struct statvfs statbuf;
629 struct dirent entry;
630 struct dirent* result;
632 if (readdir_r (dirp, &entry, &result) || result == NULL)
633 break;
635 strcpy (filename, "/dev/fs/");
636 strcat (filename, entry.d_name);
638 if (statvfs (filename, &statbuf) == 0)
640 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
642 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
643 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
644 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
646 if (statbuf.f_flag & ST_RDONLY)
647 mount_entry->is_read_only = TRUE;
649 return_list = g_list_prepend(return_list, mount_entry);
653 return_list = g_list_reverse (return_list);
655 closedir (dirp);
657 return return_list;
659 #else
660 #error No _g_get_unix_mounts() implementation for system
661 #endif
663 /* _g_get_unix_mount_points():
664 * read the fstab.
665 * don't return swap and ignore mounts.
668 static char *
669 get_fstab_file (void)
671 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
672 /* AIX */
673 return "/etc/filesystems";
674 #elif defined(_PATH_MNTTAB)
675 return _PATH_MNTTAB;
676 #elif defined(VFSTAB)
677 return VFSTAB;
678 #else
679 return "/etc/fstab";
680 #endif
683 #ifdef HAVE_MNTENT_H
684 static GList *
685 _g_get_unix_mount_points (void)
687 #ifdef HAVE_GETMNTENT_R
688 struct mntent ent;
689 char buf[1024];
690 #endif
691 struct mntent *mntent;
692 FILE *file;
693 char *read_file;
694 GUnixMountPoint *mount_entry;
695 GList *return_list;
697 read_file = get_fstab_file ();
699 file = setmntent (read_file, "r");
700 if (file == NULL)
701 return NULL;
703 return_list = NULL;
705 #ifdef HAVE_GETMNTENT_R
706 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
707 #else
708 G_LOCK (getmntent);
709 while ((mntent = getmntent (file)) != NULL)
710 #endif
712 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
713 (strcmp (mntent->mnt_dir, "swap") == 0))
714 continue;
716 mount_entry = g_new0 (GUnixMountPoint, 1);
717 mount_entry->mount_path = g_strdup (mntent->mnt_dir);
718 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
719 mount_entry->device_path = g_strdup (_resolve_dev_root ());
720 else
721 mount_entry->device_path = g_strdup (mntent->mnt_fsname);
722 mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
724 #ifdef HAVE_HASMNTOPT
725 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
726 mount_entry->is_read_only = TRUE;
728 if (hasmntopt (mntent, "loop") != NULL)
729 mount_entry->is_loopback = TRUE;
731 #endif
733 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
734 #ifdef HAVE_HASMNTOPT
735 || (hasmntopt (mntent, "user") != NULL
736 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
737 || hasmntopt (mntent, "pamconsole") != NULL
738 || hasmntopt (mntent, "users") != NULL
739 || hasmntopt (mntent, "owner") != NULL
740 #endif
742 mount_entry->is_user_mountable = TRUE;
744 return_list = g_list_prepend (return_list, mount_entry);
747 endmntent (file);
749 #ifndef HAVE_GETMNTENT_R
750 G_UNLOCK (getmntent);
751 #endif
753 return g_list_reverse (return_list);
756 #elif defined (HAVE_SYS_MNTTAB_H)
758 static GList *
759 _g_get_unix_mount_points (void)
761 struct mnttab mntent;
762 FILE *file;
763 char *read_file;
764 GUnixMountPoint *mount_entry;
765 GList *return_list;
767 read_file = get_fstab_file ();
769 file = setmntent (read_file, "r");
770 if (file == NULL)
771 return NULL;
773 return_list = NULL;
775 G_LOCK (getmntent);
776 while (! getmntent (file, &mntent))
778 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
779 (strcmp (mntent.mnt_mountp, "swap") == 0))
780 continue;
782 mount_entry = g_new0 (GUnixMountPoint, 1);
784 mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
785 mount_entry->device_path = g_strdup (mntent.mnt_special);
786 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
788 #ifdef HAVE_HASMNTOPT
789 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
790 mount_entry->is_read_only = TRUE;
792 if (hasmntopt (&mntent, "lofs") != NULL)
793 mount_entry->is_loopback = TRUE;
794 #endif
796 if ((mntent.mnt_fstype != NULL)
797 #ifdef HAVE_HASMNTOPT
798 || (hasmntopt (&mntent, "user") != NULL
799 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
800 || hasmntopt (&mntent, "pamconsole") != NULL
801 || hasmntopt (&mntent, "users") != NULL
802 || hasmntopt (&mntent, "owner") != NULL
803 #endif
805 mount_entry->is_user_mountable = TRUE;
807 return_list = g_list_prepend (return_list, mount_entry);
810 endmntent (file);
811 G_UNLOCK (getmntent);
813 return g_list_reverse (return_list);
815 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
817 /* functions to parse /etc/filesystems on aix */
819 /* read character, ignoring comments (begin with '*', end with '\n' */
820 static int
821 aix_fs_getc (FILE *fd)
823 int c;
825 while ((c = getc (fd)) == '*')
827 while (((c = getc (fd)) != '\n') && (c != EOF))
832 /* eat all continuous spaces in a file */
833 static int
834 aix_fs_ignorespace (FILE *fd)
836 int c;
838 while ((c = aix_fs_getc (fd)) != EOF)
840 if (!g_ascii_isspace (c))
842 ungetc (c,fd);
843 return c;
847 return EOF;
850 /* read one word from file */
851 static int
852 aix_fs_getword (FILE *fd,
853 char *word)
855 int c;
857 aix_fs_ignorespace (fd);
859 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
861 if (c == '"')
863 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
864 *word++ = c;
865 else
866 *word++ = c;
869 *word = 0;
871 return c;
874 typedef struct {
875 char mnt_mount[PATH_MAX];
876 char mnt_special[PATH_MAX];
877 char mnt_fstype[16];
878 char mnt_options[128];
879 } AixMountTableEntry;
881 /* read mount points properties */
882 static int
883 aix_fs_get (FILE *fd,
884 AixMountTableEntry *prop)
886 static char word[PATH_MAX] = { 0 };
887 char value[PATH_MAX];
889 /* read stanza */
890 if (word[0] == 0)
892 if (aix_fs_getword (fd, word) == EOF)
893 return EOF;
896 word[strlen(word) - 1] = 0;
897 strcpy (prop->mnt_mount, word);
899 /* read attributes and value */
901 while (aix_fs_getword (fd, word) != EOF)
903 /* test if is attribute or new stanza */
904 if (word[strlen(word) - 1] == ':')
905 return 0;
907 /* read "=" */
908 aix_fs_getword (fd, value);
910 /* read value */
911 aix_fs_getword (fd, value);
913 if (strcmp (word, "dev") == 0)
914 strcpy (prop->mnt_special, value);
915 else if (strcmp (word, "vfs") == 0)
916 strcpy (prop->mnt_fstype, value);
917 else if (strcmp (word, "options") == 0)
918 strcpy(prop->mnt_options, value);
921 return 0;
924 static GList *
925 _g_get_unix_mount_points (void)
927 struct mntent *mntent;
928 FILE *file;
929 char *read_file;
930 GUnixMountPoint *mount_entry;
931 AixMountTableEntry mntent;
932 GList *return_list;
934 read_file = get_fstab_file ();
936 file = setmntent (read_file, "r");
937 if (file == NULL)
938 return NULL;
940 return_list = NULL;
942 while (!aix_fs_get (file, &mntent))
944 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
946 mount_entry = g_new0 (GUnixMountPoint, 1);
948 mount_entry->mount_path = g_strdup (mntent.mnt_mount);
949 mount_entry->device_path = g_strdup (mntent.mnt_special);
950 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
951 mount_entry->is_read_only = TRUE;
952 mount_entry->is_user_mountable = TRUE;
954 return_list = g_list_prepend (return_list, mount_entry);
958 endmntent (file);
960 return g_list_reverse (return_list);
963 #elif defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
965 static GList *
966 _g_get_unix_mount_points (void)
968 struct fstab *fstab = NULL;
969 GUnixMountPoint *mount_entry;
970 GList *return_list;
971 #ifdef HAVE_SYS_SYSCTL_H
972 int usermnt = 0;
973 size_t len = sizeof(usermnt);
974 struct stat sb;
975 #endif
977 if (!setfsent ())
978 return NULL;
980 return_list = NULL;
982 #ifdef HAVE_SYS_SYSCTL_H
983 #if defined(HAVE_SYSCTLBYNAME)
984 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
985 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
987 int mib[2];
989 mib[0] = CTL_VFS;
990 mib[1] = VFS_USERMOUNT;
991 sysctl (mib, 2, &usermnt, &len, NULL, 0);
993 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
995 int mib[2];
997 mib[0] = CTL_KERN;
998 mib[1] = KERN_USERMOUNT;
999 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1001 #endif
1002 #endif
1004 while ((fstab = getfsent ()) != NULL)
1006 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1007 continue;
1009 mount_entry = g_new0 (GUnixMountPoint, 1);
1011 mount_entry->mount_path = g_strdup (fstab->fs_file);
1012 mount_entry->device_path = g_strdup (fstab->fs_spec);
1013 mount_entry->filesystem_type = g_strdup (fstab->fs_vfstype);
1015 if (strcmp (fstab->fs_type, "ro") == 0)
1016 mount_entry->is_read_only = TRUE;
1018 #ifdef HAVE_SYS_SYSCTL_H
1019 if (usermnt != 0)
1021 uid_t uid = getuid ();
1022 if (stat (fstab->fs_file, &sb) == 0)
1024 if (uid == 0 || sb.st_uid == uid)
1025 mount_entry->is_user_mountable = TRUE;
1028 #endif
1030 return_list = g_list_prepend (return_list, mount_entry);
1033 endfsent ();
1035 return g_list_reverse (return_list);
1037 #elif defined(__INTERIX)
1038 static GList *
1039 _g_get_unix_mount_points (void)
1041 return _g_get_unix_mounts ();
1043 #else
1044 #error No g_get_mount_table() implementation for system
1045 #endif
1047 static guint64
1048 get_mounts_timestamp (void)
1050 const char *monitor_file;
1051 struct stat buf;
1053 monitor_file = get_mtab_monitor_file ();
1054 if (monitor_file)
1056 if (stat (monitor_file, &buf) == 0)
1057 return (guint64)buf.st_mtime;
1059 return 0;
1062 static guint64
1063 get_mount_points_timestamp (void)
1065 const char *monitor_file;
1066 struct stat buf;
1068 monitor_file = get_fstab_file ();
1069 if (monitor_file)
1071 if (stat (monitor_file, &buf) == 0)
1072 return (guint64)buf.st_mtime;
1074 return 0;
1078 * g_unix_mounts_get: (skip)
1079 * @time_read: (out) (allow-none): guint64 to contain a timestamp, or %NULL
1081 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1082 * If @time_read is set, it will be filled with the mount
1083 * timestamp, allowing for checking if the mounts have changed
1084 * with g_unix_mounts_changed_since().
1086 * Returns: (element-type GUnixMountEntry) (transfer full):
1087 * a #GList of the UNIX mounts.
1089 GList *
1090 g_unix_mounts_get (guint64 *time_read)
1092 if (time_read)
1093 *time_read = get_mounts_timestamp ();
1095 return _g_get_unix_mounts ();
1099 * g_unix_mount_at: (skip)
1100 * @mount_path: path for a possible unix mount.
1101 * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1103 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1104 * is set, it will be filled with a unix timestamp for checking
1105 * if the mounts have changed since with g_unix_mounts_changed_since().
1107 * Returns: (transfer full): a #GUnixMountEntry.
1109 GUnixMountEntry *
1110 g_unix_mount_at (const char *mount_path,
1111 guint64 *time_read)
1113 GList *mounts, *l;
1114 GUnixMountEntry *mount_entry, *found;
1116 mounts = g_unix_mounts_get (time_read);
1118 found = NULL;
1119 for (l = mounts; l != NULL; l = l->next)
1121 mount_entry = l->data;
1123 if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1124 found = mount_entry;
1125 else
1126 g_unix_mount_free (mount_entry);
1128 g_list_free (mounts);
1130 return found;
1134 * g_unix_mount_points_get: (skip)
1135 * @time_read: (out) (allow-none): guint64 to contain a timestamp.
1137 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1138 * If @time_read is set, it will be filled with the mount timestamp,
1139 * allowing for checking if the mounts have changed with
1140 * g_unix_mount_points_changed_since().
1142 * Returns: (element-type GUnixMountPoint) (transfer full):
1143 * a #GList of the UNIX mountpoints.
1145 GList *
1146 g_unix_mount_points_get (guint64 *time_read)
1148 if (time_read)
1149 *time_read = get_mount_points_timestamp ();
1151 return _g_get_unix_mount_points ();
1155 * g_unix_mounts_changed_since:
1156 * @time: guint64 to contain a timestamp.
1158 * Checks if the unix mounts have changed since a given unix time.
1160 * Returns: %TRUE if the mounts have changed since @time.
1162 gboolean
1163 g_unix_mounts_changed_since (guint64 time)
1165 return get_mounts_timestamp () != time;
1169 * g_unix_mount_points_changed_since:
1170 * @time: guint64 to contain a timestamp.
1172 * Checks if the unix mount points have changed since a given unix time.
1174 * Returns: %TRUE if the mount points have changed since @time.
1176 gboolean
1177 g_unix_mount_points_changed_since (guint64 time)
1179 return get_mount_points_timestamp () != time;
1182 static void
1183 g_unix_mount_monitor_finalize (GObject *object)
1185 GUnixMountMonitor *monitor;
1187 monitor = G_UNIX_MOUNT_MONITOR (object);
1189 if (monitor->fstab_monitor)
1191 g_file_monitor_cancel (monitor->fstab_monitor);
1192 g_object_unref (monitor->fstab_monitor);
1195 if (monitor->mtab_monitor)
1197 g_file_monitor_cancel (monitor->mtab_monitor);
1198 g_object_unref (monitor->mtab_monitor);
1201 the_mount_monitor = NULL;
1203 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1207 static void
1208 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1210 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1212 gobject_class->finalize = g_unix_mount_monitor_finalize;
1215 * GUnixMountMonitor::mounts-changed:
1216 * @monitor: the object on which the signal is emitted
1218 * Emitted when the unix mounts have changed.
1220 signals[MOUNTS_CHANGED] =
1221 g_signal_new ("mounts-changed",
1222 G_TYPE_FROM_CLASS (klass),
1223 G_SIGNAL_RUN_LAST,
1225 NULL, NULL,
1226 g_cclosure_marshal_VOID__VOID,
1227 G_TYPE_NONE, 0);
1230 * GUnixMountMonitor::mountpoints-changed:
1231 * @monitor: the object on which the signal is emitted
1233 * Emitted when the unix mount points have changed.
1235 signals[MOUNTPOINTS_CHANGED] =
1236 g_signal_new ("mountpoints-changed",
1237 G_TYPE_FROM_CLASS (klass),
1238 G_SIGNAL_RUN_LAST,
1240 NULL, NULL,
1241 g_cclosure_marshal_VOID__VOID,
1242 G_TYPE_NONE, 0);
1245 static void
1246 fstab_file_changed (GFileMonitor *monitor,
1247 GFile *file,
1248 GFile *other_file,
1249 GFileMonitorEvent event_type,
1250 gpointer user_data)
1252 GUnixMountMonitor *mount_monitor;
1254 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1255 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1256 event_type != G_FILE_MONITOR_EVENT_DELETED)
1257 return;
1259 mount_monitor = user_data;
1260 g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
1263 static void
1264 mtab_file_changed (GFileMonitor *monitor,
1265 GFile *file,
1266 GFile *other_file,
1267 GFileMonitorEvent event_type,
1268 gpointer user_data)
1270 GUnixMountMonitor *mount_monitor;
1272 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1273 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1274 event_type != G_FILE_MONITOR_EVENT_DELETED)
1275 return;
1277 mount_monitor = user_data;
1278 g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1281 static void
1282 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1284 GFile *file;
1286 if (get_fstab_file () != NULL)
1288 file = g_file_new_for_path (get_fstab_file ());
1289 monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1290 g_object_unref (file);
1292 g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
1295 if (get_mtab_monitor_file () != NULL)
1297 file = g_file_new_for_path (get_mtab_monitor_file ());
1298 monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1299 g_object_unref (file);
1301 g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
1306 * g_unix_mount_monitor_set_rate_limit:
1307 * @mount_monitor: a #GUnixMountMonitor
1308 * @limit_msec: a integer with the limit in milliseconds to
1309 * poll for changes.
1311 * Sets the rate limit to which the @mount_monitor will report
1312 * consecutive change events to the mount and mount point entry files.
1314 * Since: 2.18
1316 void
1317 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1318 gint limit_msec)
1320 g_return_if_fail (G_IS_UNIX_MOUNT_MONITOR (mount_monitor));
1322 if (mount_monitor->fstab_monitor != NULL)
1323 g_file_monitor_set_rate_limit (mount_monitor->fstab_monitor, limit_msec);
1325 if (mount_monitor->mtab_monitor != NULL)
1326 g_file_monitor_set_rate_limit (mount_monitor->mtab_monitor, limit_msec);
1330 * g_unix_mount_monitor_new:
1332 * Gets a new #GUnixMountMonitor. The default rate limit for which the
1333 * monitor will report consecutive changes for the mount and mount
1334 * point entry files is the default for a #GFileMonitor. Use
1335 * g_unix_mount_monitor_set_rate_limit() to change this.
1337 * Returns: a #GUnixMountMonitor.
1339 GUnixMountMonitor *
1340 g_unix_mount_monitor_new (void)
1342 if (the_mount_monitor == NULL)
1344 the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
1345 return the_mount_monitor;
1348 return g_object_ref (the_mount_monitor);
1352 * g_unix_mount_free:
1353 * @mount_entry: a #GUnixMount.
1355 * Frees a unix mount.
1357 void
1358 g_unix_mount_free (GUnixMountEntry *mount_entry)
1360 g_return_if_fail (mount_entry != NULL);
1362 g_free (mount_entry->mount_path);
1363 g_free (mount_entry->device_path);
1364 g_free (mount_entry->filesystem_type);
1365 g_free (mount_entry);
1369 * g_unix_mount_point_free:
1370 * @mount_point: unix mount point to free.
1372 * Frees a unix mount point.
1374 void
1375 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1377 g_return_if_fail (mount_point != NULL);
1379 g_free (mount_point->mount_path);
1380 g_free (mount_point->device_path);
1381 g_free (mount_point->filesystem_type);
1382 g_free (mount_point);
1386 * g_unix_mount_compare:
1387 * @mount1: first #GUnixMountEntry to compare.
1388 * @mount2: second #GUnixMountEntry to compare.
1390 * Compares two unix mounts.
1392 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1393 * or less than @mount2, respectively.
1395 gint
1396 g_unix_mount_compare (GUnixMountEntry *mount1,
1397 GUnixMountEntry *mount2)
1399 int res;
1401 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1403 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1404 if (res != 0)
1405 return res;
1407 res = g_strcmp0 (mount1->device_path, mount2->device_path);
1408 if (res != 0)
1409 return res;
1411 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1412 if (res != 0)
1413 return res;
1415 res = mount1->is_read_only - mount2->is_read_only;
1416 if (res != 0)
1417 return res;
1419 return 0;
1423 * g_unix_mount_get_mount_path:
1424 * @mount_entry: input #GUnixMountEntry to get the mount path for.
1426 * Gets the mount path for a unix mount.
1428 * Returns: the mount path for @mount_entry.
1430 const gchar *
1431 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1433 g_return_val_if_fail (mount_entry != NULL, NULL);
1435 return mount_entry->mount_path;
1439 * g_unix_mount_get_device_path:
1440 * @mount_entry: a #GUnixMount.
1442 * Gets the device path for a unix mount.
1444 * Returns: a string containing the device path.
1446 const gchar *
1447 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1449 g_return_val_if_fail (mount_entry != NULL, NULL);
1451 return mount_entry->device_path;
1455 * g_unix_mount_get_fs_type:
1456 * @mount_entry: a #GUnixMount.
1458 * Gets the filesystem type for the unix mount.
1460 * Returns: a string containing the file system type.
1462 const gchar *
1463 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1465 g_return_val_if_fail (mount_entry != NULL, NULL);
1467 return mount_entry->filesystem_type;
1471 * g_unix_mount_is_readonly:
1472 * @mount_entry: a #GUnixMount.
1474 * Checks if a unix mount is mounted read only.
1476 * Returns: %TRUE if @mount_entry is read only.
1478 gboolean
1479 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1481 g_return_val_if_fail (mount_entry != NULL, FALSE);
1483 return mount_entry->is_read_only;
1487 * g_unix_mount_is_system_internal:
1488 * @mount_entry: a #GUnixMount.
1490 * Checks if a unix mount is a system path.
1492 * Returns: %TRUE if the unix mount is for a system path.
1494 gboolean
1495 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1497 g_return_val_if_fail (mount_entry != NULL, FALSE);
1499 return mount_entry->is_system_internal;
1503 * g_unix_mount_point_compare:
1504 * @mount1: a #GUnixMount.
1505 * @mount2: a #GUnixMount.
1507 * Compares two unix mount points.
1509 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1510 * or less than @mount2, respectively.
1512 gint
1513 g_unix_mount_point_compare (GUnixMountPoint *mount1,
1514 GUnixMountPoint *mount2)
1516 int res;
1518 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1520 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1521 if (res != 0)
1522 return res;
1524 res = g_strcmp0 (mount1->device_path, mount2->device_path);
1525 if (res != 0)
1526 return res;
1528 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1529 if (res != 0)
1530 return res;
1532 res = mount1->is_read_only - mount2->is_read_only;
1533 if (res != 0)
1534 return res;
1536 res = mount1->is_user_mountable - mount2->is_user_mountable;
1537 if (res != 0)
1538 return res;
1540 res = mount1->is_loopback - mount2->is_loopback;
1541 if (res != 0)
1542 return res;
1544 return 0;
1548 * g_unix_mount_point_get_mount_path:
1549 * @mount_point: a #GUnixMountPoint.
1551 * Gets the mount path for a unix mount point.
1553 * Returns: a string containing the mount path.
1555 const gchar *
1556 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
1558 g_return_val_if_fail (mount_point != NULL, NULL);
1560 return mount_point->mount_path;
1564 * g_unix_mount_point_get_device_path:
1565 * @mount_point: a #GUnixMountPoint.
1567 * Gets the device path for a unix mount point.
1569 * Returns: a string containing the device path.
1571 const gchar *
1572 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
1574 g_return_val_if_fail (mount_point != NULL, NULL);
1576 return mount_point->device_path;
1580 * g_unix_mount_point_get_fs_type:
1581 * @mount_point: a #GUnixMountPoint.
1583 * Gets the file system type for the mount point.
1585 * Returns: a string containing the file system type.
1587 const gchar *
1588 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
1590 g_return_val_if_fail (mount_point != NULL, NULL);
1592 return mount_point->filesystem_type;
1596 * g_unix_mount_point_is_readonly:
1597 * @mount_point: a #GUnixMountPoint.
1599 * Checks if a unix mount point is read only.
1601 * Returns: %TRUE if a mount point is read only.
1603 gboolean
1604 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
1606 g_return_val_if_fail (mount_point != NULL, FALSE);
1608 return mount_point->is_read_only;
1612 * g_unix_mount_point_is_user_mountable:
1613 * @mount_point: a #GUnixMountPoint.
1615 * Checks if a unix mount point is mountable by the user.
1617 * Returns: %TRUE if the mount point is user mountable.
1619 gboolean
1620 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
1622 g_return_val_if_fail (mount_point != NULL, FALSE);
1624 return mount_point->is_user_mountable;
1628 * g_unix_mount_point_is_loopback:
1629 * @mount_point: a #GUnixMountPoint.
1631 * Checks if a unix mount point is a loopback device.
1633 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
1635 gboolean
1636 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
1638 g_return_val_if_fail (mount_point != NULL, FALSE);
1640 return mount_point->is_loopback;
1643 static GUnixMountType
1644 guess_mount_type (const char *mount_path,
1645 const char *device_path,
1646 const char *filesystem_type)
1648 GUnixMountType type;
1649 char *basename;
1651 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
1653 if ((strcmp (filesystem_type, "udf") == 0) ||
1654 (strcmp (filesystem_type, "iso9660") == 0) ||
1655 (strcmp (filesystem_type, "cd9660") == 0))
1656 type = G_UNIX_MOUNT_TYPE_CDROM;
1657 else if ((strcmp (filesystem_type, "nfs") == 0) ||
1658 (strcmp (filesystem_type, "nfs4") == 0))
1659 type = G_UNIX_MOUNT_TYPE_NFS;
1660 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
1661 g_str_has_prefix (device_path, "/dev/fd") ||
1662 g_str_has_prefix (device_path, "/dev/floppy"))
1663 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1664 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
1665 g_str_has_prefix (device_path, "/dev/acd") ||
1666 g_str_has_prefix (device_path, "/dev/cd"))
1667 type = G_UNIX_MOUNT_TYPE_CDROM;
1668 else if (g_str_has_prefix (device_path, "/vol/"))
1670 const char *name = mount_path + strlen ("/");
1672 if (g_str_has_prefix (name, "cdrom"))
1673 type = G_UNIX_MOUNT_TYPE_CDROM;
1674 else if (g_str_has_prefix (name, "floppy") ||
1675 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
1676 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1677 else if (g_str_has_prefix (name, "rmdisk"))
1678 type = G_UNIX_MOUNT_TYPE_ZIP;
1679 else if (g_str_has_prefix (name, "jaz"))
1680 type = G_UNIX_MOUNT_TYPE_JAZ;
1681 else if (g_str_has_prefix (name, "memstick"))
1682 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1684 else
1686 basename = g_path_get_basename (mount_path);
1688 if (g_str_has_prefix (basename, "cdr") ||
1689 g_str_has_prefix (basename, "cdwriter") ||
1690 g_str_has_prefix (basename, "burn") ||
1691 g_str_has_prefix (basename, "dvdr"))
1692 type = G_UNIX_MOUNT_TYPE_CDROM;
1693 else if (g_str_has_prefix (basename, "floppy"))
1694 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1695 else if (g_str_has_prefix (basename, "zip"))
1696 type = G_UNIX_MOUNT_TYPE_ZIP;
1697 else if (g_str_has_prefix (basename, "jaz"))
1698 type = G_UNIX_MOUNT_TYPE_JAZ;
1699 else if (g_str_has_prefix (basename, "camera"))
1700 type = G_UNIX_MOUNT_TYPE_CAMERA;
1701 else if (g_str_has_prefix (basename, "memstick") ||
1702 g_str_has_prefix (basename, "memory_stick") ||
1703 g_str_has_prefix (basename, "ram"))
1704 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1705 else if (g_str_has_prefix (basename, "compact_flash"))
1706 type = G_UNIX_MOUNT_TYPE_CF;
1707 else if (g_str_has_prefix (basename, "smart_media"))
1708 type = G_UNIX_MOUNT_TYPE_SM;
1709 else if (g_str_has_prefix (basename, "sd_mmc"))
1710 type = G_UNIX_MOUNT_TYPE_SDMMC;
1711 else if (g_str_has_prefix (basename, "ipod"))
1712 type = G_UNIX_MOUNT_TYPE_IPOD;
1714 g_free (basename);
1717 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
1718 type = G_UNIX_MOUNT_TYPE_HD;
1720 return type;
1724 * g_unix_mount_guess_type:
1725 * @mount_entry: a #GUnixMount.
1727 * Guesses the type of a unix mount. If the mount type cannot be
1728 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1730 * Returns: a #GUnixMountType.
1732 static GUnixMountType
1733 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
1735 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1736 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1737 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1738 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1740 return guess_mount_type (mount_entry->mount_path,
1741 mount_entry->device_path,
1742 mount_entry->filesystem_type);
1746 * g_unix_mount_point_guess_type:
1747 * @mount_point: a #GUnixMountPoint.
1749 * Guesses the type of a unix mount point.
1750 * If the mount type cannot be determined,
1751 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1753 * Returns: a #GUnixMountType.
1755 static GUnixMountType
1756 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
1758 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1759 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1760 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1761 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1763 return guess_mount_type (mount_point->mount_path,
1764 mount_point->device_path,
1765 mount_point->filesystem_type);
1768 static const char *
1769 type_to_icon (GUnixMountType type, gboolean is_mount_point)
1771 const char *icon_name;
1773 switch (type)
1775 case G_UNIX_MOUNT_TYPE_HD:
1776 if (is_mount_point)
1777 icon_name = "drive-removable-media";
1778 else
1779 icon_name = "drive-harddisk";
1780 break;
1781 case G_UNIX_MOUNT_TYPE_FLOPPY:
1782 case G_UNIX_MOUNT_TYPE_ZIP:
1783 case G_UNIX_MOUNT_TYPE_JAZ:
1784 if (is_mount_point)
1785 icon_name = "drive-removable-media";
1786 else
1787 icon_name = "media-floppy";
1788 break;
1789 case G_UNIX_MOUNT_TYPE_CDROM:
1790 if (is_mount_point)
1791 icon_name = "drive-optical";
1792 else
1793 icon_name = "media-optical";
1794 break;
1795 case G_UNIX_MOUNT_TYPE_NFS:
1796 /* TODO: Would like a better icon here... */
1797 if (is_mount_point)
1798 icon_name = "drive-removable-media";
1799 else
1800 icon_name = "drive-harddisk";
1801 break;
1802 case G_UNIX_MOUNT_TYPE_MEMSTICK:
1803 if (is_mount_point)
1804 icon_name = "drive-removable-media";
1805 else
1806 icon_name = "media-flash";
1807 break;
1808 case G_UNIX_MOUNT_TYPE_CAMERA:
1809 if (is_mount_point)
1810 icon_name = "drive-removable-media";
1811 else
1812 icon_name = "camera-photo";
1813 break;
1814 case G_UNIX_MOUNT_TYPE_IPOD:
1815 if (is_mount_point)
1816 icon_name = "drive-removable-media";
1817 else
1818 icon_name = "multimedia-player";
1819 break;
1820 case G_UNIX_MOUNT_TYPE_UNKNOWN:
1821 default:
1822 if (is_mount_point)
1823 icon_name = "drive-removable-media";
1824 else
1825 icon_name = "drive-harddisk";
1826 break;
1829 return icon_name;
1833 * g_unix_mount_guess_name:
1834 * @mount_entry: a #GUnixMountEntry
1836 * Guesses the name of a Unix mount.
1837 * The result is a translated string.
1839 * Returns: A newly allocated string that must
1840 * be freed with g_free()
1842 gchar *
1843 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
1845 char *name;
1847 if (strcmp (mount_entry->mount_path, "/") == 0)
1848 name = g_strdup (_("Filesystem root"));
1849 else
1850 name = g_filename_display_basename (mount_entry->mount_path);
1852 return name;
1856 * g_unix_mount_guess_icon:
1857 * @mount_entry: a #GUnixMountEntry
1859 * Guesses the icon of a Unix mount.
1861 * Returns: (transfer full): a #GIcon
1863 GIcon *
1864 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
1866 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE));
1870 * g_unix_mount_point_guess_name:
1871 * @mount_point: a #GUnixMountPoint
1873 * Guesses the name of a Unix mount point.
1874 * The result is a translated string.
1876 * Returns: A newly allocated string that must
1877 * be freed with g_free()
1879 gchar *
1880 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
1882 char *name;
1884 if (strcmp (mount_point->mount_path, "/") == 0)
1885 name = g_strdup (_("Filesystem root"));
1886 else
1887 name = g_filename_display_basename (mount_point->mount_path);
1889 return name;
1893 * g_unix_mount_point_guess_icon:
1894 * @mount_point: a #GUnixMountPoint
1896 * Guesses the icon of a Unix mount point.
1898 * Returns: (transfer full): a #GIcon
1900 GIcon *
1901 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
1903 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE));
1907 * g_unix_mount_guess_can_eject:
1908 * @mount_entry: a #GUnixMountEntry
1910 * Guesses whether a Unix mount can be ejected.
1912 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
1914 gboolean
1915 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
1917 GUnixMountType guessed_type;
1919 guessed_type = g_unix_mount_guess_type (mount_entry);
1920 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
1921 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
1922 return TRUE;
1924 return FALSE;
1928 * g_unix_mount_guess_should_display:
1929 * @mount_entry: a #GUnixMountEntry
1931 * Guesses whether a Unix mount should be displayed in the UI.
1933 * Returns: %TRUE if @mount_entry is deemed to be displayable.
1935 gboolean
1936 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
1938 const char *mount_path;
1940 /* Never display internal mountpoints */
1941 if (g_unix_mount_is_system_internal (mount_entry))
1942 return FALSE;
1944 /* Only display things in /media (which are generally user mountable)
1945 and home dir (fuse stuff) */
1946 mount_path = mount_entry->mount_path;
1947 if (mount_path != NULL)
1949 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
1950 if (g_strstr_len (mount_path, -1, "/.") != NULL)
1951 return FALSE;
1953 if (g_str_has_prefix (mount_path, "/media/"))
1955 char *path;
1956 /* Avoid displaying mounts that are not accessible to the user.
1958 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
1959 * want to avoid g_access() for mount points which can potentially
1960 * block or fail stat()'ing, such as network mounts.
1962 path = g_path_get_dirname (mount_path);
1963 if (g_str_has_prefix (path, "/media/"))
1965 if (g_access (path, R_OK|X_OK) != 0)
1967 g_free (path);
1968 return FALSE;
1971 g_free (path);
1973 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
1975 struct stat st;
1976 if (g_stat (mount_entry->device_path, &st) == 0 &&
1977 S_ISBLK(st.st_mode) &&
1978 g_access (mount_path, R_OK|X_OK) != 0)
1979 return FALSE;
1981 return TRUE;
1984 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
1985 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
1986 return TRUE;
1989 return FALSE;
1993 * g_unix_mount_point_guess_can_eject:
1994 * @mount_point: a #GUnixMountPoint
1996 * Guesses whether a Unix mount point can be ejected.
1998 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2000 gboolean
2001 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2003 GUnixMountType guessed_type;
2005 guessed_type = g_unix_mount_point_guess_type (mount_point);
2006 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2007 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2008 return TRUE;
2010 return FALSE;
2014 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2015 static void
2016 _canonicalize_filename (gchar *filename)
2018 gchar *p, *q;
2019 gboolean last_was_slash = FALSE;
2021 p = filename;
2022 q = filename;
2024 while (*p)
2026 if (*p == G_DIR_SEPARATOR)
2028 if (!last_was_slash)
2029 *q++ = G_DIR_SEPARATOR;
2031 last_was_slash = TRUE;
2033 else
2035 if (last_was_slash && *p == '.')
2037 if (*(p + 1) == G_DIR_SEPARATOR ||
2038 *(p + 1) == '\0')
2040 if (*(p + 1) == '\0')
2041 break;
2043 p += 1;
2045 else if (*(p + 1) == '.' &&
2046 (*(p + 2) == G_DIR_SEPARATOR ||
2047 *(p + 2) == '\0'))
2049 if (q > filename + 1)
2051 q--;
2052 while (q > filename + 1 &&
2053 *(q - 1) != G_DIR_SEPARATOR)
2054 q--;
2057 if (*(p + 2) == '\0')
2058 break;
2060 p += 2;
2062 else
2064 *q++ = *p;
2065 last_was_slash = FALSE;
2068 else
2070 *q++ = *p;
2071 last_was_slash = FALSE;
2075 p++;
2078 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2079 q--;
2081 *q = '\0';
2084 static char *
2085 _resolve_symlink (const char *file)
2087 GError *error;
2088 char *dir;
2089 char *link;
2090 char *f;
2091 char *f1;
2093 f = g_strdup (file);
2095 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
2097 link = g_file_read_link (f, &error);
2098 if (link == NULL)
2100 g_error_free (error);
2101 g_free (f);
2102 f = NULL;
2103 goto out;
2106 dir = g_path_get_dirname (f);
2107 f1 = g_strdup_printf ("%s/%s", dir, link);
2108 g_free (dir);
2109 g_free (link);
2110 g_free (f);
2111 f = f1;
2114 out:
2115 if (f != NULL)
2116 _canonicalize_filename (f);
2117 return f;
2120 #ifdef HAVE_MNTENT_H
2121 static const char *
2122 _resolve_dev_root (void)
2124 static gboolean have_real_dev_root = FALSE;
2125 static char real_dev_root[256];
2126 struct stat statbuf;
2128 /* see if it's cached already */
2129 if (have_real_dev_root)
2130 goto found;
2132 /* otherwise we're going to find it right away.. */
2133 have_real_dev_root = TRUE;
2135 if (stat ("/dev/root", &statbuf) == 0)
2137 if (! S_ISLNK (statbuf.st_mode))
2139 dev_t root_dev = statbuf.st_dev;
2140 FILE *f;
2141 char buf[1024];
2143 /* see if device with similar major:minor as /dev/root is mention
2144 * in /etc/mtab (it usually is)
2146 f = fopen ("/etc/mtab", "r");
2147 if (f != NULL)
2149 struct mntent *entp;
2150 #ifdef HAVE_GETMNTENT_R
2151 struct mntent ent;
2152 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
2154 #else
2155 G_LOCK (getmntent);
2156 while ((entp = getmntent (f)) != NULL)
2158 #endif
2159 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2160 statbuf.st_dev == root_dev)
2162 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2163 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2164 fclose (f);
2165 goto found;
2169 endmntent (f);
2171 #ifndef HAVE_GETMNTENT_R
2172 G_UNLOCK (getmntent);
2173 #endif
2176 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2179 else
2181 char *resolved;
2182 resolved = _resolve_symlink ("/dev/root");
2183 if (resolved != NULL)
2185 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2186 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2187 g_free (resolved);
2188 goto found;
2193 /* bah sucks.. */
2194 strcpy (real_dev_root, "/dev/root");
2196 found:
2197 return real_dev_root;
2199 #endif