Add validator for max path length On some operating systems there is a limit on the path length. Allow project owners to configure a maximum path length, so that the project stays cloneable on such operating systems. Change-Id: I0d7d72c9a71f6064f2f68e57bde2707d65bfa9e1 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/FileExtensionValidator.java b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/FileExtensionValidator.java index 6f350a2..ee90792 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/FileExtensionValidator.java +++ b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/FileExtensionValidator.java
@@ -21,29 +21,19 @@ import com.google.gerrit.server.events.CommitReceivedEvent; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.validators.CommitValidationException; -import com.google.gerrit.server.git.validators.CommitValidationListener; import com.google.gerrit.server.git.validators.CommitValidationMessage; import com.google.gerrit.server.project.NoSuchProjectException; import com.google.inject.Inject; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.diff.DiffEntry; -import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.treewalk.AbstractTreeIterator; -import org.eclipse.jgit.treewalk.CanonicalTreeParser; -import org.eclipse.jgit.treewalk.TreeWalk; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; -public class FileExtensionValidator implements CommitValidationListener { +public class FileExtensionValidator extends PathValidator { public static String KEY_BLOCKED_FILE_EXTENSION = "blockedFileExtension"; private final String pluginName; @@ -95,41 +85,4 @@ return Collections.emptyList(); } - - private List<String> getFiles(Repository repo, RevCommit c) throws IOException, GitAPIException { - List<String> files = new ArrayList<>(); - - if (c.getParentCount() > 0) { - Git git = new Git(repo); - List<DiffEntry> diffEntries = - git.diff().setOldTree(getTreeIterator(repo, c.getName() + "^")) - .setNewTree(getTreeIterator(repo, c.getName())).call(); - for (DiffEntry e : diffEntries) { - if (e.getNewPath() != null) { - files.add(e.getNewPath()); - } - } - } else { - TreeWalk tw = new TreeWalk(repo); - tw.addTree(c.getTree()); - tw.setRecursive(true); - while (tw.next()) { - files.add(tw.getPathString()); - } - } - - return files; - } - - private AbstractTreeIterator getTreeIterator(Repository repo, String name) - throws IOException { - CanonicalTreeParser p = new CanonicalTreeParser(); - ObjectReader or = repo.newObjectReader(); - try { - p.reset(or, new RevWalk(repo).parseTree(repo.resolve(name))); - return p; - } finally { - or.release(); - } - } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/MaxPathLengthValidator.java b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/MaxPathLengthValidator.java new file mode 100644 index 0000000..0f6c16a --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/MaxPathLengthValidator.java
@@ -0,0 +1,83 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.uploadvalidator; + +import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.server.config.PluginConfig; +import com.google.gerrit.server.config.PluginConfigFactory; +import com.google.gerrit.server.events.CommitReceivedEvent; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.validators.CommitValidationException; +import com.google.gerrit.server.git.validators.CommitValidationMessage; +import com.google.gerrit.server.project.NoSuchProjectException; +import com.google.inject.Inject; + +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Repository; + +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class MaxPathLengthValidator extends PathValidator { + public static String KEY_MAX_PATH_LENGTH = "maxPathLength"; + + private final String pluginName; + private final PluginConfigFactory cfgFactory; + private final GitRepositoryManager repoManager; + + @Inject + MaxPathLengthValidator(@PluginName String pluginName, + PluginConfigFactory cfgFactory, GitRepositoryManager repoManager) { + this.pluginName = pluginName; + this.cfgFactory = cfgFactory; + this.repoManager = repoManager; + } + + @Override + public List<CommitValidationMessage> onCommitReceived( + CommitReceivedEvent receiveEvent) throws CommitValidationException { + try { + PluginConfig cfg = + cfgFactory.getFromProjectConfig( + receiveEvent.project.getNameKey(), pluginName); + int maxPathLength = cfg.getInt(KEY_MAX_PATH_LENGTH, 0); + if (maxPathLength > 0) { + Repository repo = repoManager.openRepository(receiveEvent.project.getNameKey()); + try { + List<CommitValidationMessage> messages = new LinkedList<>(); + List<String> files = getFiles(repo, receiveEvent.commit); + for (String file : files) { + if (file.length() > maxPathLength) { + messages.add(new CommitValidationMessage("path too long: " + file, true)); + } + } + if (!messages.isEmpty()) { + throw new CommitValidationException( + "contains files with too long paths (max path length: " + + maxPathLength + ")", messages); + } + } finally { + repo.close(); + } + } + } catch (NoSuchProjectException | IOException | GitAPIException e) { + throw new CommitValidationException("failed to check for max file path length", e); + } + + return Collections.emptyList(); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/Module.java b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/Module.java index 44bf5c6..05b4366 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/Module.java +++ b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/Module.java
@@ -16,6 +16,7 @@ import static com.googlesource.gerrit.plugins.uploadvalidator.FileExtensionValidator.KEY_BLOCKED_FILE_EXTENSION; import static com.googlesource.gerrit.plugins.uploadvalidator.FooterValidator.KEY_REQUIRED_FOOTER; +import static com.googlesource.gerrit.plugins.uploadvalidator.MaxPathLengthValidator.KEY_MAX_PATH_LENGTH; import com.google.gerrit.extensions.annotations.Exports; import com.google.gerrit.extensions.registration.DynamicSet; @@ -45,5 +46,15 @@ ProjectConfigEntry.Type.ARRAY, null, false, "Required footers. Pushes of commits that miss any" + " of the footers will be rejected.")); + + DynamicSet.bind(binder(), CommitValidationListener.class) + .to(MaxPathLengthValidator.class); + bind(ProjectConfigEntry.class) + .annotatedWith(Exports.named(KEY_MAX_PATH_LENGTH)) + .toInstance( + new ProjectConfigEntry("Max Path Length", 0, false, + "Maximum path length. Pushes of commits that " + + "contain files with longer paths will be rejected. " + + "'0' means no limit.")); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/PathValidator.java b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/PathValidator.java new file mode 100644 index 0000000..04d9b69 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/PathValidator.java
@@ -0,0 +1,73 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.uploadvalidator; + +import com.google.gerrit.server.git.validators.CommitValidationListener; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.AbstractTreeIterator; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.eclipse.jgit.treewalk.TreeWalk; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public abstract class PathValidator implements CommitValidationListener { + + protected List<String> getFiles(Repository repo, RevCommit c) + throws IOException, GitAPIException { + List<String> files = new ArrayList<>(); + + if (c.getParentCount() > 0) { + Git git = new Git(repo); + List<DiffEntry> diffEntries = + git.diff().setOldTree(getTreeIterator(repo, c.getName() + "^")) + .setNewTree(getTreeIterator(repo, c.getName())).call(); + for (DiffEntry e : diffEntries) { + if (e.getNewPath() != null) { + files.add(e.getNewPath()); + } + } + } else { + TreeWalk tw = new TreeWalk(repo); + tw.addTree(c.getTree()); + tw.setRecursive(true); + while (tw.next()) { + files.add(tw.getPathString()); + } + } + + return files; + } + + private AbstractTreeIterator getTreeIterator(Repository repo, String name) + throws IOException { + CanonicalTreeParser p = new CanonicalTreeParser(); + ObjectReader or = repo.newObjectReader(); + try { + p.reset(or, new RevWalk(repo).parseTree(repo.resolve(name))); + return p; + } finally { + or.release(); + } + } +}
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md index e9d1359..f855775 100644 --- a/src/main/resources/Documentation/about.md +++ b/src/main/resources/Documentation/about.md
@@ -1,6 +1,5 @@ This plugin allows to configure upload validations per project. -Project owners can configure blocked file extensions and required -footers. Pushes of commits that contain files with blocked extensions -or that miss a required footer in the commit message are rejected by -Gerrit. +Project owners can configure blocked file extensions, required footers +and a maximum allowed path length. Pushes of commits that violate these +settings are rejected by Gerrit.
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md index 72386b3..3381675 100644 --- a/src/main/resources/Documentation/config.md +++ b/src/main/resources/Documentation/config.md
@@ -14,6 +14,7 @@ blockedFileExtension = war blockedFileExtension = exe requiredFooter = Bug + maxPathLength = 200 ``` plugin.@PLUGIN@.blockedFileExtension @@ -25,3 +26,11 @@ : Footer that is required. Required footers are *not* inherited by child projects. + +plugin.@PLUGIN@.maxPathLength +: Maximum allowed path length. '0' means no limit. + + Defaults to '0'. + + The maximum allowed path length is *not* inherited by child + projects.