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 public Writable newInstance() { return new FsPermission(); } 044 }; 045 static { // register a ctor 046 WritableFactories.setFactory(FsPermission.class, FACTORY); 047 WritableFactories.setFactory(ImmutableFsPermission.class, FACTORY); 048 } 049 050 /** Create an immutable {@link FsPermission} object. */ 051 public static FsPermission createImmutable(short permission) { 052 return new ImmutableFsPermission(permission); 053 } 054 055 //POSIX permission style 056 private FsAction useraction = null; 057 private FsAction groupaction = null; 058 private FsAction otheraction = null; 059 private boolean stickyBit = false; 060 061 private FsPermission() {} 062 063 /** 064 * Construct by the given {@link FsAction}. 065 * @param u user action 066 * @param g group action 067 * @param o other action 068 */ 069 public FsPermission(FsAction u, FsAction g, FsAction o) { 070 this(u, g, o, false); 071 } 072 073 public FsPermission(FsAction u, FsAction g, FsAction o, boolean sb) { 074 set(u, g, o, sb); 075 } 076 077 /** 078 * Construct by the given mode. 079 * @param mode 080 * @see #toShort() 081 */ 082 public FsPermission(short mode) { fromShort(mode); } 083 084 /** 085 * Copy constructor 086 * 087 * @param other other permission 088 */ 089 public FsPermission(FsPermission other) { 090 this.useraction = other.useraction; 091 this.groupaction = other.groupaction; 092 this.otheraction = other.otheraction; 093 this.stickyBit = other.stickyBit; 094 } 095 096 /** 097 * Construct by given mode, either in octal or symbolic format. 098 * @param mode mode as a string, either in octal or symbolic format 099 * @throws IllegalArgumentException if <code>mode</code> is invalid 100 */ 101 public FsPermission(String mode) { 102 this(new UmaskParser(mode).getUMask()); 103 } 104 105 /** Return user {@link FsAction}. */ 106 public FsAction getUserAction() {return useraction;} 107 108 /** Return group {@link FsAction}. */ 109 public FsAction getGroupAction() {return groupaction;} 110 111 /** Return other {@link FsAction}. */ 112 public FsAction getOtherAction() {return otheraction;} 113 114 private void set(FsAction u, FsAction g, FsAction o, boolean sb) { 115 useraction = u; 116 groupaction = g; 117 otheraction = o; 118 stickyBit = sb; 119 } 120 121 public void fromShort(short n) { 122 FsAction[] v = FsAction.values(); 123 124 set(v[(n >>> 6) & 7], v[(n >>> 3) & 7], v[n & 7], (((n >>> 9) & 1) == 1) ); 125 } 126 127 /** {@inheritDoc} */ 128 public void write(DataOutput out) throws IOException { 129 out.writeShort(toShort()); 130 } 131 132 /** {@inheritDoc} */ 133 public void readFields(DataInput in) throws IOException { 134 fromShort(in.readShort()); 135 } 136 137 /** 138 * Create and initialize a {@link FsPermission} from {@link DataInput}. 139 */ 140 public static FsPermission read(DataInput in) throws IOException { 141 FsPermission p = new FsPermission(); 142 p.readFields(in); 143 return p; 144 } 145 146 /** 147 * Encode the object to a short. 148 */ 149 public short toShort() { 150 int s = (stickyBit ? 1 << 9 : 0) | 151 (useraction.ordinal() << 6) | 152 (groupaction.ordinal() << 3) | 153 otheraction.ordinal(); 154 155 return (short)s; 156 } 157 158 /** {@inheritDoc} */ 159 public boolean equals(Object obj) { 160 if (obj instanceof FsPermission) { 161 FsPermission that = (FsPermission)obj; 162 return this.useraction == that.useraction 163 && this.groupaction == that.groupaction 164 && this.otheraction == that.otheraction 165 && this.stickyBit == that.stickyBit; 166 } 167 return false; 168 } 169 170 /** {@inheritDoc} */ 171 public int hashCode() {return toShort();} 172 173 /** {@inheritDoc} */ 174 public String toString() { 175 String str = useraction.SYMBOL + groupaction.SYMBOL + otheraction.SYMBOL; 176 if(stickyBit) { 177 StringBuilder str2 = new StringBuilder(str); 178 str2.replace(str2.length() - 1, str2.length(), 179 otheraction.implies(FsAction.EXECUTE) ? "t" : "T"); 180 str = str2.toString(); 181 } 182 183 return str; 184 } 185 186 /** Apply a umask to this permission and return a new one */ 187 public FsPermission applyUMask(FsPermission umask) { 188 return new FsPermission(useraction.and(umask.useraction.not()), 189 groupaction.and(umask.groupaction.not()), 190 otheraction.and(umask.otheraction.not())); 191 } 192 193 /** umask property label deprecated key and code in getUMask method 194 * to accommodate it may be removed in version .23 */ 195 public static final String DEPRECATED_UMASK_LABEL = "dfs.umask"; 196 public static final String UMASK_LABEL = 197 CommonConfigurationKeys.FS_PERMISSIONS_UMASK_KEY; 198 public static final int DEFAULT_UMASK = 199 CommonConfigurationKeys.FS_PERMISSIONS_UMASK_DEFAULT; 200 201 /** 202 * Get the user file creation mask (umask) 203 * 204 * {@code UMASK_LABEL} config param has umask value that is either symbolic 205 * or octal. 206 * 207 * Symbolic umask is applied relative to file mode creation mask; 208 * the permission op characters '+' clears the corresponding bit in the mask, 209 * '-' sets bits in the mask. 210 * 211 * Octal umask, the specified bits are set in the file mode creation mask. 212 * 213 * {@code DEPRECATED_UMASK_LABEL} config param has umask value set to decimal. 214 */ 215 public static FsPermission getUMask(Configuration conf) { 216 int umask = DEFAULT_UMASK; 217 218 // To ensure backward compatibility first use the deprecated key. 219 // If the deprecated key is not present then check for the new key 220 if(conf != null) { 221 String confUmask = conf.get(UMASK_LABEL); 222 int oldUmask = conf.getInt(DEPRECATED_UMASK_LABEL, Integer.MIN_VALUE); 223 try { 224 if(confUmask != null) { 225 umask = new UmaskParser(confUmask).getUMask(); 226 } 227 } catch(IllegalArgumentException iae) { 228 // Provide more explanation for user-facing message 229 String type = iae instanceof NumberFormatException ? "decimal" 230 : "octal or symbolic"; 231 String error = "Unable to parse configuration " + UMASK_LABEL 232 + " with value " + confUmask + " as " + type + " umask."; 233 LOG.warn(error); 234 235 // If oldUmask is not set, then throw the exception 236 if (oldUmask == Integer.MIN_VALUE) { 237 throw new IllegalArgumentException(error); 238 } 239 } 240 241 if(oldUmask != Integer.MIN_VALUE) { // Property was set with old key 242 if (umask != oldUmask) { 243 LOG.warn(DEPRECATED_UMASK_LABEL 244 + " configuration key is deprecated. " + "Convert to " 245 + UMASK_LABEL + ", using octal or symbolic umask " 246 + "specifications."); 247 // Old and new umask values do not match - Use old umask 248 umask = oldUmask; 249 } 250 } 251 } 252 253 return new FsPermission((short)umask); 254 } 255 256 public boolean getStickyBit() { 257 return stickyBit; 258 } 259 260 /** Set the user file creation mask (umask) */ 261 public static void setUMask(Configuration conf, FsPermission umask) { 262 conf.set(UMASK_LABEL, String.format("%1$03o", umask.toShort())); 263 conf.setInt(DEPRECATED_UMASK_LABEL, umask.toShort()); 264 } 265 266 /** 267 * Get the default permission for directory and symlink. 268 * In previous versions, this default permission was also used to 269 * create files, so files created end up with ugo+x permission. 270 * See HADOOP-9155 for detail. 271 * Two new methods are added to solve this, please use 272 * {@link FsPermission#getDirDefault()} for directory, and use 273 * {@link FsPermission#getFileDefault()} for file. 274 * This method is kept for compatibility. 275 */ 276 public static FsPermission getDefault() { 277 return new FsPermission((short)00777); 278 } 279 280 /** 281 * Get the default permission for directory. 282 */ 283 public static FsPermission getDirDefault() { 284 return new FsPermission((short)00777); 285 } 286 287 /** 288 * Get the default permission for file. 289 */ 290 public static FsPermission getFileDefault() { 291 return new FsPermission((short)00666); 292 } 293 294 /** 295 * Create a FsPermission from a Unix symbolic permission string 296 * @param unixSymbolicPermission e.g. "-rw-rw-rw-" 297 */ 298 public static FsPermission valueOf(String unixSymbolicPermission) { 299 if (unixSymbolicPermission == null) { 300 return null; 301 } 302 else if (unixSymbolicPermission.length() != 10) { 303 throw new IllegalArgumentException("length != 10(unixSymbolicPermission=" 304 + unixSymbolicPermission + ")"); 305 } 306 307 int n = 0; 308 for(int i = 1; i < unixSymbolicPermission.length(); i++) { 309 n = n << 1; 310 char c = unixSymbolicPermission.charAt(i); 311 n += (c == '-' || c == 'T' || c == 'S') ? 0: 1; 312 } 313 314 // Add sticky bit value if set 315 if(unixSymbolicPermission.charAt(9) == 't' || 316 unixSymbolicPermission.charAt(9) == 'T') 317 n += 01000; 318 319 return new FsPermission((short)n); 320 } 321 322 private static class ImmutableFsPermission extends FsPermission { 323 public ImmutableFsPermission(short permission) { 324 super(permission); 325 } 326 public FsPermission applyUMask(FsPermission umask) { 327 throw new UnsupportedOperationException(); 328 } 329 public void readFields(DataInput in) throws IOException { 330 throw new UnsupportedOperationException(); 331 } 332 } 333}