Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP #1499

Closed
wants to merge 1 commit into from
Closed

WIP #1499

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ import okio.fakefilesystem.FakeFileSystem.Operation.WRITE
* Programs that do not attempt any of the above operations should work fine on both UNIX and
* Windows systems. Relax these constraints individually or call [emulateWindows] or [emulateUnix];
* to apply the constraints of a particular operating system.
*
* Closeable
* ---------
*
* This file system cannot be used after it is closed. Closing it does not close any of its open
* streams; those must be closed directly.
*/
class FakeFileSystem(
@JvmField
Expand All @@ -71,6 +77,9 @@ class FakeFileSystem(
/** Files that are currently open and need to be closed to avoid resource leaks. */
private val openFiles = mutableListOf<OpenFile>()

/** Forbid all access after [close]. */
private var closed = false

/**
* An absolute path with this file system's current working directory. Relative paths will be
* resolved against this directory when they are used.
Expand Down Expand Up @@ -218,6 +227,7 @@ class FakeFileSystem(

/** Don't throw [FileNotFoundException] if the path doesn't identify a file. */
private fun canonicalizeInternal(path: Path): Path {
check(!closed) { "closed" }
return workingDirectory.resolve(path, normalize = true)
}

Expand Down Expand Up @@ -764,5 +774,9 @@ class FakeFileSystem(
override fun toString() = "FileHandler(${openFile.canonicalPath})"
}

override fun close() {
closed = true
}

override fun toString() = "FakeFileSystem"
}
1 change: 1 addition & 0 deletions okio/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ kotlin {
}

val nonWasmTest by creating {
dependsOn(commonTest)
dependencies {
implementation(libs.kotlin.time)
implementation(projects.okioFakefilesystem)
Expand Down
17 changes: 16 additions & 1 deletion okio/src/commonMain/kotlin/okio/FileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,20 @@ package okio
* because the `Paths.get()` function automatically uses the default (ie. system) file system.
* In Okio's API paths are just identifiers; you must use a specific `FileSystem` object to do
* I/O with.
*
* Closeable
* ---------
*
* Implementations of this interface may need to be closed to release resources.
*
* It is the file system implementor's responsibility to document whether a file system instance
* must be closed, and what happens to its open streams when the file system is closed. For example,
* the Java NIO FileSystem closes all of its open streams when the file system is closed.
*
* The built-in `FileSystem.SYSTEM` implementation does not need to be closed and closing it has no
* effect.
*/
expect abstract class FileSystem() {
expect abstract class FileSystem() : Closeable {

/**
* Resolves [path] against the current working directory and symlinks in this file system. The
Expand Down Expand Up @@ -376,6 +388,9 @@ expect abstract class FileSystem() {
@Throws(IOException::class)
abstract fun createSymlink(source: Path, target: Path)

@Throws(IOException::class)
override fun close()

companion object {
/**
* Returns a writable temporary directory on [SYSTEM].
Expand Down
5 changes: 5 additions & 0 deletions okio/src/commonMain/kotlin/okio/ForwardingFileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -238,5 +238,10 @@ abstract class ForwardingFileSystem(
delegate.createSymlink(source, target)
}

@Throws(IOException::class)
override fun close() {
delegate.close()
}

override fun toString() = "${this::class.simpleName}($delegate)"
}
5 changes: 4 additions & 1 deletion okio/src/jsMain/kotlin/okio/FileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import okio.internal.commonExists
import okio.internal.commonListRecursively
import okio.internal.commonMetadata

actual abstract class FileSystem {
actual abstract class FileSystem : Closeable {
actual abstract fun canonicalize(path: Path): Path

actual fun metadata(path: Path): FileMetadata = commonMetadata(path)
Expand Down Expand Up @@ -84,6 +84,9 @@ actual abstract class FileSystem {

actual abstract fun createSymlink(source: Path, target: Path)

actual override fun close() {
}

actual companion object {
actual val SYSTEM_TEMPORARY_DIRECTORY: Path = tmpdir.toPath()
}
Expand Down
14 changes: 13 additions & 1 deletion okio/src/jvmMain/kotlin/okio/FileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import okio.internal.commonExists
import okio.internal.commonListRecursively
import okio.internal.commonMetadata

actual abstract class FileSystem {
actual abstract class FileSystem : Closeable {
@Throws(IOException::class)
actual abstract fun canonicalize(path: Path): Path

Expand Down Expand Up @@ -125,6 +125,10 @@ actual abstract class FileSystem {
@Throws(IOException::class)
actual abstract fun createSymlink(source: Path, target: Path)

@Throws(IOException::class)
actual override fun close() {
}

actual companion object {
/**
* The current process's host file system. Use this instance directly, or dependency inject a
Expand All @@ -150,13 +154,21 @@ actual abstract class FileSystem {
* In applications that compose multiple class loaders, this holds only the resources of
* whichever class loader includes Okio classes. Use [ClassLoader.asResourceFileSystem] for the
* resources of a specific class loader.
*
* This file system does not need to be closed. Calling its close function does nothing.
*/
@JvmField
val RESOURCES: FileSystem = ResourceFileSystem(
classLoader = ResourceFileSystem::class.java.classLoader,
indexEagerly = false,
)

/**
* Closing the returned file system will close the underlying [java.nio.file.FileSystem].
*
* Note that the [default file system][java.nio.file.FileSystems.getDefault] is not closeable
* and calling its close function will throw an [UnsupportedOperationException].
*/
@JvmName("get")
@JvmStatic
fun JavaNioFileSystem.asOkioFileSystem(): FileSystem = NioFileSystemWrappingFileSystem(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
*/
package okio

import java.nio.file.FileSystem as NioFileSystem
import java.nio.file.Path as NioPath
import java.io.InterruptedIOException
import java.nio.channels.FileChannel
import java.nio.file.FileSystem as NioFileSystem
import java.nio.file.NoSuchFileException
import java.nio.file.Path as NioPath
import java.nio.file.StandardCopyOption
import java.nio.file.StandardOpenOption
import kotlin.io.path.createDirectory
Expand Down Expand Up @@ -187,5 +187,9 @@ internal class NioFileSystemWrappingFileSystem(private val nioFileSystem: NioFil
source.resolve().createSymbolicLinkPointingTo(target.resolve())
}

override fun close() {
nioFileSystem.close()
}

override fun toString() = nioFileSystem::class.simpleName!!
}
6 changes: 5 additions & 1 deletion okio/src/nativeMain/kotlin/okio/FileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import okio.internal.commonExists
import okio.internal.commonListRecursively
import okio.internal.commonMetadata

actual abstract class FileSystem {
actual abstract class FileSystem : Closeable {
@Throws(IOException::class)
actual abstract fun canonicalize(path: Path): Path

Expand Down Expand Up @@ -102,6 +102,10 @@ actual abstract class FileSystem {
@Throws(IOException::class)
actual abstract fun createSymlink(source: Path, target: Path)

@Throws(IOException::class)
actual override fun close() {
}

actual companion object {
/**
* The current process's host file system. Use this instance directly, or dependency inject a
Expand Down
54 changes: 54 additions & 0 deletions okio/src/nonWasmTest/kotlin/okio/FakeFileSystemTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,60 @@ abstract class FakeFileSystemTest internal constructor(
}
}

@Test
fun readAfterFileSystemClose() {
val path = base / "file"

path.writeUtf8("hello, world!")

fileSystem.close()

assertFailsWith<IllegalStateException> {
fileSystem.canonicalize(path)
}
assertFailsWith<IllegalStateException> {
fileSystem.exists(path)
}
assertFailsWith<IllegalStateException> {
fileSystem.metadata(path)
}
assertFailsWith<IllegalStateException> {
fileSystem.openReadOnly(path)
}
assertFailsWith<IllegalStateException> {
fileSystem.source(path)
}
}

@Test
fun writeAfterFileSystemClose() {
val path = base / "file"

fileSystem.close()

assertFailsWith<IllegalStateException> {
fileSystem.appendingSink(path)
}
assertFailsWith<IllegalStateException> {
fileSystem.atomicMove(path, base / "file2")
}
assertFailsWith<IllegalStateException> {
fileSystem.createDirectory(base / "directory")
}
assertFailsWith<IllegalStateException> {
fileSystem.createSymlink(base / "symlink", base)
}
assertFailsWith<IllegalStateException> {
fileSystem.delete(path)
}
assertFailsWith<IllegalStateException> {
fileSystem.openReadWrite(path)
}
assertFailsWith<IllegalStateException> {
fileSystem.sink(path)
}
}

internal data class ContentTypeExtra(
val contentType: String,
)
Expand Down
5 changes: 4 additions & 1 deletion okio/src/wasmMain/kotlin/okio/FileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import okio.internal.commonExists
import okio.internal.commonListRecursively
import okio.internal.commonMetadata

actual abstract class FileSystem {
actual abstract class FileSystem : Closeable {
actual abstract fun canonicalize(path: Path): Path

actual fun metadata(path: Path): FileMetadata = commonMetadata(path)
Expand Down Expand Up @@ -84,6 +84,9 @@ actual abstract class FileSystem {

actual abstract fun createSymlink(source: Path, target: Path)

actual override fun close() {
}

actual companion object {
actual val SYSTEM_TEMPORARY_DIRECTORY: Path = "/tmp".toPath()
}
Expand Down
9 changes: 9 additions & 0 deletions okio/src/zlibMain/kotlin/okio/ZlibOkio.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,14 @@ package okio
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName

/**
* Returns a new read-only file system.
*
* This function processes the ZIP file's central directory and builds an index of its files and
* their offsets within the ZIP. If the ZIP file is changed after this function returns, this
* file system will be broken and may return inconsistent data or crash when it is accessed.
*
* Closing the returned file system is not necessary and does nothing.
*/
@Throws(IOException::class)
fun FileSystem.openZip(zipPath: Path): FileSystem = okio.internal.openZip(zipPath, this)
Loading