From 09d96f8d46a31e23fbe5a7b5d8114ae9aaba5056 Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Mon, 8 May 2017 08:48:40 +0200 Subject: [PATCH] Clean up the disk when cloning fails CloneCommand.call() has three stages: preparation, then the actual clone (init/fetch), and finally maybe checking out the working directory. Restructure such that if we fail or are cancelled during the actual clone (middle phase), we do clean up the disk again. This prevents leaving behind a partial clone in an inconsistent state: either we have a fully successfully built clone, or nothing at all. Bug: 516303 Change-Id: I9b18c60f8f99816d42a3deb7d4a33a9f22eeb709 Signed-off-by: Thomas Wolf --- .../org/eclipse/jgit/api/CloneCommand.java | 90 ++++++++++++++++--- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java index 2deff07f1d..63ca4289a0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2013 Chris Aniszczyk + * Copyright (C) 2011, 2017 Chris Aniszczyk * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -76,6 +76,8 @@ import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.TagOpt; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FileUtils; /** * Clone a repository into a new working directory @@ -109,6 +111,10 @@ public class CloneCommand extends TransportCommand { private Callback callback; + private boolean directoryExistsInitially; + + private boolean gitDirExistsInitially; + /** * Callback for status of clone operation. * @@ -167,26 +173,51 @@ public class CloneCommand extends TransportCommand { @Override public Git call() throws GitAPIException, InvalidRemoteException, org.eclipse.jgit.api.errors.TransportException { + URIish u = null; + try { + u = new URIish(uri); + verifyDirectories(u); + } catch (URISyntaxException e) { + throw new InvalidRemoteException( + MessageFormat.format(JGitText.get().invalidURL, uri)); + } Repository repository = null; + FetchResult fetchResult = null; try { - URIish u = new URIish(uri); - repository = init(u); - FetchResult result = fetch(repository, u); - if (!noCheckout) - checkout(repository, result); - return new Git(repository, true); + repository = init(); + fetchResult = fetch(repository, u); } catch (IOException ioe) { if (repository != null) { repository.close(); } + cleanup(); throw new JGitInternalException(ioe.getMessage(), ioe); } catch (URISyntaxException e) { if (repository != null) { repository.close(); } + cleanup(); throw new InvalidRemoteException(MessageFormat.format( JGitText.get().invalidRemote, remote)); + } catch (GitAPIException | RuntimeException e) { + if (repository != null) { + repository.close(); + } + cleanup(); + throw e; } + if (!noCheckout) { + try { + checkout(repository, fetchResult); + } catch (IOException ioe) { + repository.close(); + throw new JGitInternalException(ioe.getMessage(), ioe); + } catch (GitAPIException | RuntimeException e) { + repository.close(); + throw e; + } + } + return new Git(repository, true); } private static boolean isNonEmptyDirectory(File dir) { @@ -197,12 +228,12 @@ public class CloneCommand extends TransportCommand { return false; } - private Repository init(URIish u) throws GitAPIException { - InitCommand command = Git.init(); - command.setBare(bare); + private void verifyDirectories(URIish u) { if (directory == null && gitDir == null) { directory = new File(u.getHumanishName(), Constants.DOT_GIT); } + directoryExistsInitially = directory != null && directory.exists(); + gitDirExistsInitially = gitDir != null && gitDir.exists(); validateDirs(directory, gitDir, bare); if (isNonEmptyDirectory(directory)) { throw new JGitInternalException(MessageFormat.format( @@ -212,6 +243,11 @@ public class CloneCommand extends TransportCommand { throw new JGitInternalException(MessageFormat.format( JGitText.get().cloneNonEmptyDirectory, gitDir.getName())); } + } + + private Repository init() throws GitAPIException { + InitCommand command = Git.init(); + command.setBare(bare); if (directory != null) { command.setDirectory(directory); } @@ -602,4 +638,38 @@ public class CloneCommand extends TransportCommand { } } } + + private void cleanup() { + try { + if (directory != null) { + if (!directoryExistsInitially) { + FileUtils.delete(directory, FileUtils.RECURSIVE + | FileUtils.SKIP_MISSING | FileUtils.IGNORE_ERRORS); + } else { + deleteChildren(directory); + } + } + if (gitDir != null) { + if (!gitDirExistsInitially) { + FileUtils.delete(gitDir, FileUtils.RECURSIVE + | FileUtils.SKIP_MISSING | FileUtils.IGNORE_ERRORS); + } else { + deleteChildren(directory); + } + } + } catch (IOException e) { + // Ignore; this is a best-effort cleanup in error cases, and + // IOException should not be raised anyway + } + } + + private void deleteChildren(File file) throws IOException { + if (!FS.DETECTED.isDirectory(file)) { + return; + } + for (File child : file.listFiles()) { + FileUtils.delete(child, FileUtils.RECURSIVE | FileUtils.SKIP_MISSING + | FileUtils.IGNORE_ERRORS); + } + } } -- 2.39.5