Skip to content

Commit

Permalink
[Feature] Properly propagate exception in archive and throw ArchivePa…
Browse files Browse the repository at this point in the history
…sswordRequiredException.
  • Loading branch information
zhanghai committed Sep 19, 2023
1 parent 3433a74 commit 38fa9e1
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>
* All Rights Reserved.
*/

package me.zhanghai.android.files.provider.archive

import android.system.OsConstants
import java8.nio.file.FileSystemException
import java8.nio.file.Path
import me.zhanghai.android.files.provider.common.DelegateInputStream
import me.zhanghai.android.libarchive.ArchiveException
import java.io.IOException
import java.io.InputStream
import java.io.InterruptedIOException

// See also libarchive/archive_platform.h .
private const val ARCHIVE_ERRNO_MISC = -1

fun ArchiveException.toFileSystemOrInterruptedIOException(
file: String?,
other: String? = null
): IOException =
when {
// See also ReadArchive.toArchiveException .
code == OsConstants.EINTR -> InterruptedIOException(message)
// See also libarchive/archive_read_support_format_zip.c .
code == ARCHIVE_ERRNO_MISC && (
message == "Incorrect passphrase" || message == "Passphrase required for this entry"
) -> ArchivePasswordRequiredException(file, other, message)
else -> FileSystemException(file, other, message)
}.apply { initCause(this@toFileSystemOrInterruptedIOException) }

