/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.master.file.meta; import alluxio.collections.FieldIndex; import alluxio.collections.IndexDefinition; import alluxio.collections.UniqueFieldIndex; import alluxio.exception.InvalidPathException; import alluxio.master.ProtobufUtils; import alluxio.master.file.options.CreateDirectoryOptions; import alluxio.proto.journal.File.InodeDirectoryEntry; import alluxio.proto.journal.Journal.JournalEntry; import alluxio.wire.FileInfo; import com.google.common.collect.ImmutableSet; import java.util.HashSet; import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; /** * Alluxio file system's directory representation in the file system master. The inode must be * locked ({@link #lockRead()} or {@link #lockWrite()}) before methods are called. */ @NotThreadSafe public final class InodeDirectory extends Inode<InodeDirectory> { private static final IndexDefinition<Inode<?>> NAME_INDEX = new IndexDefinition<Inode<?>>(true) { @Override public Object getFieldValue(Inode<?> o) { return o.getName(); } }; /** Use UniqueFieldIndex directly for name index rather than using IndexedSet. */ private final FieldIndex<Inode<?>> mChildren = new UniqueFieldIndex<>(NAME_INDEX); private boolean mMountPoint; private boolean mDirectChildrenLoaded; /** * Creates a new instance of {@link InodeDirectory}. * * @param id the id to use */ private InodeDirectory(long id) { super(id, true); mMountPoint = false; mDirectChildrenLoaded = false; } @Override protected InodeDirectory getThis() { return this; } /** * Adds the given inode to the set of children. * * @param child the inode to add * @return true if inode was added successfully, false otherwise */ public boolean addChild(Inode<?> child) { return mChildren.add(child); } /** * @param name the name of the child * @return the inode with the given name, or null if there is no child with that name */ public Inode<?> getChild(String name) { return mChildren.getFirst(name); } /** * @param name the name of the child * @param lockList the lock list to add the lock to * @return the read-locked inode with the given name, or null if there is no such child * @throws InvalidPathException if the path to the child is invalid */ public Inode<?> getChildReadLock(String name, InodeLockList lockList) throws InvalidPathException { while (true) { Inode child = mChildren.getFirst(name); if (child == null) { return null; } lockList.lockReadAndCheckParent(child, this); if (mChildren.getFirst(name) != child) { // The locked child has changed, so unlock and try again. lockList.unlockLast(); continue; } return child; } } /** * @param name the name of the child * @param lockList the lock list to add the lock to * @return the write-locked inode with the given name, or null if there is no such child * @throws InvalidPathException if the path to the child is invalid */ public Inode<?> getChildWriteLock(String name, InodeLockList lockList) throws InvalidPathException { while (true) { Inode child = mChildren.getFirst(name); if (child == null) { return null; } lockList.lockWriteAndCheckParent(child, this); if (mChildren.getFirst(name) != child) { // The locked child has changed, so unlock and try again. lockList.unlockLast(); continue; } return child; } } /** * @return an unmodifiable set of the children inodes */ public Set<Inode<?>> getChildren() { return ImmutableSet.copyOf(mChildren.iterator()); } /** * @return the ids of the children */ public Set<Long> getChildrenIds() { Set<Long> ret = new HashSet<>(mChildren.size()); for (Inode<?> child : mChildren) { ret.add(child.getId()); } return ret; } /** * @return the number of children in the directory */ public int getNumberOfChildren() { return mChildren.size(); } /** * @return true if the inode is a mount point, false otherwise */ public boolean isMountPoint() { return mMountPoint; } /** * @return true if we have loaded all the direct children's metadata once */ public synchronized boolean isDirectChildrenLoaded() { return mDirectChildrenLoaded; } /** * Removes the given inode from the directory. * * @param child the Inode to remove * @return true if the inode was removed, false otherwise */ public boolean removeChild(Inode<?> child) { return mChildren.remove(child); } /** * Removes the given child by its name from the directory. * * @param name the name of the Inode to remove * @return true if the inode was removed, false otherwise */ public boolean removeChild(String name) { Inode<?> child = mChildren.getFirst(name); return mChildren.remove(child); } /** * @param mountPoint the mount point flag value to use * @return the updated object */ public InodeDirectory setMountPoint(boolean mountPoint) { mMountPoint = mountPoint; return getThis(); } /** * @param directChildrenLoaded whether to load the direct children if they were not loaded before * @return the updated object */ public synchronized InodeDirectory setDirectChildrenLoaded(boolean directChildrenLoaded) { mDirectChildrenLoaded = directChildrenLoaded; return getThis(); } /** * Generates client file info for a folder. * * @param path the path of the folder in the filesystem * @return the generated {@link FileInfo} */ @Override public FileInfo generateClientFileInfo(String path) { FileInfo ret = new FileInfo(); ret.setFileId(getId()); ret.setName(getName()); ret.setPath(path); ret.setLength(mChildren.size()); ret.setBlockSizeBytes(0); ret.setCreationTimeMs(getCreationTimeMs()); ret.setCompleted(true); ret.setFolder(isDirectory()); ret.setPinned(isPinned()); ret.setCacheable(false); ret.setPersisted(isPersisted()); ret.setLastModificationTimeMs(getLastModificationTimeMs()); ret.setTtl(mTtl); ret.setTtlAction(mTtlAction); ret.setOwner(getOwner()); ret.setGroup(getGroup()); ret.setMode(getMode()); ret.setPersistenceState(getPersistenceState().toString()); ret.setMountPoint(isMountPoint()); return ret; } @Override public String toString() { return toStringHelper().add("mountPoint", mMountPoint).add("children", mChildren).toString(); } /** * Converts the entry to an {@link InodeDirectory}. * * @param entry the entry to convert * @return the {@link InodeDirectory} representation */ public static InodeDirectory fromJournalEntry(InodeDirectoryEntry entry) { return new InodeDirectory(entry.getId()) .setCreationTimeMs(entry.getCreationTimeMs()) .setName(entry.getName()) .setParentId(entry.getParentId()) .setPersistenceState(PersistenceState.valueOf(entry.getPersistenceState())) .setPinned(entry.getPinned()) .setLastModificationTimeMs(entry.getLastModificationTimeMs(), true) .setOwner(entry.getOwner()) .setGroup(entry.getGroup()) .setMode((short) entry.getMode()) .setMountPoint(entry.getMountPoint()) .setTtl(entry.getTtl()) .setTtlAction(ProtobufUtils.fromProtobuf(entry.getTtlAction())) .setDirectChildrenLoaded(entry.getDirectChildrenLoaded()); } /** * Creates an {@link InodeDirectory}. * * @param id id of this inode * @param parentId id of the parent of this inode * @param name name of this inode * @param options options to create this directory * @return the {@link InodeDirectory} representation */ public static InodeDirectory create(long id, long parentId, String name, CreateDirectoryOptions options) { return new InodeDirectory(id) .setParentId(parentId) .setName(name) .setTtl(options.getTtl()) .setTtlAction(options.getTtlAction()) .setOwner(options.getOwner()) .setGroup(options.getGroup()) .setMode(options.getMode().toShort()) .setMountPoint(options.isMountPoint()); } @Override public JournalEntry toJournalEntry() { InodeDirectoryEntry inodeDirectory = InodeDirectoryEntry.newBuilder() .setCreationTimeMs(getCreationTimeMs()) .setId(getId()) .setName(getName()) .setParentId(getParentId()) .setPersistenceState(getPersistenceState().name()) .setPinned(isPinned()) .setLastModificationTimeMs(getLastModificationTimeMs()) .setMountPoint(isMountPoint()) .setTtl(getTtl()) .setTtlAction(ProtobufUtils.toProtobuf(getTtlAction())) .setDirectChildrenLoaded(isDirectChildrenLoaded()) .setOwner(getOwner()).setGroup(getGroup()).setMode(getMode()) .build(); return JournalEntry.newBuilder().setInodeDirectory(inodeDirectory).build(); } }