Adds new files consisting of given blob references.

  Args:
    client_path_blob_refs: A dictionary mapping `db.ClientPath` instances to
      lists of blob references.
    use_external_stores: A flag indicating if the files should also be added to
      external file stores.

  Returns:
    A dictionary mapping `db.ClientPath` to hash ids of the file.

  Raises:
    BlobNotFoundError: If one of the referenced blobs cannot be found.

def AddFilesWithUnknownHashes(
    client_path_blob_refs,
    use_external_stores = True
):
  """Adds new files consisting of given blob references.

  Args:
    client_path_blob_refs: A dictionary mapping `db.ClientPath` instances to
      lists of blob references.
    use_external_stores: A flag indicating if the files should also be added to
      external file stores.

  Returns:
    A dictionary mapping `db.ClientPath` to hash ids of the file.

  Raises:
    BlobNotFoundError: If one of the referenced blobs cannot be found.
  """
  hash_id_blob_refs = dict()
  client_path_hash_id = dict()
  metadatas = dict()

  all_client_path_blob_refs = list()
  for client_path, blob_refs in iteritems(client_path_blob_refs):
    # In the special case where there is only one blob, we don't need to go to
    # the data store to read said blob and rehash it, we have all that
    # information already available. For empty files without blobs, we can just
    # hash the empty string instead.
    if len(blob_refs) <= 1:
      if blob_refs:
        hash_id = rdf_objects.SHA256HashID.FromBytes(
            blob_refs[0].blob_id.AsBytes())
      else:
        hash_id = rdf_objects.SHA256HashID.FromData(b"")

      client_path_hash_id[client_path] = hash_id
      hash_id_blob_refs[hash_id] = blob_refs
      metadatas[hash_id] = FileMetadata(
          client_path=client_path, blob_refs=blob_refs)
    else:
      for blob_ref in blob_refs:
        all_client_path_blob_refs.append((client_path, blob_ref))

  client_path_offset = collections.defaultdict(lambda: 0)
  client_path_sha256 = collections.defaultdict(hashlib.sha256)
  verified_client_path_blob_refs = collections.defaultdict(list)

  client_path_blob_ref_batches = collection.Batch(
      items=all_client_path_blob_refs, size=_BLOBS_READ_BATCH_SIZE)

  for client_path_blob_ref_batch in client_path_blob_ref_batches:
    blob_id_batch = set(
        blob_ref.blob_id for _, blob_ref in client_path_blob_ref_batch)
    blobs = data_store.BLOBS.ReadBlobs(blob_id_batch)

    for client_path, blob_ref in client_path_blob_ref_batch:
      blob = blobs[blob_ref.blob_id]
      if blob is None:
        message = "Could not find one of referenced blobs: {}".format(
            blob_ref.blob_id)
        raise BlobNotFoundError(message)

      offset = client_path_offset[client_path]
      if blob_ref.size != len(blob):
        raise ValueError(
            "Got conflicting size information for blob %s: %d vs %d." %
            (blob_ref.blob_id, blob_ref.size, len(blob)))
      if blob_ref.offset != offset:
        raise ValueError(
            "Got conflicting offset information for blob %s: %d vs %d." %
            (blob_ref.blob_id, blob_ref.offset, offset))

      verified_client_path_blob_refs[client_path].append(blob_ref)
      client_path_offset[client_path] = offset + len(blob)
      client_path_sha256[client_path].update(blob)

  for client_path in iterkeys(client_path_sha256):
    sha256 = client_path_sha256[client_path].digest()
    hash_id = rdf_objects.SHA256HashID.FromBytes(sha256)

    client_path_hash_id[client_path] = hash_id
    hash_id_blob_refs[hash_id] = verified_client_path_blob_refs[client_path]

  data_store.REL_DB.WriteHashBlobReferences(hash_id_blob_refs)

  if use_external_stores:
    for client_path in iterkeys(verified_client_path_blob_refs):
      metadatas[client_path_hash_id[client_path]] = FileMetadata(
          client_path=client_path,
          blob_refs=verified_client_path_blob_refs[client_path])

    EXTERNAL_FILE_STORE.AddFiles(metadatas)

  return client_path_hash_id