(read_filesystem_list): If all_fs is negative, omit non-local filesytems.
[gnulib.git] / lib / mountlist.c
1 /* mountlist.c -- return a list of mounted filesystems
2    Copyright (C) 1991, 1992, 1997, 1998 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
133 static int
134 xatoi (cp)
135      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 xstrdup (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 (t)
270      int t;
271 {
272   struct vfs_ent *e;
273
274   e = getvfsbytype (t);
275   if (!e || !e->vfsent_name)
276     return "none";
277   else
278     return e->vfsent_name;
279 }
280 #endif /* MOUNTED_VMOUNT */
281
282 /* Return a list of the currently mounted filesystems, or NULL on error.
283    Add each entry to the tail of the list so that they stay in order.
284    If NEED_FS_TYPE is nonzero, ensure that the filesystem type fields in
285    the returned list are valid.  Otherwise, they might not be.
286    If ALL_FS is positive, return all entries; if zero, omit entries
287    for filesystems that are automounter (dummy) entries; if negative,
288    also omit non-local filesystems.  */
289
290 struct mount_entry *
291 read_filesystem_list (int need_fs_type, int all_fs)
292 {
293   struct mount_entry *mount_list;
294   struct mount_entry *me;
295   struct mount_entry **mtail = &mount_list;
296
297 #ifdef MOUNTED_LISTMNTENT
298   {
299     struct tabmntent *mntlist, *p;
300     struct mntent *mnt;
301     struct mount_entry *me;
302
303     /* the third and fourth arguments could be used to filter mounts,
304        but Crays doesn't seem to have any mounts that we want to
305        remove. Specifically, automount create normal NFS mounts.
306        */
307
308     if(listmntent(&mntlist, KMTAB, NULL, NULL) < 0)
309       return NULL;
310     for (p = mntlist; p; p = p->next) {
311       mnt = p->ment;
312       if (all_fs < 0 && REMOTE_FS_TYPE (mnt->mnt_type))
313         continue;
314       me = (struct mount_entry*) xmalloc(sizeof (struct mount_entry));
315       me->me_devname = xstrdup(mnt->mnt_fsname);
316       me->me_mountdir = xstrdup(mnt->mnt_dir);
317       me->me_type = xstrdup(mnt->mnt_type);
318       me->me_dev = -1;
319       *mtail = me;
320       mtail = &me->me_next;
321     }
322     freemntlist(mntlist);
323   }
324 #endif
325
326 #ifdef MOUNTED_GETMNTENT1       /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
327   {
328     struct mntent *mnt;
329     char *table = MOUNTED;
330     FILE *fp;
331     char *devopt;
332
333     fp = setmntent (table, "r");
334     if (fp == NULL)
335       return NULL;
336
337     while ((mnt = getmntent (fp)))
338       {
339         if (all_fs <= 0 && (!strcmp (mnt->mnt_type, "ignore")
340                             || !strcmp (mnt->mnt_type, "auto")))
341           continue;
342         if (all_fs < 0 && REMOTE_FS_TYPE (mnt->mnt_type))
343           continue;
344
345         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
346         me->me_devname = xstrdup (mnt->mnt_fsname);
347         me->me_mountdir = xstrdup (mnt->mnt_dir);
348         me->me_type = xstrdup (mnt->mnt_type);
349         devopt = strstr (mnt->mnt_opts, "dev=");
350         if (devopt)
351           {
352             if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
353               me->me_dev = xatoi (devopt + 6);
354             else
355               me->me_dev = xatoi (devopt + 4);
356           }
357         else
358           me->me_dev = (dev_t) -1;      /* Magic; means not known yet. */
359
360         /* Add to the linked list. */
361         *mtail = me;
362         mtail = &me->me_next;
363       }
364
365     if (endmntent (fp) == 0)
366       goto free_then_fail;
367   }
368 #endif /* MOUNTED_GETMNTENT1. */
369
370 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
371   {
372     struct statfs *fsp;
373     int entries;
374
375     entries = getmntinfo (&fsp, MNT_NOWAIT);
376     if (entries < 0)
377       return NULL;
378     for (; entries-- > 0; fsp++)
379       {
380         if (all_fs < 0)
381           {
382 # ifdef HAVE_F_FSTYPENAME_IN_STATFS
383             if (REMOTE_FS_TYPE (fsp->f_fstypename))
384               continue;
385 # else
386 #  ifdef MOUNT_NFS
387             if (REMOTE_FS_TYPE (fstype_to_string (fsp->f_type)))
388               continue;
389 #  endif
390 # endif
391           }
392
393         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
394         me->me_devname = xstrdup (fsp->f_mntfromname);
395         me->me_mountdir = xstrdup (fsp->f_mntonname);
396         me->me_type = fsp_to_string (fsp);
397         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
398
399         /* Add to the linked list. */
400         *mtail = me;
401         mtail = &me->me_next;
402       }
403   }
404 #endif /* MOUNTED_GETMNTINFO */
405
406 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
407   {
408     int offset = 0;
409     int val;
410     struct fs_data fsd;
411
412     while (errno = 0,
413            0 <= (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
414                                (char *) 0)))
415       {
416         if (all_fs < 0 && REMOTE_FS_TYPE (gt_names[fsd.fd_req.fstype]))
417           continue;
418
419         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
420         me->me_devname = xstrdup (fsd.fd_req.devname);
421         me->me_mountdir = xstrdup (fsd.fd_req.path);
422         me->me_type = gt_names[fsd.fd_req.fstype];
423         me->me_dev = fsd.fd_req.dev;
424
425         /* Add to the linked list. */
426         *mtail = me;
427         mtail = &me->me_next;
428       }
429     if (val < 0)
430       goto free_then_fail;
431   }
432 #endif /* MOUNTED_GETMNT. */
433
434 #if defined (MOUNTED_GETFSSTAT) /* __alpha running OSF_1 */
435   {
436     int numsys, counter, bufsize;
437     struct statfs *stats;
438
439     numsys = getfsstat ((struct statfs *)0, 0L, MNT_WAIT);
440     if (numsys < 0)
441       return (NULL);
442
443     bufsize = (1 + numsys) * sizeof (struct statfs);
444     stats = (struct statfs *)xmalloc (bufsize);
445     numsys = getfsstat (stats, bufsize, MNT_WAIT);
446
447     if (numsys < 0)
448       {
449         free (stats);
450         return (NULL);
451       }
452
453     for (counter = 0; counter < numsys; counter++)
454       {
455         if (all_fs < 0 && REMOTE_FS_TYPE (mnt_names[stats[counter].f_type]))
456           continue;
457
458         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
459         me->me_devname = xstrdup (stats[counter].f_mntfromname);
460         me->me_mountdir = xstrdup (stats[counter].f_mntonname);
461         me->me_type = mnt_names[stats[counter].f_type];
462         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
463
464         /* Add to the linked list. */
465         *mtail = me;
466         mtail = &me->me_next;
467       }
468
469     free (stats);
470   }
471 #endif /* MOUNTED_GETFSSTAT */
472
473 #if defined (MOUNTED_FREAD) || defined (MOUNTED_FREAD_FSTYP) /* SVR[23].  */
474   {
475     struct mnttab mnt;
476     char *table = "/etc/mnttab";
477     FILE *fp;
478
479     fp = fopen (table, "r");
480     if (fp == NULL)
481       return NULL;
482
483     while (fread (&mnt, sizeof mnt, 1, fp) > 0)
484       {
485         char *fs_type = "";
486
487 # ifdef GETFSTYP                        /* SVR3.  */
488         if (need_fs_type || all_fs < 0)
489           {
490             struct statfs fsd;
491             char typebuf[FSTYPSZ];
492
493             if (statfs (mnt.mt_filsys, &fsd, sizeof fsd, 0) != -1
494                 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
495               {
496                 if (all_fs < 0 && REMOTE_FS_TYPE (typebuf))
497                   continue;
498                 fs_type = xstrdup (typebuf);
499               }
500           }
501 # endif
502
503         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
504 # ifdef GETFSTYP                        /* SVR3.  */
505         me->me_devname = xstrdup (mnt.mt_dev);
506 # else
507         me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
508         strcpy (me->me_devname, "/dev/");
509         strcpy (me->me_devname + 5, mnt.mt_dev);
510 # endif
511         me->me_mountdir = xstrdup (mnt.mt_filsys);
512         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
513         me->me_type = fs_type;
514
515         /* Add to the linked list. */
516         *mtail = me;
517         mtail = &me->me_next;
518       }
519
520     if (ferror (fp))
521       {
522         int saved_errno = errno;
523         fclose (fp);
524         errno = saved_errno;
525         goto free_then_fail;
526       }
527
528     if (fclose (fp) == EOF)
529       goto free_then_fail;
530   }
531 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */
532
533 #ifdef MOUNTED_GETMNTTBL        /* DolphinOS goes it's own way */
534   {
535     struct mntent **mnttbl=getmnttbl(),**ent;
536     for (ent=mnttbl;*ent;ent++)
537       {
538         if (all_fs < 0 && REMOTE_FS_TYPE ((*ent)->mt_fstype))
539           continue;
540
541         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
542         me->me_devname = xstrdup ( (*ent)->mt_resource);
543         me->me_mountdir = xstrdup( (*ent)->mt_directory);
544         me->me_type =  xstrdup ((*ent)->mt_fstype);
545         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
546
547         /* Add to the linked list. */
548         *mtail = me;
549         mtail = &me->me_next;
550       }
551     endmnttbl();
552   }
553 #endif
554
555 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
556   {
557     struct mnttab mnt;
558     char *table = MNTTAB;
559     FILE *fp;
560     int ret;
561     int lockfd = -1;
562
563 # if defined F_RDLCK && defined F_SETLKW
564     /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
565        e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
566        for this file name, we should use their macro name instead.
567        (Why not just lock MNTTAB directly?  We don't know.)  */
568 #  ifndef MNTTAB_LOCK
569 #   define MNTTAB_LOCK "/etc/.mnttab.lock"
570 #  endif
571     lockfd = open (MNTTAB_LOCK, O_RDONLY);
572     if (0 <= lockfd)
573       {
574         struct flock flock;
575         flock.l_type = F_RDLCK;
576         flock.l_whence = SEEK_SET;
577         flock.l_start = 0;
578         flock.l_len = 0;
579         while (fcntl (lockfd, F_SETLKW, &flock) == -1)
580           if (errno != EINTR)
581             {
582               int saved_errno = errno;
583               close (lockfd);
584               errno = saved_errno;
585               return NULL;
586             }
587       }
588     else if (errno != ENOENT)
589       return NULL;
590 # endif
591
592     errno = 0;
593     fp = fopen (table, "r");
594     if (fp == NULL)
595       ret = errno;
596     else
597       {
598         while ((ret = getmntent (fp, &mnt)) == 0)
599           {
600             /* Don't show automounted filesystems twice on e.g., Solaris.  */
601             if (all_fs <= 0 && MNT_IGNORE (&mnt))
602               continue;
603
604             if (all_fs < 0 && REMOTE_FS_TYPE (mnt.mnt_fstype))
605               continue;
606
607             me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
608             me->me_devname = xstrdup (mnt.mnt_special);
609             me->me_mountdir = xstrdup (mnt.mnt_mountp);
610             me->me_type = xstrdup (mnt.mnt_fstype);
611             me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */
612
613             /* Add to the linked list. */
614             *mtail = me;
615             mtail = &me->me_next;
616           }
617
618         ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
619       }
620
621     if (0 <= lockfd && close (lockfd) != 0)
622       ret = errno;
623
624     if (0 <= ret)
625       {
626         errno = ret;
627         goto free_then_fail;
628       }
629   }
630 #endif /* MOUNTED_GETMNTENT2.  */
631
632 #ifdef MOUNTED_VMOUNT           /* AIX.  */
633   {
634     int bufsize;
635     char *entries, *thisent;
636     struct vmount *vmp;
637
638     /* Ask how many bytes to allocate for the mounted filesystem info.  */
639     mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize);
640     entries = xmalloc (bufsize);
641
642     /* Get the list of mounted filesystems.  */
643     mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
644
645     for (thisent = entries; thisent < entries + bufsize;
646          thisent += vmp->vmt_length)
647       {
648         vmp = (struct vmount *) thisent;
649         if (all_fs < 0 && vmp->vmt_flags & MNT_REMOTE)
650           continue;
651         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
652         if (vmp->vmt_flags & MNT_REMOTE)
653           {
654             char *host, *path;
655
656             /* Prepend the remote pathname.  */
657             host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
658             path = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
659             me->me_devname = xmalloc (strlen (host) + strlen (path) + 2);
660             strcpy (me->me_devname, host);
661             strcat (me->me_devname, ":");
662             strcat (me->me_devname, path);
663           }
664         else
665           {
666             me->me_devname = xstrdup (thisent +
667                                       vmp->vmt_data[VMT_OBJECT].vmt_off);
668           }
669         me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
670         me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
671         me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */
672
673         /* Add to the linked list. */
674         *mtail = me;
675         mtail = &me->me_next;
676       }
677     free (entries);
678   }
679 #endif /* MOUNTED_VMOUNT. */
680
681   *mtail = NULL;
682   return mount_list;
683
684
685  free_then_fail:
686   {
687     int saved_errno = errno;
688     *mtail = NULL;
689
690     while (mount_list)
691       {
692         me = mount_list->me_next;
693         free (mount_list->me_devname);
694         free (mount_list->me_mountdir);
695         /* FIXME: me_type is not always malloced.  */
696         free (mount_list);
697         mount_list = me;
698       }
699
700     errno = saved_errno;
701     return NULL;
702   }
703 }