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 /** 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 /** Set the user file creation mask (umask) */
298 public static void setUMask(Configuration conf, FsPermission umask) {
299 conf.set(UMASK_LABEL, String.format("%1$03o", umask.toShort()));
300 conf.setInt(DEPRECATED_UMASK_LABEL, umask.toShort());
301 }
302
303 /**
304 * Get the default permission for directory and symlink.
305 * In previous versions, this default permission was also used to
306 * create files, so files created end up with ugo+x permission.
307 * See HADOOP-9155 for detail.
308 * Two new methods are added to solve this, please use
309 * {@link FsPermission#getDirDefault()} for directory, and use
310 * {@link FsPermission#getFileDefault()} for file.
311 * This method is kept for compatibility.
312 */
313 public static FsPermission getDefault() {
314 return new FsPermission((short)00777);
315 }
316
317 /**
318 * Get the default permission for directory.
319 */
320 public static FsPermission getDirDefault() {
321 return new FsPermission((short)00777);
322 }
323
324 /**
325 * Get the default permission for file.
326 */
327 public static FsPermission getFileDefault() {
328 return new FsPermission((short)00666);
329 }
330
331 /**
332 * Get the default permission for cache pools.
333 */
334 public static FsPermission getCachePoolDefault() {
335 return new FsPermission((short)00755);
336 }
337
338 /**
339 * Create a FsPermission from a Unix symbolic permission string
340 * @param unixSymbolicPermission e.g. "-rw-rw-rw-"
341 */
342 public static FsPermission valueOf(String unixSymbolicPermission) {
343 if (unixSymbolicPermission == null) {
344 return null;
345 }
346 else if (unixSymbolicPermission.length() != MAX_PERMISSION_LENGTH) {
347 throw new IllegalArgumentException(String.format(
348 "length != %d(unixSymbolicPermission=%s)", MAX_PERMISSION_LENGTH,
349 unixSymbolicPermission));
350 }
351
352 int n = 0;
353 for(int i = 1; i < unixSymbolicPermission.length(); i++) {
354 n = n << 1;
355 char c = unixSymbolicPermission.charAt(i);
356 n += (c == '-' || c == 'T' || c == 'S') ? 0: 1;
357 }
358
359 // Add sticky bit value if set
360 if(unixSymbolicPermission.charAt(9) == 't' ||
361 unixSymbolicPermission.charAt(9) == 'T')
362 n += 01000;
363
364 return new FsPermission((short)n);
365 }
366
367 private static class ImmutableFsPermission extends FsPermission {
368 public ImmutableFsPermission(short permission) {
369 super(permission);
370 }
371 @Override
372 public FsPermission applyUMask(FsPermission umask) {
373 throw new UnsupportedOperationException();
374 }
375 @Override
376 public void readFields(DataInput in) throws IOException {
377 throw new UnsupportedOperationException();
378 }
379 }
380 }