1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* GIO - GLib Input, Output and Streaming Library
4 *
5 * Copyright (C) 2006-2007 Red Hat, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 * Author: Alexander Larsson <alexl@redhat.com>
23 */
24
25 #include "config.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #ifndef HAVE_SYSCTLBYNAME
31 #ifdef HAVE_SYS_PARAM_H
32 #include <sys/param.h>
33 #endif
34 #ifdef HAVE_SYS_POLL_H
35 #include <sys/poll.h>
36 #endif
37 #endif
38 #ifdef HAVE_POLL_H
39 #include <poll.h>
40 #endif
41 #if HAVE_SYS_STATVFS_H
42 #include <sys/statvfs.h>
43 #endif
44 #include <stdio.h>
45 #include <unistd.h>
46 #include <sys/time.h>
47 #include <errno.h>
48 #include <string.h>
49 #include <signal.h>
50 #include <gstdio.h>
51 #include <dirent.h>
52
53 #include "gunixmounts.h"
54 #include "gfile.h"
55 #include "gfilemonitor.h"
56 #include "glibintl.h"
57 #include "gthemedicon.h"
58
59 #include "gioalias.h"
60
61 static const char *_resolve_dev_root (void);
62
63 /**
64 * SECTION:gunixmounts
65 * @include: gio/gunixmounts.h
66 * @short_description: Unix Mounts
67 *
68 * Routines for managing mounted UNIX mount points and paths.
69 *
70 * Note that <filename><gio/gunixmounts.h></filename> belongs to the
71 * UNIX-specific GIO interfaces, thus you have to use the
72 * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
73 */
74
75 /*
76 * GUnixMountType:
77 * @G_UNIX_MOUNT_TYPE_UNKNOWN: Unknown UNIX mount type.
78 * @G_UNIX_MOUNT_TYPE_FLOPPY: Floppy disk UNIX mount type.
79 * @G_UNIX_MOUNT_TYPE_CDROM: CDROM UNIX mount type.
80 * @G_UNIX_MOUNT_TYPE_NFS: Network File System (NFS) UNIX mount type.
81 * @G_UNIX_MOUNT_TYPE_ZIP: ZIP UNIX mount type.
82 * @G_UNIX_MOUNT_TYPE_JAZ: JAZZ UNIX mount type.
83 * @G_UNIX_MOUNT_TYPE_MEMSTICK: Memory Stick UNIX mount type.
84 * @G_UNIX_MOUNT_TYPE_CF: Compact Flash UNIX mount type.
85 * @G_UNIX_MOUNT_TYPE_SM: Smart Media UNIX mount type.
86 * @G_UNIX_MOUNT_TYPE_SDMMC: SD/MMC UNIX mount type.
87 * @G_UNIX_MOUNT_TYPE_IPOD: iPod UNIX mount type.
88 * @G_UNIX_MOUNT_TYPE_CAMERA: Digital camera UNIX mount type.
89 * @G_UNIX_MOUNT_TYPE_HD: Hard drive UNIX mount type.
90 *
91 * Types of UNIX mounts.
92 **/
93 typedef enum {
94 G_UNIX_MOUNT_TYPE_UNKNOWN,
95 G_UNIX_MOUNT_TYPE_FLOPPY,
96 G_UNIX_MOUNT_TYPE_CDROM,
97 G_UNIX_MOUNT_TYPE_NFS,
98 G_UNIX_MOUNT_TYPE_ZIP,
99 G_UNIX_MOUNT_TYPE_JAZ,
100 G_UNIX_MOUNT_TYPE_MEMSTICK,
101 G_UNIX_MOUNT_TYPE_CF,
102 G_UNIX_MOUNT_TYPE_SM,
103 G_UNIX_MOUNT_TYPE_SDMMC,
104 G_UNIX_MOUNT_TYPE_IPOD,
105 G_UNIX_MOUNT_TYPE_CAMERA,
106 G_UNIX_MOUNT_TYPE_HD
107 } GUnixMountType;
108
109 struct _GUnixMountEntry {
110 char *mount_path;
111 char *device_path;
112 char *filesystem_type;
113 gboolean is_read_only;
114 gboolean is_system_internal;
115 };
116
117 struct _GUnixMountPoint {
118 char *mount_path;
119 char *device_path;
120 char *filesystem_type;
121 gboolean is_read_only;
122 gboolean is_user_mountable;
123 gboolean is_loopback;
124 };
125
126 enum {
127 MOUNTS_CHANGED,
128 MOUNTPOINTS_CHANGED,
129 LAST_SIGNAL
130 };
131
132 static guint signals[LAST_SIGNAL];
133
134 struct _GUnixMountMonitor {
135 GObject parent;
136
137 GFileMonitor *fstab_monitor;
138 GFileMonitor *mtab_monitor;
139 };
140
141 struct _GUnixMountMonitorClass {
142 GObjectClass parent_class;
143 };
144
145 static GUnixMountMonitor *the_mount_monitor = NULL;
146
147 static GList *_g_get_unix_mounts (void);
148 static GList *_g_get_unix_mount_points (void);
149
150 G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
151
152 #define MOUNT_POLL_INTERVAL 4000
153
154 #ifdef HAVE_SYS_MNTTAB_H
155 #define MNTOPT_RO "ro"
156 #endif
157
158 #ifdef HAVE_MNTENT_H
159 #include <mntent.h>
160 #elif defined (HAVE_SYS_MNTTAB_H)
161 #include <sys/mnttab.h>
162 #endif
163
164 #ifdef HAVE_SYS_VFSTAB_H
165 #include <sys/vfstab.h>
166 #endif
167
168 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
169 #include <sys/mntctl.h>
170 #include <sys/vfs.h>
171 #include <sys/vmount.h>
172 #include <fshelp.h>
173 #endif
174
175 #if defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
176 #include <sys/ucred.h>
177 #include <sys/mount.h>
178 #include <fstab.h>
179 #ifdef HAVE_SYS_SYSCTL_H
180 #include <sys/sysctl.h>
181 #endif
182 #endif
183
184 #ifndef HAVE_SETMNTENT
185 #define setmntent(f,m) fopen(f,m)
186 #endif
187 #ifndef HAVE_ENDMNTENT
188 #define endmntent(f) fclose(f)
189 #endif
190
191 static gboolean
is_in(const char * value,const char * set[])192 is_in (const char *value, const char *set[])
193 {
194 int i;
195 for (i = 0; set[i] != NULL; i++)
196 {
197 if (strcmp (set[i], value) == 0)
198 return TRUE;
199 }
200 return FALSE;
201 }
202
203 /**
204 * g_unix_is_mount_path_system_internal:
205 * @mount_path: a mount path, e.g. <filename>/media/disk</filename>
206 * or <filename>/usr</filename>
207 *
208 * Determines if @mount_path is considered an implementation of the
209 * OS. This is primarily used for hiding mountable and mounted volumes
210 * that only are used in the OS and has little to no relevance to the
211 * casual user.
212 *
213 * Returns: %TRUE if @mount_path is considered an implementation detail
214 * of the OS.
215 **/
216 gboolean
g_unix_is_mount_path_system_internal(const char * mount_path)217 g_unix_is_mount_path_system_internal (const char *mount_path)
218 {
219 const char *ignore_mountpoints[] = {
220 /* Includes all FHS 2.3 toplevel dirs and other specilized
221 * directories that we want to hide from the user.
222 */
223 "/", /* we already have "Filesystem root" in Nautilus */
224 "/bin",
225 "/boot",
226 "/dev",
227 "/etc",
228 "/home",
229 "/lib",
230 "/lib64",
231 "/media",
232 "/mnt",
233 "/opt",
234 "/root",
235 "/sbin",
236 "/srv",
237 "/tmp",
238 "/usr",
239 "/usr/local",
240 "/var",
241 "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
242 "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
243 "/proc",
244 "/sbin",
245 "/net",
246 NULL
247 };
248
249 if (is_in (mount_path, ignore_mountpoints))
250 return TRUE;
251
252 if (g_str_has_prefix (mount_path, "/dev") ||
253 g_str_has_prefix (mount_path, "/proc") ||
254 g_str_has_prefix (mount_path, "/sys"))
255 return TRUE;
256
257 if (strstr (mount_path, "/.gvfs") != NULL)
258 return TRUE;
259
260 return FALSE;
261 }
262
263 static gboolean
guess_system_internal(const char * mountpoint,const char * fs,const char * device)264 guess_system_internal (const char *mountpoint,
265 const char *fs,
266 const char *device)
267 {
268 const char *ignore_fs[] = {
269 "auto",
270 "autofs",
271 "devfs",
272 "devpts",
273 "ecryptfs",
274 "kernfs",
275 "linprocfs",
276 "proc",
277 "procfs",
278 "ptyfs",
279 "rootfs",
280 "selinuxfs",
281 "sysfs",
282 "tmpfs",
283 "usbfs",
284 "nfsd",
285 "rpc_pipefs",
286 "zfs",
287 NULL
288 };
289 const char *ignore_devices[] = {
290 "none",
291 "sunrpc",
292 "devpts",
293 "nfsd",
294 "/dev/loop",
295 "/dev/vn",
296 NULL
297 };
298
299 if (is_in (fs, ignore_fs))
300 return TRUE;
301
302 if (is_in (device, ignore_devices))
303 return TRUE;
304
305 if (g_unix_is_mount_path_system_internal (mountpoint))
306 return TRUE;
307
308 return FALSE;
309 }
310
311 #ifdef HAVE_MNTENT_H
312
313 static char *
get_mtab_read_file(void)314 get_mtab_read_file (void)
315 {
316 #ifdef _PATH_MOUNTED
317 # ifdef __linux__
318 return "/proc/mounts";
319 # else
320 return _PATH_MOUNTED;
321 # endif
322 #else
323 return "/etc/mtab";
324 #endif
325 }
326
327 static char *
get_mtab_monitor_file(void)328 get_mtab_monitor_file (void)
329 {
330 #ifdef _PATH_MOUNTED
331 return _PATH_MOUNTED;
332 #else
333 return "/etc/mtab";
334 #endif
335 }
336
337 #ifndef HAVE_GETMNTENT_R
338 G_LOCK_DEFINE_STATIC(getmntent);
339 #endif
340
341 static GList *
_g_get_unix_mounts(void)342 _g_get_unix_mounts (void)
343 {
344 #ifdef HAVE_GETMNTENT_R
345 struct mntent ent;
346 char buf[1024];
347 #endif
348 struct mntent *mntent;
349 FILE *file;
350 char *read_file;
351 GUnixMountEntry *mount_entry;
352 GHashTable *mounts_hash;
353 GList *return_list;
354
355 read_file = get_mtab_read_file ();
356
357 file = setmntent (read_file, "r");
358 if (file == NULL)
359 return NULL;
360
361 return_list = NULL;
362
363 mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
364
365 #ifdef HAVE_GETMNTENT_R
366 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
367 #else
368 G_LOCK (getmntent);
369 while ((mntent = getmntent (file)) != NULL)
370 #endif
371 {
372 /* ignore any mnt_fsname that is repeated and begins with a '/'
373 *
374 * We do this to avoid being fooled by --bind mounts, since
375 * these have the same device as the location they bind to.
376 * It's not an ideal solution to the problem, but it's likely that
377 * the most important mountpoint is first and the --bind ones after
378 * that aren't as important. So it should work.
379 *
380 * The '/' is to handle procfs, tmpfs and other no device mounts.
381 */
382 if (mntent->mnt_fsname != NULL &&
383 mntent->mnt_fsname[0] == '/' &&
384 g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
385 continue;
386
387 mount_entry = g_new0 (GUnixMountEntry, 1);
388 mount_entry->mount_path = g_strdup (mntent->mnt_dir);
389 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
390 mount_entry->device_path = g_strdup (_resolve_dev_root ());
391 else
392 mount_entry->device_path = g_strdup (mntent->mnt_fsname);
393 mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
394
395 #if defined (HAVE_HASMNTOPT)
396 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
397 mount_entry->is_read_only = TRUE;
398 #endif
399
400 mount_entry->is_system_internal =
401 guess_system_internal (mount_entry->mount_path,
402 mount_entry->filesystem_type,
403 mount_entry->device_path);
404
405 g_hash_table_insert (mounts_hash,
406 mount_entry->device_path,
407 mount_entry->device_path);
408
409 return_list = g_list_prepend (return_list, mount_entry);
410 }
411 g_hash_table_destroy (mounts_hash);
412
413 endmntent (file);
414
415 #ifndef HAVE_GETMNTENT_R
416 G_UNLOCK (getmntent);
417 #endif
418
419 return g_list_reverse (return_list);
420 }
421
422 #elif defined (HAVE_SYS_MNTTAB_H)
423
424 G_LOCK_DEFINE_STATIC(getmntent);
425
426 static char *
get_mtab_read_file(void)427 get_mtab_read_file (void)
428 {
429 #ifdef _PATH_MOUNTED
430 return _PATH_MOUNTED;
431 #else
432 return "/etc/mnttab";
433 #endif
434 }
435
436 static char *
get_mtab_monitor_file(void)437 get_mtab_monitor_file (void)
438 {
439 return get_mtab_read_file ();
440 }
441
442 static GList *
_g_get_unix_mounts(void)443 _g_get_unix_mounts (void)
444 {
445 struct mnttab mntent;
446 FILE *file;
447 char *read_file;
448 GUnixMountEntry *mount_entry;
449 GList *return_list;
450
451 read_file = get_mtab_read_file ();
452
453 file = setmntent (read_file, "r");
454 if (file == NULL)
455 return NULL;
456
457 return_list = NULL;
458
459 G_LOCK (getmntent);
460 while (! getmntent (file, &mntent))
461 {
462 mount_entry = g_new0 (GUnixMountEntry, 1);
463
464 mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
465 mount_entry->device_path = g_strdup (mntent.mnt_special);
466 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
467
468 #if defined (HAVE_HASMNTOPT)
469 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
470 mount_entry->is_read_only = TRUE;
471 #endif
472
473 mount_entry->is_system_internal =
474 guess_system_internal (mount_entry->mount_path,
475 mount_entry->filesystem_type,
476 mount_entry->device_path);
477
478 return_list = g_list_prepend (return_list, mount_entry);
479 }
480
481 endmntent (file);
482
483 G_UNLOCK (getmntent);
484
485 return g_list_reverse (return_list);
486 }
487
488 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
489
490 static char *
get_mtab_monitor_file(void)491 get_mtab_monitor_file (void)
492 {
493 return NULL;
494 }
495
496 static GList *
_g_get_unix_mounts(void)497 _g_get_unix_mounts (void)
498 {
499 struct vfs_ent *fs_info;
500 struct vmount *vmount_info;
501 int vmount_number;
502 unsigned int vmount_size;
503 int current;
504 GList *return_list;
505
506 if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
507 {
508 g_warning ("Unable to know the number of mounted volumes\n");
509
510 return NULL;
511 }
512
513 vmount_info = (struct vmount*)g_malloc (vmount_size);
514
515 vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
516
517 if (vmount_info->vmt_revision != VMT_REVISION)
518 g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
519
520 if (vmount_number < 0)
521 {
522 g_warning ("Unable to recover mounted volumes information\n");
523
524 g_free (vmount_info);
525 return NULL;
526 }
527
528 return_list = NULL;
529 while (vmount_number > 0)
530 {
531 mount_entry = g_new0 (GUnixMountEntry, 1);
532
533 mount_entry->device_path = g_strdup (vmt2dataptr (vmount_info, VMT_OBJECT));
534 mount_entry->mount_path = g_strdup (vmt2dataptr (vmount_info, VMT_STUB));
535 /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
536 mount_entry->is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
537
538 fs_info = getvfsbytype (vmount_info->vmt_gfstype);
539
540 if (fs_info == NULL)
541 mount_entry->filesystem_type = g_strdup ("unknown");
542 else
543 mount_entry->filesystem_type = g_strdup (fs_info->vfsent_name);
544
545 mount_entry->is_system_internal =
546 guess_system_internal (mount_entry->mount_path,
547 mount_entry->filesystem_type,
548 mount_entry->device_path);
549
550 return_list = g_list_prepend (return_list, mount_entry);
551
552 vmount_info = (struct vmount *)( (char*)vmount_info
553 + vmount_info->vmt_length);
554 vmount_number--;
555 }
556
557 g_free (vmount_info);
558
559 return g_list_reverse (return_list);
560 }
561
562 #elif defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
563
564 static char *
get_mtab_monitor_file(void)565 get_mtab_monitor_file (void)
566 {
567 return NULL;
568 }
569
570 static GList *
_g_get_unix_mounts(void)571 _g_get_unix_mounts (void)
572 {
573 struct statfs *mntent = NULL;
574 int num_mounts, i;
575 GUnixMountEntry *mount_entry;
576 GList *return_list;
577
578 /* Pass MNT_NOWAIT to avoid blocking trying to update NFS mounts. */
579 if ((num_mounts = getmntinfo (&mntent, MNT_NOWAIT)) == 0)
580 return NULL;
581
582 return_list = NULL;
583
584 for (i = 0; i < num_mounts; i++)
585 {
586 mount_entry = g_new0 (GUnixMountEntry, 1);
587
588 mount_entry->mount_path = g_strdup (mntent[i].f_mntonname);
589 mount_entry->device_path = g_strdup (mntent[i].f_mntfromname);
590 mount_entry->filesystem_type = g_strdup (mntent[i].f_fstypename);
591 if (mntent[i].f_flags & MNT_RDONLY)
592 mount_entry->is_read_only = TRUE;
593
594 mount_entry->is_system_internal =
595 guess_system_internal (mount_entry->mount_path,
596 mount_entry->filesystem_type,
597 mount_entry->device_path);
598
599 return_list = g_list_prepend (return_list, mount_entry);
600 }
601
602 return g_list_reverse (return_list);
603 }
604 #elif defined(__INTERIX)
605
606 static char *
get_mtab_monitor_file(void)607 get_mtab_monitor_file (void)
608 {
609 return NULL;
610 }
611
612 static GList *
_g_get_unix_mounts(void)613 _g_get_unix_mounts (void)
614 {
615 DIR *dirp;
616 GList* return_list = NULL;
617 char filename[9 + NAME_MAX];
618
619 dirp = opendir ("/dev/fs");
620 if (!dirp)
621 {
622 g_warning ("unable to read /dev/fs!");
623 return NULL;
624 }
625
626 while (1)
627 {
628 struct statvfs statbuf;
629 struct dirent entry;
630 struct dirent* result;
631
632 if (readdir_r (dirp, &entry, &result) || result == NULL)
633 break;
634
635 strcpy (filename, "/dev/fs/");
636 strcat (filename, entry.d_name);
637
638 if (statvfs (filename, &statbuf) == 0)
639 {
640 GUnixMountEntry* mount_entry = g_new0(GUnixMountEntry, 1);
641
642 mount_entry->mount_path = g_strdup (statbuf.f_mntonname);
643 mount_entry->device_path = g_strdup (statbuf.f_mntfromname);
644 mount_entry->filesystem_type = g_strdup (statbuf.f_fstypename);
645
646 if (statbuf.f_flag & ST_RDONLY)
647 mount_entry->is_read_only = TRUE;
648
649 return_list = g_list_prepend(return_list, mount_entry);
650 }
651 }
652
653 return_list = g_list_reverse (return_list);
654
655 closedir (dirp);
656
657 return return_list;
658 }
659 #else
660 #error No _g_get_unix_mounts() implementation for system
661 #endif
662
663 /* _g_get_unix_mount_points():
664 * read the fstab.
665 * don't return swap and ignore mounts.
666 */
667
668 static char *
get_fstab_file(void)669 get_fstab_file (void)
670 {
671 #if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
672 /* AIX */
673 return "/etc/filesystems";
674 #elif defined(_PATH_MNTTAB)
675 return _PATH_MNTTAB;
676 #elif defined(VFSTAB)
677 return VFSTAB;
678 #else
679 return "/etc/fstab";
680 #endif
681 }
682
683 #ifdef HAVE_MNTENT_H
684 static GList *
_g_get_unix_mount_points(void)685 _g_get_unix_mount_points (void)
686 {
687 #ifdef HAVE_GETMNTENT_R
688 struct mntent ent;
689 char buf[1024];
690 #endif
691 struct mntent *mntent;
692 FILE *file;
693 char *read_file;
694 GUnixMountPoint *mount_entry;
695 GList *return_list;
696
697 read_file = get_fstab_file ();
698
699 file = setmntent (read_file, "r");
700 if (file == NULL)
701 return NULL;
702
703 return_list = NULL;
704
705 #ifdef HAVE_GETMNTENT_R
706 while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL)
707 #else
708 G_LOCK (getmntent);
709 while ((mntent = getmntent (file)) != NULL)
710 #endif
711 {
712 if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
713 (strcmp (mntent->mnt_dir, "swap") == 0))
714 continue;
715
716 mount_entry = g_new0 (GUnixMountPoint, 1);
717 mount_entry->mount_path = g_strdup (mntent->mnt_dir);
718 if (strcmp (mntent->mnt_fsname, "/dev/root") == 0)
719 mount_entry->device_path = g_strdup (_resolve_dev_root ());
720 else
721 mount_entry->device_path = g_strdup (mntent->mnt_fsname);
722 mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
723
724 #ifdef HAVE_HASMNTOPT
725 if (hasmntopt (mntent, MNTOPT_RO) != NULL)
726 mount_entry->is_read_only = TRUE;
727
728 if (hasmntopt (mntent, "loop") != NULL)
729 mount_entry->is_loopback = TRUE;
730
731 #endif
732
733 if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
734 #ifdef HAVE_HASMNTOPT
735 || (hasmntopt (mntent, "user") != NULL
736 && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
737 || hasmntopt (mntent, "pamconsole") != NULL
738 || hasmntopt (mntent, "users") != NULL
739 || hasmntopt (mntent, "owner") != NULL
740 #endif
741 )
742 mount_entry->is_user_mountable = TRUE;
743
744 return_list = g_list_prepend (return_list, mount_entry);
745 }
746
747 endmntent (file);
748
749 #ifndef HAVE_GETMNTENT_R
750 G_UNLOCK (getmntent);
751 #endif
752
753 return g_list_reverse (return_list);
754 }
755
756 #elif defined (HAVE_SYS_MNTTAB_H)
757
758 static GList *
_g_get_unix_mount_points(void)759 _g_get_unix_mount_points (void)
760 {
761 struct mnttab mntent;
762 FILE *file;
763 char *read_file;
764 GUnixMountPoint *mount_entry;
765 GList *return_list;
766
767 read_file = get_fstab_file ();
768
769 file = setmntent (read_file, "r");
770 if (file == NULL)
771 return NULL;
772
773 return_list = NULL;
774
775 G_LOCK (getmntent);
776 while (! getmntent (file, &mntent))
777 {
778 if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
779 (strcmp (mntent.mnt_mountp, "swap") == 0))
780 continue;
781
782 mount_entry = g_new0 (GUnixMountPoint, 1);
783
784 mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
785 mount_entry->device_path = g_strdup (mntent.mnt_special);
786 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
787
788 #ifdef HAVE_HASMNTOPT
789 if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
790 mount_entry->is_read_only = TRUE;
791
792 if (hasmntopt (&mntent, "lofs") != NULL)
793 mount_entry->is_loopback = TRUE;
794 #endif
795
796 if ((mntent.mnt_fstype != NULL)
797 #ifdef HAVE_HASMNTOPT
798 || (hasmntopt (&mntent, "user") != NULL
799 && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
800 || hasmntopt (&mntent, "pamconsole") != NULL
801 || hasmntopt (&mntent, "users") != NULL
802 || hasmntopt (&mntent, "owner") != NULL
803 #endif
804 )
805 mount_entry->is_user_mountable = TRUE;
806
807 return_list = g_list_prepend (return_list, mount_entry);
808 }
809
810 endmntent (file);
811 G_UNLOCK (getmntent);
812
813 return g_list_reverse (return_list);
814 }
815 #elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
816
817 /* functions to parse /etc/filesystems on aix */
818
819 /* read character, ignoring comments (begin with '*', end with '\n' */
820 static int
aix_fs_getc(FILE * fd)821 aix_fs_getc (FILE *fd)
822 {
823 int c;
824
825 while ((c = getc (fd)) == '*')
826 {
827 while (((c = getc (fd)) != '\n') && (c != EOF))
828 ;
829 }
830 }
831
832 /* eat all continuous spaces in a file */
833 static int
aix_fs_ignorespace(FILE * fd)834 aix_fs_ignorespace (FILE *fd)
835 {
836 int c;
837
838 while ((c = aix_fs_getc (fd)) != EOF)
839 {
840 if (!g_ascii_isspace (c))
841 {
842 ungetc (c,fd);
843 return c;
844 }
845 }
846
847 return EOF;
848 }
849
850 /* read one word from file */
851 static int
aix_fs_getword(FILE * fd,char * word)852 aix_fs_getword (FILE *fd,
853 char *word)
854 {
855 int c;
856
857 aix_fs_ignorespace (fd);
858
859 while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
860 {
861 if (c == '"')
862 {
863 while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
864 *word++ = c;
865 else
866 *word++ = c;
867 }
868 }
869 *word = 0;
870
871 return c;
872 }
873
874 typedef struct {
875 char mnt_mount[PATH_MAX];
876 char mnt_special[PATH_MAX];
877 char mnt_fstype[16];
878 char mnt_options[128];
879 } AixMountTableEntry;
880
881 /* read mount points properties */
882 static int
aix_fs_get(FILE * fd,AixMountTableEntry * prop)883 aix_fs_get (FILE *fd,
884 AixMountTableEntry *prop)
885 {
886 static char word[PATH_MAX] = { 0 };
887 char value[PATH_MAX];
888
889 /* read stanza */
890 if (word[0] == 0)
891 {
892 if (aix_fs_getword (fd, word) == EOF)
893 return EOF;
894 }
895
896 word[strlen(word) - 1] = 0;
897 strcpy (prop->mnt_mount, word);
898
899 /* read attributes and value */
900
901 while (aix_fs_getword (fd, word) != EOF)
902 {
903 /* test if is attribute or new stanza */
904 if (word[strlen(word) - 1] == ':')
905 return 0;
906
907 /* read "=" */
908 aix_fs_getword (fd, value);
909
910 /* read value */
911 aix_fs_getword (fd, value);
912
913 if (strcmp (word, "dev") == 0)
914 strcpy (prop->mnt_special, value);
915 else if (strcmp (word, "vfs") == 0)
916 strcpy (prop->mnt_fstype, value);
917 else if (strcmp (word, "options") == 0)
918 strcpy(prop->mnt_options, value);
919 }
920
921 return 0;
922 }
923
924 static GList *
_g_get_unix_mount_points(void)925 _g_get_unix_mount_points (void)
926 {
927 struct mntent *mntent;
928 FILE *file;
929 char *read_file;
930 GUnixMountPoint *mount_entry;
931 AixMountTableEntry mntent;
932 GList *return_list;
933
934 read_file = get_fstab_file ();
935
936 file = setmntent (read_file, "r");
937 if (file == NULL)
938 return NULL;
939
940 return_list = NULL;
941
942 while (!aix_fs_get (file, &mntent))
943 {
944 if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
945 {
946 mount_entry = g_new0 (GUnixMountPoint, 1);
947
948 mount_entry->mount_path = g_strdup (mntent.mnt_mount);
949 mount_entry->device_path = g_strdup (mntent.mnt_special);
950 mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
951 mount_entry->is_read_only = TRUE;
952 mount_entry->is_user_mountable = TRUE;
953
954 return_list = g_list_prepend (return_list, mount_entry);
955 }
956 }
957
958 endmntent (file);
959
960 return g_list_reverse (return_list);
961 }
962
963 #elif defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
964
965 static GList *
_g_get_unix_mount_points(void)966 _g_get_unix_mount_points (void)
967 {
968 struct fstab *fstab = NULL;
969 GUnixMountPoint *mount_entry;
970 GList *return_list;
971 #ifdef HAVE_SYS_SYSCTL_H
972 int usermnt = 0;
973 size_t len = sizeof(usermnt);
974 struct stat sb;
975 #endif
976
977 if (!setfsent ())
978 return NULL;
979
980 return_list = NULL;
981
982 #ifdef HAVE_SYS_SYSCTL_H
983 #if defined(HAVE_SYSCTLBYNAME)
984 sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
985 #elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
986 {
987 int mib[2];
988
989 mib[0] = CTL_VFS;
990 mib[1] = VFS_USERMOUNT;
991 sysctl (mib, 2, &usermnt, &len, NULL, 0);
992 }
993 #elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
994 {
995 int mib[2];
996
997 mib[0] = CTL_KERN;
998 mib[1] = KERN_USERMOUNT;
999 sysctl (mib, 2, &usermnt, &len, NULL, 0);
1000 }
1001 #endif
1002 #endif
1003
1004 while ((fstab = getfsent ()) != NULL)
1005 {
1006 if (strcmp (fstab->fs_vfstype, "swap") == 0)
1007 continue;
1008
1009 mount_entry = g_new0 (GUnixMountPoint, 1);
1010
1011 mount_entry->mount_path = g_strdup (fstab->fs_file);
1012 mount_entry->device_path = g_strdup (fstab->fs_spec);
1013 mount_entry->filesystem_type = g_strdup (fstab->fs_vfstype);
1014
1015 if (strcmp (fstab->fs_type, "ro") == 0)
1016 mount_entry->is_read_only = TRUE;
1017
1018 #ifdef HAVE_SYS_SYSCTL_H
1019 if (usermnt != 0)
1020 {
1021 uid_t uid = getuid ();
1022 if (stat (fstab->fs_file, &sb) == 0)
1023 {
1024 if (uid == 0 || sb.st_uid == uid)
1025 mount_entry->is_user_mountable = TRUE;
1026 }
1027 }
1028 #endif
1029
1030 return_list = g_list_prepend (return_list, mount_entry);
1031 }
1032
1033 endfsent ();
1034
1035 return g_list_reverse (return_list);
1036 }
1037 #elif defined(__INTERIX)
1038 static GList *
_g_get_unix_mount_points(void)1039 _g_get_unix_mount_points (void)
1040 {
1041 return _g_get_unix_mounts ();
1042 }
1043 #else
1044 #error No g_get_mount_table() implementation for system
1045 #endif
1046
1047 static guint64
get_mounts_timestamp(void)1048 get_mounts_timestamp (void)
1049 {
1050 const char *monitor_file;
1051 struct stat buf;
1052
1053 monitor_file = get_mtab_monitor_file ();
1054 if (monitor_file)
1055 {
1056 if (stat (monitor_file, &buf) == 0)
1057 return (guint64)buf.st_mtime;
1058 }
1059 return 0;
1060 }
1061
1062 static guint64
get_mount_points_timestamp(void)1063 get_mount_points_timestamp (void)
1064 {
1065 const char *monitor_file;
1066 struct stat buf;
1067
1068 monitor_file = get_fstab_file ();
1069 if (monitor_file)
1070 {
1071 if (stat (monitor_file, &buf) == 0)
1072 return (guint64)buf.st_mtime;
1073 }
1074 return 0;
1075 }
1076
1077 /**
1078 * g_unix_mounts_get:
1079 * @time_read: guint64 to contain a timestamp.
1080 *
1081 * Gets a #GList of strings containing the unix mounts.
1082 * If @time_read is set, it will be filled with the mount
1083 * timestamp, allowing for checking if the mounts have changed
1084 * with g_unix_mounts_changed_since().
1085 *
1086 * Returns: a #GList of the UNIX mounts.
1087 **/
1088 GList *
g_unix_mounts_get(guint64 * time_read)1089 g_unix_mounts_get (guint64 *time_read)
1090 {
1091 if (time_read)
1092 *time_read = get_mounts_timestamp ();
1093
1094 return _g_get_unix_mounts ();
1095 }
1096
1097 /**
1098 * g_unix_mount_at:
1099 * @mount_path: path for a possible unix mount.
1100 * @time_read: guint64 to contain a timestamp.
1101 *
1102 * Gets a #GUnixMountEntry for a given mount path. If @time_read
1103 * is set, it will be filled with a unix timestamp for checking
1104 * if the mounts have changed since with g_unix_mounts_changed_since().
1105 *
1106 * Returns: a #GUnixMount.
1107 **/
1108 GUnixMountEntry *
g_unix_mount_at(const char * mount_path,guint64 * time_read)1109 g_unix_mount_at (const char *mount_path,
1110 guint64 *time_read)
1111 {
1112 GList *mounts, *l;
1113 GUnixMountEntry *mount_entry, *found;
1114
1115 mounts = g_unix_mounts_get (time_read);
1116
1117 found = NULL;
1118 for (l = mounts; l != NULL; l = l->next)
1119 {
1120 mount_entry = l->data;
1121
1122 if (!found && strcmp (mount_path, mount_entry->mount_path) == 0)
1123 found = mount_entry;
1124 else
1125 g_unix_mount_free (mount_entry);
1126 }
1127 g_list_free (mounts);
1128
1129 return found;
1130 }
1131
1132 /**
1133 * g_unix_mount_points_get:
1134 * @time_read: guint64 to contain a timestamp.
1135 *
1136 * Gets a #GList of strings containing the unix mount points.
1137 * If @time_read is set, it will be filled with the mount timestamp,
1138 * allowing for checking if the mounts have changed with
1139 * g_unix_mounts_points_changed_since().
1140 *
1141 * Returns: a #GList of the UNIX mountpoints.
1142 **/
1143 GList *
g_unix_mount_points_get(guint64 * time_read)1144 g_unix_mount_points_get (guint64 *time_read)
1145 {
1146 if (time_read)
1147 *time_read = get_mount_points_timestamp ();
1148
1149 return _g_get_unix_mount_points ();
1150 }
1151
1152 /**
1153 * g_unix_mounts_changed_since:
1154 * @time: guint64 to contain a timestamp.
1155 *
1156 * Checks if the unix mounts have changed since a given unix time.
1157 *
1158 * Returns: %TRUE if the mounts have changed since @time.
1159 **/
1160 gboolean
g_unix_mounts_changed_since(guint64 time)1161 g_unix_mounts_changed_since (guint64 time)
1162 {
1163 return get_mounts_timestamp () != time;
1164 }
1165
1166 /**
1167 * g_unix_mount_points_changed_since:
1168 * @time: guint64 to contain a timestamp.
1169 *
1170 * Checks if the unix mount points have changed since a given unix time.
1171 *
1172 * Returns: %TRUE if the mount points have changed since @time.
1173 **/
1174 gboolean
g_unix_mount_points_changed_since(guint64 time)1175 g_unix_mount_points_changed_since (guint64 time)
1176 {
1177 return get_mount_points_timestamp () != time;
1178 }
1179
1180 static void
g_unix_mount_monitor_finalize(GObject * object)1181 g_unix_mount_monitor_finalize (GObject *object)
1182 {
1183 GUnixMountMonitor *monitor;
1184
1185 monitor = G_UNIX_MOUNT_MONITOR (object);
1186
1187 if (monitor->fstab_monitor)
1188 {
1189 g_file_monitor_cancel (monitor->fstab_monitor);
1190 g_object_unref (monitor->fstab_monitor);
1191 }
1192
1193 if (monitor->mtab_monitor)
1194 {
1195 g_file_monitor_cancel (monitor->mtab_monitor);
1196 g_object_unref (monitor->mtab_monitor);
1197 }
1198
1199 the_mount_monitor = NULL;
1200
1201 G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
1202 }
1203
1204
1205 static void
g_unix_mount_monitor_class_init(GUnixMountMonitorClass * klass)1206 g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
1207 {
1208 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1209
1210 gobject_class->finalize = g_unix_mount_monitor_finalize;
1211
1212 /**
1213 * GUnixMountMonitor::mounts-changed:
1214 * @monitor: the object on which the signal is emitted
1215 *
1216 * Emitted when the unix mounts have changed.
1217 */
1218 signals[MOUNTS_CHANGED] =
1219 g_signal_new ("mounts-changed",
1220 G_TYPE_FROM_CLASS (klass),
1221 G_SIGNAL_RUN_LAST,
1222 0,
1223 NULL, NULL,
1224 g_cclosure_marshal_VOID__VOID,
1225 G_TYPE_NONE, 0);
1226
1227 /**
1228 * GUnixMountMonitor::mountpoints-changed:
1229 * @monitor: the object on which the signal is emitted
1230 *
1231 * Emitted when the unix mount points have changed.
1232 */
1233 signals[MOUNTPOINTS_CHANGED] =
1234 g_signal_new ("mountpoints-changed",
1235 G_TYPE_FROM_CLASS (klass),
1236 G_SIGNAL_RUN_LAST,
1237 0,
1238 NULL, NULL,
1239 g_cclosure_marshal_VOID__VOID,
1240 G_TYPE_NONE, 0);
1241 }
1242
1243 static void
fstab_file_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,gpointer user_data)1244 fstab_file_changed (GFileMonitor *monitor,
1245 GFile *file,
1246 GFile *other_file,
1247 GFileMonitorEvent event_type,
1248 gpointer user_data)
1249 {
1250 GUnixMountMonitor *mount_monitor;
1251
1252 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1253 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1254 event_type != G_FILE_MONITOR_EVENT_DELETED)
1255 return;
1256
1257 mount_monitor = user_data;
1258 g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
1259 }
1260
1261 static void
mtab_file_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,gpointer user_data)1262 mtab_file_changed (GFileMonitor *monitor,
1263 GFile *file,
1264 GFile *other_file,
1265 GFileMonitorEvent event_type,
1266 gpointer user_data)
1267 {
1268 GUnixMountMonitor *mount_monitor;
1269
1270 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1271 event_type != G_FILE_MONITOR_EVENT_CREATED &&
1272 event_type != G_FILE_MONITOR_EVENT_DELETED)
1273 return;
1274
1275 mount_monitor = user_data;
1276 g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1277 }
1278
1279 static void
g_unix_mount_monitor_init(GUnixMountMonitor * monitor)1280 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1281 {
1282 GFile *file;
1283
1284 if (get_fstab_file () != NULL)
1285 {
1286 file = g_file_new_for_path (get_fstab_file ());
1287 monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1288 g_object_unref (file);
1289
1290 g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
1291 }
1292
1293 if (get_mtab_monitor_file () != NULL)
1294 {
1295 file = g_file_new_for_path (get_mtab_monitor_file ());
1296 monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1297 g_object_unref (file);
1298
1299 g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
1300 }
1301 }
1302
1303 /**
1304 * g_unix_mount_monitor_set_rate_limit:
1305 * @mount_monitor: a #GUnixMountMonitor
1306 * @limit_msec: a integer with the limit in milliseconds to
1307 * poll for changes.
1308 *
1309 * Sets the rate limit to which the @mount_monitor will report
1310 * consecutive change events to the mount and mount point entry files.
1311 *
1312 * Since: 2.18
1313 */
1314 void
g_unix_mount_monitor_set_rate_limit(GUnixMountMonitor * mount_monitor,gint limit_msec)1315 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1316 gint limit_msec)
1317 {
1318 g_return_if_fail (G_IS_UNIX_MOUNT_MONITOR (mount_monitor));
1319
1320 if (mount_monitor->fstab_monitor != NULL)
1321 g_file_monitor_set_rate_limit (mount_monitor->fstab_monitor, limit_msec);
1322
1323 if (mount_monitor->mtab_monitor != NULL)
1324 g_file_monitor_set_rate_limit (mount_monitor->mtab_monitor, limit_msec);
1325 }
1326
1327 /**
1328 * g_unix_mount_monitor_new:
1329 *
1330 * Gets a new #GUnixMountMonitor. The default rate limit for which the
1331 * monitor will report consecutive changes for the mount and mount
1332 * point entry files is the default for a #GFileMonitor. Use
1333 * g_unix_mount_monitor_set_rate_limit() to change this.
1334 *
1335 * Returns: a #GUnixMountMonitor.
1336 */
1337 GUnixMountMonitor *
g_unix_mount_monitor_new(void)1338 g_unix_mount_monitor_new (void)
1339 {
1340 if (the_mount_monitor == NULL)
1341 {
1342 the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
1343 return the_mount_monitor;
1344 }
1345
1346 return g_object_ref (the_mount_monitor);
1347 }
1348
1349 /**
1350 * g_unix_mount_free:
1351 * @mount_entry: a #GUnixMount.
1352 *
1353 * Frees a unix mount.
1354 */
1355 void
g_unix_mount_free(GUnixMountEntry * mount_entry)1356 g_unix_mount_free (GUnixMountEntry *mount_entry)
1357 {
1358 g_return_if_fail (mount_entry != NULL);
1359
1360 g_free (mount_entry->mount_path);
1361 g_free (mount_entry->device_path);
1362 g_free (mount_entry->filesystem_type);
1363 g_free (mount_entry);
1364 }
1365
1366 /**
1367 * g_unix_mount_point_free:
1368 * @mount_point: unix mount point to free.
1369 *
1370 * Frees a unix mount point.
1371 */
1372 void
g_unix_mount_point_free(GUnixMountPoint * mount_point)1373 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1374 {
1375 g_return_if_fail (mount_point != NULL);
1376
1377 g_free (mount_point->mount_path);
1378 g_free (mount_point->device_path);
1379 g_free (mount_point->filesystem_type);
1380 g_free (mount_point);
1381 }
1382
1383 /**
1384 * g_unix_mount_compare:
1385 * @mount1: first #GUnixMountEntry to compare.
1386 * @mount2: second #GUnixMountEntry to compare.
1387 *
1388 * Compares two unix mounts.
1389 *
1390 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1391 * or less than @mount2, respectively.
1392 */
1393 gint
g_unix_mount_compare(GUnixMountEntry * mount1,GUnixMountEntry * mount2)1394 g_unix_mount_compare (GUnixMountEntry *mount1,
1395 GUnixMountEntry *mount2)
1396 {
1397 int res;
1398
1399 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1400
1401 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1402 if (res != 0)
1403 return res;
1404
1405 res = g_strcmp0 (mount1->device_path, mount2->device_path);
1406 if (res != 0)
1407 return res;
1408
1409 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1410 if (res != 0)
1411 return res;
1412
1413 res = mount1->is_read_only - mount2->is_read_only;
1414 if (res != 0)
1415 return res;
1416
1417 return 0;
1418 }
1419
1420 /**
1421 * g_unix_mount_get_mount_path:
1422 * @mount_entry: input #GUnixMountEntry to get the mount path for.
1423 *
1424 * Gets the mount path for a unix mount.
1425 *
1426 * Returns: the mount path for @mount_entry.
1427 */
1428 const gchar *
g_unix_mount_get_mount_path(GUnixMountEntry * mount_entry)1429 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1430 {
1431 g_return_val_if_fail (mount_entry != NULL, NULL);
1432
1433 return mount_entry->mount_path;
1434 }
1435
1436 /**
1437 * g_unix_mount_get_device_path:
1438 * @mount_entry: a #GUnixMount.
1439 *
1440 * Gets the device path for a unix mount.
1441 *
1442 * Returns: a string containing the device path.
1443 */
1444 const gchar *
g_unix_mount_get_device_path(GUnixMountEntry * mount_entry)1445 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1446 {
1447 g_return_val_if_fail (mount_entry != NULL, NULL);
1448
1449 return mount_entry->device_path;
1450 }
1451
1452 /**
1453 * g_unix_mount_get_fs_type:
1454 * @mount_entry: a #GUnixMount.
1455 *
1456 * Gets the filesystem type for the unix mount.
1457 *
1458 * Returns: a string containing the file system type.
1459 */
1460 const gchar *
g_unix_mount_get_fs_type(GUnixMountEntry * mount_entry)1461 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1462 {
1463 g_return_val_if_fail (mount_entry != NULL, NULL);
1464
1465 return mount_entry->filesystem_type;
1466 }
1467
1468 /**
1469 * g_unix_mount_is_readonly:
1470 * @mount_entry: a #GUnixMount.
1471 *
1472 * Checks if a unix mount is mounted read only.
1473 *
1474 * Returns: %TRUE if @mount_entry is read only.
1475 */
1476 gboolean
g_unix_mount_is_readonly(GUnixMountEntry * mount_entry)1477 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1478 {
1479 g_return_val_if_fail (mount_entry != NULL, FALSE);
1480
1481 return mount_entry->is_read_only;
1482 }
1483
1484 /**
1485 * g_unix_mount_is_system_internal:
1486 * @mount_entry: a #GUnixMount.
1487 *
1488 * Checks if a unix mount is a system path.
1489 *
1490 * Returns: %TRUE if the unix mount is for a system path.
1491 */
1492 gboolean
g_unix_mount_is_system_internal(GUnixMountEntry * mount_entry)1493 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1494 {
1495 g_return_val_if_fail (mount_entry != NULL, FALSE);
1496
1497 return mount_entry->is_system_internal;
1498 }
1499
1500 /**
1501 * g_unix_mount_point_compare:
1502 * @mount1: a #GUnixMount.
1503 * @mount2: a #GUnixMount.
1504 *
1505 * Compares two unix mount points.
1506 *
1507 * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1508 * or less than @mount2, respectively.
1509 */
1510 gint
g_unix_mount_point_compare(GUnixMountPoint * mount1,GUnixMountPoint * mount2)1511 g_unix_mount_point_compare (GUnixMountPoint *mount1,
1512 GUnixMountPoint *mount2)
1513 {
1514 int res;
1515
1516 g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1517
1518 res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1519 if (res != 0)
1520 return res;
1521
1522 res = g_strcmp0 (mount1->device_path, mount2->device_path);
1523 if (res != 0)
1524 return res;
1525
1526 res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1527 if (res != 0)
1528 return res;
1529
1530 res = mount1->is_read_only - mount2->is_read_only;
1531 if (res != 0)
1532 return res;
1533
1534 res = mount1->is_user_mountable - mount2->is_user_mountable;
1535 if (res != 0)
1536 return res;
1537
1538 res = mount1->is_loopback - mount2->is_loopback;
1539 if (res != 0)
1540 return res;
1541
1542 return 0;
1543 }
1544
1545 /**
1546 * g_unix_mount_point_get_mount_path:
1547 * @mount_point: a #GUnixMountPoint.
1548 *
1549 * Gets the mount path for a unix mount point.
1550 *
1551 * Returns: a string containing the mount path.
1552 */
1553 const gchar *
g_unix_mount_point_get_mount_path(GUnixMountPoint * mount_point)1554 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
1555 {
1556 g_return_val_if_fail (mount_point != NULL, NULL);
1557
1558 return mount_point->mount_path;
1559 }
1560
1561 /**
1562 * g_unix_mount_point_get_device_path:
1563 * @mount_point: a #GUnixMountPoint.
1564 *
1565 * Gets the device path for a unix mount point.
1566 *
1567 * Returns: a string containing the device path.
1568 */
1569 const gchar *
g_unix_mount_point_get_device_path(GUnixMountPoint * mount_point)1570 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
1571 {
1572 g_return_val_if_fail (mount_point != NULL, NULL);
1573
1574 return mount_point->device_path;
1575 }
1576
1577 /**
1578 * g_unix_mount_point_get_fs_type:
1579 * @mount_point: a #GUnixMountPoint.
1580 *
1581 * Gets the file system type for the mount point.
1582 *
1583 * Returns: a string containing the file system type.
1584 */
1585 const gchar *
g_unix_mount_point_get_fs_type(GUnixMountPoint * mount_point)1586 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
1587 {
1588 g_return_val_if_fail (mount_point != NULL, NULL);
1589
1590 return mount_point->filesystem_type;
1591 }
1592
1593 /**
1594 * g_unix_mount_point_is_readonly:
1595 * @mount_point: a #GUnixMountPoint.
1596 *
1597 * Checks if a unix mount point is read only.
1598 *
1599 * Returns: %TRUE if a mount point is read only.
1600 */
1601 gboolean
g_unix_mount_point_is_readonly(GUnixMountPoint * mount_point)1602 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
1603 {
1604 g_return_val_if_fail (mount_point != NULL, FALSE);
1605
1606 return mount_point->is_read_only;
1607 }
1608
1609 /**
1610 * g_unix_mount_point_is_user_mountable:
1611 * @mount_point: a #GUnixMountPoint.
1612 *
1613 * Checks if a unix mount point is mountable by the user.
1614 *
1615 * Returns: %TRUE if the mount point is user mountable.
1616 */
1617 gboolean
g_unix_mount_point_is_user_mountable(GUnixMountPoint * mount_point)1618 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
1619 {
1620 g_return_val_if_fail (mount_point != NULL, FALSE);
1621
1622 return mount_point->is_user_mountable;
1623 }
1624
1625 /**
1626 * g_unix_mount_point_is_loopback:
1627 * @mount_point: a #GUnixMountPoint.
1628 *
1629 * Checks if a unix mount point is a loopback device.
1630 *
1631 * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise.
1632 */
1633 gboolean
g_unix_mount_point_is_loopback(GUnixMountPoint * mount_point)1634 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
1635 {
1636 g_return_val_if_fail (mount_point != NULL, FALSE);
1637
1638 return mount_point->is_loopback;
1639 }
1640
1641 static GUnixMountType
guess_mount_type(const char * mount_path,const char * device_path,const char * filesystem_type)1642 guess_mount_type (const char *mount_path,
1643 const char *device_path,
1644 const char *filesystem_type)
1645 {
1646 GUnixMountType type;
1647 char *basename;
1648
1649 type = G_UNIX_MOUNT_TYPE_UNKNOWN;
1650
1651 if ((strcmp (filesystem_type, "udf") == 0) ||
1652 (strcmp (filesystem_type, "iso9660") == 0) ||
1653 (strcmp (filesystem_type, "cd9660") == 0))
1654 type = G_UNIX_MOUNT_TYPE_CDROM;
1655 else if ((strcmp (filesystem_type, "nfs") == 0) ||
1656 (strcmp (filesystem_type, "nfs4") == 0))
1657 type = G_UNIX_MOUNT_TYPE_NFS;
1658 else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
1659 g_str_has_prefix (device_path, "/dev/fd") ||
1660 g_str_has_prefix (device_path, "/dev/floppy"))
1661 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1662 else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
1663 g_str_has_prefix (device_path, "/dev/acd") ||
1664 g_str_has_prefix (device_path, "/dev/cd"))
1665 type = G_UNIX_MOUNT_TYPE_CDROM;
1666 else if (g_str_has_prefix (device_path, "/vol/"))
1667 {
1668 const char *name = mount_path + strlen ("/");
1669
1670 if (g_str_has_prefix (name, "cdrom"))
1671 type = G_UNIX_MOUNT_TYPE_CDROM;
1672 else if (g_str_has_prefix (name, "floppy") ||
1673 g_str_has_prefix (device_path, "/vol/dev/diskette/"))
1674 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1675 else if (g_str_has_prefix (name, "rmdisk"))
1676 type = G_UNIX_MOUNT_TYPE_ZIP;
1677 else if (g_str_has_prefix (name, "jaz"))
1678 type = G_UNIX_MOUNT_TYPE_JAZ;
1679 else if (g_str_has_prefix (name, "memstick"))
1680 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1681 }
1682 else
1683 {
1684 basename = g_path_get_basename (mount_path);
1685
1686 if (g_str_has_prefix (basename, "cdr") ||
1687 g_str_has_prefix (basename, "cdwriter") ||
1688 g_str_has_prefix (basename, "burn") ||
1689 g_str_has_prefix (basename, "dvdr"))
1690 type = G_UNIX_MOUNT_TYPE_CDROM;
1691 else if (g_str_has_prefix (basename, "floppy"))
1692 type = G_UNIX_MOUNT_TYPE_FLOPPY;
1693 else if (g_str_has_prefix (basename, "zip"))
1694 type = G_UNIX_MOUNT_TYPE_ZIP;
1695 else if (g_str_has_prefix (basename, "jaz"))
1696 type = G_UNIX_MOUNT_TYPE_JAZ;
1697 else if (g_str_has_prefix (basename, "camera"))
1698 type = G_UNIX_MOUNT_TYPE_CAMERA;
1699 else if (g_str_has_prefix (basename, "memstick") ||
1700 g_str_has_prefix (basename, "memory_stick") ||
1701 g_str_has_prefix (basename, "ram"))
1702 type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1703 else if (g_str_has_prefix (basename, "compact_flash"))
1704 type = G_UNIX_MOUNT_TYPE_CF;
1705 else if (g_str_has_prefix (basename, "smart_media"))
1706 type = G_UNIX_MOUNT_TYPE_SM;
1707 else if (g_str_has_prefix (basename, "sd_mmc"))
1708 type = G_UNIX_MOUNT_TYPE_SDMMC;
1709 else if (g_str_has_prefix (basename, "ipod"))
1710 type = G_UNIX_MOUNT_TYPE_IPOD;
1711
1712 g_free (basename);
1713 }
1714
1715 if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
1716 type = G_UNIX_MOUNT_TYPE_HD;
1717
1718 return type;
1719 }
1720
1721 /*
1722 * g_unix_mount_guess_type:
1723 * @mount_entry: a #GUnixMount.
1724 *
1725 * Guesses the type of a unix mount. If the mount type cannot be
1726 * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1727 *
1728 * Returns: a #GUnixMountType.
1729 */
1730 static GUnixMountType
g_unix_mount_guess_type(GUnixMountEntry * mount_entry)1731 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
1732 {
1733 g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1734 g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1735 g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1736 g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1737
1738 return guess_mount_type (mount_entry->mount_path,
1739 mount_entry->device_path,
1740 mount_entry->filesystem_type);
1741 }
1742
1743 /*
1744 * g_unix_mount_point_guess_type:
1745 * @mount_point: a #GUnixMountPoint.
1746 *
1747 * Guesses the type of a unix mount point.
1748 * If the mount type cannot be determined,
1749 * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1750 *
1751 * Returns: a #GUnixMountType.
1752 */
1753 static GUnixMountType
g_unix_mount_point_guess_type(GUnixMountPoint * mount_point)1754 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
1755 {
1756 g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1757 g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1758 g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1759 g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1760
1761 return guess_mount_type (mount_point->mount_path,
1762 mount_point->device_path,
1763 mount_point->filesystem_type);
1764 }
1765
1766 static const char *
type_to_icon(GUnixMountType type,gboolean is_mount_point)1767 type_to_icon (GUnixMountType type, gboolean is_mount_point)
1768 {
1769 const char *icon_name;
1770
1771 switch (type)
1772 {
1773 case G_UNIX_MOUNT_TYPE_HD:
1774 if (is_mount_point)
1775 icon_name = "drive-removable-media";
1776 else
1777 icon_name = "drive-harddisk";
1778 break;
1779 case G_UNIX_MOUNT_TYPE_FLOPPY:
1780 case G_UNIX_MOUNT_TYPE_ZIP:
1781 case G_UNIX_MOUNT_TYPE_JAZ:
1782 if (is_mount_point)
1783 icon_name = "drive-removable-media";
1784 else
1785 icon_name = "media-floppy";
1786 break;
1787 case G_UNIX_MOUNT_TYPE_CDROM:
1788 if (is_mount_point)
1789 icon_name = "drive-optical";
1790 else
1791 icon_name = "media-optical";
1792 break;
1793 case G_UNIX_MOUNT_TYPE_NFS:
1794 /* TODO: Would like a better icon here... */
1795 if (is_mount_point)
1796 icon_name = "drive-removable-media";
1797 else
1798 icon_name = "drive-harddisk";
1799 break;
1800 case G_UNIX_MOUNT_TYPE_MEMSTICK:
1801 if (is_mount_point)
1802 icon_name = "drive-removable-media";
1803 else
1804 icon_name = "media-flash";
1805 break;
1806 case G_UNIX_MOUNT_TYPE_CAMERA:
1807 if (is_mount_point)
1808 icon_name = "drive-removable-media";
1809 else
1810 icon_name = "camera-photo";
1811 break;
1812 case G_UNIX_MOUNT_TYPE_IPOD:
1813 if (is_mount_point)
1814 icon_name = "drive-removable-media";
1815 else
1816 icon_name = "multimedia-player";
1817 break;
1818 case G_UNIX_MOUNT_TYPE_UNKNOWN:
1819 default:
1820 if (is_mount_point)
1821 icon_name = "drive-removable-media";
1822 else
1823 icon_name = "drive-harddisk";
1824 break;
1825 }
1826
1827 return icon_name;
1828 }
1829
1830 /**
1831 * g_unix_mount_guess_name:
1832 * @mount_entry: a #GUnixMountEntry
1833 *
1834 * Guesses the name of a Unix mount.
1835 * The result is a translated string.
1836 *
1837 * Returns: A newly allocated string that must
1838 * be freed with g_free()
1839 */
1840 gchar *
g_unix_mount_guess_name(GUnixMountEntry * mount_entry)1841 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
1842 {
1843 char *name;
1844
1845 if (strcmp (mount_entry->mount_path, "/") == 0)
1846 name = g_strdup (_("Filesystem root"));
1847 else
1848 name = g_filename_display_basename (mount_entry->mount_path);
1849
1850 return name;
1851 }
1852
1853 /**
1854 * g_unix_mount_guess_icon:
1855 * @mount_entry: a #GUnixMountEntry
1856 *
1857 * Guesses the icon of a Unix mount.
1858 *
1859 * Returns: a #GIcon
1860 */
1861 GIcon *
g_unix_mount_guess_icon(GUnixMountEntry * mount_entry)1862 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
1863 {
1864 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE));
1865 }
1866
1867 /**
1868 * g_unix_mount_point_guess_name:
1869 * @mount_point: a #GUnixMountPoint
1870 *
1871 * Guesses the name of a Unix mount point.
1872 * The result is a translated string.
1873 *
1874 * Returns: A newly allocated string that must
1875 * be freed with g_free()
1876 */
1877 gchar *
g_unix_mount_point_guess_name(GUnixMountPoint * mount_point)1878 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
1879 {
1880 char *name;
1881
1882 if (strcmp (mount_point->mount_path, "/") == 0)
1883 name = g_strdup (_("Filesystem root"));
1884 else
1885 name = g_filename_display_basename (mount_point->mount_path);
1886
1887 return name;
1888 }
1889
1890 /**
1891 * g_unix_mount_point_guess_icon:
1892 * @mount_point: a #GUnixMountPoint
1893 *
1894 * Guesses the icon of a Unix mount point.
1895 *
1896 * Returns: a #GIcon
1897 */
1898 GIcon *
g_unix_mount_point_guess_icon(GUnixMountPoint * mount_point)1899 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
1900 {
1901 return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE));
1902 }
1903
1904 /**
1905 * g_unix_mount_guess_can_eject:
1906 * @mount_entry: a #GUnixMountEntry
1907 *
1908 * Guesses whether a Unix mount can be ejected.
1909 *
1910 * Returns: %TRUE if @mount_entry is deemed to be ejectable.
1911 */
1912 gboolean
g_unix_mount_guess_can_eject(GUnixMountEntry * mount_entry)1913 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
1914 {
1915 GUnixMountType guessed_type;
1916
1917 guessed_type = g_unix_mount_guess_type (mount_entry);
1918 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
1919 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
1920 return TRUE;
1921
1922 return FALSE;
1923 }
1924
1925 /**
1926 * g_unix_mount_guess_should_display:
1927 * @mount_entry: a #GUnixMountEntry
1928 *
1929 * Guesses whether a Unix mount should be displayed in the UI.
1930 *
1931 * Returns: %TRUE if @mount_entry is deemed to be displayable.
1932 */
1933 gboolean
g_unix_mount_guess_should_display(GUnixMountEntry * mount_entry)1934 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
1935 {
1936 const char *mount_path;
1937
1938 /* Never display internal mountpoints */
1939 if (g_unix_mount_is_system_internal (mount_entry))
1940 return FALSE;
1941
1942 /* Only display things in /media (which are generally user mountable)
1943 and home dir (fuse stuff) */
1944 mount_path = mount_entry->mount_path;
1945 if (mount_path != NULL)
1946 {
1947 if (g_str_has_prefix (mount_path, "/media/"))
1948 {
1949 char *path;
1950 /* Avoid displaying mounts that are not accessible to the user.
1951 *
1952 * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
1953 * want to avoid g_access() for mount points which can potentially
1954 * block or fail stat()'ing, such as network mounts.
1955 */
1956 path = g_path_get_dirname (mount_path);
1957 if (g_str_has_prefix (path, "/media/"))
1958 {
1959 if (g_access (path, R_OK|X_OK) != 0)
1960 {
1961 g_free (path);
1962 return FALSE;
1963 }
1964 }
1965 g_free (path);
1966
1967 if (mount_entry->device_path && mount_entry->device_path[0] == '/')
1968 {
1969 struct stat st;
1970 if (g_stat (mount_entry->device_path, &st) == 0 &&
1971 S_ISBLK(st.st_mode) &&
1972 g_access (mount_path, R_OK|X_OK) != 0)
1973 return FALSE;
1974 }
1975 return TRUE;
1976 }
1977
1978 if (g_str_has_prefix (mount_path, g_get_home_dir ()) &&
1979 mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
1980 return TRUE;
1981 }
1982
1983 return FALSE;
1984 }
1985
1986 /**
1987 * g_unix_mount_point_guess_can_eject:
1988 * @mount_point: a #GUnixMountPoint
1989 *
1990 * Guesses whether a Unix mount point can be ejected.
1991 *
1992 * Returns: %TRUE if @mount_point is deemed to be ejectable.
1993 */
1994 gboolean
g_unix_mount_point_guess_can_eject(GUnixMountPoint * mount_point)1995 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
1996 {
1997 GUnixMountType guessed_type;
1998
1999 guessed_type = g_unix_mount_point_guess_type (mount_point);
2000 if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
2001 guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
2002 return TRUE;
2003
2004 return FALSE;
2005 }
2006
2007
2008 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
2009 static void
_canonicalize_filename(gchar * filename)2010 _canonicalize_filename (gchar *filename)
2011 {
2012 gchar *p, *q;
2013 gboolean last_was_slash = FALSE;
2014
2015 p = filename;
2016 q = filename;
2017
2018 while (*p)
2019 {
2020 if (*p == G_DIR_SEPARATOR)
2021 {
2022 if (!last_was_slash)
2023 *q++ = G_DIR_SEPARATOR;
2024
2025 last_was_slash = TRUE;
2026 }
2027 else
2028 {
2029 if (last_was_slash && *p == '.')
2030 {
2031 if (*(p + 1) == G_DIR_SEPARATOR ||
2032 *(p + 1) == '\0')
2033 {
2034 if (*(p + 1) == '\0')
2035 break;
2036
2037 p += 1;
2038 }
2039 else if (*(p + 1) == '.' &&
2040 (*(p + 2) == G_DIR_SEPARATOR ||
2041 *(p + 2) == '\0'))
2042 {
2043 if (q > filename + 1)
2044 {
2045 q--;
2046 while (q > filename + 1 &&
2047 *(q - 1) != G_DIR_SEPARATOR)
2048 q--;
2049 }
2050
2051 if (*(p + 2) == '\0')
2052 break;
2053
2054 p += 2;
2055 }
2056 else
2057 {
2058 *q++ = *p;
2059 last_was_slash = FALSE;
2060 }
2061 }
2062 else
2063 {
2064 *q++ = *p;
2065 last_was_slash = FALSE;
2066 }
2067 }
2068
2069 p++;
2070 }
2071
2072 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2073 q--;
2074
2075 *q = '\0';
2076 }
2077
2078 static char *
_resolve_symlink(const char * file)2079 _resolve_symlink (const char *file)
2080 {
2081 GError *error;
2082 char *dir;
2083 char *link;
2084 char *f;
2085 char *f1;
2086
2087 f = g_strdup (file);
2088
2089 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK))
2090 {
2091 link = g_file_read_link (f, &error);
2092 if (link == NULL)
2093 {
2094 g_error_free (error);
2095 g_free (f);
2096 f = NULL;
2097 goto out;
2098 }
2099
2100 dir = g_path_get_dirname (f);
2101 f1 = g_strdup_printf ("%s/%s", dir, link);
2102 g_free (dir);
2103 g_free (link);
2104 g_free (f);
2105 f = f1;
2106 }
2107
2108 out:
2109 if (f != NULL)
2110 _canonicalize_filename (f);
2111 return f;
2112 }
2113
2114 #ifdef HAVE_MNTENT_H
2115 static const char *
_resolve_dev_root(void)2116 _resolve_dev_root (void)
2117 {
2118 static gboolean have_real_dev_root = FALSE;
2119 static char real_dev_root[256];
2120 struct stat statbuf;
2121
2122 /* see if it's cached already */
2123 if (have_real_dev_root)
2124 goto found;
2125
2126 /* otherwise we're going to find it right away.. */
2127 have_real_dev_root = TRUE;
2128
2129 if (stat ("/dev/root", &statbuf) == 0)
2130 {
2131 if (! S_ISLNK (statbuf.st_mode))
2132 {
2133 dev_t root_dev = statbuf.st_dev;
2134 FILE *f;
2135 char buf[1024];
2136
2137 /* see if device with similar major:minor as /dev/root is mention
2138 * in /etc/mtab (it usually is)
2139 */
2140 f = fopen ("/etc/mtab", "r");
2141 if (f != NULL)
2142 {
2143 struct mntent *entp;
2144 #ifdef HAVE_GETMNTENT_R
2145 struct mntent ent;
2146 while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL)
2147 {
2148 #else
2149 G_LOCK (getmntent);
2150 while ((entp = getmntent (f)) != NULL)
2151 {
2152 #endif
2153 if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2154 statbuf.st_dev == root_dev)
2155 {
2156 strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2157 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2158 fclose (f);
2159 goto found;
2160 }
2161 }
2162
2163 endmntent (f);
2164
2165 #ifndef HAVE_GETMNTENT_R
2166 G_UNLOCK (getmntent);
2167 #endif
2168 }
2169
2170 /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2171
2172 }
2173 else
2174 {
2175 char *resolved;
2176 resolved = _resolve_symlink ("/dev/root");
2177 if (resolved != NULL)
2178 {
2179 strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2180 real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2181 g_free (resolved);
2182 goto found;
2183 }
2184 }
2185 }
2186
2187 /* bah sucks.. */
2188 strcpy (real_dev_root, "/dev/root");
2189
2190 found:
2191 return real_dev_root;
2192 }
2193 #endif
2194
2195 #define __G_UNIX_MOUNTS_C__
2196 #include "gioaliasdef.c"
2197