(read_filesystem_list): Fix more memory leaks on failure.
[pspp] / 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 zero, do not return entries for filesystems that
287    are automounter (dummy) entries.  */
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     p = mntlist;
310     while(p){
311       mnt = p->ment;
312       me = (struct mount_entry*) xmalloc(sizeof (struct mount_entry));
313       me->me_devname = xstrdup(mnt->mnt_fsname);
314       me->me_mountdir = xstrdup(mnt->mnt_dir);
315       me->me_type = xstrdup(mnt->mnt_type);
316       me->me_dev = -1;
317       *mtail = me;
318       mtail = &me->me_next;
319       p = p->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 && (!strcmp (mnt->mnt_type, "ignore")
339                         || !strcmp (mnt->mnt_type, "auto")))
340           continue;
341
342         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
343         me->me_devname = xstrdup (mnt->mnt_fsname);
344         me->me_mountdir = xstrdup (mnt->mnt_dir);
345         me->me_type = xstrdup (mnt->mnt_type);
346         devopt = strstr (mnt->mnt_opts, "dev=");
347         if (devopt)
348           {
349             if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
350               me->me_dev = xatoi (devopt + 6);
351             else
352               me->me_dev = xatoi (devopt + 4);
353           }
354         else
355           me->me_dev = (dev_t) -1;      /* Magic; means not known yet. */
356
357         /* Add to the linked list. */
358         *mtail = me;
359         mtail = &me->me_next;
360       }
361
362     if (endmntent (fp) == 0)
363       goto free_then_fail;
364   }
365 #endif /* MOUNTED_GETMNTENT1. */
366
367 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
368   {
369     struct statfs *fsp;
370     int entries;
371
372     entries = getmntinfo (&fsp, MNT_NOWAIT);
373     if (entries < 0)
374       return NULL;
375     while (entries-- > 0)
376       {
377         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
378         me->me_devname = xstrdup (fsp->f_mntfromname);
379         me->me_mountdir = xstrdup (fsp->f_mntonname);
380         me->me_type = fsp_to_string (fsp);
381         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
382
383         /* Add to the linked list. */
384         *mtail = me;
385         mtail = &me->me_next;
386         fsp++;
387       }
388   }
389 #endif /* MOUNTED_GETMNTINFO */
390
391 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
392   {
393     int offset = 0;
394     int val;
395     struct fs_data fsd;
396
397     while (errno = 0,
398            0 <= (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
399                                (char *) 0)))
400       {
401         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
402         me->me_devname = xstrdup (fsd.fd_req.devname);
403         me->me_mountdir = xstrdup (fsd.fd_req.path);
404         me->me_type = gt_names[fsd.fd_req.fstype];
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_dev = (dev_t) -1;        /* Magic; means not known yet. */
442
443         /* Add to the linked list. */
444         *mtail = me;
445         mtail = &me->me_next;
446       }
447
448     free (stats);
449   }
450 #endif /* MOUNTED_GETFSSTAT */
451
452 #if defined (MOUNTED_FREAD) || defined (MOUNTED_FREAD_FSTYP) /* SVR[23].  */
453   {
454     struct mnttab mnt;
455     char *table = "/etc/mnttab";
456     FILE *fp;
457
458     fp = fopen (table, "r");
459     if (fp == NULL)
460       return NULL;
461
462     while (fread (&mnt, sizeof mnt, 1, fp) > 0)
463       {
464         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
465 # ifdef GETFSTYP                        /* SVR3.  */
466         me->me_devname = xstrdup (mnt.mt_dev);
467 # else
468         me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
469         strcpy (me->me_devname, "/dev/");
470         strcpy (me->me_devname + 5, mnt.mt_dev);
471 # endif
472         me->me_mountdir = xstrdup (mnt.mt_filsys);
473         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
474         me->me_type = "";
475 # ifdef GETFSTYP                        /* SVR3.  */
476         if (need_fs_type)
477           {
478             struct statfs fsd;
479             char typebuf[FSTYPSZ];
480
481             if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
482                 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
483               me->me_type = xstrdup (typebuf);
484           }
485 # endif
486
487         /* Add to the linked list. */
488         *mtail = me;
489         mtail = &me->me_next;
490       }
491
492     if (ferror (fp))
493       {
494         int saved_errno = errno;
495         fclose (fp);
496         errno = saved_errno;
497         goto free_then_fail;
498       }
499
500     if (fclose (fp) == EOF)
501       goto free_then_fail;
502   }
503 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */
504
505 #ifdef MOUNTED_GETMNTTBL        /* DolphinOS goes it's own way */
506   {
507     struct mntent **mnttbl=getmnttbl(),**ent;
508     for (ent=mnttbl;*ent;ent++)
509       {
510         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
511         me->me_devname = xstrdup ( (*ent)->mt_resource);
512         me->me_mountdir = xstrdup( (*ent)->mt_directory);
513         me->me_type =  xstrdup ((*ent)->mt_fstype);
514         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
515
516         /* Add to the linked list. */
517         *mtail = me;
518         mtail = &me->me_next;
519       }
520     endmnttbl();
521   }
522 #endif
523
524 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
525   {
526     struct mnttab mnt;
527     char *table = MNTTAB;
528     FILE *fp;
529     int ret;
530     int lockfd = -1;
531
532 # if defined F_RDLCK && defined F_SETLKW
533     /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
534        e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
535        for this file name, we should use their macro name instead.
536        (Why not just lock MNTTAB directly?  We don't know.)  */
537 #  ifndef MNTTAB_LOCK
538 #   define MNTTAB_LOCK "/etc/.mnttab.lock"
539 #  endif
540     lockfd = open (MNTTAB_LOCK, O_RDONLY);
541     if (0 <= lockfd)
542       {
543         struct flock flock;
544         flock.l_type = F_RDLCK;
545         flock.l_whence = SEEK_SET;
546         flock.l_start = 0;
547         flock.l_len = 0;
548         while (fcntl (lockfd, F_SETLKW, &flock) == -1)
549           if (errno != EINTR)
550             {
551               int saved_errno = errno;
552               close (lockfd);
553               errno = saved_errno;
554               return NULL;
555             }
556       }
557     else if (errno != ENOENT)
558       return NULL;
559 # endif
560
561     errno = 0;
562     fp = fopen (table, "r");
563     if (fp == NULL)
564       ret = errno;
565     else
566       {
567         while ((ret = getmntent (fp, &mnt)) == 0)
568           {
569             /* Don't show automounted filesystems twice on e.g., Solaris.  */
570             if (!all_fs && MNT_IGNORE (&mnt))
571               continue;
572
573             me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
574             me->me_devname = xstrdup (mnt.mnt_special);
575             me->me_mountdir = xstrdup (mnt.mnt_mountp);
576             me->me_type = xstrdup (mnt.mnt_fstype);
577             me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */
578
579             /* Add to the linked list. */
580             *mtail = me;
581             mtail = &me->me_next;
582           }
583
584         ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
585       }
586
587     if (0 <= lockfd && close (lockfd) != 0)
588       ret = errno;
589
590     if (0 <= ret)
591       {
592         errno = ret;
593         goto free_then_fail;
594       }
595   }
596 #endif /* MOUNTED_GETMNTENT2.  */
597
598 #ifdef MOUNTED_VMOUNT           /* AIX.  */
599   {
600     int bufsize;
601     char *entries, *thisent;
602     struct vmount *vmp;
603
604     /* Ask how many bytes to allocate for the mounted filesystem info.  */
605     mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize);
606     entries = xmalloc (bufsize);
607
608     /* Get the list of mounted filesystems.  */
609     mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
610
611     for (thisent = entries; thisent < entries + bufsize;
612          thisent += vmp->vmt_length)
613       {
614         vmp = (struct vmount *) thisent;
615         me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
616         if (vmp->vmt_flags & MNT_REMOTE)
617           {
618             char *host, *path;
619
620             /* Prepend the remote pathname.  */
621             host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
622             path = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
623             me->me_devname = xmalloc (strlen (host) + strlen (path) + 2);
624             strcpy (me->me_devname, host);
625             strcat (me->me_devname, ":");
626             strcat (me->me_devname, path);
627           }
628         else
629           {
630             me->me_devname = xstrdup (thisent +
631                                       vmp->vmt_data[VMT_OBJECT].vmt_off);
632           }
633         me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
634         me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
635         me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */
636
637         /* Add to the linked list. */
638         *mtail = me;
639         mtail = &me->me_next;
640       }
641     free (entries);
642   }
643 #endif /* MOUNTED_VMOUNT. */
644
645   *mtail = NULL;
646   return mount_list;
647
648
649  free_then_fail:
650   {
651     int saved_errno = errno;
652     *mtail = NULL;
653
654     while (mount_list)
655       {
656         me = mount_list->me_next;
657         free (mount_list->me_devname);
658         free (mount_list->me_mountdir);
659         /* FIXME: me_type is not always malloced.  */
660         free (mount_list);
661         mount_list = me;
662       }
663
664     errno = saved_errno;
665     return NULL;
666   }
667 }