This is an automated email from the ASF dual-hosted git repository.

pravin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git


The following commit(s) were added to refs/heads/master by this push:
     new 52a0399  HIVE-25609: Preserve XAttrs in normal file copy case. 
(Haymant Mangla, reviewed by Ayush Saxena)
52a0399 is described below

commit 52a0399b146814e8a6a7c38b1c5f6e215b7851fb
Author: Haymant Mangla <79496857+hmangl...@users.noreply.github.com>
AuthorDate: Wed Dec 1 14:44:18 2021 +0530

    HIVE-25609: Preserve XAttrs in normal file copy case. (Haymant Mangla, 
reviewed by Ayush Saxena)
---
 .../org/apache/hadoop/hive/common/FileUtils.java   | 145 ++++++++++++++++++++-
 .../apache/hadoop/hive/common/TestFileUtils.java   |  81 ++++++++++++
 .../parse/TestReplicationScenariosAcidTables.java  |  77 +++++++++++
 .../hadoop/hive/ql/parse/repl/CopyUtils.java       |  11 +-
 .../apache/hadoop/hive/shims/Hadoop23Shims.java    |  17 +--
 .../java/org/apache/hadoop/hive/shims/Utils.java   |  14 ++
 6 files changed, 323 insertions(+), 22 deletions(-)

diff --git a/common/src/java/org/apache/hadoop/hive/common/FileUtils.java 
b/common/src/java/org/apache/hadoop/hive/common/FileUtils.java
index fdd78cb..d5cf3d6 100644
--- a/common/src/java/org/apache/hadoop/hive/common/FileUtils.java
+++ b/common/src/java/org/apache/hadoop/hive/common/FileUtils.java
@@ -37,14 +37,13 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
+import java.util.Map;
 
 import com.google.common.annotations.VisibleForTesting;
-
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.ContentSummary;
 import org.apache.hadoop.fs.FileStatus;
-import org.apache.hadoop.fs.FileSystem;
-import org.apache.hadoop.fs.FileUtil;
 import org.apache.hadoop.fs.GlobFilter;
 import org.apache.hadoop.fs.LocalFileSystem;
 import org.apache.hadoop.fs.LocatedFileStatus;
@@ -53,10 +52,14 @@ import org.apache.hadoop.fs.PathFilter;
 import org.apache.hadoop.fs.RemoteIterator;
 import org.apache.hadoop.fs.Trash;
 import org.apache.hadoop.fs.permission.FsAction;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.apache.hadoop.fs.PathExistsException;
+import org.apache.hadoop.fs.PathIsDirectoryException;
 import org.apache.hadoop.hive.conf.HiveConf;
 import org.apache.hadoop.hive.shims.HadoopShims;
 import org.apache.hadoop.hive.shims.ShimLoader;
 import org.apache.hadoop.hive.shims.Utils;
+import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hive.common.util.ShutdownHookManager;
@@ -661,11 +664,145 @@ public final class FileUtils {
       // is tried and it fails. We depend upon that behaviour in cases like 
replication,
       // wherein if distcp fails, there is good reason to not plod along with 
a trivial
       // implementation, and fail instead.
-      copied = FileUtil.copy(srcFS, src, dstFS, dst, deleteSource, overwrite, 
conf);
+      copied = copy(srcFS, srcFS.getFileStatus(src), dstFS, dst, deleteSource, 
overwrite, shouldPreserveXAttrs(conf, srcFS, dstFS), conf);
     }
     return copied;
   }
 
