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