All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nativeMain.files.FileSystemNative.kt Maven / Gradle / Ivy

There is a newer version: 0.6.0
Show newest version
/*
 * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
 */

package kotlinx.io.files

import kotlinx.cinterop.CPointer
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.toKString
import kotlinx.io.IOException
import kotlinx.io.RawSink
import kotlinx.io.RawSource
import platform.posix.*
import kotlin.experimental.ExperimentalNativeApi

@OptIn(ExperimentalForeignApi::class)
public actual val SystemFileSystem: FileSystem = object : SystemFileSystemImpl() {
    override fun exists(path: Path): Boolean {
        return access(path.path, F_OK) == 0
    }

    @OptIn(ExperimentalForeignApi::class)
    override fun delete(path: Path, mustExist: Boolean) {
        if (!exists(path)) {
            if (mustExist) {
                throw FileNotFoundException("File does not exist: $path")
            }
            return
        }
        if (remove(path.path) != 0) {
            if (errno == EACCES) {
                if (rmdir(path.path) == 0) return
            }
            throw IOException("Delete failed for $path: ${strerror(errno)?.toKString()}")
        }
    }

    override fun createDirectories(path: Path, mustCreate: Boolean) {
        val metadata = metadataOrNull(path)
        if (metadata != null) {
            if (mustCreate) {
                throw IOException("Path already exists: $path")
            }
            if (metadata.isRegularFile) {
                throw IOException("Path already exists and it's a file: $path")
            }
            return
        }
        val paths = arrayListOf()
        var p: Path? = path
        while (p != null && !exists(p)) {
            paths.add(p.toString())
            p = p.parent
        }
        paths.asReversed().forEach {
            mkdirImpl(it)
        }
    }

    override fun atomicMove(source: Path, destination: Path) {
        if (!exists(source)) {
            throw FileNotFoundException("Source does not exist: ${source.path}")
        }
        atomicMoveImpl(source, destination)
    }

    override fun metadataOrNull(path: Path): FileMetadata? = metadataOrNullImpl(path)

    override fun resolve(path: Path): Path {
        if (!exists(path)) throw FileNotFoundException(path.path)
        return Path(realpathImpl(path.path))
    }

    override fun source(path: Path): RawSource {
        val openFile: CPointer? = fopen(path.path, "rb")
        if (openFile == null) {
            if (errno == ENOENT) {
                throw FileNotFoundException("File does not exist: $path")
            }
            throw IOException("Failed to open $path with ${strerror(errno)?.toKString()}")
        }
        return FileSource(openFile)
    }

    override fun sink(path: Path, append: Boolean): RawSink {
        val openFile: CPointer = fopen(path.path, if (append) "ab" else "wb")
            ?: throw IOException("Failed to open $path with ${strerror(errno)?.toKString()}")
        return FileSink(openFile)
    }

    override fun list(directory: Path): Collection {
        val metadata = metadataOrNull(directory) ?: throw FileNotFoundException(directory.path)
        if (!metadata.isDirectory) throw IOException("Not a directory: ${directory.path}")
        return buildList {
            opendir(directory.path).use {
                var child = it.readdir()
                while (child != null) {
                    if (child != "." && child != "..") {
                        add(Path(directory, child))
                    }
                    child = it.readdir()
                }
            }
        }
    }
}

internal expect fun metadataOrNullImpl(path: Path): FileMetadata?

internal expect fun atomicMoveImpl(source: Path, destination: Path)

internal expect fun mkdirImpl(path: String)

internal expect fun realpathImpl(path: String): String

public actual open class FileNotFoundException actual constructor(
    message: String?
) : IOException(message)

// 777 in octal, rwx for all (owner, group and others).
internal const val PermissionAllowAll: UShort = 511u

@OptIn(ExperimentalNativeApi::class)
internal actual val isWindows: Boolean = Platform.osFamily == OsFamily.WINDOWS

internal expect class OpaqueDirEntry : AutoCloseable {
    fun readdir(): String?
    override fun close()
}

internal expect fun opendir(path: String): OpaqueDirEntry




© 2015 - 2024 Weber Informatics LLC | Privacy Policy