diff --git a/BUILDING.txt b/BUILDING.txt
index 7e3d450953f..ff6dea26ad4 100644
--- a/BUILDING.txt
+++ b/BUILDING.txt
@@ -7,7 +7,7 @@ Requirements:
* JDK 1.6
* Maven 3.0
* Findbugs 1.3.9 (if running findbugs)
-* ProtocolBuffer 2.4.1+ (for MapReduce and HDFS)
+* ProtocolBuffer 2.5.0
* CMake 2.6 or newer (if compiling native code)
* Internet connection for first build (to fetch all Maven and Hadoop dependencies)
@@ -99,6 +99,16 @@ level once; and then work from the submodule. Keep in mind that SNAPSHOTs
time out after a while, using the Maven '-nsu' will stop Maven from trying
to update SNAPSHOTs from external repos.
+----------------------------------------------------------------------------------
+Protocol Buffer compiler
+
+The version of Protocol Buffer compiler, protoc, must match the version of the
+protobuf JAR.
+
+If you have multiple versions of protoc in your system, you can set in your
+build shell the HADOOP_PROTOC_PATH environment variable to point to the one you
+want to use for the Hadoop build. If you don't define this environment variable,
+protoc is looked up in the PATH.
----------------------------------------------------------------------------------
Importing projects to eclipse
diff --git a/dev-support/test-patch.sh b/dev-support/test-patch.sh
index 9bd4eb5820b..9d7dff9a873 100755
--- a/dev-support/test-patch.sh
+++ b/dev-support/test-patch.sh
@@ -426,7 +426,8 @@ checkJavadocWarnings () {
echo "There appear to be $javadocWarnings javadoc warnings generated by the patched build."
#There are 11 warnings that are caused by things that are caused by using sun internal APIs.
- OK_JAVADOC_WARNINGS=11;
+ #There are 2 warnings that are caused by the Apache DS Dn class used in MiniKdc.
+ OK_JAVADOC_WARNINGS=13;
### if current warnings greater than OK_JAVADOC_WARNINGS
if [[ $javadocWarnings -ne $OK_JAVADOC_WARNINGS ]] ; then
JIRA_COMMENT="$JIRA_COMMENT
@@ -731,32 +732,62 @@ of hadoop-common prior to running the unit tests in $ordered_modules"
fi
fi
fi
+ failed_test_builds=""
+ test_timeouts=""
for module in $ordered_modules; do
cd $module
+ module_suffix=`basename ${module}`
+ test_logfile=$PATCH_DIR/testrun_${module_suffix}.txt
echo " Running tests in $module"
echo " $MVN clean install -fn $NATIVE_PROFILE $REQUIRE_TEST_LIB_HADOOP -D${PROJECT_NAME}PatchProcess"
- $MVN clean install -fn $NATIVE_PROFILE $REQUIRE_TEST_LIB_HADOOP -D${PROJECT_NAME}PatchProcess
+ $MVN clean install -fae $NATIVE_PROFILE $REQUIRE_TEST_LIB_HADOOP -D${PROJECT_NAME}PatchProcess > $test_logfile 2>&1
+ test_build_result=$?
+ cat $test_logfile
+ module_test_timeouts=`$AWK '/^Running / { if (last) { print last } last=$2 } /^Tests run: / { last="" }' $test_logfile`
+ if [[ -n "$module_test_timeouts" ]] ; then
+ test_timeouts="$test_timeouts
+$module_test_timeouts"
+ fi
module_failed_tests=`find . -name 'TEST*.xml' | xargs $GREP -l -E "
@@ -75,13 +77,29 @@ public class KerberosAuthenticator implements Authenticator {
private static final String OS_LOGIN_MODULE_NAME;
private static final boolean windows = System.getProperty("os.name").startsWith("Windows");
+ private static final boolean is64Bit = System.getProperty("os.arch").contains("64");
+ private static final boolean aix = System.getProperty("os.name").equals("AIX");
+
+ /* Return the OS login module class name */
+ private static String getOSLoginModuleName() {
+ if (IBM_JAVA) {
+ if (windows) {
+ return is64Bit ? "com.ibm.security.auth.module.Win64LoginModule"
+ : "com.ibm.security.auth.module.NTLoginModule";
+ } else if (aix) {
+ return is64Bit ? "com.ibm.security.auth.module.AIX64LoginModule"
+ : "com.ibm.security.auth.module.AIXLoginModule";
+ } else {
+ return "com.ibm.security.auth.module.LinuxLoginModule";
+ }
+ } else {
+ return windows ? "com.sun.security.auth.module.NTLoginModule"
+ : "com.sun.security.auth.module.UnixLoginModule";
+ }
+ }
static {
- if (windows) {
- OS_LOGIN_MODULE_NAME = "com.sun.security.auth.module.NTLoginModule";
- } else {
- OS_LOGIN_MODULE_NAME = "com.sun.security.auth.module.UnixLoginModule";
- }
+ OS_LOGIN_MODULE_NAME = getOSLoginModuleName();
}
private static final AppConfigurationEntry OS_SPECIFIC_LOGIN =
@@ -92,13 +110,22 @@ public class KerberosAuthenticator implements Authenticator {
private static final Map USER_KERBEROS_OPTIONS = new HashMap();
static {
- USER_KERBEROS_OPTIONS.put("doNotPrompt", "true");
- USER_KERBEROS_OPTIONS.put("useTicketCache", "true");
- USER_KERBEROS_OPTIONS.put("renewTGT", "true");
String ticketCache = System.getenv("KRB5CCNAME");
- if (ticketCache != null) {
- USER_KERBEROS_OPTIONS.put("ticketCache", ticketCache);
+ if (IBM_JAVA) {
+ USER_KERBEROS_OPTIONS.put("useDefaultCcache", "true");
+ } else {
+ USER_KERBEROS_OPTIONS.put("doNotPrompt", "true");
+ USER_KERBEROS_OPTIONS.put("useTicketCache", "true");
}
+ if (ticketCache != null) {
+ if (IBM_JAVA) {
+ // The first value searched when "useDefaultCcache" is used.
+ System.setProperty("KRB5CCNAME", ticketCache);
+ } else {
+ USER_KERBEROS_OPTIONS.put("ticketCache", ticketCache);
+ }
+ }
+ USER_KERBEROS_OPTIONS.put("renewTGT", "true");
}
private static final AppConfigurationEntry USER_KERBEROS_LOGIN =
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java
index 07b64f48f9f..327fc5e541f 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/server/KerberosAuthenticationHandler.java
@@ -21,6 +21,7 @@ import org.apache.hadoop.security.authentication.util.KerberosUtil;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.Oid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,6 +45,8 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
+
/**
* The {@link KerberosAuthenticationHandler} implements the Kerberos SPNEGO authentication mechanism for HTTP.
*
@@ -77,18 +80,33 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
Map options = new HashMap();
- options.put("keyTab", keytab);
- options.put("principal", principal);
- options.put("useKeyTab", "true");
- options.put("storeKey", "true");
- options.put("doNotPrompt", "true");
- options.put("useTicketCache", "true");
- options.put("renewTGT", "true");
+ if (IBM_JAVA) {
+ options.put("useKeytab",
+ keytab.startsWith("file://") ? keytab : "file://" + keytab);
+ options.put("principal", principal);
+ options.put("credsType", "acceptor");
+ } else {
+ options.put("keyTab", keytab);
+ options.put("principal", principal);
+ options.put("useKeyTab", "true");
+ options.put("storeKey", "true");
+ options.put("doNotPrompt", "true");
+ options.put("useTicketCache", "true");
+ options.put("renewTGT", "true");
+ options.put("isInitiator", "false");
+ }
options.put("refreshKrb5Config", "true");
- options.put("isInitiator", "false");
String ticketCache = System.getenv("KRB5CCNAME");
if (ticketCache != null) {
- options.put("ticketCache", ticketCache);
+ if (IBM_JAVA) {
+ options.put("useDefaultCcache", "true");
+ // The first value searched when "useDefaultCcache" is used.
+ System.setProperty("KRB5CCNAME", ticketCache);
+ options.put("renewTGT", "true");
+ options.put("credsType", "both");
+ } else {
+ options.put("ticketCache", ticketCache);
+ }
}
if (LOG.isDebugEnabled()) {
options.put("debug", "true");
@@ -294,8 +312,18 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
public AuthenticationToken run() throws Exception {
AuthenticationToken token = null;
GSSContext gssContext = null;
+ GSSCredential gssCreds = null;
try {
- gssContext = gssManager.createContext((GSSCredential) null);
+ if (IBM_JAVA) {
+ // IBM JDK needs non-null credentials to be passed to createContext here, with
+ // SPNEGO mechanism specified, otherwise JGSS will use its default mechanism
+ // only, which is Kerberos V5.
+ gssCreds = gssManager.createCredential(null, GSSCredential.INDEFINITE_LIFETIME,
+ new Oid[]{KerberosUtil.getOidInstance("GSS_SPNEGO_MECH_OID"),
+ KerberosUtil.getOidInstance("GSS_KRB5_MECH_OID")},
+ GSSCredential.ACCEPT_ONLY);
+ }
+ gssContext = gssManager.createContext(gssCreds);
byte[] serverToken = gssContext.acceptSecContext(clientToken, 0, clientToken.length);
if (serverToken != null && serverToken.length > 0) {
String authenticate = base64.encodeToString(serverToken);
@@ -317,6 +345,9 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
if (gssContext != null) {
gssContext.dispose();
}
+ if (gssCreds != null) {
+ gssCreds.dispose();
+ }
}
return token;
}
diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java
index 428435df8a8..6435e75f5e3 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/KerberosUtil.java
@@ -27,6 +27,8 @@ import java.util.Locale;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
+import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
+
public class KerberosUtil {
/* Return the Kerberos login module name */
@@ -40,7 +42,11 @@ public class KerberosUtil {
throws ClassNotFoundException, GSSException, NoSuchFieldException,
IllegalAccessException {
Class> oidClass;
- if (System.getProperty("java.vendor").contains("IBM")) {
+ if (IBM_JAVA) {
+ if ("NT_GSS_KRB5_PRINCIPAL".equals(oidName)) {
+ // IBM JDK GSSUtil class does not have field for krb5 principal oid
+ return new Oid("1.2.840.113554.1.2.2.1");
+ }
oidClass = Class.forName("com.ibm.security.jgss.GSSUtil");
} else {
oidClass = Class.forName("sun.security.jgss.GSSUtil");
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/PlatformName.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/PlatformName.java
similarity index 83%
rename from hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/PlatformName.java
rename to hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/PlatformName.java
index 24846f849a5..eb52839b65a 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/PlatformName.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/PlatformName.java
@@ -22,32 +22,33 @@ import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
/**
- * A helper class for getting build-info of the java-vm.
- *
+ * A helper class for getting build-info of the java-vm.
+ *
*/
@InterfaceAudience.LimitedPrivate({"HBase"})
@InterfaceStability.Unstable
public class PlatformName {
/**
- * The complete platform 'name' to identify the platform as
+ * The complete platform 'name' to identify the platform as
* per the java-vm.
*/
public static final String PLATFORM_NAME =
- (Shell.WINDOWS ? System.getenv("os") : System.getProperty("os.name"))
+ (System.getProperty("os.name").startsWith("Windows")
+ ? System.getenv("os") : System.getProperty("os.name"))
+ "-" + System.getProperty("os.arch")
+ "-" + System.getProperty("sun.arch.data.model");
-
+
/**
- * The java vendor name used in this platform.
+ * The java vendor name used in this platform.
*/
public static final String JAVA_VENDOR_NAME = System.getProperty("java.vendor");
/**
- * A public static variable to indicate the current java vendor is
- * IBM java or not.
+ * A public static variable to indicate the current java vendor is
+ * IBM java or not.
*/
public static final boolean IBM_JAVA = JAVA_VENDOR_NAME.contains("IBM");
-
+
public static void main(String[] args) {
System.out.println(PLATFORM_NAME);
}
diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt
index 8702ab75ccf..15000ec72c6 100644
--- a/hadoop-common-project/hadoop-common/CHANGES.txt
+++ b/hadoop-common-project/hadoop-common/CHANGES.txt
@@ -56,9 +56,6 @@ Trunk (Unreleased)
HADOOP-8719. Workaround for kerberos-related log errors upon running any
hadoop command on OSX. (Jianbin Wei via harsh)
- HADOOP-8814. Replace string equals "" by String#isEmpty().
- (Brandon Li via suresh)
-
HADOOP-8588. SerializationFactory shouldn't throw a
NullPointerException if the serializations list is empty.
(Sho Shimauchi via harsh)
@@ -271,6 +268,15 @@ Trunk (Unreleased)
HADOOP-9433 TestLocalFileSystem#testHasFileDescriptor leaks file handle
(Chris Nauroth via sanjay)
+ HADOOP-9583. test-patch gives +1 despite build failure when running tests.
+ (jlowe via kihwal)
+
+ HADOOP-9847. TestGlobPath symlink tests fail to cleanup properly.
+ (cmccabe via wang)
+
+ HADOOP-9740. Fix FsShell '-text' command to be able to read Avro
+ files stored in HDFS and other filesystems. (Allan Yan via cutting)
+
OPTIMIZATIONS
HADOOP-7761. Improve the performance of raw comparisons. (todd)
@@ -285,6 +291,9 @@ Release 2.3.0 - UNRELEASED
IMPROVEMENTS
+ HADOOP 9871. Fix intermittent findbugs warnings in DefaultMetricsSystem.
+ (Junping Du via llu)
+
HADOOP-9319. Update bundled LZ4 source to r99. (Binglin Chang via llu)
HADOOP-9241. DU refresh interval is not configurable (harsh)
@@ -304,6 +313,9 @@ Release 2.3.0 - UNRELEASED
HADOOP-9758. Provide configuration option for FileSystem/FileContext
symlink resolution. (Andrew Wang via Colin Patrick McCabe)
+ HADOOP-9848. Create a MiniKDC for use with security testing.
+ (ywskycn via tucu)
+
OPTIMIZATIONS
HADOOP-9748. Reduce blocking on UGI.ensureInitialized (daryn)
@@ -319,6 +331,10 @@ Release 2.3.0 - UNRELEASED
HADOOP-9817. FileSystem#globStatus and FileContext#globStatus need to work
with symlinks. (Colin Patrick McCabe via Andrew Wang)
+ HADOOP-9652. RawLocalFs#getFileLinkStatus does not fill in the link owner
+ and mode. (Andrew Wang via Colin Patrick McCabe)
+
+
Release 2.1.1-beta - UNRELEASED
INCOMPATIBLE CHANGES
@@ -326,6 +342,8 @@ Release 2.1.1-beta - UNRELEASED
NEW FEATURES
IMPROVEMENTS
+
+ HADOOP-9446. Support Kerberos SPNEGO for IBM JDK. (Yu Gao via llu)
HADOOP-9787. ShutdownHelper util to shutdown threads and threadpools.
(Karthik Kambatla via Sandy Ryza)
@@ -340,6 +358,11 @@ Release 2.1.1-beta - UNRELEASED
HADOOP-9789. Support server advertised kerberos principals (daryn)
+ HADOOP-8814. Replace string equals "" by String#isEmpty().
+ (Brandon Li via suresh)
+
+ HADOOP-9802. Support Snappy codec on Windows. (cnauroth)
+
OPTIMIZATIONS
BUG FIXES
@@ -361,6 +384,13 @@ Release 2.1.1-beta - UNRELEASED
HADOOP-9675. use svn:eol-style native for html to prevent line ending
issues (Colin Patrick McCabe)
+ HADOOP-9757. Har metadata cache can grow without limit (Cristina Abad via daryn)
+
+ HADOOP-9857. Tests block and sometimes timeout on Windows due to invalid
+ entropy source. (cnauroth)
+
+ HADOOP-9381. Document dfs cp -f option. (Keegan Witt, suresh via suresh)
+
Release 2.1.0-beta - 2013-08-06
INCOMPATIBLE CHANGES
@@ -558,6 +588,10 @@ Release 2.1.0-beta - 2013-08-06
HADOOP-9150. Avoid unnecessary DNS resolution attempts for logical URIs
(todd)
+ HADOOP-9845. Update protobuf to 2.5 from 2.4.x. (tucu)
+
+ HADOOP-9872. Improve protoc version handling and detection. (tucu)
+
BUG FIXES
HADOOP-9294. GetGroupsTestBase fails on Windows. (Chris Nauroth via suresh)
diff --git a/hadoop-common-project/hadoop-common/pom.xml b/hadoop-common-project/hadoop-common/pom.xml
index e29c3a5dfcd..a7462ea5a3b 100644
--- a/hadoop-common-project/hadoop-common/pom.xml
+++ b/hadoop-common-project/hadoop-common/pom.xml
@@ -308,6 +308,8 @@
protoc
+ ${protobuf.version}
+ ${protoc.path}
${basedir}/src/main/proto
@@ -336,6 +338,8 @@
protoc
+ ${protobuf.version}
+ ${protoc.path}
${basedir}/src/test/proto
@@ -586,6 +590,13 @@
Windows
+
+
+
+
+ false
+ true
+
@@ -670,6 +681,10 @@
/nologo/p:Configuration=Release/p:OutDir=${project.build.directory}/bin/
+ /p:CustomSnappyPrefix=${snappy.prefix}
+ /p:CustomSnappyLib=${snappy.lib}
+ /p:CustomSnappyInclude=${snappy.include}
+ /p:RequireSnappy=${require.snappy}
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/DelegateToFileSystem.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/DelegateToFileSystem.java
index 1293448eea3..708ca4ada5b 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/DelegateToFileSystem.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/DelegateToFileSystem.java
@@ -113,7 +113,14 @@ public abstract class DelegateToFileSystem extends AbstractFileSystem {
@Override
public FileStatus getFileLinkStatus(final Path f) throws IOException {
- return getFileStatus(f);
+ FileStatus status = fsImpl.getFileLinkStatus(f);
+ // FileSystem#getFileLinkStatus qualifies the link target
+ // AbstractFileSystem needs to return it plain since it's qualified
+ // in FileContext, so re-get and set the plain target
+ if (status.isSymlink()) {
+ status.setSymlink(fsImpl.getLinkTarget(f));
+ }
+ return status;
}
@Override
@@ -199,22 +206,18 @@ public abstract class DelegateToFileSystem extends AbstractFileSystem {
@Override
public boolean supportsSymlinks() {
- return false;
+ return fsImpl.supportsSymlinks();
}
@Override
public void createSymlink(Path target, Path link, boolean createParent)
throws IOException {
- throw new IOException("File system does not support symlinks");
+ fsImpl.createSymlink(target, link, createParent);
}
@Override
public Path getLinkTarget(final Path f) throws IOException {
- /* We should never get here. Any file system that threw an
- * UnresolvedLinkException, causing this function to be called,
- * should override getLinkTarget.
- */
- throw new AssertionError();
+ return fsImpl.getLinkTarget(f);
}
@Override //AbstractFileSystem
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HarFileSystem.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HarFileSystem.java
index 39a9d55fa18..9c6f04749bb 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HarFileSystem.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HarFileSystem.java
@@ -24,11 +24,12 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.HashMap;
-import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -56,10 +57,12 @@ public class HarFileSystem extends FilterFileSystem {
private static final Log LOG = LogFactory.getLog(HarFileSystem.class);
+ public static final String METADATA_CACHE_ENTRIES_KEY = "fs.har.metadatacache.entries";
+ public static final int METADATA_CACHE_ENTRIES_DEFAULT = 10;
+
public static final int VERSION = 3;
- private static final Map harMetaCache =
- new ConcurrentHashMap();
+ private static Map harMetaCache;
// uri representation of this Har filesystem
private URI uri;
@@ -98,7 +101,14 @@ public class HarFileSystem extends FilterFileSystem {
public HarFileSystem(FileSystem fs) {
super(fs);
}
-
+
+ private synchronized void initializeMetadataCache(Configuration conf) {
+ if (harMetaCache == null) {
+ int cacheSize = conf.getInt(METADATA_CACHE_ENTRIES_KEY, METADATA_CACHE_ENTRIES_DEFAULT);
+ harMetaCache = Collections.synchronizedMap(new LruCache(cacheSize));
+ }
+ }
+
/**
* Initialize a Har filesystem per har archive. The
* archive home directory is the top level directory
@@ -114,6 +124,9 @@ public class HarFileSystem extends FilterFileSystem {
*/
@Override
public void initialize(URI name, Configuration conf) throws IOException {
+ // initialize the metadata cache, if needed
+ initializeMetadataCache(conf);
+
// decode the name
URI underLyingURI = decodeHarURI(name, conf);
// we got the right har Path- now check if this is
@@ -1117,4 +1130,18 @@ public class HarFileSystem extends FilterFileSystem {
HarMetaData getMetadata() {
return metadata;
}
+
+ private static class LruCache extends LinkedHashMap {
+ private final int MAX_ENTRIES;
+
+ public LruCache(int maxEntries) {
+ super(maxEntries + 1, 1.0f, true);
+ MAX_ENTRIES = maxEntries;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > MAX_ENTRIES;
+ }
+ }
}
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HardLink.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HardLink.java
index 5e462cdc441..bf5ed6d58f7 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HardLink.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HardLink.java
@@ -41,15 +41,6 @@ import org.apache.hadoop.util.Shell;
*/
public class HardLink {
- public enum OSType {
- OS_TYPE_UNIX,
- OS_TYPE_WIN,
- OS_TYPE_SOLARIS,
- OS_TYPE_MAC,
- OS_TYPE_FREEBSD
- }
-
- public static OSType osType;
private static HardLinkCommandGetter getHardLinkCommand;
public final LinkStats linkStats; //not static
@@ -57,19 +48,18 @@ public class HardLink {
//initialize the command "getters" statically, so can use their
//methods without instantiating the HardLink object
static {
- osType = getOSType();
- if (osType == OSType.OS_TYPE_WIN) {
+ if (Shell.WINDOWS) {
// Windows
getHardLinkCommand = new HardLinkCGWin();
} else {
- // Unix
+ // Unix or Linux
getHardLinkCommand = new HardLinkCGUnix();
//override getLinkCountCommand for the particular Unix variant
//Linux is already set as the default - {"stat","-c%h", null}
- if (osType == OSType.OS_TYPE_MAC || osType == OSType.OS_TYPE_FREEBSD) {
+ if (Shell.MAC || Shell.FREEBSD) {
String[] linkCountCmdTemplate = {"/usr/bin/stat","-f%l", null};
HardLinkCGUnix.setLinkCountCmdTemplate(linkCountCmdTemplate);
- } else if (osType == OSType.OS_TYPE_SOLARIS) {
+ } else if (Shell.SOLARIS) {
String[] linkCountCmdTemplate = {"ls","-l", null};
HardLinkCGUnix.setLinkCountCmdTemplate(linkCountCmdTemplate);
}
@@ -80,26 +70,6 @@ public class HardLink {
linkStats = new LinkStats();
}
- static private OSType getOSType() {
- String osName = System.getProperty("os.name");
- if (Shell.WINDOWS) {
- return OSType.OS_TYPE_WIN;
- }
- else if (osName.contains("SunOS")
- || osName.contains("Solaris")) {
- return OSType.OS_TYPE_SOLARIS;
- }
- else if (osName.contains("Mac")) {
- return OSType.OS_TYPE_MAC;
- }
- else if (osName.contains("FreeBSD")) {
- return OSType.OS_TYPE_FREEBSD;
- }
- else {
- return OSType.OS_TYPE_UNIX;
- }
- }
-
/**
* This abstract class bridges the OS-dependent implementations of the
* needed functionality for creating hardlinks and querying link counts.
@@ -548,7 +518,7 @@ public class HardLink {
if (inpMsg == null || exitValue != 0) {
throw createIOException(fileName, inpMsg, errMsg, exitValue, null);
}
- if (osType == OSType.OS_TYPE_SOLARIS) {
+ if (Shell.SOLARIS) {
String[] result = inpMsg.split("\\s+");
return Integer.parseInt(result[1]);
} else {
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/RawLocalFileSystem.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/RawLocalFileSystem.java
index d693214163b..42f77fc3508 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/RawLocalFileSystem.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/RawLocalFileSystem.java
@@ -51,6 +51,7 @@ import org.apache.hadoop.util.StringUtils;
public class RawLocalFileSystem extends FileSystem {
static final URI NAME = URI.create("file:///");
private Path workingDir;
+ private static final boolean useDeprecatedFileStatus = !Stat.isAvailable();
public RawLocalFileSystem() {
workingDir = getInitialWorkingDirectory();
@@ -385,8 +386,11 @@ public class RawLocalFileSystem extends FileSystem {
throw new FileNotFoundException("File " + f + " does not exist");
}
if (localf.isFile()) {
+ if (!useDeprecatedFileStatus) {
+ return new FileStatus[] { getFileStatus(f) };
+ }
return new FileStatus[] {
- new RawLocalFileStatus(localf, getDefaultBlockSize(f), this) };
+ new DeprecatedRawLocalFileStatus(localf, getDefaultBlockSize(f), this)};
}
File[] names = localf.listFiles();
@@ -516,15 +520,22 @@ public class RawLocalFileSystem extends FileSystem {
@Override
public FileStatus getFileStatus(Path f) throws IOException {
+ return getFileLinkStatusInternal(f, true);
+ }
+
+ @Deprecated
+ private FileStatus deprecatedGetFileStatus(Path f) throws IOException {
File path = pathToFile(f);
if (path.exists()) {
- return new RawLocalFileStatus(pathToFile(f), getDefaultBlockSize(f), this);
+ return new DeprecatedRawLocalFileStatus(pathToFile(f),
+ getDefaultBlockSize(f), this);
} else {
throw new FileNotFoundException("File " + f + " does not exist");
}
}
- static class RawLocalFileStatus extends FileStatus {
+ @Deprecated
+ static class DeprecatedRawLocalFileStatus extends FileStatus {
/* We can add extra fields here. It breaks at least CopyFiles.FilePair().
* We recognize if the information is already loaded by check if
* onwer.equals("").
@@ -533,7 +544,7 @@ public class RawLocalFileSystem extends FileSystem {
return !super.getOwner().isEmpty();
}
- RawLocalFileStatus(File f, long defaultBlockSize, FileSystem fs) {
+ DeprecatedRawLocalFileStatus(File f, long defaultBlockSize, FileSystem fs) {
super(f.length(), f.isDirectory(), 1, defaultBlockSize,
f.lastModified(), new Path(f.getPath()).makeQualified(fs.getUri(),
fs.getWorkingDirectory()));
@@ -699,7 +710,7 @@ public class RawLocalFileSystem extends FileSystem {
*/
@Override
public FileStatus getFileLinkStatus(final Path f) throws IOException {
- FileStatus fi = getFileLinkStatusInternal(f);
+ FileStatus fi = getFileLinkStatusInternal(f, false);
// getFileLinkStatus is supposed to return a symlink with a
// qualified path
if (fi.isSymlink()) {
@@ -710,7 +721,35 @@ public class RawLocalFileSystem extends FileSystem {
return fi;
}
- private FileStatus getFileLinkStatusInternal(final Path f) throws IOException {
+ /**
+ * Public {@link FileStatus} methods delegate to this function, which in turn
+ * either call the new {@link Stat} based implementation or the deprecated
+ * methods based on platform support.
+ *
+ * @param f Path to stat
+ * @param dereference whether to dereference the final path component if a
+ * symlink
+ * @return FileStatus of f
+ * @throws IOException
+ */
+ private FileStatus getFileLinkStatusInternal(final Path f,
+ boolean dereference) throws IOException {
+ if (!useDeprecatedFileStatus) {
+ return getNativeFileLinkStatus(f, dereference);
+ } else if (dereference) {
+ return deprecatedGetFileStatus(f);
+ } else {
+ return deprecatedGetFileLinkStatusInternal(f);
+ }
+ }
+
+ /**
+ * Deprecated. Remains for legacy support. Should be removed when {@link Stat}
+ * gains support for Windows and other operating systems.
+ */
+ @Deprecated
+ private FileStatus deprecatedGetFileLinkStatusInternal(final Path f)
+ throws IOException {
String target = FileUtil.readLink(new File(f.toString()));
try {
@@ -746,10 +785,31 @@ public class RawLocalFileSystem extends FileSystem {
throw e;
}
}
+ /**
+ * Calls out to platform's native stat(1) implementation to get file metadata
+ * (permissions, user, group, atime, mtime, etc). This works around the lack
+ * of lstat(2) in Java 6.
+ *
+ * Currently, the {@link Stat} class used to do this only supports Linux
+ * and FreeBSD, so the old {@link #deprecatedGetFileLinkStatusInternal(Path)}
+ * implementation (deprecated) remains further OS support is added.
+ *
+ * @param f File to stat
+ * @param dereference whether to dereference symlinks
+ * @return FileStatus of f
+ * @throws IOException
+ */
+ private FileStatus getNativeFileLinkStatus(final Path f,
+ boolean dereference) throws IOException {
+ checkPath(f);
+ Stat stat = new Stat(f, getDefaultBlockSize(f), dereference, this);
+ FileStatus status = stat.getFileStatus();
+ return status;
+ }
@Override
public Path getLinkTarget(Path f) throws IOException {
- FileStatus fi = getFileLinkStatusInternal(f);
+ FileStatus fi = getFileLinkStatusInternal(f, false);
// return an unqualified symlink target
return fi.getSymlink();
}
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/Stat.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/Stat.java
new file mode 100644
index 00000000000..36dd8811e77
--- /dev/null
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/Stat.java
@@ -0,0 +1,167 @@
+/**
+ * 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.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.fs.permission.FsPermission;
+import org.apache.hadoop.util.Shell;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * Wrapper for the Unix stat(1) command. Used to workaround the lack of
+ * lstat(2) in Java 6.
+ */
+@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
+@InterfaceStability.Evolving
+public class Stat extends Shell {
+
+ private final Path original;
+ private final Path qualified;
+ private final Path path;
+ private final long blockSize;
+ private final boolean dereference;
+
+ private FileStatus stat;
+
+ public Stat(Path path, long blockSize, boolean deref, FileSystem fs)
+ throws IOException {
+ super(0L, true);
+ // Original path
+ this.original = path;
+ // Qualify the original and strip out URI fragment via toUri().getPath()
+ Path stripped = new Path(
+ original.makeQualified(fs.getUri(), fs.getWorkingDirectory())
+ .toUri().getPath());
+ // Re-qualify the bare stripped path and store it
+ this.qualified =
+ stripped.makeQualified(fs.getUri(), fs.getWorkingDirectory());
+ // Strip back down to a plain path
+ this.path = new Path(qualified.toUri().getPath());
+ this.blockSize = blockSize;
+ this.dereference = deref;
+ }
+
+ public FileStatus getFileStatus() throws IOException {
+ run();
+ return stat;
+ }
+
+ /**
+ * Whether Stat is supported on the current platform
+ * @return
+ */
+ public static boolean isAvailable() {
+ if (Shell.LINUX || Shell.FREEBSD) {
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ FileStatus getFileStatusForTesting() {
+ return stat;
+ }
+
+ @Override
+ protected String[] getExecString() {
+ String derefFlag = "-";
+ if (dereference) {
+ derefFlag = "-L";
+ }
+ if (Shell.LINUX) {
+ return new String[] {
+ "stat", derefFlag + "c", "%s,%F,%Y,%X,%a,%U,%G,%N", path.toString() };
+ } else if (Shell.FREEBSD) {
+ return new String[] {
+ "stat", derefFlag + "f", "%z,%HT,%m,%a,%Op,%Su,%Sg,`link' -> `%Y'",
+ path.toString() };
+ } else {
+ throw new UnsupportedOperationException(
+ "stat is not supported on this platform");
+ }
+ }
+
+ @Override
+ protected void parseExecResult(BufferedReader lines) throws IOException {
+ // Reset stat
+ stat = null;
+
+ String line = lines.readLine();
+ if (line == null) {
+ throw new IOException("Unable to stat path: " + original);
+ }
+ if (line.endsWith("No such file or directory") ||
+ line.endsWith("Not a directory")) {
+ throw new FileNotFoundException("File " + original + " does not exist");
+ }
+ if (line.endsWith("Too many levels of symbolic links")) {
+ throw new IOException("Possible cyclic loop while following symbolic" +
+ " link " + original);
+ }
+ // 6,symbolic link,6,1373584236,1373584236,lrwxrwxrwx,andrew,andrew,`link' -> `target'
+ StringTokenizer tokens = new StringTokenizer(line, ",");
+ try {
+ long length = Long.parseLong(tokens.nextToken());
+ boolean isDir = tokens.nextToken().equalsIgnoreCase("directory") ? true
+ : false;
+ // Convert from seconds to milliseconds
+ long modTime = Long.parseLong(tokens.nextToken())*1000;
+ long accessTime = Long.parseLong(tokens.nextToken())*1000;
+ String octalPerms = tokens.nextToken();
+ // FreeBSD has extra digits beyond 4, truncate them
+ if (octalPerms.length() > 4) {
+ int len = octalPerms.length();
+ octalPerms = octalPerms.substring(len-4, len);
+ }
+ FsPermission perms = new FsPermission(Short.parseShort(octalPerms, 8));
+ String owner = tokens.nextToken();
+ String group = tokens.nextToken();
+ String symStr = tokens.nextToken();
+ // 'notalink'
+ // 'link' -> `target'
+ // '' -> ''
+ Path symlink = null;
+ StringTokenizer symTokens = new StringTokenizer(symStr, "`");
+ symTokens.nextToken();
+ try {
+ String target = symTokens.nextToken();
+ target = target.substring(0, target.length()-1);
+ if (!target.isEmpty()) {
+ symlink = new Path(target);
+ }
+ } catch (NoSuchElementException e) {
+ // null if not a symlink
+ }
+ // Set stat
+ stat = new FileStatus(length, isDir, 1, blockSize, modTime, accessTime,
+ perms, owner, group, symlink, qualified);
+ } catch (NumberFormatException e) {
+ throw new IOException("Unexpected stat output: " + line, e);
+ } catch (NoSuchElementException e) {
+ throw new IOException("Unexpected stat output: " + line, e);
+ }
+ }
+}
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/local/RawLocalFs.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/local/RawLocalFs.java
index 605bade09a8..6cb2792eebc 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/local/RawLocalFs.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/local/RawLocalFs.java
@@ -17,8 +17,6 @@
*/
package org.apache.hadoop.fs.local;
-import java.io.File;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -28,13 +26,9 @@ import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.AbstractFileSystem;
import org.apache.hadoop.fs.DelegateToFileSystem;
-import org.apache.hadoop.fs.FileStatus;
-import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.FsConstants;
import org.apache.hadoop.fs.FsServerDefaults;
-import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RawLocalFileSystem;
-import org.apache.hadoop.fs.permission.FsPermission;
/**
* The RawLocalFs implementation of AbstractFileSystem.
@@ -72,90 +66,12 @@ public class RawLocalFs extends DelegateToFileSystem {
public FsServerDefaults getServerDefaults() throws IOException {
return LocalConfigKeys.getServerDefaults();
}
-
+
@Override
- public boolean supportsSymlinks() {
+ public boolean isValidName(String src) {
+ // Different local file systems have different validation rules. Skip
+ // validation here and just let the OS handle it. This is consistent with
+ // RawLocalFileSystem.
return true;
}
-
- @Override
- public void createSymlink(Path target, Path link, boolean createParent)
- throws IOException {
- final String targetScheme = target.toUri().getScheme();
- if (targetScheme != null && !"file".equals(targetScheme)) {
- throw new IOException("Unable to create symlink to non-local file "+
- "system: "+target.toString());
- }
-
- if (createParent) {
- mkdir(link.getParent(), FsPermission.getDirDefault(), true);
- }
-
- // NB: Use createSymbolicLink in java.nio.file.Path once available
- int result = FileUtil.symLink(target.toString(), link.toString());
- if (result != 0) {
- throw new IOException("Error " + result + " creating symlink " +
- link + " to " + target);
- }
- }
-
- /**
- * Return a FileStatus representing the given path. If the path refers
- * to a symlink return a FileStatus representing the link rather than
- * the object the link refers to.
- */
- @Override
- public FileStatus getFileLinkStatus(final Path f) throws IOException {
- String target = FileUtil.readLink(new File(f.toString()));
- try {
- FileStatus fs = getFileStatus(f);
- // If f refers to a regular file or directory
- if (target.isEmpty()) {
- return fs;
- }
- // Otherwise f refers to a symlink
- return new FileStatus(fs.getLen(),
- false,
- fs.getReplication(),
- fs.getBlockSize(),
- fs.getModificationTime(),
- fs.getAccessTime(),
- fs.getPermission(),
- fs.getOwner(),
- fs.getGroup(),
- new Path(target),
- f);
- } catch (FileNotFoundException e) {
- /* The exists method in the File class returns false for dangling
- * links so we can get a FileNotFoundException for links that exist.
- * It's also possible that we raced with a delete of the link. Use
- * the readBasicFileAttributes method in java.nio.file.attributes
- * when available.
- */
- if (!target.isEmpty()) {
- return new FileStatus(0, false, 0, 0, 0, 0, FsPermission.getDefault(),
- "", "", new Path(target), f);
- }
- // f refers to a file or directory that does not exist
- throw e;
- }
- }
-
- @Override
- public boolean isValidName(String src) {
- // Different local file systems have different validation rules. Skip
- // validation here and just let the OS handle it. This is consistent with
- // RawLocalFileSystem.
- return true;
- }
-
- @Override
- public Path getLinkTarget(Path f) throws IOException {
- /* We should never get here. Valid local links are resolved transparently
- * by the underlying local file system and accessing a dangling link will
- * result in an IOException, not an UnresolvedLinkException, so FileContext
- * should never call this function.
- */
- throw new AssertionError();
- }
}
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/CopyCommands.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/CopyCommands.java
index 6c39cf897ef..db15d467ec3 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/CopyCommands.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/CopyCommands.java
@@ -133,7 +133,8 @@ class CopyCommands {
"Copy files that match the file pattern to a\n" +
"destination. When copying multiple files, the destination\n" +
"must be a directory. Passing -p preserves access and\n" +
- "modification times, ownership and the mode.\n";
+ "modification times, ownership and the mode. Passing -f\n" +
+ "overwrites the destination if it already exists.\n";
@Override
protected void processOptions(LinkedList args) throws IOException {
@@ -186,7 +187,8 @@ class CopyCommands {
"into fs. Copying fails if the file already\n" +
"exists, unless the -f flag is given. Passing\n" +
"-p preserves access and modification times,\n" +
- "ownership and the mode.\n";
+ "ownership and the mode. Passing -f overwrites\n" +
+ "the destination if it already exists.\n";
@Override
protected void processOptions(LinkedList args) throws IOException {
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Display.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Display.java
index eb8a8cfca2a..143bf12db8e 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Display.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Display.java
@@ -35,8 +35,10 @@ import org.apache.avro.Schema;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.AvroFSInput;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileChecksum;
+import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.PathIsDirectoryException;
@@ -259,8 +261,9 @@ class Display extends FsCommand {
pos = 0;
buffer = new byte[0];
GenericDatumReader