/** * 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.util.EnumSet; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.BlockLocation; import org.apache.hadoop.fs.ContentSummary; 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.FileSystem; import org.apache.hadoop.fs.FilterFileSystem; import org.apache.hadoop.fs.FsServerDefaults; import org.apache.hadoop.fs.FsStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.util.Progressable; /** * <code>ChRootedFileSystem</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 ChRootedFileSystem extends FilterFileSystem { 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; private Path workingDir; protected FileSystem getMyFs() { return getRawFileSystem(); } /** * @param path * @return full path including the chroot */ protected Path fullPath(final Path path) { super.checkPath(path); return path.isAbsolute() ? new Path((chRootPathPart.isRoot() ? "" : chRootPathPartString) + path.toUri().getPath()) : new Path(chRootPathPartString + workingDir.toUri().getPath(), path); } /** * Constructor * @param uri base file system * @param conf configuration * @throws IOException */ public ChRootedFileSystem(final URI uri, Configuration conf) throws IOException { super(FileSystem.get(uri, conf)); String pathString = uri.getPath(); if (pathString.isEmpty()) { pathString = "/"; } chRootPathPart = new Path(pathString); chRootPathPartString = chRootPathPart.toUri().getPath(); myUri = uri; workingDir = getHomeDirectory(); // We don't use the wd of the myFs } /** * Called after a new FileSystem instance is constructed. * @param name a uri whose authority section names the host, port, etc. * for this FileSystem * @param conf the configuration */ @Override public void initialize(final URI name, final Configuration conf) throws IOException { super.initialize(name, conf); setConf(conf); } @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 / * @throws IOException if the p is not prefixed with root */ String stripOutRoot(final Path p) throws IOException { try { checkPath(p); } catch (IllegalArgumentException e) { throw new IOException("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 protected Path getInitialWorkingDirectory() { /* * 3 choices here: * null or / or /user/<uname> or strip out the root out of myFs's * inital wd. * Only reasonable choice for initialWd for chrooted fds is null * so that the default rule for wd is applied */ return null; } public Path getResolvedQualifiedPath(final Path f) throws FileNotFoundException { return makeQualified( new Path(chRootPathPartString + f.toUri().toString())); } @Override public Path getWorkingDirectory() { return workingDir; } @Override public void setWorkingDirectory(final Path new_dir) { workingDir = new_dir.isAbsolute() ? new_dir : new Path(workingDir, new_dir); } @Override public FSDataOutputStream create(final Path f, final FsPermission permission, final boolean overwrite, final int bufferSize, final short replication, final long blockSize, final Progressable progress) throws IOException { return super.create(fullPath(f), permission, overwrite, bufferSize, replication, blockSize, progress); } @Override @Deprecated public FSDataOutputStream createNonRecursive(Path f, FsPermission permission, EnumSet<CreateFlag> flags, int bufferSize, short replication, long blockSize, Progressable progress) throws IOException { return super.createNonRecursive(fullPath(f), permission, flags, bufferSize, replication, blockSize, progress); } @Override public boolean delete(final Path f, final boolean recursive) throws IOException { return super.delete(fullPath(f), recursive); } @Override @SuppressWarnings("deprecation") public boolean delete(Path f) throws IOException { return delete(f, true); } @Override public BlockLocation[] getFileBlockLocations(final FileStatus fs, final long start, final long len) throws IOException { return super.getFileBlockLocations( new ViewFsFileStatus(fs, fullPath(fs.getPath())), start, len); } @Override public FileChecksum getFileChecksum(final Path f) throws IOException { return super.getFileChecksum(fullPath(f)); } @Override public FileStatus getFileStatus(final Path f) throws IOException { return super.getFileStatus(fullPath(f)); } @Override public FsStatus getStatus(Path p) throws IOException { return super.getStatus(fullPath(p)); } @Override public FileStatus[] listStatus(final Path f) throws IOException { return super.listStatus(fullPath(f)); } @Override public boolean mkdirs(final Path f, final FsPermission permission) throws IOException { return super.mkdirs(fullPath(f), permission); } @Override public FSDataInputStream open(final Path f, final int bufferSize) throws IOException { return super.open(fullPath(f), bufferSize); } @Override public FSDataOutputStream append(final Path f, final int bufferSize, final Progressable progress) throws IOException { return super.append(fullPath(f), bufferSize, progress); } @Override public boolean rename(final Path src, final Path dst) throws IOException { // note fullPath will check that paths are relative to this FileSystem. // Hence both are in same file system and a rename is valid return super.rename(fullPath(src), fullPath(dst)); } @Override public void setOwner(final Path f, final String username, final String groupname) throws IOException { super.setOwner(fullPath(f), username, groupname); } @Override public void setPermission(final Path f, final FsPermission permission) throws IOException { super.setPermission(fullPath(f), permission); } @Override public boolean setReplication(final Path f, final short replication) throws IOException { return super.setReplication(fullPath(f), replication); } @Override public void setTimes(final Path f, final long mtime, final long atime) throws IOException { super.setTimes(fullPath(f), mtime, atime); } @Override public Path resolvePath(final Path p) throws IOException { return super.resolvePath(fullPath(p)); } @Override public ContentSummary getContentSummary(Path f) throws IOException { return super.getContentSummary(fullPath(f)); } private static Path rootPath = new Path(Path.SEPARATOR); @Override public long getDefaultBlockSize() { return getDefaultBlockSize(fullPath(rootPath)); } @Override public long getDefaultBlockSize(Path f) { return super.getDefaultBlockSize(fullPath(f)); } @Override public short getDefaultReplication() { return getDefaultReplication(fullPath(rootPath)); } @Override public short getDefaultReplication(Path f) { return super.getDefaultReplication(fullPath(f)); } @Override public FsServerDefaults getServerDefaults() throws IOException { return getServerDefaults(fullPath(rootPath)); } @Override public FsServerDefaults getServerDefaults(Path f) throws IOException { return super.getServerDefaults(fullPath(f)); } }