/** * 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.viewfs; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.EnumSet; import java.util.List; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.fs.AbstractFileSystem; import org.apache.hadoop.fs.BlockLocation; import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileChecksum; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FsServerDefaults; import org.apache.hadoop.fs.FsStatus; import org.apache.hadoop.fs.Options.ChecksumOpt; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.UnresolvedLinkException; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.util.Progressable; /** * <code>ChrootedFs</code> is a file system with its root some path * below the root of its base file system. * Example: For a base file system hdfs://nn1/ with chRoot at /usr/foo, the * members will be setup as shown below. * <ul> * <li>myFs is the base file system and points to hdfs at nn1</li> * <li>myURI is hdfs://nn1/user/foo</li> * <li>chRootPathPart is /user/foo</li> * <li>workingDir is a directory related to chRoot</li> * </ul> * * The paths are resolved as follows by ChRootedFileSystem: * <ul> * <li> Absolute path /a/b/c is resolved to /user/foo/a/b/c at myFs</li> * <li> Relative path x/y is resolved to /user/foo/<workingDir>/x/y</li> * </ul> * */ @InterfaceAudience.Private @InterfaceStability.Evolving /*Evolving for a release,to be changed to Stable */ class ChRootedFs extends AbstractFileSystem { private final AbstractFileSystem myFs; // the base file system whose root is changed private final URI myUri; // the base URI + the chroot private final Path chRootPathPart; // the root below the root of the base private final String chRootPathPartString; protected AbstractFileSystem getMyFs() { return myFs; } /** * * @param path * @return return full path including the chroot */ protected Path fullPath(final Path path) { super.checkPath(path); return new Path((chRootPathPart.isRoot() ? "" : chRootPathPartString) + path.toUri().getPath()); } @Override public boolean isValidName(String src) { return myFs.isValidName(fullPath(new Path(src)).toUri().toString()); } public ChRootedFs(final AbstractFileSystem fs, final Path theRoot) throws URISyntaxException { super(fs.getUri(), fs.getUri().getScheme(), fs.getUri().getAuthority() != null, fs.getUriDefaultPort()); myFs = fs; myFs.checkPath(theRoot); chRootPathPart = new Path(myFs.getUriPath(theRoot)); chRootPathPartString = chRootPathPart.toUri().getPath(); /* * We are making URI include the chrootedPath: e.g. file:///chrootedPath. * This is questionable since Path#makeQualified(uri, path) ignores * the pathPart of a uri. Since this class is internal we can ignore * this issue but if we were to make it external then this needs * to be resolved. */ // Handle the two cases: // scheme:/// and scheme://authority/ myUri = new URI(myFs.getUri().toString() + (myFs.getUri().getAuthority() == null ? "" : Path.SEPARATOR) + chRootPathPart.toUri().getPath().substring(1)); super.checkPath(theRoot); } @Override public URI getUri() { return myUri; } /** * * Strip out the root from the path. * * @param p - fully qualified path p * @return - the remaining path without the begining / */ public String stripOutRoot(final Path p) { try { checkPath(p); } catch (IllegalArgumentException e) { throw new RuntimeException("Internal Error - path " + p + " should have been with URI" + myUri); } String pathPart = p.toUri().getPath(); return (pathPart.length() == chRootPathPartString.length()) ? "" : pathPart.substring(chRootPathPartString.length() + (chRootPathPart.isRoot() ? 0 : 1)); } @Override public Path getHomeDirectory() { return myFs.getHomeDirectory(); } @Override public Path getInitialWorkingDirectory() { /* * 3 choices here: return null or / or strip out the root out of myFs's * inital wd. * Only reasonable choice for initialWd for chrooted fds is null */ return null; } public Path getResolvedQualifiedPath(final Path f) throws FileNotFoundException { return myFs.makeQualified( new Path(chRootPathPartString + f.toUri().toString())); } @Override public FSDataOutputStream createInternal(final Path f, final EnumSet<CreateFlag> flag, final FsPermission absolutePermission, final int bufferSize, final short replication, final long blockSize, final Progressable progress, final ChecksumOpt checksumOpt, final boolean createParent) throws IOException, UnresolvedLinkException { return myFs.createInternal(fullPath(f), flag, absolutePermission, bufferSize, replication, blockSize, progress, checksumOpt, createParent); } @Override public boolean delete(final Path f, final boolean recursive) throws IOException, UnresolvedLinkException { return myFs.delete(fullPath(f), recursive); } @Override public BlockLocation[] getFileBlockLocations(final Path f, final long start, final long len) throws IOException, UnresolvedLinkException { return myFs.getFileBlockLocations(fullPath(f), start, len); } @Override public FileChecksum getFileChecksum(final Path f) throws IOException, UnresolvedLinkException { return myFs.getFileChecksum(fullPath(f)); } @Override public FileStatus getFileStatus(final Path f) throws IOException, UnresolvedLinkException { return myFs.getFileStatus(fullPath(f)); } @Override public FileStatus getFileLinkStatus(final Path f) throws IOException, UnresolvedLinkException { return myFs.getFileLinkStatus(fullPath(f)); } @Override public FsStatus getFsStatus() throws IOException { return myFs.getFsStatus(); } @Override public FsServerDefaults getServerDefaults() throws IOException { return myFs.getServerDefaults(); } @Override public int getUriDefaultPort() { return myFs.getUriDefaultPort(); } @Override public FileStatus[] listStatus(final Path f) throws IOException, UnresolvedLinkException { return myFs.listStatus(fullPath(f)); } @Override public void mkdir(final Path dir, final FsPermission permission, final boolean createParent) throws IOException, UnresolvedLinkException { myFs.mkdir(fullPath(dir), permission, createParent); } @Override public FSDataInputStream open(final Path f, final int bufferSize) throws IOException, UnresolvedLinkException { return myFs.open(fullPath(f), bufferSize); } @Override public void renameInternal(final Path src, final Path dst) throws IOException, UnresolvedLinkException { // note fullPath will check that paths are relative to this FileSystem. // Hence both are in same file system and a rename is valid myFs.renameInternal(fullPath(src), fullPath(dst)); } @Override public void renameInternal(final Path src, final Path dst, final boolean overwrite) throws IOException, UnresolvedLinkException { // note fullPath will check that paths are relative to this FileSystem. // Hence both are in same file system and a rename is valid myFs.renameInternal(fullPath(src), fullPath(dst), overwrite); } @Override public void setOwner(final Path f, final String username, final String groupname) throws IOException, UnresolvedLinkException { myFs.setOwner(fullPath(f), username, groupname); } @Override public void setPermission(final Path f, final FsPermission permission) throws IOException, UnresolvedLinkException { myFs.setPermission(fullPath(f), permission); } @Override public boolean setReplication(final Path f, final short replication) throws IOException, UnresolvedLinkException { return myFs.setReplication(fullPath(f), replication); } @Override public void setTimes(final Path f, final long mtime, final long atime) throws IOException, UnresolvedLinkException { myFs.setTimes(fullPath(f), mtime, atime); } @Override public void setVerifyChecksum(final boolean verifyChecksum) throws IOException, UnresolvedLinkException { myFs.setVerifyChecksum(verifyChecksum); } @Override public boolean supportsSymlinks() { return myFs.supportsSymlinks(); } @Override public void createSymlink(final Path target, final Path link, final boolean createParent) throws IOException, UnresolvedLinkException { /* * We leave the link alone: * If qualified or link relative then of course it is okay. * If absolute (ie / relative) then the link has to be resolved * relative to the changed root. */ myFs.createSymlink(fullPath(target), link, createParent); } @Override public Path getLinkTarget(final Path f) throws IOException { return myFs.getLinkTarget(fullPath(f)); } @Override public List<Token<?>> getDelegationTokens(String renewer) throws IOException { return myFs.getDelegationTokens(renewer); } }