Skip to content
This repository has been archived by the owner on Dec 1, 2024. It is now read-only.

Commit

Permalink
Implement fs_remove (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
05nelsonm authored Dec 7, 2023
1 parent 2b59458 commit e845062
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
**/
package io.matthewnelson.kmp.tor.binary.core

import okio.ByteString.Companion.toByteString
import okio.FileSystem
import okio.Path.Companion.toPath
import kotlin.random.Random

expect fun filesystem(): FileSystem

Expand All @@ -37,3 +39,9 @@ val TEST_OS_RELEASE_NOT_MUSL by lazy {
.resolve("not_msl")
.resolve("os-release")
}

fun randomName(): String = Random
.Default
.nextBytes(16)
.toByteString()
.hex()
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,22 @@ public actual class Resource private constructor(
throw IOException("Failed to create destinationDir[$dir]")
}

try {
fs_chmod(dir, "700")
} catch (_: IOException) {}

val map = LinkedHashMap<String, String>(resources.size, 1.0f)

try {
resources.forEach { resource ->
val file = resource.extractTo(dir)
map[resource.alias] = file
if (resource.isExecutable) {
// These are the same executable permissions
// that are set for jvm upon resource file
// extraction.
fs_chmod(file, "764")
}
fs_chmod(file, if (resource.isExecutable) "500" else "400")
}
} catch (t: Throwable) {
map.forEach { entry ->
try {
fs_rm(entry.value)
fs_remove(entry.value)
} catch (_: Throwable) {}
}

Expand All @@ -91,7 +90,7 @@ public actual class Resource private constructor(
val destination = path_join(destinationDir, fileName)
val moduleResource = resolveResource(moduleName + resourcePath)

if (fs_exists(destination) && !fs_rm(destination)) {
if (fs_exists(destination) && !fs_remove(destination)) {
throw IOException("Failed to delete $destination")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ internal external fun fs_realpathSync(path: String): String
@JsName("rmSync")
internal external fun fs_rmSync(path: String, options: Options.Remove)

/** [docs](https://nodejs.org/api/fs.html#fsrmdirsyncpath-options) */
@JsName("rmdirSync")
internal external fun fs_rmdirSync(path: String, options: Options.Remove)

/** [docs](https://nodejs.org/api/fs.html#fsunlinksyncpath) */
@JsName("unlinkSync")
internal external fun fs_unlinkSync(path: String)

/** [docs](https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options) */
@JsName("writeFileSync")
internal external fun fs_writeFileSync(path: String, data: buffer_Buffer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,29 @@ public actual fun fs_chmod(path: String, mode: String) {
}

@InternalKmpTorBinaryApi
public actual fun fs_mkdir(path: String): Boolean {
if (fs_exists(path)) return false
public actual fun fs_remove(path: String): Boolean {
try {
fs_unlinkSync(path)
return true
} catch (t: Throwable) {
if (t.errorCode == "ENOENT") return false
}

return try {
fs_rmSync(path, Options.Remove(force = true, recursive = false))
true
} catch (_: Throwable) {
try {
fs_rmdirSync(path, Options.Remove(force = true, recursive = false))
true
} catch (t: Throwable) {
throw t.toIOException()
}
}
}

@InternalKmpTorBinaryApi
public actual fun fs_mkdir(path: String): Boolean {
return try {
fs_mkdirSync(path)
true
Expand Down Expand Up @@ -90,36 +110,22 @@ public actual fun fs_readFileUtf8(path: String): String {
}

@InternalKmpTorBinaryApi
public actual fun fs_platform_realpath(path: String): String {
internal actual fun fs_platform_realpath(path: String): String {
return try {
fs_realpathSync(path)
} catch (t: Throwable) {
throw t.toIOException()
}
}

@InternalKmpTorBinaryApi
public actual fun fs_rm(
path: String,
recursively: Boolean,
force: Boolean,
): Boolean {
if (!fs_exists(path)) return false
try {
fs_rmSync(path, Options.Remove(force = force, recursive = recursively))
} catch (t: Throwable) {
// TODO: if force true, swallow exception???
throw t.toIOException()
}
return fs_exists(path)
}
private val Throwable.errorCode: dynamic get() = asDynamic().code

@InternalKmpTorBinaryApi
public fun Throwable.toIOException(): IOException {
if (this is IOException) return this

val code = try {
asDynamic().code
errorCode
} catch (_: Throwable) {
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal sealed class Options {
}

internal class Remove internal constructor(
internal val force: Boolean = true,
internal val recursive: Boolean = true,
internal val force: Boolean,
internal val recursive: Boolean,
): Options()
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,24 @@ public actual class Resource private constructor(
throw IOException("Failed to create destinationDir[$dir]")
}

dir.setExecutable(false, false)
dir.setWritable(false, false)
dir.setReadable(false, false)
dir.setExecutable(true, true)
dir.setWritable(true, true)
dir.setReadable(true, true)

val map = LinkedHashMap<String, String>(resources.size, 1.0f)

try {
resources.forEach { resource ->
val file = resource.extractTo(dir)
map[resource.alias] = file.absolutePath
file.setReadable(false, false)
file.setWritable(false, false)
file.setReadable(true, true)
if (resource.isExecutable) {
file.setExecutable(true)
file.setExecutable(true, true)
}
}
} catch (t: Throwable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,21 @@ import platform.posix.*
public actual fun fs_chmod(path: String, mode: String) { /* no-op */ }

@InternalKmpTorBinaryApi
public actual fun fs_mkdir(path: String): Boolean {
if (fs_exists(path)) return false
mkdir(path)
return fs_exists(path)
@Throws(IOException::class)
public actual fun fs_remove(path: String): Boolean {
if (remove(path) == 0) return true

if (errno == EACCES) {
if (rmdir(path) == 0) return true
}
if (errno == ENOENT) return false
throw errnoToIOException(errno)
}

internal actual fun fs_platform_mkdir(
path: String
): Int = mkdir(path)

@Throws(IOException::class)
internal actual fun fs_platform_realpath(path: String): String {
@OptIn(ExperimentalForeignApi::class, InternalKmpTorBinaryApi::class)
Expand All @@ -52,16 +61,6 @@ internal actual fun fs_platform_realpath(path: String): String {
}
}

@InternalKmpTorBinaryApi
@Throws(IOException::class)
public actual fun fs_rm(
path: String,
recursively: Boolean,
force: Boolean,
): Boolean {
TODO()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalForeignApi::class)
internal actual inline fun fs_platform_read(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,22 @@ public actual class Resource private constructor(
throw IOException("Failed to create destinationDir[$dir]")
}

try {
fs_chmod(dir, "700")
} catch (_: IOException) {}

val map = LinkedHashMap<String, String>(resources.size, 1.0f)

try {
resources.forEach { resource ->
val file = resource.extractTo(dir)
map[resource.alias] = file
if (resource.isExecutable) {
fs_chmod(file, "764")
}
fs_chmod(file, if (resource.isExecutable) "500" else "400")
}
} catch (t: Throwable) {
map.forEach { entry ->
try {
fs_rm(entry.value)
fs_remove(entry.value)
} catch (_: Throwable) {}
}

Expand All @@ -86,7 +88,7 @@ public actual class Resource private constructor(

val destination = path_join(destinationDir, fileName)

if (fs_exists(destination) && !fs_rm(destination)) {
if (fs_exists(destination) && !fs_remove(destination)) {
throw IOException("Failed to delete $destination")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public actual fun fs_exists(path: String): Boolean {
}
}

@InternalKmpTorBinaryApi
public actual fun fs_mkdir(path: String): Boolean {
return fs_platform_mkdir(path) == 0
}

@InternalKmpTorBinaryApi
@Throws(IOException::class)
public actual fun fs_readFileBytes(path: String): ByteArray {
Expand Down Expand Up @@ -101,6 +106,11 @@ internal inline fun <T: Any?> fs_withFile(
return result
}

@Suppress("NOTHING_TO_INLINE")
internal expect fun fs_platform_mkdir(
path: String,
): Int

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalForeignApi::class)
internal expect inline fun fs_platform_read(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ public expect fun fs_mkdir(path: String): Boolean
@InternalKmpTorBinaryApi
@Throws(IOException::class)
public fun fs_mkdirs(path: String): Boolean {
if (fs_exists(path)) return false
if (fs_mkdir(path)) return true

val dirsToMake = mutableListOf(fs_canonicalize(path))
Expand Down Expand Up @@ -79,13 +78,9 @@ public expect fun fs_readFileBytes(path: String): ByteArray
@Throws(IOException::class)
public expect fun fs_readFileUtf8(path: String): String

@InternalKmpTorBinaryApi
@Throws(IOException::class)
internal expect fun fs_platform_realpath(path: String): String
public expect fun fs_remove(path: String): Boolean

@InternalKmpTorBinaryApi
@Throws(IOException::class)
public expect fun fs_rm(
path: String,
recursively: Boolean = false,
force: Boolean = true,
): Boolean
internal expect fun fs_platform_realpath(path: String): String
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ package io.matthewnelson.kmp.tor.binary.core.internal
import io.matthewnelson.kmp.tor.binary.core.*
import io.matthewnelson.kmp.tor.binary.core.PROJECT_DIR_PATH
import io.matthewnelson.kmp.tor.binary.core.api.FileNotFoundException
import okio.ByteString.Companion.toByteString
import io.matthewnelson.kmp.tor.binary.core.api.IOException
import okio.FileSystem
import okio.Path.Companion.toPath
import kotlin.random.Random
import kotlin.test.*

@OptIn(InternalKmpTorBinaryApi::class)
Expand Down Expand Up @@ -93,16 +92,56 @@ class FileSystemUnitTest {
}

@Test
fun givenMkdir_whenAlreadyExists_thenReturnsFalse() {
fun givenMkdir_whenExists_thenReturnsFalse() {
assertFalse(fs_mkdir(PROJECT_DIR_PATH))
}

@Test
fun givenMkdir_whenDoesNotExist_thenReturnsTrue() {
val name = Random.Default.nextBytes(16).toByteString().hex()
val dir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY.resolve(name)
val dir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY.resolve(randomName())
assertFalse(filesystem().exists(dir))
assertTrue(fs_mkdir(dir.toString()))
filesystem().delete(dir, mustExist = true)
}

@Test
fun givenRemove_whenFileExists_thenReturnsTrue() {
val dir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY.resolve(randomName())
fs_mkdir(dir.toString())
val file = dir.resolve("test")
filesystem().write(file) { writeUtf8("test") }
try {
assertTrue(fs_remove(file.toString()))
} finally {
filesystem().deleteRecursively(dir)
}
}

@Test
fun givenRemove_whenEmptyDirExist_thenReturnsTrue() {
val dir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY.resolve(randomName())
fs_mkdir(dir.toString())
assertTrue(fs_remove(dir.toString()))
}

@Test
fun givenRemove_whenNonEmptyDirExist_thenThrowsIOException() {
val dir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY.resolve(randomName())
fs_mkdir(dir.toString())
filesystem().write(dir.resolve("test")) { writeUtf8("test") }
try {
fs_remove(dir.toString())
fail()
} catch (e: IOException) {
// pass
} finally {
filesystem().deleteRecursively(dir)
}
}

@Test
fun givenRemove_whenDoesNotExist_thenReturnsFalse() {
val doesNotExist = FileSystem.SYSTEM_TEMPORARY_DIRECTORY.resolve(randomName())
assertFalse(fs_remove(doesNotExist.toString()))
}
}
Loading

0 comments on commit e845062

Please sign in to comment.