org.apache.hadoop.fs.permission.FsPermission.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.fs.permission.FsPermission.java

Source

/**
 * 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");
        }
    }
}