mountlist: fix resource leak with MOUNTED_INTERIX_STATVFS
[gnulib.git] / lib / mountlist.c
index ccb08dd..b839cd1 100644 (file)
@@ -1,6 +1,6 @@
 /* mountlist.c -- return a list of mounted file systems
 
-   Copyright (C) 1991-1992, 1997-2010 Free Software Foundation, Inc.
+   Copyright (C) 1991-1992, 1997-2013 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 # include <sys/vfs.h>
 #endif
 
+#ifdef MOUNTED_INTERIX_STATVFS  /* Interix. */
+# include <sys/statvfs.h>
+# include <dirent.h>
+#endif
+
 #ifdef DOLPHIN
 /* So special that it's not worth putting this in autoconf.  */
 # undef MOUNTED_FREAD_FSTYP
 # include <sys/mntent.h>
 #endif
 
+#ifndef HAVE_HASMNTOPT
+# define hasmntopt(mnt, opt) ((char *) 0)
+#endif
+
 #undef MNT_IGNORE
-#if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
-# define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
+#ifdef MNTOPT_IGNORE
+# define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
 #else
 # define MNT_IGNORE(M) 0
 #endif
 #undef opendir
 #undef closedir
 
-#ifndef ME_DUMMY
-# define ME_DUMMY(Fs_name, Fs_type)             \
-    (strcmp (Fs_type, "autofs") == 0            \
-     || strcmp (Fs_type, "none") == 0           \
-     || strcmp (Fs_type, "proc") == 0           \
-     || strcmp (Fs_type, "subfs") == 0          \
-     /* for NetBSD 3.0 */                       \
-     || strcmp (Fs_type, "kernfs") == 0         \
-     /* for Irix 6.5 */                         \
-     || strcmp (Fs_type, "ignore") == 0)
+#define ME_DUMMY_0(Fs_name, Fs_type)            \
+  (strcmp (Fs_type, "autofs") == 0              \
+   || strcmp (Fs_type, "proc") == 0             \
+   || strcmp (Fs_type, "subfs") == 0            \
+   /* for Linux 2.6/3.x */                      \
+   || strcmp (Fs_type, "debugfs") == 0          \
+   || strcmp (Fs_type, "devpts") == 0           \
+   || strcmp (Fs_type, "fusectl") == 0          \
+   || strcmp (Fs_type, "mqueue") == 0           \
+   || strcmp (Fs_type, "rpc_pipefs") == 0       \
+   || strcmp (Fs_type, "sysfs") == 0            \
+   /* FreeBSD, Linux 2.4 */                     \
+   || strcmp (Fs_type, "devfs") == 0            \
+   /* for NetBSD 3.0 */                         \
+   || strcmp (Fs_type, "kernfs") == 0           \
+   /* for Irix 6.5 */                           \
+   || strcmp (Fs_type, "ignore") == 0)
+
+/* Historically, we have marked as "dummy" any file system of type "none",
+   but now that programs like du need to know about bind-mounted directories,
+   we grant an exception to any with "bind" in its list of mount options.
+   I.e., those are *not* dummy entries.  */
+#ifdef MOUNTED_GETMNTENT1
+# define ME_DUMMY(Fs_name, Fs_type, Fs_ent)    \
+  (ME_DUMMY_0 (Fs_name, Fs_type)               \
+   || (strcmp (Fs_type, "none") == 0           \
+       && !hasmntopt (Fs_ent, "bind")))
+#else
+# define ME_DUMMY(Fs_name, Fs_type)            \
+  (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
+#endif
+
+#ifdef __CYGWIN__
+# include <windows.h>
+# define ME_REMOTE me_remote
+/* All cygwin mount points include ':' or start with '//'; so it
+   requires a native Windows call to determine remote disks.  */
+static bool
+me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
+{
+  if (fs_name[0] && fs_name[1] == ':')
+    {
+      char drive[4];
+      sprintf (drive, "%c:\\", fs_name[0]);
+      switch (GetDriveType (drive))
+        {
+        case DRIVE_REMOVABLE:
+        case DRIVE_FIXED:
+        case DRIVE_CDROM:
+        case DRIVE_RAMDISK:
+          return false;
+        }
+    }
+  return true;
+}
 #endif
 
 #ifndef ME_REMOTE
-/* A file system is `remote' if its Fs_name contains a `:'
-   or if (it is of type (smbfs or cifs) and its Fs_name starts with `//').  */
+/* A file system is "remote" if its Fs_name contains a ':'
+   or if (it is of type (smbfs or cifs) and its Fs_name starts with '//').  */
 # define ME_REMOTE(Fs_name, Fs_type)            \
     (strchr (Fs_name, ':') != NULL              \
      || ((Fs_name)[0] == '/'                    \
@@ -354,19 +408,20 @@ read_file_system_list (bool need_fs_type)
 
     if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
       return NULL;
-    for (p = mntlist; p; p = p->next) {
-      mnt = p->ment;
-      me = xmalloc (sizeof *me);
-      me->me_devname = xstrdup (mnt->mnt_fsname);
-      me->me_mountdir = xstrdup (mnt->mnt_dir);
-      me->me_type = xstrdup (mnt->mnt_type);
-      me->me_type_malloced = 1;
-      me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
-      me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
-      me->me_dev = -1;
-      *mtail = me;
-      mtail = &me->me_next;
-    }
+    for (p = mntlist; p; p = p->next)
+      {
+        mnt = p->ment;
+        me = xmalloc (sizeof *me);
+        me->me_devname = xstrdup (mnt->mnt_fsname);
+        me->me_mountdir = xstrdup (mnt->mnt_dir);
+        me->me_type = xstrdup (mnt->mnt_type);
+        me->me_type_malloced = 1;
+        me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
+        me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
+        me->me_dev = -1;
+        *mtail = me;
+        mtail = &me->me_next;
+      }
     freemntlist (mntlist);
   }
 #endif
@@ -388,7 +443,7 @@ read_file_system_list (bool need_fs_type)
         me->me_mountdir = xstrdup (mnt->mnt_dir);
         me->me_type = xstrdup (mnt->mnt_type);
         me->me_type_malloced = 1;
-        me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
+        me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, mnt);
         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
         me->me_dev = dev_from_mount_options (mnt->mnt_opts);
 
@@ -564,7 +619,8 @@ read_file_system_list (bool need_fs_type)
               break;
 
           me = xmalloc (sizeof *me);
-          me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
+          me->me_devname = xstrdup (fi.device_name[0] != '\0'
+                                    ? fi.device_name : fi.fsh_name);
           me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
           me->me_type = xstrdup (fi.fsh_name);
           me->me_type_malloced = 1;
@@ -594,9 +650,9 @@ read_file_system_list (bool need_fs_type)
     size_t bufsize;
     struct statfs *stats;
 
-    numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
+    numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
     if (numsys < 0)
-      return (NULL);
+      return NULL;
     if (SIZE_MAX / sizeof *stats <= numsys)
       xalloc_die ();
 
@@ -607,7 +663,7 @@ read_file_system_list (bool need_fs_type)
     if (numsys < 0)
       {
         free (stats);
-        return (NULL);
+        return NULL;
       }
 
     for (counter = 0; counter < numsys; counter++)
@@ -693,11 +749,11 @@ read_file_system_list (bool need_fs_type)
 #ifdef MOUNTED_GETMNTTBL        /* DolphinOS goes its own way.  */
   {
     struct mntent **mnttbl = getmnttbl (), **ent;
-    for (ent=mnttbl;*ent;ent++)
+    for (ent = mnttbl; *ent; ent++)
       {
         me = xmalloc (sizeof *me);
-        me->me_devname = xstrdup ( (*ent)->mt_resource);
-        me->me_mountdir = xstrdup ( (*ent)->mt_directory);
+        me->me_devname = xstrdup ((*ent)->mt_resource);
+        me->me_mountdir = xstrdup ((*ent)->mt_directory);
         me->me_type = xstrdup ((*ent)->mt_fstype);
         me->me_type_malloced = 1;
         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
@@ -854,6 +910,46 @@ read_file_system_list (bool need_fs_type)
   }
 #endif /* MOUNTED_VMOUNT. */
 
+#ifdef MOUNTED_INTERIX_STATVFS
+  {
+    DIR *dirp = opendir ("/dev/fs");
+    char node[9 + NAME_MAX];
+
+    if (!dirp)
+      goto free_then_fail;
+
+    while (1)
+      {
+        struct statvfs dev;
+        struct dirent entry;
+        struct dirent *result;
+
+        if (readdir_r (dirp, &entry, &result) || result == NULL)
+          break;
+
+        strcpy (node, "/dev/fs/");
+        strcat (node, entry.d_name);
+
+        if (statvfs (node, &dev) == 0)
+          {
+            me = xmalloc (sizeof *me);
+            me->me_devname = xstrdup (dev.f_mntfromname);
+            me->me_mountdir = xstrdup (dev.f_mntonname);
+            me->me_type = xstrdup (dev.f_fstypename);
+            me->me_type_malloced = 1;
+            me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
+            me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
+            me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
+
+            /* Add to the linked list. */
+            *mtail = me;
+            mtail = &me->me_next;
+          }
+      }
+    closedir (dirp);
+  }
+#endif /* MOUNTED_INTERIX_STATVFS */
+
   *mtail = NULL;
   return mount_list;
 
@@ -866,11 +962,7 @@ read_file_system_list (bool need_fs_type)
     while (mount_list)
       {
         me = mount_list->me_next;
-        free (mount_list->me_devname);
-        free (mount_list->me_mountdir);
-        if (mount_list->me_type_malloced)
-          free (mount_list->me_type);
-        free (mount_list);
+        free_mount_entry (mount_list);
         mount_list = me;
       }
 
@@ -878,3 +970,14 @@ read_file_system_list (bool need_fs_type)
     return NULL;
   }
 }
+
+/* Free a mount entry as returned from read_file_system_list ().  */
+
+void free_mount_entry (struct mount_entry *me)
+{
+  free (me->me_devname);
+  free (me->me_mountdir);
+  if (me->me_type_malloced)
+    free (me->me_type);
+  free (me);
+}