docs: BigInteger buildData(Collection<DataProducer> producers, File output, final StringBuilder checksums, TarOptions options) throws NoSuchAlgorithmException, IOException, CompressorException {
  
          final File dir = output.getParentFile();
          if (dir != null && (!dir.exists() || !dir.isDirectory())) {
              throw new IOException("Cannot write data file at '" + output.getAbsolutePath() + "'");
          }
  
          final TarArchiveOutputStream tarOutputStream = new TarArchiveOutputStream(
              options.compression().toCompressedOutputStream(new FileOutputStream(output))
          );
          tarOutputStream.setLongFileMode(options.longFileMode());
          tarOutputStream.setBigNumberMode(options.bigNumberMode());
  
          final MessageDigest digest = MessageDigest.getInstance("MD5");
  
          final Total dataSize = new Total();
  
          final List<String> addedDirectories = new ArrayList<String>();
          final DataConsumer receiver = new DataConsumer() {
  
              public void onEachDir(TarArchiveEntry dirEntry) throws IOException {
                  // Check link name
                  checkField(dirEntry.getLinkName(), TarConstants.NAMELEN);
                  // Check user name
                  checkField(dirEntry.getUserName(), TarConstants.UNAMELEN);
                  // Check group name
                  checkField(dirEntry.getUserName(), TarConstants.GNAMELEN);
  
                  dirEntry.setName(fixPathTar(dirEntry.getName()));
  
                  createParentDirectories(dirEntry.getName(), dirEntry.getUserName(), dirEntry.getLongUserId(), dirEntry.getGroupName(), dirEntry.getLongGroupId());
  
                  // The directory passed in explicitly by the caller also gets the passed-in mode.  (Unlike
                  // the parent directories for now.  See related comments at "int mode =" in
                  // createParentDirectories, including about a possible bug.)
                  createDirectory(dirEntry.getName(), dirEntry.getUserName(), dirEntry.getLongUserId(), dirEntry.getGroupName(), dirEntry.getLongGroupId(), dirEntry.getMode(), 0);
  
                  console.debug("dir: " + dirEntry.getName());
              }
  
              public void onEachFile(InputStream input, TarArchiveEntry fileEntry) throws IOException {
                  // Check link name
                  checkField(fileEntry.getLinkName(), TarConstants.NAMELEN);
                  // Check user name
                  checkField(fileEntry.getUserName(), TarConstants.UNAMELEN);
                  // Check group name
                  checkField(fileEntry.getGroupName(), TarConstants.GNAMELEN);
  
                  // For md5sum
                  String rawFileEntryName = fileEntry.getName();
  
                  fileEntry.setName(fixPathTar(fileEntry.getName()));
  
                  createParentDirectories(fileEntry.getName(), fileEntry.getUserName(), fileEntry.getLongUserId(), fileEntry.getGroupName(), fileEntry.getLongGroupId());
  
                  tarOutputStream.putArchiveEntry(fileEntry);
  
                  dataSize.add(fileEntry.getSize());
                  digest.reset();
  
                  Utils.copy(input, new DigestOutputStream(tarOutputStream, digest));
  
                  final String md5 = Utils.toHex(digest.digest());
  
                  tarOutputStream.closeArchiveEntry();
  
                  console.debug(
                      "file:" + fileEntry.getName() +
                          " size:" + fileEntry.getSize() +
                          " mode:" + fileEntry.getMode() +
                          " linkname:" + fileEntry.getLinkName() +
                          " username:" + fileEntry.getUserName() +
                          " userid:" + fileEntry.getLongUserId() +
                          " groupname:" + fileEntry.getGroupName() +
                          " groupid:" + fileEntry.getLongGroupId() +
                          " modtime:" + fileEntry.getModTime() +
                          " md5: " + md5
                  );
  
                  // append to file md5 list, two spaces to be compatible with GNU coreutils md5sum
                  checksums.append(md5).append("  ").append(fixPathMd5(rawFileEntryName)).append('\n');
              }
  
              public void onEachLink(TarArchiveEntry entry) throws IOException {
                  // Check link name
                  checkField(entry.getLinkName(), TarConstants.NAMELEN);
                  // Check user name
                  checkField(entry.getUserName(), TarConstants.UNAMELEN);
                  // Check group name
                  checkField(entry.getGroupName(), TarConstants.GNAMELEN);
  
                  entry.setName(fixPathTar(entry.getName()));
  
                  createParentDirectories(entry.getName(), entry.getUserName(), entry.getLongUserId(), entry.getGroupName(), entry.getLongGroupId());
  
                  tarOutputStream.putArchiveEntry(entry);
                  tarOutputStream.closeArchiveEntry();
  
                  console.debug(
                      "link:" + entry.getName() +
                      " mode:" + entry.getMode() +
                      " linkname:" + entry.getLinkName() +
                      " username:" + entry.getUserName() +
                      " userid:" + entry.getLongUserId() +
                      " groupname:" + entry.getGroupName() +
                      " groupid:" + entry.getLongGroupId()
                   );
              }
  
  
              private void createDirectory( String directory, String user, long uid, String group, long gid, int mode, long size ) throws IOException {
                  // All dirs should end with "/" when created, or the test DebAndTaskTestCase.testTarFileSet() thinks its a file
                  // and so thinks it has the wrong permission.
                  // This consistency also helps when checking if a directory already exists in addedDirectories.
  
                  if (!directory.endsWith("/")) {
                      directory += "/";
                  }
  
                  if (!addedDirectories.contains(directory)) {
                      TarArchiveEntry entry = new TarArchiveEntry(directory, true);
                      entry.setUserName(user);
                      entry.setUserId(uid);
                      entry.setGroupName(group);
                      entry.setGroupId(gid);
                      entry.setMode(mode);
                      entry.setSize(size);
  
                      tarOutputStream.putArchiveEntry(entry);
                      tarOutputStream.closeArchiveEntry();
                      addedDirectories.add(directory); // so addedDirectories consistently have "/" for finding duplicates.
                  }
              }
  
              private void createParentDirectories( String filename, String user, long uid, String group, long gid ) throws IOException {
                  String dirname = fixPathTar(new File(filename).getParent());
  
                  // Debian packages must have parent directories created
                  // before sub-directories or files can be installed.
                  // For example, if an entry of ./usr/lib/foo/bar existed
                  // in a .deb package, but the ./usr/lib/foo directory didn't
                  // exist, the package installation would fail.  The .deb must
                  // then have an entry for ./usr/lib/foo and then ./usr/lib/foo/bar
  
                  if (dirname == null) {
                      return;
                  }
  
                  // The loop below will create entries for all parent directories
                  // to ensure that .deb packages will install correctly.
                  String[] pathParts = dirname.split("/");
                  String parentDir = "./";
                  for (int i = 1; i < pathParts.length; i++) {
                      parentDir += pathParts[i] + "/";
                      // Make it so the dirs can be traversed by users.
                      // We could instead try something more granular, like setting the directory
                      // permission to 'rx' for each of the 3 user/group/other read permissions
                      // found on the file being added (ie, only if "other" has read
                      // permission on the main node, then add o+rx permission on all the containing
                      // directories, same w/ user & group), and then also we'd have to
                      // check the parentDirs collection of those already added to
                      // see if those permissions need to be similarly updated.  (Note, it hasn't
                      // been demonstrated, but there might be a bug if a user specifically
                      // requests a directory with certain permissions,
                      // that has already been auto-created because it was a parent, and if so, go set
                      // the user-requested mode on that directory instead of this automatic one.)
                      // But for now, keeping it simple by making every dir a+rx.   Examples are:
                      // drw-r----- fs/fs   # what you get with setMode(mode)
                      // drwxr-xr-x fs/fs   # Usable. Too loose?
                      int mode = TarArchiveEntry.DEFAULT_DIR_MODE;
  
                      createDirectory(parentDir, user, uid, group, gid, mode, 0);
                  }
              }
          };
  
          try {
              for (DataProducer data : producers) {
                  data.produce(receiver);
              }
          } finally {
              tarOutputStream.close();
          }
  
          console.debug("Total size: " + dataSize);
  
          return dataSize.count;
      }