blob: c99ce3be13b0fa7a5b1ce60b0551419f7d912c0b [file] [log] [blame]
/**
* 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());
}
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.toString().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);
}
}