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 */ 018package org.apache.hadoop.fs.permission; 019 020import java.io.DataInput; 021import java.io.DataOutput; 022import java.io.IOException; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.apache.hadoop.classification.InterfaceAudience; 027import org.apache.hadoop.classification.InterfaceStability; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.fs.CommonConfigurationKeys; 030import org.apache.hadoop.io.Writable; 031import org.apache.hadoop.io.WritableFactories; 032import org.apache.hadoop.io.WritableFactory; 033 034/** 035 * A class for file/directory permissions. 036 */ 037@InterfaceAudience.Public 038@InterfaceStability.Stable 039public 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 /** Maximum acceptable length of a permission string to parse */ 052 public static final int MAX_PERMISSION_LENGTH = 10; 053 054 /** Create an immutable {@link FsPermission} object. */ 055 public static FsPermission createImmutable(short permission) { 056 return new ImmutableFsPermission(permission); 057 } 058 059 //POSIX permission style 060 private FsAction useraction = null; 061 private FsAction groupaction = null; 062 private FsAction otheraction = null; 063 private boolean stickyBit = false; 064 private boolean groupBit = false; 065 private boolean userBit = false; 066 067 private FsPermission() {} 068 069 /** 070 * Construct by the given {@link FsAction}. 071 * @param u user action 072 * @param g group action 073 * @param o other action 074 */ 075 public FsPermission(FsAction u, FsAction g, FsAction o) { 076 this(u, g, o, false); 077 } 078 079 public FsPermission(FsAction u, FsAction g, FsAction o, boolean sb) { 080 set(u, g, o, sb); 081 } 082 083 /** 084 * Construct by the given mode. 085 * @param mode 086 * @see #toShort() 087 */ 088 public FsPermission(short mode) { fromShort(mode); } 089 090 /** 091 * Copy constructor 092 * 093 * @param other other permission 094 */ 095 public FsPermission(FsPermission other) { 096 this.useraction = other.useraction; 097 this.groupaction = other.groupaction; 098 this.otheraction = other.otheraction; 099 this.stickyBit = other.stickyBit; 100 } 101 102 /** 103 * Construct by given mode, either in octal or symbolic format. 104 * @param mode mode as a string, either in octal or symbolic format 105 * @throws IllegalArgumentException if <code>mode</code> is invalid 106 */ 107 public FsPermission(String mode) { 108 this(new UmaskParser(mode).getUMask()); 109 } 110 111 /** Return user {@link FsAction}. */ 112 public FsAction getUserAction() {return useraction;} 113 114 /** Return group {@link FsAction}. */ 115 public FsAction getGroupAction() {return groupaction;} 116 117 /** Return other {@link FsAction}. */ 118 public FsAction getOtherAction() {return otheraction;} 119 120 private void set(FsAction u, FsAction g, FsAction o, boolean sb) { 121 useraction = u; 122 groupaction = g; 123 otheraction = o; 124 stickyBit = sb; 125 } 126 127 /** 128 * Setter which includes set gid and set uid bits 129 */ 130 private void set(FsAction u, FsAction g, FsAction o, boolean sb, boolean gb, boolean ub) { 131 useraction = u; 132 groupaction = g; 133 otheraction = o; 134 stickyBit = sb; 135 groupBit = gb; 136 userBit = ub; 137 } 138 139 public void fromShort(short n) { 140 FsAction[] v = FSACTION_VALUES; 141 int bit = (n >>> 9); // The last bit portion for sticky bits/set gid/uid 142 set(v[(n >>> 6) & 7], v[(n >>> 3) & 7], v[n & 7], (bit & 1) == 1, (bit & 2) == 2, (bit & 4) == 4); 143 } 144 145 @Override 146 public void write(DataOutput out) throws IOException { 147 out.writeShort(toShort()); 148 } 149 150 @Override 151 public void readFields(DataInput in) throws IOException { 152 fromShort(in.readShort()); 153 } 154 155 /** 156 * Create and initialize a {@link FsPermission} from {@link DataInput}. 157 */ 158 public static FsPermission read(DataInput in) throws IOException { 159 FsPermission p = new FsPermission(); 160 p.readFields(in); 161 return p; 162 } 163 164 /** 165 * Method that gets ordinal part of the "sticky" byte 166 * 100 = Set Uid bit, 010 Set Gid bit, 001 sticky bit. 167 * References userBit/groupBit/stickyBit boolean statements 168 * @return Int representation of the bits set depending on which boolean statements are true 169 */ 170 private int stickyOrdinal() { 171 int ret = 0; 172 173 if (userBit) { 174 ret |= 4; 175 } 176 if (groupBit) { 177 ret |= 2; 178 } 179 if (stickyBit) { 180 ret |= 1; 181 } 182 return ret; 183 } 184 185 /** 186 * Encode the object to a short. 187 */ 188 public short toShort() { 189 int s = (stickyOrdinal() << 9) | 190 (useraction.ordinal() << 6) | 191 (groupaction.ordinal() << 3) | 192 otheraction.ordinal(); 193 194 return (short)s; 195 } 196 197 /** 198 * Encodes the object to a short. Unlike {@link #toShort()}, this method may 199 * return values outside the fixed range 00000 - 01777 if extended features 200 * are encoded into this permission, such as the ACL bit. 201 * 202 * @return short extended short representation of this permission 203 */ 204 public short toExtendedShort() { 205 return toShort(); 206 } 207 208 @Override 209 public boolean equals(Object obj) { 210 if (obj instanceof FsPermission) { 211 FsPermission that = (FsPermission)obj; 212 return this.useraction == that.useraction 213 && this.groupaction == that.groupaction 214 && this.otheraction == that.otheraction 215 && this.stickyBit == that.stickyBit; 216 } 217 return false; 218 } 219 220 @Override 221 public int hashCode() {return toShort();} 222 223 @Override 224 public String toString() { 225 StringBuilder str = new StringBuilder(); 226 227 // Append permission string. Add portions of the string if they use setuid, setgid and sticky bit 228 229 // Set user portion of string from bit 230 str.append(useraction.SYMBOL); 231 // Replace execute if user bit is set 232 if (userBit) { 233 str.replace(str.length() -1, str.length(), useraction.implies(FsAction.EXECUTE) ? "s": "S"); 234 } 235 236 // Set group portion of string from bit 237 str.append(groupaction.SYMBOL); 238 // Replace execute if group bit is set 239 if (groupBit) { 240 str.replace(str.length() -1, str.length(), groupaction.implies(FsAction.EXECUTE) ? "s": "S"); 241 } 242 243 // Set others portion of string from bit 244 str.append(otheraction.SYMBOL); 245 // Replace execute if sticky bit is set 246 if (stickyBit) { 247 str.replace(str.length() -1, str.length(), otheraction.implies(FsAction.EXECUTE) ? "t": "T"); 248 } 249 250 return str.toString(); 251 } 252 253 /** 254 * Apply a umask to this permission and return a new one. 255 * 256 * The umask is used by create, mkdir, and other Hadoop filesystem operations. 257 * The mode argument for these operations is modified by removing the bits 258 * which are set in the umask. Thus, the umask limits the permissions which 259 * newly created files and directories get. 260 * 261 * @param umask The umask to use 262 * 263 * @return The effective permission 264 */ 265 public FsPermission applyUMask(FsPermission umask) { 266 return new FsPermission(useraction.and(umask.useraction.not()), 267 groupaction.and(umask.groupaction.not()), 268 otheraction.and(umask.otheraction.not())); 269 } 270 271 /** umask property label deprecated key and code in getUMask method 272 * to accommodate it may be removed in version .23 */ 273 public static final String DEPRECATED_UMASK_LABEL = "dfs.umask"; 274 public static final String UMASK_LABEL = 275 CommonConfigurationKeys.FS_PERMISSIONS_UMASK_KEY; 276 public static final int DEFAULT_UMASK = 277 CommonConfigurationKeys.FS_PERMISSIONS_UMASK_DEFAULT; 278 279 private static final FsAction[] FSACTION_VALUES = FsAction.values(); 280 281 /** 282 * Get the user file creation mask (umask) 283 * 284 * {@code UMASK_LABEL} config param has umask value that is either symbolic 285 * or octal. 286 * 287 * Symbolic umask is applied relative to file mode creation mask; 288 * the permission op characters '+' clears the corresponding bit in the mask, 289 * '-' sets bits in the mask. 290 * 291 * Octal umask, the specified bits are set in the file mode creation mask. 292 * 293 * {@code DEPRECATED_UMASK_LABEL} config param has umask value set to decimal. 294 */ 295 public static FsPermission getUMask(Configuration conf) { 296 int umask = DEFAULT_UMASK; 297 298 // To ensure backward compatibility first use the deprecated key. 299 // If the deprecated key is not present then check for the new key 300 if(conf != null) { 301 String confUmask = conf.get(UMASK_LABEL); 302 int oldUmask = conf.getInt(DEPRECATED_UMASK_LABEL, Integer.MIN_VALUE); 303 try { 304 if(confUmask != null) { 305 umask = new UmaskParser(confUmask).getUMask(); 306 } 307 } catch(IllegalArgumentException iae) { 308 // Provide more explanation for user-facing message 309 String type = iae instanceof NumberFormatException ? "decimal" 310 : "octal or symbolic"; 311 String error = "Unable to parse configuration " + UMASK_LABEL 312 + " with value " + confUmask + " as " + type + " umask."; 313 LOG.warn(error); 314 315 // If oldUmask is not set, then throw the exception 316 if (oldUmask == Integer.MIN_VALUE) { 317 throw new IllegalArgumentException(error); 318 } 319 } 320 321 if(oldUmask != Integer.MIN_VALUE) { // Property was set with old key 322 if (umask != oldUmask) { 323 LOG.warn(DEPRECATED_UMASK_LABEL 324 + " configuration key is deprecated. " + "Convert to " 325 + UMASK_LABEL + ", using octal or symbolic umask " 326 + "specifications."); 327 // Old and new umask values do not match - Use old umask 328 umask = oldUmask; 329 } 330 } 331 } 332 333 return new FsPermission((short)umask); 334 } 335 336 public boolean getStickyBit() { 337 return stickyBit; 338 } 339 340 /** 341 * Returns true if there is also an ACL (access control list). 342 * 343 * @return boolean true if there is also an ACL (access control list). 344 */ 345 public boolean getAclBit() { 346 // File system subclasses that support the ACL bit would override this. 347 return false; 348 } 349 350 /** Set the user file creation mask (umask) */ 351 public static void setUMask(Configuration conf, FsPermission umask) { 352 conf.set(UMASK_LABEL, String.format("%1$03o", umask.toShort())); 353 conf.setInt(DEPRECATED_UMASK_LABEL, umask.toShort()); 354 } 355 356 /** 357 * Get the default permission for directory and symlink. 358 * In previous versions, this default permission was also used to 359 * create files, so files created end up with ugo+x permission. 360 * See HADOOP-9155 for detail. 361 * Two new methods are added to solve this, please use 362 * {@link FsPermission#getDirDefault()} for directory, and use 363 * {@link FsPermission#getFileDefault()} for file. 364 * This method is kept for compatibility. 365 */ 366 public static FsPermission getDefault() { 367 return new FsPermission((short)00777); 368 } 369 370 /** 371 * Get the default permission for directory. 372 */ 373 public static FsPermission getDirDefault() { 374 return new FsPermission((short)00777); 375 } 376 377 /** 378 * Get the default permission for file. 379 */ 380 public static FsPermission getFileDefault() { 381 return new FsPermission((short)00666); 382 } 383 384 /** 385 * Get the default permission for cache pools. 386 */ 387 public static FsPermission getCachePoolDefault() { 388 return new FsPermission((short)00755); 389 } 390 391 /** 392 * Create a FsPermission from a Unix symbolic permission string 393 * @param unixSymbolicPermission e.g. "-rw-rw-rw-" 394 */ 395 public static FsPermission valueOf(String unixSymbolicPermission) { 396 if (unixSymbolicPermission == null) { 397 return null; 398 } 399 else if (unixSymbolicPermission.length() != MAX_PERMISSION_LENGTH) { 400 throw new IllegalArgumentException(String.format( 401 "length != %d(unixSymbolicPermission=%s)", MAX_PERMISSION_LENGTH, 402 unixSymbolicPermission)); 403 } 404 405 int n = 0; 406 for(int i = 1; i < unixSymbolicPermission.length(); i++) { 407 n = n << 1; 408 char c = unixSymbolicPermission.charAt(i); 409 n += (c == '-' || c == 'T' || c == 'S') ? 0: 1; 410 } 411 412 // Add sticky bit value if set 413 if(unixSymbolicPermission.charAt(9) == 't' || 414 unixSymbolicPermission.charAt(9) == 'T') 415 n += 01000; 416 417 return new FsPermission((short)n); 418 } 419 420 private static class ImmutableFsPermission extends FsPermission { 421 public ImmutableFsPermission(short permission) { 422 super(permission); 423 } 424 @Override 425 public FsPermission applyUMask(FsPermission umask) { 426 throw new UnsupportedOperationException(); 427 } 428 @Override 429 public void readFields(DataInput in) throws IOException { 430 throw new UnsupportedOperationException(); 431 } 432 } 433}