+  public static boolean copy(FileSystem srcFS, FileStatus srcStatus, 
FileSystem dstFS, Path dst, boolean deleteSource,
+                             boolean overwrite, boolean preserveXAttrs, 
Configuration conf) throws IOException {
+    Path src = srcStatus.getPath();
+    dst = checkDest(src.getName(), dstFS, dst, overwrite);
+    if (srcStatus.isDirectory()) {
+      checkDependencies(srcFS, src, dstFS, dst);
+      if (!dstFS.mkdirs(dst)) {
+        return false;
+      }
+
+      RemoteIterator<FileStatus> fileIterator = srcFS.listStatusIterator(src);
+      while(fileIterator.hasNext()) {
+        FileStatus file = fileIterator.next();
+        copy(srcFS, file, dstFS, new Path(dst, file.getPath().getName()), 
deleteSource, overwrite, preserveXAttrs, conf);
+      }
+      if (preserveXAttrs) {
+        preserveXAttr(srcFS, src, dstFS, dst);
+      }
+    } else {
+      InputStream in = null;
+      FSDataOutputStream out = null;
+
+      try {
+        in = srcFS.open(src);
+        out = dstFS.create(dst, overwrite);
+        IOUtils.copyBytes(in, out, conf, true);
+        if (preserveXAttrs) {
+          preserveXAttr(srcFS, src, dstFS, dst);
+        }
+      } catch (IOException var11) {
+        IOUtils.closeStream(in);
+        IOUtils.closeStream(out);
+        throw var11;
+      }
+    }
+
+    return deleteSource ? srcFS.delete(src, true) : true;
+  }
+
+  public static boolean copy(FileSystem srcFS, Path[] srcs, FileSystem dstFS, 
Path dst, boolean deleteSource, boolean overwrite, boolean preserveXAttr, 
Configuration conf) throws IOException {
+    boolean gotException = false;
+    boolean returnVal = true;
+    StringBuilder exceptions = new StringBuilder();
+    if (srcs.length == 1) {
+      return copy(srcFS, srcFS.getFileStatus(srcs[0]), dstFS, dst, 
deleteSource, overwrite, preserveXAttr, conf);
+    } else {
+      try {
+        FileStatus sdst = dstFS.getFileStatus(dst);
+        if (!sdst.isDirectory()) {
+          throw new IOException("copying multiple files, but last argument `" 
+ dst + "' is not a directory");
+        }
+      } catch (FileNotFoundException var16) {
+        throw new IOException("`" + dst + "': specified destination directory 
does not exist", var16);
+      }
+
+      Path[] var17 = srcs;
+      int var11 = srcs.length;
+
+      for(int var12 = 0; var12 < var11; ++var12) {
+        Path src = var17[var12];
+
+        try {
+          if (!copy(srcFS, srcFS.getFileStatus(src), dstFS, dst, deleteSource, 
overwrite, preserveXAttr, conf)) {
+            returnVal = false;
+          }
+        } catch (IOException var15) {
+          gotException = true;
+          exceptions.append(var15.getMessage());
+          exceptions.append("\n");
+        }
+      }
+
+      if (gotException) {
+        throw new IOException(exceptions.toString());
+      } else {
+        return returnVal;
+      }
+    }
+  }
+
+  private static void preserveXAttr(FileSystem srcFS, Path src, FileSystem 
dstFS, Path dst) throws IOException {
+    for (Map.Entry<String, byte[]> attr : srcFS.getXAttrs(src).entrySet()) {
+      dstFS.setXAttr(dst, attr.getKey(), attr.getValue());
+    }
+  }
+
+  private static Path checkDest(String srcName, FileSystem dstFS, Path dst, 
boolean overwrite) throws IOException {
+    FileStatus sdst;
+    try {
+      sdst = dstFS.getFileStatus(dst);
+    } catch (FileNotFoundException var6) {
+      sdst = null;
+    }
+    if (null != sdst) {
+      if (sdst.isDirectory()) {
+        if (null == srcName) {
+          throw new PathIsDirectoryException(dst.toString());
+        }
+
+        return checkDest((String)null, dstFS, new Path(dst, srcName), 
overwrite);
+      }
+
+      if (!overwrite) {
+        throw new PathExistsException(dst.toString(), "Target " + dst + " 
already exists");
+      }
+    }
+
+    return dst;
+  }
+
+  private static void checkDependencies(FileSystem srcFS, Path src, FileSystem 
dstFS, Path dst) throws IOException {
+    if (srcFS == dstFS) {
+      String srcq = srcFS.makeQualified(src).toString() + "/";
+      String dstq = dstFS.makeQualified(dst).toString() + "/";
+      if (dstq.startsWith(srcq)) {
+        throw new IOException((srcq.length() == dstq.length()) ?
+                "Cannot copy " + src + " to itself." : "Cannot copy " + src + 
" to its subdirectory " + dst);
+      }
+    }
+  }
+
+  public static boolean shouldPreserveXAttrs(HiveConf conf, FileSystem srcFS, 
FileSystem dstFS) throws IOException {
+    if (!Utils.checkFileSystemXAttrSupport(srcFS) || 
!Utils.checkFileSystemXAttrSupport(dstFS)){
+      return false;
+    }
+    for (Map.Entry<String,String> entry : 
conf.getPropsWithPrefix(Utils.DISTCP_OPTIONS_PREFIX).entrySet()) {
+      String distCpOption = entry.getKey();
+      if (distCpOption.startsWith("p")) {
+        return distCpOption.contains("x");
+      }
+    }
+    return true;
+  }
+
   public static boolean distCp(FileSystem srcFS, List<Path> srcPaths, Path dst,
       boolean deleteSource, UserGroupInformation proxyUser,
       HiveConf conf, HadoopShims shims) throws IOException {
diff --git 
a/itests/hive-unit-hadoop2/src/test/java/org/apache/hadoop/hive/common/TestFileUtils.java
 
b/itests/hive-unit-hadoop2/src/test/java/org/apache/hadoop/hive/common/TestFileUtils.java
index 47af79c..fa91c2c 100644
--- 
a/itests/hive-unit-hadoop2/src/test/java/org/apache/hadoop/hive/common/TestFileUtils.java
+++ 
b/itests/hive-unit-hadoop2/src/test/java/org/apache/hadoop/hive/common/TestFileUtils.java
@@ -20,6 +20,8 @@ package org.apache.hadoop.hive.common;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
 
 import org.apache.hadoop.hive.conf.HiveConf;
 import org.apache.hadoop.hive.shims.HadoopShims;
@@ -27,6 +29,8 @@ import org.apache.hadoop.hive.shims.ShimLoader;
 
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.RemoteIterator;
 
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -75,6 +79,83 @@ public class TestFileUtils {
   }
 
   @Test
+  public void testXAttrsPreserved() throws Exception {
+    //Case 1) src and dst are files.
+    Path src = new Path(basePath, "src.txt");
+    fs.create(src).close();
+    setXAttrsRecursive(src);
+    Path dst = new Path(basePath, "dst.txt");
+    Assert.assertFalse(fs.exists(dst));
+    Assert.assertTrue(FileUtils.copy(fs, fs.getFileStatus(src), fs, dst, 
false, true, true, conf));
+    Assert.assertTrue(fs.exists(dst));
+    verifyXAttrsPreserved(src, dst);
+    //Case 2) src is file and dst directory does not exist.
+    dst = new Path(basePath, "dummyDstDir");
+    Assert.assertFalse(fs.exists(dst));
+    Assert.assertTrue(FileUtils.copy(fs, fs.getFileStatus(src), fs, dst, 
false, true, true, conf));
+    Assert.assertTrue(fs.exists(dst));
+    Assert.assertTrue(fs.exists(new Path(dst, new Path(basePath, "src.txt"))));
+    verifyXAttrsPreserved(src, dst);
+    //Case 3) src is a file and dst directory exists.
+    dst = new Path(basePath, "dummyDstDir1");
+    fs.mkdirs(dst);
+    Assert.assertTrue(fs.exists(dst));
+    Assert.assertTrue(FileUtils.copy(fs, fs.getFileStatus(src), fs, dst, 
false, true, true, conf));
+    Assert.assertTrue(fs.exists(dst));
+    Assert.assertTrue(fs.exists(new Path(dst, "src.txt")));
+    verifyXAttrsPreserved(src, new Path(dst, "src.txt"));
+    //Case 4) src & dst are directories and dst does not exist.
+    src = new Path(basePath, "dummySrcDir2");
+    dst = new Path(basePath, "dummyDstDir2");
+    fs.create(new Path(src, "src.txt"));
+    setXAttrsRecursive(src);
+    Assert.assertFalse(fs.exists(dst));
+    Assert.assertTrue(FileUtils.copy(fs, fs.getFileStatus(src), fs, dst, 
false, true, true, conf));
+    Assert.assertTrue(fs.exists(dst));
+    Assert.assertTrue(fs.exists(new Path(dst, "src.txt")));
+    verifyXAttrsPreserved(src, dst);
+    //Case 5) src & dst are directories and dst directory exists
+    src = new Path(basePath, "dummySrcDir3");
+    dst = new Path(basePath, "dummyDstDir3");
+    fs.create(new Path(src, "src.txt"));
+    fs.mkdirs(dst);
+    setXAttrsRecursive(src);
+    Assert.assertTrue(fs.exists(dst));
+    Assert.assertTrue(FileUtils.copy(fs, fs.getFileStatus(src), fs, dst, 
false, true, true, conf));
+    Assert.assertTrue(fs.exists(new Path(dst, "dummySrcDir3/src.txt")));
+    verifyXAttrsPreserved(src, new Path(dst, src.getName()));
+  }
+
+  private void verifyXAttrsPreserved(Path src, Path dst) throws Exception {
+    FileStatus srcStatus = fs.getFileStatus(src);
+    FileStatus dstStatus = fs.getFileStatus(dst);
+    if (srcStatus.isDirectory()) {
+      Assert.assertTrue(dstStatus.isDirectory());
+      for(FileStatus srcContent: fs.listStatus(src)) {
+        Path dstContent = new Path(dst, srcContent.getPath().getName());
+        Assert.assertTrue(fs.exists(dstContent));
+        verifyXAttrsPreserved(srcContent.getPath(), dstContent);
+      }
+    } else {
+      Assert.assertFalse(dstStatus.isDirectory());
+    }
+    Map<String, byte[]> values = fs.getXAttrs(dst);
+    for(Map.Entry<String, byte[]> value : fs.getXAttrs(src).entrySet()) {
+      Assert.assertEquals(new String(value.getValue()), new 
String(values.get(value.getKey())));
+    }
+  }
+
+  private void setXAttrsRecursive(Path path) throws Exception {
+    if (fs.getFileStatus(path).isDirectory()) {
+      RemoteIterator<FileStatus> content = fs.listStatusIterator(path);
+      while(content.hasNext()) {
+        setXAttrsRecursive(content.next().getPath());
+      }
+    }
+    fs.setXAttr(path, "user.random", "value".getBytes(StandardCharsets.UTF_8));
+  }
+
+  @Test
   public void testCopyWithDistcp() throws IOException {
     String file1Name = "file1.txt";
     String file2Name = "file2.txt";
diff --git 
a/itests/hive-unit/src/test/java/org/apache/hadoop/hive/ql/parse/TestReplicationScenariosAcidTables.java
 
b/itests/hive-unit/src/test/java/org/apache/hadoop/hive/ql/parse/TestReplicationScenariosAcidTables.java
index f997f84..d05ff85 100644
--- 
a/itests/hive-unit/src/test/java/org/apache/hadoop/hive/ql/parse/TestReplicationScenariosAcidTables.java
+++ 
b/itests/hive-unit/src/test/java/org/apache/hadoop/hive/ql/parse/TestReplicationScenariosAcidTables.java
@@ -21,6 +21,7 @@ import org.apache.hadoop.fs.FSDataOutputStream;
 import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.RemoteIterator;
 import org.apache.hadoop.hdfs.MiniDFSCluster;
 import org.apache.hadoop.hive.cli.CliSessionState;
 import org.apache.hadoop.hive.common.repl.ReplConst;
@@ -29,6 +30,7 @@ import org.apache.hadoop.hive.metastore.ReplChangeManager;
 import org.apache.hadoop.hive.metastore.api.Database;
 import org.apache.hadoop.hive.metastore.api.AbortTxnRequest;
 import org.apache.hadoop.hive.metastore.api.AbortTxnsRequest;
+import org.apache.hadoop.hive.metastore.api.Partition;
 import org.apache.hadoop.hive.metastore.conf.MetastoreConf;
 import 
org.apache.hadoop.hive.metastore.messaging.json.gzip.GzipJSONMessageEncoder;
 import org.apache.hadoop.hive.metastore.txn.TxnStore;
@@ -43,6 +45,7 @@ import org.apache.hadoop.hive.ql.IDriver;
 import org.apache.hadoop.hive.ql.exec.repl.ReplAck;
 import org.apache.hadoop.hive.ql.exec.repl.ReplDumpWork;
 import org.apache.hadoop.hive.ql.exec.repl.util.ReplUtils;
+import org.apache.hadoop.hive.ql.metadata.Hive;
 import org.apache.hadoop.hive.ql.parse.repl.DumpType;
 import org.apache.hadoop.hive.ql.parse.repl.load.DumpMetaData;
 import org.apache.hadoop.hive.ql.parse.repl.load.FailoverMetaData;
@@ -65,6 +68,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -989,6 +993,79 @@ public class TestReplicationScenariosAcidTables extends 
BaseReplicationScenarios
   }
 
   @Test
+  public void testXAttrsPreserved() throws Throwable {
+    String nonTxnTable = "nonTxnTable";
+    String unptnedTable = "unptnedTable";
+    String ptnedTable = "ptnedTable";
+    String clusteredTable = "clusteredTable";
+    String clusteredAndPtnedTable = "clusteredAndPtnedTable";
+    primary.run("use " + primaryDbName)
+            .run("create table " + nonTxnTable + " (id int)")
+            .run("create table " + unptnedTable + " (id int) stored as orc " +
+                    "tblproperties (\"transactional\"=\"true\")")
+            .run("create table " + ptnedTable + " (id int) partitioned by 
(name string) stored as orc " +
+                    "tblproperties (\"transactional\"=\"true\")")
+            .run("create table " + clusteredTable + " (id int) clustered by 
(id) into 3 buckets stored as orc " +
+                    "tblproperties (\"transactional\"=\"true\")")
+            .run("create table " + clusteredAndPtnedTable + " (id int) 
partitioned by (name string) clustered by(id)" +
+                    "into 3 buckets stored as orc tblproperties 
(\"transactional\"=\"true\")")
+            .run("insert into " + nonTxnTable + " values (2)").run("INSERT 
into " + unptnedTable + " values(1)")
+            .run("INSERT into " + ptnedTable + " values(2, 
'temp')").run("INSERT into " + clusteredTable + " values(1)")
+            .run("INSERT into " + clusteredAndPtnedTable + " values(1, 
'temp')");
+    for (String table : primary.getAllTables(primaryDbName)) {
+      org.apache.hadoop.hive.ql.metadata.Table tb = 
Hive.get(primary.hiveConf).getTable(primaryDbName, table);
+      if (tb.isPartitioned()) {
+        List<Partition> partitions = primary.getAllPartitions(primaryDbName, 
table);
+        for (Partition partition: partitions) {
+          Path partitionLoc = new Path(partition.getSd().getLocation());
+          FileSystem fs = partitionLoc.getFileSystem(conf);
+          setXAttrsRecursive(fs, partitionLoc, true);
+        }
+      } else {
+        Path tablePath = tb.getDataLocation();
+        FileSystem fs = tablePath.getFileSystem(conf);
+        setXAttrsRecursive(fs, tablePath, true);
+      }
+    }
+    primary.dump(primaryDbName);
+    replica.load(replicatedDbName, primaryDbName);
+    Path srcDbPath = new 
Path(primary.getDatabase(primaryDbName).getLocationUri());
+    Path replicaDbPath = new 
Path(primary.getDatabase(replicatedDbName).getLocationUri());
+    verifyXAttrsPreserved(srcDbPath.getFileSystem(conf), srcDbPath, 
replicaDbPath);
+  }
+
+  private void setXAttrsRecursive(FileSystem fs, Path path, boolean isParent) 
throws Exception {
+    if (fs.getFileStatus(path).isDirectory()) {
+      RemoteIterator<FileStatus> content = fs.listStatusIterator(path);
+      while(content.hasNext()) {
+        setXAttrsRecursive(fs, content.next().getPath(), false);
+      }
+    }
+    if (!isParent) {
+      fs.setXAttr(path, "user.random", 
"value".getBytes(StandardCharsets.UTF_8));
+    }
+  }
+
+  private void verifyXAttrsPreserved(FileSystem fs, Path src, Path dst) throws 
Exception {
+    FileStatus srcStatus = fs.getFileStatus(src);
+    FileStatus dstStatus = fs.getFileStatus(dst);
+    if (srcStatus.isDirectory()) {
+      assertTrue(dstStatus.isDirectory());
+      for(FileStatus srcContent: fs.listStatus(src)) {
+        Path dstContent = new Path(dst, srcContent.getPath().getName());
+        assertTrue(fs.exists(dstContent));
+        verifyXAttrsPreserved(fs, srcContent.getPath(), dstContent);
+      }
+    } else {
+      assertFalse(dstStatus.isDirectory());
+    }
+    Map<String, byte[]> values = fs.getXAttrs(dst);
+    for(Map.Entry<String, byte[]> value : fs.getXAttrs(src).entrySet()) {
+      assertEquals(new String(value.getValue()), new 
String(values.get(value.getKey())));
+    }
+  }
+
+  @Test
   public void testAcidTablesBootstrap() throws Throwable {
     // Bootstrap
     WarehouseInstance.Tuple bootstrapDump = prepareDataAndDump(primaryDbName, 
null);
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/repl/CopyUtils.java 
b/ql/src/java/org/apache/hadoop/hive/ql/parse/repl/CopyUtils.java
index 43e4ecc..b7eb5b2 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/parse/repl/CopyUtils.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/repl/CopyUtils.java
@@ -109,10 +109,13 @@ public class CopyUtils {
   }
 
   @VisibleForTesting
-  void copyFilesBetweenFS(FileSystem sourceFs, Path[] paths, FileSystem 
destinationFs,
-                                  Path finalDestination, boolean deleteSource, 
boolean overwrite) throws IOException {
-    retryableFxn(() -> FileUtil
-            .copy(sourceFs, paths, destinationFs, finalDestination, 
deleteSource, overwrite, hiveConf));
+  void copyFilesBetweenFS(FileSystem srcFS, Path[] paths, FileSystem dstFS,
+                                  Path dst, boolean deleteSource, boolean 
overwrite) throws IOException {
+    retryableFxn(() -> {
+      boolean preserveXAttrs = FileUtils.shouldPreserveXAttrs(hiveConf, srcFS, 
dstFS);
+      FileUtils.copy(srcFS, paths, dstFS, dst, deleteSource, overwrite, 
preserveXAttrs, hiveConf);
+      return null;
+    });
   }
 
   @VisibleForTesting
diff --git 
a/shims/0.23/src/main/java/org/apache/hadoop/hive/shims/Hadoop23Shims.java 
b/shims/0.23/src/main/java/org/apache/hadoop/hive/shims/Hadoop23Shims.java
index a611dda..a4972fd 100644
--- a/shims/0.23/src/main/java/org/apache/hadoop/hive/shims/Hadoop23Shims.java
+++ b/shims/0.23/src/main/java/org/apache/hadoop/hive/shims/Hadoop23Shims.java
@@ -277,16 +277,6 @@ public class Hadoop23Shims extends HadoopShimsSecure {
         equalsIgnoreCase(conf.get(YarnConfiguration.RM_SCHEDULER));
   }
 
-  private boolean checkFileSystemXAttrSupport(FileSystem fs) throws 
IOException {
-    try {
-      fs.getXAttrs(new Path(Path.SEPARATOR));
-      return true;
-    } catch (UnsupportedOperationException e) {
-      LOG.warn("XAttr won't be preserved since it is not supported for file 
system: " + fs.getUri());
-      return false;
-    }
-  }
-
   /**
    * Returns a shim to wrap MiniMrCluster
    */
@@ -1110,8 +1100,6 @@ public class Hadoop23Shims extends HadoopShimsSecure {
     }
   }
 
-  private static final String DISTCP_OPTIONS_PREFIX = "distcp.options.";
-
   List<String> constructDistCpParams(List<Path> srcPaths, Path dst, 
Configuration conf) throws IOException {
     // -update and -delete are mandatory options for directory copy to work.
     List<String> params = constructDistCpDefaultParams(conf, 
dst.getFileSystem(conf),
@@ -1130,7 +1118,7 @@ public class Hadoop23Shims extends HadoopShimsSecure {
                                                     FileSystem sourceFs) 
throws IOException {
     List<String> params = new ArrayList<String>();
     boolean needToAddPreserveOption = true;
-    for (Map.Entry<String,String> entry : 
conf.getPropsWithPrefix(DISTCP_OPTIONS_PREFIX).entrySet()){
+    for (Map.Entry<String,String> entry : 
conf.getPropsWithPrefix(Utils.DISTCP_OPTIONS_PREFIX).entrySet()){
       String distCpOption = entry.getKey();
       String distCpVal = entry.getValue();
       if (distCpOption.startsWith("p")) {
@@ -1142,7 +1130,8 @@ public class Hadoop23Shims extends HadoopShimsSecure {
       }
     }
     if (needToAddPreserveOption) {
-      params.add((checkFileSystemXAttrSupport(dstFs) && 
checkFileSystemXAttrSupport(sourceFs)) ? "-pbx" : "-pb");
+      params.add((Utils.checkFileSystemXAttrSupport(dstFs)
+              && Utils.checkFileSystemXAttrSupport(sourceFs)) ? "-pbx" : 
"-pb");
     }
     if (!params.contains("-update")) {
       params.add("-update");
diff --git a/shims/common/src/main/java/org/apache/hadoop/hive/shims/Utils.java 
b/shims/common/src/main/java/org/apache/hadoop/hive/shims/Utils.java
index 0343ae2..7cf78dc 100644
--- a/shims/common/src/main/java/org/apache/hadoop/hive/shims/Utils.java
+++ b/shims/common/src/main/java/org/apache/hadoop/hive/shims/Utils.java
@@ -37,6 +37,8 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authentication.util.KerberosUtil;
@@ -51,6 +53,8 @@ public class Utils {
   private static final boolean IBM_JAVA = System.getProperty("java.vendor")
       .contains("IBM");
 
+  public static final String DISTCP_OPTIONS_PREFIX = "distcp.options.";
+
   public static UserGroupInformation getUGI() throws LoginException, 
IOException {
     String doAs = System.getenv("HADOOP_USER_NAME");
     if(doAs != null && doAs.length() > 0) {
@@ -156,6 +160,16 @@ public class Utils {
     return Utils.constructXSRFFilter();
   }
 
+  public static boolean checkFileSystemXAttrSupport(FileSystem fs) throws 
IOException {
+    try {
+      fs.getXAttrs(new Path(Path.SEPARATOR));
+      return true;
+    } catch (UnsupportedOperationException e) {
+      LOG.warn("XAttr won't be preserved since it is not supported for file 
system: " + fs.getUri());
+      return false;
+    }
+  }
+
   private static Filter constructXSRFFilter() {
     // Note Hadoop 2.7.1 onwards includes a RestCsrfPreventionFilter class 
that is
     // usable as-is. However, since we have to work on a multitude of hadoop 
versions

Reply via email to