Skip to content

Commit

Permalink
Add implementation of utimensat, rmdir, opendirat (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
illarionov authored Mar 27, 2024
1 parent c12c93d commit 7f3c4d0
Show file tree
Hide file tree
Showing 18 changed files with 551 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallFc
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallFstat64
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallFtruncate64
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallGetcwd
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallMkdirat
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallOpenat
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallRmdir
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallUnlinkat
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.SyscallUtimensat
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.syscallLstat64
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func.syscallStat64
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.fn
Expand Down Expand Up @@ -107,7 +110,7 @@ internal class EmscriptenEnvModuleBuilder(
nodeFactory = ::SyscallGetcwd,
)
fn("__syscall_ioctl", List(3) { I32 })
fn("__syscall_mkdirat", List(3) { I32 })
fn("__syscall_mkdirat", List(3) { I32 }, I32, ::SyscallMkdirat)
fn("__syscall_newfstatat", List(4) { I32 })
fn(
name = "__syscall_openat",
Expand All @@ -116,7 +119,7 @@ internal class EmscriptenEnvModuleBuilder(
nodeFactory = ::SyscallOpenat,
)
fn("__syscall_readlinkat", List(4) { I32 })
fn("__syscall_rmdir", listOf(I32))
fn("__syscall_rmdir", listOf(I32), I32, ::SyscallRmdir)
fn(
name = "__syscall_stat64",
paramTypes = listOf(I32, I32),
Expand All @@ -135,7 +138,7 @@ internal class EmscriptenEnvModuleBuilder(
retType = I32,
nodeFactory = ::SyscallUnlinkat,
)
fn("__syscall_utimensat", List(4) { I32 })
fn("__syscall_utimensat", List(4) { I32 }, I32, ::SyscallUtimensat)
fnVoid("_tzset_js", List(4) { I32 })

fnVoid("_emscripten_thread_set_strongref", listOf(I32))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary
import com.oracle.truffle.api.frame.VirtualFrame
import org.graalvm.wasm.WasmContext
import org.graalvm.wasm.WasmInstance
import org.graalvm.wasm.WasmLanguage
import org.graalvm.wasm.WasmModule
import org.graalvm.wasm.memory.WasmMemory
import ru.pixnews.wasm.sqlite.open.helper.common.api.Logger
import ru.pixnews.wasm.sqlite.open.helper.common.api.WasmPtr
import ru.pixnews.wasm.sqlite.open.helper.graalvm.SqliteEmbedderHost
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsInt
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsUint
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsWasmPtr
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.BaseWasmNode
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.SysException
import ru.pixnews.wasm.sqlite.open.helper.host.include.DirFd
import ru.pixnews.wasm.sqlite.open.helper.host.include.FileMode

internal class SyscallMkdirat(
language: WasmLanguage,
module: WasmModule,
private val host: SqliteEmbedderHost,
functionName: String = "__syscall_mkdirat",
) : BaseWasmNode(language, module, functionName) {
private val logger: Logger = host.rootLogger.withTag(SyscallFtruncate64::class.qualifiedName!!)
override fun executeWithContext(frame: VirtualFrame, context: WasmContext, instance: WasmInstance): Any {
val args: Array<Any> = frame.arguments
return syscallMkdirat(
memory(frame),
args.getArgAsInt(0),
args.getArgAsWasmPtr(1),
args.getArgAsUint(2),
)
}

@Suppress("MemberNameEqualsClassName")
@TruffleBoundary
private fun syscallMkdirat(
memory: WasmMemory,
rawDirFd: Int,
pathnamePtr: WasmPtr<Byte>,
rawMode: UInt,
): Int {
val fs = host.fileSystem
val dirFd = DirFd(rawDirFd)
val mode = FileMode(rawMode)
val path = memory.readString(pathnamePtr.addr, null)
try {
fs.mkdirAt(dirFd, path, mode)
return 0
} catch (e: SysException) {
logger.v(e) { "__syscall_mkdirat($dirFd, $path, $mode) error: ${e.errNo}" }
return -e.errNo.code
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsUint
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsWasmPtr
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.BaseWasmNode
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.SysException
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.resolveAbsolutePath
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.fd.resolveAbsolutePath
import ru.pixnews.wasm.sqlite.open.helper.host.include.DirFd
import ru.pixnews.wasm.sqlite.open.helper.host.include.Fcntl
import ru.pixnews.wasm.sqlite.open.helper.host.include.FileMode
import ru.pixnews.wasm.sqlite.open.helper.host.include.oMaskToString
import ru.pixnews.wasm.sqlite.open.helper.host.include.sMaskToString
import ru.pixnews.wasm.sqlite.open.helper.host.wasi.preview1.type.Fd
import java.nio.file.Path

Expand All @@ -49,33 +50,35 @@ internal class SyscallOpenat(

val fdOrErrno = openAt(
memory,
dirfd = args.getArgAsInt(0),
rawDirFd = args.getArgAsInt(0),
pathnamePtr = args.getArgAsWasmPtr(1),
flags = args.getArgAsUint(2),
mode = mode,
rawMode = mode,
)
return fdOrErrno
}

@TruffleBoundary
private fun openAt(
memory: WasmMemory,
dirfd: Int,
rawDirFd: Int,
pathnamePtr: WasmPtr<Byte>,
flags: UInt,
mode: UInt,
rawMode: UInt,
): Int {
val fs = host.fileSystem
val dirFd = DirFd(rawDirFd)
val mode = FileMode(rawMode)
val path = memory.readString(pathnamePtr.addr, null)
val absolutePath = fs.resolveAbsolutePath(dirfd, path)
val absolutePath = fs.resolveAbsolutePath(dirFd, path)

return try {
val fd = fs.open(absolutePath, flags, mode).fd
logger.v { formatCallString(dirfd, path, absolutePath, flags, mode, fd) }
logger.v { formatCallString(dirFd, path, absolutePath, flags, mode, fd) }
fd.fd
} catch (e: SysException) {
logger.v {
formatCallString(dirfd, path, absolutePath, flags, mode, null) +
formatCallString(dirFd, path, absolutePath, flags, mode, null) +
"openAt() error ${e.errNo}"
}
-e.errNo.code
Expand All @@ -84,17 +87,17 @@ internal class SyscallOpenat(

@Suppress("MagicNumber")
private fun formatCallString(
dirfd: Int,
dirfd: DirFd,
path: String,
absolutePath: Path,
flags: UInt,
mode: UInt,
mode: FileMode,
fd: Fd?,
): String = "openAt() dirfd: " +
"$dirfd, " +
"path: `$path`, " +
"full path: `$absolutePath`, " +
"flags: 0${flags.toString(8)} (${Fcntl.oMaskToString(flags)}), " +
"mode: ${Fcntl.sMaskToString(mode)}" +
"mode: $mode" +
if (fd != null) ": $fd" else ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary
import com.oracle.truffle.api.frame.VirtualFrame
import org.graalvm.wasm.WasmContext
import org.graalvm.wasm.WasmInstance
import org.graalvm.wasm.WasmLanguage
import org.graalvm.wasm.WasmModule
import org.graalvm.wasm.memory.WasmMemory
import ru.pixnews.wasm.sqlite.open.helper.common.api.WasmPtr
import ru.pixnews.wasm.sqlite.open.helper.graalvm.SqliteEmbedderHost
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsWasmPtr
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.BaseWasmNode
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.SysException

internal class SyscallRmdir(
language: WasmLanguage,
module: WasmModule,
private val host: SqliteEmbedderHost,
functionName: String = "__syscall_rmdir",
) : BaseWasmNode(language, module, functionName) {
override fun executeWithContext(frame: VirtualFrame, context: WasmContext, instance: WasmInstance): Any {
val args: Array<Any> = frame.arguments
return syscallRmdirat(
memory(frame),
args.getArgAsWasmPtr(0),
)
}

@TruffleBoundary
private fun syscallRmdirat(
memory: WasmMemory,
pathnamePtr: WasmPtr<Byte>,
): Int {
val fs = host.fileSystem
val path = memory.readString(pathnamePtr.addr, null)
try {
fs.rmdir(path)
return 0
} catch (e: SysException) {
return -e.errNo.code
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.graalvm.host.emscripten.func

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary
import com.oracle.truffle.api.frame.VirtualFrame
import org.graalvm.wasm.WasmContext
import org.graalvm.wasm.WasmInstance
import org.graalvm.wasm.WasmLanguage
import org.graalvm.wasm.WasmModule
import org.graalvm.wasm.memory.WasmMemory
import ru.pixnews.wasm.sqlite.open.helper.common.api.WasmPtr
import ru.pixnews.wasm.sqlite.open.helper.common.api.isSqlite3Null
import ru.pixnews.wasm.sqlite.open.helper.graalvm.SqliteEmbedderHost
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsInt
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsUint
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsWasmPtr
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.BaseWasmNode
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.SysException
import ru.pixnews.wasm.sqlite.open.helper.host.include.DirFd
import ru.pixnews.wasm.sqlite.open.helper.host.include.Fcntl
import ru.pixnews.wasm.sqlite.open.helper.host.include.sys.SysStat.UTIME_NOW
import ru.pixnews.wasm.sqlite.open.helper.host.include.sys.SysStat.UTIME_OMIT
import kotlin.LazyThreadSafetyMode.NONE
import kotlin.time.Duration
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.seconds

internal class SyscallUtimensat(
language: WasmLanguage,
module: WasmModule,
private val host: SqliteEmbedderHost,
functionName: String = "__syscall_utimensat",
) : BaseWasmNode(language, module, functionName) {
override fun executeWithContext(frame: VirtualFrame, context: WasmContext, instance: WasmInstance): Any {
val args: Array<Any> = frame.arguments
@Suppress("MagicNumber")
return syscallUtimensat(
memory(frame),
args.getArgAsInt(0),
args.getArgAsWasmPtr(1),
args.getArgAsWasmPtr(2),
args.getArgAsUint(3),
)
}

@Suppress("MemberNameEqualsClassName")
@TruffleBoundary
private fun syscallUtimensat(
memory: WasmMemory,
rawDirFd: Int,
pathnamePtr: WasmPtr<Byte>,
times: WasmPtr<Byte>,
flags: UInt,
): Int {
val dirFd = DirFd(rawDirFd)
val noFolowSymlinks: Boolean = (flags and Fcntl.AT_SYMLINK_NOFOLLOW) != 0U
val path = memory.readString(pathnamePtr.addr, null)
var atime: Duration?
val mtime: Duration?
@Suppress("MagicNumber")
if (times.isSqlite3Null()) {
atime = host.clock.invoke()
mtime = atime
} else {
val atimeSeconds = memory.load_i64(this, times.addr.toLong())
val atimeNanoseconds = memory.load_i64(this, times.addr.toLong() + 8)

val mtimeSeconds = memory.load_i64(this, times.addr.toLong() + 16)
val mtimeNanoseconds = memory.load_i64(this, times.addr.toLong() + 24)

val now: Duration by lazy(NONE) { host.clock.invoke() }
atime = timesToDuration(atimeSeconds, atimeNanoseconds) { now }
mtime = timesToDuration(mtimeSeconds, mtimeNanoseconds) { now }
}
try {
host.fileSystem.setTimesAt(dirFd, path, atime, mtime, noFolowSymlinks)
return 0
} catch (e: SysException) {
return -e.errNo.code
}
}

private fun timesToDuration(
seconds: Long,
nanoseconds: Long,
now: () -> Duration,
): Duration? = when (nanoseconds) {
UTIME_NOW.toLong() -> now()
UTIME_OMIT.toLong() -> null
else -> seconds.seconds + nanoseconds.nanoseconds
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import com.oracle.truffle.api.interop.InteropLibrary
import com.oracle.truffle.api.interop.TruffleObject
import com.oracle.truffle.api.library.ExportLibrary
import com.oracle.truffle.api.library.ExportMessage
import org.graalvm.wasm.WasmArguments
import org.graalvm.wasm.memory.WasmMemory
import ru.pixnews.wasm.sqlite.open.helper.common.api.InternalWasmSqliteHelperApi
import ru.pixnews.wasm.sqlite.open.helper.common.api.Logger
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsInt
import ru.pixnews.wasm.sqlite.open.helper.graalvm.ext.getArgAsLong
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.memory.SharedMemoryWaiterListStore.AtomicsWaitResult
import ru.pixnews.wasm.sqlite.open.helper.graalvm.host.memory.SharedMemoryWaiterListStore.WaiterListRecord
Expand All @@ -36,11 +36,11 @@ internal class WasmMemoryWaitCallback(
@ExportMessage
public fun execute(arguments: Array<Any>): Any {
val wasmMemory = arguments[0] as WasmMemory
val addr = arguments.getArgAsInt(0)
val addr = arguments.getArgAsLong(0).toInt()
val expectedValue = arguments.getArgAsLong(1)
val timeout = arguments.getArgAsLong(2)
val is64 = arguments.getArgAsInt(3) != 0
logger.v { "execute(): $addr $expectedValue $timeout" }
val is64 = WasmArguments.getArgument(arguments, 3) as Boolean
logger.v { "execute(): $addr $expectedValue $timeout $is64" }

val list: WaiterListRecord = waitersStore.getListForIndex(
if (is64) addr * 8 else addr * 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import ru.pixnews.wasm.sqlite.open.helper.sqlite.common.api.SqliteErrorInfo
import ru.pixnews.wasm.sqlite.open.helper.sqlite.common.api.SqliteException

internal fun SqliteException.rethrowAndroidSqliteException(msg: String? = null): Nothing {
throwAndroidSqliteException(errorInfo, msg)
throwAndroidSqliteException(errorInfo, msg, this)
}

internal fun throwAndroidSqliteException(message: String?): Nothing = throwAndroidSqliteException(
Expand All @@ -42,6 +42,7 @@ internal fun throwAndroidSqliteException(message: String?): Nothing = throwAndro
internal fun throwAndroidSqliteException(
errorInfo: SqliteErrorInfo,
message: String?,
cause: Throwable? = null,
): Nothing {
val fullErMsg = if (errorInfo.sqliteMsg != null) {
buildString {
Expand All @@ -55,6 +56,10 @@ internal fun throwAndroidSqliteException(
prefix = ": ",
)
}
if (cause != null) {
append("; ")
append(cause.toString())
}
}
} else {
message
Expand All @@ -78,7 +83,7 @@ internal fun throwAndroidSqliteException(
SqliteErrno.SQLITE_NOMEM -> AndroidSqliteOutOfMemoryException(fullErMsg)
SqliteErrno.SQLITE_MISMATCH -> AndroidSqliteDatatypeMismatchException(fullErMsg)
SqliteErrno.SQLITE_INTERRUPT -> AndroidOperationCanceledException(fullErMsg)
else -> AndroidSqliteException(fullErMsg)
else -> AndroidSqliteException(fullErMsg, cause)
}
throw androidException
}
Loading

0 comments on commit 7f3c4d0

Please sign in to comment.