.classpath | ●●●●● patch | view | raw | blame | history | |
build.moxie | ●●●●● patch | view | raw | blame | history | |
gitblit.iml | ●●●●● patch | view | raw | blame | history | |
src/main/distrib/data/gitblit.properties | ●●●●● patch | view | raw | blame | history | |
src/main/java/com/gitblit/GitBlit.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/com/gitblit/utils/JGitUtils.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/com/gitblit/utils/JnaUtils.java | ●●●●● patch | view | raw | blame | history | |
src/test/java/com/gitblit/tests/JGitUtilsTest.java | ●●●●● patch | view | raw | blame | history | |
src/test/java/com/gitblit/tests/JnaUtilsTest.java | ●●●●● patch | view | raw | blame | history |
.classpath
@@ -37,6 +37,7 @@ <classpathentry kind="lib" path="ext/jcalendar-1.3.2.jar" /> <classpathentry kind="lib" path="ext/commons-compress-1.4.1.jar" sourcepath="ext/src/commons-compress-1.4.1.jar" /> <classpathentry kind="lib" path="ext/xz-1.0.jar" sourcepath="ext/src/xz-1.0.jar" /> <classpathentry kind="lib" path="ext/commons-io-2.2.jar" sourcepath="ext/src/commons-io-2.2.jar" /> <classpathentry kind="lib" path="ext/force-partner-api-24.0.0.jar" sourcepath="ext/src/force-partner-api-24.0.0.jar" /> <classpathentry kind="lib" path="ext/force-wsc-24.0.0.jar" sourcepath="ext/src/force-wsc-24.0.0.jar" /> <classpathentry kind="lib" path="ext/js-1.7R2.jar" sourcepath="ext/src/js-1.7R2.jar" /> @@ -60,7 +61,6 @@ <classpathentry kind="lib" path="ext/httpcore-4.2.1.jar" sourcepath="ext/src/httpcore-4.2.1.jar" /> <classpathentry kind="lib" path="ext/commons-logging-1.1.1.jar" sourcepath="ext/src/commons-logging-1.1.1.jar" /> <classpathentry kind="lib" path="ext/commons-exec-1.1.jar" sourcepath="ext/src/commons-exec-1.1.jar" /> <classpathentry kind="lib" path="ext/commons-io-2.2.jar" sourcepath="ext/src/commons-io-2.2.jar" /> <classpathentry kind="output" path="bin/classes" /> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER" /> </classpath> build.moxie
@@ -148,6 +148,7 @@ - compile 'org.apache.ivy:ivy:2.2.0' :war - compile 'com.toedter:jcalendar:1.3.2' :authority - compile 'org.apache.commons:commons-compress:1.4.1' :war - compile 'commons-io:commons-io:2.2' :war - compile 'com.force.api:force-partner-api:24.0.0' :war - compile 'org.freemarker:freemarker:2.3.19' :war - compile 'com.github.dblock.waffle:waffle-jna:1.5' :war gitblit.iml
@@ -381,6 +381,17 @@ </library> </orderEntry> <orderEntry type="module-library"> <library name="commons-io-2.2.jar"> <CLASSES> <root url="jar://$MODULE_DIR$/ext/commons-io-2.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> <root url="jar://$MODULE_DIR$/ext/src/commons-io-2.2.jar!/" /> </SOURCES> </library> </orderEntry> <orderEntry type="module-library"> <library name="force-partner-api-24.0.0.jar"> <CLASSES> <root url="jar://$MODULE_DIR$/ext/force-partner-api-24.0.0.jar!/" /> @@ -630,17 +641,6 @@ <JAVADOC /> <SOURCES> <root url="jar://$MODULE_DIR$/ext/src/commons-exec-1.1.jar!/" /> </SOURCES> </library> </orderEntry> <orderEntry type="module-library" scope="TEST"> <library name="commons-io-2.2.jar"> <CLASSES> <root url="jar://$MODULE_DIR$/ext/commons-io-2.2.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES> <root url="jar://$MODULE_DIR$/ext/src/commons-io-2.2.jar!/" /> </SOURCES> </library> </orderEntry> src/main/distrib/data/gitblit.properties
@@ -191,6 +191,15 @@ # SINCE 1.3.0 git.defaultIncrementalPushTagPrefix = r # In an Unix environment where mixed access methods exist for shared repositories, # the repository should be created with 'git init --shared' to make sure that # it can be accessed e.g. via ssh (user git) and http (user www-data). # Valid values are the values available for the '--shared' option. The the manual # page for 'git init' for more information on shared repositories. # # SINCE 1.3.2 git.createRepositoriesShared = false # Enable JGit-based garbage collection. (!!EXPERIMENTAL!!) # # USE AT YOUR OWN RISK! src/main/java/com/gitblit/GitBlit.java
@@ -2427,7 +2427,8 @@ } // create repository logger.info("create repository " + repository.name); r = JGitUtils.createRepository(repositoriesFolder, repository.name); String shared = getString(Keys.git.createRepositoriesShared, "FALSE"); r = JGitUtils.createRepository(repositoriesFolder, repository.name, shared); } else { // rename repository if (!repositoryName.equalsIgnoreCase(repository.name)) { @@ -2530,6 +2531,12 @@ r.close(); } // Adjust permissions in case we updated the config files JGitUtils.adjustSharedPerm(new File(r.getDirectory().getAbsolutePath(), "config"), getString(Keys.git.createRepositoriesShared, "FALSE")); JGitUtils.adjustSharedPerm(new File(r.getDirectory().getAbsolutePath(), "HEAD"), getString(Keys.git.createRepositoriesShared, "FALSE")); // update repository cache removeFromCachedRepositoryList(repositoryName); // model will actually be replaced on next load because config is stale src/main/java/com/gitblit/utils/JGitUtils.java
@@ -32,6 +32,7 @@ import java.util.Map.Entry; import java.util.regex.Pattern; import org.apache.commons.io.filefilter.TrueFileFilter; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.FetchCommand; import org.eclipse.jgit.api.Git; @@ -58,6 +59,7 @@ import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache.FileKey; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.lib.TreeFormatter; import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; @@ -259,13 +261,187 @@ * @return Repository */ public static Repository createRepository(File repositoriesFolder, String name) { return createRepository(repositoriesFolder, name, "FALSE"); } /** * Creates a bare, shared repository. * * @param repositoriesFolder * @param name * @param shared * the setting for the --shared option of "git init". * @return Repository */ public static Repository createRepository(File repositoriesFolder, String name, String shared) { try { Repository repo = null; try { Git git = Git.init().setDirectory(new File(repositoriesFolder, name)).setBare(true).call(); return git.getRepository(); repo = git.getRepository(); } catch (GitAPIException e) { throw new RuntimeException(e); } GitConfigSharedRepository sharedRepository = new GitConfigSharedRepository(shared); if (sharedRepository.isShared()) { StoredConfig config = repo.getConfig(); config.setString("core", null, "sharedRepository", sharedRepository.getValue()); config.setBoolean("receive", null, "denyNonFastforwards", true); config.save(); if (! JnaUtils.isWindows()) { Iterator<File> iter = org.apache.commons.io.FileUtils.iterateFilesAndDirs(repo.getDirectory(), TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); // Adjust permissions on file/directory while (iter.hasNext()) { adjustSharedPerm(iter.next(), sharedRepository); } } } return repo; } catch (IOException e) { throw new RuntimeException(e); } } private enum GitConfigSharedRepositoryValue { UMASK("0", 0), FALSE("0", 0), OFF("0", 0), NO("0", 0), GROUP("1", 0660), TRUE("1", 0660), ON("1", 0660), YES("1", 0660), ALL("2", 0664), WORLD("2", 0664), EVERYBODY("2", 0664), Oxxx(null, -1); private String configValue; private int permValue; private GitConfigSharedRepositoryValue(String config, int perm) { configValue = config; permValue = perm; }; public String getConfigValue() { return configValue; }; public int getPerm() { return permValue; }; } private static class GitConfigSharedRepository { private int intValue; private GitConfigSharedRepositoryValue enumValue; GitConfigSharedRepository(String s) { if ( s == null || s.trim().isEmpty() ) { enumValue = GitConfigSharedRepositoryValue.GROUP; } else { try { // Try one of the string values enumValue = GitConfigSharedRepositoryValue.valueOf(s.trim().toUpperCase()); } catch (IllegalArgumentException iae) { try { // Try if this is an octal number int i = Integer.parseInt(s, 8); if ( (i & 0600) != 0600 ) { String msg = String.format("Problem with core.sharedRepository filemode value (0%03o).\nThe owner of files must always have read and write permissions.", i); throw new IllegalArgumentException(msg); } intValue = i & 0666; enumValue = GitConfigSharedRepositoryValue.Oxxx; } catch (NumberFormatException nfe) { throw new IllegalArgumentException("Bad configuration value for 'shared': '" + s + "'"); } } } } String getValue() { if ( enumValue == GitConfigSharedRepositoryValue.Oxxx ) { if (intValue == 0) return "0"; return String.format("0%o", intValue); } return enumValue.getConfigValue(); } int getPerm() { if ( enumValue == GitConfigSharedRepositoryValue.Oxxx ) return intValue; return enumValue.getPerm(); } boolean isCustom() { return enumValue == GitConfigSharedRepositoryValue.Oxxx; } boolean isShared() { return (enumValue.getPerm() > 0) || enumValue == GitConfigSharedRepositoryValue.Oxxx; } } /** * Adjust file permissions of a file/directory for shared repositories * * @param path * File that should get its permissions changed. * @param configShared * Configuration string value for the shared mode. * @return Upon successful completion, a value of 0 is returned. Otherwise, a value of -1 is returned. */ public static int adjustSharedPerm(File path, String configShared) { return adjustSharedPerm(path, new GitConfigSharedRepository(configShared)); } /** * Adjust file permissions of a file/directory for shared repositories * * @param path * File that should get its permissions changed. * @param configShared * Configuration setting for the shared mode. * @return Upon successful completion, a value of 0 is returned. Otherwise, a value of -1 is returned. */ public static int adjustSharedPerm(File path, GitConfigSharedRepository configShared) { if (! configShared.isShared()) return 0; if (! path.exists()) return -1; int perm = configShared.getPerm(); JnaUtils.Filestat stat = JnaUtils.getFilestat(path); if (stat == null) return -1; int mode = stat.mode; if (mode < 0) return -1; // Now, here is the kicker: Under Linux, chmod'ing a sgid file whose guid is different from the process' // effective guid will reset the sgid flag of the file. Since there is no way to get the sgid flag back in // that case, we decide to rather not touch is and getting the right permissions will have to be achieved // in a different way, e.g. by using an appropriate umask for the Gitblit process. if (System.getProperty("os.name").toLowerCase().startsWith("linux")) { if ( ((mode & (JnaUtils.S_ISGID | JnaUtils.S_ISUID)) != 0) && stat.gid != JnaUtils.getegid() ) { LOGGER.debug("Not adjusting permissions to prevent clearing suid/sgid bits for '" + path + "'" ); return 0; } } // If the owner has no write access, delete it from group and other, too. if ((mode & JnaUtils.S_IWUSR) == 0) perm &= ~0222; // If the owner has execute access, set it for all blocks that have read access. if ((mode & JnaUtils.S_IXUSR) == JnaUtils.S_IXUSR) perm |= (perm & 0444) >> 2; if (configShared.isCustom()) { // Use the custom value for access permissions. mode = (mode & ~0777) | perm; } else { // Just add necessary bits to existing permissions. mode |= perm; } if (path.isDirectory()) { mode |= (mode & 0444) >> 2; mode |= JnaUtils.S_ISGID; } return JnaUtils.setFilemode(path, mode); } /** * Returns a list of repository names in the specified folder. src/main/java/com/gitblit/utils/JnaUtils.java
New file @@ -0,0 +1,364 @@ /* * Copyright 2013 gitblit.com. * * 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.gitblit.utils; import com.sun.jna.Library; import com.sun.jna.Native; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Collection of static methods to access native OS library functionality. * * @author Florian Zschocke */ public class JnaUtils { public static final int S_ISUID = 0004000; // set user id on execution public static final int S_ISGID = 0002000; // set group id on execution public static final int S_ISVTX = 0001000; // sticky bit, save swapped text even after use public static final int S_IRWXU = 0000700; // RWX mask for owner public static final int S_IRUSR = 0000400; // read permission for owner public static final int S_IWUSR = 0000200; // write permission for owner public static final int S_IXUSR = 0000100; // execute/search permission for owner public static final int S_IRWXG = 0000070; // RWX mask for group public static final int S_IRGRP = 0000040; // read permission for group public static final int S_IWGRP = 0000020; // write permission for group public static final int S_IXGRP = 0000010; // execute/search permission for group public static final int S_IRWXO = 0000007; // RWX mask for other public static final int S_IROTH = 0000004; // read permission for other public static final int S_IWOTH = 0000002; // write permission for other public static final int S_IXOTH = 0000001; // execute/search permission for other public static final int S_IFMT = 0170000; // type of file mask public static final int S_IFIFO = 0010000; // named pipe (fifo) public static final int S_IFCHR = 0020000; // character special device public static final int S_IFDIR = 0040000; // directory public static final int S_IFBLK = 0060000; // block special device public static final int S_IFREG = 0100000; // regular file public static final int S_IFLNK = 0120000; // symbolic link public static final int S_IFSOCK = 0140000; // socket private static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class); private static UnixCLibrary unixlibc = null; /** * Utility method to check if the JVM is running on a Windows OS. * * @return true, if the system property 'os.name' starts with 'Windows'. */ public static boolean isWindows() { return System.getProperty("os.name").toLowerCase().startsWith("windows"); } private interface UnixCLibrary extends Library { public int chmod(String path, int mode); public int getgid(); public int getegid(); } public static int getgid() { if (isWindows()) { throw new UnsupportedOperationException("The method JnaUtils.getgid is not supported under Windows."); } return getUnixCLibrary().getgid(); } public static int getegid() { if (isWindows()) { throw new UnsupportedOperationException("The method JnaUtils.getegid is not supported under Windows."); } return getUnixCLibrary().getegid(); } /** * Set the permission bits of a file. * * The permission bits are set to the provided mode. This method is only * implemented for OSes of the Unix family and makes use of the 'chmod' * function of the native C library. See 'man 2 chmod' for more information. * * @param path * File/directory to set the permission bits for. * @param mode * A mode created from or'd permission bit masks S_I* * @return Upon successful completion, a value of 0 returned. Otherwise, a value of -1 is returned. */ public static int setFilemode(File file, int mode) { return setFilemode(file.getAbsolutePath(), mode); } /** * Set the permission bits of a file. * * The permission bits are set to the provided mode. This method is only * implemented for OSes of the Unix family and makes use of the 'chmod' * function of the native C library. See 'man 2 chmod' for more information. * * @param path * Path to a file/directory to set the permission bits for. * @param mode * A mode created from or'd permission bit masks S_I* * @return Upon successful completion, a value of 0 returned. Otherwise, a value of -1 is returned. */ public static int setFilemode(String path, int mode) { if (isWindows()) { throw new UnsupportedOperationException("The method JnaUtils.getFilemode is not supported under Windows."); } return getUnixCLibrary().chmod(path, mode); } /** * Get the file mode bits of a file. * * This method is only implemented for OSes of the Unix family. It returns the file mode * information as available in the st_mode member of the resulting struct stat when calling * 'lstat' on a file. * * @param path * File/directory to get the file mode from. * @return Upon successful completion, the file mode bits are returned. Otherwise, a value of -1 is returned. */ public static int getFilemode(File path) { return getFilemode(path.getAbsolutePath()); } /** * Get the file mode bits of a file. * * This method is only implemented for OSes of the Unix family. It returns the file mode * information as available in the st_mode member of the resulting struct stat when calling * 'lstat' on a file. * * @param path * Path to a file/directory to get the file mode from. * @return Upon successful completion, the file mode bits are returned. Otherwise, a value of -1 is returned. */ public static int getFilemode(String path) { if (isWindows()) { throw new UnsupportedOperationException("The method JnaUtils.getFilemode is not supported under Windows."); } Filestat stat = getFilestat(path); if ( stat == null ) return -1; return stat.mode; } /** * Status information of a file. */ public static class Filestat { public int mode; // file mode, permissions, type public int uid; // user Id of owner public int gid; // group Id of owner Filestat(int mode, int uid, int gid) { this.mode = mode; this.uid = uid; this.gid = gid; } } /** * Get Unix file status information for a file. * * This method is only implemented for OSes of the Unix family. It returns file status * information for a file. Currently this is the file mode, the user id and group id of the owner. * * @param path * File/directory to get the file status from. * @return Upon successful completion, a Filestat object containing the file information is returned. * Otherwise, null is returned. */ public static Filestat getFilestat(File path) { return getFilestat(path.getAbsolutePath()); } /** * Get Unix file status information for a file. * * This method is only implemented for OSes of the Unix family. It returns file status * information for a file. Currently this is the file mode, the user id and group id of the owner. * * @param path * Path to a file/directory to get the file status from. * @return Upon successful completion, a Filestat object containing the file information is returned. * Otherwise, null is returned. */ public static Filestat getFilestat(String path) { if (isWindows()) { throw new UnsupportedOperationException("The method JnaUtils.getFilestat is not supported under Windows."); } int mode = 0; // Use a Runtime, because implementing stat() via JNA is just too much trouble. // This could be done with the 'stat' command, too. But that may have a shell specific implementation, so we use 'ls' instead. String lsLine = runProcessLs(path); if (lsLine == null) { LOGGER.debug("Could not get file information for path " + path); return null; } Pattern p = Pattern.compile("^(([-bcdlspCDMnP?])([-r][-w][-xSs])([-r][-w][-xSs])([-r][-w][-xTt]))[@+.]? +[0-9]+ +([0-9]+) +([0-9]+) "); Matcher m = p.matcher(lsLine); if ( !m.lookingAt() ) { LOGGER.debug("Could not parse valid file mode information for path " + path); return null; } // Parse mode string to mode bits String group = m.group(2); switch (group.charAt(0)) { case 'p' : mode |= 0010000; break; case 'c': mode |= 0020000; break; case 'd': mode |= 0040000; break; case 'b': mode |= 0060000; break; case '-': mode |= 0100000; break; case 'l': mode |= 0120000; break; case 's': mode |= 0140000; break; } for ( int i = 0; i < 3; i++) { group = m.group(3 + i); switch (group.charAt(0)) { case 'r': mode |= (0400 >> i*3); break; case '-': break; } switch (group.charAt(1)) { case 'w': mode |= (0200 >> i*3); break; case '-': break; } switch (group.charAt(2)) { case 'x': mode |= (0100 >> i*3); break; case 'S': mode |= (04000 >> i); break; case 's': mode |= (0100 >> i*3); mode |= (04000 >> i); break; case 'T': mode |= 01000; break; case 't': mode |= (0100 >> i*3); mode |= 01000; break; case '-': break; } } return new Filestat(mode, Integer.parseInt(m.group(6)), Integer.parseInt(m.group(7))); } /** * Run the unix command 'ls -ldn' on a single file and return the resulting output line. * * @param path * Path to a single file or directory. * @return The first line of output from the 'ls' command. Null, if an error occurred and no line could be read. */ private static String runProcessLs(String path) { String cmd = "ls -ldn " + path; Runtime rt = Runtime.getRuntime(); Process pr = null; InputStreamReader ir = null; BufferedReader br = null; String output = null; try { pr = rt.exec(cmd); ir = new InputStreamReader(pr.getInputStream()); br = new BufferedReader(ir); output = br.readLine(); while (br.readLine() != null) ; // Swallow remaining output } catch (IOException e) { LOGGER.debug("Exception while running unix command '" + cmd + "': " + e); } finally { if (pr != null) try { pr.waitFor(); } catch (Exception ignored) {} if (br != null) try { br.close(); } catch (Exception ignored) {} if (ir != null) try { ir.close(); } catch (Exception ignored) {} if (pr != null) try { pr.getOutputStream().close(); } catch (Exception ignored) {} if (pr != null) try { pr.getInputStream().close(); } catch (Exception ignored) {} if (pr != null) try { pr.getErrorStream().close(); } catch (Exception ignored) {} } return output; } private static UnixCLibrary getUnixCLibrary() { if (unixlibc == null) { unixlibc = (UnixCLibrary) Native.loadLibrary("c", UnixCLibrary.class); if (unixlibc == null) throw new RuntimeException("Could not initialize native C library."); } return unixlibc; } } src/test/java/com/gitblit/tests/JGitUtilsTest.java
@@ -52,6 +52,7 @@ import com.gitblit.models.RefModel; import com.gitblit.utils.CompressionUtils; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.JnaUtils; import com.gitblit.utils.StringUtils; public class JGitUtilsTest { @@ -149,6 +150,139 @@ } @Test public void testCreateRepositoryShared() throws Exception { String[] repositories = { "NewSharedTestRepository.git" }; for (String repositoryName : repositories) { Repository repository = JGitUtils.createRepository(GitBlitSuite.REPOSITORIES, repositoryName, "group"); File folder = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, repositoryName), FS.DETECTED); assertNotNull(repository); assertFalse(JGitUtils.hasCommits(repository)); assertNull(JGitUtils.getFirstCommit(repository, null)); assertEquals("1", repository.getConfig().getString("core", null, "sharedRepository")); assertTrue(folder.exists()); if (! JnaUtils.isWindows()) { int mode = JnaUtils.getFilemode(folder); assertEquals(JnaUtils.S_ISGID, mode & JnaUtils.S_ISGID); assertEquals(JnaUtils.S_IRWXG, mode & JnaUtils.S_IRWXG); mode = JnaUtils.getFilemode(folder.getAbsolutePath() + "/HEAD"); assertEquals(JnaUtils.S_IRGRP | JnaUtils.S_IWGRP, mode & JnaUtils.S_IRWXG); mode = JnaUtils.getFilemode(folder.getAbsolutePath() + "/config"); assertEquals(JnaUtils.S_IRGRP | JnaUtils.S_IWGRP, mode & JnaUtils.S_IRWXG); } repository.close(); RepositoryCache.close(repository); FileUtils.delete(repository.getDirectory(), FileUtils.RECURSIVE); } } @Test public void testCreateRepositorySharedCustom() throws Exception { String[] repositories = { "NewSharedTestRepository.git" }; for (String repositoryName : repositories) { Repository repository = JGitUtils.createRepository(GitBlitSuite.REPOSITORIES, repositoryName, "660"); File folder = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, repositoryName), FS.DETECTED); assertNotNull(repository); assertFalse(JGitUtils.hasCommits(repository)); assertNull(JGitUtils.getFirstCommit(repository, null)); assertEquals("0660", repository.getConfig().getString("core", null, "sharedRepository")); assertTrue(folder.exists()); if (! JnaUtils.isWindows()) { int mode = JnaUtils.getFilemode(folder); assertEquals(JnaUtils.S_ISGID, mode & JnaUtils.S_ISGID); assertEquals(JnaUtils.S_IRWXG, mode & JnaUtils.S_IRWXG); assertEquals(0, mode & JnaUtils.S_IRWXO); mode = JnaUtils.getFilemode(folder.getAbsolutePath() + "/HEAD"); assertEquals(JnaUtils.S_IRGRP | JnaUtils.S_IWGRP, mode & JnaUtils.S_IRWXG); assertEquals(0, mode & JnaUtils.S_IRWXO); mode = JnaUtils.getFilemode(folder.getAbsolutePath() + "/config"); assertEquals(JnaUtils.S_IRGRP | JnaUtils.S_IWGRP, mode & JnaUtils.S_IRWXG); assertEquals(0, mode & JnaUtils.S_IRWXO); } repository.close(); RepositoryCache.close(repository); FileUtils.delete(repository.getDirectory(), FileUtils.RECURSIVE); } } @Test public void testCreateRepositorySharedSgidParent() throws Exception { if (! JnaUtils.isWindows()) { String repositoryAll = "NewTestRepositoryAll.git"; String repositoryUmask = "NewTestRepositoryUmask.git"; String sgidParent = "sgid"; File parent = new File(GitBlitSuite.REPOSITORIES, sgidParent); File folder = null; boolean parentExisted = parent.exists(); try { if (!parentExisted) { assertTrue("Could not create SGID parent folder.", parent.mkdir()); } int mode = JnaUtils.getFilemode(parent); assertTrue(mode > 0); assertEquals(0, JnaUtils.setFilemode(parent, mode | JnaUtils.S_ISGID | JnaUtils.S_IWGRP)); Repository repository = JGitUtils.createRepository(parent, repositoryAll, "all"); folder = FileKey.resolve(new File(parent, repositoryAll), FS.DETECTED); assertNotNull(repository); assertEquals("2", repository.getConfig().getString("core", null, "sharedRepository")); assertTrue(folder.exists()); mode = JnaUtils.getFilemode(folder); assertEquals(JnaUtils.S_ISGID, mode & JnaUtils.S_ISGID); mode = JnaUtils.getFilemode(folder.getAbsolutePath() + "/HEAD"); assertEquals(JnaUtils.S_IRGRP | JnaUtils.S_IWGRP, mode & JnaUtils.S_IRWXG); assertEquals(JnaUtils.S_IROTH, mode & JnaUtils.S_IRWXO); mode = JnaUtils.getFilemode(folder.getAbsolutePath() + "/config"); assertEquals(JnaUtils.S_IRGRP | JnaUtils.S_IWGRP, mode & JnaUtils.S_IRWXG); assertEquals(JnaUtils.S_IROTH, mode & JnaUtils.S_IRWXO); repository.close(); RepositoryCache.close(repository); repository = JGitUtils.createRepository(parent, repositoryUmask, "umask"); folder = FileKey.resolve(new File(parent, repositoryUmask), FS.DETECTED); assertNotNull(repository); assertEquals(null, repository.getConfig().getString("core", null, "sharedRepository")); assertTrue(folder.exists()); mode = JnaUtils.getFilemode(folder); assertEquals(JnaUtils.S_ISGID, mode & JnaUtils.S_ISGID); repository.close(); RepositoryCache.close(repository); } finally { FileUtils.delete(new File(parent, repositoryAll), FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS); FileUtils.delete(new File(parent, repositoryUmask), FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS); if (!parentExisted) { FileUtils.delete(parent, FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS); } } } } @Test public void testRefs() throws Exception { Repository repository = GitBlitSuite.getJGitRepository(); Map<ObjectId, List<RefModel>> map = JGitUtils.getAllRefs(repository); src/test/java/com/gitblit/tests/JnaUtilsTest.java
New file @@ -0,0 +1,152 @@ /* * Copyright 2011 gitblit.com. * * 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.gitblit.tests; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.JnaUtils; import java.io.File; import java.io.IOException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import org.apache.commons.io.FileUtils; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache; import org.eclipse.jgit.lib.RepositoryCache.FileKey; import org.eclipse.jgit.util.FS; import org.junit.Test; /** * * @author Florian Zschocke */ public class JnaUtilsTest { @Test public void testGetgid() { if (JnaUtils.isWindows()) { try { JnaUtils.getFilemode(GitBlitSuite.REPOSITORIES); } catch(UnsupportedOperationException e) {} } else { int gid = JnaUtils.getgid(); assertTrue(gid >= 0); int egid = JnaUtils.getegid(); assertTrue(egid >= 0); assertTrue("Really? You're running unit tests as root?!", gid > 0); System.out.println("gid: " + gid + " egid: " + egid); } } @Test public void testGetFilemode() throws IOException { if (JnaUtils.isWindows()) { try { JnaUtils.getFilemode(GitBlitSuite.REPOSITORIES); } catch(UnsupportedOperationException e) {} } else { String repositoryName = "NewJnaTestRepository.git"; Repository repository = JGitUtils.createRepository(GitBlitSuite.REPOSITORIES, repositoryName); File folder = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, repositoryName), FS.DETECTED); assertTrue(folder.exists()); int mode = JnaUtils.getFilemode(folder); assertTrue(mode > 0); assertEquals(JnaUtils.S_IFDIR, (mode & JnaUtils.S_IFMT)); // directory assertEquals(JnaUtils.S_IRUSR | JnaUtils.S_IWUSR | JnaUtils.S_IXUSR, (mode & JnaUtils.S_IRWXU)); // owner full access mode = JnaUtils.getFilemode(folder.getAbsolutePath() + "/config"); assertTrue(mode > 0); assertEquals(JnaUtils.S_IFREG, (mode & JnaUtils.S_IFMT)); // directory assertEquals(JnaUtils.S_IRUSR | JnaUtils.S_IWUSR, (mode & JnaUtils.S_IRWXU)); // owner full access repository.close(); RepositoryCache.close(repository); FileUtils.deleteDirectory(repository.getDirectory()); } } @Test public void testSetFilemode() throws IOException { if (JnaUtils.isWindows()) { try { JnaUtils.getFilemode(GitBlitSuite.REPOSITORIES); } catch(UnsupportedOperationException e) {} } else { String repositoryName = "NewJnaTestRepository.git"; Repository repository = JGitUtils.createRepository(GitBlitSuite.REPOSITORIES, repositoryName); File folder = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, repositoryName), FS.DETECTED); assertTrue(folder.exists()); File path = new File(folder, "refs"); int mode = JnaUtils.getFilemode(path); assertTrue(mode > 0); assertEquals(JnaUtils.S_IFDIR, (mode & JnaUtils.S_IFMT)); // directory assertEquals(JnaUtils.S_IRUSR | JnaUtils.S_IWUSR | JnaUtils.S_IXUSR, (mode & JnaUtils.S_IRWXU)); // owner full access mode |= JnaUtils.S_ISGID; mode |= JnaUtils.S_IRWXG; int ret = JnaUtils.setFilemode(path, mode); assertEquals(0, ret); mode = JnaUtils.getFilemode(path); assertTrue(mode > 0); assertEquals(JnaUtils.S_ISGID, (mode & JnaUtils.S_ISGID)); // set-gid-bit set assertEquals(JnaUtils.S_IRGRP | JnaUtils.S_IWGRP | JnaUtils.S_IXGRP, (mode & JnaUtils.S_IRWXG)); // group full access path = new File(folder, "config"); mode = JnaUtils.getFilemode(path.getAbsolutePath()); assertTrue(mode > 0); assertEquals(JnaUtils.S_IFREG, (mode & JnaUtils.S_IFMT)); // directory assertEquals(JnaUtils.S_IRUSR | JnaUtils.S_IWUSR, (mode & JnaUtils.S_IRWXU)); // owner full access mode |= (JnaUtils.S_IRGRP | JnaUtils.S_IWGRP); ret = JnaUtils.setFilemode(path.getAbsolutePath(), mode); assertEquals(0, ret); mode = JnaUtils.getFilemode(path.getAbsolutePath()); assertTrue(mode > 0); assertEquals(JnaUtils.S_IRGRP | JnaUtils.S_IWGRP, (mode & JnaUtils.S_IRWXG)); // group full access repository.close(); RepositoryCache.close(repository); FileUtils.deleteDirectory(repository.getDirectory()); } } @Test public void testGetFilestat() { if (JnaUtils.isWindows()) { try { JnaUtils.getFilemode(GitBlitSuite.REPOSITORIES); } catch(UnsupportedOperationException e) {} } else { JnaUtils.Filestat stat = JnaUtils.getFilestat(GitBlitSuite.REPOSITORIES); assertNotNull(stat); assertTrue(stat.mode > 0); assertTrue(stat.uid > 0); assertTrue(stat.gid > 0); } } }