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