001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.hadoop.fs.permission; 019 020 import java.io.DataInput; 021 import java.io.DataOutput; 022 import java.io.IOException; 023 024 import org.apache.commons.logging.Log; 025 import org.apache.commons.logging.LogFactory; 026 import org.apache.hadoop.classification.InterfaceAudience; 027 import org.apache.hadoop.classification.InterfaceStability; 028 import org.apache.hadoop.conf.Configuration; 029 import org.apache.hadoop.fs.CommonConfigurationKeys; 030 import org.apache.hadoop.io.Writable; 031 import org.apache.hadoop.io.WritableFactories; 032 import org.apache.hadoop.io.WritableFactory; 033 034 /** 035 * A class for file/directory permissions. 036 */ 037 @InterfaceAudience.Public 038 @InterfaceStability.Stable 039 public class FsPermission implements Writable { 040 private static final Log LOG = LogFactory.getLog(FsPermission.class); 041 042 static final WritableFactory FACTORY = new WritableFactory() { 043 @Override 044 public Writable newInstance() { return new FsPermission(); } 045 }; 046 static { // register a ctor 047 WritableFactories.setFactory(FsPermission.class, FACTORY); 048 WritableFactories.setFactory(ImmutableFsPermission.class, FACTORY); 049 } 050 051 /** Create an immutable {@link FsPermission} object. */ 052 public static FsPermission createImmutable(short permission) { 053 return new ImmutableFsPermission(permission); 054 } 055 056 //POSIX permission style 057 private FsAction useraction = null; 058 private FsAction groupaction = null; 059 private FsAction otheraction = null; 060 private boolean stickyBit = false; 061 062 private FsPermission() {} 063 064 /** 065 * Construct by the given {@link FsAction}. 066 * @param u user action 067 * @param g group action 068 * @param o other action 069 */ 070 public FsPermission(FsAction u, FsAction g, FsAction o) { 071 this(u, g, o, false); 072 } 073 074 public FsPermission(FsAction u, FsAction g, FsAction o, boolean sb) { 075 set(u, g, o, sb); 076 } 077 078 /** 079 * Construct by the given mode. 080 * @param mode 081 * @see #toShort() 082 */ 083 public FsPermission(short mode) { fromShort(mode); } 084 085 /** 086 * Copy constructor 087 * 088 * @param other other permission 089 */ 090 public FsPermission(FsPermission other) { 091 this.useraction = other.useraction; 092 this.groupaction = other.groupaction; 093 this.otheraction = other.otheraction; 094 this.stickyBit = other.stickyBit; 095 } 096 097 /** 098 * Construct by given mode, either in octal or symbolic format. 099 * @param mode mode as a string, either in octal or symbolic format 100 * @throws IllegalArgumentException if <code>mode</code> is invalid 101 */ 102 public FsPermission(String mode) { 103 this(new UmaskParser(mode).getUMask()); 104 } 105 106 /** Return user {@link FsAction}. */ 107 public FsAction getUserAction() {return useraction;} 108 109 /** Return group {@link FsAction}. */ 110 public FsAction getGroupAction() {return groupaction;} 111 112 /** Return other {@link FsAction}. */ 113 public FsAction getOtherAction() {return otheraction;} 114 115 private void set(FsAction u, FsAction g, FsAction o, boolean sb) { 116 useraction = u; 117 groupaction = g; 118 otheraction = o; 119 stickyBit = sb; 120 } 121 122 public void fromShort(short n) { 123 FsAction[] v = FsAction.values(); 124 125 set(v[(n >>> 6) & 7], v[(n >>> 3) & 7], v[n & 7], (((n >>> 9) & 1) == 1) ); 126 } 127 128 @Override 129 public void write(DataOutput out) throws IOException { 130 out.writeShort(toShort()); 131 } 132 133 @Override 134 public void readFields(DataInput in) throws IOException { 135 fromShort(in.readShort()); 136 } 137 138 /** 139 * Create and initialize a {@link FsPermission} from {@link DataInput}. 140 */ 141 public static FsPermission read(DataInput in) throws IOException { 142 FsPermission p = new FsPermission(); 143 p.readFields(in); 144 return p; 145 } 146 147 /** 148 * Encode the object to a short. 149 */ 150 public short toShort() { 151 int s = (stickyBit ? 1 << 9 : 0) | 152 (useraction.ordinal() << 6) | 153 (groupaction.ordinal() << 3) | 154 otheraction.ordinal(); 155 156 return (short)s; 157 } 158 159 @Override 160 public boolean equals(Object obj) { 161 if (obj instanceof FsPermission) { 162 FsPermission that = (FsPermission)obj; 163 return this.useraction == that.useraction 164 && this.groupaction == that.groupaction 165 && this.otheraction == that.otheraction 166 && this.stickyBit == that.stickyBit; 167 } 168 return false; 169 } 170 171 @Override 172 public int hashCode() {return toShort();} 173 174 @Override 175 public String toString() { 176 String str = useraction.SYMBOL + groupaction.SYMBOL + otheraction.SYMBOL; 177 if(stickyBit) { 178 StringBuilder str2 = new StringBuilder(str); 179 str2.replace(str2.length() - 1, str2.length(), 180 otheraction.implies(FsAction.EXECUTE) ? "t" : "T"); 181 str = str2.toString(); 182 } 183 184 return str; 185 } 186 187 /** Apply a umask to this permission and return a new one */ 188 public FsPermission applyUMask(FsPermission umask) { 189 return new FsPermission(useraction.and(umask.useraction.not()), 190 groupaction.and(umask.groupaction.not()), 191 otheraction.and(umask.otheraction.not())); 192 } 193 194 /** umask property label deprecated key and code in getUMask method 195 * to accommodate it may be removed in version .23 */ 196 public static final String DEPRECATED_UMASK_LABEL = "dfs.umask"; 197 public static final String UMASK_LABEL = 198 CommonConfigurationKeys.FS_PERMISSIONS_UMASK_KEY; 199 public static final int DEFAULT_UMASK = 200 CommonConfigurationKeys.FS_PERMISSIONS_UMASK_DEFAULT; 201 202 /** 203 * Get the user file creation mask (umask) 204 * 205 * {@code UMASK_LABEL} config param has umask value that is either symbolic 206 * or octal. 207 * 208 * Symbolic umask is applied relative to file mode creation mask; 209 * the permission op characters '+' clears the corresponding bit in the mask, 210 * '-' sets bits in the mask. 211 * 212 * Octal umask, the specified bits are set in the file mode creation mask. 213 * 214 * {@code DEPRECATED_UMASK_LABEL} config param has umask value set to decimal. 215 */ 216 public static FsPermission getUMask(Configuration conf) { 217 int umask = DEFAULT_UMASK; 218 219 // To ensure backward compatibility first use the deprecated key. 220 // If the deprecated key is not present then check for the new key 221 if(conf != null) { 222 String confUmask = conf.get(UMASK_LABEL); 223 int oldUmask = conf.getInt(DEPRECATED_UMASK_LABEL, Integer.MIN_VALUE); 224 try { 225 if(confUmask != null) { 226 umask = new UmaskParser(confUmask).getUMask(); 227 } 228 } catch(IllegalArgumentException iae) { 229 // Provide more explanation for user-facing message 230 String type = iae instanceof NumberFormatException ? "decimal" 231 : "octal or symbolic"; 232 String error = "Unable to parse configuration " + UMASK_LABEL 233 + " with value " + confUmask + " as " + type + " umask."; 234 LOG.warn(error); 235 236 // If oldUmask is not set, then throw the exception 237 if (oldUmask == Integer.MIN_VALUE) { 238 throw new IllegalArgumentException(error); 239 } 240 } 241 242 if(oldUmask != Integer.MIN_VALUE) { // Property was set with old key 243 if (umask != oldUmask) { 244 LOG.warn(DEPRECATED_UMASK_LABEL 245 + " configuration key is deprecated. " + "Convert to " 246 + UMASK_LABEL + ", using octal or symbolic umask " 247 + "specifications."); 248 // Old and new umask values do not match - Use old umask 249 umask = oldUmask; 250 } 251 } 252 } 253 254 return new FsPermission((short)umask); 255 } 256 257 public boolean getStickyBit() { 258 return stickyBit; 259 } 260 261 /** Set the user file creation mask (umask) */ 262 public static void setUMask(Configuration conf, FsPermission umask) { 263 conf.set(UMASK_LABEL, String.format("%1$03o", umask.toShort())); 264 conf.setInt(DEPRECATED_UMASK_LABEL, umask.toShort()); 265 } 266 267 /** Get the default permission. */ 268 public static FsPermission getDefault() { 269 return new FsPermission((short)00777); 270 } 271 272 /** 273 * Create a FsPermission from a Unix symbolic permission string 274 * @param unixSymbolicPermission e.g. "-rw-rw-rw-" 275 */ 276 public static FsPermission valueOf(String unixSymbolicPermission) { 277 if (unixSymbolicPermission == null) { 278 return null; 279 } 280 else if (unixSymbolicPermission.length() != 10) { 281 throw new IllegalArgumentException("length != 10(unixSymbolicPermission=" 282 + unixSymbolicPermission + ")"); 283 } 284 285 int n = 0; 286 for(int i = 1; i < unixSymbolicPermission.length(); i++) { 287 n = n << 1; 288 char c = unixSymbolicPermission.charAt(i); 289 n += (c == '-' || c == 'T' || c == 'S') ? 0: 1; 290 } 291 292 // Add sticky bit value if set 293 if(unixSymbolicPermission.charAt(9) == 't' || 294 unixSymbolicPermission.charAt(9) == 'T') 295 n += 01000; 296 297 return new FsPermission((short)n); 298 } 299 300 private static class ImmutableFsPermission extends FsPermission { 301 public ImmutableFsPermission(short permission) { 302 super(permission); 303 } 304 @Override 305 public FsPermission applyUMask(FsPermission umask) { 306 throw new UnsupportedOperationException(); 307 } 308 @Override 309 public void readFields(DataInput in) throws IOException { 310 throw new UnsupportedOperationException(); 311 } 312 } 313 }