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

dongjoon-hyun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new 4b0e62443fee [SPARK-38632][CORE] Use `FileUtil.unTar` for `.tar` files 
at `Utils.unpack`
4b0e62443fee is described below

commit 4b0e62443fee542b06b1ea13809b7161af0c3e44
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Wed Jul 8 21:21:02 2026 -0700

    [SPARK-38632][CORE] Use `FileUtil.unTar` for `.tar` files at `Utils.unpack`
    
    ### What changes were proposed in this pull request?
    
    This PR aims to remove the Java-based un-tarring workaround 
(`unTarUsingJava`) from `Utils.unpack` and use Hadoop's `FileUtil.unTar` for 
`.tar` files again, like `.tar.gz` and `.tgz` files.
    
    ### Why are the changes needed?
    
    SPARK-38631 introduced this workaround because Hadoop's `FileUtil.unTar` 
did not escape the file name before passing it to a shell command, which 
allowed arbitrary shell command injection. This was fixed by 
[HADOOP-18136](https://issues.apache.org/jira/browse/HADOOP-18136) via 
`FileUtil.makeSecureShellPath` and shipped in Apache Hadoop 3.2.4, 3.3.3 or 
upper. Since Apache Spark requires Hadoop 3.4.0 or later (SPARK-58053) and the 
master branch uses Hadoop 3.5.0, the workaround is no lo [...]
    
    Removing it also has the following benefits.
    - `.tar` files keep the original file permissions and symlinks because the 
un-tarring is delegated to the native `tar` utility, which resolves the 
original TODO of SPARK-38632.
    - The `setAccessible(true)` reflection hack on a Hadoop private method is 
removed.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes, unpacking `.tar` archives now preserves file permissions and symlinks 
like `.tar.gz` and `.tgz` archives. This is the original behavior.
    
    ### How was this patch tested?
    
    Pass the CIs.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Fable 5
    
    Closes #57150 from dongjoon-hyun/dongjoon/commit-057c051285e-review-3aaa5c.
    
    Authored-by: Dongjoon Hyun <[email protected]>
    Signed-off-by: Dongjoon Hyun <[email protected]>
---
 .../main/scala/org/apache/spark/util/Utils.scala   | 29 ++--------------------
 1 file changed, 2 insertions(+), 27 deletions(-)

diff --git a/core/src/main/scala/org/apache/spark/util/Utils.scala 
b/core/src/main/scala/org/apache/spark/util/Utils.scala
index 39f9ca8a472c..c8030e5a4c7c 100644
--- a/core/src/main/scala/org/apache/spark/util/Utils.scala
+++ b/core/src/main/scala/org/apache/spark/util/Utils.scala
@@ -455,11 +455,9 @@ private[spark] object Utils
     } else if (lowerSrc.endsWith(".zip")) {
       // After issue HADOOP-18145, unzip could keep file permissions.
       FileUtil.unZip(source, dest)
-    } else if (lowerSrc.endsWith(".tar.gz") || lowerSrc.endsWith(".tgz")) {
+    } else if (
+      lowerSrc.endsWith(".tar.gz") || lowerSrc.endsWith(".tgz") || 
lowerSrc.endsWith(".tar")) {
       FileUtil.unTar(source, dest)
-    } else if (lowerSrc.endsWith(".tar")) {
-      // TODO(SPARK-38632): should keep file permissions. Java implementation 
doesn't.
-      unTarUsingJava(source, dest)
     } else {
       logWarning(log"Cannot unpack ${MDC(LogKeys.FILE_NAME, source)}, " +
         log"just copying it to ${MDC(FILE_NAME2, dest)}.")
@@ -467,29 +465,6 @@ private[spark] object Utils
     }
   }
 
-  /**
-   * The method below was copied from `FileUtil.unTar` but uses Java-based 
implementation
-   * to work around a security issue, see also SPARK-38631.
-   */
-  private def unTarUsingJava(source: File, dest: File): Unit = {
-    if (!Utils.createDirectory(dest) && !dest.isDirectory) {
-      throw new IOException(s"Mkdirs failed to create $dest")
-    } else {
-      try {
-        // Should not fail because all Hadoop 2.1+ (from HADOOP-9264)
-        // have 'unTarUsingJava'.
-        val mth = classOf[FileUtil].getDeclaredMethod(
-          "unTarUsingJava", classOf[File], classOf[File], classOf[Boolean])
-        mth.setAccessible(true)
-        mth.invoke(null, source, dest, java.lang.Boolean.FALSE)
-      } catch {
-        // Re-throw the original exception.
-        case e: java.lang.reflect.InvocationTargetException if e.getCause != 
null =>
-          throw e.getCause
-      }
-    }
-  }
-
   /** Records the duration of running `body`. */
   def timeTakenMs[T](body: => T): (T, Long) = {
     val startTime = System.nanoTime()


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to