HDFS-2514. Link resolution bug for intermediate symlinks with relative targets. Contributed by Eli Collins

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1204370 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Eli Collins 2011-11-21 07:01:29 +00:00
parent 5bc66c4c86
commit 9c2f4f634d
10 changed files with 86 additions and 49 deletions

View File

@ -118,6 +118,9 @@ Release 0.23.1 - Unreleased
HADOOP-7802. Hadoop scripts unconditionally source HADOOP-7802. Hadoop scripts unconditionally source
"$bin"/../libexec/hadoop-config.sh. (Bruno Mahé via tomwhite) "$bin"/../libexec/hadoop-config.sh. (Bruno Mahé via tomwhite)
HADOOP-6614. RunJar should provide more diags when it can't create
a temp file. (Jonathan Hsieh via eli)
OPTIMIZATIONS OPTIMIZATIONS
BUG FIXES BUG FIXES

View File

@ -149,7 +149,18 @@ public class RunJar {
File tmpDir = new File(new Configuration().get("hadoop.tmp.dir")); File tmpDir = new File(new Configuration().get("hadoop.tmp.dir"));
ensureDirectory(tmpDir); ensureDirectory(tmpDir);
final File workDir = File.createTempFile("hadoop-unjar", "", tmpDir); final File workDir;
try {
workDir = File.createTempFile("hadoop-unjar", "", tmpDir);
} catch (IOException ioe) {
// If user has insufficient perms to write to tmpDir, default
// "Permission denied" message doesn't specify a filename.
System.err.println("Error creating temp dir in hadoop.tmp.dir "
+ tmpDir + " due to " + ioe.getMessage());
System.exit(-1);
return;
}
if (!workDir.delete()) { if (!workDir.delete()) {
System.err.println("Delete failed for " + workDir); System.err.println("Delete failed for " + workDir);
System.exit(-1); System.exit(-1);

View File

@ -1934,6 +1934,9 @@ Release 0.22.0 - Unreleased
HDFS-2002. Incorrect computation of needed blocks in getTurnOffTip(). HDFS-2002. Incorrect computation of needed blocks in getTurnOffTip().
(Plamen Jeliazkov via shv) (Plamen Jeliazkov via shv)
HDFS-2514. Link resolution bug for intermediate symlinks with
relative targets. (eli)
Release 0.21.1 - Unreleased Release 0.21.1 - Unreleased
HDFS-1466. TestFcHdfsSymlink relies on /tmp/test not existing. (eli) HDFS-1466. TestFcHdfsSymlink relies on /tmp/test not existing. (eli)

View File

@ -828,9 +828,9 @@ public interface ClientProtocol extends VersionedProtocol {
/** /**
* Create symlink to a file or directory. * Create symlink to a file or directory.
* @param target The pathname of the destination that the * @param target The path of the destination that the
* link points to. * link points to.
* @param link The pathname of the link being created. * @param link The path of the link being created.
* @param dirPerm permissions to use when creating parent directories * @param dirPerm permissions to use when creating parent directories
* @param createParent - if true then missing parent dirs are created * @param createParent - if true then missing parent dirs are created
* if false then parent must exist * if false then parent must exist
@ -851,14 +851,16 @@ public interface ClientProtocol extends VersionedProtocol {
IOException; IOException;
/** /**
* Resolve the first symbolic link on the specified path. * Return the target of the given symlink. If there is an intermediate
* @param path The pathname that needs to be resolved * symlink in the path (ie a symlink leading up to the final path component)
* * then the given path is returned with this symlink resolved.
* @return The pathname after resolving the first symbolic link if any.
* *
* @param path The path with a link that needs resolution.
* @return The path after resolving the first symbolic link in the path.
* @throws AccessControlException permission denied * @throws AccessControlException permission denied
* @throws FileNotFoundException If <code>path</code> does not exist * @throws FileNotFoundException If <code>path</code> does not exist
* @throws IOException If an I/O error occurred * @throws IOException If the given path does not refer to a symlink
* or an I/O error occurred
*/ */
public String getLinkTarget(String path) throws AccessControlException, public String getLinkTarget(String path) throws AccessControlException,
FileNotFoundException, IOException; FileNotFoundException, IOException;

View File

@ -32,10 +32,10 @@ import org.apache.hadoop.fs.Path;
@InterfaceStability.Evolving @InterfaceStability.Evolving
public final class UnresolvedPathException extends UnresolvedLinkException { public final class UnresolvedPathException extends UnresolvedLinkException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String originalPath; // The original path containing the link private String path; // The path containing the link
private String linkTarget; // The target of the link private String preceding; // The path part preceding the link
private String remainingPath; // The path part following the link private String remainder; // The path part following the link
private String linkTarget; // The link's target
/** /**
* Used by RemoteException to instantiate an UnresolvedPathException. * Used by RemoteException to instantiate an UnresolvedPathException.
@ -44,22 +44,30 @@ public final class UnresolvedPathException extends UnresolvedLinkException {
super(msg); super(msg);
} }
public UnresolvedPathException(String originalPath, String remainingPath, public UnresolvedPathException(String path, String preceding,
String linkTarget) { String remainder, String linkTarget) {
this.originalPath = originalPath; this.path = path;
this.remainingPath = remainingPath; this.preceding = preceding;
this.remainder = remainder;
this.linkTarget = linkTarget; this.linkTarget = linkTarget;
} }
public Path getUnresolvedPath() throws IOException { /**
return new Path(originalPath); * Return a path with the link resolved with the target.
} */
public Path getResolvedPath() throws IOException { public Path getResolvedPath() throws IOException {
if (remainingPath == null || "".equals(remainingPath)) { // If the path is absolute we cam throw out the preceding part and
return new Path(linkTarget); // just append the remainder to the target, otherwise append each
// piece to resolve the link in path.
boolean noRemainder = (remainder == null || "".equals(remainder));
Path target = new Path(linkTarget);
if (target.isUriPathAbsolute()) {
return noRemainder ? target : new Path(target, remainder);
} else {
return noRemainder
? new Path(preceding, target)
: new Path(new Path(preceding, linkTarget), remainder);
} }
return new Path(linkTarget, remainingPath);
} }
@Override @Override
@ -68,7 +76,7 @@ public final class UnresolvedPathException extends UnresolvedLinkException {
if (msg != null) { if (msg != null) {
return msg; return msg;
} }
String myMsg = "Unresolved path " + originalPath; String myMsg = "Unresolved path " + path;
try { try {
return getResolvedPath().toString(); return getResolvedPath().toString();
} catch (IOException e) { } catch (IOException e) {

View File

@ -2016,10 +2016,12 @@ public class FSNamesystem implements Namesystem, FSClusterStats,
} }
} }
/** Get the file info for a specific file. /**
* Get the file info for a specific file.
*
* @param src The string representation of the path to the file * @param src The string representation of the path to the file
* @param resolveLink whether to throw UnresolvedLinkException * @param resolveLink whether to throw UnresolvedLinkException
* if src refers to a symlinks * if src refers to a symlink
* *
* @throws AccessControlException if access is denied * @throws AccessControlException if access is denied
* @throws UnresolvedLinkException if a symlink is encountered. * @throws UnresolvedLinkException if a symlink is encountered.

View File

@ -372,14 +372,16 @@ public abstract class INode implements Comparable<byte[]>, FSInodeInfo {
/** /**
* Given some components, create a path name. * Given some components, create a path name.
* @param components * @param components The path components
* @param start index
* @param end index
* @return concatenated path * @return concatenated path
*/ */
static String constructPath(byte[][] components, int start) { static String constructPath(byte[][] components, int start, int end) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
for (int i = start; i < components.length; i++) { for (int i = start; i < end; i++) {
buf.append(DFSUtil.bytes2String(components[i])); buf.append(DFSUtil.bytes2String(components[i]));
if (i < components.length - 1) { if (i < end - 1) {
buf.append(Path.SEPARATOR); buf.append(Path.SEPARATOR);
} }
} }

View File

@ -189,18 +189,19 @@ class INodeDirectory extends INode {
existing[index] = curNode; existing[index] = curNode;
} }
if (curNode.isLink() && (!lastComp || (lastComp && resolveLink))) { if (curNode.isLink() && (!lastComp || (lastComp && resolveLink))) {
if(NameNode.stateChangeLog.isDebugEnabled()) { final String path = constructPath(components, 0, components.length);
final String preceding = constructPath(components, 0, count);
final String remainder =
constructPath(components, count + 1, components.length);
final String link = DFSUtil.bytes2String(components[count]);
final String target = ((INodeSymlink)curNode).getLinkValue();
if (NameNode.stateChangeLog.isDebugEnabled()) {
NameNode.stateChangeLog.debug("UnresolvedPathException " + NameNode.stateChangeLog.debug("UnresolvedPathException " +
" count: " + count + " path: " + path + " preceding: " + preceding +
" componenent: " + DFSUtil.bytes2String(components[count]) + " count: " + count + " link: " + link + " target: " + target +
" full path: " + constructPath(components, 0) + " remainder: " + remainder);
" remaining path: " + constructPath(components, count+1) +
" symlink: " + ((INodeSymlink)curNode).getLinkValue());
} }
final String linkTarget = ((INodeSymlink)curNode).getLinkValue(); throw new UnresolvedPathException(path, preceding, remainder, target);
throw new UnresolvedPathException(constructPath(components, 0),
constructPath(components, count+1),
linkTarget);
} }
if (lastComp || !curNode.isDirectory()) { if (lastComp || !curNode.isDirectory()) {
break; break;

View File

@ -766,10 +766,6 @@ class NameNodeRpcServer implements NamenodeProtocols {
@Override // ClientProtocol @Override // ClientProtocol
public String getLinkTarget(String path) throws IOException { public String getLinkTarget(String path) throws IOException {
metrics.incrGetLinkTargetOps(); metrics.incrGetLinkTargetOps();
/* Resolves the first symlink in the given path, returning a
* new path consisting of the target of the symlink and any
* remaining path components from the original path.
*/
try { try {
HdfsFileStatus stat = namesystem.getFileInfo(path, false); HdfsFileStatus stat = namesystem.getFileInfo(path, false);
if (stat != null) { if (stat != null) {

View File

@ -20,6 +20,9 @@ package org.apache.hadoop.fs;
import java.io.*; import java.io.*;
import java.net.URI; import java.net.URI;
import org.apache.commons.logging.impl.Log4JLogger;
import org.apache.log4j.Level;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileContext; import org.apache.hadoop.fs.FileContext;
@ -28,9 +31,11 @@ import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.server.namenode.NameNode;
import org.apache.hadoop.hdfs.protocol.HdfsConstants; import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import static org.apache.hadoop.fs.FileContextTestHelper.*; import static org.apache.hadoop.fs.FileContextTestHelper.*;
import org.apache.hadoop.ipc.RemoteException; import org.apache.hadoop.ipc.RemoteException;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.Test; import org.junit.Test;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -41,6 +46,10 @@ import org.junit.AfterClass;
*/ */
public class TestFcHdfsSymlink extends FileContextSymlinkBaseTest { public class TestFcHdfsSymlink extends FileContextSymlinkBaseTest {
{
((Log4JLogger)NameNode.stateChangeLog).getLogger().setLevel(Level.ALL);
}
private static MiniDFSCluster cluster; private static MiniDFSCluster cluster;
protected String getScheme() { protected String getScheme() {
@ -250,8 +259,8 @@ public class TestFcHdfsSymlink extends FileContextSymlinkBaseTest {
Path link = new Path(testBaseDir1(), "symlinkToFile"); Path link = new Path(testBaseDir1(), "symlinkToFile");
createAndWriteFile(file); createAndWriteFile(file);
fc.createSymlink(file, link, false); fc.createSymlink(file, link, false);
FileStatus stat_file = fc.getFileStatus(file); FileStatus statFile = fc.getFileStatus(file);
FileStatus stat_link = fc.getFileStatus(link); FileStatus statLink = fc.getFileStatus(link);
assertEquals(stat_link.getOwner(), stat_file.getOwner()); assertEquals(statLink.getOwner(), statFile.getOwner());
} }
} }