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.