0277eeb8cf86be4ed7ca0f8145d806921b97d59c
[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    * 
1148    * Emitted when the unix mounts have changed.
1149    **/ 
1150   signals[MOUNTS_CHANGED] =
1151     g_signal_new ("mounts_changed",
1152                   G_TYPE_FROM_CLASS (klass),
1153                   G_SIGNAL_RUN_LAST,
1154                   0,
1155                   NULL, NULL,
1156                   g_cclosure_marshal_VOID__VOID,
1157                   G_TYPE_NONE, 0);
1158   /**
1159    * GUnixMountMonitor::mountpoints-changed:
1160    * 
1161    * Emitted when the unix mount points have changed.
1162    **/
1163   signals[MOUNTPOINTS_CHANGED] =
1164     g_signal_new ("mountpoints_changed",
1165                   G_TYPE_FROM_CLASS (klass),
1166                   G_SIGNAL_RUN_LAST,
1167                   0,
1168                   NULL, NULL,
1169                   g_cclosure_marshal_VOID__VOID,
1170                   G_TYPE_NONE, 0);
1171 }
1172
1173 static void
1174 fstab_file_changed (GFileMonitor      *monitor,
1175                     GFile             *file,
1176                     GFile             *other_file,
1177                     GFileMonitorEvent  event_type,
1178                     gpointer           user_data)
1179 {
1180   GUnixMountMonitor *mount_monitor;
1181
1182   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1183       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1184       event_type != G_FILE_MONITOR_EVENT_DELETED)
1185     return;
1186
1187   mount_monitor = user_data;
1188   g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
1189 }
1190
1191 static void
1192 mtab_file_changed (GFileMonitor      *monitor,
1193                    GFile             *file,
1194                    GFile             *other_file,
1195                    GFileMonitorEvent  event_type,
1196                    gpointer           user_data)
1197 {
1198   GUnixMountMonitor *mount_monitor;
1199
1200   if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1201       event_type != G_FILE_MONITOR_EVENT_CREATED &&
1202       event_type != G_FILE_MONITOR_EVENT_DELETED)
1203     return;
1204   
1205   mount_monitor = user_data;
1206   g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
1207 }
1208
1209 static void
1210 g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
1211 {
1212   GFile *file;
1213     
1214   if (get_fstab_file () != NULL)
1215     {
1216       file = g_file_new_for_path (get_fstab_file ());
1217       monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1218       g_object_unref (file);
1219       
1220       g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
1221     }
1222   
1223   if (get_mtab_monitor_file () != NULL)
1224     {
1225       file = g_file_new_for_path (get_mtab_monitor_file ());
1226       monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
1227       g_object_unref (file);
1228       
1229       g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
1230     }
1231 }
1232
1233 /**
1234  * g_unix_mount_monitor_set_rate_limit:
1235  * @mount_monitor: a #GUnixMountMonitor
1236  * @limit_msec: a integer with the limit in milliseconds to
1237  *     poll for changes.
1238  *
1239  * Sets the rate limit to which the @mount_monitor will report
1240  * consecutive change events to the mount and mount point entry files.
1241  *
1242  * Since: 2.18
1243  **/
1244 void
1245 g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
1246                                      int                limit_msec)
1247 {
1248   g_return_if_fail (G_IS_UNIX_MOUNT_MONITOR (mount_monitor));
1249
1250   if (mount_monitor->fstab_monitor != NULL)
1251     g_file_monitor_set_rate_limit (mount_monitor->fstab_monitor, limit_msec);
1252
1253   if (mount_monitor->mtab_monitor != NULL)
1254     g_file_monitor_set_rate_limit (mount_monitor->mtab_monitor, limit_msec);
1255 }
1256
1257 /**
1258  * g_unix_mount_monitor_new:
1259  * 
1260  * Gets a new #GUnixMountMonitor. The default rate limit for which the
1261  * monitor will report consecutive changes for the mount and mount
1262  * point entry files is the default for a #GFileMonitor. Use
1263  * g_unix_mount_monitor_set_rate_limit() to change this.
1264  * 
1265  * Returns: a #GUnixMountMonitor. 
1266  **/
1267 GUnixMountMonitor *
1268 g_unix_mount_monitor_new (void)
1269 {
1270   if (the_mount_monitor == NULL)
1271     {
1272       the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
1273       return the_mount_monitor;
1274     }
1275   
1276   return g_object_ref (the_mount_monitor);
1277 }
1278
1279 /**
1280  * g_unix_mount_free:
1281  * @mount_entry: a #GUnixMount.
1282  * 
1283  * Frees a unix mount.
1284  **/
1285 void
1286 g_unix_mount_free (GUnixMountEntry *mount_entry)
1287 {
1288   g_return_if_fail (mount_entry != NULL);
1289
1290   g_free (mount_entry->mount_path);
1291   g_free (mount_entry->device_path);
1292   g_free (mount_entry->filesystem_type);
1293   g_free (mount_entry);
1294 }
1295
1296 /**
1297  * g_unix_mount_point_free:
1298  * @mount_point: unix mount point to free.
1299  * 
1300  * Frees a unix mount point.
1301  **/
1302 void
1303 g_unix_mount_point_free (GUnixMountPoint *mount_point)
1304 {
1305   g_return_if_fail (mount_point != NULL);
1306
1307   g_free (mount_point->mount_path);
1308   g_free (mount_point->device_path);
1309   g_free (mount_point->filesystem_type);
1310   g_free (mount_point);
1311 }
1312
1313 /**
1314  * g_unix_mount_compare:
1315  * @mount1: first #GUnixMountEntry to compare.
1316  * @mount2: second #GUnixMountEntry to compare.
1317  * 
1318  * Compares two unix mounts.
1319  * 
1320  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1321  * or less than @mount2, respectively. 
1322  **/
1323 gint
1324 g_unix_mount_compare (GUnixMountEntry *mount1,
1325                       GUnixMountEntry *mount2)
1326 {
1327   int res;
1328
1329   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1330   
1331   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1332   if (res != 0)
1333     return res;
1334         
1335   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1336   if (res != 0)
1337     return res;
1338         
1339   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1340   if (res != 0)
1341     return res;
1342
1343   res =  mount1->is_read_only - mount2->is_read_only;
1344   if (res != 0)
1345     return res;
1346   
1347   return 0;
1348 }
1349
1350 /**
1351  * g_unix_mount_get_mount_path:
1352  * @mount_entry: input #GUnixMountEntry to get the mount path for.
1353  * 
1354  * Gets the mount path for a unix mount.
1355  * 
1356  * Returns: the mount path for @mount_entry.
1357  **/
1358 const char *
1359 g_unix_mount_get_mount_path (GUnixMountEntry *mount_entry)
1360 {
1361   g_return_val_if_fail (mount_entry != NULL, NULL);
1362
1363   return mount_entry->mount_path;
1364 }
1365
1366 /**
1367  * g_unix_mount_get_device_path:
1368  * @mount_entry: a #GUnixMount.
1369  * 
1370  * Gets the device path for a unix mount.
1371  * 
1372  * Returns: a string containing the device path.
1373  **/
1374 const char *
1375 g_unix_mount_get_device_path (GUnixMountEntry *mount_entry)
1376 {
1377   g_return_val_if_fail (mount_entry != NULL, NULL);
1378
1379   return mount_entry->device_path;
1380 }
1381
1382 /**
1383  * g_unix_mount_get_fs_type:
1384  * @mount_entry: a #GUnixMount.
1385  * 
1386  * Gets the filesystem type for the unix mount.
1387  * 
1388  * Returns: a string containing the file system type.
1389  **/
1390 const char *
1391 g_unix_mount_get_fs_type (GUnixMountEntry *mount_entry)
1392 {
1393   g_return_val_if_fail (mount_entry != NULL, NULL);
1394
1395   return mount_entry->filesystem_type;
1396 }
1397
1398 /**
1399  * g_unix_mount_is_readonly:
1400  * @mount_entry: a #GUnixMount.
1401  * 
1402  * Checks if a unix mount is mounted read only.
1403  * 
1404  * Returns: %TRUE if @mount_entry is read only.
1405  **/
1406 gboolean
1407 g_unix_mount_is_readonly (GUnixMountEntry *mount_entry)
1408 {
1409   g_return_val_if_fail (mount_entry != NULL, FALSE);
1410
1411   return mount_entry->is_read_only;
1412 }
1413
1414 /**
1415  * g_unix_mount_is_system_internal:
1416  * @mount_entry: a #GUnixMount.
1417  * 
1418  * Checks if a unix mount is a system path.
1419  * 
1420  * Returns: %TRUE if the unix mount is for a system path.
1421  **/
1422 gboolean
1423 g_unix_mount_is_system_internal (GUnixMountEntry *mount_entry)
1424 {
1425   g_return_val_if_fail (mount_entry != NULL, FALSE);
1426
1427   return mount_entry->is_system_internal;
1428 }
1429
1430 /**
1431  * g_unix_mount_point_compare:
1432  * @mount1: a #GUnixMount.
1433  * @mount2: a #GUnixMount.
1434  * 
1435  * Compares two unix mount points.
1436  * 
1437  * Returns: 1, 0 or -1 if @mount1 is greater than, equal to,
1438  * or less than @mount2, respectively.
1439  **/
1440 gint
1441 g_unix_mount_point_compare (GUnixMountPoint *mount1,
1442                             GUnixMountPoint *mount2)
1443 {
1444   int res;
1445
1446   g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
1447
1448   res = g_strcmp0 (mount1->mount_path, mount2->mount_path);
1449   if (res != 0) 
1450     return res;
1451         
1452   res = g_strcmp0 (mount1->device_path, mount2->device_path);
1453   if (res != 0) 
1454     return res;
1455         
1456   res = g_strcmp0 (mount1->filesystem_type, mount2->filesystem_type);
1457   if (res != 0) 
1458     return res;
1459
1460   res =  mount1->is_read_only - mount2->is_read_only;
1461   if (res != 0) 
1462     return res;
1463
1464   res = mount1->is_user_mountable - mount2->is_user_mountable;
1465   if (res != 0) 
1466     return res;
1467
1468   res = mount1->is_loopback - mount2->is_loopback;
1469   if (res != 0)
1470     return res;
1471   
1472   return 0;
1473 }
1474
1475 /**
1476  * g_unix_mount_point_get_mount_path:
1477  * @mount_point: a #GUnixMountPoint.
1478  * 
1479  * Gets the mount path for a unix mount point.
1480  * 
1481  * Returns: a string containing the mount path.
1482  **/
1483 const char *
1484 g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
1485 {
1486   g_return_val_if_fail (mount_point != NULL, NULL);
1487
1488   return mount_point->mount_path;
1489 }
1490
1491 /**
1492  * g_unix_mount_point_get_device_path:
1493  * @mount_point: a #GUnixMountPoint.
1494  * 
1495  * Gets the device path for a unix mount point.
1496  * 
1497  * Returns: a string containing the device path.
1498  **/
1499 const char *
1500 g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
1501 {
1502   g_return_val_if_fail (mount_point != NULL, NULL);
1503
1504   return mount_point->device_path;
1505 }
1506
1507 /**
1508  * g_unix_mount_point_get_fs_type:
1509  * @mount_point: a #GUnixMountPoint.
1510  * 
1511  * Gets the file system type for the mount point.
1512  * 
1513  * Returns: a string containing the file system type.
1514  **/
1515 const char *
1516 g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
1517 {
1518   g_return_val_if_fail (mount_point != NULL, NULL);
1519
1520   return mount_point->filesystem_type;
1521 }
1522
1523 /**
1524  * g_unix_mount_point_is_readonly:
1525  * @mount_point: a #GUnixMountPoint.
1526  * 
1527  * Checks if a unix mount point is read only.
1528  * 
1529  * Returns: %TRUE if a mount point is read only.
1530  **/
1531 gboolean
1532 g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
1533 {
1534   g_return_val_if_fail (mount_point != NULL, FALSE);
1535
1536   return mount_point->is_read_only;
1537 }
1538
1539 /**
1540  * g_unix_mount_point_is_user_mountable:
1541  * @mount_point: a #GUnixMountPoint.
1542  * 
1543  * Checks if a unix mount point is mountable by the user.
1544  * 
1545  * Returns: %TRUE if the mount point is user mountable.
1546  **/
1547 gboolean
1548 g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
1549 {
1550   g_return_val_if_fail (mount_point != NULL, FALSE);
1551
1552   return mount_point->is_user_mountable;
1553 }
1554
1555 /**
1556  * g_unix_mount_point_is_loopback:
1557  * @mount_point: a #GUnixMountPoint.
1558  * 
1559  * Checks if a unix mount point is a loopback device.
1560  * 
1561  * Returns: %TRUE if the mount point is a loopback. %FALSE otherwise. 
1562  **/
1563 gboolean
1564 g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
1565 {
1566   g_return_val_if_fail (mount_point != NULL, FALSE);
1567
1568   return mount_point->is_loopback;
1569 }
1570
1571 static GUnixMountType
1572 guess_mount_type (const char *mount_path,
1573                   const char *device_path,
1574                   const char *filesystem_type)
1575 {
1576   GUnixMountType type;
1577   char *basename;
1578
1579   type = G_UNIX_MOUNT_TYPE_UNKNOWN;
1580   
1581   if ((strcmp (filesystem_type, "udf") == 0) ||
1582       (strcmp (filesystem_type, "iso9660") == 0) ||
1583       (strcmp (filesystem_type, "cd9660") == 0))
1584     type = G_UNIX_MOUNT_TYPE_CDROM;
1585   else if ((strcmp (filesystem_type, "nfs") == 0) ||
1586            (strcmp (filesystem_type, "nfs4") == 0))
1587     type = G_UNIX_MOUNT_TYPE_NFS;
1588   else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
1589            g_str_has_prefix (device_path, "/dev/fd") ||
1590            g_str_has_prefix (device_path, "/dev/floppy"))
1591     type = G_UNIX_MOUNT_TYPE_FLOPPY;
1592   else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
1593            g_str_has_prefix (device_path, "/dev/acd") ||
1594            g_str_has_prefix (device_path, "/dev/cd"))
1595     type = G_UNIX_MOUNT_TYPE_CDROM;
1596   else if (g_str_has_prefix (device_path, "/vol/"))
1597     {
1598       const char *name = mount_path + strlen ("/");
1599       
1600       if (g_str_has_prefix (name, "cdrom"))
1601         type = G_UNIX_MOUNT_TYPE_CDROM;
1602       else if (g_str_has_prefix (name, "floppy") ||
1603                g_str_has_prefix (device_path, "/vol/dev/diskette/")) 
1604         type = G_UNIX_MOUNT_TYPE_FLOPPY;
1605       else if (g_str_has_prefix (name, "rmdisk")) 
1606         type = G_UNIX_MOUNT_TYPE_ZIP;
1607       else if (g_str_has_prefix (name, "jaz"))
1608         type = G_UNIX_MOUNT_TYPE_JAZ;
1609       else if (g_str_has_prefix (name, "memstick"))
1610         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1611     }
1612   else
1613     {
1614       basename = g_path_get_basename (mount_path);
1615       
1616       if (g_str_has_prefix (basename, "cdr") ||
1617           g_str_has_prefix (basename, "cdwriter") ||
1618           g_str_has_prefix (basename, "burn") ||
1619           g_str_has_prefix (basename, "dvdr"))
1620         type = G_UNIX_MOUNT_TYPE_CDROM;
1621       else if (g_str_has_prefix (basename, "floppy"))
1622         type = G_UNIX_MOUNT_TYPE_FLOPPY;
1623       else if (g_str_has_prefix (basename, "zip"))
1624         type = G_UNIX_MOUNT_TYPE_ZIP;
1625       else if (g_str_has_prefix (basename, "jaz"))
1626         type = G_UNIX_MOUNT_TYPE_JAZ;
1627       else if (g_str_has_prefix (basename, "camera"))
1628         type = G_UNIX_MOUNT_TYPE_CAMERA;
1629       else if (g_str_has_prefix (basename, "memstick") ||
1630                g_str_has_prefix (basename, "memory_stick") ||
1631                g_str_has_prefix (basename, "ram"))
1632         type = G_UNIX_MOUNT_TYPE_MEMSTICK;
1633       else if (g_str_has_prefix (basename, "compact_flash"))
1634         type = G_UNIX_MOUNT_TYPE_CF;
1635       else if (g_str_has_prefix (basename, "smart_media"))
1636         type = G_UNIX_MOUNT_TYPE_SM;
1637       else if (g_str_has_prefix (basename, "sd_mmc"))
1638         type = G_UNIX_MOUNT_TYPE_SDMMC;
1639       else if (g_str_has_prefix (basename, "ipod"))
1640         type = G_UNIX_MOUNT_TYPE_IPOD;
1641       
1642       g_free (basename);
1643     }
1644   
1645   if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
1646     type = G_UNIX_MOUNT_TYPE_HD;
1647   
1648   return type;
1649 }
1650
1651 /*
1652  * g_unix_mount_guess_type:
1653  * @mount_entry: a #GUnixMount.
1654  * 
1655  * Guesses the type of a unix mount. If the mount type cannot be 
1656  * determined, returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1657  * 
1658  * Returns: a #GUnixMountType. 
1659  **/
1660 static GUnixMountType
1661 g_unix_mount_guess_type (GUnixMountEntry *mount_entry)
1662 {
1663   g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1664   g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1665   g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1666   g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1667
1668   return guess_mount_type (mount_entry->mount_path,
1669                            mount_entry->device_path,
1670                            mount_entry->filesystem_type);
1671 }
1672
1673 /*
1674  * g_unix_mount_point_guess_type:
1675  * @mount_point: a #GUnixMountPoint.
1676  * 
1677  * Guesses the type of a unix mount point. 
1678  * If the mount type cannot be determined, 
1679  * returns %G_UNIX_MOUNT_TYPE_UNKNOWN.
1680  * 
1681  * Returns: a #GUnixMountType.
1682  **/
1683 static GUnixMountType
1684 g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
1685 {
1686   g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1687   g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1688   g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1689   g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
1690
1691   return guess_mount_type (mount_point->mount_path,
1692                            mount_point->device_path,
1693                            mount_point->filesystem_type);
1694 }
1695
1696 static const char *
1697 type_to_icon (GUnixMountType type, gboolean is_mount_point)
1698 {
1699   const char *icon_name;
1700   
1701   switch (type)
1702     {
1703     case G_UNIX_MOUNT_TYPE_HD:
1704       if (is_mount_point)
1705         icon_name = "drive-removable-media";
1706       else
1707         icon_name = "drive-harddisk";
1708       break;
1709     case G_UNIX_MOUNT_TYPE_FLOPPY:
1710     case G_UNIX_MOUNT_TYPE_ZIP:
1711     case G_UNIX_MOUNT_TYPE_JAZ:
1712       if (is_mount_point)
1713         icon_name = "drive-removable-media";
1714       else
1715         icon_name = "media-floppy";
1716       break;
1717     case G_UNIX_MOUNT_TYPE_CDROM:
1718       if (is_mount_point)
1719         icon_name = "drive-optical";
1720       else
1721         icon_name = "media-optical";
1722       break;
1723     case G_UNIX_MOUNT_TYPE_NFS:
1724       /* TODO: Would like a better icon here... */
1725       if (is_mount_point)
1726         icon_name = "drive-removable-media";
1727       else
1728         icon_name = "drive-harddisk";
1729       break;
1730     case G_UNIX_MOUNT_TYPE_MEMSTICK:
1731       if (is_mount_point)
1732         icon_name = "drive-removable-media";
1733       else
1734         icon_name = "media-flash";
1735       break;
1736     case G_UNIX_MOUNT_TYPE_CAMERA:
1737       if (is_mount_point)
1738         icon_name = "drive-removable-media";
1739       else
1740         icon_name = "camera-photo";
1741       break;
1742     case G_UNIX_MOUNT_TYPE_IPOD:
1743       if (is_mount_point)
1744         icon_name = "drive-removable-media";
1745       else
1746         icon_name = "multimedia-player";
1747       break;
1748     case G_UNIX_MOUNT_TYPE_UNKNOWN:
1749     default:
1750       if (is_mount_point)
1751         icon_name = "drive-removable-media";
1752       else
1753         icon_name = "drive-harddisk";
1754       break;
1755     }
1756
1757   return icon_name;
1758 }
1759
1760 /**
1761  * g_unix_mount_guess_name:
1762  * @mount_entry: a #GUnixMountEntry
1763  * 
1764  * Guesses the name of a Unix mount. 
1765  * The result is a translated string.
1766  *
1767  * Returns: A newly allocated string that must
1768  *     be freed with g_free()
1769  */
1770 char *
1771 g_unix_mount_guess_name (GUnixMountEntry *mount_entry)
1772 {
1773   char *name;
1774
1775   if (strcmp (mount_entry->mount_path, "/") == 0)
1776     name = g_strdup (_("Filesystem root"));
1777   else
1778     name = g_filename_display_basename (mount_entry->mount_path);
1779
1780   return name;
1781 }
1782
1783 /**
1784  * g_unix_mount_guess_icon:
1785  * @mount_entry: a #GUnixMountEntry
1786  * 
1787  * Guesses the icon of a Unix mount. 
1788  *
1789  * Returns: a #GIcon
1790  */
1791 GIcon *
1792 g_unix_mount_guess_icon (GUnixMountEntry *mount_entry)
1793 {
1794   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_guess_type (mount_entry), FALSE));
1795 }
1796
1797 /**
1798  * g_unix_mount_point_guess_name:
1799  * @mount_point: a #GUnixMountPoint
1800  * 
1801  * Guesses the name of a Unix mount point. 
1802  * The result is a translated string.
1803  *
1804  * Returns: A newly allocated string that must 
1805  *     be freed with g_free()
1806  */
1807 char *
1808 g_unix_mount_point_guess_name (GUnixMountPoint *mount_point)
1809 {
1810   char *name;
1811
1812   if (strcmp (mount_point->mount_path, "/") == 0)
1813     name = g_strdup (_("Filesystem root"));
1814   else
1815     name = g_filename_display_basename (mount_point->mount_path);
1816
1817   return name;
1818 }
1819
1820 /**
1821  * g_unix_mount_point_guess_icon:
1822  * @mount_point: a #GUnixMountPoint
1823  * 
1824  * Guesses the icon of a Unix mount point. 
1825  *
1826  * Returns: a #GIcon
1827  */
1828 GIcon *
1829 g_unix_mount_point_guess_icon (GUnixMountPoint *mount_point)
1830 {
1831   return g_themed_icon_new_with_default_fallbacks (type_to_icon (g_unix_mount_point_guess_type (mount_point), TRUE));
1832 }
1833
1834 /**
1835  * g_unix_mount_guess_can_eject:
1836  * @mount_entry: a #GUnixMountEntry
1837  * 
1838  * Guesses whether a Unix mount can be ejected.
1839  *
1840  * Returns: %TRUE if @mount_entry is deemed to be ejectable.
1841  */
1842 gboolean
1843 g_unix_mount_guess_can_eject (GUnixMountEntry *mount_entry)
1844 {
1845   GUnixMountType guessed_type;
1846
1847   guessed_type = g_unix_mount_guess_type (mount_entry);
1848   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
1849       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
1850     return TRUE;
1851
1852   return FALSE;
1853 }
1854
1855 /**
1856  * g_unix_mount_guess_should_display:
1857  * @mount_entry: a #GUnixMountEntry
1858  * 
1859  * Guesses whether a Unix mount should be displayed in the UI.
1860  *
1861  * Returns: %TRUE if @mount_entry is deemed to be displayable.
1862  */
1863 gboolean
1864 g_unix_mount_guess_should_display (GUnixMountEntry *mount_entry)
1865 {
1866   const char *mount_path;
1867
1868   /* Never display internal mountpoints */
1869   if (g_unix_mount_is_system_internal (mount_entry))
1870     return FALSE;
1871   
1872   /* Only display things in /media (which are generally user mountable)
1873      and home dir (fuse stuff) */
1874   mount_path = mount_entry->mount_path;
1875   if (mount_path != NULL)
1876     {
1877       if (g_str_has_prefix (mount_path, "/media/")) {
1878         char *path;
1879         /* Avoid displaying mounts that are not accessible to the user.
1880          *
1881          * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
1882          * want to avoid g_access() for every mount point.
1883          */
1884         path = g_path_get_dirname (mount_path);
1885         if (g_str_has_prefix (path, "/media/"))
1886           {
1887             if (g_access (path, R_OK|X_OK) != 0) {
1888               g_free (path);
1889               return FALSE;
1890             }
1891           }
1892         g_free (path);
1893         return TRUE;
1894       }
1895       
1896       if (g_str_has_prefix (mount_path, g_get_home_dir ()) && mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
1897         return TRUE;
1898     }
1899   
1900   return FALSE;
1901 }
1902
1903 /**
1904  * g_unix_mount_point_guess_can_eject:
1905  * @mount_point: a #GUnixMountPoint
1906  * 
1907  * Guesses whether a Unix mount point can be ejected.
1908  *
1909  * Returns: %TRUE if @mount_point is deemed to be ejectable.
1910  */
1911 gboolean
1912 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
1913 {
1914   GUnixMountType guessed_type;
1915
1916   guessed_type = g_unix_mount_point_guess_type (mount_point);
1917   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
1918       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
1919     return TRUE;
1920
1921   return FALSE;
1922 }
1923
1924
1925 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
1926 static void
1927 _canonicalize_filename (gchar *filename)
1928 {
1929   gchar *p, *q;
1930   gboolean last_was_slash = FALSE;
1931   
1932   p = filename;
1933   q = filename;
1934   
1935   while (*p)
1936     {
1937       if (*p == G_DIR_SEPARATOR)
1938         {
1939           if (!last_was_slash)
1940             *q++ = G_DIR_SEPARATOR;
1941           
1942           last_was_slash = TRUE;
1943         }
1944       else
1945         {
1946           if (last_was_slash && *p == '.')
1947             {
1948               if (*(p + 1) == G_DIR_SEPARATOR ||
1949                   *(p + 1) == '\0')
1950                 {
1951                   if (*(p + 1) == '\0')
1952                     break;
1953                   
1954                   p += 1;
1955                 }
1956               else if (*(p + 1) == '.' &&
1957                        (*(p + 2) == G_DIR_SEPARATOR ||
1958                         *(p + 2) == '\0'))
1959                 {
1960                   if (q > filename + 1)
1961                     {
1962                       q--;
1963                       while (q > filename + 1 &&
1964                              *(q - 1) != G_DIR_SEPARATOR)
1965                         q--;
1966                     }
1967                   
1968                   if (*(p + 2) == '\0')
1969                     break;
1970                   
1971                   p += 2;
1972                 }
1973               else
1974                 {
1975                   *q++ = *p;
1976                   last_was_slash = FALSE;
1977                 }
1978             }
1979           else
1980             {
1981               *q++ = *p;
1982               last_was_slash = FALSE;
1983             }
1984         }
1985       
1986       p++;
1987     }
1988   
1989   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
1990     q--;
1991   
1992   *q = '\0';
1993 }
1994
1995 static char *
1996 _resolve_symlink (const char *file)
1997 {
1998   GError *error;
1999   char *dir;
2000   char *link;
2001   char *f;
2002   char *f1;
2003   
2004   f = g_strdup (file);
2005   
2006   while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) {
2007     link = g_file_read_link (f, &error);
2008     if (link == NULL) {
2009       g_error_free (error);
2010       g_free (f);
2011       f = NULL;
2012       goto out;
2013     }
2014     
2015     dir = g_path_get_dirname (f);
2016     f1 = g_strdup_printf ("%s/%s", dir, link);
2017     g_free (dir);
2018     g_free (link);
2019     g_free (f);
2020     f = f1;
2021   }
2022   
2023  out:
2024   if (f != NULL)
2025     _canonicalize_filename (f);
2026   return f;
2027 }
2028
2029 #ifdef HAVE_MNTENT_H
2030 static const char *
2031 _resolve_dev_root (void)
2032 {
2033   static gboolean have_real_dev_root = FALSE;
2034   static char real_dev_root[256];
2035   struct stat statbuf;
2036   
2037   /* see if it's cached already */
2038   if (have_real_dev_root)
2039     goto found;
2040   
2041   /* otherwise we're going to find it right away.. */
2042   have_real_dev_root = TRUE;
2043   
2044   if (stat ("/dev/root", &statbuf) == 0) {
2045     if (! S_ISLNK (statbuf.st_mode)) {
2046       dev_t root_dev = statbuf.st_dev;
2047       FILE *f;
2048       char buf[1024];
2049       
2050       /* see if device with similar major:minor as /dev/root is mention
2051        * in /etc/mtab (it usually is) 
2052        */
2053       f = fopen ("/etc/mtab", "r");
2054       if (f != NULL) {
2055         struct mntent *entp;
2056 #ifdef HAVE_GETMNTENT_R        
2057         struct mntent ent;
2058         while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL) {
2059 #else
2060         G_LOCK (getmntent);
2061         while ((entp = getmntent (f)) != NULL) { 
2062 #endif          
2063           if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2064               statbuf.st_dev == root_dev) {
2065             strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2066             real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2067             fclose (f);
2068             goto found;
2069           }
2070         }
2071
2072         endmntent (f);
2073
2074 #ifndef HAVE_GETMNTENT_R
2075         G_UNLOCK (getmntent);
2076 #endif
2077       }                                        
2078       
2079       /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2080       
2081     } else {
2082       char *resolved;
2083       resolved = _resolve_symlink ("/dev/root");
2084       if (resolved != NULL) {
2085         strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2086         real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2087         g_free (resolved);
2088         goto found;
2089       }
2090     }
2091   }
2092   
2093   /* bah sucks.. */
2094   strcpy (real_dev_root, "/dev/root");
2095   
2096  found:
2097   return real_dev_root;
2098 }
2099 #endif
2100
2101 #define __G_UNIX_MOUNTS_C__
2102 #include "gioaliasdef.c"