Fix a doc typo
[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 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         char *path;
1892         /* Avoid displaying mounts that are not accessible to the user.
1893          *
1894          * See http://bugzilla.gnome.org/show_bug.cgi?id=526320 for why we
1895          * want to avoid g_access() for every mount point.
1896          */
1897         path = g_path_get_dirname (mount_path);
1898         if (g_str_has_prefix (path, "/media/"))
1899           {
1900             if (g_access (path, R_OK|X_OK) != 0) {
1901               g_free (path);
1902               return FALSE;
1903             }
1904           }
1905         g_free (path);
1906         return TRUE;
1907       }
1908       
1909       if (g_str_has_prefix (mount_path, g_get_home_dir ()) && mount_path[strlen (g_get_home_dir())] == G_DIR_SEPARATOR)
1910         return TRUE;
1911     }
1912   
1913   return FALSE;
1914 }
1915
1916 /**
1917  * g_unix_mount_point_guess_can_eject:
1918  * @mount_point: a #GUnixMountPoint
1919  * 
1920  * Guesses whether a Unix mount point can be ejected.
1921  *
1922  * Returns: %TRUE if @mount_point is deemed to be ejectable.
1923  */
1924 gboolean
1925 g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
1926 {
1927   GUnixMountType guessed_type;
1928
1929   guessed_type = g_unix_mount_point_guess_type (mount_point);
1930   if (guessed_type == G_UNIX_MOUNT_TYPE_IPOD ||
1931       guessed_type == G_UNIX_MOUNT_TYPE_CDROM)
1932     return TRUE;
1933
1934   return FALSE;
1935 }
1936
1937
1938 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
1939 static void
1940 _canonicalize_filename (gchar *filename)
1941 {
1942   gchar *p, *q;
1943   gboolean last_was_slash = FALSE;
1944   
1945   p = filename;
1946   q = filename;
1947   
1948   while (*p)
1949     {
1950       if (*p == G_DIR_SEPARATOR)
1951         {
1952           if (!last_was_slash)
1953             *q++ = G_DIR_SEPARATOR;
1954           
1955           last_was_slash = TRUE;
1956         }
1957       else
1958         {
1959           if (last_was_slash && *p == '.')
1960             {
1961               if (*(p + 1) == G_DIR_SEPARATOR ||
1962                   *(p + 1) == '\0')
1963                 {
1964                   if (*(p + 1) == '\0')
1965                     break;
1966                   
1967                   p += 1;
1968                 }
1969               else if (*(p + 1) == '.' &&
1970                        (*(p + 2) == G_DIR_SEPARATOR ||
1971                         *(p + 2) == '\0'))
1972                 {
1973                   if (q > filename + 1)
1974                     {
1975                       q--;
1976                       while (q > filename + 1 &&
1977                              *(q - 1) != G_DIR_SEPARATOR)
1978                         q--;
1979                     }
1980                   
1981                   if (*(p + 2) == '\0')
1982                     break;
1983                   
1984                   p += 2;
1985                 }
1986               else
1987                 {
1988                   *q++ = *p;
1989                   last_was_slash = FALSE;
1990                 }
1991             }
1992           else
1993             {
1994               *q++ = *p;
1995               last_was_slash = FALSE;
1996             }
1997         }
1998       
1999       p++;
2000     }
2001   
2002   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
2003     q--;
2004   
2005   *q = '\0';
2006 }
2007
2008 static char *
2009 _resolve_symlink (const char *file)
2010 {
2011   GError *error;
2012   char *dir;
2013   char *link;
2014   char *f;
2015   char *f1;
2016   
2017   f = g_strdup (file);
2018   
2019   while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) {
2020     link = g_file_read_link (f, &error);
2021     if (link == NULL) {
2022       g_error_free (error);
2023       g_free (f);
2024       f = NULL;
2025       goto out;
2026     }
2027     
2028     dir = g_path_get_dirname (f);
2029     f1 = g_strdup_printf ("%s/%s", dir, link);
2030     g_free (dir);
2031     g_free (link);
2032     g_free (f);
2033     f = f1;
2034   }
2035   
2036  out:
2037   if (f != NULL)
2038     _canonicalize_filename (f);
2039   return f;
2040 }
2041
2042 #ifdef HAVE_MNTENT_H
2043 static const char *
2044 _resolve_dev_root (void)
2045 {
2046   static gboolean have_real_dev_root = FALSE;
2047   static char real_dev_root[256];
2048   struct stat statbuf;
2049   
2050   /* see if it's cached already */
2051   if (have_real_dev_root)
2052     goto found;
2053   
2054   /* otherwise we're going to find it right away.. */
2055   have_real_dev_root = TRUE;
2056   
2057   if (stat ("/dev/root", &statbuf) == 0) {
2058     if (! S_ISLNK (statbuf.st_mode)) {
2059       dev_t root_dev = statbuf.st_dev;
2060       FILE *f;
2061       char buf[1024];
2062       
2063       /* see if device with similar major:minor as /dev/root is mention
2064        * in /etc/mtab (it usually is) 
2065        */
2066       f = fopen ("/etc/mtab", "r");
2067       if (f != NULL) {
2068         struct mntent *entp;
2069 #ifdef HAVE_GETMNTENT_R        
2070         struct mntent ent;
2071         while ((entp = getmntent_r (f, &ent, buf, sizeof (buf))) != NULL) {
2072 #else
2073         G_LOCK (getmntent);
2074         while ((entp = getmntent (f)) != NULL) { 
2075 #endif          
2076           if (stat (entp->mnt_fsname, &statbuf) == 0 &&
2077               statbuf.st_dev == root_dev) {
2078             strncpy (real_dev_root, entp->mnt_fsname, sizeof (real_dev_root) - 1);
2079             real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2080             fclose (f);
2081             goto found;
2082           }
2083         }
2084
2085         endmntent (f);
2086
2087 #ifndef HAVE_GETMNTENT_R
2088         G_UNLOCK (getmntent);
2089 #endif
2090       }                                        
2091       
2092       /* no, that didn't work.. next we could scan /dev ... but I digress.. */
2093       
2094     } else {
2095       char *resolved;
2096       resolved = _resolve_symlink ("/dev/root");
2097       if (resolved != NULL) {
2098         strncpy (real_dev_root, resolved, sizeof (real_dev_root) - 1);
2099         real_dev_root[sizeof (real_dev_root) - 1] = '\0';
2100         g_free (resolved);
2101         goto found;
2102       }
2103     }
2104   }
2105   
2106   /* bah sucks.. */
2107   strcpy (real_dev_root, "/dev/root");
2108   
2109  found:
2110   return real_dev_root;
2111 }
2112 #endif
2113
2114 #define __G_UNIX_MOUNTS_C__
2115 #include "gioaliasdef.c"