public boolean sync() throws IOException {
    if (!preSyncCheck()) {
      return false;
    }

    if (!getAllDiffs()) {
      return false;
    }

    List<Path> sourcePaths = context.getSourcePaths();
    final Path sourceDir = sourcePaths.get(0);
    final Path targetDir = context.getTargetPath();
    final FileSystem tfs = targetDir.getFileSystem(conf);

    Path tmpDir = null;
    try {
      tmpDir = createTargetTmpDir(tfs, targetDir);
      DiffInfo[] renameAndDeleteDiffs =
          getRenameAndDeleteDiffsForSync(targetDir);
      if (renameAndDeleteDiffs.length > 0) {
        // do the real sync work: deletion and rename
        syncDiff(renameAndDeleteDiffs, tfs, tmpDir);
      }
      return true;
    } catch (Exception e) {
      DistCp.LOG.warn("Failed to use snapshot diff for distcp", e);
      return false;
    } finally {
      deleteTargetTmpDir(tfs, tmpDir);
      // TODO: since we have tmp directory, we can support "undo" with failures
      // set the source path using the snapshot path
      context.setSourcePaths(Arrays.asList(getSnapshotPath(sourceDir,
          context.getToSnapshot())));
    }
  }
@Test
  public void testFallback() throws Exception {
    // the source/target dir are not snapshottable dir
    Assert.assertFalse(sync());
    // make sure the source path has been updated to the snapshot path
    final Path spath = new Path(source,
        HdfsConstants.DOT_SNAPSHOT_DIR + Path.SEPARATOR + "s2");
    Assert.assertEquals(spath, context.getSourcePaths().get(0));

    // reset source path in options
    context.setSourcePaths(Collections.singletonList(source));
    // the source/target does not have the given snapshots
    dfs.allowSnapshot(source);
    dfs.allowSnapshot(target);
    Assert.assertFalse(sync());
    Assert.assertEquals(spath, context.getSourcePaths().get(0));

    // reset source path in options
    context.setSourcePaths(Collections.singletonList(source));
    dfs.createSnapshot(source, "s1");
    dfs.createSnapshot(source, "s2");
    dfs.createSnapshot(target, "s1");
    Assert.assertTrue(sync());

    // reset source paths in options
    context.setSourcePaths(Collections.singletonList(source));
    // changes have been made in target
    final Path subTarget = new Path(target, "sub");
    dfs.mkdirs(subTarget);
    Assert.assertFalse(sync());
    // make sure the source path has been updated to the snapshot path
    Assert.assertEquals(spath, context.getSourcePaths().get(0));

    // reset source paths in options
    context.setSourcePaths(Collections.singletonList(source));
    dfs.delete(subTarget, true);
    Assert.assertTrue(sync());
  }