Blame SOURCES/coreutils-8.22-df-filtermountlistupdate.patch

522fea
diff -urNp coreutils-8.22-orig/src/df.c coreutils-8.22/src/df.c
522fea
--- coreutils-8.22-orig/src/df.c	2015-07-06 15:51:56.218101224 +0200
522fea
+++ coreutils-8.22/src/df.c	2015-07-06 16:07:14.429941697 +0200
522fea
@@ -47,12 +47,12 @@ struct devlist
522fea
 
522fea
 /* Filled with device numbers of examined file systems to avoid
522fea
    duplicities in output.  */
522fea
-struct devlist
522fea
+static struct devlist
522fea
 {
522fea
   dev_t dev_num;
522fea
   struct mount_entry *me;
522fea
   struct devlist *next;
522fea
-};
522fea
+} *device_list;
522fea
 
522fea
 /* If true, show even file systems with zero size or
522fea
    uninteresting types.  */
522fea
@@ -617,7 +617,7 @@ excluded_fstype (const char *fstype)
522fea
    me_mountdir wins.  */
522fea
 
522fea
 static void
522fea
-filter_mount_list (void)
522fea
+filter_mount_list (bool devices_only)
522fea
 {
522fea
   struct mount_entry *me;
522fea
 
522fea
@@ -621,9 +621,6 @@ filter_mount_list (void)
522fea
 {
522fea
   struct mount_entry *me;
522fea
 
522fea
-  /* Store of already-processed device numbers.  */
522fea
-  struct devlist *devlist_head = NULL;
522fea
-
522fea
   /* Sort all 'wanted' entries into the list devlist_head.  */
522fea
   for (me = mount_list; me;)
522fea
     {
522fea
@@ -631,41 +628,66 @@ filter_mount_list (void)
522fea
       struct devlist *devlist;
522fea
       struct mount_entry *discard_me = NULL;
522fea
 
522fea
-      if (-1 == stat (me->me_mountdir, &buf))
522fea
+      /* Avoid stating remote file systems as that may hang.
522fea
+         On Linux we probably have me_dev populated from /proc/self/mountinfo,
522fea
+         however we still stat() in case another device was mounted later.  */
522fea
+      if ((me->me_remote && show_local_fs)
522fea
+          || -1 == stat (me->me_mountdir, &buf))
522fea
         {
522fea
-          /* Stat failed - add ME to be able to complain about it later.  */
522fea
+          /* If remote, and showing just local, add ME for filtering later.
522fea
+             If stat failed; add ME to be able to complain about it later.  */
522fea
           buf.st_dev = me->me_dev;
522fea
         }
522fea
       else
522fea
         {
522fea
-          /* If the device name is a real path name ...  */
522fea
-          if (strchr (me->me_devname, '/'))
522fea
-            {
522fea
-              /* ... try to find its device number in the devlist.  */
522fea
-              for (devlist = devlist_head; devlist; devlist = devlist->next)
522fea
-                if (devlist->dev_num == buf.st_dev)
522fea
-                  break;
522fea
+          /* If we've already seen this device...  */
522fea
+          for (devlist = device_list; devlist; devlist = devlist->next)
522fea
+            if (devlist->dev_num == buf.st_dev)
522fea
+              break;
522fea
 
522fea
-              if (devlist)
522fea
+          if (devlist)
522fea
+            {
522fea
+              if (! print_grand_total && me->me_remote && devlist->me->me_remote
522fea
+                  && ! STREQ (devlist->me->me_devname, me->me_devname))
522fea
                 {
522fea
+                  /* Don't discard remote entries with different locations,
522fea
+                     as these are more likely to be explicitly mounted.
522fea
+                     However avoid this when producing a total to give
522fea
+                     a more accurate value in that case.  */
522fea
+                }
522fea
+              else if ((strchr (me->me_devname, '/')
522fea
+                       /* let "real" devices with '/' in the name win.  */
522fea
+                        && ! strchr (devlist->me->me_devname, '/'))
522fea
+                       /* let a shorter mountdir win.  */
522fea
+                       || (strlen (devlist->me->me_mountdir)
522fea
+                           > strlen (me->me_mountdir))
522fea
+                       /* let an entry overmounted on a new device win...  */
522fea
+                       || (! STREQ (devlist->me->me_devname, me->me_devname)
522fea
+                           /* ... but only when matching an existing mnt point,
522fea
+                              to avoid problematic replacement when given
522fea
+                              inaccurate mount lists, seen with some chroot
522fea
+                              environments for example.  */
522fea
+                           && STREQ (me->me_mountdir,
522fea
+                                     devlist->me->me_mountdir)))
522fea
+                {
522fea
+                  /* Discard mount entry for existing device.  */
522fea
+                  discard_me = devlist->me;
522fea
+                  devlist->me = me;
522fea
+                }
522fea
+              else
522fea
+                {
522fea
+                  /* Discard mount entry currently being processed.  */
522fea
                   discard_me = me;
522fea
-
522fea
-                  /* Let the shorter mountdir win.  */
522fea
-                  if (! strchr (devlist->me->me_devname, '/')
522fea
-                      || (strlen (devlist->me->me_mountdir)
522fea
-                         > strlen (me->me_mountdir)))
522fea
-                    {
522fea
-                      discard_me = devlist->me;
522fea
-                      devlist->me = me;
522fea
-                    }
522fea
                 }
522fea
+
522fea
             }
522fea
         }
522fea
 
522fea
       if (discard_me)
522fea
         {
522fea
            me = me->me_next;
522fea
-           free_mount_entry (discard_me);
522fea
+           if (! devices_only)
522fea
+             free_mount_entry (discard_me);
522fea
         }
522fea
       else
522fea
         {
522fea
@@ -673,28 +695,49 @@ filter_mount_list (void)
522fea
           devlist = xmalloc (sizeof *devlist);
522fea
           devlist->me = me;
522fea
           devlist->dev_num = buf.st_dev;
522fea
-          devlist->next = devlist_head;
522fea
-          devlist_head = devlist;
522fea
+          devlist->next = device_list;
522fea
+          device_list = devlist;
522fea
 
522fea
           me = me->me_next;
522fea
         }
522fea
     }
522fea
 
522fea
   /* Finally rebuild the mount_list from the devlist.  */
522fea
-  mount_list = NULL;
522fea
-  while (devlist_head)
522fea
-    {
522fea
-      /* Add the mount entry.  */
522fea
-      me = devlist_head->me;
522fea
-      me->me_next = mount_list;
522fea
-      mount_list = me;
522fea
-      /* Free devlist entry and advance.  */
522fea
-      struct devlist *devlist = devlist_head->next;
522fea
-      free (devlist_head);
522fea
-      devlist_head = devlist;
522fea
-    }
522fea
+  if (! devices_only) {
522fea
+    mount_list = NULL;
522fea
+    while (device_list)
522fea
+      {
522fea
+        /* Add the mount entry.  */
522fea
+        me = device_list->me;
522fea
+        me->me_next = mount_list;
522fea
+        mount_list = me;
522fea
+        /* Free devlist entry and advance.  */
522fea
+        struct devlist *devlist = device_list->next;
522fea
+        free (device_list);
522fea
+        device_list = devlist;
522fea
+      }
522fea
+  }
522fea
 }
522fea
 
522fea
+/* Search a mount entry list for device id DEV.
522fea
+   Return the corresponding mount entry if found or NULL if not.  */
522fea
+
522fea
+static struct mount_entry const * _GL_ATTRIBUTE_PURE
522fea
+me_for_dev (dev_t dev)
522fea
+{
522fea
+  struct devlist *dl = device_list;
522fea
+
522fea
+  while (dl)
522fea
+    {
522fea
+      if (dl->dev_num == dev)
522fea
+        return dl->me;
522fea
+      dl = dl->next;
522fea
+    }
522fea
+
522fea
+  return NULL;
522fea
+}
522fea
+
522fea
+
522fea
 /* Return true if N is a known integer value.  On many file systems,
522fea
    UINTMAX_MAX represents an unknown value; on AIX, UINTMAX_MAX - 1
522fea
    represents unknown.  Use a rule that works on AIX file systems, and
522fea
@@ -887,6 +887,11 @@ get_dev (char const *disk, char const *m
522fea
   if (!selected_fstype (fstype) || excluded_fstype (fstype))
522fea
     return;
522fea
 
522fea
+  /* Ignore relative MOUNT_POINTs, which are present for example
522fea
+     in /proc/mounts on Linux with network namespaces.  */
522fea
+  if (!force_fsu && mount_point && ! IS_ABSOLUTE_FILE_NAME (mount_point))
522fea
+    return;
522fea
+
522fea
   /* If MOUNT_POINT is NULL, then the file system is not mounted, and this
522fea
      program reports on the file system that the special file is on.
522fea
      It would be better to report on the unmounted file system,
522fea
@@ -900,9 +924,43 @@ get_dev (char const *disk, char const *m
522fea
     fsu = *force_fsu;
522fea
   else if (get_fs_usage (stat_file, disk, &fsu))
522fea
     {
522fea
-      error (0, errno, "%s", quote (stat_file));
522fea
-      exit_status = EXIT_FAILURE;
522fea
-      return;
522fea
+      /* If we can't access a system provided entry due
522fea
+         to it not being present (now), or due to permissions,
522fea
+         just output placeholder values rather than failing.  */
522fea
+      if (process_all && (errno == EACCES || errno == ENOENT))
522fea
+        {
522fea
+          if (! show_all_fs)
522fea
+            return;
522fea
+
522fea
+          fstype = "-";
522fea
+          fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree =
522fea
+          fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX;
522fea
+        }
522fea
+      else
522fea
+        {
522fea
+          error (0, errno, "%s", quote (stat_file));
522fea
+          exit_status = EXIT_FAILURE;
522fea
+          return;
522fea
+        }
522fea
+    }
522fea
+  else if (process_all && show_all_fs)
522fea
+    {
522fea
+      /* Ensure we don't output incorrect stats for over-mounted directories.
522fea
+         Discard stats when the device name doesn't match.  Though don't
522fea
+         discard when used and current mount entries are both remote due
522fea
+         to the possibility of aliased host names or exports.  */
522fea
+      struct stat sb;
522fea
+      if (stat (stat_file, &sb) == 0)
522fea
+        {
522fea
+          struct mount_entry const * dev_me = me_for_dev (sb.st_dev);
522fea
+          if (dev_me && ! STREQ (dev_me->me_devname, disk)
522fea
+              && (! dev_me->me_remote || ! me_remote))
522fea
+            {
522fea
+              fstype = "-";
522fea
+              fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree =
522fea
+              fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX;
522fea
+            }
522fea
+        }
522fea
     }
522fea
 
522fea
   if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
522fea
@@ -1320,8 +1325,7 @@ get_all_entries (void)
522fea
 {
522fea
   struct mount_entry *me;
522fea
 
522fea
-  if (!show_all_fs)
522fea
-    filter_mount_list ();
522fea
+  filter_mount_list (show_all_fs);
522fea
 
522fea
   for (me = mount_list; me; me = me->me_next)
522fea
     get_dev (me->me_devname, me->me_mountdir, NULL, NULL, me->me_type,
522fea
diff -urNp coreutils-8.22-orig/doc/coreutils.texi coreutils-8.22/doc/coreutils.texi
522fea
--- coreutils-8.22-orig/doc/coreutils.texi	2015-07-06 17:44:13.266328267 +0200
522fea
+++ coreutils-8.22/doc/coreutils.texi	2015-07-06 17:48:41.612327379 +0200
522fea
@@ -11149,11 +11149,15 @@ The program accepts the following option
522fea
 @itemx --all
522fea
 @opindex -a
522fea
 @opindex --all
522fea
-@cindex automounter file systems
522fea
 @cindex ignore file systems
522fea
-Include in the listing dummy file systems, which
522fea
-are omitted by default.  Such file systems are typically special-purpose
522fea
-pseudo-file-systems, such as automounter entries.
522fea
+Include in the listing dummy, duplicate, or inaccessible file systems, which
522fea
+are omitted by default. Dummy file systems are typically special purpose
522fea
+pseudo file systems such as @samp{/proc}, with no associated storage.
522fea
+Duplicate file systems are local or remote file systems that are mounted
522fea
+at separate locations in the local file hierarchy, or bind mounted locations.
522fea
+Inaccessible file systems are those which are mounted but subsequently
522fea
+over-mounted by another file system at that point, or otherwise inaccessible
522fea
+due to permissions of the mount point etc.
522fea
 
522fea
 @item -B @var{size}
522fea
 @itemx --block-size=@var{size}
522fea
diff -urNp coreutils-8.22-orig/tests/df/skip-duplicates.sh coreutils-8.22/tests/df/skip-duplicates.sh
522fea
--- coreutils-8.22-orig/tests/df/skip-duplicates.sh	2013-12-04 15:48:30.000000000 +0100
522fea
+++ coreutils-8.22/tests/df/skip-duplicates.sh	2015-07-06 17:45:47.176027871 +0200
522fea
@@ -2,7 +2,7 @@
522fea
 # Test df's behavior when the mount list contains duplicate entries.
522fea
 # This test is skipped on systems that lack LD_PRELOAD support; that's fine.
522fea
 
522fea
-# Copyright (C) 2012-2013 Free Software Foundation, Inc.
522fea
+# Copyright (C) 2012-2015 Free Software Foundation, Inc.
522fea
 
522fea
 # This program is free software: you can redistribute it and/or modify
522fea
 # it under the terms of the GNU General Public License as published by
522fea
@@ -21,19 +21,73 @@
522fea
 print_ver_ df
522fea
 require_gcc_shared_
522fea
 
522fea
-df || skip_ "df fails"
522fea
-
522fea
-# Simulate an mtab file with two entries of the same device number.
522fea
-# Also add entries with unstatable mount dirs to ensure that's handled.
522fea
-cat > k.c <<'EOF' || framework_failure_
522fea
+# We use --local here so as to not activate
522fea
+# potentially very many remote mounts.
522fea
+df --local || skip_ 'df fails'
522fea
+
522fea
+export CU_NONROOT_FS=$(df --local --output=target 2>&1 | grep /. | head -n1)
522fea
+export CU_REMOTE_FS=$(df --local --output=target 2>&1 | grep /. |
522fea
+                      tail -n+2 | head -n1)
522fea
+
522fea
+unique_entries=1
522fea
+test -z "$CU_NONROOT_FS" || unique_entries=$(expr $unique_entries + 1)
522fea
+test -z "$CU_REMOTE_FS" || unique_entries=$(expr $unique_entries + 2)
522fea
+
522fea
+grep '^#define HAVE_MNTENT_H 1' $CONFIG_HEADER > /dev/null \
522fea
+      || skip_ "no mntent.h available to confirm the interface"
522fea
+
522fea
+grep '^#define HAVE_GETMNTENT 1' $CONFIG_HEADER > /dev/null \
522fea
+      || skip_ "getmntent is not used on this system"
522fea
+
522fea
+# Simulate an mtab file to test various cases.
522fea
+cat > k.c <
522fea
+#define _GNU_SOURCE
522fea
 #include <stdio.h>
522fea
 #include <stdlib.h>
522fea
+#include <errno.h>
522fea
 #include <mntent.h>
522fea
+#include <string.h>
522fea
+#include <dlfcn.h>
522fea
+
522fea
+#define STREQ(a, b) (strcmp (a, b) == 0)
522fea
+
522fea
+FILE* fopen(const char *path, const char *mode)
522fea
+{
522fea
+  static FILE* (*fopen_func)(char const *, char const *);
522fea
+
522fea
+  /* get reference to original (libc provided) fopen */
522fea
+  if (!fopen_func)
522fea
+    {
522fea
+      fopen_func = (FILE*(*)(char const *, char const *))
522fea
+                   dlsym(RTLD_NEXT, "fopen");
522fea
+      if (!fopen_func)
522fea
+        {
522fea
+          fprintf (stderr, "Failed to find fopen()\n");
522fea
+          errno = ESRCH;
522fea
+          return NULL;
522fea
+        }
522fea
+    }
522fea
+
522fea
+  /* Returning ENOENT here will get read_file_system_list()
522fea
+     to fall back to using getmntent() below.  */
522fea
+  if (STREQ (path, "/proc/self/mountinfo"))
522fea
+    {
522fea
+      errno = ENOENT;
522fea
+      return NULL;
522fea
+    }
522fea
+  else
522fea
+    return fopen_func(path, mode);
522fea
+}
522fea
+
522fea
+#define STREQ(a, b) (strcmp (a, b) == 0)
522fea
 
522fea
 struct mntent *getmntent (FILE *fp)
522fea
 {
522fea
+  static char *nonroot_fs;
522fea
+  static char *remote_fs;
522fea
+  static int done;
522fea
+
522fea
   /* Prove that LD_PRELOAD works. */
522fea
-  static int done = 0;
522fea
   if (!done)
522fea
     {
522fea
       fclose (fopen ("x", "w"));
522fea
@@ -41,50 +95,92 @@ struct mntent *getmntent (FILE *fp)
522fea
     }
522fea
 
522fea
   static struct mntent mntents[] = {
522fea
-    {.mnt_fsname="/short",  .mnt_dir="/invalid/mount/dir"},
522fea
-    {.mnt_fsname="fsname",  .mnt_dir="/",},
522fea
-    {.mnt_fsname="/fsname", .mnt_dir="/root"},
522fea
-    {.mnt_fsname="/fsname", .mnt_dir="/"},
522fea
+    {.mnt_fsname="/short",  .mnt_dir="/invalid/mount/dir",       .mnt_opts=""},
522fea
+    {.mnt_fsname="fsname",  .mnt_dir="/",                        .mnt_opts=""},
522fea
+    {.mnt_fsname="/fsname", .mnt_dir="/.",                       .mnt_opts=""},
522fea
+    {.mnt_fsname="/fsname", .mnt_dir="/",                        .mnt_opts=""},
522fea
+    {.mnt_fsname="virtfs",  .mnt_dir="/NONROOT", .mnt_type="t1", .mnt_opts=""},
522fea
+    {.mnt_fsname="virtfs2", .mnt_dir="/NONROOT", .mnt_type="t2", .mnt_opts=""},
522fea
+    {.mnt_fsname="netns",   .mnt_dir="net:[1234567]",            .mnt_opts=""},
522fea
+    {.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE",                  .mnt_opts=""},
522fea
+    {.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE",                  .mnt_opts=""},
522fea
+    {.mnt_fsname="rem:ote2",.mnt_dir="/REMOTE",                  .mnt_opts=""},
522fea
   };
522fea
 
522fea
-  if (!getenv ("CU_TEST_DUPE_INVALID") && done == 1)
522fea
+  if (done == 1)
522fea
+    {
522fea
+      nonroot_fs = getenv ("CU_NONROOT_FS");
522fea
+      if (!nonroot_fs || !*nonroot_fs)
522fea
+        nonroot_fs = "/"; /* merge into / entries.  */
522fea
+
522fea
+      remote_fs = getenv ("CU_REMOTE_FS");
522fea
+    }
522fea
+
522fea
+  if (done == 1 && !getenv ("CU_TEST_DUPE_INVALID"))
522fea
     done++;  /* skip the first entry.  */
522fea
 
522fea
-  while (done++ <= 4)
522fea
+  while (done++ <= 10)
522fea
     {
522fea
-      mntents[done-2].mnt_type = "-";
522fea
+      if (!mntents[done-2].mnt_type)
522fea
+        mntents[done-2].mnt_type = "-";
522fea
+      if (!mntents[done-2].mnt_opts)
522fea
+        mntents[done-2].mnt_opts = "-";
522fea
+      if (STREQ (mntents[done-2].mnt_dir, "/NONROOT"))
522fea
+        mntents[done-2].mnt_dir = nonroot_fs;
522fea
+      if (STREQ (mntents[done-2].mnt_dir, "/REMOTE"))
522fea
+        {
522fea
+          if (!remote_fs || !*remote_fs)
522fea
+            continue;
522fea
+          else
522fea
+            mntents[done-2].mnt_dir = remote_fs;
522fea
+        }
522fea
       return &mntents[done-2];
522fea
     }
522fea
+
522fea
   return NULL;
522fea
 }
522fea
 EOF
522fea
 
522fea
 # Then compile/link it:
522fea
-gcc --std=gnu99 -shared -fPIC -ldl -O2 k.c -o k.so \
522fea
+gcc_shared_ k.c k.so \
522fea
   || framework_failure_ 'failed to build shared library'
522fea
 
522fea
 # Test if LD_PRELOAD works:
522fea
-LD_PRELOAD=./k.so df
522fea
+LD_PRELOAD=$LD_PRELOAD:./k.so df
522fea
 test -f x || skip_ "internal test failure: maybe LD_PRELOAD doesn't work?"
522fea
 
522fea
 # The fake mtab file should only contain entries
522fea
 # having the same device number; thus the output should
522fea
-# consist of a header and one entry.
522fea
-LD_PRELOAD=./k.so df >out || fail=1
522fea
-test $(wc -l 
522fea
-
522fea
-# Ensure we fail when unable to stat invalid entries
522fea
-LD_PRELOAD=./k.so CU_TEST_DUPE_INVALID=1 df >out && fail=1
522fea
-test $(wc -l 
522fea
+# consist of a header and unique entries.
522fea
+LD_PRELOAD=$LD_PRELOAD:./k.so df -T >out || fail=1
522fea
+test $(wc -l 
522fea
+
522fea
+# With --total we should suppress the duplicate but separate remote file system
522fea
+LD_PRELOAD=$LD_PRELOAD:./k.so df --total >out || fail=1
522fea
+test "$CU_REMOTE_FS" && elide_remote=1 || elide_remote=0
522fea
+test $(wc -l 
522fea
+  { fail=1; cat out; }
522fea
+
522fea
+# Ensure we don't fail when unable to stat (currently) unavailable entries
522fea
+LD_PRELOAD=$LD_PRELOAD:./k.so CU_TEST_DUPE_INVALID=1 df -T >out || fail=1
522fea
+test $(wc -l 
522fea
 
522fea
 # df should also prefer "/fsname" over "fsname"
522fea
-test $(grep -c '/fsname' 
522fea
-# ... and "/fsname" with '/' as Mounted on over '/root'
522fea
-test $(grep -c '/root' 
522fea
+if test "$unique_entries" = 2; then
522fea
+  test $(grep -c '/fsname' 
522fea
+  # ... and "/fsname" with '/' as Mounted on over '/.'
522fea
+  test $(grep -cF '/.' 
522fea
+fi
522fea
+
522fea
+# df should use the last seen devname (mnt_fsname) and devtype (mnt_type)
522fea
+test $(grep -c 'virtfs2.*t2' 
522fea
 
522fea
 # Ensure that filtering duplicates does not affect -a processing.
522fea
-LD_PRELOAD=./k.so df -a >out || fail=1
522fea
-test $(wc -l 
522fea
+LD_PRELOAD=$LD_PRELOAD:./k.so df -a >out || fail=1
522fea
+total_fs=6; test "$CU_REMOTE_FS" && total_fs=$(expr $total_fs + 3)
522fea
+test $(wc -l 
522fea
+# Ensure placeholder "-" values used for the eclipsed "virtfs"
522fea
+test $(grep -c 'virtfs *-' 
522fea
 
522fea
 # Ensure that filtering duplicates does not affect
522fea
 # argument processing (now without the fake getmntent()).
522fea
diff -urNp coreutils-8.22-orig/init.cfg coreutils-8.22/init.cfg
522fea
--- coreutils-8.22-orig/init.cfg	2015-07-06 18:04:08.930235675 +0200
522fea
+++ coreutils-8.22/init.cfg	2015-07-06 18:04:41.939481587 +0200
522fea
@@ -472,6 +472,18 @@ require_sparse_support_()
522fea
   fi
522fea
 }
522fea
 
522fea
+# Compile a shared lib using the GCC options for doing so.
522fea
+# Pass input and output file as parameters respectively.
522fea
+# Any other optional parmeters are passed to $CC.
522fea
+gcc_shared_()
522fea
+{
522fea
+  local in=$1
522fea
+  local out=$2
522fea
+  shift 2 || return 1
522fea
+
522fea
+  $CC -Wall -shared --std=gnu99 -fPIC -O2 $* "$in" -o "$out" -ldl
522fea
+}
522fea
+
522fea
 # There are a myriad of ways to build shared libs,
522fea
 # so we only consider running tests requiring shared libs,
522fea
 # on platforms that support building them as follows.