Recursively downloads a file entry to the target path.

  Args:
    dir_obj: An aff4 object that contains children.
    target_dir: Full path of the directory to write to.
    max_depth: Depth to download to. 1 means just the directory itself.
    depth: Current depth of recursion.
    overwrite: Should we overwrite files that exist.
    max_threads: Use this many threads to do the downloads.

def RecursiveDownload(dir_obj,
                      target_dir,
                      max_depth=10,
                      depth=1,
                      overwrite=False,
                      max_threads=10):
  """Recursively downloads a file entry to the target path.

  Args:
    dir_obj: An aff4 object that contains children.
    target_dir: Full path of the directory to write to.
    max_depth: Depth to download to. 1 means just the directory itself.
    depth: Current depth of recursion.
    overwrite: Should we overwrite files that exist.
    max_threads: Use this many threads to do the downloads.
  """
  if not isinstance(dir_obj, aff4.AFF4Volume):
    return

  # Reuse the same threadpool as we call recursively.
  thread_pool = threadpool.ThreadPool.Factory("Downloader", max_threads)
  thread_pool.Start()

  for sub_file_entry in dir_obj.OpenChildren():
    path_elements = [target_dir]
    sub_target_dir = u"/".join(path_elements)
    try:
      # Any file-like object with data in AFF4 should inherit AFF4Stream.
      if isinstance(sub_file_entry, aff4.AFF4Stream):
        args = (sub_file_entry.urn, sub_target_dir, sub_file_entry.token,
                overwrite)
        thread_pool.AddTask(
            target=CopyAFF4ToLocal, args=args, name="Downloader")
      elif "Container" in sub_file_entry.behaviours:
        if depth >= max_depth:  # Don't go any deeper.
          continue
        try:
          os.makedirs(sub_target_dir)
        except OSError:
          pass
        RecursiveDownload(
            sub_file_entry,
            sub_target_dir,
            overwrite=overwrite,
            depth=depth + 1)
    except IOError:
      logging.exception("Unable to download %s", sub_file_entry.urn)
    finally:
      sub_file_entry.Close()

  # Join and stop the threadpool.
  if depth <= 1:
    thread_pool.Stop(join_timeout=THREADPOOL_JOIN_TIMEOUT)