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 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 /** Get the default permission. */
267 public static FsPermission getDefault() {
268 return new FsPermission((short)00777);
269 }
270
271 /**
272 * Create a FsPermission from a Unix symbolic permission string
273 * @param unixSymbolicPermission e.g. "-rw-rw-rw-"
274 */
275 public static FsPermission valueOf(String unixSymbolicPermission) {
276 if (unixSymbolicPermission == null) {
277 return null;
278 }
279 else if (unixSymbolicPermission.length() != 10) {
280 throw new IllegalArgumentException("length != 10(unixSymbolicPermission="
281 + unixSymbolicPermission + ")");
282 }
283
284 int n = 0;
285 for(int i = 1; i < unixSymbolicPermission.length(); i++) {
286 n = n << 1;
287 char c = unixSymbolicPermission.charAt(i);
288 n += (c == '-' || c == 'T' || c == 'S') ? 0: 1;
289 }
290
291 // Add sticky bit value if set
292 if(unixSymbolicPermission.charAt(9) == 't' ||
293 unixSymbolicPermission.charAt(9) == 'T')
294 n += 01000;
295
296 return new FsPermission((short)n);
297 }
298
299 private static class ImmutableFsPermission extends FsPermission {
300 public ImmutableFsPermission(short permission) {
301 super(permission);
302 }
303 public FsPermission applyUMask(FsPermission umask) {
304 throw new UnsupportedOperationException();
305 }
306 public void readFields(DataInput in) throws IOException {
307 throw new UnsupportedOperationException();
308 }
309 }
310 }