--- /dev/null
+/* CTF archive files.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   This file is part of libctf.
+
+   libctf is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+   See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <ctf-impl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+static off_t arc_write_one_ctf (ctf_file_t * f, int fd, size_t threshold);
+static ctf_file_t *ctf_arc_open_by_offset (const struct ctf_archive *arc,
+                                          const ctf_sect_t *symsect,
+                                          const ctf_sect_t *strsect,
+                                          size_t offset, int *errp);
+static int sort_modent_by_name (const void *one, const void *two, void *n);
+static void *arc_mmap_header (int fd, size_t headersz);
+static void *arc_mmap_file (int fd, size_t size);
+static int arc_mmap_writeout (int fd, void *header, size_t headersz,
+                             const char **errmsg);
+static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg);
+
+/* bsearch() internal state.  */
+static __thread char *search_nametbl;
+
+/* Write out a CTF archive.  The entries in CTF_FILES are referenced by name:
+   the names are passed in the names array, which must have CTF_FILES entries.
+
+   Returns 0 on success, or an errno, or an ECTF_* value.  */
+int
+ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
+              const char **names, size_t threshold)
+{
+  const char *errmsg;
+  struct ctf_archive *archdr;
+  int fd;
+  size_t i;
+  char dummy = 0;
+  size_t headersz;
+  ssize_t namesz;
+  size_t ctf_startoffs;                /* Start of the section we are working over.  */
+  char *nametbl = NULL;                /* The name table.  */
+  char *np;
+  off_t nameoffs;
+  struct ctf_archive_modent *modent;
+
+  ctf_dprintf ("Writing archive %s with %zi files\n", file, ctf_file_cnt);
+
+  if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0)
+    {
+      errmsg = "ctf_arc_write(): cannot create %s: %s\n";
+      goto err;
+    }
+
+  /* Figure out the size of the mmap()ed header, including the
+     ctf_archive_modent array.  We assume that all of this needs no
+     padding: a likely assumption, given that it's all made up of
+     uint64_t's.  */
+  headersz = sizeof (struct ctf_archive)
+    + (ctf_file_cnt * sizeof (uint64_t) * 2);
+  ctf_dprintf ("headersz is %zi\n", headersz);
+
+  /* From now on we work in two pieces: an mmap()ed region from zero up to the
+     headersz, and a region updated via write() starting after that, containing
+     all the tables.  Platforms that do not support mmap() just use write().  */
+  ctf_startoffs = headersz;
+  if (lseek (fd, ctf_startoffs - 1, SEEK_SET) < 0)
+    {
+      errmsg = "ctf_arc_write(): cannot extend file while writing %s: %s\n";
+      goto err_close;
+    }
+
+  if (write (fd, &dummy, 1) < 0)
+    {
+      errmsg = "ctf_arc_write(): cannot extend file while writing %s: %s\n";
+      goto err_close;
+    }
+
+  if ((archdr = arc_mmap_header (fd, headersz)) == NULL)
+    {
+      errmsg = "ctf_arc_write(): Cannot mmap() %s: %s\n";
+      goto err_close;
+    }
+
+  /* Fill in everything we can, which is everything other than the name
+     table offset.  */
+  archdr->ctfa_magic = htole64 (CTFA_MAGIC);
+  archdr->ctfa_nfiles = htole64 (ctf_file_cnt);
+  archdr->ctfa_ctfs = htole64 (ctf_startoffs);
+
+  /* We could validate that all CTF files have the same data model, but
+     since any reasonable construction process will be building things of
+     only one bitness anyway, this is pretty pointless, so just use the
+     model of the first CTF file for all of them.  (It *is* valid to
+     create an empty archive: the value of ctfa_model is irrelevant in
+     this case, but we must be sure not to dereference uninitialized
+     memory.)  */
+
+  if (ctf_file_cnt > 0)
+    archdr->ctfa_model = htole64 (ctf_getmodel (ctf_files[0]));
+
+  /* Now write out the CTFs: ctf_archive_modent array via the mapping,
+     ctfs via write().  The names themselves have not been written yet: we
+     track them in a local strtab until the time is right, and sort the
+     modents array after construction.
+
+    The name table is not sorted.  */
+
+  for (i = 0, namesz = 0; i < le64toh (archdr->ctfa_nfiles); i++)
+    namesz += strlen (names[i]) + 1;
+
+  nametbl = malloc (namesz);
+  if (nametbl == NULL)
+    {
+      errmsg = "Error writing named CTF to %s: %s\n";
+      goto err_unmap;
+    }
+
+  for (i = 0, namesz = 0,
+       modent = (ctf_archive_modent_t *) ((char *) archdr
+                                         + sizeof (struct ctf_archive));
+       i < le64toh (archdr->ctfa_nfiles); i++)
+    {
+      off_t off;
+
+      strcpy (&nametbl[namesz], names[i]);
+
+      off = arc_write_one_ctf (ctf_files[i], fd, threshold);
+      ctf_dprintf ("Written %s, offset now %zi\n", names[i], off);
+      if ((off < 0) && (off > -ECTF_BASE))
+       {
+         errmsg = "ctf_arc_write(): Cannot determine file "
+           "position while writing %s: %s";
+         goto err_free;
+       }
+      if (off < 0)
+       {
+         errmsg = "ctf_arc_write(): Cannot write CTF file to %s: %s\n";
+         errno = off * -1;
+         goto err_free;
+       }
+
+      modent->name_offset = htole64 (namesz);
+      modent->ctf_offset = htole64 (off - ctf_startoffs);
+      namesz += strlen (names[i]) + 1;
+      modent++;
+    }
+
+  qsort_r ((ctf_archive_modent_t *) ((char *) archdr
+                                    + sizeof (struct ctf_archive)),
+          le64toh (archdr->ctfa_nfiles),
+          sizeof (struct ctf_archive_modent), sort_modent_by_name, nametbl);
+
+   /* Now the name table.  */
+
+  if ((nameoffs = lseek (fd, 0, SEEK_CUR)) < 0)
+    {
+      errmsg = "ctf_arc_write(): Cannot get current file position "
+       "in %s: %s\n";
+      goto err_free;
+    }
+  archdr->ctfa_names = htole64 (nameoffs);
+  np = nametbl;
+  while (namesz > 0)
+    {
+      ssize_t len;
+      if ((len = write (fd, np, namesz)) < 0)
+       {
+         errmsg = "ctf_arc_write(): Cannot write name table in %s: %s\n";
+         goto err_free;
+       }
+      namesz -= len;
+      np += len;
+    }
+  free (nametbl);
+
+  if (arc_mmap_writeout (fd, archdr, headersz, &errmsg) < 0)
+    goto err_unmap;
+  if (arc_mmap_unmap (archdr, headersz, &errmsg) < 0)
+    goto err_unlink;
+  if (close (fd) < 0)
+    {
+      errmsg = "ctf_arc_write(): Cannot close after writing to %s: %s\n";
+      goto err_unlink;
+    }
+
+  return 0;
+
+err_free:
+  free (nametbl);
+err_unmap:
+  arc_mmap_unmap (archdr, headersz, NULL);
+err_close:
+  close (fd);
+err_unlink:
+  unlink (file);
+err:
+  ctf_dprintf (errmsg, file, errno < ECTF_BASE ? strerror (errno) :
+              ctf_errmsg (errno));
+  return errno;
+}
+
+/* Write one CTF file out.  Return the file position of the written file (or
+   rather, of the file-size uint64_t that precedes it): negative return is a
+   negative errno or ctf_errno value.  On error, the file position may no longer
+   be at the end of the file.  */
+static off_t
+arc_write_one_ctf (ctf_file_t * f, int fd, size_t threshold)
+{
+  off_t off, end_off;
+  uint64_t ctfsz = 0;
+  char *ctfszp;
+  size_t ctfsz_len;
+  int (*writefn) (ctf_file_t * fp, int fd);
+
+  if ((off = lseek (fd, 0, SEEK_CUR)) < 0)
+    return errno * -1;
+
+  if (f->ctf_size > threshold)
+    writefn = ctf_compress_write;
+  else
+    writefn = ctf_write;
+
+  /* This zero-write turns into the size in a moment. */
+  ctfsz_len = sizeof (ctfsz);
+  ctfszp = (char *) &ctfsz;
+  while (ctfsz_len > 0)
+    {
+      ssize_t writelen = write (fd, ctfszp, ctfsz_len);
+      if (writelen < 0)
+       return errno * -1;
+      ctfsz_len -= writelen;
+      ctfszp += writelen;
+    }
+
+  if (writefn (f, fd) != 0)
+    return f->ctf_errno * -1;
+
+  if ((end_off = lseek (fd, 0, SEEK_CUR)) < 0)
+    return errno * -1;
+  ctfsz = htole64 (end_off - off);
+
+  if ((lseek (fd, off, SEEK_SET)) < 0)
+    return errno * -1;
+
+  /* ... here.  */
+  ctfsz_len = sizeof (ctfsz);
+  ctfszp = (char *) &ctfsz;
+  while (ctfsz_len > 0)
+    {
+      ssize_t writelen = write (fd, ctfszp, ctfsz_len);
+      if (writelen < 0)
+       return errno * -1;
+      ctfsz_len -= writelen;
+      ctfszp += writelen;
+    }
+
+  end_off = LCTF_ALIGN_OFFS (end_off, 8);
+  if ((lseek (fd, end_off, SEEK_SET)) < 0)
+    return errno * -1;
+
+  return off;
+}
+
+/* qsort() function to sort the array of struct ctf_archive_modents into
+   ascending name order.  */
+static int
+sort_modent_by_name (const void *one, const void *two, void *n)
+{
+  const struct ctf_archive_modent *a = one;
+  const struct ctf_archive_modent *b = two;
+  char *nametbl = n;
+
+  return strcmp (&nametbl[le64toh (a->name_offset)],
+                &nametbl[le64toh (b->name_offset)]);
+}
+
+/* bsearch() function to search for a given name in the sorted array of struct
+   ctf_archive_modents.  */
+static int
+search_modent_by_name (const void *key, const void *ent)
+{
+  const char *k = key;
+  const struct ctf_archive_modent *v = ent;
+
+  return strcmp (k, &search_nametbl[le64toh (v->name_offset)]);
+}
+
+/* A trivial wrapper: open a CTF archive, from data in a buffer (which the
+   caller must preserve until ctf_arc_close() time).  Returns the archive, or
+   NULL and an error in *err (if not NULL).  */
+struct ctf_archive *
+ctf_arc_bufopen (const void *buf, size_t size _libctf_unused_, int *errp)
+{
+  struct ctf_archive *arc = (struct ctf_archive *) buf;
+
+  if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
+    {
+      if (errp)
+       *errp = ECTF_FMT;
+      return NULL;
+    }
+  return arc;
+}
+
+/* Open a CTF archive.  Returns the archive, or NULL and an error in *err (if
+   not NULL).  */
+struct ctf_archive *
+ctf_arc_open_internal (const char *filename, int *errp)
+{
+  const char *errmsg;
+  int fd;
+  struct stat s;
+  struct ctf_archive *arc;             /* (Actually the whole file.)  */
+
+  libctf_init_debug();
+  if ((fd = open (filename, O_RDONLY)) < 0)
+    {
+      errmsg = "ctf_arc_open(): cannot open %s: %s\n";
+      goto err;
+    }
+  if (fstat (fd, &s) < 0)
+    {
+      errmsg = "ctf_arc_open(): cannot stat %s: %s\n";
+      goto err_close;
+    }
+
+  if ((arc = arc_mmap_file (fd, s.st_size)) == NULL)
+    {
+      errmsg = "ctf_arc_open(): Cannot read in %s: %s\n";
+      goto err_close;
+    }
+
+  if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
+    {
+      errmsg = "ctf_arc_open(): Invalid magic number";
+      errno = ECTF_FMT;
+      goto err_unmap;
+    }
+
+  /* This horrible hack lets us know how much to unmap when the file is
+     closed.  (We no longer need the magic number, and the mapping
+     is private.)  */
+  arc->ctfa_magic = s.st_size;
+  close (fd);
+  return arc;
+
+err_unmap:
+  arc_mmap_unmap (arc, s.st_size, NULL);
+err_close:
+  close (fd);
+err:
+  if (errp)
+    *errp = errno;
+  ctf_dprintf (errmsg, filename, errno < ECTF_BASE ? strerror (errno) :
+              ctf_errmsg (errno));
+  return NULL;
+}
+
+/* Close an archive.  */
+void
+ctf_arc_close_internal (struct ctf_archive *arc)
+{
+  if (arc == NULL)
+    return;
+
+  /* See the comment in ctf_arc_open().  */
+  arc_mmap_unmap (arc, arc->ctfa_magic, NULL);
+}
+
+/* Public entry point: close an archive, or CTF file.  */
+void
+ctf_arc_close (ctf_archive_t *arc)
+{
+  if (arc == NULL)
+    return;
+
+  if (arc->ctfi_is_archive)
+    ctf_arc_close_internal (arc->ctfi_archive);
+  else
+    ctf_file_close (arc->ctfi_file);
+  free ((void *) arc->ctfi_symsect.cts_data);
+  free ((void *) arc->ctfi_strsect.cts_data);
+  free (arc->ctfi_data);
+  free (arc);
+}
+
+/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+   non-NULL.  A name of NULL means to open the default file.  */
+static ctf_file_t *
+ctf_arc_open_by_name_internal (const struct ctf_archive *arc,
+                              const ctf_sect_t *symsect,
+                              const ctf_sect_t *strsect,
+                              const char *name, int *errp)
+{
+  struct ctf_archive_modent *modent;
+
+  if (name == NULL)
+    name = _CTF_SECTION;                /* The default name.  */
+
+  ctf_dprintf ("ctf_arc_open_by_name(%s): opening\n", name);
+
+  modent = (ctf_archive_modent_t *) ((char *) arc
+                                    + sizeof (struct ctf_archive));
+
+  search_nametbl = (char *) arc + le64toh (arc->ctfa_names);
+  modent = bsearch (name, modent, le64toh (arc->ctfa_nfiles),
+                   sizeof (struct ctf_archive_modent),
+                   search_modent_by_name);
+
+  /* This is actually a common case and normal operation: no error
+     debug output.  */
+  if (modent == NULL)
+    {
+      if (errp)
+       *errp = ECTF_ARNNAME;
+      return NULL;
+    }
+
+  return ctf_arc_open_by_offset (arc, symsect, strsect,
+                                le64toh (modent->ctf_offset), errp);
+}
+
+/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+   non-NULL.  A name of NULL means to open the default file.
+
+   Use the specified string and symbol table sections.
+
+   Public entry point.  */
+ctf_file_t *
+ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
+                              const ctf_sect_t *symsect,
+                              const ctf_sect_t *strsect,
+                              const char *name,
+                              int *errp)
+{
+  if (arc->ctfi_is_archive)
+    {
+      ctf_file_t *ret;
+      ret = ctf_arc_open_by_name_internal (arc->ctfi_archive, symsect, strsect,
+                                          name, errp);
+      if (ret)
+       ret->ctf_archive = (ctf_archive_t *) arc;
+      return ret;
+    }
+
+  if ((name != NULL) && (strcmp (name, _CTF_SECTION) != 0))
+    {
+      if (errp)
+       *errp = ECTF_ARNNAME;
+      return NULL;
+    }
+  arc->ctfi_file->ctf_archive = (ctf_archive_t *) arc;
+
+  /* Bump the refcount so that the user can ctf_file_close() it.  */
+  arc->ctfi_file->ctf_refcnt++;
+  return arc->ctfi_file;
+}
+
+/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+   non-NULL.  A name of NULL means to open the default file.
+
+   Public entry point.  */
+ctf_file_t *
+ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name, int *errp)
+{
+  const ctf_sect_t *symsect = &arc->ctfi_symsect;
+  const ctf_sect_t *strsect = &arc->ctfi_strsect;
+
+  if (symsect->cts_name == NULL)
+    symsect = NULL;
+  if (strsect->cts_name == NULL)
+    strsect = NULL;
+
+  return ctf_arc_open_by_name_sections (arc, symsect, strsect, name, errp);
+}
+
+/* Return the ctf_file_t at the given ctfa_ctfs-relative offset, or NULL if
+   none, setting 'err' if non-NULL.  */
+static ctf_file_t *
+ctf_arc_open_by_offset (const struct ctf_archive *arc,
+                       const ctf_sect_t *symsect,
+                       const ctf_sect_t *strsect, size_t offset,
+                       int *errp)
+{
+  ctf_sect_t ctfsect;
+  ctf_file_t *fp;
+
+  ctf_dprintf ("ctf_arc_open_by_offset(%zi): opening\n", offset);
+
+  bzero (&ctfsect, sizeof (ctf_sect_t));
+
+  offset += le64toh (arc->ctfa_ctfs);
+
+  ctfsect.cts_name = _CTF_SECTION;
+  ctfsect.cts_type = SHT_PROGBITS;
+  ctfsect.cts_flags = SHF_ALLOC;
+  ctfsect.cts_size = le64toh (*((uint64_t *) ((char *) arc + offset)));
+  ctfsect.cts_entsize = 1;
+  ctfsect.cts_offset = 0;
+  ctfsect.cts_data = (void *) ((char *) arc + offset + sizeof (uint64_t));
+  fp = ctf_bufopen (&ctfsect, symsect, strsect, errp);
+  if (fp)
+    ctf_setmodel (fp, le64toh (arc->ctfa_model));
+  return fp;
+}
+
+/* Raw iteration over all CTF files in an archive.  We pass the raw data for all
+   CTF files in turn to the specified callback function.  */
+static int
+ctf_archive_raw_iter_internal (const struct ctf_archive *arc,
+                              ctf_archive_raw_member_f *func, void *data)
+{
+  int rc;
+  size_t i;
+  struct ctf_archive_modent *modent;
+  const char *nametbl;
+
+  modent = (ctf_archive_modent_t *) ((char *) arc
+                                    + sizeof (struct ctf_archive));
+  nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
+
+  for (i = 0; i < le64toh (arc->ctfa_nfiles); i++)
+    {
+      const char *name;
+      char *fp;
+
+      name = &nametbl[le64toh (modent[i].name_offset)];
+      fp = ((char *) arc + le64toh (arc->ctfa_ctfs)
+           + le64toh (modent[i].ctf_offset));
+
+      if ((rc = func (name, (void *) (fp + sizeof (uint64_t)),
+                     le64toh (*((uint64_t *) fp)), data)) != 0)
+       return rc;
+    }
+  return 0;
+}
+
+/* Raw iteration over all CTF files in an archive: public entry point.
+
+   Returns -EINVAL if not supported for this sort of archive.  */
+int
+ctf_archive_raw_iter (const ctf_archive_t *arc,
+                     ctf_archive_raw_member_f * func, void *data)
+{
+  if (arc->ctfi_is_archive)
+    return ctf_archive_raw_iter_internal (arc->ctfi_archive, func, data);
+
+  return -EINVAL;                       /* Not supported. */
+}
+
+/* Iterate over all CTF files in an archive.  We pass all CTF files in turn to
+   the specified callback function.  */
+static int
+ctf_archive_iter_internal (const ctf_archive_t *wrapper,
+                          const struct ctf_archive *arc,
+                          const ctf_sect_t *symsect,
+                          const ctf_sect_t *strsect,
+                          ctf_archive_member_f *func, void *data)
+{
+  int rc;
+  size_t i;
+  ctf_file_t *f;
+  struct ctf_archive_modent *modent;
+  const char *nametbl;
+
+  modent = (ctf_archive_modent_t *) ((char *) arc
+                                    + sizeof (struct ctf_archive));
+  nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
+
+  for (i = 0; i < le64toh (arc->ctfa_nfiles); i++)
+    {
+      const char *name;
+
+      name = &nametbl[le64toh (modent[i].name_offset)];
+      if ((f = ctf_arc_open_by_name_internal (arc, symsect, strsect,
+                                             name, &rc)) == NULL)
+       return rc;
+
+      f->ctf_archive = (ctf_archive_t *) wrapper;
+      if ((rc = func (f, name, data)) != 0)
+       {
+         ctf_file_close (f);
+         return rc;
+       }
+
+      ctf_file_close (f);
+    }
+  return 0;
+}
+
+/* Iterate over all CTF files in an archive: public entry point.  We pass all
+   CTF files in turn to the specified callback function.  */
+int
+ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func,
+                 void *data)
+{
+  const ctf_sect_t *symsect = &arc->ctfi_symsect;
+  const ctf_sect_t *strsect = &arc->ctfi_strsect;
+
+  if (symsect->cts_name == NULL)
+    symsect = NULL;
+  if (strsect->cts_name == NULL)
+    strsect = NULL;
+
+  if (arc->ctfi_is_archive)
+    return ctf_archive_iter_internal (arc, arc->ctfi_archive, symsect, strsect,
+                                     func, data);
+
+  return func (arc->ctfi_file, _CTF_SECTION, data);
+}
+
+#ifdef HAVE_MMAP
+/* Map the header in.  Only used on new, empty files.  */
+static void *arc_mmap_header (int fd, size_t headersz)
+{
+  void *hdr;
+  if ((hdr = mmap (NULL, headersz, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+                  0)) == MAP_FAILED)
+    return NULL;
+  return hdr;
+}
+
+/* mmap() the whole file, for reading only.  (Map it writably, but privately: we
+   need to modify the region, but don't need anyone else to see the
+   modifications.)  */
+static void *arc_mmap_file (int fd, size_t size)
+{
+  void *arc;
+  if ((arc = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+                  fd, 0)) == MAP_FAILED)
+    return NULL;
+  return arc;
+}
+
+/* Persist the header to disk.  */
+static int arc_mmap_writeout (int fd _libctf_unused_, void *header,
+                             size_t headersz, const char **errmsg)
+{
+    if (msync (header, headersz, MS_ASYNC) < 0)
+    {
+      if (errmsg)
+       *errmsg = "arc_mmap_writeout(): Cannot sync after writing to %s: %s\n";
+      return -1;
+    }
+    return 0;
+}
+
+/* Unmap the region.  */
+static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg)
+{
+  if (munmap (header, headersz) < 0)
+    {
+      if (errmsg)
+       *errmsg = "arc_mmap_munmap(): Cannot unmap after writing to %s: %s\n";
+      return -1;
+    }
+    return 0;
+}
+#else
+/* Map the header in.  Only used on new, empty files.  */
+static void *arc_mmap_header (int fd, size_t headersz)
+{
+  void *hdr;
+  if ((hdr = malloc (headersz)) == NULL)
+    return NULL;
+  return hdr;
+}
+
+/* Pull in the whole file, for reading only.  We assume the current file
+   position is at the start of the file.  */
+static void *arc_mmap_file (int fd, size_t size)
+{
+  char *data;
+
+  if ((data = malloc (size)) == NULL)
+    return NULL;
+
+  if (ctf_pread (fd, data, size, 0) < 0)
+    {
+      free (data);
+      return NULL;
+    }
+  return data;
+}
+
+/* Persist the header to disk.  */
+static int arc_mmap_writeout (int fd, void *header, size_t headersz,
+                             const char **errmsg)
+{
+  ssize_t len;
+  size_t acc = 0;
+  char *data = (char *) header;
+  ssize_t count = headersz;
+
+  if ((lseek (fd, 0, SEEK_SET)) < 0)
+    {
+      if (errmsg)
+       *errmsg = "arc_mmap_writeout(): Cannot seek while writing header to "
+         "%s: %s\n";
+      return -1;
+    }
+
+  while (headersz > 0)
+    {
+      if ((len = write (fd, data, count)) < 0)
+       {
+         if (errmsg)
+           *errmsg = "arc_mmap_writeout(): Cannot write header to %s: %s\n";
+         return len;
+       }
+      if (len == EINTR)
+       continue;
+
+      acc += len;
+      if (len == 0)                            /* EOF.  */
+       break;
+
+      count -= len;
+      data += len;
+    }
+  return 0;
+}
+
+/* Unmap the region.  */
+static int arc_mmap_unmap (void *header, size_t headersz _libctf_unused_,
+                          const char **errmsg _libctf_unused_)
+{
+  free (header);
+  return 0;
+}
+#endif