docs: def AddFile(self, fd):
      """Adds a file to the hash file store.
  
      We take a file in the client space:
        aff4:/C.123123123/fs/os/usr/local/blah
  
      Hash it, update the hash in the original file if its different to the
      one calculated on the client, and copy the original AFF4 object to
  
        aff4:/files/hash/generic/sha256/123123123 (canonical reference)
  
      We then create symlinks for all other hash types:
  
        aff4:/files/hash/generic/sha1/345345345
        aff4:/files/hash/generic/md5/456456456
        aff4:/files/hash/pecoff/md5/aaaaaaaa (only for PEs)
        aff4:/files/hash/pecoff/sha1/bbbbbbbb (only for PEs)
  
      When present in PE files, the signing data (revision, cert_type,
      certificate) is added to the original object.
  
      This can't be done simply in the FileStore.Write() method with fixed hash
      buffer sizes because the authenticode hashes need to track hashing of
      different-sized regions based on the signature information.
  
      Args:
        fd: File open for reading.
  
      Raises:
        IOError: If there was an error writing the file.
      """
      hashes = self._HashFile(fd)
  
      # The empty file is very common, we don't keep the back references for it
      # in the DB since it just takes up too much space.
      empty_hash = ("e3b0c44298fc1c149afbf4c8996fb924"
                    "27ae41e4649b934ca495991b7852b855")
      if hashes.sha256 == empty_hash:
        return
  
      # Update the hashes field now that we have calculated them all.
      fd.Set(fd.Schema.HASH, hashes)
      fd.Flush()
  
      # sha256 is the canonical location.
      canonical_urn = self.PATH.Add("generic/sha256").Add(str(hashes.sha256))
      if not list(aff4.FACTORY.Stat([canonical_urn])):
        aff4.FACTORY.Copy(fd.urn, canonical_urn)
        # Remove the STAT entry, it makes no sense to copy it between clients.
        with aff4.FACTORY.Open(
            canonical_urn, mode="rw", token=self.token) as new_fd:
          new_fd.Set(new_fd.Schema.STAT(None))
  
      self._AddToIndex(canonical_urn, fd.urn)
  
      for hash_type, hash_digest in hashes.ListSetFields():
        # Determine fingerprint type.
        hash_type = hash_type.name
        # No need to create a symlink for sha256, it's the canonical location.
        if hash_type == "sha256":
          continue
        hash_digest = str(hash_digest)
        fingerprint_type = "generic"
        if hash_type.startswith("pecoff_"):
          fingerprint_type = "pecoff"
          hash_type = hash_type[len("pecoff_"):]
        if hash_type not in self.HASH_TYPES[fingerprint_type]:
          continue
  
        file_store_urn = self.PATH.Add(fingerprint_type).Add(hash_type).Add(
            hash_digest)
  
        with aff4.FACTORY.Create(
            file_store_urn, aff4.AFF4Symlink, token=self.token) as symlink:
          symlink.Set(symlink.Schema.SYMLINK_TARGET, canonical_urn)
  
      # We do not want to be externally written here.
      return None