public boolean takeFullSnapshot(
      String snapshotDirPath,
      String tempSnapshotId,
      String finalSnapshotId,
      boolean flushBeforeSnapshot)
      throws DirectoryNotLegalException, IOException {
    File snapshotDir = new File(snapshotDirPath);
    if (snapshotDir.exists()
        && snapshotDir.listFiles() != null
        && Objects.requireNonNull(snapshotDir.listFiles()).length > 0) {
      // the directory should be empty or not exists
      throw new DirectoryNotLegalException(
          String.format("%s already exists and is not empty", snapshotDirPath));
    }

    if (!snapshotDir.exists() && !snapshotDir.mkdirs()) {
      throw new IOException(String.format("Failed to create directory %s", snapshotDir));
    }

    File snapshotLog = new File(snapshotDir, SnapshotLogger.SNAPSHOT_LOG_NAME);
    try {
      snapshotLogger = new SnapshotLogger(snapshotLog);
      boolean success;
      snapshotLogger.logSnapshotId(finalSnapshotId);

      try {
        readLockTheFile();
        if (flushBeforeSnapshot) {
          try {
            dataRegion.writeLock("snapshotTaker");
            dataRegion.syncCloseAllWorkingTsFileProcessors();
          } finally {
            dataRegion.writeUnlock();
          }
        }
        success = createSnapshot(seqFiles, tempSnapshotId);
        success = success && createSnapshot(unseqFiles, tempSnapshotId);
      } finally {
        readUnlockTheFile();
      }

      if (!success) {
        LOGGER.warn(
            "Failed to take snapshot for {}-{}, clean up",
            dataRegion.getDatabaseName(),
            dataRegion.getDataRegionId());
        cleanUpWhenFail(finalSnapshotId);
      } else {
        snapshotLogger.logEnd();
        LOGGER.info(
            "Successfully take snapshot for {}-{}, snapshot directory is {}",
            dataRegion.getDatabaseName(),
            dataRegion.getDataRegionId(),
            snapshotDir.getParentFile().getAbsolutePath() + File.separator + finalSnapshotId);
      }

      return success;
    } catch (Exception e) {
      LOGGER.error(
          "Exception occurs when taking snapshot for {}-{}",
          dataRegion.getDatabaseName(),
          dataRegion.getDataRegionId(),
          e);
      return false;
    } finally {
      try {
        snapshotLogger.close();
      } catch (Exception e) {
        LOGGER.error("Failed to close snapshot logger", e);
      }
    }
  }
SnapshotTaker takes data snapshot for a DataRegion in one time. It does so by creating hard link
for files or copying them. SnapshotTaker supports two different ways of snapshot: Full Snapshot
and Incremental Snapshot. The former takes a snapshot for all files in an empty directory, and
the latter takes a snapshot based on the snapshot that took before.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotTaker.java