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