You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by om...@apache.org on 2011/03/04 04:36:23 UTC

svn commit: r1077057 - in /hadoop/common/branches/branch-0.20-security-patches/src: core/org/apache/hadoop/fs/ core/org/apache/hadoop/fs/permission/ docs/src/documentation/content/xdocs/ test/org/apache/hadoop/fs/permission/

Author: omalley
Date: Fri Mar  4 03:36:22 2011
New Revision: 1077057

URL: http://svn.apache.org/viewvc?rev=1077057&view=rev
Log:
commit 70111423f7cb44eeae478800213076dd4bcdd85f
Author: Suresh Srinivas <su...@yahoo-inc.com>
Date:   Fri Nov 20 17:01:21 2009 -0800

    HADOOP:6234 from https://issues.apache.org/jira/secure/attachment/12425635/COMMON-6234.rel20.1.patch
    
    +++ b/YAHOO-CHANGES.txt
    +    HADOOP-6234. Add new option dfs.umaskmode to set umask in configuration
    +    to use octal or symbolic instead of decimal. (Jakob Homan via suresh)

Added:
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/ChmodParser.java
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/PermissionParser.java
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/UmaskParser.java
Modified:
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/FsShellPermissions.java
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/FsPermission.java
    hadoop/common/branches/branch-0.20-security-patches/src/docs/src/documentation/content/xdocs/hdfs_permissions_guide.xml
    hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/fs/permission/TestFsPermission.java

Modified: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/FsShellPermissions.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/FsShellPermissions.java?rev=1077057&r1=1077056&r2=1077057&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/FsShellPermissions.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/FsShellPermissions.java Fri Mar  4 03:36:22 2011
@@ -23,6 +23,7 @@ import java.util.regex.Pattern;
 
 import org.apache.hadoop.fs.FsShell.CmdHandler;
 import org.apache.hadoop.fs.permission.FsPermission;
+import org.apache.hadoop.fs.permission.ChmodParser;
 
 
 /**
@@ -37,146 +38,33 @@ class FsShellPermissions {
    * chmod shell command. The main restriction is that we recognize only rwxX.
    * To reduce errors we also enforce 3 digits for octal mode.
    */  
-  private static Pattern chmodNormalPattern = 
-             Pattern.compile("\\G\\s*([ugoa]*)([+=-]+)([rwxX]+)([,\\s]*)\\s*");
-  private static Pattern chmodOctalPattern =
-            Pattern.compile("^\\s*[+]?([0-7]{3})\\s*$");
   
   static String CHMOD_USAGE = 
                             "-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...";
 
