/** * 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; import java.io.IOException; import java.net.URI; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; /** * Used primarily by {@link FileContext} to operate on and resolve * symlinks in a path. Operations can potentially span multiple * {@link AbstractFileSystem}s. * * @see FileSystemLinkResolver */ @InterfaceAudience.Private @InterfaceStability.Evolving public abstract class FSLinkResolver<T> { /** * Return a fully-qualified version of the given symlink target if it * has no scheme and authority. Partially and fully-qualified paths * are returned unmodified. * @param pathURI URI of the filesystem of pathWithLink * @param pathWithLink Path that contains the symlink * @param target The symlink's absolute target * @return Fully qualified version of the target. */ public static Path qualifySymlinkTarget(final URI pathURI, Path pathWithLink, Path target) { // NB: makeQualified uses the target's scheme and authority, if // specified, and the scheme and authority of pathURI, if not. final URI targetUri = target.toUri(); final String scheme = targetUri.getScheme(); final String auth = targetUri.getAuthority(); return (scheme == null && auth == null) ? target.makeQualified(pathURI, pathWithLink.getParent()) : target; } /** * Generic helper function overridden on instantiation to perform a * specific operation on the given file system using the given path * which may result in an UnresolvedLinkException. * @param fs AbstractFileSystem to perform the operation on. * @param p Path given the file system. * @return Generic type determined by the specific implementation. * @throws UnresolvedLinkException If symbolic link <code>path</code> could * not be resolved * @throws IOException an I/O error occurred */ abstract public T next(final AbstractFileSystem fs, final Path p) throws IOException, UnresolvedLinkException; /** * Performs the operation specified by the next function, calling it * repeatedly until all symlinks in the given path are resolved. * @param fc FileContext used to access file systems. * @param path The path to resolve symlinks on. * @return Generic type determined by the implementation of next. * @throws IOException */ public T resolve(final FileContext fc, final Path path) throws IOException { int count = 0; T in = null; Path p = path; // NB: More than one AbstractFileSystem can match a scheme, eg // "file" resolves to LocalFs but could have come by RawLocalFs. AbstractFileSystem fs = fc.getFSofPath(p); // Loop until all symlinks are resolved or the limit is reached for (boolean isLink = true; isLink;) { try { in = next(fs, p); isLink = false; } catch (UnresolvedLinkException e) { if (!fc.resolveSymlinks) { throw new IOException("Path " + path + " contains a symlink" + " and symlink resolution is disabled (" + CommonConfigurationKeys.FS_CLIENT_RESOLVE_REMOTE_SYMLINKS_KEY + ").", e); } if (!FileSystem.areSymlinksEnabled()) { throw new IOException("Symlink resolution is disabled in" + " this version of Hadoop."); } if (count++ > FsConstants.MAX_PATH_LINKS) { throw new IOException("Possible cyclic loop while " + "following symbolic link " + path); } // Resolve the first unresolved path component p = qualifySymlinkTarget(fs.getUri(), p, fs.getLinkTarget(p)); fs = fc.getFSofPath(p); } } return in; } }