hadoop-common-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sra...@apache.org
Subject svn commit: r1344527 [1/2] - in /hadoop/common/branches/branch-1-win: ./ src/core/org/apache/hadoop/fs/ src/core/org/apache/hadoop/security/ src/core/org/apache/hadoop/util/ src/mapred/org/apache/hadoop/mapred/ src/mapred/org/apache/hadoop/mapreduce/ s...
Date Thu, 31 May 2012 01:52:16 GMT
Author: sradia
Date: Thu May 31 01:52:15 2012
New Revision: 1344527

URL: http://svn.apache.org/viewvc?rev=1344527&view=rev
Log:
HADOOP-8235 Support file permissions and ownership on Windows for RawLocalFileSystem  (Chuan Liu via sanjay)

Added:
    hadoop/common/branches/branch-1-win/src/winutils/
    hadoop/common/branches/branch-1-win/src/winutils/chmod.c
    hadoop/common/branches/branch-1-win/src/winutils/chown.c
    hadoop/common/branches/branch-1-win/src/winutils/common.c
    hadoop/common/branches/branch-1-win/src/winutils/common.h
    hadoop/common/branches/branch-1-win/src/winutils/groups.c
    hadoop/common/branches/branch-1-win/src/winutils/hardlink.c
    hadoop/common/branches/branch-1-win/src/winutils/ls.c
    hadoop/common/branches/branch-1-win/src/winutils/main.c
    hadoop/common/branches/branch-1-win/src/winutils/winutils.sln
    hadoop/common/branches/branch-1-win/src/winutils/winutils.vcxproj
Modified:
    hadoop/common/branches/branch-1-win/CHANGES.txt
    hadoop/common/branches/branch-1-win/build.xml
    hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java
    hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/RawLocalFileSystem.java
    hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java
    hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java
    hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapred/JobTracker.java
    hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapreduce/JobSubmissionFiles.java
    hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/TestFileStatus.java
    hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/server/datanode/TestDiskError.java
    hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/security/TestUserGroupInformation.java
    hadoop/common/branches/branch-1-win/src/test/testshell/ExternalMapReduce.java

Modified: hadoop/common/branches/branch-1-win/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/CHANGES.txt?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/CHANGES.txt (original)
+++ hadoop/common/branches/branch-1-win/CHANGES.txt Thu May 31 01:52:15 2012
@@ -18,6 +18,8 @@ branch-hadoop-1-win - unreleased
     HADOOP-8412 TestModTime, TestDelegationToken and TestAuthenticationToken fail
                 intermittently on Windows  (Ivan Mitic via Sanjay Radia)
 
+    HADOOP-8235 Support file permissions and ownership on Windows for RawLocalFileSystem  (Chuan Liu via sanjay)
+
 Release 1.1.0 - unreleased
 
   NEW FEATURES

Modified: hadoop/common/branches/branch-1-win/build.xml
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/build.xml?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/build.xml (original)
+++ hadoop/common/branches/branch-1-win/build.xml Thu May 31 01:52:15 2012
@@ -63,6 +63,7 @@
   <property name="c++.libhdfs.src" value="${c++.src}/libhdfs"/>
   <property name="librecordio.src" value="${c++.src}/librecordio"/>
   <property name="tools.src" value="${basedir}/src/tools"/>
+  <property name="winutils.src.dir" value="${basedir}/src/winutils"/>
 
   <property name="package.prefix" value="/usr"/>
   <property name="package.conf.dir" value="/etc/hadoop"/>
@@ -87,6 +88,7 @@
             value="${nonspace.os}-${os.arch}-${sun.arch.data.model}"/>
   <property name="jvm.arch" 
             value="${sun.arch.data.model}"/>
+  <property name="build.winutils" value="${build.dir}/winutils/${os.arch}"/>
   <property name="build.native" value="${build.dir}/native/${build.platform}"/>
   <property name="build.c++" value="${build.dir}/c++-build/${build.platform}"/>
   <property name="build.c++.utils" value="${build.c++}/utils"/>
@@ -169,6 +171,7 @@
   <property name="grep.cmd" value="grep"/>
   <property name="patch.cmd" value="patch"/>
   <property name="make.cmd" value="make"/>
+  <property name="msbuild.cmd" value="msbuild"/>
 
   <property name="jsvc.build.dir" value="${build.dir}/jsvc.${os.arch}" />
   <property name="jsvc.install.dir" value="${dist.dir}/libexec" /> 
@@ -592,6 +595,22 @@
     </copy>
   </target>
 
+  <target name="compile-winutils">
+    <antcall target="compile-ms-winutils">
+      <param name="windows" value="true"/>
+    </antcall>
+  </target>
+
+  <target name="compile-ms-winutils" if="windows">
+    <mkdir dir="${build.winutils}"/>
+
+    <exec dir="${build.winutils}" executable="${msbuild.cmd}" failonerror="true">
+      <arg line="${winutils.src.dir}/winutils.vcxproj /p:Configuration=Release;OutDir=${build.winutils}/"/>
+    </exec>
+
+    <copy file="${build.winutils}/winutils.exe" todir="${basedir}/bin"/>
+  </target>
+
   <target name="compile-native">
     <antcall target="compile-core-native">
       <param name="compile.native" value="true"/>
@@ -665,7 +684,7 @@
 
   <target name="compile-core"
           depends="clover,compile-core-classes,compile-mapred-classes,
-  	compile-hdfs-classes,compile-core-native,compile-c++" 
+  	compile-hdfs-classes,compile-core-native,compile-c++,compile-ms-winutils" 
   	description="Compile core only">
   </target>
 
@@ -1028,6 +1047,7 @@
              timeout="${test.timeout}"
              errorProperty="tests.failed"
              failureProperty="tests.failed">
+        <env key="HADOOP_HOME" value="${basedir}"/>
         <sysproperty key="test.build.data" value="${test.build.data}" />
         <sysproperty key="test.tools.input.dir"
                      value="${test.tools.input.dir}" />
@@ -1619,7 +1639,7 @@
       </fileset>
     </copy>
 
-        <exec dir="${dist.dir}" executable="sh" failonerror="true">
+    <exec dir="${dist.dir}" executable="sh" failonerror="true">
           <env key="BASE_NATIVE_LIB_DIR" value="${lib.dir}/native"/>
           <env key="BUILD_NATIVE_DIR" value="${build.dir}/native"/>
           <env key="DIST_LIB_DIR" value="${dist.dir}/lib/native"/>
@@ -2045,7 +2065,7 @@
   <!-- ================================================================== -->
   <!-- Clean.  Delete the build files, and their directories              -->
   <!-- ================================================================== -->
-  <target name="clean" depends="clean-contrib, clean-sign, clean-fi" description="Clean.  Delete the build files, and their directories">
+  <target name="clean" depends="clean-contrib, clean-sign, clean-fi, clean-ms-winutils" description="Clean.  Delete the build files, and their directories">
     <delete dir="${build.dir}"/>
     <delete dir="${docs.src}/build"/>
     <delete dir="${src.docs.cn}/build"/>
@@ -2070,6 +2090,24 @@
     <delete file="${ivy.jar}"/>
   </target>
 
+  <target name="clean-ms-winutils" if="windows" description="Delete ant and editor generated files for wintuils">
+    <delete file="${basedir}/bin/winutils.exe" failonerror="false"/>
+    <delete dir="${build.winutils}" failonerror="false"/>
+    <delete failonerror="false">
+      <fileset dir="${winutils.src.dir}" includes="**/*">
+        <exclude name="**/*.c"/>
+        <exclude name="**/*.cpp"/>
+        <exclude name="**/*.h"/>
+        <exclude name="**/*.sln"/>
+        <exclude name="**/*.vcxproj"/>
+        <exclude name="**/*.vcxproj.user"/>
+      </fileset>
+    </delete>
+    <delete failonerror="false">
+      <dirset dir="${winutils.src.dir}" includes="**/*"/>
+    </delete>
+  </target>
+
 
   <!-- ================================================================== -->
   <!-- Clean contrib target. For now, must be called explicitly           -->

