From a502d96a860456ec5e8c96761db70f7cabb74751 Mon Sep 17 00:00:00 2001
From: Paul Martin <paul@paulsputer.com>
Date: Sat, 30 Apr 2016 04:19:14 -0400
Subject: [PATCH] Merge pull request #1073 from gitblit/1062-DocEditorUpdates

---
 src/main/java/com/gitblit/utils/JnaUtils.java |  204 ++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 171 insertions(+), 33 deletions(-)

diff --git a/src/main/java/com/gitblit/utils/JnaUtils.java b/src/main/java/com/gitblit/utils/JnaUtils.java
index b7d7209..2b80719 100644
--- a/src/main/java/com/gitblit/utils/JnaUtils.java
+++ b/src/main/java/com/gitblit/utils/JnaUtils.java
@@ -15,9 +15,6 @@
  */
 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;
@@ -28,37 +25,40 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
 /**
  * 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; // 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_ISUID =  0004000;
-	public static final int S_ISGID =  0002000;
-	public static final int S_ISVTX =  0001000;
+	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_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;
+	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);
@@ -66,6 +66,11 @@
 	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");
@@ -74,14 +79,62 @@
 
 	private interface UnixCLibrary extends Library {
 		public int chmod(String path, int mode);
+		public int getgid();
+		public int getegid();
 	}
 
 
-	public static int setFilemode(File path, int mode)
+	public static int getgid()
 	{
-		return setFilemode(path.getAbsolutePath(), mode);
+		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()) {
@@ -93,32 +146,110 @@
 
 
 
+	/**
+	 * 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 -1;
+			return null;
 		}
 
-		Pattern p = Pattern.compile("^(([-bcdlsp])([-r][-w][-xSs])([-r][-w][-xSs])([-r][-w][-xTt])) ");
+		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 -1;
+			return null;
 		}
 
 		// Parse mode string to mode bits
@@ -174,13 +305,20 @@
 			}
 		}
 
-		return mode;
+		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 -ldO " + path;
+		String cmd = "ls -ldn " + path;
 		Runtime rt = Runtime.getRuntime();
 		Process pr = null;
 		InputStreamReader ir = null;

--
Gitblit v1.9.1