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 /**
188 * Apply a umask to this permission and return a new one.
189 *
190 * The umask is used by create, mkdir, and other Hadoop filesystem operations.
191 * The mode argument for these operations is modified by removing the bits
192 * which are set in the umask. Thus, the umask limits the permissions which
193 * newly created files and directories get.
194 *
195 * @param umask The umask to use
196 *
197 * @return The effective permission
198 */
199 public FsPermission applyUMask(FsPermission umask) {
200 return new FsPermission(useraction.and(umask.useraction.not()),
201 groupaction.and(umask.groupaction.not()),
202 otheraction.and(umask.otheraction.not()));
203 }
204
205 /** umask property label deprecated key and code in getUMask method
206 * to accommodate it may be removed in version .23 */
207 public static final String DEPRECATED_UMASK_LABEL = "dfs.umask";
208 public static final String UMASK_LABEL =
209 CommonConfigurationKeys.FS_PERMISSIONS_UMASK_KEY;
210 public static final int DEFAULT_UMASK =
211 CommonConfigurationKeys.FS_PERMISSIONS_UMASK_DEFAULT;
212
213 /**
214 * Get the user file creation mask (umask)
215 *
216 * {@code UMASK_LABEL} config param has umask value that is either symbolic
217 * or octal.
218 *
219 * Symbolic umask is applied relative to file mode creation mask;
220 * the permission op characters '+' clears the corresponding bit in the mask,
221 * '-' sets bits in the mask.
222 *
223 * Octal umask, the specified bits are set in the file mode creation mask.
224 *
225 * {@code DEPRECATED_UMASK_LABEL} config param has umask value set to decimal.
226 */
227 public static FsPermission getUMask(Configuration conf) {
228 int umask = DEFAULT_UMASK;
229
230 // To ensure backward compatibility first use the deprecated key.
231 // If the deprecated key is not present then check for the new key
232 if(conf != null) {
233 String confUmask = conf.get(UMASK_LABEL);
234 int oldUmask = conf.getInt(DEPRECATED_UMASK_LABEL, Integer.MIN_VALUE);
235 try {
236 if(confUmask != null) {
237 umask = new UmaskParser(confUmask).getUMask();
238 }
239 } catch(IllegalArgumentException iae) {
240 // Provide more explanation for user-facing message
241 String type = iae instanceof NumberFormatException ? "decimal"
242 : "octal or symbolic";
243 String error = "Unable to parse configuration " + UMASK_LABEL
244 + " with value " + confUmask + " as " + type + " umask.";
245 LOG.warn(error);
246
247 // If oldUmask is not set, then throw the exception
248 if (oldUmask == Integer.MIN_VALUE) {
249 throw new IllegalArgumentException(error);
250 }
251 }
252
253 if(oldUmask != Integer.MIN_VALUE) { // Property was set with old key
254 if (umask != oldUmask) {
255 LOG.warn(DEPRECATED_UMASK_LABEL
256 + " configuration key is deprecated. " + "Convert to "
257 + UMASK_LABEL + ", using octal or symbolic umask "
258 + "specifications.");
259 // Old and new umask values do not match - Use old umask
260 umask = oldUmask;
261 }
262 }
263 }
264
265 return new FsPermission((short)umask);
266 }
267
268 public boolean getStickyBit() {
269 return stickyBit;
270 }
271
272 /** Set the user file creation mask (umask) */
273 public static void setUMask(Configuration conf, FsPermission umask) {
274 conf.set(UMASK_LABEL, String.format("%1$03o", umask.toShort()));
275 conf.setInt(DEPRECATED_UMASK_LABEL, umask.toShort());
276 }
277
278 /**
279 * Get the default permission for directory and symlink.
280 * In previous versions, this default permission was also used to
281 * create files, so files created end up with ugo+x permission.
282 * See HADOOP-9155 for detail.
283 * Two new methods are added to solve this, please use
284 * {@link FsPermission#getDirDefault()} for directory, and use
285 * {@link FsPermission#getFileDefault()} for file.
286 * This method is kept for compatibility.
287 */
288 public static FsPermission getDefault() {
289 return new FsPermission((short)00777);
290 }
291
292 /**
293 * Get the default permission for directory.
294 */
295 public static FsPermission getDirDefault() {
296 return new FsPermission((short)00777);
297 }
298
299 /**
300 * Get the default permission for file.
301 */
302 public static FsPermission getFileDefault() {
303 return new FsPermission((short)00666);
304 }
305
306 /**
307 * Create a FsPermission from a Unix symbolic permission string
308 * @param unixSymbolicPermission e.g. "-rw-rw-rw-"
309 */
310 public static FsPermission valueOf(String unixSymbolicPermission) {
311 if (unixSymbolicPermission == null) {
312 return null;
313 }
314 else if (unixSymbolicPermission.length() != 10) {
315 throw new IllegalArgumentException("length != 10(unixSymbolicPermission="
316 + unixSymbolicPermission + ")");
317 }
318
319 int n = 0;
320 for(int i = 1; i < unixSymbolicPermission.length(); i++) {
321 n = n << 1;
322 char c = unixSymbolicPermission.charAt(i);
323 n += (c == '-' || c == 'T' || c == 'S') ? 0: 1;
324 }
325
326 // Add sticky bit value if set
327 if(unixSymbolicPermission.charAt(9) == 't' ||
328 unixSymbolicPermission.charAt(9) == 'T')
329 n += 01000;
330
331 return new FsPermission((short)n);
332 }
333
334 private static class ImmutableFsPermission extends FsPermission {
335 public ImmutableFsPermission(short permission) {
336 super(permission);
337 }
338 @Override
339 public FsPermission applyUMask(FsPermission umask) {
340 throw new UnsupportedOperationException();
341 }
342 @Override
343 public void readFields(DataInput in) throws IOException {
344 throw new UnsupportedOperationException();
345 }
346 }
347 }