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