class ArchiveExceptionInputStream(
inputStream: InputStream,
private val file: Path
) : DelegateInputStream(inputStream) {
@Throws(IOException::class)
override fun read(): Int =
try {
super.read()
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
}

@Throws(IOException::class)
override fun read(b: ByteArray): Int =
try {
super.read(b)
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
}

@Throws(IOException::class)
override fun read(b: ByteArray, off: Int, len: Int): Int =
try {
super.read(b, off, len)
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
}

@Throws(IOException::class)
override fun skip(n: Long): Long = try {
super.skip(n)
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
}

@Throws(IOException::class)
override fun available(): Int =
try {
super.available()
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
}

@Throws(IOException::class)
override fun close() {
try {
super.close()
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
}
}

@Throws(IOException::class)
override fun reset() {
try {
super.reset()
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>
* All Rights Reserved.
*/

package me.zhanghai.android.files.provider.archive

import android.content.Intent
import me.zhanghai.android.files.provider.common.UserActionRequiredException

class ArchivePasswordRequiredException : UserActionRequiredException {
constructor(file: String?) : super(file)

constructor(file: String?, other: String?, reason: String?) : super(file, other, reason)

override fun getUserAction(): Intent = TODO()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import java8.nio.file.FileStore
import java8.nio.file.FileSystem
import java8.nio.file.NoSuchFileException
import java8.nio.file.NotDirectoryException
import java8.nio.file.NotLinkException
import java8.nio.file.Path
import java8.nio.file.PathMatcher
import java8.nio.file.WatchService
Expand All @@ -20,7 +21,9 @@ import me.zhanghai.android.files.provider.archive.archiver.ReadArchive
import me.zhanghai.android.files.provider.common.ByteString
import me.zhanghai.android.files.provider.common.ByteStringBuilder
import me.zhanghai.android.files.provider.common.ByteStringListPathCreator
import me.zhanghai.android.files.provider.common.IsDirectoryException
import me.zhanghai.android.files.provider.common.toByteString
import me.zhanghai.android.libarchive.ArchiveException
import java.io.IOException
import java.io.InputStream

Expand Down Expand Up @@ -56,7 +59,7 @@ internal class LocalArchiveFileSystem(
@Throws(IOException::class)
fun getEntry(path: Path): ReadArchive.Entry =
synchronized(lock) {
ensureEntriesLocked()
ensureEntriesLocked(path)
getEntryLocked(path)
}

Expand All @@ -69,15 +72,23 @@ internal class LocalArchiveFileSystem(
@Throws(IOException::class)
fun newInputStream(file: Path): InputStream =
synchronized(lock) {
ensureEntriesLocked()
ensureEntriesLocked(file)
val entry = getEntryLocked(file)
ArchiveReader.newInputStream(archiveFile, entry)
if (entry.isDirectory) {
throw IsDirectoryException(file.toString())
}
val inputStream = try {
ArchiveReader.newInputStream(archiveFile, entry)
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
} ?: throw NoSuchFileException(file.toString())
ArchiveExceptionInputStream(inputStream, file)
}

@Throws(IOException::class)
fun getDirectoryChildren(directory: Path): List<Path> =
synchronized(lock) {
ensureEntriesLocked()
ensureEntriesLocked(directory)
val entry = getEntryLocked(directory)
if (!entry.isDirectory) {
throw NotDirectoryException(directory.toString())
Expand All @@ -88,9 +99,12 @@ internal class LocalArchiveFileSystem(
@Throws(IOException::class)
fun readSymbolicLink(link: Path): String =
synchronized(lock) {
ensureEntriesLocked()
ensureEntriesLocked(link)
val entry = getEntryLocked(link)
ArchiveReader.readSymbolicLink(archiveFile, entry)
if (!entry.isSymbolicLink) {
throw NotLinkException(link.toString())
}
entry.symbolicLinkTarget ?: ""
}

fun refresh() {
Expand All @@ -103,12 +117,16 @@ internal class LocalArchiveFileSystem(
}

@Throws(IOException::class)
private fun ensureEntriesLocked() {
private fun ensureEntriesLocked(file: Path) {
if (!isOpen) {
throw ClosedFileSystemException()
}
if (isRefreshNeeded) {
val entriesAndTree = ArchiveReader.readEntries(archiveFile, rootDirectory)
val entriesAndTree = try {
ArchiveReader.readEntries(archiveFile, rootDirectory)
} catch (e: ArchiveException) {
throw e.toFileSystemOrInterruptedIOException(file.toString())
}
entries = entriesAndTree.first
tree = entriesAndTree.second
isRefreshNeeded = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@ package me.zhanghai.android.files.provider.archive.archiver
import androidx.preference.PreferenceManager
import java8.nio.channels.SeekableByteChannel
import java8.nio.charset.StandardCharsets
import java8.nio.file.NoSuchFileException
import java8.nio.file.NotLinkException
import java8.nio.file.Path
import me.zhanghai.android.files.R
import me.zhanghai.android.files.provider.common.DelegateForceableSeekableByteChannel
import me.zhanghai.android.files.provider.common.DelegateInputStream
import me.zhanghai.android.files.provider.common.DelegateNonForceableSeekableByteChannel
import me.zhanghai.android.files.provider.common.ForceableChannel
import me.zhanghai.android.files.provider.common.IsDirectoryException
import me.zhanghai.android.files.provider.common.PosixFileMode
import me.zhanghai.android.files.provider.common.PosixFileType
import me.zhanghai.android.files.provider.common.newByteChannel
Expand Down Expand Up @@ -104,10 +101,7 @@ object ArchiveReader {
}

@Throws(IOException::class)
fun newInputStream(file: Path, entry: ReadArchive.Entry): InputStream {
if (entry.isDirectory) {
throw IsDirectoryException(file.toString())
}
fun newInputStream(file: Path, entry: ReadArchive.Entry): InputStream? {
val charset = archiveFileNameCharset
val (archive, closeable) = openArchive(file)
var successful = false
Expand All @@ -123,7 +117,7 @@ object ArchiveReader {
if (successful) {
CloseableInputStream(archive.newDataInputStream(), closeable)
} else {
throw NoSuchFileException(file.toString())
null
}
} finally {
if (!successful) {
Expand All @@ -132,6 +126,7 @@ object ArchiveReader {
}
}

@Throws(IOException::class)
private fun openArchive(file: Path): Pair<ReadArchive, ArchiveCloseable> {
val channel = try {
CacheSizeSeekableByteChannel(file.newByteChannel())
Expand Down Expand Up @@ -232,12 +227,4 @@ object ArchiveReader {
closeable.close()
}
}

@Throws(IOException::class)
fun readSymbolicLink(file: Path, entry: ReadArchive.Entry): String {
if (entry.type != PosixFileType.SYMBOLIC_LINK) {
throw NotLinkException(file.toString())
}
return entry.symbolicLinkTarget ?: ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.threeten.bp.Instant
import java.io.Closeable
import java.io.IOException
import java.io.InputStream
import java.io.InterruptedIOException
import java.nio.ByteBuffer
import java.nio.charset.Charset

Expand All @@ -42,7 +43,7 @@ class ReadArchive : Closeable {
val bytesRead = try {
inputStream.read(buffer.array())
} catch (e: IOException) {
throw ArchiveException(Archive.ERRNO_FATAL, "InputStream.read", e)
throw e.toArchiveException("InputStream.read")
}
if (bytesRead != -1) {
buffer.limit(bytesRead)
Expand All @@ -55,7 +56,7 @@ class ReadArchive : Closeable {
try {
inputStream.skip(request)
} catch (e: IOException) {
throw ArchiveException(Archive.ERRNO_FATAL, "InputStream.skip", e)
throw e.toArchiveException("InputStream.skip")
}
}
Archive.readOpen1(archive)
Expand All @@ -81,7 +82,7 @@ class ReadArchive : Closeable {
val bytesRead = try {
channel.read(buffer)
} catch (e: IOException) {
throw ArchiveException(Archive.ERRNO_FATAL, "SeekableByteChannel.read", e)
throw e.toArchiveException("SeekableByteChannel.read")
}
if (bytesRead != -1) {
buffer.flip()
Expand All @@ -94,7 +95,7 @@ class ReadArchive : Closeable {
try {
channel.position(channel.position() + request)
} catch (e: IOException) {
throw ArchiveException(Archive.ERRNO_FATAL, "SeekableByteChannel.position", e)
throw e.toArchiveException("SeekableByteChannel.position")
}
request
}
Expand All @@ -112,7 +113,7 @@ class ReadArchive : Closeable {
}
channel.position(newPosition)
} catch (e: IOException) {
throw ArchiveException(Archive.ERRNO_FATAL, "SeekableByteChannel.position", e)
throw e.toArchiveException("SeekableByteChannel.position")
}
newPosition
}
Expand All @@ -125,6 +126,12 @@ class ReadArchive : Closeable {
}
}

private fun IOException.toArchiveException(message: String): ArchiveException =
when (this) {
is InterruptedIOException -> ArchiveException(OsConstants.EINTR, message, this)
else -> ArchiveException(Archive.ERRNO_FATAL, message, this)
}

@Throws(ArchiveException::class)
fun readEntry(charset: Charset): Entry? {
val entry = Archive.readNextHeader(archive)
Expand Down Expand Up @@ -186,15 +193,6 @@ class ReadArchive : Closeable {
private fun getEntryString(stringUtf8: String?, string: ByteArray?, charset: Charset): String? =
stringUtf8 ?: string?.toString(charset)

fun hasEncryptedEntries(): Boolean? {
val hasEncryptedEntries = Archive.readHasEncryptedEntries(archive)
return when {
hasEncryptedEntries > 0 -> true
hasEncryptedEntries == 0 -> false
else -> null
}
}

@Throws(ArchiveException::class)
fun newDataInputStream(): InputStream = DataInputStream()

Expand Down Expand Up @@ -223,6 +221,9 @@ class ReadArchive : Closeable {
) {
val isDirectory: Boolean
get() = type == PosixFileType.DIRECTORY

val isSymbolicLink: Boolean
get() = type == PosixFileType.SYMBOLIC_LINK
}

private inner class DataInputStream : InputStream() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>
* All Rights Reserved.
*/

package me.zhanghai.android.files.provider.common

import android.content.Intent
import java8.nio.file.FileSystemException

abstract class UserActionRequiredException : FileSystemException {
constructor(file: String?) : super(file)

constructor(file: String?, other: String?, reason: String?) : super(file, other, reason)

abstract fun getUserAction(): Intent
}

0 comments on commit 38fa9e1

Please sign in to comment.