From 7882c213206b62caf23491a34b9346e45b68c6c6 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Tue, 27 Feb 2024 23:30:58 -0800 Subject: [PATCH] Feat: Remove root archive file system provider Now that ArchiveReader supports SeekableByteChannel. Fixes: #1140 --- .../archive/ArchiveFileAttributeView.kt | 67 ++-- .../provider/archive/ArchiveFileStore.kt | 50 +-- .../provider/archive/ArchiveFileSystem.kt | 226 +++++++++++--- .../archive/ArchiveFileSystemProvider.kt | 281 ++++++++++++++++- .../archive/LocalArchiveFileAttributeView.kt | 60 ---- .../provider/archive/LocalArchiveFileStore.kt | 42 --- .../archive/LocalArchiveFileSystem.kt | 233 -------------- .../archive/LocalArchiveFileSystemProvider.kt | 290 ------------------ .../archive/RootArchiveFileAttributeView.kt | 23 -- .../provider/archive/RootArchiveFileSystem.kt | 57 ---- .../archive/RootArchiveFileSystemProvider.kt | 51 --- 11 files changed, 529 insertions(+), 851 deletions(-) delete mode 100644 app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileAttributeView.kt delete mode 100644 app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileStore.kt delete mode 100644 app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileSystem.kt delete mode 100644 app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileSystemProvider.kt delete mode 100644 app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileAttributeView.kt delete mode 100644 app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileSystem.kt delete mode 100644 app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileSystemProvider.kt diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileAttributeView.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileAttributeView.kt index d9566a24a..683146881 100644 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileAttributeView.kt +++ b/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileAttributeView.kt @@ -1,37 +1,60 @@ /* - * Copyright (c) 2018 Hai Zhang + * Copyright (c) 2019 Hai Zhang * All Rights Reserved. */ package me.zhanghai.android.files.provider.archive -import android.os.Parcel -import android.os.Parcelable -import me.zhanghai.android.files.provider.root.RootablePosixFileAttributeView -import me.zhanghai.android.files.util.readParcelable +import java8.nio.file.Path +import java8.nio.file.attribute.FileTime +import me.zhanghai.android.files.provider.common.ByteString +import me.zhanghai.android.files.provider.common.PosixFileAttributeView +import me.zhanghai.android.files.provider.common.PosixFileModeBit +import me.zhanghai.android.files.provider.common.PosixGroup +import me.zhanghai.android.files.provider.common.PosixUser +import java.io.IOException + +internal class ArchiveFileAttributeView(private val path: Path) : PosixFileAttributeView { + override fun name(): String = NAME + + @Throws(IOException::class) + override fun readAttributes(): ArchiveFileAttributes { + val fileSystem = path.fileSystem as ArchiveFileSystem + val entry = fileSystem.getEntry(path) + return ArchiveFileAttributes.from(fileSystem.archiveFile, entry) + } -internal class ArchiveFileAttributeView( - private val path: ArchivePath -) : RootablePosixFileAttributeView( - path, LocalArchiveFileAttributeView(path), { RootArchiveFileAttributeView(it, path) } -) { - private constructor(source: Parcel) : this(source.readParcelable()!!) + override fun setTimes( + lastModifiedTime: FileTime?, + lastAccessTime: FileTime?, + createTime: FileTime? + ) { + throw UnsupportedOperationException() + } - override fun describeContents(): Int = 0 + override fun setOwner(owner: PosixUser) { + throw UnsupportedOperationException() + } - override fun writeToParcel(dest: Parcel, flags: Int) { - dest.writeParcelable(path as Parcelable, flags) + override fun setGroup(group: PosixGroup) { + throw UnsupportedOperationException() } - companion object { - val SUPPORTED_NAMES = LocalArchiveFileAttributeView.SUPPORTED_NAMES + override fun setMode(mode: Set) { + throw UnsupportedOperationException() + } - @JvmField - val CREATOR = object : Parcelable.Creator { - override fun createFromParcel(source: Parcel): ArchiveFileAttributeView = - ArchiveFileAttributeView(source) + override fun setSeLinuxContext(context: ByteString) { + throw UnsupportedOperationException() + } + + override fun restoreSeLinuxContext() { + throw UnsupportedOperationException() + } + + companion object { + private val NAME = ArchiveFileSystemProvider.scheme - override fun newArray(size: Int): Array = arrayOfNulls(size) - } + val SUPPORTED_NAMES = setOf("basic", "posix", NAME) } } diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileStore.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileStore.kt index a7ca58b47..71a8fb0e2 100644 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileStore.kt +++ b/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileStore.kt @@ -1,36 +1,42 @@ /* - * Copyright (c) 2018 Hai Zhang + * Copyright (c) 2019 Hai Zhang * All Rights Reserved. */ package me.zhanghai.android.files.provider.archive -import android.os.Parcel -import android.os.Parcelable import java8.nio.file.Path -import me.zhanghai.android.files.provider.root.RootPosixFileStore -import me.zhanghai.android.files.provider.root.RootablePosixFileStore +import java8.nio.file.attribute.FileAttributeView +import me.zhanghai.android.files.file.MimeType +import me.zhanghai.android.files.file.guessFromPath +import me.zhanghai.android.files.provider.common.PosixFileStore +import me.zhanghai.android.files.provider.common.size +import java.io.IOException -internal class ArchiveFileStore(private val archiveFile: Path) : RootablePosixFileStore( - archiveFile, LocalArchiveFileStore(archiveFile), { RootPosixFileStore(it) } -) { - private constructor(source: Parcel) : this( - source.readParcelable(Path::class.java.classLoader) as Path - ) +internal class ArchiveFileStore(private val archiveFile: Path) : PosixFileStore() { + override fun refresh() {} - override fun describeContents(): Int = 0 + override fun name(): String = archiveFile.toString() - override fun writeToParcel(dest: Parcel, flags: Int) { - dest.writeParcelable(archiveFile as Parcelable, flags) - } + override fun type(): String = MimeType.guessFromPath(archiveFile.toString()).value - companion object { - @JvmField - val CREATOR = object : Parcelable.Creator { - override fun createFromParcel(source: Parcel): ArchiveFileStore = - ArchiveFileStore(source) + override fun isReadOnly(): Boolean = true - override fun newArray(size: Int): Array = arrayOfNulls(size) - } + @Throws(IOException::class) + override fun setReadOnly(readOnly: Boolean) { + throw UnsupportedOperationException() } + + @Throws(IOException::class) + override fun getTotalSpace(): Long = archiveFile.size() + + override fun getUsableSpace(): Long = 0 + + override fun getUnallocatedSpace(): Long = 0 + + override fun supportsFileAttributeView(type: Class): Boolean = + ArchiveFileSystemProvider.supportsFileAttributeView(type) + + override fun supportsFileAttributeView(name: String): Boolean = + name in ArchiveFileAttributeView.SUPPORTED_NAMES } diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystem.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystem.kt index dcbc4953d..ef4b0dc22 100644 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystem.kt +++ b/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystem.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Hai Zhang + * Copyright (c) 2019 Hai Zhang * All Rights Reserved. */ @@ -7,72 +7,224 @@ package me.zhanghai.android.files.provider.archive import android.os.Parcel import android.os.Parcelable +import java8.nio.file.ClosedFileSystemException +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 +import java8.nio.file.attribute.UserPrincipalLookupService +import java8.nio.file.spi.FileSystemProvider +import me.zhanghai.android.files.provider.archive.archiver.ArchiveReader 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.remote.RemoteFileSystemException -import me.zhanghai.android.files.provider.root.RootableFileSystem +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 internal class ArchiveFileSystem( - provider: ArchiveFileSystemProvider, - archiveFile: Path -) : RootableFileSystem( - { LocalArchiveFileSystem(it as ArchiveFileSystem, provider, archiveFile) }, - { RootArchiveFileSystem(it) } -), ByteStringListPathCreator { - override val localFileSystem: LocalArchiveFileSystem - get() = super.localFileSystem as LocalArchiveFileSystem - - override val rootFileSystem: RootArchiveFileSystem - get() = super.rootFileSystem as RootArchiveFileSystem + private val provider: ArchiveFileSystemProvider, + val archiveFile: Path +) : FileSystem(), ByteStringListPathCreator, Parcelable { + val rootDirectory = ArchivePath(this, SEPARATOR_BYTE_STRING) - val rootDirectory: ArchivePath - get() = localFileSystem.rootDirectory + init { + if (!rootDirectory.isAbsolute) { + throw AssertionError("Root directory $rootDirectory must be absolute") + } + if (rootDirectory.nameCount != 0) { + throw AssertionError("Root directory $rootDirectory must contain no names") + } + } val defaultDirectory: ArchivePath - get() = localFileSystem.defaultDirectory + get() = rootDirectory - val archiveFile: Path - get() = localFileSystem.archiveFile + private val lock = Any() + + private var isOpen = true + + private var passwords = listOf() + + private var isRefreshNeeded = true + + private var entries: Map? = null + + private var tree: Map>? = null @Throws(IOException::class) - fun getEntryAsLocal(path: Path): ReadArchive.Entry = localFileSystem.getEntry(path) + fun getEntry(path: Path): ReadArchive.Entry = + synchronized(lock) { + ensureEntriesLocked(path) + getEntryLocked(path) + } @Throws(IOException::class) - fun newInputStreamAsLocal(file: Path): InputStream = localFileSystem.newInputStream(file) + private fun getEntryLocked(path: Path): ReadArchive.Entry = + synchronized(lock) { + entries!![path] ?: throw NoSuchFileException(path.toString()) + } @Throws(IOException::class) - fun getDirectoryChildrenAsLocal(directory: Path): List = - localFileSystem.getDirectoryChildren(directory) + fun newInputStream(file: Path): InputStream = + synchronized(lock) { + ensureEntriesLocked(file) + val entry = getEntryLocked(file) + if (entry.isDirectory) { + throw IsDirectoryException(file.toString()) + } + val inputStream = try { + ArchiveReader.newInputStream(archiveFile, passwords, entry) + } catch (e: ArchiveException) { + throw e.toFileSystemOrInterruptedIOException(file) + } ?: throw NoSuchFileException(file.toString()) + ArchiveExceptionInputStream(inputStream, file) + } @Throws(IOException::class) - fun readSymbolicLinkAsLocal(link: Path): String = localFileSystem.readSymbolicLink(link) + fun getDirectoryChildren(directory: Path): List = + synchronized(lock) { + ensureEntriesLocked(directory) + val entry = getEntryLocked(directory) + if (!entry.isDirectory) { + throw NotDirectoryException(directory.toString()) + } + tree!![directory]!! + } + + @Throws(IOException::class) + fun readSymbolicLink(link: Path): String = + synchronized(lock) { + ensureEntriesLocked(link) + val entry = getEntryLocked(link) + if (!entry.isSymbolicLink) { + throw NotLinkException(link.toString()) + } + entry.symbolicLinkTarget.orEmpty() + } fun addPassword(password: String) { - localFileSystem.addPassword(password) - rootFileSystem.addPassword(password) + synchronized(lock) { + if (!isOpen) { + throw ClosedFileSystemException() + } + passwords += password + } } fun setPasswords(passwords: List) { - localFileSystem.setPasswords(passwords) - rootFileSystem.setPasswords(passwords) + synchronized(lock) { + if (!isOpen) { + throw ClosedFileSystemException() + } + this.passwords = passwords + } } fun refresh() { - localFileSystem.refresh() - rootFileSystem.refresh() + synchronized(lock) { + if (!isOpen) { + throw ClosedFileSystemException() + } + isRefreshNeeded = true + } + } + + @Throws(IOException::class) + private fun ensureEntriesLocked(file: Path) { + if (!isOpen) { + throw ClosedFileSystemException() + } + if (isRefreshNeeded) { + val entriesAndTree = try { + ArchiveReader.readEntries(archiveFile, passwords, rootDirectory) + } catch (e: ArchiveException) { + throw e.toFileSystemOrInterruptedIOException(file) + } + entries = entriesAndTree.first + tree = entriesAndTree.second + isRefreshNeeded = false + } + } + + override fun provider(): FileSystemProvider = provider + + override fun close() { + synchronized(lock) { + if (!isOpen) { + return + } + provider.removeFileSystem(this) + isRefreshNeeded = false + entries = null + tree = null + isOpen = false + } + } + + override fun isOpen(): Boolean = synchronized(lock) { isOpen } + + override fun isReadOnly(): Boolean = true + + override fun getSeparator(): String = SEPARATOR_STRING + + override fun getRootDirectories(): Iterable = listOf(rootDirectory) + + override fun getFileStores(): Iterable { + // TODO + throw UnsupportedOperationException() } - @Throws(RemoteFileSystemException::class) - fun prepareAsRoot() { - rootFileSystem.prepare() + override fun supportedFileAttributeViews(): Set = + ArchiveFileAttributeView.SUPPORTED_NAMES + + override fun getPath(first: String, vararg more: String): ArchivePath { + val path = ByteStringBuilder(first.toByteString()) + .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } } + .toByteString() + return ArchivePath(this, path) + } + + override fun getPath(first: ByteString, vararg more: ByteString): ArchivePath { + val path = ByteStringBuilder(first) + .apply { more.forEach { append(SEPARATOR).append(it) } } + .toByteString() + return ArchivePath(this, path) + } + + override fun getPathMatcher(syntaxAndPattern: String): PathMatcher { + throw UnsupportedOperationException() + } + + override fun getUserPrincipalLookupService(): UserPrincipalLookupService { + throw UnsupportedOperationException() + } + + @Throws(IOException::class) + override fun newWatchService(): WatchService { + // TODO + throw UnsupportedOperationException() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (javaClass != other?.javaClass) { + return false + } + other as ArchiveFileSystem + return archiveFile == other.archiveFile } - override fun getPath(first: ByteString, vararg more: ByteString): ArchivePath = - localFileSystem.getPath(first, *more) + override fun hashCode(): Int = archiveFile.hashCode() override fun describeContents(): Int = 0 @@ -81,7 +233,9 @@ internal class ArchiveFileSystem( } companion object { - const val SEPARATOR: Byte = LocalArchiveFileSystem.SEPARATOR + const val SEPARATOR = '/'.code.toByte() + private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString() + private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString() @JvmField val CREATOR = object : Parcelable.Creator { diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystemProvider.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystemProvider.kt index dba8e6d0b..79f2ace2d 100644 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystemProvider.kt +++ b/app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystemProvider.kt @@ -1,35 +1,286 @@ /* - * Copyright (c) 2018 Hai Zhang + * Copyright (c) 2019 Hai Zhang * All Rights Reserved. */ package me.zhanghai.android.files.provider.archive +import java8.nio.channels.FileChannel +import java8.nio.channels.SeekableByteChannel +import java8.nio.file.AccessDeniedException +import java8.nio.file.AccessMode +import java8.nio.file.CopyOption +import java8.nio.file.DirectoryStream +import java8.nio.file.FileStore +import java8.nio.file.FileSystem +import java8.nio.file.LinkOption +import java8.nio.file.OpenOption import java8.nio.file.Path +import java8.nio.file.Paths +import java8.nio.file.ProviderMismatchException +import java8.nio.file.attribute.BasicFileAttributes +import java8.nio.file.attribute.FileAttribute import java8.nio.file.attribute.FileAttributeView -import me.zhanghai.android.files.provider.root.RootableFileSystemProvider +import java8.nio.file.spi.FileSystemProvider +import me.zhanghai.android.files.provider.common.ByteStringPath +import me.zhanghai.android.files.provider.common.FileSystemCache +import me.zhanghai.android.files.provider.common.PathListDirectoryStream +import me.zhanghai.android.files.provider.common.ReadOnlyFileSystemException +import me.zhanghai.android.files.provider.common.Searchable +import me.zhanghai.android.files.provider.common.WalkFileTreeSearchable +import me.zhanghai.android.files.provider.common.decodedPathByteString +import me.zhanghai.android.files.provider.common.decodedQueryByteString +import me.zhanghai.android.files.provider.common.isSameFile +import me.zhanghai.android.files.provider.common.toAccessModes +import me.zhanghai.android.files.provider.common.toByteString +import me.zhanghai.android.files.provider.common.toOpenOptions +import java.io.IOException +import java.io.InputStream +import java.net.URI -object ArchiveFileSystemProvider : RootableFileSystemProvider( - { LocalArchiveFileSystemProvider(it as ArchiveFileSystemProvider) }, - { RootArchiveFileSystemProvider(LocalArchiveFileSystemProvider.SCHEME) } -) { - override val localProvider: LocalArchiveFileSystemProvider - get() = super.localProvider as LocalArchiveFileSystemProvider +object ArchiveFileSystemProvider : FileSystemProvider(), Searchable { + private const val SCHEME = "archive" - override val rootProvider: RootArchiveFileSystemProvider - get() = super.rootProvider as RootArchiveFileSystemProvider + private val fileSystems = FileSystemCache() + + override fun getScheme(): String = SCHEME + + override fun newFileSystem(uri: URI, env: Map): FileSystem { + uri.requireSameScheme() + val archiveFile = uri.archiveFile + return fileSystems.create(archiveFile) { newFileSystem(archiveFile) } + } + + override fun newFileSystem(file: Path, env: Map): FileSystem = newFileSystem(file) internal fun getOrNewFileSystem(archiveFile: Path): ArchiveFileSystem = - localProvider.getOrNewFileSystem(archiveFile) + fileSystems.getOrCreate(archiveFile) { newFileSystem(archiveFile) } + + private fun newFileSystem(archiveFile: Path): ArchiveFileSystem = + ArchiveFileSystem(this, archiveFile) + + override fun getFileSystem(uri: URI): FileSystem { + uri.requireSameScheme() + val archiveFile = uri.archiveFile + return fileSystems[archiveFile] + } internal fun removeFileSystem(fileSystem: ArchiveFileSystem) { - localProvider.removeFileSystem(fileSystem) + fileSystems.remove(fileSystem.archiveFile, fileSystem) + } + + override fun getPath(uri: URI): Path { + uri.requireSameScheme() + val archiveFile = uri.archiveFile + val path = uri.decodedQueryByteString + ?: throw IllegalArgumentException("URI must have a query") + return getOrNewFileSystem(archiveFile).getPath(path) + } + + private fun URI.requireSameScheme() { + val scheme = scheme + require(scheme == SCHEME) { "URI scheme $scheme must be $SCHEME" } + } + + private val URI.archiveFile: Path + get() { + val path = decodedPathByteString + ?: throw IllegalArgumentException("URI must have a path") + // Drop the first character which is always a slash. + val archiveUri = URI.create(path.toString().drop(1)) + return Paths.get(archiveUri) + } + + @Throws(IOException::class) + override fun newInputStream(file: Path, vararg options: OpenOption): InputStream { + file as? ArchivePath ?: throw ProviderMismatchException(file.toString()) + options.toOpenOptions().checkForArchive() + return file.fileSystem.newInputStream(file) + } + + override fun newFileChannel( + file: Path, + options: Set, + vararg attributes: FileAttribute<*> + ): FileChannel { + file as? ArchivePath ?: throw ProviderMismatchException(file.toString()) + options.toOpenOptions().checkForArchive() + if (attributes.isNotEmpty()) { + throw UnsupportedOperationException(attributes.contentToString()) + } + throw UnsupportedOperationException() + } + + override fun newByteChannel( + file: Path, + options: Set, + vararg attributes: FileAttribute<*> + ): SeekableByteChannel { + file as? ArchivePath ?: throw ProviderMismatchException(file.toString()) + options.toOpenOptions().checkForArchive() + if (attributes.isNotEmpty()) { + throw UnsupportedOperationException(attributes.contentToString()) + } + throw UnsupportedOperationException() + } + + @Throws(IOException::class) + override fun newDirectoryStream( + directory: Path, + filter: DirectoryStream.Filter + ): DirectoryStream { + directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString()) + val children = directory.fileSystem.getDirectoryChildren(directory) + return PathListDirectoryStream(children, filter) + } + + @Throws(IOException::class) + override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) { + directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString()) + throw ReadOnlyFileSystemException(directory.toString()) + } + + @Throws(IOException::class) + override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) { + link as? ArchivePath ?: throw ProviderMismatchException(link.toString()) + when (target) { + is ArchivePath, is ByteStringPath -> {} + else -> throw ProviderMismatchException(target.toString()) + } + throw ReadOnlyFileSystemException(link.toString(), target.toString(), null) + } + + @Throws(IOException::class) + override fun createLink(link: Path, existing: Path) { + link as? ArchivePath ?: throw ProviderMismatchException(link.toString()) + existing as? ArchivePath ?: throw ProviderMismatchException(existing.toString()) + throw ReadOnlyFileSystemException(link.toString(), existing.toString(), null) + } + + @Throws(IOException::class) + override fun delete(path: Path) { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + throw ReadOnlyFileSystemException(path.toString()) + } + + @Throws(IOException::class) + override fun readSymbolicLink(link: Path): Path { + link as? ArchivePath ?: throw ProviderMismatchException(link.toString()) + val target = link.fileSystem.readSymbolicLink(link) + return ByteStringPath(target.toByteString()) + } + + @Throws(IOException::class) + override fun copy(source: Path, target: Path, vararg options: CopyOption) { + source as? ArchivePath ?: throw ProviderMismatchException(source.toString()) + target as? ArchivePath ?: throw ProviderMismatchException(target.toString()) + throw ReadOnlyFileSystemException(source.toString(), target.toString(), null) + } + + @Throws(IOException::class) + override fun move(source: Path, target: Path, vararg options: CopyOption) { + source as? ArchivePath ?: throw ProviderMismatchException(source.toString()) + target as? ArchivePath ?: throw ProviderMismatchException(target.toString()) + throw ReadOnlyFileSystemException(source.toString(), target.toString(), null) + } + + @Throws(IOException::class) + override fun isSameFile(path: Path, path2: Path): Boolean { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + if (path == path2) { + return true + } + if (path2 !is ArchivePath) { + return false + } + val fileSystem = path.fileSystem + if (!fileSystem.archiveFile.isSameFile(path2.fileSystem.archiveFile)) { + return false + } + return path == fileSystem.getPath(path2.toString()) + } + + override fun isHidden(path: Path): Boolean { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + return false + } + + override fun getFileStore(path: Path): FileStore { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + val archiveFile = path.fileSystem.archiveFile + return ArchiveFileStore(archiveFile) + } + + @Throws(IOException::class) + override fun checkAccess(path: Path, vararg modes: AccessMode) { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + val accessModes = modes.toAccessModes() + path.fileSystem.getEntry(path) + if (accessModes.write || accessModes.execute) { + throw AccessDeniedException(path.toString()) + } + } + + override fun getFileAttributeView( + path: Path, + type: Class, + vararg options: LinkOption + ): V? { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + if (!supportsFileAttributeView(type)) { + return null + } + @Suppress("UNCHECKED_CAST") + return getFileAttributeView(path) as V } internal fun supportsFileAttributeView(type: Class): Boolean = - LocalArchiveFileSystemProvider.supportsFileAttributeView(type) + type.isAssignableFrom(ArchiveFileAttributeView::class.java) + + @Throws(IOException::class) + override fun readAttributes( + path: Path, + type: Class, + vararg options: LinkOption + ): A { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + if (!type.isAssignableFrom(ArchiveFileAttributes::class.java)) { + throw UnsupportedOperationException(type.toString()) + } + @Suppress("UNCHECKED_CAST") + return getFileAttributeView(path).readAttributes() as A + } + + private fun getFileAttributeView(path: ArchivePath): ArchiveFileAttributeView = + ArchiveFileAttributeView(path) + + override fun readAttributes( + path: Path, + attributes: String, + vararg options: LinkOption + ): Map { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + throw UnsupportedOperationException() + } + + override fun setAttribute( + path: Path, + attribute: String, + value: Any, + vararg options: LinkOption + ) { + path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) + throw UnsupportedOperationException() + } - internal fun prepareFileSystem(path: Path) { - rootProvider.prepareFileSystem(path) + @Throws(IOException::class) + override fun search( + directory: Path, + query: String, + intervalMillis: Long, + listener: (List) -> Unit + ) { + directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString()) + WalkFileTreeSearchable.search(directory, query, intervalMillis, listener) } } diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileAttributeView.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileAttributeView.kt deleted file mode 100644 index 534735925..000000000 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileAttributeView.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.provider.archive - -import java8.nio.file.Path -import java8.nio.file.attribute.FileTime -import me.zhanghai.android.files.provider.common.ByteString -import me.zhanghai.android.files.provider.common.PosixFileAttributeView -import me.zhanghai.android.files.provider.common.PosixFileModeBit -import me.zhanghai.android.files.provider.common.PosixGroup -import me.zhanghai.android.files.provider.common.PosixUser -import java.io.IOException - -internal class LocalArchiveFileAttributeView(private val path: Path) : PosixFileAttributeView { - override fun name(): String = NAME - - @Throws(IOException::class) - override fun readAttributes(): ArchiveFileAttributes { - val fileSystem = path.fileSystem as ArchiveFileSystem - val entry = fileSystem.getEntryAsLocal(path) - return ArchiveFileAttributes.from(fileSystem.archiveFile, entry) - } - - override fun setTimes( - lastModifiedTime: FileTime?, - lastAccessTime: FileTime?, - createTime: FileTime? - ) { - throw UnsupportedOperationException() - } - - override fun setOwner(owner: PosixUser) { - throw UnsupportedOperationException() - } - - override fun setGroup(group: PosixGroup) { - throw UnsupportedOperationException() - } - - override fun setMode(mode: Set) { - throw UnsupportedOperationException() - } - - override fun setSeLinuxContext(context: ByteString) { - throw UnsupportedOperationException() - } - - override fun restoreSeLinuxContext() { - throw UnsupportedOperationException() - } - - companion object { - private val NAME = ArchiveFileSystemProvider.scheme - - val SUPPORTED_NAMES = setOf("basic", "posix", NAME) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileStore.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileStore.kt deleted file mode 100644 index 85ee12fa2..000000000 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileStore.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.provider.archive - -import java8.nio.file.Path -import java8.nio.file.attribute.FileAttributeView -import me.zhanghai.android.files.file.MimeType -import me.zhanghai.android.files.file.guessFromPath -import me.zhanghai.android.files.provider.common.PosixFileStore -import me.zhanghai.android.files.provider.common.size -import java.io.IOException - -internal class LocalArchiveFileStore(private val archiveFile: Path) : PosixFileStore() { - override fun refresh() {} - - override fun name(): String = archiveFile.toString() - - override fun type(): String = MimeType.guessFromPath(archiveFile.toString()).value - - override fun isReadOnly(): Boolean = true - - @Throws(IOException::class) - override fun setReadOnly(readOnly: Boolean) { - throw UnsupportedOperationException() - } - - @Throws(IOException::class) - override fun getTotalSpace(): Long = archiveFile.size() - - override fun getUsableSpace(): Long = 0 - - override fun getUnallocatedSpace(): Long = 0 - - override fun supportsFileAttributeView(type: Class): Boolean = - ArchiveFileSystemProvider.supportsFileAttributeView(type) - - override fun supportsFileAttributeView(name: String): Boolean = - name in ArchiveFileAttributeView.SUPPORTED_NAMES -} diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileSystem.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileSystem.kt deleted file mode 100644 index 14cc12597..000000000 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileSystem.kt +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.provider.archive - -import java8.nio.file.ClosedFileSystemException -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 -import java8.nio.file.attribute.UserPrincipalLookupService -import java8.nio.file.spi.FileSystemProvider -import me.zhanghai.android.files.provider.archive.archiver.ArchiveReader -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 - -internal class LocalArchiveFileSystem( - private val fileSystem: ArchiveFileSystem, - private val provider: ArchiveFileSystemProvider, - val archiveFile: Path -) : FileSystem(), ByteStringListPathCreator { - val rootDirectory = ArchivePath(fileSystem, SEPARATOR_BYTE_STRING) - - init { - if (!rootDirectory.isAbsolute) { - throw AssertionError("Root directory $rootDirectory must be absolute") - } - if (rootDirectory.nameCount != 0) { - throw AssertionError("Root directory $rootDirectory must contain no names") - } - } - - val defaultDirectory: ArchivePath - get() = rootDirectory - - private val lock = Any() - - private var isOpen = true - - private var passwords = listOf() - - private var isRefreshNeeded = true - - private var entries: Map? = null - - private var tree: Map>? = null - - @Throws(IOException::class) - fun getEntry(path: Path): ReadArchive.Entry = - synchronized(lock) { - ensureEntriesLocked(path) - getEntryLocked(path) - } - - @Throws(IOException::class) - private fun getEntryLocked(path: Path): ReadArchive.Entry = - synchronized(lock) { - entries!![path] ?: throw NoSuchFileException(path.toString()) - } - - @Throws(IOException::class) - fun newInputStream(file: Path): InputStream = - synchronized(lock) { - ensureEntriesLocked(file) - val entry = getEntryLocked(file) - if (entry.isDirectory) { - throw IsDirectoryException(file.toString()) - } - val inputStream = try { - ArchiveReader.newInputStream(archiveFile, passwords, entry) - } catch (e: ArchiveException) { - throw e.toFileSystemOrInterruptedIOException(file) - } ?: throw NoSuchFileException(file.toString()) - ArchiveExceptionInputStream(inputStream, file) - } - - @Throws(IOException::class) - fun getDirectoryChildren(directory: Path): List = - synchronized(lock) { - ensureEntriesLocked(directory) - val entry = getEntryLocked(directory) - if (!entry.isDirectory) { - throw NotDirectoryException(directory.toString()) - } - tree!![directory]!! - } - - @Throws(IOException::class) - fun readSymbolicLink(link: Path): String = - synchronized(lock) { - ensureEntriesLocked(link) - val entry = getEntryLocked(link) - if (!entry.isSymbolicLink) { - throw NotLinkException(link.toString()) - } - entry.symbolicLinkTarget.orEmpty() - } - - fun addPassword(password: String) { - synchronized(lock) { - if (!isOpen) { - throw ClosedFileSystemException() - } - passwords += password - } - } - - fun setPasswords(passwords: List) { - synchronized(lock) { - if (!isOpen) { - throw ClosedFileSystemException() - } - this.passwords = passwords - } - } - - fun refresh() { - synchronized(lock) { - if (!isOpen) { - throw ClosedFileSystemException() - } - isRefreshNeeded = true - } - } - - @Throws(IOException::class) - private fun ensureEntriesLocked(file: Path) { - if (!isOpen) { - throw ClosedFileSystemException() - } - if (isRefreshNeeded) { - val entriesAndTree = try { - ArchiveReader.readEntries(archiveFile, passwords, rootDirectory) - } catch (e: ArchiveException) { - throw e.toFileSystemOrInterruptedIOException(file) - } - entries = entriesAndTree.first - tree = entriesAndTree.second - isRefreshNeeded = false - } - } - - override fun provider(): FileSystemProvider = provider - - override fun close() { - synchronized(lock) { - if (!isOpen) { - return - } - provider.removeFileSystem(fileSystem) - isRefreshNeeded = false - entries = null - tree = null - isOpen = false - } - } - - override fun isOpen(): Boolean = synchronized(lock) { isOpen } - - override fun isReadOnly(): Boolean = true - - override fun getSeparator(): String = SEPARATOR_STRING - - override fun getRootDirectories(): Iterable = listOf(rootDirectory) - - override fun getFileStores(): Iterable { - // TODO - throw UnsupportedOperationException() - } - - override fun supportedFileAttributeViews(): Set = - ArchiveFileAttributeView.SUPPORTED_NAMES - - override fun getPath(first: String, vararg more: String): ArchivePath { - val path = ByteStringBuilder(first.toByteString()) - .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } } - .toByteString() - return ArchivePath(fileSystem, path) - } - - override fun getPath(first: ByteString, vararg more: ByteString): ArchivePath { - val path = ByteStringBuilder(first) - .apply { more.forEach { append(SEPARATOR).append(it) } } - .toByteString() - return ArchivePath(fileSystem, path) - } - - override fun getPathMatcher(syntaxAndPattern: String): PathMatcher { - throw UnsupportedOperationException() - } - - override fun getUserPrincipalLookupService(): UserPrincipalLookupService { - throw UnsupportedOperationException() - } - - @Throws(IOException::class) - override fun newWatchService(): WatchService { - // TODO - throw UnsupportedOperationException() - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - if (javaClass != other?.javaClass) { - return false - } - other as LocalArchiveFileSystem - return archiveFile == other.archiveFile - } - - override fun hashCode(): Int = archiveFile.hashCode() - - companion object { - const val SEPARATOR = '/'.code.toByte() - private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString() - private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString() - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileSystemProvider.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileSystemProvider.kt deleted file mode 100644 index 808d38c09..000000000 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/LocalArchiveFileSystemProvider.kt +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.provider.archive - -import java8.nio.channels.FileChannel -import java8.nio.channels.SeekableByteChannel -import java8.nio.file.AccessDeniedException -import java8.nio.file.AccessMode -import java8.nio.file.CopyOption -import java8.nio.file.DirectoryStream -import java8.nio.file.FileStore -import java8.nio.file.FileSystem -import java8.nio.file.LinkOption -import java8.nio.file.OpenOption -import java8.nio.file.Path -import java8.nio.file.Paths -import java8.nio.file.ProviderMismatchException -import java8.nio.file.attribute.BasicFileAttributes -import java8.nio.file.attribute.FileAttribute -import java8.nio.file.attribute.FileAttributeView -import java8.nio.file.spi.FileSystemProvider -import me.zhanghai.android.files.provider.common.ByteStringPath -import me.zhanghai.android.files.provider.common.FileSystemCache -import me.zhanghai.android.files.provider.common.PathListDirectoryStream -import me.zhanghai.android.files.provider.common.ReadOnlyFileSystemException -import me.zhanghai.android.files.provider.common.Searchable -import me.zhanghai.android.files.provider.common.WalkFileTreeSearchable -import me.zhanghai.android.files.provider.common.decodedPathByteString -import me.zhanghai.android.files.provider.common.decodedQueryByteString -import me.zhanghai.android.files.provider.common.isSameFile -import me.zhanghai.android.files.provider.common.toAccessModes -import me.zhanghai.android.files.provider.common.toByteString -import me.zhanghai.android.files.provider.common.toOpenOptions -import java.io.IOException -import java.io.InputStream -import java.net.URI - -class LocalArchiveFileSystemProvider( - private val provider: ArchiveFileSystemProvider -) : FileSystemProvider(), Searchable { - private val fileSystems = FileSystemCache() - - override fun getScheme(): String = SCHEME - - override fun newFileSystem(uri: URI, env: Map): FileSystem { - uri.requireSameScheme() - val archiveFile = uri.archiveFile - return fileSystems.create(archiveFile) { newFileSystem(archiveFile) } - } - - override fun newFileSystem(file: Path, env: Map): FileSystem = newFileSystem(file) - - internal fun getOrNewFileSystem(archiveFile: Path): ArchiveFileSystem = - fileSystems.getOrCreate(archiveFile) { newFileSystem(archiveFile) } - - private fun newFileSystem(archiveFile: Path): ArchiveFileSystem = - ArchiveFileSystem(provider, archiveFile) - - override fun getFileSystem(uri: URI): FileSystem { - uri.requireSameScheme() - val archiveFile = uri.archiveFile - return fileSystems[archiveFile] - } - - internal fun removeFileSystem(fileSystem: ArchiveFileSystem) { - fileSystems.remove(fileSystem.archiveFile, fileSystem) - } - - override fun getPath(uri: URI): Path { - uri.requireSameScheme() - val archiveFile = uri.archiveFile - val path = uri.decodedQueryByteString - ?: throw IllegalArgumentException("URI must have a query") - return getOrNewFileSystem(archiveFile).getPath(path) - } - - private fun URI.requireSameScheme() { - val scheme = scheme - require(scheme == SCHEME) { "URI scheme $scheme must be $SCHEME" } - } - - private val URI.archiveFile: Path - get() { - val path = decodedPathByteString - ?: throw IllegalArgumentException("URI must have a path") - // Drop the first character which is always a slash. - val archiveUri = URI.create(path.toString().drop(1)) - return Paths.get(archiveUri) - } - - @Throws(IOException::class) - override fun newInputStream(file: Path, vararg options: OpenOption): InputStream { - file as? ArchivePath ?: throw ProviderMismatchException(file.toString()) - options.toOpenOptions().checkForArchive() - return file.fileSystem.newInputStreamAsLocal(file) - } - - override fun newFileChannel( - file: Path, - options: Set, - vararg attributes: FileAttribute<*> - ): FileChannel { - file as? ArchivePath ?: throw ProviderMismatchException(file.toString()) - options.toOpenOptions().checkForArchive() - if (attributes.isNotEmpty()) { - throw UnsupportedOperationException(attributes.contentToString()) - } - throw UnsupportedOperationException() - } - - override fun newByteChannel( - file: Path, - options: Set, - vararg attributes: FileAttribute<*> - ): SeekableByteChannel { - file as? ArchivePath ?: throw ProviderMismatchException(file.toString()) - options.toOpenOptions().checkForArchive() - if (attributes.isNotEmpty()) { - throw UnsupportedOperationException(attributes.contentToString()) - } - throw UnsupportedOperationException() - } - - @Throws(IOException::class) - override fun newDirectoryStream( - directory: Path, - filter: DirectoryStream.Filter - ): DirectoryStream { - directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString()) - val children = directory.fileSystem.getDirectoryChildrenAsLocal(directory) - return PathListDirectoryStream(children, filter) - } - - @Throws(IOException::class) - override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) { - directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString()) - throw ReadOnlyFileSystemException(directory.toString()) - } - - @Throws(IOException::class) - override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) { - link as? ArchivePath ?: throw ProviderMismatchException(link.toString()) - when (target) { - is ArchivePath, is ByteStringPath -> {} - else -> throw ProviderMismatchException(target.toString()) - } - throw ReadOnlyFileSystemException(link.toString(), target.toString(), null) - } - - @Throws(IOException::class) - override fun createLink(link: Path, existing: Path) { - link as? ArchivePath ?: throw ProviderMismatchException(link.toString()) - existing as? ArchivePath ?: throw ProviderMismatchException(existing.toString()) - throw ReadOnlyFileSystemException(link.toString(), existing.toString(), null) - } - - @Throws(IOException::class) - override fun delete(path: Path) { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - throw ReadOnlyFileSystemException(path.toString()) - } - - @Throws(IOException::class) - override fun readSymbolicLink(link: Path): Path { - link as? ArchivePath ?: throw ProviderMismatchException(link.toString()) - val target = link.fileSystem.readSymbolicLinkAsLocal(link) - return ByteStringPath(target.toByteString()) - } - - @Throws(IOException::class) - override fun copy(source: Path, target: Path, vararg options: CopyOption) { - source as? ArchivePath ?: throw ProviderMismatchException(source.toString()) - target as? ArchivePath ?: throw ProviderMismatchException(target.toString()) - throw ReadOnlyFileSystemException(source.toString(), target.toString(), null) - } - - @Throws(IOException::class) - override fun move(source: Path, target: Path, vararg options: CopyOption) { - source as? ArchivePath ?: throw ProviderMismatchException(source.toString()) - target as? ArchivePath ?: throw ProviderMismatchException(target.toString()) - throw ReadOnlyFileSystemException(source.toString(), target.toString(), null) - } - - @Throws(IOException::class) - override fun isSameFile(path: Path, path2: Path): Boolean { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - if (path == path2) { - return true - } - if (path2 !is ArchivePath) { - return false - } - val fileSystem = path.fileSystem - if (!fileSystem.archiveFile.isSameFile(path2.fileSystem.archiveFile)) { - return false - } - return path == fileSystem.getPath(path2.toString()) - } - - override fun isHidden(path: Path): Boolean { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - return false - } - - override fun getFileStore(path: Path): FileStore { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - val archiveFile = path.fileSystem.archiveFile - return ArchiveFileStore(archiveFile) - } - - @Throws(IOException::class) - override fun checkAccess(path: Path, vararg modes: AccessMode) { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - val accessModes = modes.toAccessModes() - path.fileSystem.getEntryAsLocal(path) - if (accessModes.write || accessModes.execute) { - throw AccessDeniedException(path.toString()) - } - } - - override fun getFileAttributeView( - path: Path, - type: Class, - vararg options: LinkOption - ): V? { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - if (!supportsFileAttributeView(type)) { - return null - } - @Suppress("UNCHECKED_CAST") - return getFileAttributeView(path) as V - } - - @Throws(IOException::class) - override fun readAttributes( - path: Path, - type: Class, - vararg options: LinkOption - ): A { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - if (!type.isAssignableFrom(ArchiveFileAttributes::class.java)) { - throw UnsupportedOperationException(type.toString()) - } - @Suppress("UNCHECKED_CAST") - return getFileAttributeView(path).readAttributes() as A - } - - private fun getFileAttributeView(path: ArchivePath): ArchiveFileAttributeView = - ArchiveFileAttributeView(path) - - override fun readAttributes( - path: Path, - attributes: String, - vararg options: LinkOption - ): Map { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - throw UnsupportedOperationException() - } - - override fun setAttribute( - path: Path, - attribute: String, - value: Any, - vararg options: LinkOption - ) { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - throw UnsupportedOperationException() - } - - @Throws(IOException::class) - override fun search( - directory: Path, - query: String, - intervalMillis: Long, - listener: (List) -> Unit - ) { - directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString()) - WalkFileTreeSearchable.search(directory, query, intervalMillis, listener) - } - - companion object { - internal const val SCHEME = "archive" - - internal fun supportsFileAttributeView(type: Class): Boolean = - type.isAssignableFrom(ArchiveFileAttributeView::class.java) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileAttributeView.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileAttributeView.kt deleted file mode 100644 index f04a0a17a..000000000 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileAttributeView.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.provider.archive - -import java8.nio.file.Path -import me.zhanghai.android.files.provider.common.PosixFileAttributeView -import me.zhanghai.android.files.provider.common.PosixFileAttributes -import me.zhanghai.android.files.provider.root.RootPosixFileAttributeView -import java.io.IOException - -internal class RootArchiveFileAttributeView( - attributeView: PosixFileAttributeView, - private val path: Path -) : RootPosixFileAttributeView(attributeView) { - @Throws(IOException::class) - override fun readAttributes(): PosixFileAttributes { - ArchiveFileSystemProvider.prepareFileSystem(path) - return super.readAttributes() - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileSystem.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileSystem.kt deleted file mode 100644 index 33466ffe1..000000000 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileSystem.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.provider.archive - -import java8.nio.file.FileSystem -import me.zhanghai.android.files.provider.remote.RemoteFileSystemException -import me.zhanghai.android.files.provider.root.RootFileService -import me.zhanghai.android.files.provider.root.RootFileSystem - -internal class RootArchiveFileSystem( - private val fileSystem: FileSystem -) : RootFileSystem(fileSystem) { - private var passwords = listOf() - - private var isSetPasswordNeeded = false - - private var isRefreshNeeded = false - - private val lock = Any() - - fun addPassword(password: String) { - synchronized(lock) { - passwords += password - isSetPasswordNeeded = true - } - } - - fun setPasswords(passwords: List) { - synchronized(lock) { - this.passwords = passwords - isSetPasswordNeeded = true - } - } - - fun refresh() { - synchronized(lock) { - isRefreshNeeded = true - } - } - - @Throws(RemoteFileSystemException::class) - fun prepare() { - synchronized(lock) { - if (isSetPasswordNeeded) { - RootFileService.setArchivePasswords(fileSystem, passwords) - isSetPasswordNeeded = false - } - if (isRefreshNeeded) { - RootFileService.refreshArchiveFileSystem(fileSystem) - isRefreshNeeded = false - } - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileSystemProvider.kt b/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileSystemProvider.kt deleted file mode 100644 index 89725857c..000000000 --- a/app/src/main/java/me/zhanghai/android/files/provider/archive/RootArchiveFileSystemProvider.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.provider.archive - -import java8.nio.file.AccessMode -import java8.nio.file.DirectoryStream -import java8.nio.file.OpenOption -import java8.nio.file.Path -import java8.nio.file.ProviderMismatchException -import me.zhanghai.android.files.provider.remote.RemoteFileSystemException -import me.zhanghai.android.files.provider.root.RootFileSystemProvider -import java.io.IOException -import java.io.InputStream - -class RootArchiveFileSystemProvider(scheme: String) : RootFileSystemProvider(scheme) { - @Throws(IOException::class) - override fun newInputStream(file: Path, vararg options: OpenOption): InputStream { - prepareFileSystem(file) - return super.newInputStream(file, *options) - } - - @Throws(IOException::class) - override fun newDirectoryStream( - directory: Path, - filter: DirectoryStream.Filter - ): DirectoryStream { - prepareFileSystem(directory) - return super.newDirectoryStream(directory, filter) - } - - @Throws(IOException::class) - override fun readSymbolicLink(link: Path): Path { - prepareFileSystem(link) - return super.readSymbolicLink(link) - } - - @Throws(IOException::class) - override fun checkAccess(path: Path, vararg modes: AccessMode) { - prepareFileSystem(path) - super.checkAccess(path, *modes) - } - - @Throws(RemoteFileSystemException::class) - internal fun prepareFileSystem(path: Path) { - path as? ArchivePath ?: throw ProviderMismatchException(path.toString()) - path.fileSystem.prepareAsRoot() - } -}