1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* GIO - GLib Input, Output and Streaming Library
4 *
5 * Copyright (C) 2006-2007 Red Hat, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
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.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23 /* Prologue {{{1 */
24
25 #include "config.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #ifndef HAVE_SYSCTLBYNAME
31 #ifdef HAVE_SYS_PARAM_H
32 #include <sys/param.h>
33 #endif
34 #endif
35 #ifdef HAVE_POLL
36 #include <poll.h>
37 #endif
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <sys/time.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <signal.h>
44 #include <gstdio.h>
45 #include <dirent.h>
46
47 #if HAVE_SYS_STATFS_H
48 #include <sys/statfs.h>
49 #endif
50 #if HAVE_SYS_STATVFS_H
51 #include <sys/statvfs.h>
52 #endif
53 #if HAVE_SYS_VFS_H
54 #include <sys/vfs.h>
55 #elif HAVE_SYS_MOUNT_H
56 #if HAVE_SYS_PARAM_H
57 #include <sys/param.h>
58 #endif
59 #include <sys/mount.h>
60 #endif
61
62 #ifndef O_BINARY
63 #define O_BINARY 0
64 #endif
65
66 #include "gunixmounts.h"
67 #include "glocalfileprivate.h"
68 #include "gfile.h"
69 #include "gfilemonitor.h"
70 #include "glibintl.h"
71 #include "gthemedicon.h"
72 #include "gcontextspecificgroup.h"
73
74
75 #ifdef HAVE_MNTENT_H
76 static const char *_resolve_dev_root (void);
77 #endif
78
79 /**
80 * SECTION:gunixmounts
81 * @include: gio/gunixmounts.h
82 * @short_description: UNIX mounts
83 *
84 * Routines for managing mounted UNIX mount points and paths.
85 *
86 * Note that `<gio/gunixmounts.h>` belongs to the UNIX-specific GIO
87 * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
88 * file when using it.
89 */
90
91 /**
92 * GUnixMountType:
93 * @G_UNIX_MOUNT_TYPE_UNKNOWN: Unknown UNIX mount type.
94 * @G_UNIX_MOUNT_TYPE_FLOPPY: Floppy disk UNIX mount type.
95 * @G_UNIX_MOUNT_TYPE_CDROM: CDROM UNIX mount type.
96 * @G_UNIX_MOUNT_TYPE_NFS: Network File System (NFS) UNIX mount type.
97 * @G_UNIX_MOUNT_TYPE_ZIP: ZIP UNIX mount type.
98 * @G_UNIX_MOUNT_TYPE_JAZ: JAZZ UNIX mount type.
99 * @G_UNIX_MOUNT_TYPE_MEMSTICK: Memory Stick UNIX mount type.
100 * @G_UNIX_MOUNT_TYPE_CF: Compact Flash UNIX mount type.
101 * @G_UNIX_MOUNT_TYPE_SM: Smart Media UNIX mount type.
102 * @G_UNIX_MOUNT_TYPE_SDMMC: SD/MMC UNIX mount type.
103 * @G_UNIX_MOUNT_TYPE_IPOD: iPod UNIX mount type.
104 * @G_UNIX_MOUNT_TYPE_CAMERA: Digital camera UNIX mount type.
105 * @G_UNIX_MOUNT_TYPE_HD: Hard drive UNIX mount type.
106 *
107 * Types of UNIX mounts.
108 **/
109 typedef enum {
110 G_UNIX_MOUNT_TYPE_UNKNOWN,
111 G_UNIX_MOUNT_TYPE_FLOPPY,
112 G_UNIX_MOUNT_TYPE_CDROM,
113 G_UNIX_MOUNT_TYPE_NFS,
114 G_UNIX_MOUNT_TYPE_ZIP,
115 G_UNIX_MOUNT_TYPE_JAZ,
116 G_UNIX_MOUNT_TYPE_MEMSTICK,
117 G_UNIX_MOUNT_TYPE_CF,
118 G_UNIX_MOUNT_TYPE_SM,
119 G_UNIX_MOUNT_TYPE_SDMMC,
120 G_UNIX_MOUNT_TYPE_IPOD,
121 G_UNIX_MOUNT_TYPE_CAMERA,
122 G_UNIX_MOUNT_TYPE_HD
123 } GUnixMountType;
124
125 struct _GUnixMountEntry {
126 char *mount_path;
127 char *device_path;
128 char *root_path;
129 char *filesystem_type;
130 char *options;
131 gboolean is_read_only;
132 gboolean is_system_internal;
133 };
134
135 G_DEFINE_BOXED_TYPE (GUnixMountEntry, g_unix_mount_entry,
136 g_unix_mount_copy, g_unix_mount_free)
137
138 struct _GUnixMountPoint {
139 char *mount_path;
140 char *device_path;
141 char *filesystem_type;
142 char *options;
143 gboolean is_read_only;
144 gboolean is_user_mountable;
145 gboolean is_loopback;
146 };
147
148 G_DEFINE_BOXED_TYPE (GUnixMountPoint, g_unix_mount_point,
149 g_unix_mount_point_copy, g_unix_mount_point_free)
150
151 static GList *_g_get_unix_mounts (void);
152 static GList *_g_get_unix_mount_points (void);
153 static gboolean proc_mounts_watch_is_running (void);
154
155 G_LOCK_DEFINE_STATIC (proc_mounts_source);
156
157 /* Protected by proc_mounts_source lock */
158 static guint64 mount_poller_time = 0;
159 static GSource *proc_mounts_watch_source;
160
161 #ifdef HAVE_SYS_MNTTAB_H
162 #define MNTOPT_RO "ro"
163 #endif
164
165 #ifdef HAVE_MNTENT_H
166 #include <mntent.h>
167 #ifdef HAVE_LIBMOUNT
168 #include <libmount.h>
169 #endif
170 #elif defined (HAVE_SYS_MNTTAB_H)
171 #include <sys/mnttab.h>
172 #if defined(__sun) && !defined(mnt_opts)
173 #define mnt_opts mnt_mntopts
174 #endif
175 #endif
176
177 #ifdef HAVE_SYS_VFSTAB_H
178 #include <sys/vfstab.h>
179 #endif
180
181 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
182 #include <sys/mntctl.h>
183 #include <sys/vfs.h>
184 #include <sys/vmount.h>
185 #include <fshelp.h>
186 #endif
187
188 #if (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
189 #include <sys/param.h>
190 #include <sys/ucred.h>
191 #include <sys/mount.h>
192 #include <fstab.h>
193 #ifdef HAVE_SYS_SYSCTL_H
194 #include <sys/sysctl.h>
195 #endif
196 #endif
197
198 #ifndef HAVE_SETMNTENT
199 #define setmntent(f,m) fopen(f,m)
200 #endif
201 #ifndef HAVE_ENDMNTENT
202 #define endmntent(f) fclose(f)
203 #endif
204
205 static gboolean
is_in(const char * value,const char * set[])206 is_in (const char *value, const char *set[])
207 {
208 int i;
209 for (i = 0; set[i] != NULL; i++)
210 {
211 if (strcmp (set[i], value) == 0)
212 return TRUE;
213 }
214 return FALSE;
215 }
216
217 /**
218 * g_unix_is_mount_path_system_internal:
219 * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr`
220 *
221 * Determines if @mount_path is considered an implementation of the
222 * OS. This is primarily used for hiding mountable and mounted volumes
223 * that only are used in the OS and has little to no relevance to the
224 * casual user.
225 *
226 * Returns: %TRUE if @mount_path is considered an implementation detail
227 * of the OS.
228 **/
229 gboolean
g_unix_is_mount_path_system_internal(const char * mount_path)230 g_unix_is_mount_path_system_internal (const char *mount_path)
231 {
232 const char *ignore_mountpoints[] = {
233 /* Includes all FHS 2.3 toplevel dirs and other specialized
234 * directories that we want to hide from the user.
235 */
236 "/", /* we already have "Filesystem root" in Nautilus */
237 "/bin",
238 "/boot",
239 "/compat/linux/proc",
240 "/compat/linux/sys",
241 "/dev",
242 "/etc",
243 "/home",
244 "/lib",
245 "/lib64",
246 "/libexec",
247 "/live/cow",
248 "/live/image",
249 "/media",
250 "/mnt",
251 "/opt",
252 "/rescue",
253 "/root",
254 "/sbin",
255 "/srv",
256 "/tmp",
257 "/usr",
258 "/usr/X11R6",
259 "/usr/local",
260 "/usr/obj",
261 "/usr/ports",
262 "/usr/src",
263 "/usr/xobj",
264 "/var",
265 "/var/crash",
266 "/var/local",
267 "/var/log",
268 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
269 "/var/mail",
270 "/var/run",
271 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
272 "/proc",
273 "/sbin",
274 "/net",
275 "/sys",
276 NULL
277 };
278
279 if (is_in (mount_path, ignore_mountpoints))
280 return TRUE;
281
282 if (g_str_has_prefix (mount_path, "/dev/") ||
283 g_str_has_prefix (mount_path, "/proc/") ||
284 g_str_has_prefix (mount_path, "/sys/"))
285 return TRUE;
286
287 if (g_str_has_suffix (mount_path, "/.gvfs"))
288 return TRUE;
289
290 return FALSE;
291 }
292
293 /**
294 * g_unix_is_system_fs_type:
295 * @fs_type: a file system type, e.g. `procfs` or `tmpfs`
296 *
297 * Determines if @fs_type is considered a type of file system which is only
298 * used in implementation of the OS. This is primarily used for hiding
299 * mounted volumes that are intended as APIs for programs to read, and system
300 * administrators at a shell; rather than something that should, for example,
301 * appear in a GUI. For example, the Linux `/proc` filesystem.
302 *
303 * The list of file system types considered ‘system’ ones may change over time.
304 *
305 * Returns: %TRUE if @fs_type is considered an implementation detail of the OS.
306 * Since: 2.56
307 */
308 gboolean
g_unix_is_system_fs_type(const char * fs_type)309 g_unix_is_system_fs_type (const char *fs_type)
310 {
311 const char *ignore_fs[] = {
312 "adfs",
313 "afs",
314 "auto",
315 "autofs",
316 "autofs4",
317 "cgroup",
318 "configfs",
319 "cxfs",
320 "debugfs",
321 "devfs",
322 "devpts",
323 "devtmpfs",
324 "ecryptfs",
325 "fdescfs",
326 "fusectl",
327 "gfs",
328 "gfs2",
329 "gpfs",
330 "hugetlbfs",
331 "kernfs",
332 "linprocfs",
333 "linsysfs",
334 "lustre",
335 "lustre_lite",
336 "mfs",
337 "mqueue",
338 "ncpfs",
339 "nfsd",
340 "nullfs",
341 "ocfs2",
342 "overlay",
343 "proc",
344 "procfs",
345 "pstore",
346 "ptyfs",
347 "rootfs",
348 "rpc_pipefs",
349 "securityfs",
350 "selinuxfs",
351 "sysfs",
352 "tmpfs",
353 "usbfs",
354 NULL
355 };
356
357 g_return_val_if_fail (fs_type != NULL && *fs_type != '\0', FALSE);
358
359 return is_in (fs_type, ignore_fs);
360 }
361
362 /**
363 * g_unix_is_system_device_path:
364 * @device_path: a device path, e.g. `/dev/loop0` or `nfsd`
365 *
366 * Determines if @device_path is considered a block device path which is only
367 * used in implementation of the OS. This is primarily used for hiding
368 * mounted volumes that are intended as APIs for programs to read, and system
369 * administrators at a shell; rather than something that should, for example,
370 * appear in a GUI. For example, the Linux `/proc` filesystem.
371 *
372 * The list of device paths considered ‘system’ ones may change over time.
373 *
374 * Returns: %TRUE if @device_path is considered an implementation detail of
375 * the OS.
376 * Since: 2.56
377 */
378 gboolean
g_unix_is_system_device_path(const char * device_path)379 g_unix_is_system_device_path (const char *device_path)
380 {
381 const char *ignore_devices[] = {
382 "none",
383 "sunrpc",
384 "devpts",
385 "nfsd",
386 "/dev/loop",
387 "/dev/vn",
388 NULL
389 };
390
391 g_return_val_if_fail (device_path != NULL && *device_path != '\0', FALSE);
392
393 return is_in (device_path, ignore_devices);
394 }
395
396 static gboolean
guess_system_internal(const char * mountpoint,const char * fs,const char * device,const char * root)397 guess_system_internal (const char *mountpoint,
398 const char *fs,
399 const char *device,
400 const char *root)
401 {
402 if (g_unix_is_system_fs_type (fs))
403 return TRUE;
404
405 if (g_unix_is_system_device_path (device))
406 return TRUE;
407
408 if (g_unix_is_mount_path_system_internal (mountpoint))
409 return TRUE;
410
411 /* It is not possible to reliably detect mounts which were created by bind
412 * operation. mntent-based _g_get_unix_mounts() implementation blindly skips
413 * mounts with a device path that is repeated (e.g. mounts created by bind
414 * operation, btrfs subvolumes). This usually chooses the most important
415 * mounts (i.e. which points to the root of filesystem), but it doesn't work
416 * in all cases and also it is not ideal that those mounts are completely
417 * ignored (e.g. x-gvfs-show doesn't work for them, trash backend can't handle
418 * files on btrfs subvolumes). libmount-based _g_get_unix_mounts()
419 * implementation provides a root path. So there is no need to completely
420 * ignore those mounts, because e.g. our volume monitors can use the root path
421 * to not mengle those mounts with the "regular" mounts (i.e. which points to
422 * the root). But because those mounts usually just duplicate other mounts and
423 * are completely ignored with mntend-based implementation, let's mark them as
424 * system internal. Given the different approches it doesn't mean that all
425 * mounts which were ignored will be system internal now, but this should work
426 * in most cases. For more info, see g_unix_mount_get_root_path() annotation,
427 * comment in mntent-based _g_get_unix_mounts() implementation and the
428 * https://gitlab.gnome.org/GNOME/glib/issues/1271 issue.
429 */
430 if (root != NULL && g_strcmp0 (root, "/") != 0)
431 return TRUE;
432
433 return FALSE;
434 }
435
436 /* GUnixMounts (ie: mtab) implementations {{{1 */
437
438 static GUnixMountEntry *
create_unix_mount_entry(const char * device_path,const char * mount_path,const char * root_path,const char * filesystem_type,const char * options,gboolean is_read_only)439 create_unix_mount_entry (const char *device_path,
440 const char *mount_path,
441 const char *root_path,
442 const char *filesystem_type,
443 const char *options,
444 gboolean is_read_only)
445 {
446 GUnixMountEntry *mount_entry = NULL;
447
448 mount_entry = g_new0 (GUnixMountEntry, 1);
449 mount_entry->device_path = g_strdup (device_path);
450 mount_entry->mount_path = g_strdup (mount_path);
451 mount_entry->root_path = g_strdup (root_path);
452 mount_entry->filesystem_type = g_strdup (filesystem_type);
453 mount_entry->options = g_strdup (options);
454 mount_entry->is_read_only = is_read_only;
455
456 mount_entry->is_system_internal =
457 guess_system_internal (mount_entry->mount_path,
458 mount_entry->filesystem_type,
459 mount_entry->device_path,
460 mount_entry->root_path);
461
462 return mount_entry;
463 }
464
465 static GUnixMountPoint *
create_unix_mount_point(const char * device_path,const char * mount_path,const char * filesystem_type,const char * options,gboolean is_read_only,gboolean is_user_mountable,gboolean is_loopback)466 create_unix_mount_point (const char *device_path,
467 const char *mount_path,
468 const char *filesystem_type,
469 const char *options,
470 gboolean is_read_only,
471 gboolean is_user_mountable,
472 gboolean is_loopback)
473 {
474 GUnixMountPoint *mount_point = NULL;
475
476 mount_point = g_new0 (GUnixMountPoint, 1);
477 mount_point->device_path = g_strdup (device_path);
478 mount_point->mount_path = g_strdup (mount_path);
479 mount_point->filesystem_type = g_strdup (filesystem_type);
480 mount_point->options = g_strdup (options);
481 mount_point->is_read_only = is_read_only;
482 mount_point->is_user_mountable = is_user_mountable;
483 mount_point->is_loopback = is_loopback;
484
485 return mount_point;
486 }
487
488 /* mntent.h (Linux, GNU, NSS) {{{2 */
489 #ifdef HAVE_MNTENT_H
490
491 #ifdef HAVE_LIBMOUNT
492
493 /* For documentation on /proc/self/mountinfo see
494 * http://www.kernel.org/doc/Documentation/filesystems/proc.txt
495 */
496 #define PROC_MOUNTINFO_PATH "/proc/self/mountinfo"
497
498 static GList *
_g_get_unix_mounts(void)499 _g_get_unix_mounts (void)
500 {
501 struct libmnt_table *table = NULL;
502 struct libmnt_iter* iter = NULL;
503 struct libmnt_fs *fs = NULL;
504 GUnixMountEntry *mount_entry = NULL;
505 GList *return_list = NULL;
506
507 table = mnt_new_table ();
508 if (mnt_table_parse_mtab (table, NULL) < 0)
509 goto out;
510
511 iter = mnt_new_iter (MNT_ITER_FORWARD);
512 while (mnt_table_next_fs (table, iter, &fs) == 0)
513 {
514 const char *device_path = NULL;
515 char *mount_options = NULL;
516 unsigned long mount_flags = 0;
517 gboolean is_read_only = FALSE;
518
519 device_path = mnt_fs_get_source (fs);
520 if (g_strcmp0 (device_path, "/dev/root") == 0)
521 device_path = _resolve_dev_root ();
522
523 mount_options = mnt_fs_strdup_options (fs);
524 if (mount_options)
525 {
526 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
527 g_free (mount_options);
528 }
529 is_read_only = (mount_flags & MS_RDONLY) ? TRUE : FALSE;
530
531 mount_entry = create_unix_mount_entry (device_path,
532 mnt_fs_get_target (fs),
533 mnt_fs_get_root (fs),
534 mnt_fs_get_fstype (fs),
535 mnt_fs_get_options (fs),
536 is_read_only);
537
538 return_list = g_list_prepend (return_list, mount_entry);
539 }
540 mnt_free_iter (iter);
541
542 out:
543 mnt_free_table (table);
544
545 return g_list_reverse (return_list);
546 }
547
548 #else
549
550 static const char *
get_mtab_read_file(void)551 get_mtab_read_file (void)
552 {
553 #ifdef _PATH_MOUNTED
554 # ifdef __linux__
555 return "/proc/mounts";
556 # else
557 return _PATH_MOUNTED;
558 # endif
559 #else
560 return "/etc/mtab";
561 #endif
562 }
563
564 #ifndef HAVE_GETMNTENT_R
565 G_LOCK_DEFINE_STATIC(getmntent);
566 #endif
567
568 static GList *
_g_get_unix_mounts(void)569 _g_get_unix_mounts (void)
570 {
571 #ifdef HAVE_GETMNTENT_R
572 struct mntent ent;
573 char buf[1024];
574 #endif
575 struct mntent *mntent;
576 FILE *file;
577 const char *read_file;
578 GUnixMountEntry *mount_entry;
579 GHashTable *mounts_hash;
580 GList *return_list;
581
582 read_file = get_mtab_read_file ();
583
584 file = setmntent (read_file, "r");
585 if (file == NULL)
586 return NULL;
587
588 return_list = NULL;
589
590 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
591
592 #ifdef HAVE_GETMNTENT_R
593 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
594 #else
595 G_LOCK (getmntent);
596 while ((mntent = getmntent (file)) != NULL)
597 #endif
598 {
599 const char *device_path = NULL;
600 gboolean is_read_only = FALSE;
601
602 /* ignore any mnt_fsname that is repeated and begins with a '/'
603 *
604 * We do this to avoid being fooled by --bind mounts, since
605 * these have the same device as the location they bind to.
606 * It's not an ideal solution to the problem, but it's likely that
607 * the most important mountpoint is first and the --bind ones after
608 * that aren't as important. So it should work.
609 *
610 * The '/' is to handle procfs, tmpfs and other no device mounts.
611 */
612 if (mntent->mnt_fsname != NULL &&
613 mntent->mnt_fsname[0] == '/' &&
614 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
615 continue;
616
617 if (g_strcmp0 (mntent->mnt_fsname, "/dev/root") == 0)
618 device_path = _resolve_dev_root ();
619 else
620 device_path = mntent->mnt_fsname;
621
622 #if defined (HAVE_HASMNTOPT)
623 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
624 is_read_only = TRUE;
625 #endif
626
627 mount_entry = create_unix_mount_entry (device_path,
628 mntent->mnt_dir,
629 NULL,
630 mntent->mnt_type,
631 mntent->mnt_opts,
632 is_read_only);
633
634 g_hash_table_insert (mounts_hash,
635 mount_entry->device_path,
636 mount_entry->device_path);
637
638 return_list = g_list_prepend (return_list, mount_entry);
639 }
640 g_hash_table_destroy (mounts_hash);
641
642 endmntent (file);
643
644 #ifndef HAVE_GETMNTENT_R
645 G_UNLOCK (getmntent);
646 #endif
647
648 return g_list_reverse (return_list);
649 }
650
651 #endif /* HAVE_LIBMOUNT */
652
653 static const char *
get_mtab_monitor_file(void)654 get_mtab_monitor_file (void)
655 {
656 static const char *mountinfo_path = NULL;
657 #ifdef HAVE_LIBMOUNT
658 struct stat buf;
659 #endif
660
661 if (mountinfo_path != NULL)
662 return mountinfo_path;
663
664 #ifdef HAVE_LIBMOUNT
665 /* The mtab file is still used by some distros, so it has to be monitored in
666 * order to avoid races between g_unix_mounts_get and "mounts-changed" signal:
667 * https://bugzilla.gnome.org/show_bug.cgi?id=782814
668 */
669 if (mnt_has_regular_mtab (&mountinfo_path, NULL))
670 {
671 return mountinfo_path;
672 }
673
674 if (stat (PROC_MOUNTINFO_PATH, &buf) == 0)
675 {
676 mountinfo_path = PROC_MOUNTINFO_PATH;
677 return mountinfo_path;
678 }
679 #endif
680
681 #ifdef _PATH_MOUNTED
682 # ifdef __linux__
683 mountinfo_path = "/proc/mounts";
684 # else
685 mountinfo_path = _PATH_MOUNTED;
686 # endif
687 #else
688 mountinfo_path = "/etc/mtab";
689 #endif
690
691 return mountinfo_path;
692 }
693
694 /* mnttab.h {{{2 */
695 #elif defined (HAVE_SYS_MNTTAB_H)
696
697 G_LOCK_DEFINE_STATIC(getmntent);
698
699 static const char *
get_mtab_read_file(void)700 get_mtab_read_file (void)
701 {
702 #ifdef _PATH_MOUNTED
703 return _PATH_MOUNTED;
704 #else
705 return "/etc/mnttab";
706 #endif
707 }
708
709 static const char *
get_mtab_monitor_file(void)710 get_mtab_monitor_file (void)
711 {
712 return get_mtab_read_file ();
713 }
714
715 static GList *
_g_get_unix_mounts(void)716 _g_get_unix_mounts (void)
717 {
718 struct mnttab mntent;
719 FILE *file;
720 const char *read_file;
721 GUnixMountEntry *mount_entry;
722 GList *return_list;
723
724 read_file = get_mtab_read_file ();
725
726 file = setmntent (read_file, "r");
727 if (file == NULL)
728 return NULL;
729
730 return_list = NULL;
731
732 G_LOCK (getmntent);
733 while (! getmntent (file, &mntent))
734 {
735 gboolean is_read_only = FALSE;
736
737 #if defined (HAVE_HASMNTOPT)
738 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
739 is_read_only = TRUE;
740 #endif
741
742 mount_entry = create_unix_mount_entry (mntent.mnt_special,
743 mntent.mnt_mountp,
744 NULL,
745 mntent.mnt_fstype,
746 mntent.mnt_opts,
747 is_read_only);
748
749 return_list = g_list_prepend (return_list, mount_entry);
750 }
751
752 endmntent (file);
753
754 G_UNLOCK (getmntent);
755
756 return g_list_reverse (return_list);
757 }
758
759 /* mntctl.h (AIX) {{{2 */
760 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
761
762 static const char *
get_mtab_monitor_file(void)763 get_mtab_monitor_file (void)
764 {
765 return NULL;
766 }
767
768 static GList *
_g_get_unix_mounts(void)769 _g_get_unix_mounts (void)
770 {
771 struct vfs_ent *fs_info;
772 struct vmount *vmount_info;
773 int vmount_number;
774 unsigned int vmount_size;
775 int current;
776 GList *return_list;
777
778 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
779 {
780 g_warning ("Unable to know the number of mounted volumes");
781
782 return NULL;
783 }
784
785 vmount_info = (struct vmount*)g_malloc (vmount_size);
786
787 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
788
789 if (vmount_info->vmt_revision != VMT_REVISION)
790 g_warning ("Bad vmount structure revision number, want %d, got %d", VMT_REVISION, vmount_info->vmt_revision);
791
792 if (vmount_number < 0)
793 {
794 g_warning ("Unable to recover mounted volumes information");
795
796 g_free (vmount_info);
797 return NULL;
798 }
799
800 return_list = NULL;
801 while (vmount_number > 0)
802 {
803 gboolean is_read_only = FALSE;
804
805 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
806
807 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
808 is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
809
810 mount_entry = create_unix_mount_entry (vmt2dataptr (vmount_info, VMT_OBJECT),
811 vmt2dataptr (vmount_info, VMT_STUB),
812 NULL,
813 fs_info == NULL ? "unknown" : fs_info->vfsent_name,
814 NULL,
815 is_read_only);
816
817 return_list = g_list_prepend (return_list, mount_entry);
818
819 vmount_info = (struct vmount *)( (char*)vmount_info
820 + vmount_info->vmt_length);
821 vmount_number--;
822 }
823
824 g_free (vmount_info);
825
826 return g_list_reverse (return_list);
827 }
828
829 /* sys/mount.h {{{2 */
830 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
831
832 static const char *
get_mtab_monitor_file(void)833 get_mtab_monitor_file (void)
834 {
835 return NULL;
836 }
837
838 static GList *
_g_get_unix_mounts(void)839 _g_get_unix_mounts (void)
840 {
841 #if defined(USE_STATVFS)
842 struct statvfs *mntent = NULL;
843 #elif defined(USE_STATFS)
844 struct statfs *mntent = NULL;
845 #else
846 #error statfs juggling failed
847 #endif
848 size_t bufsize;
849 int num_mounts, i;
850 GUnixMountEntry *mount_entry;
851 GList *return_list;
852
853 /* Pass NOWAIT to avoid blocking trying to update NFS mounts. */
854 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
855 num_mounts = getvfsstat (NULL, 0, ST_NOWAIT);
856 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
857 num_mounts = getfsstat (NULL, 0, MNT_NOWAIT);
858 #endif
859 if (num_mounts == -1)
860 return NULL;
861
862 bufsize = num_mounts * sizeof (*mntent);
863 mntent = g_malloc (bufsize);
864 #if defined(USE_STATVFS) && defined(HAVE_GETVFSSTAT)
865 num_mounts = getvfsstat (mntent, bufsize, ST_NOWAIT);
866 #elif defined(USE_STATFS) && defined(HAVE_GETFSSTAT)
867 num_mounts = getfsstat (mntent, bufsize, MNT_NOWAIT);
868 #endif
869 if (num_mounts == -1)
870 return NULL;
871
872 return_list = NULL;
873
874 for (i = 0; i < num_mounts; i++)
875 {
876 gboolean is_read_only = FALSE;
877
878 #if defined(USE_STATVFS)
879 if (mntent[i].f_flag & ST_RDONLY)
880 #elif defined(USE_STATFS)
881 if (mntent[i].f_flags & MNT_RDONLY)
882 #else
883 #error statfs juggling failed
884 #endif
885 is_read_only = TRUE;
886
887 mount_entry = create_unix_mount_entry (mntent[i].f_mntfromname,
888 mntent[i].f_mntonname,
889 NULL,
890 mntent[i].f_fstypename,
891 NULL,
892 is_read_only);
893
894 return_list = g_list_prepend (return_list, mount_entry);
895 }
896
897 g_free (mntent);
898
899 return g_list_reverse (return_list);
900 }
901
902 /* Interix {{{2 */
903 #elif defined(__INTERIX)
904
905 static const char *
get_mtab_monitor_file(void)906 get_mtab_monitor_file (void)
907 {
908 return NULL;
909 }
910
911 static GList *
_g_get_unix_mounts(void)912 _g_get_unix_mounts (void)
913 {
914 DIR *dirp;
915 GList* return_list = NULL;
916 char filename[9 + NAME_MAX];
917
918 dirp = opendir ("/dev/fs");
919 if (!dirp)
920 {
921 g_warning ("unable to read /dev/fs!");
922 return NULL;
923 }
924
925 while (1)
926 {
927 struct statvfs statbuf;
928 struct dirent entry;
929 struct dirent* result;
930
931 if (readdir_r (dirp, &entry, &result) || result == NULL)
932 break;
933
934 strcpy (filename, "/dev/fs/");
935 strcat (filename, entry.d_name);
936
937 if (statvfs (filename, &statbuf) == 0)
938 {
939 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
940
941 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
942 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
943 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
944
945 if (statbuf.f_flag & ST_RDONLY)
946 mount_entry->is_read_only = TRUE;
947
948 return_list = g_list_prepend(return_list, mount_entry);
949 }
950 }
951
952 return_list = g_list_reverse (return_list);
953
954 closedir (dirp);
955
956 return return_list;
957 }
958
959 /* Common code {{{2 */
960 #else
961 #error No _g_get_unix_mounts() implementation for system
962 #endif
963
964 /* GUnixMountPoints (ie: fstab) implementations {{{1 */
965
966 /* _g_get_unix_mount_points():
967 * read the fstab.
968 * don't return swap and ignore mounts.
969 */
970
971 static char *
get_fstab_file(void)972 get_fstab_file (void)
973 {
974 #ifdef HAVE_LIBMOUNT
975 return (char *) mnt_get_fstab_path ();
976 #else
977 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
978 /* AIX */
979 return "/etc/filesystems";
980 #elif defined(_PATH_MNTTAB)
981 return _PATH_MNTTAB;
982 #elif defined(VFSTAB)
983 return VFSTAB;
984 #else
985 return "/etc/fstab";
986 #endif
987 #endif
988 }
989
990 /* mntent.h (Linux, GNU, NSS) {{{2 */
991 #ifdef HAVE_MNTENT_H
992
993 #ifdef HAVE_LIBMOUNT
994
995 static GList *
_g_get_unix_mount_points(void)996 _g_get_unix_mount_points (void)
997 {
998 struct libmnt_table *table = NULL;
999 struct libmnt_iter* iter = NULL;
1000 struct libmnt_fs *fs = NULL;
1001 GUnixMountPoint *mount_point = NULL;
1002 GList *return_list = NULL;
1003
1004 table = mnt_new_table ();
1005 if (mnt_table_parse_fstab (table, NULL) < 0)
1006 goto out;
1007
1008 iter = mnt_new_iter (MNT_ITER_FORWARD);
1009 while (mnt_table_next_fs (table, iter, &fs) == 0)
1010 {
1011 const char *device_path = NULL;
1012 const char *mount_path = NULL;
1013 const char *mount_fstype = NULL;
1014 char *mount_options = NULL;
1015 gboolean is_read_only = FALSE;
1016 gboolean is_user_mountable = FALSE;
1017 gboolean is_loopback = FALSE;
1018
1019 mount_path = mnt_fs_get_target (fs);
1020 if ((strcmp (mount_path, "ignore") == 0) ||
1021 (strcmp (mount_path, "swap") == 0) ||
1022 (strcmp (mount_path, "none") == 0))
1023 continue;
1024
1025 mount_fstype = mnt_fs_get_fstype (fs);
1026 mount_options = mnt_fs_strdup_options (fs);
1027 if (mount_options)
1028 {
1029 unsigned long mount_flags = 0;
1030 unsigned long userspace_flags = 0;
1031
1032 mnt_optstr_get_flags (mount_options, &mount_flags, mnt_get_builtin_optmap (MNT_LINUX_MAP));
1033 mnt_optstr_get_flags (mount_options, &userspace_flags, mnt_get_builtin_optmap (MNT_USERSPACE_MAP));
1034
1035 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1036 if (mount_flags & MS_BIND)
1037 {
1038 g_free (mount_options);
1039 continue;
1040 }
1041
1042 is_read_only = (mount_flags & MS_RDONLY) != 0;
1043 is_loopback = (userspace_flags & MNT_MS_LOOP) != 0;
1044
1045 if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
1046 ((userspace_flags & MNT_MS_USER) &&
1047 (g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
1048 (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
1049 (userspace_flags & MNT_MS_USERS) ||
1050 (userspace_flags & MNT_MS_OWNER))
1051 {
1052 is_user_mountable = TRUE;
1053 }
1054 }
1055
1056 device_path = mnt_fs_get_source (fs);
1057 if (g_strcmp0 (device_path, "/dev/root") == 0)
1058 device_path = _resolve_dev_root ();
1059
1060 mount_point = create_unix_mount_point (device_path,
1061 mount_path,
1062 mount_fstype,
1063 mount_options,
1064 is_read_only,
1065 is_user_mountable,
1066 is_loopback);
1067 if (mount_options)
1068 g_free (mount_options);
1069
1070 return_list = g_list_prepend (return_list, mount_point);
1071 }
1072 mnt_free_iter (iter);
1073
1074 out:
1075 mnt_free_table (table);
1076
1077 return g_list_reverse (return_list);
1078 }
1079
1080 #else
1081
1082 static GList *
_g_get_unix_mount_points(void)1083 _g_get_unix_mount_points (void)
1084 {
1085 #ifdef HAVE_GETMNTENT_R
1086 struct mntent ent;
1087 char buf[1024];
1088 #endif
1089 struct mntent *mntent;
1090 FILE *file;
1091 char *read_file;
1092 GUnixMountPoint *mount_point;
1093 GList *return_list;
1094
1095 read_file = get_fstab_file ();
1096
1097 file = setmntent (read_file, "r");
1098 if (file == NULL)
1099 return NULL;
1100
1101 return_list = NULL;
1102
1103 #ifdef HAVE_GETMNTENT_R
1104 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
1105 #else
1106 G_LOCK (getmntent);
1107 while ((mntent = getmntent (file)) != NULL)
1108 #endif
1109 {
1110 const char *device_path = NULL;
1111 gboolean is_read_only = FALSE;
1112 gboolean is_user_mountable = FALSE;
1113 gboolean is_loopback = FALSE;
1114
1115 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
1116 (strcmp (mntent->mnt_dir, "swap") == 0) ||
1117 (strcmp (mntent->mnt_dir, "none") == 0))
1118 continue;
1119
1120 #ifdef HAVE_HASMNTOPT
1121 /* We ignore bind fstab entries, as we ignore bind mounts anyway */
1122 if (hasmntopt (mntent, "bind"))
1123 continue;
1124 #endif
1125
1126 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
1127 device_path = _resolve_dev_root ();
1128 else
1129 device_path = mntent->mnt_fsname;
1130
1131 #ifdef HAVE_HASMNTOPT
1132 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
1133 is_read_only = TRUE;
1134
1135 if (hasmntopt (mntent, "loop") != NULL)
1136 is_loopback = TRUE;
1137
1138 #endif
1139
1140 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
1141 #ifdef HAVE_HASMNTOPT
1142 || (hasmntopt (mntent, "user") != NULL
1143 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
1144 || hasmntopt (mntent, "pamconsole") != NULL
1145 || hasmntopt (mntent, "users") != NULL
1146 || hasmntopt (mntent, "owner") != NULL
1147 #endif
1148 )
1149 is_user_mountable = TRUE;
1150
1151 mount_point = create_unix_mount_point (device_path,
1152 mntent->mnt_dir,
1153 mntent->mnt_type,
1154 mntent->mnt_opts,
1155 is_read_only,
1156 is_user_mountable,
1157 is_loopback);
1158
1159 return_list = g_list_prepend (return_list, mount_point);
1160 }
1161
1162 endmntent (file);
1163
1164 #ifndef HAVE_GETMNTENT_R
1165 G_UNLOCK (getmntent);
1166 #endif
1167
1168 return g_list_reverse (return_list);
1169 }
1170
1171 #endif /* HAVE_LIBMOUNT */
1172
1173 /* mnttab.h {{{2 */
1174 #elif defined (HAVE_SYS_MNTTAB_H)
1175
1176 static GList *
_g_get_unix_mount_points(void)1177 _g_get_unix_mount_points (void)
1178 {
1179 struct mnttab mntent;
1180 FILE *file;
1181 char *read_file;
1182 GUnixMountPoint *mount_point;
1183 GList *return_list;
1184
1185 read_file = get_fstab_file ();
1186
1187 file = setmntent (read_file, "r");
1188 if (file == NULL)
1189 return NULL;
1190
1191 return_list = NULL;
1192
1193 G_LOCK (getmntent);
1194 while (! getmntent (file, &mntent))
1195 {
1196 gboolean is_read_only = FALSE;
1197 gboolean is_user_mountable = FALSE;
1198 gboolean is_loopback = FALSE;
1199
1200 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
1201 (strcmp (mntent.mnt_mountp, "swap") == 0) ||
1202 (strcmp (mntent.mnt_mountp, "none") == 0))
1203 continue;
1204
1205 #ifdef HAVE_HASMNTOPT
1206 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
1207 is_read_only = TRUE;
1208
1209 if (hasmntopt (&mntent, "lofs") != NULL)
1210 is_loopback = TRUE;
1211 #endif
1212
1213 if ((mntent.mnt_fstype != NULL)
1214 #ifdef HAVE_HASMNTOPT
1215 || (hasmntopt (&mntent, "user") != NULL
1216 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
1217 || hasmntopt (&mntent, "pamconsole") != NULL
1218 || hasmntopt (&mntent, "users") != NULL
1219 || hasmntopt (&mntent, "owner") != NULL
1220 #endif
1221 )
1222 is_user_mountable = TRUE;
1223
1224 mount_point = create_unix_mount_point (mntent.mnt_special,
1225 mntent.mnt_mountp,
1226 mntent.mnt_fstype,
1227 mntent.mnt_mntopts,
1228 is_read_only,
1229 is_user_mountable,
1230 is_loopback);
1231
1232 return_list = g_list_prepend (return_list, mount_point);
1233 }
1234
1235 endmntent (file);
1236 G_UNLOCK (getmntent);
1237
1238 return g_list_reverse (return_list);
1239 }
1240
1241 /* mntctl.h (AIX) {{{2 */
1242 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
1243
1244 /* functions to parse /etc/filesystems on aix */
1245
1246 /* read character, ignoring comments (begin with '*', end with '\n' */
1247 static int
aix_fs_getc(FILE * fd)1248 aix_fs_getc (FILE *fd)
1249 {
1250 int c;
1251
1252 while ((c = getc (fd)) == '*')
1253 {
1254 while (((c = getc (fd)) != '\n') && (c != EOF))
1255 ;
1256 }
1257 }
1258
1259 /* eat all continuous spaces in a file */
1260 static int
aix_fs_ignorespace(FILE * fd)1261 aix_fs_ignorespace (FILE *fd)
1262 {
1263 int c;
1264
1265 while ((c = aix_fs_getc (fd)) != EOF)
1266 {
1267 if (!g_ascii_isspace (c))
1268 {
1269 ungetc (c,fd);
1270 return c;
1271 }
1272 }
1273
1274 return EOF;
1275 }
1276
1277 /* read one word from file */
1278 static int
aix_fs_getword(FILE * fd,char * word)1279 aix_fs_getword (FILE *fd,
1280 char *word)
1281 {
1282 int c;
1283
1284 aix_fs_ignorespace (fd);
1285
1286 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
1287 {
1288 if (c == '"')
1289 {
1290 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
1291 *word++ = c;
1292 else
1293 *word++ = c;
1294 }
1295 }
1296 *word = 0;
1297
1298 return c;
1299 }
1300
1301 typedef struct {
1302 char mnt_mount[PATH_MAX];
1303 char mnt_special[PATH_MAX];
1304 char mnt_fstype[16];
1305 char mnt_options[128];
1306 } AixMountTableEntry;
1307
1308 /* read mount points properties */
1309 static int
aix_fs_get(FILE * fd,AixMountTableEntry * prop)1310 aix_fs_get (FILE *fd,
1311 AixMountTableEntry *prop)
1312 {
1313 static char word[PATH_MAX] = { 0 };
1314 char value[PATH_MAX];
1315
1316 /* read stanza */
1317 if (word[0] == 0)
1318 {
1319 if (aix_fs_getword (fd, word) == EOF)
1320 return EOF;
1321 }
1322
1323 word[strlen(word) - 1] = 0;
1324 strcpy (prop->mnt_mount, word);
1325
1326 /* read attributes and value */
1327
1328 while (aix_fs_getword (fd, word) != EOF)
1329 {
1330 /* test if is attribute or new stanza */
1331 if (word[strlen(word) - 1] == ':')
1332 return 0;
1333
1334 /* read "=" */
1335 aix_fs_getword (fd, value);
1336
1337 /* read value */
1338 aix_fs_getword (fd, value);
1339
1340 if (strcmp (word, "dev") == 0)
1341 strcpy (prop->mnt_special, value);
1342 else if (strcmp (word, "vfs") == 0)
1343 strcpy (prop->mnt_fstype, value);
1344 else if (strcmp (word, "options") == 0)
1345 strcpy(prop->mnt_options, value);
1346 }
1347
1348 return 0;
1349 }
1350
1351 static GList *
_g_get_unix_mount_points(void)1352 _g_get_unix_mount_points (void)
1353 {
1354 struct mntent *mntent;
1355 FILE *file;
1356 char *read_file;
1357 GUnixMountPoint *mount_point;
1358 AixMountTableEntry mntent;
1359 GList *return_list;
1360
1361 read_file = get_fstab_file ();
1362
1363 file = setmntent (read_file, "r");
1364 if (file == NULL)
1365 return NULL;
1366
1367 return_list = NULL;
1368
1369 while (!aix_fs_get (file, &mntent))
1370 {
1371 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
1372 {
1373 mount_point = create_unix_mount_point (mntent.mnt_special,
1374 mntent.mnt_mount,
1375 mntent.mnt_fstype,
1376 mntent.mnt_options,
1377 TRUE,
1378 TRUE,
1379 FALSE);
1380
1381 return_list = g_list_prepend (return_list, mount_point);
1382 }
1383 }
1384
1385 endmntent (file);
1386
1387 return g_list_reverse (return_list);
1388 }
1389
1390 #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
1391
1392 static GList *
_g_get_unix_mount_points(void)1393 _g_get_unix_mount_points (void)
1394 {
1395 struct fstab *fstab = NULL;
1396 GUnixMountPoint *mount_point;
1397 GList *return_list;
1398 #ifdef HAVE_SYS_SYSCTL_H
1399 int usermnt = 0;
1400 struct stat sb;
1401 #endif
1402
1403 if (!setfsent ())
1404 return NULL;
1405
1406 return_list = NULL;
1407
1408 #ifdef HAVE_SYS_SYSCTL_H
1409 #if defined(HAVE_SYSCTLBYNAME)
1410 {
1411 size_t len = sizeof(usermnt);
1412
1413 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
1414 }
1415 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
1416 {
1417 int mib[2];
1418 size_t len = sizeof(usermnt);
1419
1420 mib[0] = CTL_VFS;
1421 mib[1] = VFS_USERMOUNT;
1422 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1423 }
1424 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
1425 {
1426 int mib[2];
1427 size_t len = sizeof(usermnt);
1428
1429 mib[0] = CTL_KERN;
1430 mib[1] = KERN_USERMOUNT;
1431 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1432 }
1433 #endif
1434 #endif
1435
1436 while ((fstab = getfsent ()) != NULL)
1437 {
1438 gboolean is_read_only = FALSE;
1439 gboolean is_user_mountable = FALSE;
1440
1441 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1442 continue;
1443
1444 if (strcmp (fstab->fs_type, "ro") == 0)
1445 is_read_only = TRUE;
1446
1447 #ifdef HAVE_SYS_SYSCTL_H
1448 if (usermnt != 0)
1449 {
1450 uid_t uid = getuid ();
1451 if (stat (fstab->fs_file, &sb) == 0)
1452 {
1453 if (uid == 0 || sb.st_uid == uid)
1454 is_user_mountable = TRUE;
1455 }
1456 }
1457 #endif
1458
1459 mount_point = create_unix_mount_point (fstab->fs_spec,
1460 fstab->fs_file,
1461 fstab->fs_vfstype,
1462 fstab->fs_mntops,
1463 is_read_only,
1464 is_user_mountable,
1465 FALSE);
1466
1467 return_list = g_list_prepend (return_list, mount_point);
1468 }
1469
1470 endfsent ();
1471
1472 return g_list_reverse (return_list);
1473 }
1474 /* Interix {{{2 */
1475 #elif defined(__INTERIX)
1476 static GList *
_g_get_unix_mount_points(void)1477 _g_get_unix_mount_points (void)
1478 {
1479 return _g_get_unix_mounts ();
1480 }
1481
1482 /* Common code {{{2 */
1483 #else
1484 #error No g_get_mount_table() implementation for system
1485 #endif
1486
1487 static guint64
get_mounts_timestamp(void)1488 get_mounts_timestamp (void)
1489 {
1490 const char *monitor_file;
1491 struct stat buf;
1492 guint64 timestamp = 0;
1493
1494 G_LOCK (proc_mounts_source);
1495
1496 monitor_file = get_mtab_monitor_file ();
1497 /* Don't return mtime for /proc/ files */
1498 if (monitor_file && !g_str_has_prefix (monitor_file, "/proc/"))
1499 {
1500 if (stat (monitor_file, &buf) == 0)
1501 timestamp = buf.st_mtime;
1502 }
1503 else if (proc_mounts_watch_is_running ())
1504 {
1505 /* it's being monitored by poll, so return mount_poller_time */
1506 timestamp = mount_poller_time;
1507 }
1508 else
1509 {
1510 /* Case of /proc/ file not being monitored - Be on the safe side and
1511 * send a new timestamp to force g_unix_mounts_changed_since() to
1512 * return TRUE so any application caches depending on it (like eg.
1513 * the one in GIO) get invalidated and don't hold possibly outdated
1514 * data - see Bug 787731 */
1515 timestamp = g_get_monotonic_time ();
1516 }
1517
1518 G_UNLOCK (proc_mounts_source);
1519
1520 return timestamp;
1521 }
1522
1523 static guint64
get_mount_points_timestamp(void)1524 get_mount_points_timestamp (void)
1525 {
1526 const char *monitor_file;
1527 struct stat buf;
1528
1529 monitor_file = get_fstab_file ();
1530 if (monitor_file)
1531 {
1532 if (stat (monitor_file, &buf) == 0)
1533 return (guint64)buf.st_mtime;
1534 }
1535 return 0;
1536 }
1537
1538 /**
1539 * g_unix_mounts_get:
1540 * @time_read: (out) (optional): guint64 to contain a timestamp, or %NULL
1541 *
1542 * Gets a #GList of #GUnixMountEntry containing the unix mounts.
1543 * If @time_read is set, it will be filled with the mount
1544 * timestamp, allowing for checking if the mounts have changed
1545 * with g_unix_mounts_changed_since().
1546 *
1547 * Returns: (element-type GUnixMountEntry) (transfer full):
1548 * a #GList of the UNIX mounts.
1549 **/
1550 GList *
g_unix_mounts_get(guint64 * time_read)1551 g_unix_mounts_get (guint64 *time_read)
1552 {
1553 if (time_read)
1554 *time_read = get_mounts_timestamp ();
1555
1556 return _g_get_unix_mounts ();
1557 }
1558
1559 /**
1560 * g_unix_mount_at:
1561 * @mount_path: (type filename): path for a possible unix mount.
1562 * @time_read: (out) (optional): guint64 to contain a timestamp.
1563 *
1564 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1565 * is set, it will be filled with a unix timestamp for checking
1566 * if the mounts have changed since with g_unix_mounts_changed_since().
1567 *
1568 * If more mounts have the same mount path, the last matching mount
1569 * is returned.
1570 *
1571 * Returns: (transfer full): a #GUnixMountEntry.
1572 **/
1573 GUnixMountEntry *
g_unix_mount_at(const char * mount_path,guint64 * time_read)1574 g_unix_mount_at (const char *mount_path,
1575 guint64 *time_read)
1576 {
1577 GList *mounts, *l;
1578 GUnixMountEntry *mount_entry, *found;
1579
1580 mounts = g_unix_mounts_get (time_read);
1581
1582 found = NULL;
1583 for (l = mounts; l != NULL; l = l->next)
1584 {
1585 mount_entry = l->data;
1586
1587 if (strcmp (mount_path, mount_entry->mount_path) == 0)
1588 {
1589 if (found != NULL)
1590 g_unix_mount_free (found);
1591
1592 found = mount_entry;
1593 }
1594 else
1595 g_unix_mount_free (mount_entry);
1596 }
1597 g_list_free (mounts);
1598
1599 return found;
1600 }
1601
1602 /**
1603 * g_unix_mount_for:
1604 * @file_path: (type filename): file path on some unix mount.
1605 * @time_read: (out) (optional): guint64 to contain a timestamp.
1606 *
1607 * Gets a #GUnixMountEntry for a given file path. If @time_read
1608 * is set, it will be filled with a unix timestamp for checking
1609 * if the mounts have changed since with g_unix_mounts_changed_since().
1610 *
1611 * If more mounts have the same mount path, the last matching mount
1612 * is returned.
1613 *
1614 * Returns: (transfer full): a #GUnixMountEntry.
1615 *
1616 * Since: 2.52
1617 **/
1618 GUnixMountEntry *
g_unix_mount_for(const char * file_path,guint64 * time_read)1619 g_unix_mount_for (const char *file_path,
1620 guint64 *time_read)
1621 {
1622 GUnixMountEntry *entry;
1623
1624 g_return_val_if_fail (file_path != NULL, NULL);
1625
1626 entry = g_unix_mount_at (file_path, time_read);
1627 if (entry == NULL)
1628 {
1629 char *topdir;
1630
1631 topdir = _g_local_file_find_topdir_for (file_path);
1632 if (topdir != NULL)
1633 {
1634 entry = g_unix_mount_at (topdir, time_read);
1635 g_free (topdir);
1636 }
1637 }
1638
1639 return entry;
1640 }
1641
1642 /**
1643 * g_unix_mount_points_get:
1644 * @time_read: (out) (optional): guint64 to contain a timestamp.
1645 *
1646 * Gets a #GList of #GUnixMountPoint containing the unix mount points.
1647 * If @time_read is set, it will be filled with the mount timestamp,
1648 * allowing for checking if the mounts have changed with
1649 * g_unix_mount_points_changed_since().
1650 *
1651 * Returns: (element-type GUnixMountPoint) (transfer full):
1652 * a #GList of the UNIX mountpoints.
1653 **/
1654 GList *
g_unix_mount_points_get(guint64 * time_read)1655 g_unix_mount_points_get (guint64 *time_read)
1656 {
1657 if (time_read)
1658 *time_read = get_mount_points_timestamp ();
1659
1660 return _g_get_unix_mount_points ();
1661 }
1662
1663 /**
1664 * g_unix_mounts_changed_since:
1665 * @time: guint64 to contain a timestamp.
1666 *
1667 * Checks if the unix mounts have changed since a given unix time.
1668 *
1669 * Returns: %TRUE if the mounts have changed since @time.
1670 **/
1671 gboolean
g_unix_mounts_changed_since(guint64 time)1672 g_unix_mounts_changed_since (guint64 time)
1673 {
1674 return get_mounts_timestamp () != time;
1675 }
1676
1677 /**
1678 * g_unix_mount_points_changed_since:
1679 * @time: guint64 to contain a timestamp.
1680 *
1681 * Checks if the unix mount points have changed since a given unix time.
1682 *
1683 * Returns: %TRUE if the mount points have changed since @time.
1684 **/
1685 gboolean
g_unix_mount_points_changed_since(guint64 time)1686 g_unix_mount_points_changed_since (guint64 time)
1687 {
1688 return get_mount_points_timestamp () != time;
1689 }
1690
1691 /* GUnixMountMonitor {{{1 */
1692
1693 enum {
1694 MOUNTS_CHANGED,
1695 MOUNTPOINTS_CHANGED,
1696 LAST_SIGNAL
1697 };
1698
1699 static guint signals[LAST_SIGNAL];
1700
1701 struct _GUnixMountMonitor {
1702 GObject parent;
1703
1704 GMainContext *context;
1705 };
1706
1707 struct _GUnixMountMonitorClass {
1708 GObjectClass parent_class;
1709 };
1710
1711
1712 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT)
1713
1714 static GContextSpecificGroup mount_monitor_group;
1715 static GFileMonitor *fstab_monitor;
1716 static GFileMonitor *mtab_monitor;
1717 static GList *mount_poller_mounts;
1718 static guint mtab_file_changed_id;
1719
1720 /* Called with proc_mounts_source lock held. */
1721 static gboolean
proc_mounts_watch_is_running(void)1722 proc_mounts_watch_is_running (void)
1723 {
1724 return proc_mounts_watch_source != NULL &&
1725 !g_source_is_destroyed (proc_mounts_watch_source);
1726 }
1727
1728 static void
fstab_file_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,gpointer user_data)1729 fstab_file_changed (GFileMonitor *monitor,
1730 GFile *file,
1731 GFile *other_file,
1732 GFileMonitorEvent event_type,
1733 gpointer user_data)
1734 {
1735 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1736 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1737 event_type != G_FILE_MONITOR_EVENT_DELETED)
1738 return;
1739
1740 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1741 }
1742
1743 static gboolean
mtab_file_changed_cb(gpointer user_data)1744 mtab_file_changed_cb (gpointer user_data)
1745 {
1746 mtab_file_changed_id = 0;
1747 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1748
1749 return G_SOURCE_REMOVE;
1750 }
1751
1752 static void
mtab_file_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,gpointer user_data)1753 mtab_file_changed (GFileMonitor *monitor,
1754 GFile *file,
1755 GFile *other_file,
1756 GFileMonitorEvent event_type,
1757 gpointer user_data)
1758 {
1759 GMainContext *context;
1760 GSource *source;
1761
1762 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1763 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1764 event_type != G_FILE_MONITOR_EVENT_DELETED)
1765 return;
1766
1767 /* Skip accumulated events from file monitor which we are not able to handle
1768 * in a real time instead of emitting mounts_changed signal several times.
1769 * This should behave equally to GIOChannel based monitoring. See Bug 792235.
1770 */
1771 if (mtab_file_changed_id > 0)
1772 return;
1773
1774 context = g_main_context_get_thread_default ();
1775 if (!context)
1776 context = g_main_context_default ();
1777
1778 source = g_idle_source_new ();
1779 g_source_set_priority (source, G_PRIORITY_DEFAULT);
1780 g_source_set_callback (source, mtab_file_changed_cb, NULL, NULL);
1781 g_source_set_name (source, "[gio] mtab_file_changed_cb");
1782 g_source_attach (source, context);
1783 g_source_unref (source);
1784 }
1785
1786 static gboolean
proc_mounts_changed(GIOChannel * channel,GIOCondition cond,gpointer user_data)1787 proc_mounts_changed (GIOChannel *channel,
1788 GIOCondition cond,
1789 gpointer user_data)
1790 {
1791 if (cond & G_IO_ERR)
1792 {
1793 G_LOCK (proc_mounts_source);
1794 mount_poller_time = (guint64) g_get_monotonic_time ();
1795 G_UNLOCK (proc_mounts_source);
1796
1797 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
1798 }
1799
1800 return TRUE;
1801 }
1802
1803 static gboolean
mount_change_poller(gpointer user_data)1804 mount_change_poller (gpointer user_data)
1805 {
1806 GList *current_mounts, *new_it, *old_it;
1807 gboolean has_changed = FALSE;
1808
1809 current_mounts = _g_get_unix_mounts ();
1810
1811 for ( new_it = current_mounts, old_it = mount_poller_mounts;
1812 new_it != NULL && old_it != NULL;
1813 new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
1814 {
1815 if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
1816 {
1817 has_changed = TRUE;
1818 break;
1819 }
1820 }
1821 if (!(new_it == NULL && old_it == NULL))
1822 has_changed = TRUE;
1823
1824 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1825
1826 mount_poller_mounts = current_mounts;
1827
1828 if (has_changed)
1829 {
1830 G_LOCK (proc_mounts_source);
1831 mount_poller_time = (guint64) g_get_monotonic_time ();
1832 G_UNLOCK (proc_mounts_source);
1833
1834 g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
1835 }
1836
1837 return TRUE;
1838 }
1839
1840
1841 static void
mount_monitor_stop(void)1842 mount_monitor_stop (void)
1843 {
1844 if (fstab_monitor)
1845 {
1846 g_file_monitor_cancel (fstab_monitor);
1847 g_object_unref (fstab_monitor);
1848 }
1849
1850 G_LOCK (proc_mounts_source);
1851 if (proc_mounts_watch_source != NULL)
1852 {
1853 g_source_destroy (proc_mounts_watch_source);
1854 proc_mounts_watch_source = NULL;
1855 }
1856 G_UNLOCK (proc_mounts_source);
1857
1858 if (mtab_monitor)
1859 {
1860 g_file_monitor_cancel (mtab_monitor);
1861 g_object_unref (mtab_monitor);
1862 }
1863
1864 if (mtab_file_changed_id)
1865 {
1866 g_source_remove (mtab_file_changed_id);
1867 mtab_file_changed_id = 0;
1868 }
1869
1870 g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
1871 }
1872
1873 static void
mount_monitor_start(void)1874 mount_monitor_start (void)
1875 {
1876 GFile *file;
1877
1878 if (get_fstab_file () != NULL)
1879 {
1880 file = g_file_new_for_path (get_fstab_file ());
1881 fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1882 g_object_unref (file);
1883
1884 g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
1885 }
1886
1887 if (get_mtab_monitor_file () != NULL)
1888 {
1889 const gchar *mtab_path;
1890
1891 mtab_path = get_mtab_monitor_file ();
1892 /* Monitoring files in /proc/ is special - can't just use GFileMonitor.
1893 * See 'man proc' for more details.
1894 */
1895 if (g_str_has_prefix (mtab_path, "/proc/"))
1896 {
1897 GIOChannel *proc_mounts_channel;
1898 GError *error = NULL;
1899 proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
1900 if (proc_mounts_channel == NULL)
1901 {
1902 g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
1903 error->message, g_quark_to_string (error->domain), error->code);
1904 g_error_free (error);
1905 }
1906 else
1907 {
1908 G_LOCK (proc_mounts_source);
1909
1910 proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
1911 mount_poller_time = (guint64) g_get_monotonic_time ();
1912 g_source_set_callback (proc_mounts_watch_source,
1913 (GSourceFunc) proc_mounts_changed,
1914 NULL, NULL);
1915 g_source_attach (proc_mounts_watch_source,
1916 g_main_context_get_thread_default ());
1917 g_source_unref (proc_mounts_watch_source);
1918 g_io_channel_unref (proc_mounts_channel);
1919
1920 G_UNLOCK (proc_mounts_source);
1921 }
1922 }
1923 else
1924 {
1925 file = g_file_new_for_path (mtab_path);
1926 mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1927 g_object_unref (file);
1928 g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
1929 }
1930 }
1931 else
1932 {
1933 G_LOCK (proc_mounts_source);
1934
1935 proc_mounts_watch_source = g_timeout_source_new_seconds (3);
1936 mount_poller_mounts = _g_get_unix_mounts ();
1937 mount_poller_time = (guint64)g_get_monotonic_time ();
1938 g_source_set_callback (proc_mounts_watch_source,
1939 mount_change_poller,
1940 NULL, NULL);
1941 g_source_attach (proc_mounts_watch_source,
1942 g_main_context_get_thread_default ());
1943 g_source_unref (proc_mounts_watch_source);
1944
1945 G_UNLOCK (proc_mounts_source);
1946 }
1947 }
1948
1949 static void
g_unix_mount_monitor_finalize(GObject * object)1950 g_unix_mount_monitor_finalize (GObject *object)
1951 {
1952 GUnixMountMonitor *monitor;
1953
1954 monitor = G_UNIX_MOUNT_MONITOR (object);
1955
1956 g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
1957
1958 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1959 }
1960
1961 static void
g_unix_mount_monitor_class_init(GUnixMountMonitorClass * klass)1962 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1963 {
1964 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1965
1966 gobject_class->finalize = g_unix_mount_monitor_finalize;
1967
1968 /**
1969 * GUnixMountMonitor::mounts-changed:
1970 * @monitor: the object on which the signal is emitted
1971 *
1972 * Emitted when the unix mounts have changed.
1973 */
1974 signals[MOUNTS_CHANGED] =
1975 g_signal_new (I_("mounts-changed"),
1976 G_TYPE_FROM_CLASS (klass),
1977 G_SIGNAL_RUN_LAST,
1978 0,
1979 NULL, NULL,
1980 NULL,
1981 G_TYPE_NONE, 0);
1982
1983 /**
1984 * GUnixMountMonitor::mountpoints-changed:
1985 * @monitor: the object on which the signal is emitted
1986 *
1987 * Emitted when the unix mount points have changed.
1988 */
1989 signals[MOUNTPOINTS_CHANGED] =
1990 g_signal_new (I_("mountpoints-changed"),
1991 G_TYPE_FROM_CLASS (klass),
1992 G_SIGNAL_RUN_LAST,
1993 0,
1994 NULL, NULL,
1995 NULL,
1996 G_TYPE_NONE, 0);
1997 }
1998
1999 static void
g_unix_mount_monitor_init(GUnixMountMonitor * monitor)2000 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
2001 {
2002 }
2003
2004 /**
2005 * g_unix_mount_monitor_set_rate_limit:
2006 * @mount_monitor: a #GUnixMountMonitor
2007 * @limit_msec: a integer with the limit in milliseconds to
2008 * poll for changes.
2009 *
2010 * This function does nothing.
2011 *
2012 * Before 2.44, this was a partially-effective way of controlling the
2013 * rate at which events would be reported under some uncommon
2014 * circumstances. Since @mount_monitor is a singleton, it also meant
2015 * that calling this function would have side effects for other users of
2016 * the monitor.
2017 *
2018 * Since: 2.18
2019 *
2020 * Deprecated:2.44:This function does nothing. Don't call it.
2021 */
2022 void
g_unix_mount_monitor_set_rate_limit(GUnixMountMonitor * mount_monitor,gint limit_msec)2023 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
2024 gint limit_msec)
2025 {
2026 }
2027
2028 /**
2029 * g_unix_mount_monitor_get:
2030 *
2031 * Gets the #GUnixMountMonitor for the current thread-default main
2032 * context.
2033 *
2034 * The mount monitor can be used to monitor for changes to the list of
2035 * mounted filesystems as well as the list of mount points (ie: fstab
2036 * entries).
2037 *
2038 * You must only call g_object_unref() on the return value from under
2039 * the same main context as you called this function.
2040 *
2041 * Returns: (transfer full): the #GUnixMountMonitor.
2042 *
2043 * Since: 2.44
2044 **/
2045 GUnixMountMonitor *
g_unix_mount_monitor_get(void)2046 g_unix_mount_monitor_get (void)
2047 {
2048 return g_context_specific_group_get (&mount_monitor_group,
2049 G_TYPE_UNIX_MOUNT_MONITOR,
2050 G_STRUCT_OFFSET(GUnixMountMonitor, context),
2051 mount_monitor_start);
2052 }
2053
2054 /**
2055 * g_unix_mount_monitor_new:
2056 *
2057 * Deprecated alias for g_unix_mount_monitor_get().
2058 *
2059 * This function was never a true constructor, which is why it was
2060 * renamed.
2061 *
2062 * Returns: a #GUnixMountMonitor.
2063 *
2064 * Deprecated:2.44:Use g_unix_mount_monitor_get() instead.
2065 */
2066 GUnixMountMonitor *
g_unix_mount_monitor_new(void)2067 g_unix_mount_monitor_new (void)
2068 {
2069 return g_unix_mount_monitor_get ();
2070 }
2071
2072 /* GUnixMount {{{1 */
2073 /**
2074 * g_unix_mount_free:
2075 * @mount_entry: a #GUnixMountEntry.
2076 *
2077 * Frees a unix mount.
2078 */
2079 void
g_unix_mount_free(GUnixMountEntry * mount_entry)2080 g_unix_mount_free (GUnixMountEntry *mount_entry)
2081 {
2082 g_return_if_fail (mount_entry != NULL);
2083
2084 g_free (mount_entry->mount_path);
2085 g_free (mount_entry->device_path);
2086 g_free (mount_entry->root_path);
2087 g_free (mount_entry->filesystem_type);
2088 g_free (mount_entry->options);
2089 g_free (mount_entry);
2090 }
2091
2092 /**
2093 * g_unix_mount_copy:
2094 * @mount_entry: a #GUnixMountEntry.
2095 *
2096 * Makes a copy of @mount_entry.
2097 *
2098 * Returns: (transfer full): a new #GUnixMountEntry
2099 *
2100 * Since: 2.54
2101 */
2102 GUnixMountEntry *
g_unix_mount_copy(GUnixMountEntry * mount_entry)2103 g_unix_mount_copy (GUnixMountEntry *mount_entry)
2104 {
2105 GUnixMountEntry *copy;
2106
2107 g_return_val_if_fail (mount_entry != NULL, NULL);
2108
2109 copy = g_new0 (GUnixMountEntry, 1);
2110 copy->mount_path = g_strdup (mount_entry->mount_path);
2111 copy->device_path = g_strdup (mount_entry->device_path);
2112 copy->root_path = g_strdup (mount_entry->root_path);
2113 copy->filesystem_type = g_strdup (mount_entry->filesystem_type);
2114 copy->options = g_strdup (mount_entry->options);
2115 copy->is_read_only = mount_entry->is_read_only;
2116 copy->is_system_internal = mount_entry->is_system_internal;
2117
2118 return copy;
2119 }
2120
2121 /**
2122 * g_unix_mount_point_free:
2123 * @mount_point: unix mount point to free.
2124 *
2125 * Frees a unix mount point.
2126 */
2127 void
g_unix_mount_point_free(GUnixMountPoint * mount_point)2128 g_unix_mount_point_free (GUnixMountPoint *mount_point)
2129 {
2130 g_return_if_fail (mount_point != NULL);
2131
2132 g_free (mount_point->mount_path);
2133 g_free (mount_point->device_path);
2134 g_free (mount_point->filesystem_type);
2135 g_free (mount_point->options);
2136 g_free (mount_point);
2137 }
2138
2139 /**
2140 * g_unix_mount_point_copy:
2141 * @mount_point: a #GUnixMountPoint.
2142 *
2143 * Makes a copy of @mount_point.
2144 *
2145 * Returns: (transfer full): a new #GUnixMountPoint
2146 *
2147 * Since: 2.54
2148 */
2149 GUnixMountPoint*
g_unix_mount_point_copy(GUnixMountPoint * mount_point)2150 g_unix_mount_point_copy (GUnixMountPoint *mount_point)
2151 {
2152 GUnixMountPoint *copy;
2153
2154 g_return_val_if_fail (mount_point != NULL, NULL);
2155
2156 copy = g_new0 (GUnixMountPoint, 1);
2157 copy->mount_path = g_strdup (mount_point->mount_path);
2158 copy->device_path = g_strdup (mount_point->device_path);
2159 copy->filesystem_type = g_strdup (mount_point->filesystem_type);
2160 copy->options = g_strdup (mount_point->options);
2161 copy->is_read_only = mount_point->is_read_only;
2162 copy->is_user_mountable = mount_point->is_user_mountable;
2163 copy->is_loopback = mount_point->is_loopback;
2164
2165 return copy;
2166 }
2167
2168 /**
2169 * g_unix_mount_compare:
2170 * @mount1: first #GUnixMountEntry to compare.
2171 * @mount2: second #GUnixMountEntry to compare.
2172 *
2173 * Compares two unix mounts.
2174 *
2175 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2176 * or less than @mount2, respectively.
2177 */
2178 gint
g_unix_mount_compare(GUnixMountEntry * mount1,GUnixMountEntry * mount2)2179 g_unix_mount_compare (GUnixMountEntry *mount1,
2180 GUnixMountEntry *mount2)
2181 {
2182 int res;
2183
2184 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2185
2186 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2187 if (res != 0)
2188 return res;
2189
2190 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2191 if (res != 0)
2192 return res;
2193
2194 res = g_strcmp0 (mount1->root_path, mount2->root_path);
2195 if (res != 0)
2196 return res;
2197
2198 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2199 if (res != 0)
2200 return res;
2201
2202 res = g_strcmp0 (mount1->options, mount2->options);
2203 if (res != 0)
2204 return res;
2205
2206 res = mount1->is_read_only - mount2->is_read_only;
2207 if (res != 0)
2208 return res;
2209
2210 return 0;
2211 }
2212
2213 /**
2214 * g_unix_mount_get_mount_path:
2215 * @mount_entry: input #GUnixMountEntry to get the mount path for.
2216 *
2217 * Gets the mount path for a unix mount.
2218 *
2219 * Returns: (type filename): the mount path for @mount_entry.
2220 */
2221 const gchar *
g_unix_mount_get_mount_path(GUnixMountEntry * mount_entry)2222 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
2223 {
2224 g_return_val_if_fail (mount_entry != NULL, NULL);
2225
2226 return mount_entry->mount_path;
2227 }
2228
2229 /**
2230 * g_unix_mount_get_device_path:
2231 * @mount_entry: a #GUnixMount.
2232 *
2233 * Gets the device path for a unix mount.
2234 *
2235 * Returns: (type filename): a string containing the device path.
2236 */
2237 const gchar *
g_unix_mount_get_device_path(GUnixMountEntry * mount_entry)2238 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
2239 {
2240 g_return_val_if_fail (mount_entry != NULL, NULL);
2241
2242 return mount_entry->device_path;
2243 }
2244
2245 /**
2246 * g_unix_mount_get_root_path:
2247 * @mount_entry: a #GUnixMountEntry.
2248 *
2249 * Gets the root of the mount within the filesystem. This is useful e.g. for
2250 * mounts created by bind operation, or btrfs subvolumes.
2251 *
2252 * For example, the root path is equal to "/" for mount created by
2253 * "mount /dev/sda1 /mnt/foo" and "/bar" for
2254 * "mount --bind /mnt/foo/bar /mnt/bar".
2255 *
2256 * Returns: (nullable): a string containing the root, or %NULL if not supported.
2257 *
2258 * Since: 2.60
2259 */
2260 const gchar *
g_unix_mount_get_root_path(GUnixMountEntry * mount_entry)2261 g_unix_mount_get_root_path (GUnixMountEntry *mount_entry)
2262 {
2263 g_return_val_if_fail (mount_entry != NULL, NULL);
2264
2265 return mount_entry->root_path;
2266 }
2267
2268 /**
2269 * g_unix_mount_get_fs_type:
2270 * @mount_entry: a #GUnixMount.
2271 *
2272 * Gets the filesystem type for the unix mount.
2273 *
2274 * Returns: a string containing the file system type.
2275 */
2276 const gchar *
g_unix_mount_get_fs_type(GUnixMountEntry * mount_entry)2277 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
2278 {
2279 g_return_val_if_fail (mount_entry != NULL, NULL);
2280
2281 return mount_entry->filesystem_type;
2282 }
2283
2284 /**
2285 * g_unix_mount_get_options:
2286 * @mount_entry: a #GUnixMountEntry.
2287 *
2288 * Gets a comma-separated list of mount options for the unix mount. For example,
2289 * `rw,relatime,seclabel,data=ordered`.
2290 *
2291 * This is similar to g_unix_mount_point_get_options(), but it takes
2292 * a #GUnixMountEntry as an argument.
2293 *
2294 * Returns: (nullable): a string containing the options, or %NULL if not
2295 * available.
2296 *
2297 * Since: 2.58
2298 */
2299 const gchar *
g_unix_mount_get_options(GUnixMountEntry * mount_entry)2300 g_unix_mount_get_options (GUnixMountEntry *mount_entry)
2301 {
2302 g_return_val_if_fail (mount_entry != NULL, NULL);
2303
2304 return mount_entry->options;
2305 }
2306
2307 /**
2308 * g_unix_mount_is_readonly:
2309 * @mount_entry: a #GUnixMount.
2310 *
2311 * Checks if a unix mount is mounted read only.
2312 *
2313 * Returns: %TRUE if @mount_entry is read only.
2314 */
2315 gboolean
g_unix_mount_is_readonly(GUnixMountEntry * mount_entry)2316 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
2317 {
2318 g_return_val_if_fail (mount_entry != NULL, FALSE);
2319
2320 return mount_entry->is_read_only;
2321 }
2322
2323 /**
2324 * g_unix_mount_is_system_internal:
2325 * @mount_entry: a #GUnixMount.
2326 *
2327 * Checks if a Unix mount is a system mount. This is the Boolean OR of
2328 * g_unix_is_system_fs_type(), g_unix_is_system_device_path() and
2329 * g_unix_is_mount_path_system_internal() on @mount_entry’s properties.
2330 *
2331 * The definition of what a ‘system’ mount entry is may change over time as new
2332 * file system types and device paths are ignored.
2333 *
2334 * Returns: %TRUE if the unix mount is for a system path.
2335 */
2336 gboolean
g_unix_mount_is_system_internal(GUnixMountEntry * mount_entry)2337 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
2338 {
2339 g_return_val_if_fail (mount_entry != NULL, FALSE);
2340
2341 return mount_entry->is_system_internal;
2342 }
2343
2344 /* GUnixMountPoint {{{1 */
2345 /**
2346 * g_unix_mount_point_compare:
2347 * @mount1: a #GUnixMount.
2348 * @mount2: a #GUnixMount.
2349 *
2350 * Compares two unix mount points.
2351 *
2352 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
2353 * or less than @mount2, respectively.
2354 */
2355 gint
g_unix_mount_point_compare(GUnixMountPoint * mount1,GUnixMountPoint * mount2)2356 g_unix_mount_point_compare (GUnixMountPoint *mount1,
2357 GUnixMountPoint *mount2)
2358 {
2359 int res;
2360
2361 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
2362
2363 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
2364 if (res != 0)
2365 return res;
2366
2367 res = g_strcmp0 (mount1->device_path, mount2->device_path);
2368 if (res != 0)
2369 return res;
2370
2371 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
2372 if (res != 0)
2373 return res;
2374
2375 res = g_strcmp0 (mount1->options, mount2->options);
2376 if (res != 0)
2377 return res;
2378
2379 res = mount1->is_read_only - mount2->is_read_only;
2380 if (res != 0)
2381 return res;
2382
2383 res = mount1->is_user_mountable - mount2->is_user_mountable;
2384 if (res != 0)
2385 return res;
2386
2387 res = mount1->is_loopback - mount2->is_loopback;
2388 if (res != 0)
2389 return res;
2390
2391 return 0;
2392 }
2393
2394 /**
2395 * g_unix_mount_point_get_mount_path:
2396 * @mount_point: a #GUnixMountPoint.
2397 *
2398 * Gets the mount path for a unix mount point.
2399 *
2400 * Returns: (type filename): a string containing the mount path.
2401 */
2402 const gchar *
g_unix_mount_point_get_mount_path(GUnixMountPoint * mount_point)2403 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
2404 {
2405 g_return_val_if_fail (mount_point != NULL, NULL);
2406
2407 return mount_point->mount_path;
2408 }
2409
2410 /**
2411 * g_unix_mount_point_get_device_path:
2412 * @mount_point: a #GUnixMountPoint.
2413 *
2414 * Gets the device path for a unix mount point.
2415 *
2416 * Returns: (type filename): a string containing the device path.
2417 */
2418 const gchar *
g_unix_mount_point_get_device_path(GUnixMountPoint * mount_point)2419 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
2420 {
2421 g_return_val_if_fail (mount_point != NULL, NULL);
2422
2423 return mount_point->device_path;
2424 }
2425
2426 /**
2427 * g_unix_mount_point_get_fs_type:
2428 * @mount_point: a #GUnixMountPoint.
2429 *
2430 * Gets the file system type for the mount point.
2431 *
2432 * Returns: a string containing the file system type.
2433 */
2434 const gchar *
g_unix_mount_point_get_fs_type(GUnixMountPoint * mount_point)2435 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
2436 {
2437 g_return_val_if_fail (mount_point != NULL, NULL);
2438
2439 return mount_point->filesystem_type;
2440 }
2441
2442 /**
2443 * g_unix_mount_point_get_options:
2444 * @mount_point: a #GUnixMountPoint.
2445 *
2446 * Gets the options for the mount point.
2447 *
2448 * Returns: a string containing the options.
2449 *
2450 * Since: 2.32
2451 */
2452 const gchar *
g_unix_mount_point_get_options(GUnixMountPoint * mount_point)2453 g_unix_mount_point_get_options (GUnixMountPoint *mount_point)
2454 {
2455 g_return_val_if_fail (mount_point != NULL, NULL);
2456
2457 return mount_point->options;
2458 }
2459
2460 /**
2461 * g_unix_mount_point_is_readonly:
2462 * @mount_point: a #GUnixMountPoint.
2463 *
2464 * Checks if a unix mount point is read only.
2465 *
2466 * Returns: %TRUE if a mount point is read only.
2467 */
2468 gboolean
g_unix_mount_point_is_readonly(GUnixMountPoint * mount_point)2469 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
2470 {
2471 g_return_val_if_fail (mount_point != NULL, FALSE);
2472
2473 return mount_point->is_read_only;
2474 }
2475
2476 /**
2477 * g_unix_mount_point_is_user_mountable:
2478 * @mount_point: a #GUnixMountPoint.
2479 *
2480 * Checks if a unix mount point is mountable by the user.
2481 *
2482 * Returns: %TRUE if the mount point is user mountable.
2483 */
2484 gboolean
g_unix_mount_point_is_user_mountable(GUnixMountPoint * mount_point)2485 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
2486 {
2487 g_return_val_if_fail (mount_point != NULL, FALSE);
2488
2489 return mount_point->is_user_mountable;
2490 }
2491
2492 /**
2493 * g_unix_mount_point_is_loopback:
2494 * @mount_point: a #GUnixMountPoint.
2495 *
2496 * Checks if a unix mount point is a loopback device.
2497 *
2498 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
2499 */
2500 gboolean
g_unix_mount_point_is_loopback(GUnixMountPoint * mount_point)2501 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
2502 {
2503 g_return_val_if_fail (mount_point != NULL, FALSE);
2504
2505 return mount_point->is_loopback;
2506 }
2507
2508 static GUnixMountType
guess_mount_type(const char * mount_path,const char * device_path,const char * filesystem_type)2509 guess_mount_type (const char *mount_path,
2510 const char *device_path,
2511 const char *filesystem_type)
2512 {
2513 GUnixMountType type;
2514 char *basename;
2515
2516 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
2517
2518 if ((strcmp (filesystem_type, "udf") == 0) ||
2519 (strcmp (filesystem_type, "iso9660") == 0) ||
2520 (strcmp (filesystem_type, "cd9660") == 0))
2521 type = G_UNIX_MOUNT_TYPE_CDROM;
2522 else if ((strcmp (filesystem_type, "nfs") == 0) ||
2523 (strcmp (filesystem_type, "nfs4") == 0))
2524 type = G_UNIX_MOUNT_TYPE_NFS;
2525 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
2526 g_str_has_prefix (device_path, "/dev/fd") ||
2527 g_str_has_prefix (device_path, "/dev/floppy"))
2528 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2529 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
2530 g_str_has_prefix (device_path, "/dev/acd") ||
2531 g_str_has_prefix (device_path, "/dev/cd"))
2532 type = G_UNIX_MOUNT_TYPE_CDROM;
2533 else if (g_str_has_prefix (device_path, "/vol/"))
2534 {
2535 const char *name = mount_path + strlen ("/");
2536
2537 if (g_str_has_prefix (name, "cdrom"))
2538 type = G_UNIX_MOUNT_TYPE_CDROM;
2539 else if (g_str_has_prefix (name, "floppy") ||
2540 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
2541 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2542 else if (g_str_has_prefix (name, "rmdisk"))
2543 type = G_UNIX_MOUNT_TYPE_ZIP;
2544 else if (g_str_has_prefix (name, "jaz"))
2545 type = G_UNIX_MOUNT_TYPE_JAZ;
2546 else if (g_str_has_prefix (name, "memstick"))
2547 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2548 }
2549 else
2550 {
2551 basename = g_path_get_basename (mount_path);
2552
2553 if (g_str_has_prefix (basename, "cdr") ||
2554 g_str_has_prefix (basename, "cdwriter") ||
2555 g_str_has_prefix (basename, "burn") ||
2556 g_str_has_prefix (basename, "dvdr"))
2557 type = G_UNIX_MOUNT_TYPE_CDROM;
2558 else if (g_str_has_prefix (basename, "floppy"))
2559 type = G_UNIX_MOUNT_TYPE_FLOPPY;
2560 else if (g_str_has_prefix (basename, "zip"))
2561 type = G_UNIX_MOUNT_TYPE_ZIP;
2562 else if (g_str_has_prefix (basename, "jaz"))
2563 type = G_UNIX_MOUNT_TYPE_JAZ;
2564 else if (g_str_has_prefix (basename, "camera"))
2565 type = G_UNIX_MOUNT_TYPE_CAMERA;
2566 else if (g_str_has_prefix (basename, "memstick") ||
2567 g_str_has_prefix (basename, "memory_stick") ||
2568 g_str_has_prefix (basename, "ram"))
2569 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
2570 else if (g_str_has_prefix (basename, "compact_flash"))
2571 type = G_UNIX_MOUNT_TYPE_CF;
2572 else if (g_str_has_prefix (basename, "smart_media"))
2573 type = G_UNIX_MOUNT_TYPE_SM;
2574 else if (g_str_has_prefix (basename, "sd_mmc"))
2575 type = G_UNIX_MOUNT_TYPE_SDMMC;
2576 else if (g_str_has_prefix (basename, "ipod"))
2577 type = G_UNIX_MOUNT_TYPE_IPOD;
2578
2579 g_free (basename);
2580 }
2581
2582 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
2583 type = G_UNIX_MOUNT_TYPE_HD;
2584
2585 return type;
2586 }
2587
2588 /**
2589 * g_unix_mount_guess_type:
2590 * @mount_entry: a #GUnixMount.
2591 *
2592 * Guesses the type of a unix mount. If the mount type cannot be
2593 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2594 *
2595 * Returns: a #GUnixMountType.
2596 */
2597 static GUnixMountType
g_unix_mount_guess_type(GUnixMountEntry * mount_entry)2598 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
2599 {
2600 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2601 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2602 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2603 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2604
2605 return guess_mount_type (mount_entry->mount_path,
2606 mount_entry->device_path,
2607 mount_entry->filesystem_type);
2608 }
2609
2610 /**
2611 * g_unix_mount_point_guess_type:
2612 * @mount_point: a #GUnixMountPoint.
2613 *
2614 * Guesses the type of a unix mount point.
2615 * If the mount type cannot be determined,
2616 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
2617 *
2618 * Returns: a #GUnixMountType.
2619 */
2620 static GUnixMountType
g_unix_mount_point_guess_type(GUnixMountPoint * mount_point)2621 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
2622 {
2623 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2624 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2625 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2626 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
2627
2628 return guess_mount_type (mount_point->mount_path,
2629 mount_point->device_path,
2630 mount_point->filesystem_type);
2631 }
2632
2633 static const char *
type_to_icon(GUnixMountType type,gboolean is_mount_point,gboolean use_symbolic)2634 type_to_icon (GUnixMountType type, gboolean is_mount_point, gboolean use_symbolic)
2635 {
2636 const char *icon_name;
2637
2638 switch (type)
2639 {
2640 case G_UNIX_MOUNT_TYPE_HD:
2641 if (is_mount_point)
2642 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2643 else
2644 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2645 break;
2646 case G_UNIX_MOUNT_TYPE_FLOPPY:
2647 case G_UNIX_MOUNT_TYPE_ZIP:
2648 case G_UNIX_MOUNT_TYPE_JAZ:
2649 if (is_mount_point)
2650 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2651 else
2652 icon_name = use_symbolic ? "media-removable-symbolic" : "media-floppy";
2653 break;
2654 case G_UNIX_MOUNT_TYPE_CDROM:
2655 if (is_mount_point)
2656 icon_name = use_symbolic ? "drive-optical-symbolic" : "drive-optical";
2657 else
2658 icon_name = use_symbolic ? "media-optical-symbolic" : "media-optical";
2659 break;
2660 case G_UNIX_MOUNT_TYPE_NFS:
2661 icon_name = use_symbolic ? "folder-remote-symbolic" : "folder-remote";
2662 break;
2663 case G_UNIX_MOUNT_TYPE_MEMSTICK:
2664 if (is_mount_point)
2665 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2666 else
2667 icon_name = use_symbolic ? "media-removable-symbolic" : "media-flash";
2668 break;
2669 case G_UNIX_MOUNT_TYPE_CAMERA:
2670 if (is_mount_point)
2671 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2672 else
2673 icon_name = use_symbolic ? "camera-photo-symbolic" : "camera-photo";
2674 break;
2675 case G_UNIX_MOUNT_TYPE_IPOD:
2676 if (is_mount_point)
2677 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2678 else
2679 icon_name = use_symbolic ? "multimedia-player-symbolic" : "multimedia-player";
2680 break;
2681 case G_UNIX_MOUNT_TYPE_UNKNOWN:
2682 default:
2683 if (is_mount_point)
2684 icon_name = use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
2685 else
2686 icon_name = use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
2687 break;
2688 }
2689
2690 return icon_name;
2691 }
2692
2693 /**
2694 * g_unix_mount_guess_name:
2695 * @mount_entry: a #GUnixMountEntry
2696 *
2697 * Guesses the name of a Unix mount.
2698 * The result is a translated string.
2699 *
2700 * Returns: A newly allocated string that must
2701 * be freed with g_free()
2702 */
2703 gchar *
g_unix_mount_guess_name(GUnixMountEntry * mount_entry)2704 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
2705 {
2706 char *name;
2707
2708 if (strcmp (mount_entry->mount_path, "/") == 0)
2709 name = g_strdup (_("Filesystem root"));
2710 else
2711 name = g_filename_display_basename (mount_entry->mount_path);
2712
2713 return name;
2714 }
2715
2716 /**
2717 * g_unix_mount_guess_icon:
2718 * @mount_entry: a #GUnixMountEntry
2719 *
2720 * Guesses the icon of a Unix mount.
2721 *
2722 * Returns: (transfer full): a #GIcon
2723 */
2724 GIcon *
g_unix_mount_guess_icon(GUnixMountEntry * mount_entry)2725 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
2726 {
2727 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, FALSE));
2728 }
2729
2730 /**
2731 * g_unix_mount_guess_symbolic_icon:
2732 * @mount_entry: a #GUnixMountEntry
2733 *
2734 * Guesses the symbolic icon of a Unix mount.
2735 *
2736 * Returns: (transfer full): a #GIcon
2737 *
2738 * Since: 2.34
2739 */
2740 GIcon *
g_unix_mount_guess_symbolic_icon(GUnixMountEntry * mount_entry)2741 g_unix_mount_guess_symbolic_icon (GUnixMountEntry *mount_entry)
2742 {
2743 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE, TRUE));
2744 }
2745
2746 /**
2747 * g_unix_mount_point_guess_name:
2748 * @mount_point: a #GUnixMountPoint
2749 *
2750 * Guesses the name of a Unix mount point.
2751 * The result is a translated string.
2752 *
2753 * Returns: A newly allocated string that must
2754 * be freed with g_free()
2755 */
2756 gchar *
g_unix_mount_point_guess_name(GUnixMountPoint * mount_point)2757 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
2758 {
2759 char *name;
2760
2761 if (strcmp (mount_point->mount_path, "/") == 0)
2762 name = g_strdup (_("Filesystem root"));
2763 else
2764 name = g_filename_display_basename (mount_point->mount_path);
2765
2766 return name;
2767 }
2768
2769 /**
2770 * g_unix_mount_point_guess_icon:
2771 * @mount_point: a #GUnixMountPoint
2772 *
2773 * Guesses the icon of a Unix mount point.
2774 *
2775 * Returns: (transfer full): a #GIcon
2776 */
2777 GIcon *
g_unix_mount_point_guess_icon(GUnixMountPoint * mount_point)2778 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
2779 {
2780 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, FALSE));
2781 }
2782
2783 /**
2784 * g_unix_mount_point_guess_symbolic_icon:
2785 * @mount_point: a #GUnixMountPoint
2786 *
2787 * Guesses the symbolic icon of a Unix mount point.
2788 *
2789 * Returns: (transfer full): a #GIcon
2790 *
2791 * Since: 2.34
2792 */
2793 GIcon *
g_unix_mount_point_guess_symbolic_icon(GUnixMountPoint * mount_point)2794 g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount_point)
2795 {
2796 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE, TRUE));
2797 }
2798
2799 /**
2800 * g_unix_mount_guess_can_eject:
2801 * @mount_entry: a #GUnixMountEntry
2802 *
2803 * Guesses whether a Unix mount can be ejected.
2804 *
2805 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
2806 */
2807 gboolean
g_unix_mount_guess_can_eject(GUnixMountEntry * mount_entry)2808 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
2809 {
2810 GUnixMountType guessed_type;
2811
2812 guessed_type = g_unix_mount_guess_type (mount_entry);
2813 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2814 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2815 return TRUE;
2816
2817 return FALSE;
2818 }
2819
2820 /**
2821 * g_unix_mount_guess_should_display:
2822 * @mount_entry: a #GUnixMountEntry
2823 *
2824 * Guesses whether a Unix mount should be displayed in the UI.
2825 *
2826 * Returns: %TRUE if @mount_entry is deemed to be displayable.
2827 */
2828 gboolean
g_unix_mount_guess_should_display(GUnixMountEntry * mount_entry)2829 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
2830 {
2831 const char *mount_path;
2832 const gchar *user_name;
2833 gsize user_name_len;
2834
2835 /* Never display internal mountpoints */
2836 if (g_unix_mount_is_system_internal (mount_entry))
2837 return FALSE;
2838
2839 /* Only display things in /media (which are generally user mountable)
2840 and home dir (fuse stuff) and /run/media/$USER */
2841 mount_path = mount_entry->mount_path;
2842 if (mount_path != NULL)
2843 {
2844 const gboolean running_as_root = (getuid () == 0);
2845 gboolean is_in_runtime_dir = FALSE;
2846
2847 /* Hide mounts within a dot path, suppose it was a purpose to hide this mount */
2848 if (g_strstr_len (mount_path, -1, "/.") != NULL)
2849 return FALSE;
2850
2851 /* Check /run/media/$USER/. If running as root, display any mounts below
2852 * /run/media/. */
2853 if (running_as_root)
2854 {
2855 if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0)
2856 is_in_runtime_dir = TRUE;
2857 }
2858 else
2859 {
2860 user_name = g_get_user_name ();
2861 user_name_len = strlen (user_name);
2862 if (strncmp (mount_path, "/run/media/", strlen ("/run/media/")) == 0 &&
2863 strncmp (mount_path + strlen ("/run/media/"), user_name, user_name_len) == 0 &&
2864 mount_path[strlen ("/run/media/") + user_name_len] == '/')
2865 is_in_runtime_dir = TRUE;
2866 }
2867
2868 if (is_in_runtime_dir || g_str_has_prefix (mount_path, "/media/"))
2869 {
2870 char *path;
2871 /* Avoid displaying mounts that are not accessible to the user.
2872 *
2873 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
2874 * want to avoid g_access() for mount points which can potentially
2875 * block or fail stat()'ing, such as network mounts.
2876 */
2877 path = g_path_get_dirname (mount_path);
2878 if (g_str_has_prefix (path, "/media/"))
2879 {
2880 if (g_access (path, R_OK|X_OK) != 0)
2881 {
2882 g_free (path);
2883 return FALSE;
2884 }
2885 }
2886 g_free (path);
2887
2888 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
2889 {
2890 struct stat st;
2891 if (g_stat (mount_entry->device_path, &st) == 0 &&
2892 S_ISBLK(st.st_mode) &&
2893 g_access (mount_path, R_OK|X_OK) != 0)
2894 return FALSE;
2895 }
2896 return TRUE;
2897 }
2898
2899 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
2900 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
2901 return TRUE;
2902 }
2903
2904 return FALSE;
2905 }
2906
2907 /**
2908 * g_unix_mount_point_guess_can_eject:
2909 * @mount_point: a #GUnixMountPoint
2910 *
2911 * Guesses whether a Unix mount point can be ejected.
2912 *
2913 * Returns: %TRUE if @mount_point is deemed to be ejectable.
2914 */
2915 gboolean
g_unix_mount_point_guess_can_eject(GUnixMountPoint * mount_point)2916 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
2917 {
2918 GUnixMountType guessed_type;
2919
2920 guessed_type = g_unix_mount_point_guess_type (mount_point);
2921 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2922 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2923 return TRUE;
2924
2925 return FALSE;
2926 }
2927
2928 /* Utility functions {{{1 */
2929
2930 #ifdef HAVE_MNTENT_H
2931 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2932 static void
_canonicalize_filename(gchar * filename)2933 _canonicalize_filename (gchar *filename)
2934 {
2935 gchar *p, *q;
2936 gboolean last_was_slash = FALSE;
2937
2938 p = filename;
2939 q = filename;
2940
2941 while (*p)
2942 {
2943 if (*p == G_DIR_SEPARATOR)
2944 {
2945 if (!last_was_slash)
2946 *q++ = G_DIR_SEPARATOR;
2947
2948 last_was_slash = TRUE;
2949 }
2950 else
2951 {
2952 if (last_was_slash && *p == '.')
2953 {
2954 if (*(p + 1) == G_DIR_SEPARATOR ||
2955 *(p + 1) == '\0')
2956 {
2957 if (*(p + 1) == '\0')
2958 break;
2959
2960 p += 1;
2961 }
2962 else if (*(p + 1) == '.' &&
2963 (*(p + 2) == G_DIR_SEPARATOR ||
2964 *(p + 2) == '\0'))
2965 {
2966 if (q > filename + 1)
2967 {
2968 q--;
2969 while (q > filename + 1 &&
2970 *(q - 1) != G_DIR_SEPARATOR)
2971 q--;
2972 }
2973
2974 if (*(p + 2) == '\0')
2975 break;
2976
2977 p += 2;
2978 }
2979 else
2980 {
2981 *q++ = *p;
2982 last_was_slash = FALSE;
2983 }
2984 }
2985 else
2986 {
2987 *q++ = *p;
2988 last_was_slash = FALSE;
2989 }
2990 }
2991
2992 p++;
2993 }
2994
2995 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2996 q--;
2997
2998 *q = '\0';
2999 }
3000
3001 static char *
_resolve_symlink(const char * file)3002 _resolve_symlink (const char *file)
3003 {
3004 GError *error;
3005 char *dir;
3006 char *link;
3007 char *f;
3008 char *f1;
3009
3010 f = g_strdup (file);
3011
3012 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
3013 {
3014 link = g_file_read_link (f, &error);
3015 if (link == NULL)
3016 {
3017 g_error_free (error);
3018 g_free (f);
3019 f = NULL;
3020 goto out;
3021 }
3022
3023 dir = g_path_get_dirname (f);
3024 f1 = g_strdup_printf ("%s/%s", dir, link);
3025 g_free (dir);
3026 g_free (link);
3027 g_free (f);
3028 f = f1;
3029 }
3030
3031 out:
3032 if (f != NULL)
3033 _canonicalize_filename (f);
3034 return f;
3035 }
3036
3037 static const char *
_resolve_dev_root(void)3038 _resolve_dev_root (void)
3039 {
3040 static gboolean have_real_dev_root = FALSE;
3041 static char real_dev_root[256];
3042 struct stat statbuf;
3043
3044 /* see if it's cached already */
3045 if (have_real_dev_root)
3046 goto found;
3047
3048 /* otherwise we're going to find it right away.. */
3049 have_real_dev_root = TRUE;
3050
3051 if (stat ("/dev/root", &statbuf) == 0)
3052 {
3053 if (! S_ISLNK (statbuf.st_mode))
3054 {
3055 dev_t root_dev = statbuf.st_dev;
3056 FILE *f;
3057
3058 /* see if device with similar major:minor as /dev/root is mention
3059 * in /etc/mtab (it usually is)
3060 */
3061 f = fopen ("/etc/mtab", "r");
3062 if (f != NULL)
3063 {
3064 struct mntent *entp;
3065 #ifdef HAVE_GETMNTENT_R
3066 struct mntent ent;
3067 char buf[1024];
3068 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
3069 {
3070 #else
3071 G_LOCK (getmntent);
3072 while ((entp = getmntent (f)) != NULL)
3073 {
3074 #endif
3075 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
3076 statbuf.st_dev == root_dev)
3077 {
3078 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
3079 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
3080 fclose (f);
3081 goto found;
3082 }
3083 }
3084
3085 endmntent (f);
3086
3087 #ifndef HAVE_GETMNTENT_R
3088 G_UNLOCK (getmntent);
3089 #endif
3090 }
3091
3092 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
3093
3094 }
3095 else
3096 {
3097 char *resolved;
3098 resolved = _resolve_symlink ("/dev/root");
3099 if (resolved != NULL)
3100 {
3101 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
3102 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
3103 g_free (resolved);
3104 goto found;
3105 }
3106 }
3107 }
3108
3109 /* bah sucks.. */
3110 strcpy (real_dev_root, "/dev/root");
3111
3112 found:
3113 return real_dev_root;
3114 }
3115 #endif
3116
3117 /* Epilogue {{{1 */
3118 /* vim:set foldmethod=marker: */
3119