/** * 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.hdfs.server.namenode; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.fs.permission.PermissionStatus; import org.apache.hadoop.hdfs.protocol.QuotaExceededException; import org.apache.hadoop.hdfs.server.namenode.INode.Feature; import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot; import org.apache.hadoop.util.LightWeightGSet.LinkedElement; import com.google.common.base.Preconditions; /** * {@link INode} with additional fields including id, name, permission, * access time and modification time. */ @InterfaceAudience.Private public abstract class INodeWithAdditionalFields extends INode implements LinkedElement { static enum PermissionStatusFormat { MODE(0, 16), GROUP(MODE.OFFSET + MODE.LENGTH, 25), USER(GROUP.OFFSET + GROUP.LENGTH, 23); final int OFFSET; final int LENGTH; //bit length final long MASK; PermissionStatusFormat(int offset, int length) { OFFSET = offset; LENGTH = length; MASK = ((-1L) >>> (64 - LENGTH)) << OFFSET; } long retrieve(long record) { return (record & MASK) >>> OFFSET; } long combine(long bits, long record) { return (record & ~MASK) | (bits << OFFSET); } /** Encode the {@link PermissionStatus} to a long. */ static long toLong(PermissionStatus ps) { long permission = 0L; final int user = SerialNumberManager.INSTANCE.getUserSerialNumber( ps.getUserName()); permission = USER.combine(user, permission); final int group = SerialNumberManager.INSTANCE.getGroupSerialNumber( ps.getGroupName()); permission = GROUP.combine(group, permission); final int mode = ps.getPermission().toShort(); permission = MODE.combine(mode, permission); return permission; } } /** The inode id. */ final private long id; /** * The inode name is in java UTF8 encoding; * The name in HdfsFileStatus should keep the same encoding as this. * if this encoding is changed, implicitly getFileInfo and listStatus in * clientProtocol are changed; The decoding at the client * side should change accordingly. */ private byte[] name = null; /** * Permission encoded using {@link PermissionStatusFormat}. * Codes other than {@link #clonePermissionStatus(INodeWithAdditionalFields)} * and {@link #updatePermissionStatus(PermissionStatusFormat, long)} * should not modify it. */ private long permission = 0L; /** The last modification time*/ private long modificationTime = 0L; /** The last access time*/ private long accessTime = 0L; /** For implementing {@link LinkedElement}. */ private LinkedElement next = null; /** An array {@link Feature}s. */ private static final Feature[] EMPTY_FEATURE = new Feature[0]; protected Feature[] features = EMPTY_FEATURE; private INodeWithAdditionalFields(INode parent, long id, byte[] name, long permission, long modificationTime, long accessTime) { super(parent); this.id = id; this.name = name; this.permission = permission; this.modificationTime = modificationTime; this.accessTime = accessTime; } INodeWithAdditionalFields(long id, byte[] name, PermissionStatus permissions, long modificationTime, long accessTime) { this(null, id, name, PermissionStatusFormat.toLong(permissions), modificationTime, accessTime); } /** @param other Other node to be copied */ INodeWithAdditionalFields(INodeWithAdditionalFields other) { this(other.getParentReference() != null ? other.getParentReference() : other.getParent(), other.getId(), other.getLocalNameBytes(), other.permission, other.modificationTime, other.accessTime); } @Override public void setNext(LinkedElement next) { this.next = next; } @Override public LinkedElement getNext() { return next; } /** Get inode id */ @Override public final long getId() { return this.id; } @Override public final byte[] getLocalNameBytes() { return name; } @Override public final void setLocalName(byte[] name) { this.name = name; } /** Clone the {@link PermissionStatus}. */ final void clonePermissionStatus(INodeWithAdditionalFields that) { this.permission = that.permission; } @Override final PermissionStatus getPermissionStatus(int snapshotId) { return new PermissionStatus(getUserName(snapshotId), getGroupName(snapshotId), getFsPermission(snapshotId)); } private final void updatePermissionStatus(PermissionStatusFormat f, long n) { this.permission = f.combine(n, permission); } @Override final String getUserName(int snapshotId) { if (snapshotId != Snapshot.CURRENT_STATE_ID) { return getSnapshotINode(snapshotId).getUserName(); } int n = (int)PermissionStatusFormat.USER.retrieve(permission); return SerialNumberManager.INSTANCE.getUser(n); } @Override final void setUser(String user) { int n = SerialNumberManager.INSTANCE.getUserSerialNumber(user); updatePermissionStatus(PermissionStatusFormat.USER, n); } @Override final String getGroupName(int snapshotId) { if (snapshotId != Snapshot.CURRENT_STATE_ID) { return getSnapshotINode(snapshotId).getGroupName(); } int n = (int)PermissionStatusFormat.GROUP.retrieve(permission); return SerialNumberManager.INSTANCE.getGroup(n); } @Override final void setGroup(String group) { int n = SerialNumberManager.INSTANCE.getGroupSerialNumber(group); updatePermissionStatus(PermissionStatusFormat.GROUP, n); } @Override final FsPermission getFsPermission(int snapshotId) { if (snapshotId != Snapshot.CURRENT_STATE_ID) { return getSnapshotINode(snapshotId).getFsPermission(); } return new FsPermission(getFsPermissionShort()); } @Override public final short getFsPermissionShort() { return (short)PermissionStatusFormat.MODE.retrieve(permission); } @Override void setPermission(FsPermission permission) { final short mode = permission.toShort(); updatePermissionStatus(PermissionStatusFormat.MODE, mode); } @Override public long getPermissionLong() { return permission; } @Override final AclFeature getAclFeature(int snapshotId) { if (snapshotId != Snapshot.CURRENT_STATE_ID) { return getSnapshotINode(snapshotId).getAclFeature(); } return getFeature(AclFeature.class); } @Override final long getModificationTime(int snapshotId) { if (snapshotId != Snapshot.CURRENT_STATE_ID) { return getSnapshotINode(snapshotId).getModificationTime(); } return this.modificationTime; } /** Update modification time if it is larger than the current value. */ @Override public final INode updateModificationTime(long mtime, int latestSnapshotId) throws QuotaExceededException { Preconditions.checkState(isDirectory()); if (mtime <= modificationTime) { return this; } return setModificationTime(mtime, latestSnapshotId); } final void cloneModificationTime(INodeWithAdditionalFields that) { this.modificationTime = that.modificationTime; } @Override public final void setModificationTime(long modificationTime) { this.modificationTime = modificationTime; } @Override final long getAccessTime(int snapshotId) { if (snapshotId != Snapshot.CURRENT_STATE_ID) { return getSnapshotINode(snapshotId).getAccessTime(); } return accessTime; } /** * Set last access time of inode. */ @Override public final void setAccessTime(long accessTime) { this.accessTime = accessTime; } protected void addFeature(Feature f) { int size = features.length; Feature[] arr = new Feature[size + 1]; if (size != 0) { System.arraycopy(features, 0, arr, 0, size); } arr[size] = f; features = arr; } protected void removeFeature(Feature f) { int size = features.length; Preconditions.checkState(size > 0, "Feature " + f.getClass().getSimpleName() + " not found."); if (size == 1) { Preconditions.checkState(features[0] == f, "Feature " + f.getClass().getSimpleName() + " not found."); features = EMPTY_FEATURE; return; } Feature[] arr = new Feature[size - 1]; int j = 0; boolean overflow = false; for (Feature f1 : features) { if (f1 != f) { if (j == size - 1) { overflow = true; break; } else { arr[j++] = f1; } } } Preconditions.checkState(!overflow && j == size - 1, "Feature " + f.getClass().getSimpleName() + " not found."); features = arr; } protected <T extends Feature> T getFeature(Class<? extends Feature> clazz) { for (Feature f : features) { if (f.getClass() == clazz) { @SuppressWarnings("unchecked") T ret = (T) f; return ret; } } return null; } public void removeAclFeature() { AclFeature f = getAclFeature(); Preconditions.checkNotNull(f); removeFeature(f); } public void addAclFeature(AclFeature f) { AclFeature f1 = getAclFeature(); if (f1 != null) throw new IllegalStateException("Duplicated ACLFeature"); addFeature(f); } public final Feature[] getFeatures() { return features; } }