|
@@ -0,0 +1,816 @@
|
|
|
|
+/**
|
|
|
|
+ * 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 static org.apache.hadoop.fs.viewfs.Constants.PERMISSION_RRR;
|
|
|
|
+
|
|
|
|
+import java.io.FileNotFoundException;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.net.URI;
|
|
|
|
+import java.net.URISyntaxException;
|
|
|
|
+import java.util.EnumSet;
|
|
|
|
+import java.util.Map.Entry;
|
|
|
|
+
|
|
|
|
+import org.apache.hadoop.classification.InterfaceAudience;
|
|
|
|
+import org.apache.hadoop.classification.InterfaceStability;
|
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
|
+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.FileAlreadyExistsException;
|
|
|
|
+import org.apache.hadoop.fs.FileChecksum;
|
|
|
|
+import org.apache.hadoop.fs.FileStatus;
|
|
|
|
+import org.apache.hadoop.fs.FsConstants;
|
|
|
|
+import org.apache.hadoop.fs.FsServerDefaults;
|
|
|
|
+import org.apache.hadoop.fs.FsStatus;
|
|
|
|
+import org.apache.hadoop.fs.ParentNotDirectoryException;
|
|
|
|
+import org.apache.hadoop.fs.Path;
|
|
|
|
+import org.apache.hadoop.fs.RemoteIterator;
|
|
|
|
+import org.apache.hadoop.fs.UnresolvedLinkException;
|
|
|
|
+import org.apache.hadoop.fs.UnsupportedFileSystemException;
|
|
|
|
+import org.apache.hadoop.fs.local.LocalConfigKeys;
|
|
|
|
+import org.apache.hadoop.fs.permission.FsPermission;
|
|
|
|
+import org.apache.hadoop.fs.viewfs.InodeTree.INode;
|
|
|
|
+import org.apache.hadoop.fs.viewfs.InodeTree.INodeLink;
|
|
|
|
+import org.apache.hadoop.security.AccessControlException;
|
|
|
|
+import org.apache.hadoop.security.UserGroupInformation;
|
|
|
|
+import org.apache.hadoop.util.Progressable;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ *
|
|
|
|
+ * ViewFs (extends the AbstractFileSystem interface) implements a client-side
|
|
|
|
+ * mount table. The viewFs file system is implemented completely in memory on
|
|
|
|
+ * the client side. The client-side mount table allows a client to provide a
|
|
|
|
+ * customized view of a file system namespace that is composed from
|
|
|
|
+ * one or more individual file systems (a localFs or Hdfs, S3fs, etc).
|
|
|
|
+ * For example one could have a mount table that provides links such as
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> /user -> hdfs://nnContainingUserDir/user
|
|
|
|
+ * <li> /project/foo -> hdfs://nnProject1/projects/foo
|
|
|
|
+ * <li> /project/bar -> hdfs://nnProject2/projects/bar
|
|
|
|
+ * <li> /tmp -> hdfs://nnTmp/privateTmpForUserXXX
|
|
|
|
+ * </ul>
|
|
|
|
+ *
|
|
|
|
+ * ViewFs is specified with the following URI: <b>viewfs:///</b>
|
|
|
|
+ * <p>
|
|
|
|
+ * To use viewfs one would typically set the default file system in the
|
|
|
|
+ * config (i.e. fs.defaultFS = viewfs:///) along with the
|
|
|
|
+ * mount table config variables as described below.
|
|
|
|
+ *
|
|
|
|
+ * <p>
|
|
|
|
+ * <b> ** Config variables to specify the mount table entries ** </b>
|
|
|
|
+ * <p>
|
|
|
|
+ *
|
|
|
|
+ * The file system is initialized from the standard Hadoop config through
|
|
|
|
+ * config variables.
|
|
|
|
+ * See {@link FsConstants} for URI and Scheme constants;
|
|
|
|
+ * See {@link Constants} for config var constants;
|
|
|
|
+ * see {@link ConfigUtil} for convenient lib.
|
|
|
|
+ *
|
|
|
|
+ * <p>
|
|
|
|
+ * All the mount table config entries for view fs are prefixed by
|
|
|
|
+ * <b>fs.viewFs.</b>
|
|
|
|
+ * For example the above example can be specfied with the following
|
|
|
|
+ * config variables:
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> fs.viewFs.defaultMT.link./user=hdfs://nnContainingUserDir/user
|
|
|
|
+ * <li> fs.viewFs.defaultMT.link./project/foo=hdfs://nnProject1/projects/foo
|
|
|
|
+ * <li> fs.viewFs.defaultMT.link./project/bar=hdfs://nnProject2/projects/bar
|
|
|
|
+ * <li> fs.viewFs.defaultMT.link./tmp=hdfs://nnTmp/privateTmpForUserXXX
|
|
|
|
+ * </ul>
|
|
|
|
+ *
|
|
|
|
+ * The default mount table (when no authority is specified) is
|
|
|
|
+ * from config variables prefixed by <b>fs.viewFs.defaultMT </b>
|
|
|
|
+ * The authority component of a URI can be used to specify a different mount
|
|
|
|
+ * table. For example,
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> viewfs://sanjayMountable/
|
|
|
|
+ * </ul>
|
|
|
|
+ * is initialized from the fs.viewFs.sanjayMountable.* config variables.
|
|
|
|
+ *
|
|
|
|
+ * <p>
|
|
|
|
+ * <b> **** Merge Mounts **** </b>(NOTE: merge mounts are not implemented yet.)
|
|
|
|
+ * <p>
|
|
|
|
+ *
|
|
|
|
+ * One can also use "MergeMounts" to merge several directories (this is
|
|
|
|
+ * sometimes called union-mounts or junction-mounts in the literature.
|
|
|
|
+ * For example of the home directories are stored on say two file systems
|
|
|
|
+ * (because they do not fit on one) then one could specify a mount
|
|
|
|
+ * entry such as following merges two dirs:
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> /user -> hdfs://nnUser1/user,hdfs://nnUser2/user
|
|
|
|
+ * </ul>
|
|
|
|
+
|
|
|
|
+ * Such a mergeLink can be specifed with the following config var where ","
|
|
|
|
+ * is used as the seperater for each of links to be merged:
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> fs.viewFs.defaultMT.linkMerge./user=
|
|
|
|
+ * hdfs://nnUser1/user,hdfs://nnUser1/user
|
|
|
|
+ * </ul>
|
|
|
|
+ * A special case of the merge mount is where mount table's root is merged
|
|
|
|
+ * with the root (slash) of another file system:
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> fs.viewFs.defaultMT.linkMergeSlash=hdfs://nn99/
|
|
|
|
+ * </ul>
|
|
|
|
+ * In this cases the root of the mount table is merged with the root of
|
|
|
|
+ * <b>hdfs://nn99/ </b>
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+/**
|
|
|
|
+ * ViewFs (extends the AbstractFileSystem interface) implements a client-side
|
|
|
|
+ * mount table. The viewFs file system is implemented completely in memory on
|
|
|
|
+ * the client side. The client-side mount table allows a client to provide a
|
|
|
|
+ * customized view of a file system namespace that is composed from
|
|
|
|
+ * one or more individual file systems (a localFs or Hdfs, S3fs, etc).
|
|
|
|
+ * For example one could have a mount table that provides links such as
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> /user -> hdfs://nnContainingUserDir/user
|
|
|
|
+ * <li> /project/foo -> hdfs://nnProject1/projects/foo
|
|
|
|
+ * <li> /project/bar -> hdfs://nnProject2/projects/bar
|
|
|
|
+ * <li> /tmp -> hdfs://nnTmp/privateTmpForUserXXX
|
|
|
|
+ * </ul>
|
|
|
|
+ *
|
|
|
|
+ * ViewFileSystem is specified with the following URI: <b>viewfs:///</b>
|
|
|
|
+ * <p>
|
|
|
|
+ * To use viewfs one would typically set the default file system in the
|
|
|
|
+ * config (i.e. fs.default.name< = viewfs:///) along with the
|
|
|
|
+ * mount table config variables as described below.
|
|
|
|
+ * If your core-site.xml does not have the following config value please add it
|
|
|
|
+ * to your config: fs.viewfs.impl = org.apache.hadoop.fs.viewfs.ViewFileSystem
|
|
|
|
+ *
|
|
|
|
+ * <p>
|
|
|
|
+ * <b> ** Config variables to specify the mount table entries ** </b>
|
|
|
|
+ * <p>
|
|
|
|
+ *
|
|
|
|
+ * The file system is initialized from the standard Hadoop config through
|
|
|
|
+ * config variables.
|
|
|
|
+ * See {@link FsConstants} for URI and Scheme constants;
|
|
|
|
+ * See {@link Constants} for config var constants;
|
|
|
|
+ * see {@link ConfigUtil} for convenient lib.
|
|
|
|
+ *
|
|
|
|
+ * <p>
|
|
|
|
+ * All the mount table config entries for view fs are prefixed by
|
|
|
|
+ * <b>fs.viewfs.mounttable.</b>
|
|
|
|
+ * For example the above example can be specified with the following
|
|
|
|
+ * config variables:
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> fs.viewfs.mounttable.default.link./user=
|
|
|
|
+ * hdfs://nnContainingUserDir/user
|
|
|
|
+ * <li> fs.viewfs.mounttable.default.link./project/foo=
|
|
|
|
+ * hdfs://nnProject1/projects/foo
|
|
|
|
+ * <li> fs.viewfs.mounttable.default.link./project/bar=
|
|
|
|
+ * hdfs://nnProject2/projects/bar
|
|
|
|
+ * <li> fs.viewfs.mounttable.default.link./tmp=
|
|
|
|
+ * hdfs://nnTmp/privateTmpForUserXXX
|
|
|
|
+ * </ul>
|
|
|
|
+ *
|
|
|
|
+ * The default mount table (when no authority is specified) is
|
|
|
|
+ * from config variables prefixed by <b>fs.viewFs.defaultMT </b>
|
|
|
|
+ * The authority component of a URI can be used to specify a different mount
|
|
|
|
+ * table. For example,
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> viewfs://sanjayMountable/
|
|
|
|
+ * </ul>
|
|
|
|
+ * is initialized from the fs.viewFs.sanjayMountable.* config variables.
|
|
|
|
+ *
|
|
|
|
+ * <p>
|
|
|
|
+ * <b> **** Merge Mounts **** </b>(NOTE: merge mounts are not implemented yet.)
|
|
|
|
+ * <p>
|
|
|
|
+ *
|
|
|
|
+ * One can also use "MergeMounts" to merge several directories (this is
|
|
|
|
+ * sometimes called union-mounts or junction-mounts in the literature.
|
|
|
|
+ * For example of the home directories are stored on say two file systems
|
|
|
|
+ * (because they do not fit on one) then one could specify a mount
|
|
|
|
+ * entry such as following merges two dirs:
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> /user -> hdfs://nnUser1/user,hdfs://nnUser2/user
|
|
|
|
+ * </ul>
|
|
|
|
+
|
|
|
|
+ * Such a mergeLink can be specified with the following config var where ","
|
|
|
|
+ * is used as the separator for each of links to be merged:
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> fs.viewfs.mounttable.default.linkMerge./user=
|
|
|
|
+ * hdfs://nnUser1/user,hdfs://nnUser1/user
|
|
|
|
+ * </ul>
|
|
|
|
+ * A special case of the merge mount is where mount table's root is merged
|
|
|
|
+ * with the root (slash) of another file system:
|
|
|
|
+ * <ul>
|
|
|
|
+ * <li> fs.viewfs.mounttable.default.linkMergeSlash=hdfs://nn99/
|
|
|
|
+ * </ul>
|
|
|
|
+ * In this cases the root of the mount table is merged with the root of
|
|
|
|
+ * <b>hdfs://nn99/ </b>
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+@InterfaceAudience.Public
|
|
|
|
+@InterfaceStability.Evolving /*Evolving for a release,to be changed to Stable */
|
|
|
|
+public class ViewFs extends AbstractFileSystem {
|
|
|
|
+ final long creationTime; // of the the mount table
|
|
|
|
+ final UserGroupInformation ugi; // the user/group of user who created mtable
|
|
|
|
+ final Configuration config;
|
|
|
|
+ InodeTree<AbstractFileSystem> fsState; // the fs state; ie the mount table
|
|
|
|
+
|
|
|
|
+ static final AccessControlException READONLY_MOUNTABLE =
|
|
|
|
+ new AccessControlException("InternalDir of ViewFs is readonly");
|
|
|
|
+ public ViewFs(final Configuration conf) throws IOException,
|
|
|
|
+ URISyntaxException {
|
|
|
|
+ this(FsConstants.VIEWFS_URI, conf);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * This constructor has the signature needed by
|
|
|
|
+ * {@link AbstractFileSystem#createFileSystem(URI, Configuration)}.
|
|
|
|
+ *
|
|
|
|
+ * @param theUri which must be that of ViewFs
|
|
|
|
+ * @param conf
|
|
|
|
+ * @throws IOException
|
|
|
|
+ * @throws URISyntaxException
|
|
|
|
+ */
|
|
|
|
+ ViewFs(final URI theUri, final Configuration conf) throws IOException,
|
|
|
|
+ URISyntaxException {
|
|
|
|
+ super(theUri, FsConstants.VIEWFS_SCHEME, false, -1);
|
|
|
|
+ creationTime = System.currentTimeMillis();
|
|
|
|
+ ugi = UserGroupInformation.getCurrentUser();
|
|
|
|
+ config = conf;
|
|
|
|
+ // Now build client side view (i.e. client side mount table) from config.
|
|
|
|
+ String authority = theUri.getAuthority();
|
|
|
|
+ fsState = new InodeTree<AbstractFileSystem>(conf, authority) {
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ protected
|
|
|
|
+ AbstractFileSystem getTargetFileSystem(final URI uri)
|
|
|
|
+ throws URISyntaxException, UnsupportedFileSystemException {
|
|
|
|
+ return new ChRootedFs(
|
|
|
|
+ AbstractFileSystem.createFileSystem(uri, config),
|
|
|
|
+ new Path(uri.getPath()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ protected
|
|
|
|
+ AbstractFileSystem getTargetFileSystem(
|
|
|
|
+ final INodeDir<AbstractFileSystem> dir) throws URISyntaxException {
|
|
|
|
+ return new InternalDirOfViewFs(dir, creationTime, ugi);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ protected
|
|
|
|
+ AbstractFileSystem getTargetFileSystem(URI[] mergeFsURIList)
|
|
|
|
+ throws URISyntaxException, UnsupportedFileSystemException {
|
|
|
|
+ throw new UnsupportedFileSystemException("mergefs not implemented yet");
|
|
|
|
+ // return MergeFs.createMergeFs(mergeFsURIList, config);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FsServerDefaults getServerDefaults() throws IOException {
|
|
|
|
+ return LocalConfigKeys.getServerDefaults();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int getUriDefaultPort() {
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @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 int bytesPerChecksum,
|
|
|
|
+ final boolean createParent) throws AccessControlException,
|
|
|
|
+ FileAlreadyExistsException, FileNotFoundException,
|
|
|
|
+ ParentNotDirectoryException, UnsupportedFileSystemException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res;
|
|
|
|
+ try {
|
|
|
|
+ res = fsState.resolve(getUriPath(f), false);
|
|
|
|
+ } catch (FileNotFoundException e) {
|
|
|
|
+ if (createParent) {
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ } else {
|
|
|
|
+ throw e;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ assert(res.remainingPath != null);
|
|
|
|
+ return res.targetFileSystem.createInternal(res.remainingPath, flag,
|
|
|
|
+ absolutePermission, bufferSize, replication,
|
|
|
|
+ blockSize, progress, bytesPerChecksum,
|
|
|
|
+ createParent);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean delete(final Path f, final boolean recursive)
|
|
|
|
+ throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ // If internal dir or target is a mount link (ie remainingPath is Slash)
|
|
|
|
+ if (res.isInternalDir() || res.remainingPath == InodeTree.SlashPath) {
|
|
|
|
+ throw new AccessControlException(
|
|
|
|
+ "Cannot delete internal mount table directory: " + f);
|
|
|
|
+ }
|
|
|
|
+ return res.targetFileSystem.delete(res.remainingPath, recursive);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public BlockLocation[] getFileBlockLocations(final Path f, final long start,
|
|
|
|
+ final long len) throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ return
|
|
|
|
+ res.targetFileSystem.getFileBlockLocations(res.remainingPath, start, len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileChecksum getFileChecksum(final Path f)
|
|
|
|
+ throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ return res.targetFileSystem.getFileChecksum(f);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileStatus getFileStatus(final Path f) throws AccessControlException,
|
|
|
|
+ FileNotFoundException, UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+
|
|
|
|
+ // FileStatus#getPath is a fully qualified path relative to the root of
|
|
|
|
+ // target file system.
|
|
|
|
+ // We need to change it to viewfs URI - relative to root of mount table.
|
|
|
|
+
|
|
|
|
+ // The implementors of RawLocalFileSystem were trying to be very smart.
|
|
|
|
+ // They implement FileStatus#getOwener lazily -- the object
|
|
|
|
+ // returned is really a RawLocalFileSystem that expect the
|
|
|
|
+ // FileStatus#getPath to be unchanged so that it can get owner when needed.
|
|
|
|
+ // Hence we need to interpose a new ViewFsFileStatus that works around.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ FileStatus status = res.targetFileSystem.getFileStatus(res.remainingPath);
|
|
|
|
+ return new ViewFsFileStatus(status, this.makeQualified(f));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileStatus getFileLinkStatus(final Path f)
|
|
|
|
+ throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnsupportedFileSystemException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), false); // do not follow mount link
|
|
|
|
+ return res.targetFileSystem.getFileLinkStatus(res.remainingPath);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FsStatus getFsStatus() throws AccessControlException,
|
|
|
|
+ FileNotFoundException, IOException {
|
|
|
|
+ return new FsStatus(0, 0, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public RemoteIterator<FileStatus> listStatusIterator(final Path f)
|
|
|
|
+ throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ final InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ final RemoteIterator<FileStatus> fsIter =
|
|
|
|
+ res.targetFileSystem.listStatusIterator(res.remainingPath);
|
|
|
|
+ if (res.isInternalDir()) {
|
|
|
|
+ return fsIter;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return new RemoteIterator<FileStatus>() {
|
|
|
|
+ final RemoteIterator<FileStatus> myIter;
|
|
|
|
+ final ChRootedFs targetFs;
|
|
|
|
+ { // Init
|
|
|
|
+ myIter = fsIter;
|
|
|
|
+ targetFs = (ChRootedFs) res.targetFileSystem;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean hasNext() throws IOException {
|
|
|
|
+ return myIter.hasNext();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileStatus next() throws IOException {
|
|
|
|
+ FileStatus status = myIter.next();
|
|
|
|
+ String suffix = targetFs.stripOutRoot(status.getPath());
|
|
|
|
+ return new ViewFsFileStatus(status, makeQualified(
|
|
|
|
+ suffix.length() == 0 ? f : new Path(res.resolvedPath, suffix)));
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileStatus[] listStatus(final Path f) throws AccessControlException,
|
|
|
|
+ FileNotFoundException, UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+
|
|
|
|
+ FileStatus[] statusLst = res.targetFileSystem.listStatus(res.remainingPath);
|
|
|
|
+ if (!res.isInternalDir()) {
|
|
|
|
+ // We need to change the name in the FileStatus as described in
|
|
|
|
+ // {@link #getFileStatus }
|
|
|
|
+ ChRootedFs targetFs;
|
|
|
|
+ targetFs = (ChRootedFs) res.targetFileSystem;
|
|
|
|
+ int i = 0;
|
|
|
|
+ for (FileStatus status : statusLst) {
|
|
|
|
+ String suffix = targetFs.stripOutRoot(status.getPath());
|
|
|
|
+ statusLst[i++] = new ViewFsFileStatus(status, this.makeQualified(
|
|
|
|
+ suffix.length() == 0 ? f : new Path(res.resolvedPath, suffix)));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return statusLst;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void mkdir(final Path dir, final FsPermission permission,
|
|
|
|
+ final boolean createParent) throws AccessControlException,
|
|
|
|
+ FileAlreadyExistsException,
|
|
|
|
+ FileNotFoundException, UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(dir), false);
|
|
|
|
+ res.targetFileSystem.mkdir(res.remainingPath, permission, createParent);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FSDataInputStream open(final Path f, final int bufferSize)
|
|
|
|
+ throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ return res.targetFileSystem.open(res.remainingPath, bufferSize);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void renameInternal(final Path src, final Path dst,
|
|
|
|
+ final boolean overwrite) throws IOException, UnresolvedLinkException {
|
|
|
|
+ // passing resolveLastComponet as false to catch renaming a mount point
|
|
|
|
+ // itself we need to catch this as an internal operation and fail.
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> resSrc =
|
|
|
|
+ fsState.resolve(getUriPath(src), false);
|
|
|
|
+
|
|
|
|
+ if (resSrc.isInternalDir()) {
|
|
|
|
+ throw new AccessControlException(
|
|
|
|
+ "Cannot Rename within internal dirs of mount table: it is readOnly");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> resDst =
|
|
|
|
+ fsState.resolve(getUriPath(dst), false);
|
|
|
|
+ if (resDst.isInternalDir()) {
|
|
|
|
+ throw new AccessControlException(
|
|
|
|
+ "Cannot Rename within internal dirs of mount table: it is readOnly");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ // Alternate 1: renames within same file system - valid but we disallow
|
|
|
|
+ // Alternate 2: (as described in next para - valid but we have disallowed it
|
|
|
|
+ //
|
|
|
|
+ // Note we compare the URIs. the URIs include the link targets.
|
|
|
|
+ // hence we allow renames across mount links as long as the mount links
|
|
|
|
+ // point to the same target.
|
|
|
|
+ if (!resSrc.targetFileSystem.getUri().equals(
|
|
|
|
+ resDst.targetFileSystem.getUri())) {
|
|
|
|
+ throw new IOException("Renames across Mount points not supported");
|
|
|
|
+ }
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+ // Alternate 3 : renames ONLY within the the same mount links.
|
|
|
|
+ //
|
|
|
|
+
|
|
|
|
+ if (resSrc.targetFileSystem !=resDst.targetFileSystem) {
|
|
|
|
+ throw new IOException("Renames across Mount points not supported");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ resSrc.targetFileSystem.renameInternal(resSrc.remainingPath,
|
|
|
|
+ resDst.remainingPath, overwrite);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void renameInternal(final Path src, final Path dst)
|
|
|
|
+ throws AccessControlException, FileAlreadyExistsException,
|
|
|
|
+ FileNotFoundException, ParentNotDirectoryException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ renameInternal(src, dst, false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean supportsSymlinks() {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void createSymlink(final Path target, final Path link,
|
|
|
|
+ final boolean createParent) throws IOException, UnresolvedLinkException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res;
|
|
|
|
+ try {
|
|
|
|
+ res = fsState.resolve(getUriPath(link), false);
|
|
|
|
+ } catch (FileNotFoundException e) {
|
|
|
|
+ if (createParent) {
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ } else {
|
|
|
|
+ throw e;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ assert(res.remainingPath != null);
|
|
|
|
+ res.targetFileSystem.createSymlink(target, res.remainingPath,
|
|
|
|
+ createParent);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Path getLinkTarget(final Path f) throws IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), false); // do not follow mount link
|
|
|
|
+ return res.targetFileSystem.getLinkTarget(res.remainingPath);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setOwner(final Path f, final String username,
|
|
|
|
+ final String groupname) throws AccessControlException,
|
|
|
|
+ FileNotFoundException, UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ res.targetFileSystem.setOwner(res.remainingPath, username, groupname);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setPermission(final Path f, final FsPermission permission)
|
|
|
|
+ throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ res.targetFileSystem.setPermission(res.remainingPath, permission);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean setReplication(final Path f, final short replication)
|
|
|
|
+ throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ return res.targetFileSystem.setReplication(res.remainingPath, replication);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setTimes(final Path f, final long mtime, final long atime)
|
|
|
|
+ throws AccessControlException, FileNotFoundException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ InodeTree.ResolveResult<AbstractFileSystem> res =
|
|
|
|
+ fsState.resolve(getUriPath(f), true);
|
|
|
|
+ res.targetFileSystem.setTimes(res.remainingPath, mtime, atime);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setVerifyChecksum(final boolean verifyChecksum)
|
|
|
|
+ throws AccessControlException, IOException {
|
|
|
|
+ // This is a file system level operations, however ViewFs
|
|
|
|
+ // points to many file systems. Noop for ViewFs.
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * An instance of this class represents an internal dir of the viewFs
|
|
|
|
+ * ie internal dir of the mount table.
|
|
|
|
+ * It is a ready only mount tbale and create, mkdir or delete operations
|
|
|
|
+ * are not allowed.
|
|
|
|
+ * If called on create or mkdir then this target is the parent of the
|
|
|
|
+ * directory in which one is trying to create or mkdir; hence
|
|
|
|
+ * in this case the path name passed in is the last component.
|
|
|
|
+ * Otherwise this target is the end point of the path and hence
|
|
|
|
+ * the path name passed in is null.
|
|
|
|
+ */
|
|
|
|
+ static class InternalDirOfViewFs extends AbstractFileSystem {
|
|
|
|
+
|
|
|
|
+ final InodeTree.INodeDir<AbstractFileSystem> theInternalDir;
|
|
|
|
+ final long creationTime; // of the the mount table
|
|
|
|
+ final UserGroupInformation ugi; // the user/group of user who created mtable
|
|
|
|
+
|
|
|
|
+ public InternalDirOfViewFs(final InodeTree.INodeDir<AbstractFileSystem> dir,
|
|
|
|
+ final long cTime, final UserGroupInformation ugi)
|
|
|
|
+ throws URISyntaxException {
|
|
|
|
+ super(FsConstants.VIEWFS_URI, FsConstants.VIEWFS_SCHEME, false, -1);
|
|
|
|
+ theInternalDir = dir;
|
|
|
|
+ creationTime = cTime;
|
|
|
|
+ this.ugi = ugi;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static private void checkPathIsSlash(final Path f) throws IOException {
|
|
|
|
+ if (f != InodeTree.SlashPath) {
|
|
|
|
+ throw new IOException (
|
|
|
|
+ "Internal implementation error: expected file name to be /" );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @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 int bytesPerChecksum,
|
|
|
|
+ final boolean createParent) throws AccessControlException,
|
|
|
|
+ FileAlreadyExistsException, FileNotFoundException,
|
|
|
|
+ ParentNotDirectoryException, UnsupportedFileSystemException,
|
|
|
|
+ UnresolvedLinkException, IOException {
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean delete(final Path f, final boolean recursive)
|
|
|
|
+ throws AccessControlException, IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public BlockLocation[] getFileBlockLocations(final Path f, final long start,
|
|
|
|
+ final long len) throws FileNotFoundException, IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ throw new FileNotFoundException("Path points to dir not a file");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileChecksum getFileChecksum(final Path f)
|
|
|
|
+ throws FileNotFoundException, IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ throw new FileNotFoundException("Path points to dir not a file");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileStatus getFileStatus(final Path f) throws IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ return new FileStatus(0, true, 0, 0, creationTime, creationTime,
|
|
|
|
+ PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0],
|
|
|
|
+ new Path(theInternalDir.fullPath).makeQualified(
|
|
|
|
+ FsConstants.VIEWFS_URI, null));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileStatus getFileLinkStatus(final Path f)
|
|
|
|
+ throws FileNotFoundException {
|
|
|
|
+ // look up i internalDirs children - ignore first Slash
|
|
|
|
+ INode<AbstractFileSystem> inode =
|
|
|
|
+ theInternalDir.children.get(f.toUri().toString().substring(1));
|
|
|
|
+ if (inode == null) {
|
|
|
|
+ throw new FileNotFoundException(
|
|
|
|
+ "viewFs internal mount table - missing entry:" + f);
|
|
|
|
+ }
|
|
|
|
+ FileStatus result;
|
|
|
|
+ if (inode instanceof INodeLink) {
|
|
|
|
+ INodeLink<AbstractFileSystem> inodelink =
|
|
|
|
+ (INodeLink<AbstractFileSystem>) inode;
|
|
|
|
+ result = new FileStatus(0, false, 0, 0, creationTime, creationTime,
|
|
|
|
+ PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0],
|
|
|
|
+ inodelink.getTargetLink(),
|
|
|
|
+ new Path(inode.fullPath).makeQualified(
|
|
|
|
+ FsConstants.VIEWFS_URI, null));
|
|
|
|
+ } else {
|
|
|
|
+ result = new FileStatus(0, true, 0, 0, creationTime, creationTime,
|
|
|
|
+ PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0],
|
|
|
|
+ new Path(inode.fullPath).makeQualified(
|
|
|
|
+ FsConstants.VIEWFS_URI, null));
|
|
|
|
+ }
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FsStatus getFsStatus() {
|
|
|
|
+ return new FsStatus(0, 0, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FsServerDefaults getServerDefaults() throws IOException {
|
|
|
|
+ throw new IOException("FsServerDefaults not implemented yet");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int getUriDefaultPort() {
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FileStatus[] listStatus(final Path f) throws AccessControlException,
|
|
|
|
+ IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ FileStatus[] result = new FileStatus[theInternalDir.children.size()];
|
|
|
|
+ int i = 0;
|
|
|
|
+ for (Entry<String, INode<AbstractFileSystem>> iEntry :
|
|
|
|
+ theInternalDir.children.entrySet()) {
|
|
|
|
+ INode<AbstractFileSystem> inode = iEntry.getValue();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (inode instanceof INodeLink ) {
|
|
|
|
+ INodeLink<AbstractFileSystem> link =
|
|
|
|
+ (INodeLink<AbstractFileSystem>) inode;
|
|
|
|
+
|
|
|
|
+ result[i++] = new FileStatus(0, false, 0, 0,
|
|
|
|
+ creationTime, creationTime,
|
|
|
|
+ PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0],
|
|
|
|
+ link.getTargetLink(),
|
|
|
|
+ new Path(inode.fullPath).makeQualified(
|
|
|
|
+ FsConstants.VIEWFS_URI, null));
|
|
|
|
+ } else {
|
|
|
|
+ result[i++] = new FileStatus(0, true, 0, 0,
|
|
|
|
+ creationTime, creationTime,
|
|
|
|
+ PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0],
|
|
|
|
+ new Path(inode.fullPath).makeQualified(
|
|
|
|
+ FsConstants.VIEWFS_URI, null));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void mkdir(final Path dir, final FsPermission permission,
|
|
|
|
+ final boolean createParent) throws AccessControlException,
|
|
|
|
+ FileAlreadyExistsException {
|
|
|
|
+ if (theInternalDir.isRoot & dir == null) {
|
|
|
|
+ throw new FileAlreadyExistsException("/ already exits");
|
|
|
|
+ }
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public FSDataInputStream open(final Path f, final int bufferSize)
|
|
|
|
+ throws FileNotFoundException, IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ throw new FileNotFoundException("Path points to dir not a file");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void renameInternal(final Path src, final Path dst)
|
|
|
|
+ throws AccessControlException, IOException {
|
|
|
|
+ checkPathIsSlash(src);
|
|
|
|
+ checkPathIsSlash(dst);
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean supportsSymlinks() {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void createSymlink(final Path target, final Path link,
|
|
|
|
+ final boolean createParent) throws AccessControlException {
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Path getLinkTarget(final Path f) throws FileNotFoundException,
|
|
|
|
+ IOException {
|
|
|
|
+ return getFileLinkStatus(f).getSymlink();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setOwner(final Path f, final String username,
|
|
|
|
+ final String groupname) throws AccessControlException, IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setPermission(final Path f, final FsPermission permission)
|
|
|
|
+ throws AccessControlException, IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean setReplication(final Path f, final short replication)
|
|
|
|
+ throws AccessControlException, IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setTimes(final Path f, final long mtime, final long atime)
|
|
|
|
+ throws AccessControlException, IOException {
|
|
|
|
+ checkPathIsSlash(f);
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setVerifyChecksum(final boolean verifyChecksum)
|
|
|
|
+ throws AccessControlException {
|
|
|
|
+ throw READONLY_MOUNTABLE;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|