docs: private byte[] copyFileWithCheckSum(HdfsFile source, File dest, CheckSumType checkSumType)
              throws IOException {
          byte[] checkSum = null;
          CheckSum bufferCheckSumGenerator = null;
          logger.debug("Starting copy of " + source + " to " + dest);
  
          // Check if its Gzip compressed
          boolean isCompressed = source.isCompressed();
          FilterInputStream input = null;
  
          OutputStream output = null;
          long startTimeMS = System.currentTimeMillis();
          int previousAttempt = 0;
  
          for (int attempt = 1; attempt <= fetcher.getMaxAttempts(); attempt++) {
              boolean success = false;
              long totalBytesRead = 0;
              boolean fsOpened = false;
              bufferCheckSumGenerator = null;
  
              stats.singleFileFetchStart(attempt != 1);
              try {
                  // Create a per file checksum generator
                  if (checkSumType != null) {
                      bufferCheckSumGenerator = CheckSum.getInstance(checkSumType);
                  }
  
                  logger.info("Starting attempt #" + attempt + "/" + fetcher.getMaxAttempts() +
                          " to fetch remote file: " + source + " to local destination: " + dest);
  
                  input = new ThrottledInputStream(fs.open(source.getPath()), fetcher.getThrottler(), stats);
  
                  if (isCompressed) {
                      // We are already bounded by the "hdfs.fetcher.buffer.size"
                      // specified in the Voldemort config, the default value of
                      // which is 64K. Using the same as the buffer size for
                      // GZIPInputStream as well.
                      input = new GZIPInputStream(input, this.bufferSize);
                  }
                  fsOpened = true;
  
                  output = new BufferedOutputStream(new FileOutputStream(dest));
  
                  int read;
  
                  while (true) {
                      if (status != null && status.hasException()) {
                          Exception ex = status.getException();
                          if (ex instanceof AsyncOperationStoppedException) {
                              // Then stop() has been called, so let's bubble up the exception
                              throw (AsyncOperationStoppedException) ex;
                          }
                      }
  
                      read = input.read(buffer);
                      if (read < 0) {
                          break;
                      } else {
                          output.write(buffer, 0, read);
                      }
  
                      // Update the per file checksum
                      if(bufferCheckSumGenerator != null) {
                          bufferCheckSumGenerator.update(buffer, 0, read);
                      }
  
                      stats.recordBytesWritten(read);
                      totalBytesRead += read;
                      boolean reportIntervalPassed = stats.getBytesTransferredSinceLastReport() > fetcher.getReportingIntervalBytes();
                      if (attempt != previousAttempt || reportIntervalPassed) {
                          previousAttempt = attempt;
                          NumberFormat format = NumberFormat.getNumberInstance();
                          format.setMaximumFractionDigits(2);
                          String message = stats.getTotalBytesTransferred() / (1024 * 1024) + " MB copied at "
                                  + format.format(stats.getBytesTransferredPerSecond() / (1024 * 1024)) + " MB/sec"
                                  + ", " + format.format(stats.getPercentCopied()) + " % complete"
                                  + ", attempt: #" + attempt + "/" + fetcher.getMaxAttempts()
                                  + ", current file: " + dest.getName();
                          if (this.status == null) {
                              // This is to accommodate tests and the old ReadOnlyStoreManagementServlet code path
                              // FIXME: Delete this when we get rid of the old code which does not use status
                              logger.info(message);
                          } else {
                              this.status.setStatus(message);
                              // status.toString() is more detailed than just the message. We print the whole
                              // thing so that server-side logs are very similar to client (BnP) -side logs.
                              logger.info(this.status.toString());
                          }
  
                          if (reportIntervalPassed) {
                              stats.reset();
                          }
                      }
                  }
                  if(bufferCheckSumGenerator != null) {
                      checkSum = bufferCheckSumGenerator.getCheckSum();
                  }
                  stats.reportFileDownloaded(dest,
                                             startTimeMS,
                                             source.getSize(),
                                             System.currentTimeMillis() - startTimeMS,
                                             attempt,
                                             totalBytesRead,
                                             checkSum);
                  logger.info("Completed copy of " + source + " to " + dest);
                  success = true;
              } catch (IOException e) {
                  if(!fsOpened) {
                      logger.error("Error while opening the file stream to " + source, e);
                  } else {
                      logger.error("Error while copying file " + source + " after " + totalBytesRead + " bytes.", e);
                  }
                  if(e.getCause() != null) {
                      logger.error("Cause of error ", e.getCause());
                  }
  
                  if(attempt < fetcher.getMaxAttempts()) {
                      logger.info("Will retry copying after " + fetcher.getRetryDelayMs() + " ms");
                      sleepForRetryDelayMs();
                  } else {
                      stats.reportFileError(dest, fetcher.getMaxAttempts(), startTimeMS, e);
                      logger.info("Fetcher giving up copy after " + fetcher.getMaxAttempts() + " attempts");
                      throw e;
                  }
              } finally {
                  stats.singleFileFetchEnd();
  
                  IOUtils.closeQuietly(output);
                  IOUtils.closeQuietly(input);
                  if(success) {
                      break;
                  }
              }
          }
  
          //second time checksum validation. Check if the local file is consistent with the buffer
          if (bufferCheckSumGenerator != null){
              CheckSum fileCheckSumGenerator = CheckSum.getInstance(checkSumType);
              BufferedInputStream in = new BufferedInputStream(new FileInputStream(dest));
              int read;
  
              try {
                  while ((read = in.read(buffer)) >= 0) {
                      fileCheckSumGenerator.update(buffer, 0, read);
                  }
  
                  if (ByteUtils.compare(fileCheckSumGenerator.getCheckSum(), checkSum) != 0)
                      throw new VoldemortException("Local file: " + dest.getAbsolutePath() +
                              " checksum (" + ByteUtils.toHexString(fileCheckSumGenerator.getCheckSum()) +
                              ") does not match with the checksum in the buffer (" +
                              ByteUtils.toHexString(fileCheckSumGenerator.getCheckSum()) + ")");
              }finally {
                  IOUtils.closeQuietly(in);
              }
          }
  
          return checkSum;
      }