summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdwin Kempin <edwin.kempin@sap.com>2012-04-26 09:50:11 -0700
committerEdwin Kempin <edwin.kempin@sap.com>2012-04-27 09:42:06 +0200
commitcd94b4b7a381527f9bd216ffc7b7b3180075d16f (patch)
treee39f36172934bc062480c0db511b1a489e927d88
parent19192caf52d61e8933d7fb6b7fc707ee1c656a8c (diff)
Fix case check for project name so that symlinks work again
The check for the case of the project name when opening a git repository that was introduced by change 30015 (commit a7e928daaf7ba4176b5044797f90b21f420ab2f7, "Verify the case of the project name before opening git repository") prevents the usage of symbolic links under the git base path. There are some scenarios where symbolic links for git repositories make sense. E.g. if a project was renamed it may make sense to create a symbolic link with the old name that points to the new location so that old references to this project can still be resolved. The case check for the project name is now implemented in such a way that it compares the input project name against a cached list of all existing project names. With this new implementation symbolic links for git repositories should work again. Bug: issue 1353 Change-Id: Iad2be143fd7e2057700cc447f3426df732f9b8cb Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java7
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java61
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java17
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java48
4 files changed, 67 insertions, 66 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
index cce318a129..e0c7425511 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
@@ -70,11 +70,12 @@ public interface GitRepositoryManager {
* @param name the repository name, relative to the base directory.
* @return the cached Repository instance. Caller must call {@code close()}
* when done to decrement the resource handle.
- * @throws RepositoryNotFoundException the name does not denote an existing
- * repository, or the name cannot be read as a repository.
+ * @throws RepositoryCaseMismatchException the name collides with an existing
+ * repository name, but only in case of a character within the name.
+ * @throws RepositoryNotFoundException the name is invalid.
*/
public abstract Repository createRepository(Project.NameKey name)
- throws RepositoryNotFoundException;
+ throws RepositoryCaseMismatchException, RepositoryNotFoundException;
/** @return set of all known projects, sorted by natural NameKey order. */
public abstract SortedSet<Project.NameKey> list();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
index be444f875d..d9f95a1f00 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -46,6 +46,8 @@ import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
/** Manages Git repositories stored on the local filesystem. */
@Singleton
@@ -91,6 +93,8 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
}
private final File basePath;
+ private final Lock namesUpdateLock;
+ private volatile SortedSet<Project.NameKey> names;
@Inject
LocalDiskRepositoryManager(final SitePaths site,
@@ -99,6 +103,8 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
if (basePath == null) {
throw new IllegalStateException("gerrit.basePath must be configured");
}
+ namesUpdateLock = new ReentrantLock(true /* fair */);
+ names = list();
}
/** @return base directory under which all projects are stored. */
@@ -115,13 +121,10 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
if (isUnreasonableName(name)) {
throw new RepositoryNotFoundException("Invalid name: " + name);
}
-
- final FileKey loc = FileKey.lenient(gitDirOf(name), FS.DETECTED);
-
- if (!getProjectName(loc.getFile()).equals(name)) {
+ if (!names.contains(name)) {
throw new RepositoryNotFoundException(gitDirOf(name));
}
-
+ final FileKey loc = FileKey.lenient(gitDirOf(name), FS.DETECTED);
try {
return RepositoryCache.open(loc);
} catch (IOException e1) {
@@ -133,7 +136,7 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
}
public Repository createRepository(final Project.NameKey name)
- throws RepositoryNotFoundException {
+ throws RepositoryNotFoundException, RepositoryCaseMismatchException {
if (isUnreasonableName(name)) {
throw new RepositoryNotFoundException("Invalid name: " + name);
}
@@ -145,10 +148,8 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
//
loc = FileKey.exact(dir, FS.DETECTED);
- final Project.NameKey nameOfExistingProject =
- getProjectName(loc.getFile());
- if (!nameOfExistingProject.equals(name)) {
- throw new RepositoryCaseMismatchException(name, nameOfExistingProject);
+ if (!names.contains(name)) {
+ throw new RepositoryCaseMismatchException(name);
}
} else {
// It doesn't exist under any of the standard permutations
@@ -170,6 +171,8 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
config.save();
+ onCreateProject(name);
+
return db;
} catch (IOException e1) {
final RepositoryNotFoundException e2;
@@ -179,6 +182,17 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
}
}
+ private void onCreateProject(final Project.NameKey newProjectName) {
+ namesUpdateLock.lock();
+ try {
+ SortedSet<Project.NameKey> n = new TreeSet<Project.NameKey>(names);
+ n.add(newProjectName);
+ names = Collections.unmodifiableSortedSet(n);
+ } finally {
+ namesUpdateLock.unlock();
+ }
+ }
+
public String getProjectDescription(final Project.NameKey name)
throws RepositoryNotFoundException, IOException {
final Repository e = openRepository(name);
@@ -268,9 +282,18 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
@Override
public SortedSet<Project.NameKey> list() {
- SortedSet<Project.NameKey> names = new TreeSet<Project.NameKey>();
- scanProjects(basePath, "", names);
- return Collections.unmodifiableSortedSet(names);
+ // The results of this method are cached by ProjectCacheImpl. Control only
+ // enters here if the cache was flushed by the administrator to force
+ // scanning the filesystem. Don't rely on the cached names collection.
+ namesUpdateLock.lock();
+ try {
+ SortedSet<Project.NameKey> n = new TreeSet<Project.NameKey>();
+ scanProjects(basePath, "", n);
+ names = Collections.unmodifiableSortedSet(n);
+ return n;
+ } finally {
+ namesUpdateLock.unlock();
+ }
}
private void scanProjects(final File dir, final String prefix,
@@ -312,16 +335,4 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
return new Project.NameKey(projectName);
}
-
- private Project.NameKey getProjectName(final File gitDir) {
- String relativeGitPath =
- getBasePath().toURI().relativize(gitDir.toURI()).getPath();
- if (!relativeGitPath.endsWith("/")) {
- relativeGitPath = relativeGitPath + "/";
- }
- final String prefix =
- relativeGitPath.substring(0, relativeGitPath.length() - 1
- - gitDir.getName().length());
- return getProjectName(prefix, gitDir.getName());
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java
index 3c8a1a58f3..3c90798b43 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.git;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.Project.NameKey;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -31,21 +30,11 @@ public class RepositoryCaseMismatchException extends
private static final long serialVersionUID = 1L;
- private final NameKey nameOfExistingProject;
-
/**
* @param projectName name of the project that cannot be created
- * @param nameOfExistingProject name of the project that already exists and
- * occupies the name for the git repository in the file system
*/
- public RepositoryCaseMismatchException(final Project.NameKey projectName,
- final Project.NameKey nameOfExistingProject) {
- super("Name occupied in other case: " + projectName.get() + "; project "
- + nameOfExistingProject.get() + " exists");
- this.nameOfExistingProject = nameOfExistingProject;
- }
-
- public NameKey getNameOfExistingProject() {
- return nameOfExistingProject;
+ public RepositoryCaseMismatchException(final Project.NameKey projectName) {
+ super("Name occupied in other case. Project " + projectName.get()
+ + " cannot be created.");
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
index 474c5c4ce4..c60a8d471c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
@@ -93,8 +93,8 @@ public class CreateProject {
public void createProject() throws ProjectCreationFailedException {
validateParameters();
+ final Project.NameKey nameKey = createProjectArgs.getProject();
try {
- final Project.NameKey nameKey = createProjectArgs.getProject();
final String head =
createProjectArgs.permissionsOnly ? GitRepositoryManager.REF_CONFIG
: createProjectArgs.branch;
@@ -115,12 +115,30 @@ public class CreateProject {
} finally {
repo.close();
}
- } catch (IllegalStateException e) {
- handleRepositoryExistsException(createProjectArgs.getProject(), e);
- } catch (RepositoryCaseMismatchException ee) {
- handleRepositoryExistsException(ee.getNameOfExistingProject(), ee);
+ } catch (RepositoryCaseMismatchException e) {
+ throw new ProjectCreationFailedException("Cannot create " + nameKey.get()
+ + " because the name is already occupied by another project."
+ + " The other project has the same name, only spelled in a"
+ + " different case.", e);
+ } catch (RepositoryNotFoundException badName) {
+ throw new ProjectCreationFailedException("Cannot create " + nameKey, badName);
+ } catch (IllegalStateException err) {
+ try {
+ final Repository repo = repoManager.openRepository(nameKey);
+ try {
+ if (repo.getObjectDatabase().exists()) {
+ throw new ProjectCreationFailedException("project \"" + nameKey + "\" exists");
+ }
+ } finally {
+ repo.close();
+ }
+ } catch (RepositoryNotFoundException doesNotExist) {
+ final String msg = "Cannot create " + nameKey;
+ log.error(msg, err);
+ throw new ProjectCreationFailedException(msg, err);
+ }
} catch (Exception e) {
- final String msg = "Cannot create " + createProjectArgs.getProject();
+ final String msg = "Cannot create " + nameKey;
log.error(msg, e);
throw new ProjectCreationFailedException(msg, e);
}
@@ -245,22 +263,4 @@ public class CreateProject {
oi.release();
}
}
-
- private void handleRepositoryExistsException(final Project.NameKey nameKey,
- Exception e) throws ProjectCreationFailedException {
- try {
- Repository repo = repoManager.openRepository(nameKey);
- try {
- if (repo.getObjectDatabase().exists()) {
- throw new ProjectCreationFailedException("Project \"" + nameKey
- + "\" exists", e);
- }
- } finally {
- repo.close();
- }
- } catch (RepositoryNotFoundException er) {
- throw new ProjectCreationFailedException("Cannot create \"" + nameKey
- + "\"", er);
- }
- }
}