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