|
@@ -6,9 +6,9 @@
|
|
|
* 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
|
|
|
- *
|
|
|
+ * <p>
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+ * <p>
|
|
|
* 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.
|
|
@@ -37,47 +37,45 @@ import org.apache.hadoop.fs.UnsupportedFileSystemException;
|
|
|
import org.apache.hadoop.security.UserGroupInformation;
|
|
|
import org.apache.hadoop.util.StringUtils;
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* InodeTree implements a mount-table as a tree of inodes.
|
|
|
* It is used to implement ViewFs and ViewFileSystem.
|
|
|
* In order to use it the caller must subclass it and implement
|
|
|
* the abstract methods {@link #getTargetFileSystem(INodeDir)}, etc.
|
|
|
- *
|
|
|
+ *
|
|
|
* The mountable is initialized from the config variables as
|
|
|
* specified in {@link ViewFs}
|
|
|
*
|
|
|
* @param <T> is AbstractFileSystem or FileSystem
|
|
|
- *
|
|
|
- * The three main methods are
|
|
|
- * {@link #InodeTreel(Configuration)} // constructor
|
|
|
+ *
|
|
|
+ * The two main methods are
|
|
|
* {@link #InodeTree(Configuration, String)} // constructor
|
|
|
* {@link #resolve(String, boolean)}
|
|
|
*/
|
|
|
|
|
|
@InterfaceAudience.Private
|
|
|
-@InterfaceStability.Unstable
|
|
|
+@InterfaceStability.Unstable
|
|
|
abstract class InodeTree<T> {
|
|
|
- static enum ResultKind {isInternalDir, isExternalDir;};
|
|
|
+ enum ResultKind {
|
|
|
+ INTERNAL_DIR,
|
|
|
+ EXTERNAL_DIR
|
|
|
+ }
|
|
|
+
|
|
|
static final Path SlashPath = new Path("/");
|
|
|
-
|
|
|
- final INodeDir<T> root; // the root of the mount table
|
|
|
-
|
|
|
- final String homedirPrefix; // the homedir config value for this mount table
|
|
|
-
|
|
|
- List<MountPoint<T>> mountPoints = new ArrayList<MountPoint<T>>();
|
|
|
-
|
|
|
-
|
|
|
+ private final INodeDir<T> root; // the root of the mount table
|
|
|
+ private final String homedirPrefix; // the homedir for this mount table
|
|
|
+ private List<MountPoint<T>> mountPoints = new ArrayList<MountPoint<T>>();
|
|
|
+
|
|
|
static class MountPoint<T> {
|
|
|
String src;
|
|
|
INodeLink<T> target;
|
|
|
+
|
|
|
MountPoint(String srcPath, INodeLink<T> mountLink) {
|
|
|
src = srcPath;
|
|
|
target = mountLink;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Breaks file path into component names.
|
|
|
* @param path
|
|
@@ -85,18 +83,19 @@ abstract class InodeTree<T> {
|
|
|
*/
|
|
|
static String[] breakIntoPathComponents(final String path) {
|
|
|
return path == null ? null : path.split(Path.SEPARATOR);
|
|
|
- }
|
|
|
-
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Internal class for inode tree
|
|
|
* @param <T>
|
|
|
*/
|
|
|
abstract static class INode<T> {
|
|
|
final String fullPath; // the full path to the root
|
|
|
+
|
|
|
public INode(String pathToNode, UserGroupInformation aUgi) {
|
|
|
fullPath = pathToNode;
|
|
|
}
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* Internal class to represent an internal dir of the mount table
|
|
@@ -106,37 +105,28 @@ abstract class InodeTree<T> {
|
|
|
final Map<String,INode<T>> children = new HashMap<String,INode<T>>();
|
|
|
T InodeDirFs = null; // file system of this internal directory of mountT
|
|
|
boolean isRoot = false;
|
|
|
-
|
|
|
+
|
|
|
INodeDir(final String pathToNode, final UserGroupInformation aUgi) {
|
|
|
super(pathToNode, aUgi);
|
|
|
}
|
|
|
|
|
|
- INode<T> resolve(final String pathComponent) throws FileNotFoundException {
|
|
|
- final INode<T> result = resolveInternal(pathComponent);
|
|
|
- if (result == null) {
|
|
|
- throw new FileNotFoundException();
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
INode<T> resolveInternal(final String pathComponent) {
|
|
|
return children.get(pathComponent);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
INodeDir<T> addDir(final String pathComponent,
|
|
|
- final UserGroupInformation aUgi)
|
|
|
- throws FileAlreadyExistsException {
|
|
|
+ final UserGroupInformation aUgi) throws FileAlreadyExistsException {
|
|
|
if (children.containsKey(pathComponent)) {
|
|
|
throw new FileAlreadyExistsException();
|
|
|
}
|
|
|
- final INodeDir<T> newDir = new INodeDir<T>(fullPath+ (isRoot ? "" : "/") +
|
|
|
- pathComponent, aUgi);
|
|
|
+ final INodeDir<T> newDir = new INodeDir<T>(fullPath +
|
|
|
+ (isRoot ? "" : "/") + pathComponent, aUgi);
|
|
|
children.put(pathComponent, newDir);
|
|
|
return newDir;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
void addLink(final String pathComponent, final INodeLink<T> link)
|
|
|
- throws FileAlreadyExistsException {
|
|
|
+ throws FileAlreadyExistsException {
|
|
|
if (children.containsKey(pathComponent)) {
|
|
|
throw new FileAlreadyExistsException();
|
|
|
}
|
|
@@ -145,14 +135,14 @@ abstract class InodeTree<T> {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * In internal class to represent a mount link
|
|
|
+ * An internal class to represent a mount link.
|
|
|
* A mount link can be single dir link or a merge dir link.
|
|
|
|
|
|
* A merge dir link is a merge (junction) of links to dirs:
|
|
|
- * example : <merge of 2 dirs
|
|
|
+ * example : merge of 2 dirs
|
|
|
* /users -> hdfs:nn1//users
|
|
|
* /users -> hdfs:nn2//users
|
|
|
- *
|
|
|
+ *
|
|
|
* For a merge, each target is checked to be dir when created but if target
|
|
|
* is changed later it is then ignored (a dir with null entries)
|
|
|
*/
|
|
@@ -163,8 +153,9 @@ abstract class InodeTree<T> {
|
|
|
// Function to initialize file system. Only applicable for simple links
|
|
|
private Function<URI, T> fileSystemInitMethod;
|
|
|
private final Object lock = new Object();
|
|
|
+
|
|
|
/**
|
|
|
- * Construct a mergeLink
|
|
|
+ * Construct a mergeLink.
|
|
|
*/
|
|
|
INodeLink(final String pathToNode, final UserGroupInformation aUgi,
|
|
|
final T targetMergeFs, final URI[] aTargetDirLinkList) {
|
|
@@ -173,9 +164,9 @@ abstract class InodeTree<T> {
|
|
|
targetDirLinkList = aTargetDirLinkList;
|
|
|
isMergeLink = true;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
- * Construct a simple link (i.e. not a mergeLink)
|
|
|
+ * Construct a simple link (i.e. not a mergeLink).
|
|
|
*/
|
|
|
INodeLink(final String pathToNode, final UserGroupInformation aUgi,
|
|
|
Function<URI, T> createFileSystemMethod,
|
|
@@ -187,16 +178,15 @@ abstract class InodeTree<T> {
|
|
|
isMergeLink = false;
|
|
|
this.fileSystemInitMethod = createFileSystemMethod;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
- * Get the target of the link
|
|
|
- * If a merge link then it returned as "," separated URI list.
|
|
|
+ * Get the target of the link. If a merge link then it returned
|
|
|
+ * as "," separated URI list.
|
|
|
*/
|
|
|
Path getTargetLink() {
|
|
|
- // is merge link - use "," as separator between the merged URIs
|
|
|
- //String result = targetDirLinkList[0].toString();
|
|
|
StringBuilder result = new StringBuilder(targetDirLinkList[0].toString());
|
|
|
- for (int i=1; i < targetDirLinkList.length; ++i) {
|
|
|
+ // If merge link, use "," as separator between the merged URIs
|
|
|
+ for (int i = 1; i < targetDirLinkList.length; ++i) {
|
|
|
result.append(',').append(targetDirLinkList[i].toString());
|
|
|
}
|
|
|
return new Path(result.toString());
|
|
@@ -230,22 +220,21 @@ abstract class InodeTree<T> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
private void createLink(final String src, final String target,
|
|
|
final boolean isLinkMerge, final UserGroupInformation aUgi)
|
|
|
throws URISyntaxException, IOException,
|
|
|
- FileAlreadyExistsException, UnsupportedFileSystemException {
|
|
|
+ FileAlreadyExistsException, UnsupportedFileSystemException {
|
|
|
// Validate that src is valid absolute path
|
|
|
- final Path srcPath = new Path(src);
|
|
|
+ final Path srcPath = new Path(src);
|
|
|
if (!srcPath.isAbsoluteAndSchemeAuthorityNull()) {
|
|
|
- throw new IOException("ViewFs:Non absolute mount name in config:" + src);
|
|
|
+ throw new IOException("ViewFs: Non absolute mount name in config:" + src);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
final String[] srcPaths = breakIntoPathComponents(src);
|
|
|
INodeDir<T> curInode = root;
|
|
|
int i;
|
|
|
// Ignore first initial slash, process all except last component
|
|
|
- for (i = 1; i < srcPaths.length-1; i++) {
|
|
|
+ for (i = 1; i < srcPaths.length - 1; i++) {
|
|
|
final String iPath = srcPaths[i];
|
|
|
INode<T> nextInode = curInode.resolveInternal(iPath);
|
|
|
if (nextInode == null) {
|
|
@@ -258,11 +247,11 @@ abstract class InodeTree<T> {
|
|
|
throw new FileAlreadyExistsException("Path " + nextInode.fullPath +
|
|
|
" already exists as link");
|
|
|
} else {
|
|
|
- assert(nextInode instanceof INodeDir);
|
|
|
+ assert (nextInode instanceof INodeDir);
|
|
|
curInode = (INodeDir<T>) nextInode;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Now process the last component
|
|
|
// Add the link in 2 cases: does not exist or a link exists
|
|
|
String iPath = srcPaths[i];// last component
|
|
@@ -273,9 +262,9 @@ abstract class InodeTree<T> {
|
|
|
strB.append('/').append(srcPaths[j]);
|
|
|
}
|
|
|
throw new FileAlreadyExistsException("Path " + strB +
|
|
|
- " already exists as dir; cannot create link here");
|
|
|
+ " already exists as dir; cannot create link here");
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
final INodeLink<T> newLink;
|
|
|
final String fullPath = curInode.fullPath + (curInode == root ? "" : "/")
|
|
|
+ iPath;
|
|
@@ -295,24 +284,20 @@ abstract class InodeTree<T> {
|
|
|
curInode.addLink(iPath, newLink);
|
|
|
mountPoints.add(new MountPoint<T>(src, newLink));
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * Below the "public" methods of InodeTree
|
|
|
- */
|
|
|
|
|
|
/**
|
|
|
* The user of this class must subclass and implement the following
|
|
|
* 3 abstract methods.
|
|
|
- * @throws IOException
|
|
|
+ * @throws IOException
|
|
|
*/
|
|
|
protected abstract Function<URI, T> initAndGetTargetFs();
|
|
|
-
|
|
|
+
|
|
|
protected abstract T getTargetFileSystem(final INodeDir<T> dir)
|
|
|
- throws URISyntaxException;
|
|
|
-
|
|
|
+ throws URISyntaxException;
|
|
|
+
|
|
|
protected abstract T getTargetFileSystem(final URI[] mergeFsURIList)
|
|
|
- throws UnsupportedFileSystemException, URISyntaxException;
|
|
|
-
|
|
|
+ throws UnsupportedFileSystemException, URISyntaxException;
|
|
|
+
|
|
|
/**
|
|
|
* Create Inode Tree from the specified mount-table specified in Config
|
|
|
* @param config - the mount table keys are prefixed with
|
|
@@ -325,7 +310,7 @@ abstract class InodeTree<T> {
|
|
|
*/
|
|
|
protected InodeTree(final Configuration config, final String viewName)
|
|
|
throws UnsupportedFileSystemException, URISyntaxException,
|
|
|
- FileAlreadyExistsException, IOException {
|
|
|
+ FileAlreadyExistsException, IOException {
|
|
|
String vName = viewName;
|
|
|
if (vName == null) {
|
|
|
vName = Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE;
|
|
@@ -334,9 +319,9 @@ abstract class InodeTree<T> {
|
|
|
root = new INodeDir<T>("/", UserGroupInformation.getCurrentUser());
|
|
|
root.InodeDirFs = getTargetFileSystem(root);
|
|
|
root.isRoot = true;
|
|
|
-
|
|
|
- final String mtPrefix = Constants.CONFIG_VIEWFS_PREFIX + "." +
|
|
|
- vName + ".";
|
|
|
+
|
|
|
+ final String mtPrefix = Constants.CONFIG_VIEWFS_PREFIX + "." +
|
|
|
+ vName + ".";
|
|
|
final String linkPrefix = Constants.CONFIG_VIEWFS_LINK + ".";
|
|
|
final String linkMergePrefix = Constants.CONFIG_VIEWFS_LINK_MERGE + ".";
|
|
|
boolean gotMountTableEntry = false;
|
|
@@ -362,18 +347,17 @@ abstract class InodeTree<T> {
|
|
|
// ignore - we set home dir from config
|
|
|
continue;
|
|
|
} else {
|
|
|
- throw new IOException(
|
|
|
- "ViewFs: Cannot initialize: Invalid entry in Mount table in config: "+
|
|
|
- src);
|
|
|
+ throw new IOException("ViewFs: Cannot initialize: Invalid entry in " +
|
|
|
+ "Mount table in config: " + src);
|
|
|
}
|
|
|
final String target = si.getValue(); // link or merge link
|
|
|
- createLink(src, target, isMergeLink, ugi);
|
|
|
+ createLink(src, target, isMergeLink, ugi);
|
|
|
}
|
|
|
}
|
|
|
if (!gotMountTableEntry) {
|
|
|
throw new IOException(
|
|
|
"ViewFs: Cannot initialize: Empty Mount table in config for " +
|
|
|
- "viewfs://" + vName + "/");
|
|
|
+ "viewfs://" + vName + "/");
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -381,7 +365,7 @@ abstract class InodeTree<T> {
|
|
|
* Resolve returns ResolveResult.
|
|
|
* The caller can continue the resolution of the remainingPath
|
|
|
* in the targetFileSystem.
|
|
|
- *
|
|
|
+ *
|
|
|
* If the input pathname leads to link to another file system then
|
|
|
* the targetFileSystem is the one denoted by the link (except it is
|
|
|
* file system chrooted to link target.
|
|
@@ -393,7 +377,7 @@ abstract class InodeTree<T> {
|
|
|
final T targetFileSystem;
|
|
|
final String resolvedPath;
|
|
|
final Path remainingPath; // to resolve in the target FileSystem
|
|
|
-
|
|
|
+
|
|
|
ResolveResult(final ResultKind k, final T targetFs, final String resolveP,
|
|
|
final Path remainingP) {
|
|
|
kind = k;
|
|
@@ -401,31 +385,31 @@ abstract class InodeTree<T> {
|
|
|
resolvedPath = resolveP;
|
|
|
remainingPath = remainingP;
|
|
|
}
|
|
|
-
|
|
|
- // isInternalDir of path resolution completed within the mount table
|
|
|
+
|
|
|
+ // Internal dir path resolution completed within the mount table
|
|
|
boolean isInternalDir() {
|
|
|
- return (kind == ResultKind.isInternalDir);
|
|
|
+ return (kind == ResultKind.INTERNAL_DIR);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Resolve the pathname p relative to root InodeDir
|
|
|
* @param p - inout path
|
|
|
- * @param resolveLastComponent
|
|
|
+ * @param resolveLastComponent
|
|
|
* @return ResolveResult which allows further resolution of the remaining path
|
|
|
* @throws FileNotFoundException
|
|
|
*/
|
|
|
ResolveResult<T> resolve(final String p, final boolean resolveLastComponent)
|
|
|
- throws IOException {
|
|
|
+ throws IOException {
|
|
|
// TO DO: - more efficient to not split the path, but simply compare
|
|
|
String[] path = breakIntoPathComponents(p);
|
|
|
if (path.length <= 1) { // special case for when path is "/"
|
|
|
- ResolveResult<T> res =
|
|
|
- new ResolveResult<T>(ResultKind.isInternalDir,
|
|
|
+ ResolveResult<T> res =
|
|
|
+ new ResolveResult<T>(ResultKind.INTERNAL_DIR,
|
|
|
root.InodeDirFs, root.fullPath, SlashPath);
|
|
|
return res;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
INodeDir<T> curInode = root;
|
|
|
int i;
|
|
|
// ignore first slash
|
|
@@ -433,27 +417,27 @@ abstract class InodeTree<T> {
|
|
|
INode<T> nextInode = curInode.resolveInternal(path[i]);
|
|
|
if (nextInode == null) {
|
|
|
StringBuilder failedAt = new StringBuilder(path[0]);
|
|
|
- for ( int j = 1; j <=i; ++j) {
|
|
|
+ for (int j = 1; j <= i; ++j) {
|
|
|
failedAt.append('/').append(path[j]);
|
|
|
}
|
|
|
- throw (new FileNotFoundException(failedAt.toString()));
|
|
|
+ throw (new FileNotFoundException(failedAt.toString()));
|
|
|
}
|
|
|
|
|
|
if (nextInode instanceof INodeLink) {
|
|
|
final INodeLink<T> link = (INodeLink<T>) nextInode;
|
|
|
final Path remainingPath;
|
|
|
- if (i >= path.length-1) {
|
|
|
+ if (i >= path.length - 1) {
|
|
|
remainingPath = SlashPath;
|
|
|
} else {
|
|
|
- StringBuilder remainingPathStr = new StringBuilder("/" + path[i+1]);
|
|
|
- for (int j = i+2; j< path.length; ++j) {
|
|
|
+ StringBuilder remainingPathStr = new StringBuilder("/" + path[i + 1]);
|
|
|
+ for (int j = i + 2; j < path.length; ++j) {
|
|
|
remainingPathStr.append('/').append(path[j]);
|
|
|
}
|
|
|
remainingPath = new Path(remainingPathStr.toString());
|
|
|
}
|
|
|
- final ResolveResult<T> res =
|
|
|
- new ResolveResult<T>(ResultKind.isExternalDir,
|
|
|
- link.getTargetFileSystem(), nextInode.fullPath, remainingPath);
|
|
|
+ final ResolveResult<T> res =
|
|
|
+ new ResolveResult<T>(ResultKind.EXTERNAL_DIR,
|
|
|
+ link.getTargetFileSystem(), nextInode.fullPath, remainingPath);
|
|
|
return res;
|
|
|
} else if (nextInode instanceof INodeDir) {
|
|
|
curInode = (INodeDir<T>) nextInode;
|
|
@@ -470,23 +454,23 @@ abstract class InodeTree<T> {
|
|
|
// that follows will do a children.get(remaningPath) and will have to
|
|
|
// strip-out the initial /
|
|
|
StringBuilder remainingPathStr = new StringBuilder("/" + path[i]);
|
|
|
- for (int j = i+1; j< path.length; ++j) {
|
|
|
+ for (int j = i + 1; j < path.length; ++j) {
|
|
|
remainingPathStr.append('/').append(path[j]);
|
|
|
}
|
|
|
remainingPath = new Path(remainingPathStr.toString());
|
|
|
}
|
|
|
- final ResolveResult<T> res =
|
|
|
- new ResolveResult<T>(ResultKind.isInternalDir,
|
|
|
- curInode.InodeDirFs, curInode.fullPath, remainingPath);
|
|
|
+ final ResolveResult<T> res =
|
|
|
+ new ResolveResult<T>(ResultKind.INTERNAL_DIR,
|
|
|
+ curInode.InodeDirFs, curInode.fullPath, remainingPath);
|
|
|
return res;
|
|
|
}
|
|
|
-
|
|
|
- List<MountPoint<T>> getMountPoints() {
|
|
|
+
|
|
|
+ List<MountPoint<T>> getMountPoints() {
|
|
|
return mountPoints;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
- *
|
|
|
+ *
|
|
|
* @return home dir value from mount table; null if no config value
|
|
|
* was found.
|
|
|
*/
|