Modified: hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java (original)
+++ hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/FileUtil.java Thu May 31 01:52:15 2012
@@ -520,21 +520,24 @@ public class FileUtil {
     StringBuffer untarCommand = new StringBuffer();
     boolean gzipped = inFile.toString().endsWith("gz");
     if (gzipped) {
-      untarCommand.append(" gzip -dc '");
+      untarCommand.append((Shell.WINDOWS) ? " gzip -dc \"" : " gzip -dc '");
       untarCommand.append(FileUtil.makeShellPath(inFile));
-      untarCommand.append("' | (");
+      untarCommand.append((Shell.WINDOWS) ? "\" | (" : "' | (");
     } 
-    untarCommand.append("cd '");
+    untarCommand.append((Shell.WINDOWS) ? "cd \"" : "cd '");
     untarCommand.append(FileUtil.makeShellPath(untarDir)); 
-    untarCommand.append("' ; ");
-    untarCommand.append("tar -xf ");
-    
+    untarCommand.append((Shell.WINDOWS) ? "\" & " : "' ; ");
+
+    // Force the archive path as local on Windows as it can have a colon
+    untarCommand.append((Shell.WINDOWS) ? "tar --force-local -xf " : "tar -xf ");
+
     if (gzipped) {
       untarCommand.append(" -)");
     } else {
       untarCommand.append(FileUtil.makeShellPath(inFile));
     }
-    String[] shellCmd = {(Path.WINDOWS)?"cmd":"bash", (Path.WINDOWS)?"/c":"-c", untarCommand.toString() };
+    String[] shellCmd = {(Shell.WINDOWS)?"cmd":"bash", (Shell.WINDOWS)?"/c":"-c",
+      untarCommand.toString() };
     ShellCommandExecutor shexec = new ShellCommandExecutor(shellCmd);
     shexec.execute();
     int exitcode = shexec.getExitCode();
@@ -592,6 +595,15 @@ public class FileUtil {
       throw new IOException("FileCopy: " + "source file is unreadable: "
                             + fromFileName);
 
+    // Make sure the parent directory exist for the toFileName
+    if (toFile.getParent() != null) {
+      File toFileParentDir = new File(toFile.getParent());
+      if (!toFileParentDir.exists() && !toFileParentDir.mkdirs()) {
+        throw new IOException("FileCopy: failed to create target directory: "
+                              + toFileParentDir.getPath());
+      }
+    }
+
     InputStream from = null;
     OutputStream to = null;
     try {
@@ -683,8 +695,10 @@ public class FileUtil {
    */
   public static int chmod(String filename, String perm, boolean recursive)
                             throws IOException {
-    if (Path.WINDOWS)
+    if (Shell.DISABLEWINDOWS_TEMPORARILY) {
       return 0;
+    }
+
     StringBuffer cmdBuf = new StringBuffer();
     cmdBuf.append("chmod ");
     if (recursive) {
@@ -714,16 +728,13 @@ public class FileUtil {
    */
   public static void setPermission(File f, FsPermission permission
                                    ) throws IOException {
-    if (Shell.DISABLEWINDOWS_TEMPORARILY)
-      return;
-
     FsAction user = permission.getUserAction();
     FsAction group = permission.getGroupAction();
     FsAction other = permission.getOtherAction();
 
     // use the native/fork if the group/other permissions are different
-    // or if the native is available    
-    if (group != other || NativeIO.isAvailable()) {
+    // or if the native is available or on Windows
+    if (group != other || NativeIO.isAvailable() || Shell.WINDOWS) {
       execSetPermission(f, permission);
       return;
     }
@@ -771,8 +782,8 @@ public class FileUtil {
     if (NativeIO.isAvailable()) {
       NativeIO.chmod(f.getCanonicalPath(), permission.toShort());
     } else {
-      execCommand(f, Shell.SET_PERMISSION_COMMAND,
-                  String.format("%04o", permission.toShort()));
+      execCommand(f, Shell.getSetPermissionCommand(
+                  String.format("%04o", permission.toShort())));
     }
   }
   

Modified: hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/RawLocalFileSystem.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/RawLocalFileSystem.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/RawLocalFileSystem.java (original)
+++ hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/fs/RawLocalFileSystem.java Thu May 31 01:52:15 2012
@@ -284,7 +284,33 @@ public class RawLocalFileSystem extends 
       return true;
     }
     LOG.debug("Falling through to a copy of " + src + " to " + dst);
-    return FileUtil.copy(this, src, this, dst, true, getConf());
+
+    // TODO: What if src and dst are same or subset of another?
+
+    if (this.exists(dst)) {
+      FileStatus sdst = this.getFileStatus(dst);
+      if (sdst.isDir()) {
+        // If dst exists and is a folder, we have to copy the source content, otherwise
+        // we will copy the src folder into the dst folder what is not the desired
+        // behavior for rename
+
+        FileStatus contents[] = this.listStatus(src);
+        for (int i = 0; i < contents.length; i++) {
+          FileUtil.copy(this, contents[i].getPath(), this,
+            new Path(dst, contents[i].getPath().getName()),
+            true, getConf());
+        }
+
+        // Delete the source folder
+        return this.delete(src, true);
+      }
+      else {
+        return FileUtil.copy(this, src, this, dst, true, getConf());
+      }
+    }
+    else {
+      return FileUtil.copy(this, src, this, dst, true, getConf());
+    }
   }
   
   @Deprecated
@@ -440,17 +466,11 @@ public class RawLocalFileSystem extends 
 
     /// loads permissions, owner, and group from `ls -ld`
     private void loadPermissionInfo() {
-      if (Shell.DISABLEWINDOWS_TEMPORARILY){
-        setPermission(null);
-        setOwner(null);
-        setGroup(null);
-        return;
-      }
       IOException e = null;
       try {
         StringTokenizer t = new StringTokenizer(
             FileUtil.execCommand(new File(getPath().toUri()), 
-                                 Shell.getGET_PERMISSION_COMMAND()));
+                                          Shell.getGetPermissionCommand()));
         //expected format
         //-rw-------    1 username groupname ...
         String permission = t.nextToken();
@@ -459,7 +479,18 @@ public class RawLocalFileSystem extends 
         }
         setPermission(FsPermission.valueOf(permission));
         t.nextToken();
-        setOwner(t.nextToken());
+        
+        String owner = t.nextToken();
+        // If on windows domain, token format is DOMAIN\\user and we want to
+        // extract only the user name
+        if (Shell.WINDOWS) {
+          int i = owner.indexOf('\\');
+          if (i != -1)
+            owner = owner.substring(i + 1);
+        }
+        setOwner(owner);
+
+        // FIXME: Group names could have spaces on Windows
         setGroup(t.nextToken());
       } catch (Shell.ExitCodeException ioe) {
         if (ioe.getExitCode() != 1) {
@@ -504,7 +535,7 @@ public class RawLocalFileSystem extends 
     } else {
       //OWNER[:[GROUP]]
       String s = username + (groupname == null? "": ":" + groupname);
-      FileUtil.execCommand(pathToFile(p), Shell.SET_OWNER_COMMAND, s);
+      FileUtil.execCommand(pathToFile(p), Shell.getSetOwnerCommand(s));
     }
   }
 
@@ -513,8 +544,6 @@ public class RawLocalFileSystem extends 
    */
   @Override
   public void setPermission(Path p, FsPermission permission) throws IOException {
-    if (Shell.DISABLEWINDOWS_TEMPORARILY)
-      return;
     FileUtil.setPermission(pathToFile(p), permission);
   }
 }

Modified: hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java (original)
+++ hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java Thu May 31 01:52:15 2012
@@ -60,37 +60,6 @@ public class ShellBasedUnixGroupsMapping
    */
   private static List<String> getUserGroups(final String user) throws IOException {
     List<String> groups = new LinkedList<String>();
-    if (Shell.WINDOWS) {
-      String result = Shell.execCommand(Shell.getGroupsForUserCommand(user));
-      String[] lines = result.split("\\r\\n");
-        String line = lines[0];
-        if (!line.startsWith("User name")) {
-          throw new IOException(
-              "Command result did not start with \"User name\"");
-        }
-        String[] splits = line.substring(9).split("\\s");
-        if (splits.length == 0 || !splits[splits.length-1].equals(user)) {
-          throw new IOException("Bad user name returned");
-        }
-        for (int i=1; i<lines.length; ++i) {
-          line = lines[i];
-          // not handling global group memberships now
-          // it might be better to handle them via a specific domain controller
-          // plugin
-          if (line.startsWith("Local Group Memberships")) {
-            splits = line.substring(23).split("\\s");
-            for (String group : splits) {
-              if (group.length() > 0) {
-                if (group.charAt(0) == '*') {
-                  group = group.substring(1);
-                }
-                groups.add(group);
-              }
-            }
-          }
-        }
-    }
-    else {
       String result = "";
       try {
         result = Shell.execCommand(Shell.getGroupsForUserCommand(user));
@@ -102,9 +71,7 @@ public class ShellBasedUnixGroupsMapping
       
       while (tokenizer.hasMoreTokens()) {
         groups.add(tokenizer.nextToken());
-      }
-    }
-    
+      }    
     return groups;
   }
 }

Modified: hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java (original)
+++ hadoop/common/branches/branch-1-win/src/core/org/apache/hadoop/util/Shell.java Thu May 31 01:52:15 2012
@@ -40,34 +40,53 @@ import org.apache.hadoop.conf.Configurat
 abstract public class Shell {
   
   public static final Log LOG = LogFactory.getLog(Shell.class);
-  
+
+  /** a Windows utility to emulate Unix commands */
+  public static final String WINUTILS = System.getenv("HADOOP_HOME")
+                                        + "\\bin\\winutils";
+
   /** a Unix command to get the current user's name */
   public final static String USER_NAME_COMMAND = "whoami";
-  /** a Unix command to get the current user's groups list */
+
+  /** a Unix command to set the change user's groups list */
+  public static final String SET_GROUP_COMMAND = "chgrp";
+
+  /** Return a command to get the current user's groups list */
   public static String[] getGroupsCommand() {
-    return (WINDOWS)? new String[]{"cmd", "/c", "groups"}:
-        new String[]{"bash", "-c", "groups"};
+    return (WINDOWS)? new String[]{"cmd", "/c", "groups"}
+                    : new String[]{"bash", "-c", "groups"};
   }
-  /** a Unix command to get a given user's groups list */
+
+  /** Return a command to get a given user's groups list */
   public static String[] getGroupsForUserCommand(final String user) {
     //'groups username' command return is non-consistent across different unixes
-	  return (WINDOWS)? new String[] {"cmd", "/c", "net user " + user}:
-        new String [] {"bash", "-c", "id -Gn " + user};
+    return (WINDOWS)? new String[] { WINUTILS, "groups", user}
+                    : new String [] {"bash", "-c", "id -Gn " + user};
   }
-  /** a Unix command to get a given netgroup's user list */
+
+  /** Return a command to get a given netgroup's user list */
   public static String[] getUsersForNetgroupCommand(final String netgroup) {
     //'groups username' command return is non-consistent across different unixes
-    return (WINDOWS)? new String [] {"cmd", "/c", "getent netgroup " + netgroup}: new String [] {"bash", "-c", "getent netgroup " + netgroup};
+    return (WINDOWS)? new String [] {"cmd", "/c", "getent netgroup " + netgroup}
+                    : new String [] {"bash", "-c", "getent netgroup " + netgroup};
   }
-  /** a Unix command to set permission */
-  public static final String SET_PERMISSION_COMMAND = "chmod";
-  /** a Unix command to set owner */
-  public static final String SET_OWNER_COMMAND = "chown";
-  public static final String SET_GROUP_COMMAND = "chgrp";
-  /** Return a Unix command to get permission information. */
-  public static String[] getGET_PERMISSION_COMMAND() {
-    //force /bin/ls, except on windows.
-    return new String[] {(WINDOWS ? "ls" : "/bin/ls"), "-ld"};
+
+  /** Return a command to get permission information. */
+  public static String[] getGetPermissionCommand() {
+    return (WINDOWS) ? new String[] { WINUTILS, "ls" }
+                     : new String[] { "/bin/ls", "-ld" };
+  }
+
+  /** Return a command to set permission */
+  public static String[] getSetPermissionCommand(String perm) {
+    return (WINDOWS) ? new String[] { WINUTILS, "chmod", perm }
+                     : new String[] { "chmod", perm };
+  }
+
+  /** Return a command to set owner */
+  public static String[] getSetOwnerCommand(String owner) {
+    return (WINDOWS) ? new String[] { WINUTILS, "chown", owner }
+                     : new String[] { "chown", owner };
   }
 
   /**Time after which the executing script would be timedout*/

Modified: hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapred/JobTracker.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapred/JobTracker.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapred/JobTracker.java (original)
+++ hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapred/JobTracker.java Thu May 31 01:52:15 2012
@@ -102,6 +102,7 @@ import org.apache.hadoop.security.author
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.util.HostsFileReader;
 import org.apache.hadoop.util.ReflectionUtils;
+import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.VersionInfo;
 
@@ -2410,6 +2411,12 @@ public class JobTracker implements MRCon
         fs.delete(systemDir, true);
         if (FileSystem.mkdirs(fs, systemDir, 
             new FsPermission(SYSTEM_DIR_PERMISSION))) {
+          if (Shell.WINDOWS) {
+            // Explicitly set ownership on Windows, as in some scenarios
+            // Administrators group would end up being the owner what is
+            // currently not supported by the Hadoop security model.
+            fs.setOwner(systemDir, getMROwner().getShortUserName(), null);
+          }
           break;
         }
         LOG.error("Mkdirs failed to create " + systemDir);

Modified: hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapreduce/JobSubmissionFiles.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapreduce/JobSubmissionFiles.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapreduce/JobSubmissionFiles.java (original)
+++ hadoop/common/branches/branch-1-win/src/mapred/org/apache/hadoop/mapreduce/JobSubmissionFiles.java Thu May 31 01:52:15 2012
@@ -26,6 +26,7 @@ import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.mapred.JobClient;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.conf.Configuration;
 /**
  * A utility to manage job submission files.<br/>
@@ -113,8 +114,17 @@ public class JobSubmissionFiles {
                       "by " + realUser + " and permissions must be rwx------");
       }
     } else {
-      fs.mkdirs(stagingArea, 
-          new FsPermission(JOB_DIR_PERMISSION));
+      if (fs.mkdirs(stagingArea, new FsPermission(JOB_DIR_PERMISSION))) {
+        if (Shell.WINDOWS) {
+          // On Windows, if a file or directory is created by users in
+          // Administrators group, the file owner will be the Administrators
+          // group as opposed to the actual users. This causes problem
+          // because Hadoop security model assumes whoever created the
+          // file should be the owner. We explicitly set ownership here
+          // to fix the problem on Windows.
+          fs.setOwner(stagingArea, realUser, null);
+        }
+      }
     }
     return stagingArea;
   }

Modified: hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/TestFileStatus.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/TestFileStatus.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/TestFileStatus.java (original)
+++ hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/TestFileStatus.java Thu May 31 01:52:15 2012
@@ -234,16 +234,15 @@ public class TestFileStatus {
       assertEquals(file2.toString(), stats[3].getPath().toString());
       assertEquals(file3.toString(), stats[4].getPath().toString());
 
-      if(!Shell.WINDOWS){ //test permission error on hftp 
-        fs.setPermission(dir, new FsPermission((short)0));
-        try {
-          final String username = UserGroupInformation.getCurrentUser().getShortUserName() + "1";
-          final HftpFileSystem hftp2 = cluster.getHftpFileSystemAs(username, conf, "somegroup");
-          hftp2.getContentSummary(dir);
-          fail();
-        } catch(IOException ioe) {
-          FileSystem.LOG.info("GOOD: getting an exception", ioe);
-        }
+      //test permission error on hftp 
+      fs.setPermission(dir, new FsPermission((short)0));
+      try {
+        final String username = UserGroupInformation.getCurrentUser().getShortUserName() + "1";
+        final HftpFileSystem hftp2 = cluster.getHftpFileSystemAs(username, conf, "somegroup");
+        hftp2.getContentSummary(dir);
+        fail();
+      } catch(IOException ioe) {
+        FileSystem.LOG.info("GOOD: getting an exception", ioe);
       }
     } finally {
       fs.close();

Modified: hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/server/datanode/TestDiskError.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/server/datanode/TestDiskError.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/server/datanode/TestDiskError.java (original)
+++ hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/hdfs/server/datanode/TestDiskError.java Thu May 31 01:52:15 2012
@@ -33,6 +33,7 @@ import org.apache.hadoop.hdfs.protocol.L
 import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
 import org.apache.hadoop.io.Text;
 import org.apache.hadoop.util.DiskChecker;
+import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.hdfs.security.token.block.BlockTokenSecretManager;
 
 import junit.framework.TestCase;
@@ -155,7 +156,7 @@ public class TestDiskError extends TestC
   
   public void testLocalDirs() throws Exception {
     Configuration conf = new Configuration();
-    final String permStr = "755";
+    final String permStr = (Shell.WINDOWS) ? "700" : "755";
     FsPermission expected = new FsPermission(permStr);
     conf.set(DataNode.DATA_DIR_PERMISSION_KEY, permStr);
     MiniDFSCluster cluster = null; 

Modified: hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/security/TestUserGroupInformation.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/security/TestUserGroupInformation.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/security/TestUserGroupInformation.java (original)
+++ hadoop/common/branches/branch-1-win/src/test/org/apache/hadoop/security/TestUserGroupInformation.java Thu May 31 01:52:15 2012
@@ -43,6 +43,7 @@ import org.apache.hadoop.security.token.
 import org.apache.hadoop.security.token.TokenIdentifier;
 import org.junit.Test;
 import static org.apache.hadoop.test.MetricsAsserts.*;
+import org.apache.hadoop.util.Shell;
 
 public class TestUserGroupInformation {
   final private static String USER_NAME = "user1@HADOOP.APACHE.ORG";
@@ -88,10 +89,20 @@ public class TestUserGroupInformation {
     BufferedReader br = new BufferedReader
                           (new InputStreamReader(pp.getInputStream()));
     String userName = br.readLine().trim();
+    // If on windows domain, token format is DOMAIN\\user and we want to
+    // extract only the user name
+    if(Shell.WINDOWS) {
+      int sp = userName.lastIndexOf('\\');
+      if (sp != -1) {
+        userName = userName.substring(sp + 1);
+      }
+    }
     // get the groups
-    pp = Runtime.getRuntime().exec("id -Gn");
+    pp = Runtime.getRuntime().exec(Shell.WINDOWS ?
+      Shell.WINUTILS + " groups" : "id -Gn");
     br = new BufferedReader(new InputStreamReader(pp.getInputStream()));
     String line = br.readLine();
+
     System.out.println(userName + ":" + line);
    
     List<String> groups = new ArrayList<String> ();    
@@ -101,10 +112,11 @@ public class TestUserGroupInformation {
     
     final UserGroupInformation login = UserGroupInformation.getCurrentUser();
     assertEquals(userName, login.getShortUserName());
+
     String[] gi = login.getGroupNames();
     assertEquals(groups.size(), gi.length);
     for(int i=0; i < gi.length; i++) {
-      assertEquals(groups.get(i), gi[i]);
+    	assertEquals(groups.get(i), gi[i]);
     }
     
     final UserGroupInformation fakeUser = 

Modified: hadoop/common/branches/branch-1-win/src/test/testshell/ExternalMapReduce.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/test/testshell/ExternalMapReduce.java?rev=1344527&r1=1344526&r2=1344527&view=diff
==============================================================================
--- hadoop/common/branches/branch-1-win/src/test/testshell/ExternalMapReduce.java (original)
+++ hadoop/common/branches/branch-1-win/src/test/testshell/ExternalMapReduce.java Thu May 31 01:52:15 2012
@@ -37,6 +37,7 @@ import org.apache.hadoop.mapred.Mapper;
 import org.apache.hadoop.mapred.OutputCollector;
 import org.apache.hadoop.mapred.Reducer;
 import org.apache.hadoop.mapred.Reporter;
+import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
 
@@ -55,6 +56,51 @@ public class ExternalMapReduce extends C
 
   }
 
+  // Executes the given shell command. Returns zero on success.
+  private static int execCommandAndCatchEx(String []argv) throws IOException {
+    Process p = Runtime.getRuntime().exec(argv);
+    int ret = -1;
+    try {
+      ret = p.waitFor();
+    } catch(InterruptedException ie) {
+      //do nothing here.
+    }
+    return ret;
+  }
+
+  // Verifies that the given list of files exist on the local file system.
+  // Throws on failure.
+  private static void verifyFilesExist(String [] fileList) throws IOException {
+    // fork off ls to see if the file exists.
+    // java file.exists() does not work on symlinks on java6
+    if (Shell.WINDOWS) {
+      // FIXME: Checking file existence one by one until multiple file
+      // scenario is supported by winutils
+      for (int i = 0; i< fileList.length; ++i) {
+        String[] argv = new String[3];
+        argv[0] = Shell.WINUTILS;
+        argv[1] = "ls";
+        argv[2] = fileList[i];
+
+        int ret = execCommandAndCatchEx(argv);
+        if (ret != 0) {
+          throw new IOException(fileList[i] + " does not exist");
+        }
+      }
+    } else {
+      String[] argv = new String[fileList.length + 1];
+      argv[0] = "ls";
+      for (int i = 0; i < fileList.length; ++i) {
+        argv[i + 1] = fileList[i];
+      }
+
+      int ret = execCommandAndCatchEx(argv);
+      if (ret != 0) {
+        throw new IOException("files_tmp does not exist");
+      }
+    }
+  }
+
   public static class MapClass extends MapReduceBase 
     implements Mapper<WritableComparable, Writable,
                       WritableComparable, IntWritable> {
@@ -71,28 +117,12 @@ public class ExternalMapReduce extends C
         throw new IOException("failed to find the library test.jar in" 
             + classpath);
       }
-      //fork off ls to see if the file exists.
-      // java file.exists() will not work on 
-      // cygwin since it is a symlink
-      String[] argv = new String[8];
-      argv[0] = "ls";
-      argv[1] = "files_tmp";
-      argv[2] = "localfilelink";
-      argv[3] = "dfsfilelink";
-      argv[4] = "tarlink";
-      argv[5] = "ziplink";
-      argv[6] = "test.tgz";
-      argv[7] = "jarlink";
-      Process p = Runtime.getRuntime().exec(argv);
-      int ret = -1;
-      try {
-        ret = p.waitFor();
-      } catch(InterruptedException ie) {
-        //do nothing here.
-      }
-      if (ret != 0) {
-        throw new IOException("files_tmp does not exist");
-      }
+
+      String[] expectedFileList = { "files_tmp", "localfilelink",
+          "dfsfilelink", "tarlink", "ziplink", "test.tgz", "jarlink" }; 
+
+      verifyFilesExist(expectedFileList);
+
       File file = new File("./jarlink/test.txt");
       if (!file.canExecute()) {
         throw new IOException("jarlink/test.txt is not executable");

Added: hadoop/common/branches/branch-1-win/src/winutils/chmod.c
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/chmod.c?rev=1344527&view=auto
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/chmod.c (added)
+++ hadoop/common/branches/branch-1-win/src/winutils/chmod.c Thu May 31 01:52:15 2012
@@ -0,0 +1,1199 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with this
+* work for additional information regarding copyright ownership. The ASF
+* licenses this file to you 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.
+*/
+
+#include "common.h"
+#include <errno.h>
+
+enum CHMOD_WHO
+{
+  CHMOD_WHO_NONE  =    0,
+  CHMOD_WHO_OTHER =   07,
+  CHMOD_WHO_GROUP =  070,
+  CHMOD_WHO_USER  = 0700,
+  CHMOD_WHO_ALL   = CHMOD_WHO_OTHER | CHMOD_WHO_GROUP | CHMOD_WHO_USER
+};
+
+enum CHMOD_OP
+{
+  CHMOD_OP_INVALID,
+  CHMOD_OP_PLUS,
+  CHMOD_OP_MINUS,
+  CHMOD_OP_EQUAL,
+};
+
+enum CHMOD_PERM
+{
+  CHMOD_PERM_NA =  00,
+  CHMOD_PERM_R  =  01,
+  CHMOD_PERM_W  =  02,
+  CHMOD_PERM_X  =  04,
+  CHMOD_PERM_LX = 010,
+};
+
+/*
+ * We use the following struct to build a linked list of mode change actions.
+ * The mode is described by the following grammar:
+ *  mode         ::= clause [, clause ...]
+ *  clause       ::= [who ...] [action ...]
+ *  action       ::= op [perm ...] | op [ref]
+ *  who          ::= a | u | g | o
+ *  op           ::= + | - | =
+ *  perm         ::= r | w | x | X
+ *  ref          ::= u | g | o
+ */
+typedef struct _MODE_CHANGE_ACTION
+{
+  USHORT who;
+  USHORT op;
+  USHORT perm;
+  USHORT ref;
+  struct _MODE_CHANGE_ACTION *next_action;
+} MODE_CHANGE_ACTION, *PMODE_CHANGE_ACTION;
+
+
+const MODE_CHANGE_ACTION INIT_MODE_CHANGE_ACTION = {
+  CHMOD_WHO_NONE, CHMOD_OP_INVALID, CHMOD_PERM_NA, CHMOD_WHO_NONE, NULL
+};
+
+
+static BOOL ParseOctalMode(LPCWSTR tsMask, USHORT *uMask);
+
+static BOOL ParseMode(LPCWSTR modeString, PMODE_CHANGE_ACTION *actions);
+
+static BOOL FreeActions(PMODE_CHANGE_ACTION actions);
+
+static BOOL GetWindowsDACLs(__in USHORT unixMask, __in PSID pOwnerSid,
+  __in PSID pGroupSid, __out PACL *ppNewDACL);
+
+static BOOL ParseCommandLineArguments(__in int argc, __in wchar_t *argv[],
+  __out BOOL *rec, __out_opt USHORT *mask,
+  __out_opt PMODE_CHANGE_ACTION *actions, __out LPCWSTR *path);
+
+static BOOL ChangeFileModeByMask(__in LPCWSTR path, USHORT mode);
+
+static BOOL ChangeFileModeByActions(__in LPCWSTR path,
+  PMODE_CHANGE_ACTION actions);
+
+static BOOL ChangeFileMode(__in LPCWSTR path, __in_opt USHORT mode,
+  __in_opt PMODE_CHANGE_ACTION actions);
+
+static BOOL ChangeFileModeRecursively(__in LPCWSTR path, __in_opt USHORT mode,
+  __in_opt PMODE_CHANGE_ACTION actions);
+
+static USHORT ComputeNewMode(__in USHORT oldMode, __in USHORT who,
+  __in USHORT op, __in USHORT perm, __in USHORT ref);
+
+
+//----------------------------------------------------------------------------
+// Function: Chmod
+//
+// Description:
+//	The main method for chmod command
+//
+// Returns:
+//	0: on success
+//
+// Notes:
+//
+int Chmod(int argc, wchar_t *argv[])
+{
+  LPWSTR pathName = NULL;
+  LPWSTR longPathName = NULL;
+
+  BOOL recursive = FALSE;
+
+  PMODE_CHANGE_ACTION actions = NULL;
+
+  USHORT unixAccessMask = 0x0000;
+
+  DWORD dwRtnCode = 0;
+
+  int ret = EXIT_FAILURE;
+
+  // Parsing chmod arguments
+  //
+  if (!ParseCommandLineArguments(argc, argv,
+    &recursive, &unixAccessMask, &actions, &pathName))
+  {
+    fwprintf(stderr, L"Incorrect command line arguments.\n\n");
+    ChmodUsage(argv[0]);
+    return EXIT_FAILURE;
+  }
+
+  // Convert the path the the long path
+  //
+  dwRtnCode = ConvertToLongPath(pathName, &longPathName);
+  if (dwRtnCode != ERROR_SUCCESS)
+  {
+    ReportErrorCode(L"ConvertToLongPath", dwRtnCode);
+    goto ChmodEnd;
+  }
+
+  if (!recursive)
+  {
+    if (ChangeFileMode(longPathName, unixAccessMask, actions))
+    {
+      ret = EXIT_SUCCESS;
+    }
+  }
+  else
+  {
+    if (ChangeFileModeRecursively(longPathName, unixAccessMask, actions))
+    {
+      ret = EXIT_SUCCESS;
+    }
+  }
+
+ChmodEnd:
+  FreeActions(actions);
+  LocalFree(longPathName);
+
+  return ret;
+}
+
+//----------------------------------------------------------------------------
+// Function: ChangeFileMode
+//
+// Description:
+//	Wrapper function for change file mode. Choose either change by action or by
+//  access mask.
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//
+static BOOL ChangeFileMode(__in LPCWSTR path, __in_opt USHORT unixAccessMask,
+  __in_opt PMODE_CHANGE_ACTION actions)
+{
+  if (actions != NULL)
+    return ChangeFileModeByActions(path, actions);
+  else
+    return ChangeFileModeByMask(path, unixAccessMask);
+}
+
+//----------------------------------------------------------------------------
+// Function: ChangeFileModeRecursively
+//
+// Description:
+//	Travel the directory recursively to change the permissions.
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//  The recursion works in the following way:
+//    - If the path is not a directory, change its mode and return;
+//    - Otherwise, call the method on all its children, then change its mode.
+//
+static BOOL ChangeFileModeRecursively(__in LPCWSTR path, __in_opt USHORT mode,
+  __in_opt PMODE_CHANGE_ACTION actions)
+{
+  LPWSTR dir = NULL;
+
+  size_t pathSize = 0;
+  size_t dirSize = 0;
+
+  HANDLE hFind = INVALID_HANDLE_VALUE;
+  WIN32_FIND_DATA ffd;
+  DWORD dwRtnCode = ERROR_SUCCESS;
+  BOOL ret = FALSE;
+
+  BY_HANDLE_FILE_INFORMATION fileInfo;
+
+  if ((dwRtnCode = GetFileInformationByName(path, &fileInfo)) != ERROR_SUCCESS)
+  {
+    ReportErrorCode(L"GetFileInformationByName", dwRtnCode);
+    return FALSE;
+  }
+
+  if (!IsDirFileInfo(&fileInfo))
+  {
+     if (ChangeFileMode(path, mode, actions))
+     {
+       return TRUE;
+     }
+     else
+     {
+       return FALSE;
+     }
+  }
+
+  // MAX_PATH is used here, because we use relative path, and relative
+  // paths are always limited to a total of MAX_PATH characters.
+  //
+  if (FAILED(StringCchLengthW(path, MAX_PATH - 3, &pathSize)))
+  {
+    return FALSE;
+  }
+  dirSize = pathSize + 3;
+  dir = (LPWSTR)LocalAlloc(LPTR, dirSize * sizeof(WCHAR));
+  if (dir == NULL)
+  {
+    ReportErrorCode(L"LocalAlloc", GetLastError());
+    goto ChangeFileModeRecursivelyEnd;
+  }
+
+  if (FAILED(StringCchCopyW(dir, dirSize, path)) ||
+    FAILED(StringCchCatW(dir, dirSize, L"\\*")))
+  {
+    goto ChangeFileModeRecursivelyEnd;
+  }
+
+  hFind = FindFirstFile(dir, &ffd);
+  if (hFind == INVALID_HANDLE_VALUE)
+  {
+    ReportErrorCode(L"FindFirstFile", GetLastError());
+    goto ChangeFileModeRecursivelyEnd;
+  }
+
+  do
+  {
+    LPWSTR filename = NULL;
+    size_t filenameSize = 0;
+
+    if (wcscmp(ffd.cFileName, L".") == 0 ||
+      wcscmp(ffd.cFileName, L"..") == 0)
+      continue;
+
+    filenameSize = pathSize + wcslen(ffd.cFileName) + 2;
+    filename = (LPWSTR)LocalAlloc(LPTR, filenameSize * sizeof(WCHAR));
+    if (filename == NULL)
+    {
+      ReportErrorCode(L"LocalAlloc", GetLastError());
+      goto ChangeFileModeRecursivelyEnd;
+    }
+
+    if (FAILED(StringCchCopyW(filename, filenameSize, path)) ||
+      FAILED(StringCchCatW(filename, filenameSize, L"\\")) ||
+      FAILED(StringCchCatW(filename, filenameSize, ffd.cFileName)))
+    {
+      LocalFree(filename);
+      goto ChangeFileModeRecursivelyEnd;
+    }
+     
+    if(!ChangeFileModeRecursively(filename, mode, actions))
+    {
+      LocalFree(filename);
+      goto ChangeFileModeRecursivelyEnd;
+    }
+
+    LocalFree(filename);
+
+  } while (FindNextFileW(hFind, &ffd));
+
+  if (!ChangeFileMode(path, mode, actions))
+  {
+    goto ChangeFileModeRecursivelyEnd;
+  }
+
+  ret = TRUE;
+
+ChangeFileModeRecursivelyEnd:
+  LocalFree(dir);
+
+  return ret;
+}
+
+//----------------------------------------------------------------------------
+// Function: ChangeFileModeByMask
+//
+// Description:
+//	Change a file or direcotry at the path to Unix mode
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//
+static BOOL ChangeFileModeByMask(__in LPCWSTR path, USHORT mode)
+{
+  PACL pOldDACL = NULL;
+  PACL pNewDACL = NULL;
+  PSID pOwnerSid = NULL;
+  PSID pGroupSid = NULL;
+  PSECURITY_DESCRIPTOR pSD = NULL;
+
+  SECURITY_DESCRIPTOR_CONTROL control;
+  DWORD revision = 0;
+
+  PSECURITY_DESCRIPTOR pAbsSD = NULL;
+  PACL pAbsDacl = NULL;
+  PACL pAbsSacl = NULL;
+  PSID pAbsOwner = NULL;
+  PSID pAbsGroup = NULL;
+
+  DWORD dwRtnCode = 0;
+  DWORD dwErrorCode = 0;
+
+  BOOL ret = FALSE;
+
+  // Get owner and group Sids
+  //
+  dwRtnCode = GetNamedSecurityInfoW(
+    path,
+    SE_FILE_OBJECT, 
+    OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+    DACL_SECURITY_INFORMATION,
+    &pOwnerSid,
+    &pGroupSid,
+    &pOldDACL,
+    NULL,
+    &pSD);
+  if (ERROR_SUCCESS != dwRtnCode)
+  {
+    ReportErrorCode(L"GetNamedSecurityInfo", dwRtnCode);
+    goto ChangeFileMode; 
+  }
+
+  // SetSecurityDescriptorDacl function used below only accepts security
+  // descriptor in absolute format, meaning that its members must be pointers to
+  // other structures, rather than offsets to contiguous data.
+  // To determine whether a security descriptor is self-relative or absolute,
+  // call the GetSecurityDescriptorControl function and check the
+  // SE_SELF_RELATIVE flag of the SECURITY_DESCRIPTOR_CONTROL parameter.
+  //
+  if (!GetSecurityDescriptorControl(pSD, &control, &revision))
+  {
+    ReportErrorCode(L"GetSecurityDescriptorControl", GetLastError());
+    goto ChangeFileMode;
+  }
+
+  // If the security descriptor is self-relative, we use MakeAbsoluteSD function
+  // to convert it to absolute format.
+  //
+  if ((control & SE_SELF_RELATIVE) == SE_SELF_RELATIVE)
+  {
+    DWORD absSDSize = 0;
+    DWORD daclSize = 0;
+    DWORD saclSize = 0;
+    DWORD ownerSize = 0;
+    DWORD primaryGroupSize = 0;
+    MakeAbsoluteSD(pSD, NULL, &absSDSize, NULL, &daclSize, NULL,
+      &saclSize, NULL, &ownerSize, NULL, &primaryGroupSize);
+    if ((dwErrorCode = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
+    {
+      ReportErrorCode(L"MakeAbsoluteSD", dwErrorCode);
+      goto ChangeFileMode;
+    }
+    pAbsSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, absSDSize);
+    pAbsDacl = (PACL) LocalAlloc(LPTR, daclSize);
+    pAbsSacl = (PACL) LocalAlloc(LPTR, saclSize);
+    pAbsOwner = (PSID) LocalAlloc(LPTR, ownerSize);
+    pAbsGroup = (PSID) LocalAlloc(LPTR, primaryGroupSize);
+    if (pAbsSD == NULL || pAbsDacl == NULL || pAbsSacl == NULL ||
+      pAbsOwner == NULL || pAbsGroup == NULL)
+    {
+      ReportErrorCode(L"LocalAlloc", GetLastError());
+      goto ChangeFileMode;
+    }
+
+    if (!MakeAbsoluteSD(pSD, pAbsSD, &absSDSize, pAbsDacl, &daclSize, pAbsSacl,
+      &saclSize, pAbsOwner, &ownerSize, pAbsGroup, &primaryGroupSize))
+    {
+      ReportErrorCode(L"MakeAbsoluteSD", GetLastError());
+      goto ChangeFileMode;
+    }
+  }
+
+  // Get Windows DACLs based on Unix access mask
+  //
+  if (!GetWindowsDACLs(mode, pOwnerSid, pGroupSid, &pNewDACL))
+    goto ChangeFileMode;
+
+  // Set the DACL information in the security descriptor; if a DACL is already
+  // present in the security descriptor, the DACL is replaced. The security
+  // descriptor is then used to set the security of a file or directory.
+  //
+  if (!SetSecurityDescriptorDacl(pAbsSD, TRUE, pNewDACL, FALSE))
+  {
+    ReportErrorCode(L"SetSecurityDescriptorDacl", GetLastError());
+    goto ChangeFileMode;
+  }
+
+  // MSDN states "This function is obsolete. Use the SetNamedSecurityInfo
+  // function instead." However we have the following problem when using
+  // SetNamedSecurityInfo:
+  //  - When PROTECTED_DACL_SECURITY_INFORMATION is not passed in as part of
+  //    security information, the object will include inheritable permissions
+  //    from its parent.
+  //  - When PROTECTED_DACL_SECURITY_INFORMATION is passsed in to set
+  //    permissions on a directory, the child object of the directory will lose
+  //    inheritable permissions from their parent (the current directory).
+  // By using SetFileSecurity, we have the nice property that the new
+  // permissions of the object does not include the inheritable permissions from
+  // its parent, and the child objects will not lose their inherited permissions
+  // from the current object.
+  //
+  if (!SetFileSecurity(path, DACL_SECURITY_INFORMATION, pAbsSD))
+  {
+    ReportErrorCode(L"SetFileSecurity", GetLastError());
+    goto ChangeFileMode;
+  }
+
+  ret = TRUE;
+
+ChangeFileMode:
+  LocalFree(pSD);
+  LocalFree(pNewDACL);
+  LocalFree(pAbsDacl);
+  LocalFree(pAbsSacl);
+  LocalFree(pAbsOwner);
+  LocalFree(pAbsGroup);
+  LocalFree(pAbsSD);
+
+  return ret;
+}
+
+//----------------------------------------------------------------------------
+// Function: ParseCommandLineArguments
+//
+// Description:
+//	Parse command line arguments for chmod.
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//	1. Recursive is only set on directories
+//  2. 'actions' is NULL if the mode is octal
+//
+static BOOL ParseCommandLineArguments(__in int argc, __in wchar_t *argv[],
+  __out BOOL *rec,
+  __out_opt USHORT *mask,
+  __out_opt PMODE_CHANGE_ACTION *actions,
+  __out LPCWSTR *path)
+{
+  LPCWSTR maskString;
+  BY_HANDLE_FILE_INFORMATION fileInfo;
+  DWORD dwRtnCode = ERROR_SUCCESS;
+
+  assert(path != NULL);
+
+  if (argc != 3 && argc != 4)
+    return FALSE;
+
+  *rec = FALSE;
+  if (argc == 4)
+  {
+    maskString = argv[2];
+    *path = argv[3];
+
+    if (wcscmp(argv[1], L"-R") == 0)
+    {
+      // Check if the given path name is a file or directory
+      // Only set recursive flag if the given path is a directory
+      //
+      dwRtnCode = GetFileInformationByName(*path, &fileInfo);
+      if (dwRtnCode != ERROR_SUCCESS)
+      {
+        ReportErrorCode(L"GetFileInformationByName", dwRtnCode);
+        return FALSE;
+      }
+
+      if (IsDirFileInfo(&fileInfo))
+      {
+        *rec = TRUE;
+      }
+    }
+    else
+      return FALSE;
+  }
+  else
+  {
+    maskString = argv[1];
+    *path = argv[2];
+  }
+
+  if (ParseOctalMode(maskString, mask))
+  {
+    return TRUE;
+  }
+  else if (ParseMode(maskString, actions))
+  {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+//----------------------------------------------------------------------------
+// Function: FreeActions
+//
+// Description:
+//	Free a linked list of mode change actions given the head node.
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//	none
+//
+static BOOL FreeActions(PMODE_CHANGE_ACTION actions)
+{
+  PMODE_CHANGE_ACTION curr = NULL;
+  PMODE_CHANGE_ACTION next = NULL;
+  
+  // Nothing to free if NULL is passed in
+  //
+  if (actions == NULL)
+  {
+    return TRUE;
+  }
+
+  curr = actions;
+  while (curr != NULL)
+  {
+    next = curr->next_action;
+    LocalFree(curr);
+    curr = next;
+  }
+  actions = NULL;
+
+  return TRUE;
+}
+
+//----------------------------------------------------------------------------
+// Function: ComputeNewMode
+//
+// Description:
+//	Compute a new mode based on the old mode and a mode change action.
+//
+// Returns:
+//	The newly computed mode
+//
+// Notes:
+//	Apply 'rwx' permission mask or reference permission mode according to the
+//  '+', '-', or '=' operator.
+//
+static USHORT ComputeNewMode(__in USHORT oldMode,
+  __in USHORT who, __in USHORT op,
+  __in USHORT perm, __in USHORT ref)
+{
+  static const USHORT readMask  = 0444;
+  static const USHORT writeMask = 0222;
+  static const USHORT exeMask   = 0111;
+
+  USHORT mask = 0;
+  USHORT mode = 0;
+
+  // Operation and reference mode are exclusive
+  //
+  assert(op == CHMOD_OP_EQUAL || op == CHMOD_OP_PLUS || op == CHMOD_OP_MINUS);
+  assert(ref == CHMOD_WHO_GROUP || ref == CHMOD_WHO_USER ||
+    ref == CHMOD_WHO_OTHER);
+
+  if(perm == CHMOD_PERM_NA && ref == CHMOD_WHO_NONE)
+  {
+    return oldMode;
+  }
+
+  if (perm != CHMOD_PERM_NA)
+  {
+    if ((perm & CHMOD_PERM_R) == CHMOD_PERM_R)
+      mask |= readMask;
+    if ((perm & CHMOD_PERM_W) == CHMOD_PERM_W)
+      mask |= writeMask;
+    if ((perm & CHMOD_PERM_X) == CHMOD_PERM_X)
+      mask |= exeMask;
+    if (((perm & CHMOD_PERM_LX) == CHMOD_PERM_LX))
+    {
+      // It applies execute permissions to directories regardless of their
+      // current permissions and applies execute permissions to a file which
+      // already has at least 1 execute permission bit already set (either user,
+      // group or other). It is only really useful when used with '+' and
+      // usually in combination with the -R option for giving group or other
+      // access to a big directory tree without setting execute permission on
+      // normal files (such as text files), which would normally happen if you
+      // just used "chmod -R a+rx .", whereas with 'X' you can do
+      // "chmod -R a+rX ." instead (Source: Wikipedia)
+      //
+      if ((oldMode & UX_DIRECTORY) == UX_DIRECTORY || (oldMode & exeMask))
+        mask |= exeMask;
+    }
+  }
+
+  mask |= oldMode & ref;
+
+  mask &= who;
+
+  if (op == CHMOD_OP_EQUAL)
+  {
+    mode = mask;
+  }
+  else if (op == CHMOD_OP_MINUS)
+  {
+    mode = oldMode & (~mask);
+  }
+  else if (op == CHMOD_OP_PLUS)
+  {
+    mode = oldMode | mask;
+  }
+
+  return mode;
+}
+
+//----------------------------------------------------------------------------
+// Function: ConvertActionsToMask
+//
+// Description:
+//	Convert a linked list of mode change actions to the Unix permission mask
+//  given the head node.
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//	none
+//
+static BOOL ConvertActionsToMask(__in LPCWSTR path,
+  __in PMODE_CHANGE_ACTION actions, __out PUSHORT puMask)
+{
+  PMODE_CHANGE_ACTION curr = NULL;
+
+  BY_HANDLE_FILE_INFORMATION fileInformation;
+  DWORD dwErrorCode = ERROR_SUCCESS;
+
+  USHORT mode = 0;
+
+  dwErrorCode = GetFileInformationByName(path, &fileInformation);
+  if (dwErrorCode != ERROR_SUCCESS)
+  {
+    ReportErrorCode(L"GetFileInformationByName", dwErrorCode);
+    return FALSE;
+  }
+  if (IsDirFileInfo(&fileInformation))
+  {
+    mode |= UX_DIRECTORY;
+  }
+  if (!FindFileOwnerAndPermission(path, NULL, NULL, &mode))
+  {
+    return FALSE;
+  }
+  *puMask = mode;
+
+  // Nothing to change if NULL is passed in
+  //
+  if (actions == NULL)
+  {
+    return TRUE;
+  }
+
+  for (curr = actions; curr != NULL; curr = curr->next_action)
+  {
+    mode = ComputeNewMode(mode, curr->who, curr->op, curr->perm, curr->ref);
+  }
+
+  *puMask = mode;
+  return TRUE;
+}
+
+//----------------------------------------------------------------------------
+// Function: ChangeFileModeByActions
+//
+// Description:
+//	Change a file mode through a list of actions.
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//	none
+//
+static BOOL ChangeFileModeByActions(__in LPCWSTR path,
+  PMODE_CHANGE_ACTION actions)
+{
+  USHORT mask = 0;
+
+  if (ConvertActionsToMask(path, actions, &mask))
+    return ChangeFileModeByMask(path, mask);
+  else
+    return FALSE;
+}
+
+//----------------------------------------------------------------------------
+// Function: ParseMode
+//
+// Description:
+//	Convert a mode string into a linked list of actions
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//	Take a state machine approach to parse the mode. Each mode change action
+//  will be a node in the output linked list. The state machine has five state,
+//  and each will only transit to the next; the end state can transit back to
+//  the first state, and thus form a circle. In each state, if we see a
+//  a character not belongs to the state, we will move to next state. WHO, PERM,
+//  and REF states are optional; OP and END states are required; and errors
+//  will only be reported at the latter two states.
+//
+static BOOL ParseMode(LPCWSTR modeString, PMODE_CHANGE_ACTION *pActions)
+{
+  enum __PARSE_MODE_ACTION_STATE
+  {
+    PARSE_MODE_ACTION_WHO_STATE,
+    PARSE_MODE_ACTION_OP_STATE,
+    PARSE_MODE_ACTION_PERM_STATE,
+    PARSE_MODE_ACTION_REF_STATE,
+    PARSE_MODE_ACTION_END_STATE
+  } state = PARSE_MODE_ACTION_WHO_STATE;
+
+  MODE_CHANGE_ACTION action = INIT_MODE_CHANGE_ACTION;
+  PMODE_CHANGE_ACTION actionsEnd = NULL;
+  PMODE_CHANGE_ACTION actionsLast = NULL;
+  USHORT lastWho;
+  WCHAR c = 0;
+  size_t len = 0;
+  size_t i = 0;
+
+  assert(modeString != NULL && pActions != NULL);
+
+  if (FAILED(StringCchLengthW(modeString, STRSAFE_MAX_CCH, &len)))
+  {
+    return FALSE;
+  }
+
+  actionsEnd = *pActions;
+  while(i <= len)
+  {
+    c = modeString[i];
+    if (state == PARSE_MODE_ACTION_WHO_STATE)
+    {
+      switch (c)
+      {
+      case L'a':
+        action.who |= CHMOD_WHO_ALL;
+        i++;
+        break;
+      case L'u':
+        action.who |= CHMOD_WHO_USER;
+        i++;
+        break;
+      case L'g':
+        action.who |= CHMOD_WHO_GROUP;
+        i++;
+        break;
+      case L'o':
+        action.who |= CHMOD_WHO_OTHER;
+        i++;
+        break;
+      default:
+        state = PARSE_MODE_ACTION_OP_STATE;
+      } // WHO switch
+    }
+    else if (state == PARSE_MODE_ACTION_OP_STATE)
+    {
+      switch (c)
+      {
+      case L'+':
+        action.op = CHMOD_OP_PLUS;
+        break;
+      case L'-':
+        action.op = CHMOD_OP_MINUS;
+        break;
+      case L'=':
+        action.op = CHMOD_OP_EQUAL;
+        break;
+      default:
+        fwprintf(stderr, L"Invalid mode: '%s'\n", modeString);
+        FreeActions(*pActions);
+        return FALSE;
+      } // OP switch
+      i++;
+      state = PARSE_MODE_ACTION_PERM_STATE;
+    }
+    else if (state == PARSE_MODE_ACTION_PERM_STATE)
+    {
+      switch (c)
+      {
+      case L'r':
+        action.perm |= CHMOD_PERM_R;
+        i++;
+        break;
+      case L'w':
+        action.perm |= CHMOD_PERM_W;
+        i++;
+        break;
+      case L'x':
+        action.perm |= CHMOD_PERM_X;
+        i++;
+        break;
+      case L'X':
+        action.perm |= CHMOD_PERM_LX;
+        i++;
+        break;
+      default:
+        state = PARSE_MODE_ACTION_REF_STATE;
+      } // PERM switch
+    }
+    else if (state == PARSE_MODE_ACTION_REF_STATE)
+    {
+      switch (c)
+      {
+      case L'u':
+        action.ref = CHMOD_WHO_USER;
+        i++;
+        break;
+      case L'g':
+        action.ref = CHMOD_WHO_GROUP;
+        i++;
+        break;
+      case L'o':
+        action.ref = CHMOD_WHO_OTHER;
+        i++;
+        break;
+      default:
+        state = PARSE_MODE_ACTION_END_STATE;
+      } // REF switch
+    }
+    else if (state == PARSE_MODE_ACTION_END_STATE)
+    {
+      switch (c)
+      {
+      case NULL:
+      case L',':
+        i++;
+      case L'+':
+      case L'-':
+      case L'=':
+        state = PARSE_MODE_ACTION_WHO_STATE;
+
+        // Append the current action to the end of the linked list
+        //
+        assert(actionsEnd == NULL);
+        // Allocate memory
+        actionsEnd = (PMODE_CHANGE_ACTION) LocalAlloc(LPTR,
+          sizeof(MODE_CHANGE_ACTION));
+        if (actionsEnd == NULL)
+        {
+          ReportErrorCode(L"LocalAlloc", GetLastError());
+          FreeActions(*pActions);
+          return FALSE;
+        }
+        if (action.who == CHMOD_WHO_NONE) action.who = CHMOD_WHO_ALL;
+        // Copy the action to the new node
+        *actionsEnd = action;
+        // Append to the last node in the linked list
+        if (actionsLast != NULL) actionsLast->next_action = actionsEnd;
+        // pActions should point to the head of the linked list
+        if (*pActions == NULL) *pActions = actionsEnd;
+        // Update the two pointers to point to the last node and the tail
+        actionsLast = actionsEnd;
+        actionsEnd = actionsLast->next_action;
+
+        // Reset action
+        //
+        lastWho = action.who;
+        action = INIT_MODE_CHANGE_ACTION;
+        if (c != L',')
+        {
+          action.who = lastWho;
+        }
+
+        break;
+      default:
+        fwprintf(stderr, L"Invalid mode: '%s'\n", modeString);
+        FreeActions(*pActions);
+        return FALSE;
+      } // END switch
+    }
+  } // while
+  return TRUE;
+}
+
+//----------------------------------------------------------------------------
+// Function: ParseOctalMode
+//
+// Description:
+//	Convert the 3 or 4 digits Unix mask string into the binary representation
+//  of the Unix access mask, i.e. 9 bits each an indicator of the permission
+//  of 'rwxrwxrwx', i.e. user's, group's, and owner's read, write, and
+//  execute/search permissions.
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//	none
+//
+static BOOL ParseOctalMode(LPCWSTR tsMask, USHORT *uMask)
+{
+  size_t tsMaskLen = 0;
+  DWORD i;
+  LONG l;
+  WCHAR *end;
+
+  if (uMask == NULL)
+    return FALSE;
+
+  if (FAILED(StringCchLengthW(tsMask, STRSAFE_MAX_CCH, &tsMaskLen)))
+    return FALSE;
+
+  if (tsMaskLen != 4 && tsMaskLen != 3)
+  {
+    return FALSE;
+  }
+
+  for (i = 0; i < tsMaskLen; i++)
+  {
+    if (!(tsMask[tsMaskLen - i - 1] >= L'0' &&
+      tsMask[tsMaskLen - i - 1] <= L'7'))
+      return FALSE;
+  }
+
+  errno = 0;
+  if (tsMaskLen == 4)
+    // Windows does not have any equivalent of setuid/setgid and sticky bit.
+    // So the first bit is omitted for the 4 digit octal mode case.
+    //
+    l = wcstol(tsMask + 1, &end, 8);
+  else
+    l = wcstol(tsMask, &end, 8);
+
+  if (errno || l > 0x0777 || l < 0 || *end != 0)
+  {
+    return FALSE;
+  }
+
+  *uMask = (USHORT) l;
+
+  return TRUE;
+}
+
+//----------------------------------------------------------------------------
+// Function: GetWindowsAccessMask
+//
+// Description:
+//	Get the Windows AccessMask for user, group and everyone based on the Unix
+//  permission mask
+//
+// Returns:
+//	none
+//
+// Notes:
+//	none
+//
+static void GetWindowsAccessMask(USHORT unixMask,
+  ACCESS_MASK *userAllow,
+  ACCESS_MASK *userDeny,
+  ACCESS_MASK *groupAllow,
+  ACCESS_MASK *groupDeny,
+  ACCESS_MASK *otherAllow)
+{
+  assert (userAllow != NULL && userDeny != NULL &&
+    groupAllow != NULL && groupDeny != NULL &&
+    otherAllow != NULL);
+
+  *userAllow = WinMasks[WIN_ALL] | WinMasks[WIN_OWNER_SE];
+  if ((unixMask & UX_U_READ) == UX_U_READ)
+    *userAllow |= WinMasks[WIN_READ];
+
+  if ((unixMask & UX_U_WRITE) == UX_U_WRITE)
+    *userAllow |= WinMasks[WIN_WRITE];
+
+  if ((unixMask & UX_U_EXECUTE) == UX_U_EXECUTE)
+    *userAllow |= WinMasks[WIN_EXECUTE];
+
+  *userDeny = 0;
+  if ((unixMask & UX_U_READ) != UX_U_READ &&
+    ((unixMask & UX_G_READ) == UX_G_READ ||
+    (unixMask & UX_O_READ) == UX_O_READ))
+    *userDeny |= WinMasks[WIN_READ];
+
+  if ((unixMask & UX_U_WRITE) != UX_U_WRITE &&
+    ((unixMask & UX_G_WRITE) == UX_G_WRITE ||
+    (unixMask & UX_O_WRITE) == UX_O_WRITE))
+    *userDeny |= WinMasks[WIN_WRITE];
+
+  if ((unixMask & UX_U_EXECUTE) != UX_U_EXECUTE &&
+    ((unixMask & UX_G_EXECUTE) == UX_G_EXECUTE ||
+    (unixMask & UX_O_EXECUTE) == UX_O_EXECUTE))
+    *userDeny |= WinMasks[WIN_EXECUTE];
+
+  *groupAllow = WinMasks[WIN_ALL];
+  if ((unixMask & UX_G_READ) == UX_G_READ)
+    *groupAllow |= FILE_GENERIC_READ;
+
+  if ((unixMask & UX_G_WRITE) == UX_G_WRITE)
+    *groupAllow |= WinMasks[WIN_WRITE];
+
+  if ((unixMask & UX_G_EXECUTE) == UX_G_EXECUTE)
+    *groupAllow |= WinMasks[WIN_EXECUTE];
+
+  *groupDeny = 0;
+  if ((unixMask & UX_G_READ) != UX_G_READ &&
+    (unixMask & UX_O_READ) == UX_O_READ)
+    *groupDeny |= WinMasks[WIN_READ];
+
+  if ((unixMask & UX_G_WRITE) != UX_G_WRITE &&
+    (unixMask & UX_O_WRITE) == UX_O_WRITE)
+    *groupDeny |= WinMasks[WIN_WRITE];
+
+  if ((unixMask & UX_G_EXECUTE) != UX_G_EXECUTE &&
+    (unixMask & UX_O_EXECUTE) == UX_O_EXECUTE)
+    *groupDeny |= WinMasks[WIN_EXECUTE];
+
+  *otherAllow = WinMasks[WIN_ALL];
+  if ((unixMask & UX_O_READ) == UX_O_READ)
+    *otherAllow |= WinMasks[WIN_READ];
+
+  if ((unixMask & UX_O_WRITE) == UX_O_WRITE)
+    *otherAllow |= WinMasks[WIN_WRITE];
+
+  if ((unixMask & UX_O_EXECUTE) == UX_O_EXECUTE)
+    *otherAllow |= WinMasks[WIN_EXECUTE];
+}
+
+//----------------------------------------------------------------------------
+// Function: GetWindowsDACLs
+//
+// Description:
+//	Get the Windows DACs based the Unix access mask
+//
+// Returns:
+//	TRUE: on success
+//  FALSE: otherwise
+//
+// Notes:
+//	none
+//
+static BOOL GetWindowsDACLs(__in USHORT unixMask,
+  __in PSID pOwnerSid, __in PSID pGroupSid, __out PACL *ppNewDACL)
+{
+  DWORD winUserAccessDenyMask;
+  DWORD winUserAccessAllowMask;
+  DWORD winGroupAccessDenyMask;
+  DWORD winGroupAccessAllowMask;
+  DWORD winOtherAccessAllowMask;
+
+  PSID pEveryoneSid = NULL;
+
+  PACL pNewDACL = NULL;
+  DWORD dwNewAclSize = 0;
+
+  SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
+
+  BOOL ret = FALSE;
+
+  GetWindowsAccessMask(unixMask,
+    &winUserAccessAllowMask, &winUserAccessDenyMask,
+    &winGroupAccessAllowMask, &winGroupAccessDenyMask,
+    &winOtherAccessAllowMask);
+
+  // Create a well-known SID for the Everyone group
+  //
+  if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
+    SECURITY_WORLD_RID,
+    0, 0, 0, 0, 0, 0, 0,
+    &pEveryoneSid))
+  {
+    return FALSE;
+  }
+
+  // Create the new DACL
+  //
+  dwNewAclSize = sizeof(ACL);
+  dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
+    GetLengthSid(pOwnerSid) - sizeof(DWORD);
+  if (winUserAccessDenyMask)
+    dwNewAclSize += sizeof(ACCESS_DENIED_ACE) +
+    GetLengthSid(pOwnerSid) - sizeof(DWORD);
+  dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
+    GetLengthSid(pGroupSid) - sizeof(DWORD);
+  if (winGroupAccessDenyMask)
+    dwNewAclSize += sizeof(ACCESS_DENIED_ACE) +
+    GetLengthSid(pGroupSid) - sizeof(DWORD);
+  dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
+    GetLengthSid(pEveryoneSid) - sizeof(DWORD);
+  pNewDACL = (PACL)LocalAlloc(LPTR, dwNewAclSize);
+  if (pNewDACL == NULL)
+  {
+    ReportErrorCode(L"LocalAlloc", GetLastError());
+    goto GetWindowsDACLsEnd;
+  }
+  if (!InitializeAcl(pNewDACL, dwNewAclSize, ACL_REVISION))
+  {
+    ReportErrorCode(L"InitializeAcl", GetLastError());
+    goto GetWindowsDACLsEnd;
+  }
+
+  if (winUserAccessDenyMask &&
+    !AddAccessDeniedAce(pNewDACL, ACL_REVISION,
+    winUserAccessDenyMask, pOwnerSid))
+  {
+    ReportErrorCode(L"AddAccessDeniedAce", GetLastError());
+    goto GetWindowsDACLsEnd;
+  }
+  if (!AddAccessAllowedAce(pNewDACL, ACL_REVISION,
+    winUserAccessAllowMask, pOwnerSid))
+  {
+    ReportErrorCode(L"AddAccessAllowedAce", GetLastError());
+    goto GetWindowsDACLsEnd;
+  }
+  if (winGroupAccessDenyMask &&
+    !AddAccessDeniedAce(pNewDACL, ACL_REVISION,
+    winGroupAccessDenyMask, pGroupSid))
+  {
+    ReportErrorCode(L"AddAccessDeniedAce", GetLastError());
+    goto GetWindowsDACLsEnd;
+  }
+  if (!AddAccessAllowedAce(pNewDACL, ACL_REVISION,
+    winGroupAccessAllowMask, pGroupSid))
+  {
+    ReportErrorCode(L"AddAccessAllowedAce", GetLastError());
+    goto GetWindowsDACLsEnd;
+  }
+  if (!AddAccessAllowedAce(pNewDACL, ACL_REVISION,
+    winOtherAccessAllowMask, pEveryoneSid))
+  {
+    ReportErrorCode(L"AddAccessAllowedAce", GetLastError());
+    goto GetWindowsDACLsEnd;
+  }
+
+  *ppNewDACL = pNewDACL;
+  ret = TRUE;
+
+GetWindowsDACLsEnd:
+  if (pEveryoneSid) FreeSid(pEveryoneSid);
+  if (!ret) LocalFree(pNewDACL);
+  
+  return ret;
+}
+
+void ChmodUsage(LPCWSTR program)
+{
+  fwprintf(stdout, L"\
+Usage: %s [OPTION] OCTAL-MODE [FILE]\n\
+   or: %s [OPTION] MODE [FILE]\n\
+Change the mode of the FILE to MODE.\n\
+\n\
+   -R: change files and directories recursively\n\
+\n\
+Each MODE is of the form '[ugoa]*([-+=]([rwxX]*|[ugo]))+'.\n",
+program, program);
+}
\ No newline at end of file

Added: hadoop/common/branches/branch-1-win/src/winutils/chown.c
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1-win/src/winutils/chown.c?rev=1344527&view=auto
==============================================================================
--- hadoop/common/branches/branch-1-win/src/winutils/chown.c (added)
+++ hadoop/common/branches/branch-1-win/src/winutils/chown.c Thu May 31 01:52:15 2012
@@ -0,0 +1,488 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.
+ */
+
+#include "common.h"
+
+//----------------------------------------------------------------------------
+// Function: GetNewAclSize
+//
+// Description:
+//	Compute the extra size of the new ACL if we replace the old owner Sid with
+//  the new owner Sid.
+//
+// Returns:
+//	The extra size needed for the new ACL compared with the ACL passed in. If
+//  the value is negative, it means the size of the new ACL could be reduced.
+//
+// Notes:
+//
+static BOOL GetNewAclSizeDelta(__in PACL pDACL,
+  __in PSID pOldOwnerSid, __in PSID pNewOwnerSid, __out PLONG pDelta)
+{
+  PVOID pAce = NULL;
+  DWORD i;
+  PSID aceSid = NULL;
+  ACE_HEADER *aceHeader = NULL;
+  PACCESS_ALLOWED_ACE accessAllowedAce = NULL;
+  PACCESS_DENIED_ACE accessDenieddAce = NULL;
+
+  assert(pDACL != NULL && pNewOwnerSid != NULL &&
+    pOldOwnerSid != NULL && pDelta != NULL);
+
+  *pDelta = 0;
+  for (i = 0; i < pDACL->AceCount; i++)
+  {
+    if (!GetAce(pDACL, i, &pAce))
+    {
+      ReportErrorCode(L"GetAce", GetLastError());
+      return FALSE;
+    }
+
+    aceHeader = (ACE_HEADER *) pAce;
+    if (aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
+    {
+      accessAllowedAce = (PACCESS_ALLOWED_ACE) pAce;
+      aceSid = (PSID) &accessAllowedAce->SidStart;
+    }
+    else if (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE)
+    {
+      accessDenieddAce = (PACCESS_DENIED_ACE) pAce;
+      aceSid = (PSID) &accessDenieddAce->SidStart;
+    }
+    else
+    {
+      continue;
+    }
+
+    if (EqualSid(pOldOwnerSid, aceSid))
+    {
+      *pDelta += GetLengthSid(pNewOwnerSid) - GetLengthSid(pOldOwnerSid);
+    }
+  }
+
+  return TRUE;
+}
+
+//----------------------------------------------------------------------------
+// Function: AddNewAce
+//
+// Description:
+//	Add an Ace of new owner to the new ACL
+//
+// Returns:
+//	TRUE: on success
+//
+// Notes:
+//  The Ace type should be either ACCESS_ALLOWED_ACE or ACCESS_DENIED_ACE
+//
+static BOOL AddNewAce(PACL pNewDACL, PVOID pOldAce,
+  PSID pOwnerSid, PSID pUserSid)
+{
+  PVOID pNewAce = NULL;
+  DWORD newAceSize = 0;
+
+  assert(pNewDACL != NULL && pOldAce != NULL &&
+    pOwnerSid != NULL && pUserSid != NULL);
+  assert(((PACE_HEADER)pOldAce)->AceType == ACCESS_ALLOWED_ACE_TYPE ||
+    ((PACE_HEADER)pOldAce)->AceType == ACCESS_DENIED_ACE_TYPE);
+
+  newAceSize =  ((PACE_HEADER)pOldAce)->AceSize +
+    GetLengthSid(pUserSid) - GetLengthSid(pOwnerSid);
+  pNewAce = LocalAlloc(LPTR, newAceSize);
+  if (pNewAce == NULL)
+  {
+    ReportErrorCode(L"LocalAlloc", GetLastError());
+    return FALSE;
+  }
+
+  ((PACE_HEADER)pNewAce)->AceType = ((PACE_HEADER) pOldAce)->AceType;
+  ((PACE_HEADER)pNewAce)->AceFlags = ((PACE_HEADER) pOldAce)->AceFlags;
+  ((PACE_HEADER)pNewAce)->AceSize = (WORD) newAceSize;
+
+  if (((PACE_HEADER)pOldAce)->AceType == ACCESS_ALLOWED_ACE_TYPE)
+  {
+    ((PACCESS_ALLOWED_ACE)pNewAce)->Mask = ((PACCESS_ALLOWED_ACE)pOldAce)->Mask;
+    if (!CopySid(GetLengthSid(pUserSid),
+      &((PACCESS_ALLOWED_ACE) pNewAce)->SidStart, pUserSid))
+    {
+      ReportErrorCode(L"CopySid", GetLastError());
+      LocalFree(pNewAce);
+      return FALSE;
+    }
+  }
+  else
+  {
+    ((PACCESS_DENIED_ACE)pNewAce)->Mask = ((PACCESS_DENIED_ACE)pOldAce)->Mask;
+    if (!CopySid(GetLengthSid(pUserSid),
+      &((PACCESS_DENIED_ACE) pNewAce)->SidStart, pUserSid))
+    {
+      ReportErrorCode(L"CopySid", GetLastError());
+      LocalFree(pNewAce);
+      return FALSE;
+    }
+  }
+
+  if (!AddAce(pNewDACL, ACL_REVISION, MAXDWORD,
+    pNewAce, ((PACE_HEADER)pNewAce)->AceSize))
+  {
+    ReportErrorCode(L"AddAce", GetLastError());
+    LocalFree(pNewAce);
+    return FALSE;
+  }
+
+  LocalFree(pNewAce);
+  return TRUE;
+}
+
+//----------------------------------------------------------------------------
+// Function: CreateDaclForNewOwner
+//
+// Description:
+//	Create a new DACL for the new owner
+//
+// Returns:
+//	TRUE: on success
+//
+// Notes:
+//  Caller needs to destroy the memory of the new DACL by calling LocalFree()
+//
+static BOOL CreateDaclForNewOwner(
+  __in PACL pDACL,
+  __in_opt PSID pOldOwnerSid,
+  __in_opt PSID pNewOwnerSid,
+  __in_opt PSID pOldGroupSid,
+  __in_opt PSID pNewGroupSid,
+  __out PACL *ppNewDACL)
+{
+  PSID aceSid = NULL;
+  PACE_HEADER aceHeader = NULL;
+  PACCESS_ALLOWED_ACE accessAllowedAce = NULL;
+  PACCESS_DENIED_ACE accessDenieddAce = NULL;
+  PVOID pAce = NULL;
+  ACL_SIZE_INFORMATION aclSizeInfo;
+  LONG delta = 0;
+  DWORD dwNewAclSize = 0;
+  DWORD dwRtnCode = 0;
+  DWORD i;
+
+  assert(pDACL != NULL && ppNewDACL != NULL);
+  assert(pOldOwnerSid != NULL && pOldGroupSid != NULL);
+  assert(pNewOwnerSid != NULL || pNewOwnerSid != NULL);
+
+  if (!GetAclInformation(pDACL, (LPVOID)&aclSizeInfo,
+    sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
+  {
+    ReportErrorCode(L"GetAclInformation", GetLastError());
+    return FALSE;
+  }
+
+  dwNewAclSize = aclSizeInfo.AclBytesInUse + aclSizeInfo.AclBytesFree;
+
+  delta = 0;
+  if (pNewOwnerSid != NULL &&
+    !GetNewAclSizeDelta(pDACL, pOldOwnerSid, pNewOwnerSid, &delta))
+  {
+    return FALSE;
+  }
+  dwNewAclSize += delta;
+
+  delta = 0;
+  if (pNewGroupSid != NULL &&
+    !GetNewAclSizeDelta(pDACL, pOldGroupSid, pNewGroupSid, &delta))
+  {
+    return FALSE;
+  }
+  dwNewAclSize += delta;
+
+  *ppNewDACL = (PACL)LocalAlloc(LPTR, dwNewAclSize);
+  if (*ppNewDACL == NULL)
+  {
+    ReportErrorCode(L"LocalAlloc", GetLastError());
+    return FALSE;
+  }
+
+  if (!InitializeAcl(*ppNewDACL, dwNewAclSize, ACL_REVISION))
+  {
+    ReportErrorCode(L"InitializeAcl", GetLastError());
+    return FALSE;
+  }
+
+  // Go through the DACL to change permissions
+  //
+  for (i = 0; i < pDACL->AceCount; i++)
+  {
+    if (!GetAce(pDACL, i, &pAce))
+    {
+      ReportErrorCode(L"GetAce", GetLastError());
+      return FALSE;
+    }
+
+    aceHeader = (PACE_HEADER) pAce;
+    aceSid = NULL;
+    if (aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
+    {
+      accessAllowedAce = (PACCESS_ALLOWED_ACE) pAce;
+      aceSid = (PSID) &accessAllowedAce->SidStart;
+    }
+    else if (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE)
+    {
+      accessDenieddAce = (PACCESS_DENIED_ACE) pAce;
+      aceSid = (PSID) &accessDenieddAce->SidStart;
+    }
+
+    if (aceSid != NULL)
+    {
+      if (pNewOwnerSid != NULL && EqualSid(pOldOwnerSid, aceSid))
+      {
+        if (!AddNewAce(*ppNewDACL, pAce, pOldOwnerSid, pNewOwnerSid))
+          return FALSE;
+        else
+          continue;
+      }
+      else if (pNewGroupSid != NULL && EqualSid(pOldGroupSid, aceSid))
+      {
+        if (!AddNewAce(*ppNewDACL, pAce, pOldGroupSid, pNewGroupSid))
+          return FALSE;
+        else
+          continue;
+      }
+    }
+
+    // At this point, either:
+    // 1. The Ace is not of type ACCESS_ALLOWED_ACE or ACCESS_DENIED_ACE;
+    // 2. The Ace does not belong to the owner
+    // For both cases, we just add the oringal Ace to the new ACL.
+    //
+    if (!AddAce(*ppNewDACL, ACL_REVISION, MAXDWORD, pAce, aceHeader->AceSize))
+    {
+      ReportErrorCode(L"AddAce", dwRtnCode);
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+
+//----------------------------------------------------------------------------
+// Function: Chown
+//
+// Description:
+//	The main method for chown command
+//
+// Returns:
+//	0: on success
+//
+// Notes:
+//
+//
+int Chown(int argc, wchar_t *argv[])
+{
+  LPWSTR pathName = NULL;
+  LPWSTR longPathName = NULL;
+
+  LPWSTR ownerInfo = NULL;
+
+  LPWSTR colonPos = NULL;
+
+  LPWSTR userName = NULL;
+  size_t userNameLen = 0;
+
+  LPWSTR groupName = NULL;
+  size_t groupNameLen = 0;
+
+  PSID pNewOwnerSid = NULL;
+  PSID pNewGroupSid = NULL;
+
+  PACL pDACL = NULL;
+  PACL pNewDACL = NULL;
+
+  PSID pOldOwnerSid = NULL;
+  PSID pOldGroupSid = NULL;
+
+  PSECURITY_DESCRIPTOR pSD = NULL;
+
+  SECURITY_INFORMATION securityInformation;
+
+  DWORD dwRtnCode = 0;
+
+  int ret = EXIT_FAILURE;
+
+  if (argc >= 3)
+  {
+    ownerInfo = argv[1];
+    pathName = argv[2];
+  }
+  else
+  {
+    fwprintf(stderr, L"Incorrect command line arguments.\n\n");
+    ChownUsage(argv[0]);
+    return ret;
+  }
+
+  // Parsing the owner name
+  //
+  if ((colonPos = wcschr(ownerInfo, L':')) != NULL)
+  {
+    if (colonPos - ownerInfo != 0)
+    {
+      // Length includes NULL terminator
+      userNameLen = colonPos - ownerInfo + 1;
+      userName = (LPTSTR)LocalAlloc(LPTR, userNameLen * sizeof(WCHAR));
+      if (userName == NULL)
+      {
+        ReportErrorCode(L"LocalAlloc", GetLastError());
+        goto ChownEnd;
+      }
+      if (FAILED(StringCchCopyNW(userName, userNameLen,
+        ownerInfo, userNameLen - 1)))
+        goto ChownEnd;
+    }
+
+    if (colonPos + 1 != NULL)
+    {
+      // Length includes NULL terminator
+      groupNameLen = wcslen(ownerInfo) - (colonPos - ownerInfo) + 1;
+      groupName = (LPTSTR)LocalAlloc(LPTR, groupNameLen * sizeof(WCHAR));
+      if (groupName == NULL)
+      {
+        ReportErrorCode(L"LocalAlloc", GetLastError());
+        goto ChownEnd;
+      }
+      if (FAILED(StringCchCopyNW(groupName, groupNameLen,
+        colonPos + 1, groupNameLen)))
+        goto ChownEnd;
+    }
+  }
+  else
+  {
+    // Length includes NULL terminator
+    userNameLen = wcslen(ownerInfo) + 1;
+    userName = (LPWSTR)LocalAlloc(LPTR, userNameLen * sizeof(WCHAR));
+    if (userName == NULL)
+    {
+      ReportErrorCode(L"LocalAlloc", GetLastError());
+      goto ChownEnd;
+    }
+    if (FAILED(StringCchCopyNW(userName, userNameLen, ownerInfo, userNameLen)))
+      goto ChownEnd;
+  }
+
+  if ((userName == NULL || wcslen(userName) == 0) &&
+    (groupName == NULL || wcslen(groupName) == 0))
+  {
+    fwprintf(stderr, L"User name and group name cannot both be empty.");
+    goto ChownEnd;
+  }
+
+  if (userName != NULL && !GetSidFromAcctNameW(userName, &pNewOwnerSid))
+  {
+    fwprintf(stderr, L"Invalid user name: %s\n", userName);
+    goto ChownEnd;
+  }
+
+  if (groupName != NULL && !GetSidFromAcctNameW(groupName, &pNewGroupSid))
+  {
+    fwprintf(stderr, L"Invalid group name: %s\n", groupName);
+    goto ChownEnd;
+  }
+
+  if (wcslen(pathName) == 0 || wcsspn(pathName, L"/?|><:*\"") != 0)
+  {
+    fwprintf(stderr, L"Incorrect file name format: %s\n", pathName);
+    goto ChownEnd;
+  }
+
+  // Convert the path the the long path
+  //
+  dwRtnCode = ConvertToLongPath(pathName, &longPathName);
+  if (dwRtnCode != ERROR_SUCCESS)
+  {
+    ReportErrorCode(L"ConvertToLongPath", dwRtnCode);
+    goto ChownEnd;
+  }
+
+  // Get a pointer to the existing owner information and DACL
+  //
+  dwRtnCode = GetNamedSecurityInfoW(
+    longPathName,
+    SE_FILE_OBJECT, 
+    OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+    DACL_SECURITY_INFORMATION,
+    &pOldOwnerSid,
+    &pOldGroupSid,
+    &pDACL,
+    NULL,
+    &pSD);
+  if (dwRtnCode != ERROR_SUCCESS)
+  {
+    ReportErrorCode(L"GetNamedSecurityInfo", dwRtnCode);
+    goto ChownEnd;
+  }
+
+  // Create the new DACL
+  //
+  if (!CreateDaclForNewOwner(pDACL,
+    pOldOwnerSid, pNewOwnerSid,
+    pOldGroupSid, pNewGroupSid,
+    &pNewDACL))
+  {
+    goto ChownEnd;
+  }
+
+  // Set the owner and DACLs in the object's security descriptor. Use
+  // PROTECTED_DACL_SECURITY_INFORMATION flag to remove permission inheritance
+  //
+  securityInformation =
+    DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
+  if (pNewOwnerSid != NULL) securityInformation |= OWNER_SECURITY_INFORMATION;
+  if (pNewGroupSid != NULL) securityInformation |= GROUP_SECURITY_INFORMATION;
+  dwRtnCode = SetNamedSecurityInfoW(
+    longPathName,
+    SE_FILE_OBJECT,
+    securityInformation,
+    pNewOwnerSid,
+    pNewGroupSid,
+    pNewDACL,
+    NULL);
+  if (dwRtnCode != ERROR_SUCCESS)
+  {
+    ReportErrorCode(L"SetNamedSecurityInfo", dwRtnCode);
+    goto ChownEnd;
+  }
+
+  ret = EXIT_SUCCESS;
+
+ChownEnd:
+  LocalFree(userName);
+  LocalFree(groupName);
+  LocalFree(pNewOwnerSid);
+  LocalFree(pNewGroupSid);
+  LocalFree(pNewDACL);
+  LocalFree(pSD);
+  LocalFree(longPathName);
+
+  return ret;
+}
+
+void ChownUsage(LPCWSTR program)
+{
+  fwprintf(stdout, L"\
+Usage: %s [OWNER][:[GROUP]] [FILE]\n\
+Change the owner and/or group of the FILE to OWNER and/or GROUP.\n",
+program);
+}



Mime
View raw message