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