blob: 51c113af2702eec9623e2da89f307a89e4d0d3e4 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.fs.permission;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputValidation;
import java.io.Serializable;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableFactories;
import org.apache.hadoop.io.WritableFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A class for file/directory permissions.
*/
@InterfaceAudience.Public
@InterfaceStability.Stable
public class FsPermission implements Writable, Serializable,
ObjectInputValidation {
private static final Logger LOG = LoggerFactory.getLogger(FsPermission.class);
private static final long serialVersionUID = 0x2fe08564;
static final WritableFactory FACTORY = new WritableFactory() {
@Override
public Writable newInstance() { return new FsPermission(); }
};
static { // register a ctor
WritableFactories.setFactory(FsPermission.class, FACTORY);
WritableFactories.setFactory(ImmutableFsPermission.class, FACTORY);
}
/** Maximum acceptable length of a permission string to parse */
public static final int MAX_PERMISSION_LENGTH = 10;
/** Create an immutable {@link FsPermission} object. */
public static FsPermission createImmutable(short permission) {
return new ImmutableFsPermission(permission);
}
//POSIX permission style
private FsAction useraction = null;
private FsAction groupaction = null;
private FsAction otheraction = null;
private Boolean stickyBit = false;
private FsPermission() {}
/**
* Construct by the given {@link FsAction}.
* @param u user action
* @param g group action
* @param o other action
*/
public FsPermission(FsAction u, FsAction g, FsAction o) {
this(u, g, o, false);
}
public FsPermission(FsAction u, FsAction g, FsAction o, boolean sb) {
set(u, g, o, sb);
}
/**
* Construct by the given mode.
* @param mode
* @see #toShort()
*/
public FsPermission(short mode) { fromShort(mode); }
/**
* Construct by the given mode.
*
* octal mask is applied.
*
*<pre>
* before mask after mask file type sticky bit
*
* octal 100644 644 file no
* decimal 33188 420
*
* octal 101644 1644 file yes
* decimal 33700 1420
*
* octal 40644 644 directory no
* decimal 16804 420
*
* octal 41644 1644 directory yes
* decimal 17316 1420
*</pre>
*
* 100644 becomes 644 while 644 remains as 644
*
* @param mode Mode is supposed to come from the result of native stat() call.
* It contains complete permission information: rwxrwxrwx, sticky
* bit, whether it is a directory or a file, etc. Upon applying
* mask, only permission and sticky bit info will be kept because
* they are the only parts to be used for now.
* @see #FsPermission(short mode)
*/
public FsPermission(int mode) {
this((short)(mode & 01777));
}
/**
* Copy constructor
*
* @param other other permission
*/
public FsPermission(FsPermission other) {
this.useraction = other.useraction;
this.groupaction = other.groupaction;
this.otheraction = other.otheraction;
this.stickyBit = other.stickyBit;
}
/**
* Construct by given mode, either in octal or symbolic format.
* @param mode mode as a string, either in octal or symbolic format
* @throws IllegalArgumentException if <code>mode</code> is invalid
*/
public FsPermission(String mode) {
this(new RawParser(mode).getPermission());
}
/** Return user {@link FsAction}. */
public FsAction getUserAction() {return useraction;}
/** Return group {@link FsAction}. */
public FsAction getGroupAction() {return groupaction;}
/** Return other {@link FsAction}. */
public FsAction getOtherAction() {return otheraction;}
private void set(FsAction u, FsAction g, FsAction o, boolean sb) {
useraction = u;
groupaction = g;
otheraction = o;
stickyBit = sb;
}
public void fromShort(short n) {
FsAction[] v = FSACTION_VALUES;
set(v[(n >>> 6) & 7], v[(n >>> 3) & 7], v[n & 7], (((n >>> 9) & 1) == 1) );
}
@Override
@Deprecated
public void write(DataOutput out) throws IOException {
out.writeShort(toShort());
}
@Override
@Deprecated
public void readFields(DataInput in) throws IOException {
fromShort(in.readShort());
}
/**
* Get masked permission if exists.
*/
public FsPermission getMasked() {
return null;
}
/**
* Get unmasked permission if exists.
*/
public FsPermission getUnmasked() {
return null;
}
/**
* Create and initialize a {@link FsPermission} from {@link DataInput}.
*/
public static FsPermission read(DataInput in) throws IOException {
FsPermission p = new FsPermission();
p.fromShort(in.readShort());
return p;
}
/**
* Encode the object to a short.
*/
public short toShort() {
int s = (stickyBit ? 1 << 9 : 0) |
(useraction.ordinal() << 6) |
(groupaction.ordinal() << 3) |
otheraction.ordinal();
return (short)s;
}
/**
* Encodes the object to a short. Unlike {@link #toShort()}, this method may
* return values outside the fixed range 00000 - 01777 if extended features
* are encoded into this permission, such as the ACL bit.
*
* @return short extended short representation of this permission
*/
@Deprecated
public short toExtendedShort() {
return toShort();
}
/**
* Returns the FsPermission in an octal format.
*
* @return short Unlike {@link #toShort()} which provides a binary
* representation, this method returns the standard octal style permission.
*/
public short toOctal() {
int n = this.toShort();
int octal = (n>>>9&1)*1000 + (n>>>6&7)*100 + (n>>>3&7)*10 + (n&7);
return (short)octal;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FsPermission) {
FsPermission that = (FsPermission)obj;
return this.useraction == that.useraction
&& this.groupaction == that.groupaction
&& this.otheraction == that.otheraction
&& this.stickyBit.booleanValue() == that.stickyBit.booleanValue();
}
return false;
}
@Override
public int hashCode() {return toShort();}
@Override
public String toString() {
String str = useraction.SYMBOL + groupaction.SYMBOL + otheraction.SYMBOL;
if(stickyBit) {
StringBuilder str2 = new StringBuilder(str);
str2.replace(str2.length() - 1, str2.length(),
otheraction.implies(FsAction.EXECUTE) ? "t" : "T");
str = str2.toString();
}
return str;
}
/**
* Apply a umask to this permission and return a new one.
*
* The umask is used by create, mkdir, and other Hadoop filesystem operations.
* The mode argument for these operations is modified by removing the bits
* which are set in the umask. Thus, the umask limits the permissions which
* newly created files and directories get.
*
* @param umask The umask to use
*
* @return The effective permission
*/
public FsPermission applyUMask(FsPermission umask) {
return new FsPermission(useraction.and(umask.useraction.not()),
groupaction.and(umask.groupaction.not()),
otheraction.and(umask.otheraction.not()));
}
public static final String UMASK_LABEL =
CommonConfigurationKeys.FS_PERMISSIONS_UMASK_KEY;
public static final int DEFAULT_UMASK =
CommonConfigurationKeys.FS_PERMISSIONS_UMASK_DEFAULT;
private static final FsAction[] FSACTION_VALUES = FsAction.values();
/**
* Get the user file creation mask (umask)
*
* {@code UMASK_LABEL} config param has umask value that is either symbolic
* or octal.
*
* Symbolic umask is applied relative to file mode creation mask;
* the permission op characters '+' clears the corresponding bit in the mask,
* '-' sets bits in the mask.
*
* Octal umask, the specified bits are set in the file mode creation mask.
*/
public static FsPermission getUMask(Configuration conf) {
int umask = DEFAULT_UMASK;
// To ensure backward compatibility first use the deprecated key.
// If the deprecated key is not present then check for the new key
if(conf != null) {
String confUmask = conf.get(UMASK_LABEL);
try {
if(confUmask != null) {
umask = new UmaskParser(confUmask).getUMask();
}
} catch(IllegalArgumentException iae) {
// Provide more explanation for user-facing message
String type = iae instanceof NumberFormatException ? "decimal"
: "octal or symbolic";
String error = "Unable to parse configuration " + UMASK_LABEL
+ " with value " + confUmask + " as " + type + " umask.";
LOG.warn(error);
throw new IllegalArgumentException(error);
}
}
return new FsPermission((short)umask);
}
public boolean getStickyBit() {
return stickyBit;
}
/**
* Returns true if there is also an ACL (access control list).
*
* @return boolean true if there is also an ACL (access control list).
* @deprecated Get acl bit from the {@link org.apache.hadoop.fs.FileStatus}
* object.
*/
@Deprecated
public boolean getAclBit() {
// File system subclasses that support the ACL bit would override this.
return false;
}
/**
* Returns true if the file is encrypted or directory is in an encryption zone
* @deprecated Get encryption bit from the
* {@link org.apache.hadoop.fs.FileStatus} object.
*/
@Deprecated
public boolean getEncryptedBit() {
return false;
}
/**
* Returns true if the file or directory is erasure coded.
* @deprecated Get ec bit from the {@link org.apache.hadoop.fs.FileStatus}
* object.
*/
@Deprecated
public boolean getErasureCodedBit() {
return false;
}
/** Set the user file creation mask (umask) */
public static void setUMask(Configuration conf, FsPermission umask) {
conf.set(UMASK_LABEL, String.format("%1$03o", umask.toShort()));
}
/**
* Get the default permission for directory and symlink.
* In previous versions, this default permission was also used to
* create files, so files created end up with ugo+x permission.
* See HADOOP-9155 for detail.
* Two new methods are added to solve this, please use
* {@link FsPermission#getDirDefault()} for directory, and use
* {@link FsPermission#getFileDefault()} for file.
* This method is kept for compatibility.
*/
public static FsPermission getDefault() {
return new FsPermission((short)00777);
}
/**
* Get the default permission for directory.
*/
public static FsPermission getDirDefault() {
return new FsPermission((short)00777);
}
/**
* Get the default permission for file.
*/
public static FsPermission getFileDefault() {
return new FsPermission((short)00666);
}
/**
* Get the default permission for cache pools.
*/
public static FsPermission getCachePoolDefault() {
return new FsPermission((short)00755);
}
/**
* Create a FsPermission from a Unix symbolic permission string
* @param unixSymbolicPermission e.g. "-rw-rw-rw-"
*/
public static FsPermission valueOf(String unixSymbolicPermission) {
if (unixSymbolicPermission == null) {
return null;
}
else if (unixSymbolicPermission.length() != MAX_PERMISSION_LENGTH) {
throw new IllegalArgumentException(String.format(
"length != %d(unixSymbolicPermission=%s)", MAX_PERMISSION_LENGTH,
unixSymbolicPermission));
}
int n = 0;
for(int i = 1; i < unixSymbolicPermission.length(); i++) {
n = n << 1;
char c = unixSymbolicPermission.charAt(i);
n += (c == '-' || c == 'T' || c == 'S') ? 0: 1;
}
// Add sticky bit value if set
if(unixSymbolicPermission.charAt(9) == 't' ||
unixSymbolicPermission.charAt(9) == 'T')
n += 01000;
return new FsPermission((short)n);
}
private static class ImmutableFsPermission extends FsPermission {
private static final long serialVersionUID = 0x1bab54bd;
public ImmutableFsPermission(short permission) {
super(permission);
}
@Override
public void readFields(DataInput in) throws IOException {
throw new UnsupportedOperationException();
}
}
@Override
public void validateObject() throws InvalidObjectException {
if (null == useraction || null == groupaction || null == otheraction) {
throw new InvalidObjectException("Invalid mode in FsPermission");
}
if (null == stickyBit) {
throw new InvalidObjectException("No sticky bit in FsPermission");
}
}
}