Android执行tar解压文件时部分设备报gunzip Permission denied如何解决
报错原因
- 你当前解压的是gzip压缩后的tar包(通常后缀为
.tar.gz或.tgz),调用系统tar命令使用-xvf参数时,部分版本的tar会自动识别gzip压缩格式,自动fork子进程调用系统内置的gunzip工具完成预解压,报错的本质是你的应用没有执行系统gunzip二进制文件的权限。 - 该问题仅在少数设备出现,是因为这些设备的厂商定制ROM时修改了系统二进制文件的权限配置,将
/system/bin/gunzip的执行权限限制到了特定用户组,普通第三方应用的UID不在允许执行的范围内,所以触发权限拒绝。
可行解决方案
优先推荐方案2,完全不依赖系统工具,不存在版本适配问题。
方案1:拆分解压流程,规避tar自动调用gunzip
先在代码层用内置API完成gzip解压,再调用tar命令解压普通tar包,避免tar内部调用系统gunzip触发权限限制。
示例逻辑:
// 先处理gzip解压生成临时普通tar文件 val gzipIs = GZIPInputStream(FileInputStream(file)) val tempTarFile = File(tempDirectory, "temp_plain.tar") FileOutputStream(tempTarFile).use { out -> gzipIs.copyTo(out) } // 再调用tar命令解压无压缩的普通tar包 val untar = arrayOf("tar", "-xvf", tempTarFile.absolutePath, "-C", tempDirectory.absolutePath) val process = Runtime.getRuntime().exec(untar) process.waitFor() // 执行完成后删除临时文件 tempTarFile.delete()
方案2:改用纯Java/Kotlin实现的解压库,完全规避系统工具调用
使用Apache Commons Compress库实现全内置解压,不需要调用任何系统二进制文件,兼容性最高。
首先引入依赖(build.gradle.kts):
dependencies { implementation("org.apache.commons:commons-compress:1.24.0") }
解压逻辑示例:
fun untarGz(sourceFile: File, targetDir: File) { targetDir.mkdirs() GZIPInputStream(FileInputStream(sourceFile)).use { gzInputStream -> TarArchiveInputStream(gzInputStream).use { tarInputStream -> var entry: TarArchiveEntry? = tarInputStream.nextTarEntry while (entry != null) { val outputFile = File(targetDir, entry.name) if (entry.isDirectory) { outputFile.mkdirs() } else { outputFile.parentFile?.mkdirs() FileOutputStream(outputFile).use { outStream -> tarInputStream.copyTo(outStream) } // 可选:保留原文件的权限配置 outputFile.setExecutable(entry.mode and 0b111 != 0) outputFile.setWritable(entry.mode and 0b110 != 0) outputFile.setReadable(entry.mode and 0b100 != 0) } entry = tarInputStream.nextTarEntry } } } }
方案3:内置自定义二进制工具到应用私有目录
将编译好的对应ABI架构的tar、gunzip二进制文件放到项目assets目录,运行时拷贝到应用私有目录(/data/data/[你的包名]/files路径下),赋予执行权限后再调用,完全绕过系统二进制的权限限制。
注:该方案需要适配所有目标ABI架构(armeabi-v7a、arm64-v8a、x86等),会少量增加应用包体积。
内容的提问来源于stack exchange,提问作者M Dissi
相关产品推荐
相关产品推荐

