import com.gitblit.models.PathModel.PathChangeModel;\r
import com.gitblit.models.RefModel;\r
import com.gitblit.models.SubmoduleModel;\r
-import com.sun.jna.Library;\r
-import com.sun.jna.Native;\r
\r
/**\r
* Collection of static methods for retrieving information from a repository.\r
}\r
}\r
\r
- /**\r
- * Creates a bare, shared repository.\r
- * \r
- * @param repositoriesFolder\r
- * @param name\r
- * @param shared\r
- * the setting for the --shared option of "git init".\r
- * @return Repository\r
- */\r
- public static Repository createRepository(File repositoriesFolder, String name, String shared) {\r
- try {\r
- Repository repo = createRepository(repositoriesFolder, name);\r
-\r
- GitConfigSharedRepository sharedRepository = new GitConfigSharedRepository(shared);\r
- if (sharedRepository.isShared()) {\r
- StoredConfig config = repo.getConfig();\r
- config.setString("core", null, "sharedRepository", sharedRepository.getValue());\r
- config.setBoolean("receive", null, "denyNonFastforwards", true);\r
- config.save();\r
-\r
- if (! System.getProperty("os.name").toLowerCase().startsWith("windows")) {\r
- final CLibrary libc = (CLibrary) Native.loadLibrary("c", CLibrary.class);\r
-\r
- //libc.chmod("/path/to/file", 0755);\r
- }\r
- }\r
-\r
- return repo;\r
- } catch (IOException e) {\r
- throw new RuntimeException(e);\r
- }\r
- }\r
- interface CLibrary extends Library {\r
- public int chmod(String path, int mode);\r
- }\r
- private enum GitConfigSharedRepositoryValue {\r
- UMASK("0", 0), FALSE("0", 0), OFF("0", 0), NO("0", 0),\r
- GROUP("1", 0660), TRUE("1", 0660), ON("1", 0660), YES("1", 0660),\r
- ALL("2", 0664), WORLD("2", 0664), EVERYBODY("2", 0664),\r
- Oxxx(null, -1);\r
-\r
- private String configValue;\r
- private int permValue;\r
- private GitConfigSharedRepositoryValue(String config, int perm) { configValue = config; permValue = perm; };\r
-\r
- public String getConfigValue() { return configValue; };\r
- public int getPerm() { return permValue; };\r
-\r
- }\r
- private static class GitConfigSharedRepository\r
- {\r
- private int intValue;\r
- GitConfigSharedRepositoryValue enumValue;\r
-\r
- GitConfigSharedRepository(String s)\r
- {\r
- if ( s == null || s.trim().isEmpty() ) {\r
- enumValue = GitConfigSharedRepositoryValue.GROUP;\r
- }\r
- else {\r
- try {\r
- // Try one of the string values\r
- enumValue = GitConfigSharedRepositoryValue.valueOf(s.trim().toUpperCase());\r
- } catch (IllegalArgumentException iae) {\r
- try {\r
- // Try if this is an octal number\r
- int i = Integer.parseInt(s, 8);\r
- if ( (i & 0600) != 0600 ) {\r
- String msg = String.format("Problem with core.sharedRepository filemode value (0%03o).\nThe owner of files must always have read and write permissions.", i);\r
- throw new IllegalArgumentException(msg);\r
- }\r
- intValue = i & 0666;\r
- enumValue = GitConfigSharedRepositoryValue.Oxxx;\r
- } catch (NumberFormatException nfe) {\r
- throw new IllegalArgumentException("Bad configuration value for 'shared': '" + s + "'");\r
- }\r
- }\r
- }\r
- }\r
- \r
- String getValue()\r
- {\r
- if ( enumValue == GitConfigSharedRepositoryValue.Oxxx ) return Integer.toOctalString(intValue);\r
- return enumValue.getConfigValue();\r
- }\r
-\r
- int getPerm()\r
- {\r
- if ( enumValue == GitConfigSharedRepositoryValue.Oxxx ) return intValue;\r
- return enumValue.getPerm();\r
- }\r
-\r
- boolean isShared()\r
- {\r
- return (enumValue.getPerm() > 0) || enumValue == GitConfigSharedRepositoryValue.Oxxx;\r
- }\r
- }\r
+ /**\r
+ * Creates a bare, shared repository.\r
+ * \r
+ * @param repositoriesFolder\r
+ * @param name\r
+ * @param shared\r
+ * the setting for the --shared option of "git init".\r
+ * @return Repository\r
+ */\r
+ public static Repository createRepository(File repositoriesFolder, String name, String shared) {\r
+ try {\r
+ Repository repo = createRepository(repositoriesFolder, name);\r
+\r
+ GitConfigSharedRepository sharedRepository = new GitConfigSharedRepository(shared);\r
+ if (sharedRepository.isShared()) {\r
+ StoredConfig config = repo.getConfig();\r
+ config.setString("core", null, "sharedRepository", sharedRepository.getValue());\r
+ config.setBoolean("receive", null, "denyNonFastforwards", true);\r
+ config.save();\r
+\r
+ if (! JnaUtils.isWindows()) {\r
+\r
+ //libc.chmod("/path/to/file", 0755);\r
+ }\r
+ }\r
+\r
+ return repo;\r
+ } catch (IOException e) {\r
+ throw new RuntimeException(e);\r
+ }\r
+ }\r
+ private enum GitConfigSharedRepositoryValue {\r
+ UMASK("0", 0), FALSE("0", 0), OFF("0", 0), NO("0", 0),\r
+ GROUP("1", 0660), TRUE("1", 0660), ON("1", 0660), YES("1", 0660),\r
+ ALL("2", 0664), WORLD("2", 0664), EVERYBODY("2", 0664),\r
+ Oxxx(null, -1);\r
+\r
+ private String configValue;\r
+ private int permValue;\r
+ private GitConfigSharedRepositoryValue(String config, int perm) { configValue = config; permValue = perm; };\r
+\r
+ public String getConfigValue() { return configValue; };\r
+ public int getPerm() { return permValue; };\r
+\r
+ }\r
+ private static class GitConfigSharedRepository\r
+ {\r
+ private int intValue;\r
+ GitConfigSharedRepositoryValue enumValue;\r
+\r
+ GitConfigSharedRepository(String s)\r
+ {\r
+ if ( s == null || s.trim().isEmpty() ) {\r
+ enumValue = GitConfigSharedRepositoryValue.GROUP;\r
+ }\r
+ else {\r
+ try {\r
+ // Try one of the string values\r
+ enumValue = GitConfigSharedRepositoryValue.valueOf(s.trim().toUpperCase());\r
+ } catch (IllegalArgumentException iae) {\r
+ try {\r
+ // Try if this is an octal number\r
+ int i = Integer.parseInt(s, 8);\r
+ if ( (i & 0600) != 0600 ) {\r
+ String msg = String.format("Problem with core.sharedRepository filemode value (0%03o).\nThe owner of files must always have read and write permissions.", i);\r
+ throw new IllegalArgumentException(msg);\r
+ }\r
+ intValue = i & 0666;\r
+ enumValue = GitConfigSharedRepositoryValue.Oxxx;\r
+ } catch (NumberFormatException nfe) {\r
+ throw new IllegalArgumentException("Bad configuration value for 'shared': '" + s + "'");\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ String getValue()\r
+ {\r
+ if ( enumValue == GitConfigSharedRepositoryValue.Oxxx ) return Integer.toOctalString(intValue);\r
+ return enumValue.getConfigValue();\r
+ }\r
+\r
+ int getPerm()\r
+ {\r
+ if ( enumValue == GitConfigSharedRepositoryValue.Oxxx ) return intValue;\r
+ return enumValue.getPerm();\r
+ }\r
+\r
+ boolean isShared()\r
+ {\r
+ return (enumValue.getPerm() > 0) || enumValue == GitConfigSharedRepositoryValue.Oxxx;\r
+ }\r
+ }\r
\r
\r
/**\r
--- /dev/null
+/*
+ * 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_IFMT = 0170000;
+ public static final int S_IFIFO = 0010000;
+ public static final int S_IFCHR = 0020000;
+ public static final int S_IFDIR = 0040000;
+ public static final int S_IFBLK = 0060000;
+ public static final int S_IFREG = 0100000;
+ public static final int S_IFLNK = 0120000;
+ public static final int S_IFSOCK = 0140000;
+
+ public static final int S_ISUID = 0004000;
+ public static final int S_ISGID = 0002000;
+ public static final int S_ISVTX = 0001000;
+
+ public static final int S_IRWXU = 0000700;
+ public static final int S_IRUSR = 0000400;
+ public static final int S_IWUSR = 0000200;
+ public static final int S_IXUSR = 0000100;
+ public static final int S_IRWXG = 0000070;
+ public static final int S_IRGRP = 0000040;
+ public static final int S_IWGRP = 0000020;
+ public static final int S_IXGRP = 0000010;
+ public static final int S_IRWXO = 0000007;
+ public static final int S_IROTH = 0000004;
+ public static final int S_IWOTH = 0000002;
+ public static final int S_IXOTH = 0000001;
+
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
+
+ private static UnixCLibrary unixlibc = null;
+
+
+ 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 static int setFilemode(File path, int mode)
+ {
+ return setFilemode(path.getAbsolutePath(), mode);
+ }
+
+ 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);
+ }
+
+
+
+ public static int getFilemode(File path)
+ {
+ return getFilemode(path.getAbsolutePath());
+ }
+
+ public static int getFilemode(String path)
+ {
+ if (isWindows()) {
+ throw new UnsupportedOperationException("The method JnaUtils.getFilemode is not supported under Windows.");
+ }
+
+
+ int mode = 0;
+
+ // Use a Runtime, because implementing stat() via JNA is just too much trouble.
+ String lsLine = runProcessLs(path);
+ if (lsLine == null) {
+ LOGGER.debug("Could not get file information for path " + path);
+ return -1;
+ }
+
+ Pattern p = Pattern.compile("^(([-bcdlsp])([-r][-w][-xSs])([-r][-w][-xSs])([-r][-w][-xTt])) ");
+ Matcher m = p.matcher(lsLine);
+ if ( !m.lookingAt() ) {
+ LOGGER.debug("Could not parse valid file mode information for path " + path);
+ return -1;
+ }
+
+ // 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 mode;
+ }
+
+
+ private static String runProcessLs(String path)
+ {
+ String cmd = "ls -ldO " + 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;
+ }
+
+}
}\r
}\r
\r
+ @Test\r
+ public void testCreateRepositoryShared() throws Exception {\r
+ String[] repositories = { "NewTestRepository.git", "NewTestRepository" };\r
+ for (String repositoryName : repositories) {\r
+ Repository repository = JGitUtils.createRepository(GitBlitSuite.REPOSITORIES,\r
+ repositoryName, "group");\r
+ File folder = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, repositoryName),\r
+ FS.DETECTED);\r
+ assertNotNull(repository);\r
+ assertFalse(JGitUtils.hasCommits(repository));\r
+ assertNull(JGitUtils.getFirstCommit(repository, null));\r
+ assertEquals(folder.lastModified(), JGitUtils.getFirstChange(repository, null)\r
+ .getTime());\r
+ assertEquals(folder.lastModified(), JGitUtils.getLastChange(repository).when.getTime());\r
+ assertNull(JGitUtils.getCommit(repository, null));\r
+ repository.close();\r
+ RepositoryCache.close(repository);\r
+// FileUtils.delete(repository.getDirectory(), FileUtils.RECURSIVE);\r
+ }\r
+ }\r
+\r
@Test\r
public void testRefs() throws Exception {\r
Repository repository = GitBlitSuite.getJGitRepository();\r
--- /dev/null
+/*
+ * 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.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 testGetFilemode() throws IOException {
+ 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 {
+ 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());
+ }
+}