+  private static  ChmodParser pp;
+  
   private static class ChmodHandler extends CmdHandler {
 
-    private short userMode, groupMode, othersMode;
-    private char userType = '+', groupType = '+', othersType='+';
-
-    private void applyNormalPattern(String modeStr, Matcher matcher)
-                                    throws IOException {
-      boolean commaSeperated = false;
-
-      for(int i=0; i < 1 || matcher.end() < modeStr.length(); i++) {
-        if (i>0 && (!commaSeperated || !matcher.find())) {
-          patternError(modeStr);
-        }
-
-        /* groups : 1 : [ugoa]*
-         *          2 : [+-=]
-         *          3 : [rwxX]+
-         *          4 : [,\s]*
-         */
-
-        String str = matcher.group(2);
-        char type = str.charAt(str.length() - 1);
-
-        boolean user, group, others;
-        user = group = others = false;
-
-        for(char c : matcher.group(1).toCharArray()) {
-          switch (c) {
-          case 'u' : user = true; break;
-          case 'g' : group = true; break;
-          case 'o' : others = true; break;
-          case 'a' : break;
-          default  : throw new RuntimeException("Unexpected");          
-          }
-        }
-
-        if (!(user || group || others)) { // same as specifying 'a'
-          user = group = others = true;
-        }
-
-        short  mode = 0;
-        for(char c : matcher.group(3).toCharArray()) {
-          switch (c) {
-          case 'r' : mode |= 4; break;
-          case 'w' : mode |= 2; break;
-          case 'x' : mode |= 1; break;
-          case 'X' : mode |= 8; break;
-          default  : throw new RuntimeException("Unexpected");
-          }
-        }
-
-        if ( user ) {
-          userMode = mode;
-          userType = type;
-        }
-
-        if ( group ) {
-          groupMode = mode;
-          groupType = type;
-        }
-
-        if ( others ) {
-          othersMode = mode;
-          othersType = type;
-        }
-
-        commaSeperated = matcher.group(4).contains(",");
-      }
-    }
-
-    private void applyOctalPattern(String modeStr, Matcher matcher) {
-      userType = groupType = othersType = '=';
-      String str = matcher.group(1);
-      userMode = Short.valueOf(str.substring(0, 1));
-      groupMode = Short.valueOf(str.substring(1, 2));
-      othersMode = Short.valueOf(str.substring(2, 3));      
-    }
-
-    private void patternError(String mode) throws IOException {
-      throw new IOException("chmod : mode '" + mode + 
-                            "' does not match the expected pattern.");      
-    }
-
     ChmodHandler(FileSystem fs, String modeStr) throws IOException {
       super("chmod", fs);
-      Matcher matcher = null;
-
-      if ((matcher = chmodNormalPattern.matcher(modeStr)).find()) {
-        applyNormalPattern(modeStr, matcher);
-      } else if ((matcher = chmodOctalPattern.matcher(modeStr)).matches()) {
-        applyOctalPattern(modeStr, matcher);
-      } else {
-        patternError(modeStr);
+      try {
+        pp = new ChmodParser(modeStr);
+      } catch(IllegalArgumentException iea) {
+        patternError(iea.getMessage());
       }
     }
 
-    private int applyChmod(char type, int mode, int existing, boolean exeOk) {
-      boolean capX = false;
-
-      if ((mode&8) != 0) { // convert X to x;
-        capX = true;
-        mode &= ~8;
-        mode |= 1;
-      }
-
-      switch (type) {
-      case '+' : mode = mode | existing; break;
-      case '-' : mode = (~mode) & existing; break;
-      case '=' : break;
-      default  : throw new RuntimeException("Unexpected");      
-      }
-
-      // if X is specified add 'x' only if exeOk or x was already set.
-      if (capX && !exeOk && (mode&1) != 0 && (existing&1) == 0) {
-        mode &= ~1; // remove x
-      }
-
-      return mode;
+    private void patternError(String mode) throws IOException {
+     throw new IOException("chmod : mode '" + mode + 
+         "' does not match the expected pattern.");      
     }
-
+    
     @Override
     public void run(FileStatus file, FileSystem srcFs) throws IOException {
-      FsPermission perms = file.getPermission();
-      int existing = perms.toShort();
-      boolean exeOk = file.isDir() || (existing & 0111) != 0;
-      int newperms = ( applyChmod(userType, userMode, 
-                                  (existing>>>6)&7, exeOk) << 6 |
-                       applyChmod(groupType, groupMode, 
-                                  (existing>>>3)&7, exeOk) << 3 |
-                       applyChmod(othersType, othersMode, existing&7, exeOk) );
+      int newperms = pp.applyNewPermission(file);
 
-      if (existing != newperms) {
+      if (file.getPermission().toShort() != newperms) {
         try {
           srcFs.setPermission(file.getPath(), 
                                 new FsPermission((short)newperms));

Added: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/ChmodParser.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/ChmodParser.java?rev=1077057&view=auto
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/ChmodParser.java (added)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/ChmodParser.java Fri Mar  4 03:36:22 2011
@@ -0,0 +1,51 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.fs.permission;
+
+import java.util.regex.Pattern;
+
+import org.apache.hadoop.fs.FileStatus;
+
+/**
+ * Parse a permission mode passed in from a chmod command and apply that
+ * mode against an existing file.
+ */
+public class ChmodParser extends PermissionParser {
+  private static Pattern chmodOctalPattern =
+    Pattern.compile("^\\s*[+]?([0-7]{3})\\s*$");
+  private static Pattern chmodNormalPattern =
+    Pattern.compile("\\G\\s*([ugoa]*)([+=-]+)([rwxX]+)([,\\s]*)\\s*");
+  
+  public ChmodParser(String modeStr) throws IllegalArgumentException {
+    super(modeStr, chmodNormalPattern, chmodOctalPattern);
+  }
+
+  /**
+   * Apply permission against specified file and determine what the
+   * new mode would be
+   * @param file File against which to apply mode
+   * @return File's new mode if applied.
+   */
+  public short applyNewPermission(FileStatus file) {
+    FsPermission perms = file.getPermission();
+    int existing = perms.toShort();
+    boolean exeOk = file.isDir() || (existing & 0111) != 0;
+    
+    return (short)combineModes(existing, exeOk);
+  }
+}

Modified: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/FsPermission.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/FsPermission.java?rev=1077057&r1=1077056&r2=1077057&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/FsPermission.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/FsPermission.java Fri Mar  4 03:36:22 2011
@@ -17,17 +17,23 @@
  */
 package org.apache.hadoop.fs.permission;
 
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.io.*;
-
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableFactories;
+import org.apache.hadoop.io.WritableFactory;
+
 /**
  * A class for file/directory permissions.
  */
 public class FsPermission implements Writable {
+  private static final Log LOG = LogFactory.getLog(FsPermission.class);
+  
   static final WritableFactory FACTORY = new WritableFactory() {
     public Writable newInstance() { return new FsPermission(); }
   };
@@ -154,15 +160,31 @@ public class FsPermission implements Wri
   }
 
   /** umask property label */
-  public static final String UMASK_LABEL = "dfs.umask";
+  public static final String DEPRECATED_UMASK_LABEL = "dfs.umask"; 
+  public static final String UMASK_LABEL = "dfs.umaskmode";
   public static final int DEFAULT_UMASK = 0022;
 
   /** Get the user file creation mask (umask) */
   public static FsPermission getUMask(Configuration conf) {
     int umask = DEFAULT_UMASK;
-    if (conf != null) {
-      umask = conf.getInt(UMASK_LABEL, DEFAULT_UMASK);
+    
+    // Attempt to pull value from configuration, trying new key first and then
+    // deprecated key, along with a warning, if not present
+    if(conf != null) {
+      String confUmask = conf.get(UMASK_LABEL);
+      if(confUmask != null) { // UMASK_LABEL is set
+        umask = new UmaskParser(confUmask).getUMask();
+      } else { // check for deprecated key label
+        int oldStyleValue = conf.getInt(DEPRECATED_UMASK_LABEL, Integer.MIN_VALUE);
+        if(oldStyleValue != Integer.MIN_VALUE) { // Property was set with old key
+          LOG.warn(DEPRECATED_UMASK_LABEL + " configuration key is deprecated. " +
+              "Convert to " + UMASK_LABEL + ", using octal or symbolic umask " +
+              "specifications.");
+          umask = oldStyleValue;
+        }
+      }
     }
+    
     return new FsPermission((short)umask);
   }
   /** Set the user file creation mask (umask) */

Added: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/PermissionParser.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/PermissionParser.java?rev=1077057&view=auto
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/PermissionParser.java (added)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/PermissionParser.java Fri Mar  4 03:36:22 2011
@@ -0,0 +1,178 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.fs.permission;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Base class for parsing either chmod permissions or umask permissions.
+ * Includes common code needed by either operation as implemented in
+ * UmaskParser and ChmodParser classes.
+ */
+class PermissionParser {
+  protected short userMode;
+  protected short groupMode;
+  protected short othersMode;
+  protected char userType = '+';
+  protected char groupType = '+';
+  protected char othersType = '+';
+  
+  /**
+   * Begin parsing permission stored in modeStr
+   * 
+   * @param modeStr Permission mode, either octal or symbolic
+   * @param symbolic Use-case specific symbolic pattern to match against
+   * @throws IllegalArgumentException if unable to parse modeStr
+   */
+  public PermissionParser(String modeStr, Pattern symbolic, Pattern octal) 
+       throws IllegalArgumentException {
+    Matcher matcher = null;
+
+    if ((matcher = symbolic.matcher(modeStr)).find()) {
+      applyNormalPattern(modeStr, matcher);
+    } else if ((matcher = octal.matcher(modeStr)).matches()) {
+      applyOctalPattern(modeStr, matcher);
+    } else {
+      throw new IllegalArgumentException(modeStr);
+    }
+  }
+
+  private void applyNormalPattern(String modeStr, Matcher matcher) {
+    // Are there multiple permissions stored in one chmod?
+    boolean commaSeperated = false;
+
+    for (int i = 0; i < 1 || matcher.end() < modeStr.length(); i++) {
+      if (i > 0 && (!commaSeperated || !matcher.find())) {
+        throw new IllegalArgumentException(modeStr);
+      }
+
+      /*
+       * groups : 1 : [ugoa]* 2 : [+-=] 3 : [rwxX]+ 4 : [,\s]*
+       */
+
+      String str = matcher.group(2);
+      char type = str.charAt(str.length() - 1);
+
+      boolean user, group, others;
+      user = group = others = false;
+
+      for (char c : matcher.group(1).toCharArray()) {
+        switch (c) {
+        case 'u':
+          user = true;
+          break;
+        case 'g':
+          group = true;
+          break;
+        case 'o':
+          others = true;
+          break;
+        case 'a':
+          break;
+        default:
+          throw new RuntimeException("Unexpected");
+        }
+      }
+
+      if (!(user || group || others)) { // same as specifying 'a'
+        user = group = others = true;
+      }
+
+      short mode = 0;
+
+      for (char c : matcher.group(3).toCharArray()) {
+        switch (c) {
+        case 'r':
+          mode |= 4;
+          break;
+        case 'w':
+          mode |= 2;
+          break;
+        case 'x':
+          mode |= 1;
+          break;
+        case 'X':
+          mode |= 8;
+          break;
+        default:
+          throw new RuntimeException("Unexpected");
+        }
+      }
+
+      if (user) {
+        userMode = mode;
+        userType = type;
+      }
+
+      if (group) {
+        groupMode = mode;
+        groupType = type;
+      }
+
+      if (others) {
+        othersMode = mode;
+        othersType = type;
+      }
+
+      commaSeperated = matcher.group(4).contains(",");
+    }
+  }
+
+  private void applyOctalPattern(String modeStr, Matcher matcher) {
+    userType = groupType = othersType = '=';
+
+    String str = matcher.group(1);
+    userMode = Short.valueOf(str.substring(0, 1));
+    groupMode = Short.valueOf(str.substring(1, 2));
+    othersMode = Short.valueOf(str.substring(2, 3));
+  }
+
+  protected int combineModes(int existing, boolean exeOk) {
+    return   combineModeSegments(userType, userMode,
+                (existing>>>6)&7, exeOk) << 6 |
+             combineModeSegments(groupType, groupMode,
+                (existing>>>3)&7, exeOk) << 3 |
+             combineModeSegments(othersType, othersMode, existing&7, exeOk);
+  }
+  
+  protected int combineModeSegments(char type, int mode, 
+                                    int existing, boolean exeOk) {
+    boolean capX = false;
+
+    if ((mode&8) != 0) { // convert X to x;
+      capX = true;
+      mode &= ~8;
+      mode |= 1;
+    }
+
+    switch (type) {
+    case '+' : mode = mode | existing; break;
+    case '-' : mode = (~mode) & existing; break;
+    case '=' : break;
+    default  : throw new RuntimeException("Unexpected");      
+    }
+
+    // if X is specified add 'x' only if exeOk or x was already set.
+    if (capX && !exeOk && (mode&1) != 0 && (existing&1) == 0) {
+      mode &= ~1; // remove x
+    }
+
+    return mode;
+  }
+}

Added: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/UmaskParser.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/UmaskParser.java?rev=1077057&view=auto
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/UmaskParser.java (added)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/fs/permission/UmaskParser.java Fri Mar  4 03:36:22 2011
@@ -0,0 +1,43 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.fs.permission;
+
+import java.util.regex.Pattern;
+
+/**
+ * Parse umask value provided as a string, either in octal or symbolic
+ * format and return it as a short value. Umask values are slightly
+ * different from standard modes as they cannot specify X.
+ */
+class UmaskParser extends PermissionParser {
+  private static Pattern chmodOctalPattern =
+    Pattern.compile("^\\s*[+]?([0-7]{3})\\s*$");
+  private static Pattern umaskSymbolicPattern =    /* not allow X */
+    Pattern.compile("\\G\\s*([ugoa]*)([+=-]+)([rwx]+)([,\\s]*)\\s*");
+  final short umaskMode;
+  
+  public UmaskParser(String modeStr) throws IllegalArgumentException {
+    super(modeStr, umaskSymbolicPattern, chmodOctalPattern);
+
+    umaskMode = (short)combineModes(0, false);
+  }
+
+  public short getUMask() {
+    return umaskMode;
+  }
+}

Modified: hadoop/common/branches/branch-0.20-security-patches/src/docs/src/documentation/content/xdocs/hdfs_permissions_guide.xml
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/docs/src/documentation/content/xdocs/hdfs_permissions_guide.xml?rev=1077057&r1=1077056&r2=1077057&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/docs/src/documentation/content/xdocs/hdfs_permissions_guide.xml (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/docs/src/documentation/content/xdocs/hdfs_permissions_guide.xml Fri Mar  4 03:36:22 2011
@@ -177,9 +177,9 @@ If a cluster starts with a version 0.15 
 	<dd>
 		The choice of initial mode during upgrade. The <em>x</em> permission is <em>never</em> set for files. For configuration files, the decimal value <em>511<sub>10</sub></em> may be used.
 	</dd>
-	<dt><code>dfs.umask = 022</code></dt>
+	<dt><code>dfs.umaskmode = 022</code></dt>
 	<dd>
-		The <code>umask</code> used when creating files and directories. For configuration files, the decimal value <em>18<sub>10</sub></em> may be used.
+		The <code>umask</code> used when creating files and directories. May be specified either via three octal digits or symbolic values, with the same constraints as the dfs chmod command.
 	</dd>
 </dl>
 </section>

Modified: hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/fs/permission/TestFsPermission.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/fs/permission/TestFsPermission.java?rev=1077057&r1=1077056&r2=1077057&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/fs/permission/TestFsPermission.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/fs/permission/TestFsPermission.java Fri Mar  4 03:36:22 2011
@@ -17,6 +17,10 @@
  */
 package org.apache.hadoop.fs.permission;
 
+import java.io.IOException;
+
+import org.apache.hadoop.conf.Configuration;
+
 import junit.framework.TestCase;
 
 import static org.apache.hadoop.fs.permission.FsAction.*;
@@ -67,4 +71,60 @@ public class TestFsPermission extends Te
       assertEquals(i, FsPermission.valueOf(b.toString()).toShort());
     }
   }
+
+  public void testUMaskParser() throws IOException {
+    Configuration conf = new Configuration();
+    
+    // Ensure that we get the right octal values back for all legal values
+    for(FsAction u : FsAction.values()) {
+      for(FsAction g : FsAction.values()) {
+        for(FsAction o : FsAction.values()) {
+          FsPermission f = new FsPermission(u, g, o);
+          String asOctal = String.format("%1$03o", f.toShort());
+          conf.set(FsPermission.UMASK_LABEL, asOctal);
+          FsPermission fromConf = FsPermission.getUMask(conf);
+          assertEquals(f, fromConf);
+        }
+      }
+    }
+  }
+
+  public void TestSymbolicUmasks() {
+    Configuration conf = new Configuration();
+    
+    // Test some symbolic settings       Setting       Octal result
+    String [] symbolic = new String [] { "a+rw",        "666",
+                                         "u=x,g=r,o=w", "142",
+                                         "u=x",         "100" };
+    
+    for(int i = 0; i < symbolic.length; i += 2) {
+      conf.set(FsPermission.UMASK_LABEL, symbolic[i]);
+      short val = Short.valueOf(symbolic[i + 1], 8);
+      assertEquals(val, FsPermission.getUMask(conf).toShort());
+    }
+  }
+
+  public void testBadUmasks() {
+    Configuration conf = new Configuration();
+    
+    for(String b : new String [] {"1777", "22", "99", "foo", ""}) {
+      conf.set(FsPermission.UMASK_LABEL, b); 
+      try {
+        FsPermission.getUMask(conf);
+        fail("Shouldn't have been able to parse bad umask");
+      } catch(IllegalArgumentException iae) {
+        assertEquals(iae.getMessage(), b);
+      }
+    }
+  }
+  
+  // Ensure that when the deprecated decimal umask key is used, it is correctly
+  // parsed as such and converted correctly to an FsPermission value
+  public void testDeprecatedUmask() {
+    Configuration conf = new Configuration();
+    conf.set(FsPermission.DEPRECATED_UMASK_LABEL, "302"); // 302 = 0456
+    FsPermission umask = FsPermission.getUMask(conf);
+
+    assertEquals(0456, umask.toShort());
+  }
 }