| /** |
| * 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.ArrayList; |
| import java.util.EnumSet; |
| import java.util.List; |
| 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.Options.ChecksumOpt; |
| 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.security.token.Token; |
| import org.apache.hadoop.util.Progressable; |
| import org.apache.hadoop.util.Time; |
| |
| |
| /** |
| * 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.default.name< = 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.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.mounttable.default </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 fs.viewFs.mounttable.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 |
| Path homeDir = null; |
| |
| static AccessControlException readOnlyMountTable(final String operation, |
| final String p) { |
| return new AccessControlException( |
| "InternalDir of ViewFileSystem is readonly; operation=" + operation + |
| "Path=" + p); |
| } |
| static AccessControlException readOnlyMountTable(final String operation, |
| final Path p) { |
| return readOnlyMountTable(operation, p.toString()); |
| } |
| |
| |
| static public class MountPoint { |
| private Path src; // the src of the mount |
| private URI[] targets; // target of the mount; Multiple targets imply mergeMount |
| MountPoint(Path srcPath, URI[] targetURIs) { |
| src = srcPath; |
| targets = targetURIs; |
| } |
| Path getSrc() { |
| return src; |
| } |
| URI[] getTargets() { |
| return targets; |
| } |
| } |
| |
| 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 = Time.now(); |
| 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, getUri()); |
| } |
| |
| @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 Path getHomeDirectory() { |
| if (homeDir == null) { |
| String base = fsState.getHomeDirPrefixValue(); |
| if (base == null) { |
| base = "/user"; |
| } |
| homeDir = |
| this.makeQualified(new Path(base + "/" + ugi.getShortUserName())); |
| } |
| return homeDir; |
| } |
| |
| @Override |
| public Path resolvePath(final Path f) throws FileNotFoundException, |
| AccessControlException, UnresolvedLinkException, IOException { |
| final InodeTree.ResolveResult<AbstractFileSystem> res; |
| res = fsState.resolve(getUriPath(f), true); |
| if (res.isInternalDir()) { |
| return f; |
| } |
| return res.targetFileSystem.resolvePath(res.remainingPath); |
| |
| } |
| |
| @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 AccessControlException, |
| FileAlreadyExistsException, FileNotFoundException, |
| ParentNotDirectoryException, UnsupportedFileSystemException, |
| UnresolvedLinkException, IOException { |
| InodeTree.ResolveResult<AbstractFileSystem> res; |
| try { |
| res = fsState.resolve(getUriPath(f), false); |
| } catch (FileNotFoundException e) { |
| if (createParent) { |
| throw readOnlyMountTable("create", f); |
| } else { |
| throw e; |
| } |
| } |
| assert(res.remainingPath != null); |
| return res.targetFileSystem.createInternal(res.remainingPath, flag, |
| absolutePermission, bufferSize, replication, |
| blockSize, progress, checksumOpt, |
| 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(res.remainingPath); |
| } |
| |
| @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 readOnlyMountTable("createSymlink", link); |
| } 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. |
| } |
| |
| public MountPoint[] getMountPoints() { |
| List<InodeTree.MountPoint<AbstractFileSystem>> mountPoints = |
| fsState.getMountPoints(); |
| |
| MountPoint[] result = new MountPoint[mountPoints.size()]; |
| for ( int i = 0; i < mountPoints.size(); ++i ) { |
| result[i] = new MountPoint(new Path(mountPoints.get(i).src), |
| mountPoints.get(i).target.targetDirLinkList); |
| } |
| return result; |
| } |
| |
| @Override |
| public List<Token<?>> getDelegationTokens(String renewer) throws IOException { |
| List<InodeTree.MountPoint<AbstractFileSystem>> mountPoints = |
| fsState.getMountPoints(); |
| int initialListSize = 0; |
| for (InodeTree.MountPoint<AbstractFileSystem> im : mountPoints) { |
| initialListSize += im.target.targetDirLinkList.length; |
| } |
| List<Token<?>> result = new ArrayList<Token<?>>(initialListSize); |
| for ( int i = 0; i < mountPoints.size(); ++i ) { |
| List<Token<?>> tokens = |
| mountPoints.get(i).target.targetFileSystem.getDelegationTokens(renewer); |
| if (tokens != null) { |
| result.addAll(tokens); |
| } |
| } |
| return result; |
| } |
| |
| |
| |
| /* |
| * 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 |
| final URI myUri; // the URI of the outer ViewFs |
| |
| public InternalDirOfViewFs(final InodeTree.INodeDir<AbstractFileSystem> dir, |
| final long cTime, final UserGroupInformation ugi, final URI uri) |
| throws URISyntaxException { |
| super(FsConstants.VIEWFS_URI, FsConstants.VIEWFS_SCHEME, false, -1); |
| theInternalDir = dir; |
| creationTime = cTime; |
| this.ugi = ugi; |
| myUri = uri; |
| } |
| |
| 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 ChecksumOpt checksumOpt, |
| final boolean createParent) throws AccessControlException, |
| FileAlreadyExistsException, FileNotFoundException, |
| ParentNotDirectoryException, UnsupportedFileSystemException, |
| UnresolvedLinkException, IOException { |
| throw readOnlyMountTable("create", f); |
| } |
| |
| @Override |
| public boolean delete(final Path f, final boolean recursive) |
| throws AccessControlException, IOException { |
| checkPathIsSlash(f); |
| throw readOnlyMountTable("delete", f); |
| } |
| |
| @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( |
| myUri, 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( |
| myUri, null)); |
| } else { |
| result = new FileStatus(0, true, 0, 0, creationTime, creationTime, |
| PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0], |
| new Path(inode.fullPath).makeQualified( |
| myUri, 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( |
| myUri, null)); |
| } else { |
| result[i++] = new FileStatus(0, true, 0, 0, |
| creationTime, creationTime, |
| PERMISSION_RRR, ugi.getUserName(), ugi.getGroupNames()[0], |
| new Path(inode.fullPath).makeQualified( |
| myUri, 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 readOnlyMountTable("mkdir", dir); |
| } |
| |
| @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 readOnlyMountTable("rename", src); |
| } |
| |
| @Override |
| public boolean supportsSymlinks() { |
| return true; |
| } |
| |
| @Override |
| public void createSymlink(final Path target, final Path link, |
| final boolean createParent) throws AccessControlException { |
| throw readOnlyMountTable("createSymlink", link); |
| } |
| |
| @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 readOnlyMountTable("setOwner", f); |
| } |
| |
| @Override |
| public void setPermission(final Path f, final FsPermission permission) |
| throws AccessControlException, IOException { |
| checkPathIsSlash(f); |
| throw readOnlyMountTable("setPermission", f); |
| } |
| |
| @Override |
| public boolean setReplication(final Path f, final short replication) |
| throws AccessControlException, IOException { |
| checkPathIsSlash(f); |
| throw readOnlyMountTable("setReplication", f); |
| } |
| |
| @Override |
| public void setTimes(final Path f, final long mtime, final long atime) |
| throws AccessControlException, IOException { |
| checkPathIsSlash(f); |
| throw readOnlyMountTable("setTimes", f); |
| } |
| |
| @Override |
| public void setVerifyChecksum(final boolean verifyChecksum) |
| throws AccessControlException { |
| throw readOnlyMountTable("setVerifyChecksum", ""); |
| } |
| } |
| } |