*** empty log message ***
[gnulib.git] / lib / mountlist.c
1 /* mountlist.c -- return a list of mounted filesystems
2    Copyright (C) 1991, 1992, 1997, 1998, 1999 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 #if HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include "mountlist.h"
25
26 #ifdef STDC_HEADERS
27 # include <stdlib.h>
28 #else
29 void free ();
30 #endif
31 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
32 # include <string.h>
33 #else
34 # include <strings.h>
35 #endif
36
37 #ifndef strstr
38 char *strstr ();
39 #endif
40 char *xmalloc ();
41 char *xrealloc ();
42 char *xstrdup ();
43 void error ();
44
45 #include <errno.h>
46 #ifndef errno
47 extern int errno;
48 #endif
49
50 #ifdef HAVE_FCNTL_H
51 # include <fcntl.h>
52 #endif
53
54 #ifdef HAVE_UNISTD_H
55 # include <unistd.h>
56 #endif
57
58 #if HAVE_SYS_PARAM_H
59 # include <sys/param.h>
60 #endif
61
62 #if defined (MOUNTED_GETFSSTAT) /* __alpha running OSF_1 */
63 # include <sys/mount.h>
64 # include <sys/fs_types.h>
65 #endif /* MOUNTED_GETFSSTAT */
66
67 #ifdef MOUNTED_GETMNTENT1       /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
68 # include <mntent.h>
69 # if !defined(MOUNTED)
70 #  if defined(MNT_MNTTAB)       /* HP-UX.  */
71 #   define MOUNTED MNT_MNTTAB
72 #  endif
73 #  if defined(MNTTABNAME)       /* Dynix.  */
74 #   define MOUNTED MNTTABNAME
75 #  endif
76 # endif
77 #endif
78
79 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
80 # include <sys/mount.h>
81 #endif
82
83 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
84 # include <sys/mount.h>
85 # include <sys/fs_types.h>
86 #endif
87
88 #ifdef MOUNTED_FREAD            /* SVR2.  */
89 # include <mnttab.h>
90 #endif
91
92 #ifdef MOUNTED_FREAD_FSTYP      /* SVR3.  */
93 # include <mnttab.h>
94 # include <sys/fstyp.h>
95 # include <sys/statfs.h>
96 #endif
97
98 #ifdef MOUNTED_LISTMNTENT
99 # include <mntent.h>
100 #endif
101
102 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
103 # include <sys/mnttab.h>
104 #endif
105
106 #ifdef MOUNTED_VMOUNT           /* AIX.  */
107 # include <fshelp.h>
108 # include <sys/vfs.h>
109 #endif
110
111 #ifdef DOLPHIN
112 /* So special that it's not worth putting this in autoconf.  */
113 # undef MOUNTED_FREAD_FSTYP
114 # define MOUNTED_GETMNTTBL
115 #endif
116
117 #if HAVE_SYS_MNTENT_H
118 /* This is to get MNTOPT_IGNORE on e.g. SVR4.  */
119 # include <sys/mntent.h>
120 #endif
121
122 #if defined (MNTOPT_IGNORE) && defined (HAVE_HASMNTOPT)
123 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
124 #else
125 # define MNT_IGNORE(M) 0
126 #endif
127
128 #ifdef MOUNTED_GETMNTENT1       /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
129 /* Return the value of the hexadecimal number represented by CP.
130    No prefix (like '0x') or suffix (like 'h') is expected to be
131    part of CP. */
132 /* FIXME: this can overflow */
133
134 static int
135 xatoi (char *cp)
136 {
137   int val;
138
139   val = 0;
140   while (*cp)
141     {
142       if (*cp >= 'a' && *cp <= 'f')
143         val = val * 16 + *cp - 'a' + 10;
144       else if (*cp >= 'A' && *cp <= 'F')
145         val = val * 16 + *cp - 'A' + 10;
146       else if (*cp >= '0' && *cp <= '9')
147         val = val * 16 + *cp - '0';
148       else
149         break;
150       cp++;
151     }
152   return val;
153 }
154 #endif /* MOUNTED_GETMNTENT1.  */
155
156 #if MOUNTED_GETMNTINFO
157
158 # if ! HAVE_F_FSTYPENAME_IN_STATFS
159 static char *
160 fstype_to_string (short t)
161 {
162   switch (t)
163     {
164 #  ifdef MOUNT_PC
165     case MOUNT_PC:
166       return "pc";
167 #  endif
168 #  ifdef MOUNT_MFS
169     case MOUNT_MFS:
170       return "mfs";
171 #  endif
172 #  ifdef MOUNT_LO
173     case MOUNT_LO:
174       return "lo";
175 #  endif
176 #  ifdef MOUNT_TFS
177     case MOUNT_TFS:
178       return "tfs";
179 #  endif
180 #  ifdef MOUNT_TMP
181     case MOUNT_TMP:
182       return "tmp";
183 #  endif
184 #  ifdef MOUNT_UFS
185    case MOUNT_UFS:
186      return "ufs" ;
187 #  endif
188 #  ifdef MOUNT_NFS
189    case MOUNT_NFS:
190      return "nfs" ;
191 #  endif
192 #  ifdef MOUNT_MSDOS
193    case MOUNT_MSDOS:
194      return "msdos" ;
195 #  endif
196 #  ifdef MOUNT_LFS
197    case MOUNT_LFS:
198      return "lfs" ;
199 #  endif
200 #  ifdef MOUNT_LOFS
201    case MOUNT_LOFS:
202      return "lofs" ;
203 #  endif
204 #  ifdef MOUNT_FDESC
205    case MOUNT_FDESC:
206      return "fdesc" ;
207 #  endif
208 #  ifdef MOUNT_PORTAL
209    case MOUNT_PORTAL:
210      return "portal" ;
211 #  endif
212 #  ifdef MOUNT_NULL
213    case MOUNT_NULL:
214      return "null" ;
215 #  endif
216 #  ifdef MOUNT_UMAP
217    case MOUNT_UMAP:
218      return "umap" ;
219 #  endif
220 #  ifdef MOUNT_KERNFS
221    case MOUNT_KERNFS:
222      return "kernfs" ;
223 #  endif
224 #  ifdef MOUNT_PROCFS
225    case MOUNT_PROCFS:
226      return "procfs" ;
227 #  endif
228 #  ifdef MOUNT_AFS
229    case MOUNT_AFS:
230      return "afs" ;
231 #  endif
232 #  ifdef MOUNT_CD9660
233    case MOUNT_CD9660:
234      return "cd9660" ;
235 #  endif
236 #  ifdef MOUNT_UNION
237    case MOUNT_UNION:
238      return "union" ;
239 #  endif
240 #  ifdef MOUNT_DEVFS
241    case MOUNT_DEVFS:
242      return "devfs" ;
243 #  endif
244 #  ifdef MOUNT_EXT2FS
245    case MOUNT_EXT2FS:
246      return "ext2fs" ;
247 #  endif
248     default:
249       return "?";
250     }
251 }
252 # endif /* ! HAVE_F_FSTYPENAME_IN_STATFS */
253
254 /* __NetBSD__ || BSD_NET2 || __OpenBSD__ */
255 static char *
256 fsp_to_string (const struct statfs *fsp)
257 {
258 # if defined HAVE_F_FSTYPENAME_IN_STATFS
259   return fsp->f_fstypename;
260 # else
261   return fstype_to_string (fsp->f_type);
262 # endif
263 }
264
265 #endif /* MOUNTED_GETMNTINFO */
266
267 #ifdef MOUNTED_VMOUNT           /* AIX.  */
268 static char *
269 fstype_to_string (int t)
270 {
271   struct vfs_ent *e;
272
273   e = getvfsbytype (t);
274   if (!e || !e->vfsent_name)
275     return "none";
276   else
277     return e->vfsent_name;
278 }
279 #endif /* MOUNTED_VMOUNT */
280
281 /* Return a list of the currently mounted filesystems, or NULL on error.
282    Add each entry to the tail of the list so that they stay in order.
283    If NEED_FS_TYPE is nonzero, ensure that the filesystem type fields in
284    the returned list are valid.  Otherwise, they might not be.  */
285
286 struct mount_entry *
287 read_filesystem_list (int need_fs_type)
288 {
289   struct mount_entry *mount_list;
290   struct mount_entry *me;
291   struct mount_entry **mtail = &mount_list;
292
293 #ifdef MOUNTED_LISTMNTENT
294   {
295     struct tabmntent *mntlist, *p;
296     struct mntent *mnt;
297     struct mount_entry *me;
298
299     /* the third and fourth arguments could be used to filter mounts,
300        but Crays doesn't seem to have any mounts that we want to
301        remove. Specifically, automount create normal NFS mounts.
302        */
303
304     if(listmntent(&mntlist, KMTAB, NULL, NULL) < 0)
305       return NULL;
306     for (p = mntlist; p; p = p->next) {
307       mnt = p->ment;
308       me = (struct mount_entry*) xmalloc(sizeof (struct mount_entry));
309       me->me_devname = xstrdup(mnt->mnt_fsname);
310       me->me_mountdir = xstrdup(mnt->mnt_dir);
311       me->me_type = xstrdup(mnt->mnt_type);
312       me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
313       me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
314       me->me_dev = -1;
315       *mtail = me;
316       mtail = &me->me_next;
317     }
318     freemntlist(mntlist);
319   }
320 #endif
321
322 #ifdef MOUNTED_GETMNTENT1       /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
323   {
324     struct mntent *mnt;
325     char *table = MOUNTED;
326     FILE *fp;
327     char *devopt;
328
329     fp = setmntent (table, "r");
330     if (fp == NULL)
331       return NULL;
332
333     while ((mnt = getmntent (fp)))
334       {
335         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
336         me->me_devname = xstrdup (mnt->mnt_fsname);
337         me->me_mountdir = xstrdup (mnt->mnt_dir);
338         me->me_type = xstrdup (mnt->mnt_type);
339         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
340         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
341         devopt = strstr (mnt->mnt_opts, "dev=");
342         if (devopt)
343           {
344             if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
345               me->me_dev = xatoi (devopt + 6);
346             else
347               me->me_dev = xatoi (devopt + 4);
348           }
349         else
350           me->me_dev = (dev_t) -1;      /* Magic; means not known yet. */
351
352         /* Add to the linked list. */
353         *mtail = me;
354         mtail = &me->me_next;
355       }
356
357     if (endmntent (fp) == 0)
358       goto free_then_fail;
359   }
360 #endif /* MOUNTED_GETMNTENT1. */
361
362 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
363   {
364     struct statfs *fsp;
365     int entries;
366
367     entries = getmntinfo (&fsp, MNT_NOWAIT);
368     if (entries < 0)
369       return NULL;
370     for (; entries-- > 0; fsp++)
371       {
372         char *fs_type = fsp_to_string (fsp);
373
374         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
375         me->me_devname = xstrdup (fsp->f_mntfromname);
376         me->me_mountdir = xstrdup (fsp->f_mntonname);
377         me->me_type = fs_type;
378         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
379         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
380         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
381
382         /* Add to the linked list. */
383         *mtail = me;
384         mtail = &me->me_next;
385       }
386   }
387 #endif /* MOUNTED_GETMNTINFO */
388
389 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
390   {
391     int offset = 0;
392     int val;
393     struct fs_data fsd;
394
395     while (errno = 0,
396            0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
397                               (char *) 0)))
398       {
399         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
400         me->me_devname = xstrdup (fsd.fd_req.devname);
401         me->me_mountdir = xstrdup (fsd.fd_req.path);
402         me->me_type = gt_names[fsd.fd_req.fstype];
403         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
404         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
405         me->me_dev = fsd.fd_req.dev;
406
407         /* Add to the linked list. */
408         *mtail = me;
409         mtail = &me->me_next;
410       }
411     if (val < 0)
412       goto free_then_fail;
413   }
414 #endif /* MOUNTED_GETMNT. */
415
416 #if defined (MOUNTED_GETFSSTAT) /* __alpha running OSF_1 */
417   {
418     int numsys, counter, bufsize;
419     struct statfs *stats;
420
421     numsys = getfsstat ((struct statfs *)0, 0L, MNT_WAIT);
422     if (numsys < 0)
423       return (NULL);
424
425     bufsize = (1 + numsys) * sizeof (struct statfs);
426     stats = (struct statfs *)xmalloc (bufsize);
427     numsys = getfsstat (stats, bufsize, MNT_WAIT);
428
429     if (numsys < 0)
430       {
431         free (stats);
432         return (NULL);
433       }
434
435     for (counter = 0; counter < numsys; counter++)
436       {
437         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
438         me->me_devname = xstrdup (stats[counter].f_mntfromname);
439         me->me_mountdir = xstrdup (stats[counter].f_mntonname);
440         me->me_type = mnt_names[stats[counter].f_type];
441         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
442         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
443         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
444
445         /* Add to the linked list. */
446         *mtail = me;
447         mtail = &me->me_next;
448       }
449
450     free (stats);
451   }
452 #endif /* MOUNTED_GETFSSTAT */
453
454 #if defined (MOUNTED_FREAD) || defined (MOUNTED_FREAD_FSTYP) /* SVR[23].  */
455   {
456     struct mnttab mnt;
457     char *table = "/etc/mnttab";
458     FILE *fp;
459
460     fp = fopen (table, "r");
461     if (fp == NULL)
462       return NULL;
463
464     while (fread (&mnt, sizeof mnt, 1, fp) > 0)
465       {
466         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
467 # ifdef GETFSTYP                        /* SVR3.  */
468         me->me_devname = xstrdup (mnt.mt_dev);
469 # else
470         me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
471         strcpy (me->me_devname, "/dev/");
472         strcpy (me->me_devname + 5, mnt.mt_dev);
473 # endif
474         me->me_mountdir = xstrdup (mnt.mt_filsys);
475         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
476         me->me_type = "";
477 # ifdef GETFSTYP                        /* SVR3.  */
478         if (need_fs_type)
479           {
480             struct statfs fsd;
481             char typebuf[FSTYPSZ];
482
483             if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
484                 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
485               me->me_type = xstrdup (typebuf);
486           }
487 # endif
488         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
489         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
490
491         /* Add to the linked list. */
492         *mtail = me;
493         mtail = &me->me_next;
494       }
495
496     if (ferror (fp))
497       {
498         int saved_errno = errno;
499         fclose (fp);
500         errno = saved_errno;
501         goto free_then_fail;
502       }
503
504     if (fclose (fp) == EOF)
505       goto free_then_fail;
506   }
507 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */
508
509 #ifdef MOUNTED_GETMNTTBL        /* DolphinOS goes it's own way */
510   {
511     struct mntent **mnttbl=getmnttbl(),**ent;
512     for (ent=mnttbl;*ent;ent++)
513       {
514         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
515         me->me_devname = xstrdup ( (*ent)->mt_resource);
516         me->me_mountdir = xstrdup( (*ent)->mt_directory);
517         me->me_type =  xstrdup ((*ent)->mt_fstype);
518         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
519         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
520         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
521
522         /* Add to the linked list. */
523         *mtail = me;
524         mtail = &me->me_next;
525       }
526     endmnttbl();
527   }
528 #endif
529
530 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
531   {
532     struct mnttab mnt;
533     char *table = MNTTAB;
534     FILE *fp;
535     int ret;
536     int lockfd = -1;
537
538 # if defined F_RDLCK && defined F_SETLKW
539     /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
540        e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
541        for this file name, we should use their macro name instead.
542        (Why not just lock MNTTAB directly?  We don't know.)  */
543 #  ifndef MNTTAB_LOCK
544 #   define MNTTAB_LOCK "/etc/.mnttab.lock"
545 #  endif
546     lockfd = open (MNTTAB_LOCK, O_RDONLY);
547     if (0 <= lockfd)
548       {
549         struct flock flock;
550         flock.l_type = F_RDLCK;
551         flock.l_whence = SEEK_SET;
552         flock.l_start = 0;
553         flock.l_len = 0;
554         while (fcntl (lockfd, F_SETLKW, &flock) == -1)
555           if (errno != EINTR)
556             {
557               int saved_errno = errno;
558               close (lockfd);
559               errno = saved_errno;
560               return NULL;
561             }
562       }
563     else if (errno != ENOENT)
564       return NULL;
565 # endif
566
567     errno = 0;
568     fp = fopen (table, "r");
569     if (fp == NULL)
570       ret = errno;
571     else
572       {
573         while ((ret = getmntent (fp, &mnt)) == 0)
574           {
575             me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
576             me->me_devname = xstrdup (mnt.mnt_special);
577             me->me_mountdir = xstrdup (mnt.mnt_mountp);
578             me->me_type = xstrdup (mnt.mnt_fstype);
579             me->me_dummy = MNT_IGNORE (&mnt) != 0;
580             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
581             me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */
582
583             /* Add to the linked list. */
584             *mtail = me;
585             mtail = &me->me_next;
586           }
587
588         ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
589       }
590
591     if (0 <= lockfd && close (lockfd) != 0)
592       ret = errno;
593
594     if (0 <= ret)
595       {
596         errno = ret;
597         goto free_then_fail;
598       }
599   }
600 #endif /* MOUNTED_GETMNTENT2.  */
601
602 #ifdef MOUNTED_VMOUNT           /* AIX.  */
603   {
604     int bufsize;
605     char *entries, *thisent;
606     struct vmount *vmp;
607
608     /* Ask how many bytes to allocate for the mounted filesystem info.  */
609     mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize);
610     entries = xmalloc (bufsize);
611
612     /* Get the list of mounted filesystems.  */
613     mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
614
615     for (thisent = entries; thisent < entries + bufsize;
616          thisent += vmp->vmt_length)
617       {
618         vmp = (struct vmount *) thisent;
619         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
620         if (vmp->vmt_flags & MNT_REMOTE)
621           {
622             char *host, *path;
623
624             me->me_remote = 1;
625             /* Prepend the remote pathname.  */
626             host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
627             path = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
628             me->me_devname = xmalloc (strlen (host) + strlen (path) + 2);
629             strcpy (me->me_devname, host);
630             strcat (me->me_devname, ":");
631             strcat (me->me_devname, path);
632           }
633         else
634           {
635             me->me_remote = 0;
636             me->me_devname = xstrdup (thisent +
637                                       vmp->vmt_data[VMT_OBJECT].vmt_off);
638           }
639         me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
640         me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
641         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
642         me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */
643
644         /* Add to the linked list. */
645         *mtail = me;
646         mtail = &me->me_next;
647       }
648     free (entries);
649   }
650 #endif /* MOUNTED_VMOUNT. */
651
652   *mtail = NULL;
653   return mount_list;
654
655
656  free_then_fail:
657   {
658     int saved_errno = errno;
659     *mtail = NULL;
660
661     while (mount_list)
662       {
663         me = mount_list->me_next;
664         free (mount_list->me_devname);
665         free (mount_list->me_mountdir);
666         /* FIXME: me_type is not always malloced.  */
667         free (mount_list);
668         mount_list = me;
669       }
670
671     errno = saved_errno;
672     return NULL;
673   }
674 }