]> source.dussan.org Git - gitblit.git/commitdiff
Add proper implementation of methods in JnaUtils.
authorFlorian Zschocke <florian.zschocke@cycos.com>
Sat, 17 Aug 2013 16:37:41 +0000 (18:37 +0200)
committerFlorian Zschocke <florian.zschocke@cycos.com>
Mon, 26 Aug 2013 10:39:57 +0000 (12:39 +0200)
Implement the methods getFilemode and setFilemode
in JnaUtils. Not using the libc names as we don't
necessarily use JNA and because it is not necessarily
a one to one mapping.

src/main/java/com/gitblit/utils/JGitUtils.java
src/main/java/com/gitblit/utils/JnaUtils.java [new file with mode: 0644]
src/test/java/com/gitblit/tests/JGitUtilsTest.java
src/test/java/com/gitblit/tests/JnaUtilsTest.java [new file with mode: 0644]

index 345375a96479fd9a967700ac8ca86a02c0d39cfc..03b54ee91c2d3e8479d5cee41332a3b4265b6074 100644 (file)
@@ -89,8 +89,6 @@ import com.gitblit.models.PathModel;
 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
@@ -270,103 +268,99 @@ public class JGitUtils {
                }\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
diff --git a/src/main/java/com/gitblit/utils/JnaUtils.java b/src/main/java/com/gitblit/utils/JnaUtils.java
new file mode 100644 (file)
index 0000000..b7d7209
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * 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;
+       }
+
+}
index 375dbd5a109293ba5e1132c9023ceb58b20ec37f..4affca2d9a1926c4f215d4103cf1c9bab4df1ffd 100644 (file)
@@ -148,6 +148,27 @@ public class JGitUtilsTest {
                }\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
diff --git a/src/test/java/com/gitblit/tests/JnaUtilsTest.java b/src/test/java/com/gitblit/tests/JnaUtilsTest.java
new file mode 100644 (file)
index 0000000..574686b
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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());
+       }
+}