code: private LocatedBlock recoverBlock(int namespaceId, Block block, boolean keepLength,
        DatanodeID[] datanodeids, boolean closeFile, long deadline) throws IOException {
      InjectionHandler.processEvent(InjectionEvent.DATANODE_BEFORE_RECOVERBLOCK,
          this);
  
      // If the block is already being recovered, then skip recovering it.
      // This can happen if the namenode and client start recovering the same
      // file at the same time.
      synchronized (ongoingRecovery) {
        Block tmp = new Block();
        tmp.set(block.getBlockId(), block.getNumBytes(), GenerationStamp.WILDCARD_STAMP);
        if (ongoingRecovery.get(tmp) != null) {
          String msg = "Block " + block + " is already being recovered, " +
                       " ignoring this request to recover it.";
          LOG.info(msg);
          throw new IOException(msg);
        }
        ongoingRecovery.put(block, block);
      }
      try {
        BlockRecoveryCoordinator brc = new BlockRecoveryCoordinator(LOG,
            getConf(), socketTimeout, this, namespaceManager.get(namespaceId),
            getDNRegistrationForNS(namespaceId));
        
        return brc.recoverBlock(namespaceId, block, keepLength, datanodeids, closeFile,
            deadline);
      } finally {
        synchronized (ongoingRecovery) {
          ongoingRecovery.remove(block);
        }
      }
    }
docs: Recover a block
  @param keepLength if true, will only recover replicas that have the same length
  as the block passed in. Otherwise, will calculate the minimum length of the
  replicas and truncate the rest to that length.