aboutsummaryrefslogtreecommitdiff
path: root/okio/src/unixMain/kotlin/okio
diff options
context:
space:
mode:
Diffstat (limited to 'okio/src/unixMain/kotlin/okio')
-rw-r--r--okio/src/unixMain/kotlin/okio/UnixFileHandle.kt98
-rw-r--r--okio/src/unixMain/kotlin/okio/UnixPosixVariant.kt206
2 files changed, 304 insertions, 0 deletions
diff --git a/okio/src/unixMain/kotlin/okio/UnixFileHandle.kt b/okio/src/unixMain/kotlin/okio/UnixFileHandle.kt
new file mode 100644
index 00000000..62e7849f
--- /dev/null
+++ b/okio/src/unixMain/kotlin/okio/UnixFileHandle.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package okio
+
+import kotlinx.cinterop.CPointer
+import kotlinx.cinterop.addressOf
+import kotlinx.cinterop.alloc
+import kotlinx.cinterop.memScoped
+import kotlinx.cinterop.ptr
+import kotlinx.cinterop.usePinned
+import platform.posix.FILE
+import platform.posix.errno
+import platform.posix.fclose
+import platform.posix.fflush
+import platform.posix.fileno
+import platform.posix.fstat
+import platform.posix.ftruncate
+import platform.posix.stat
+
+internal class UnixFileHandle(
+ readWrite: Boolean,
+ private val file: CPointer<FILE>,
+) : FileHandle(readWrite) {
+ override fun protectedSize(): Long {
+ memScoped {
+ val stat = alloc<stat>()
+ if (fstat(fileno(file), stat.ptr) != 0) {
+ throw errnoToIOException(errno)
+ }
+ return stat.st_size
+ }
+ }
+
+ override fun protectedRead(
+ fileOffset: Long,
+ array: ByteArray,
+ arrayOffset: Int,
+ byteCount: Int,
+ ): Int {
+ val bytesRead = if (array.isNotEmpty()) {
+ array.usePinned { pinned ->
+ variantPread(file, pinned.addressOf(arrayOffset), byteCount, fileOffset)
+ }
+ } else {
+ 0
+ }
+ if (bytesRead == -1) throw errnoToIOException(errno)
+ if (bytesRead == 0) return -1
+ return bytesRead
+ }
+
+ override fun protectedWrite(
+ fileOffset: Long,
+ array: ByteArray,
+ arrayOffset: Int,
+ byteCount: Int,
+ ) {
+ val bytesWritten = if (array.isNotEmpty()) {
+ array.usePinned { pinned ->
+ variantPwrite(file, pinned.addressOf(arrayOffset), byteCount, fileOffset)
+ }
+ } else {
+ 0
+ }
+ if (bytesWritten != byteCount) throw errnoToIOException(errno)
+ }
+
+ override fun protectedFlush() {
+ if (fflush(file) != 0) {
+ throw errnoToIOException(errno)
+ }
+ }
+
+ override fun protectedResize(size: Long) {
+ if (ftruncate(fileno(file), size) == -1) {
+ throw errnoToIOException(errno)
+ }
+ }
+
+ override fun protectedClose() {
+ if (fclose(file) != 0) {
+ throw errnoToIOException(errno)
+ }
+ }
+}
diff --git a/okio/src/unixMain/kotlin/okio/UnixPosixVariant.kt b/okio/src/unixMain/kotlin/okio/UnixPosixVariant.kt
new file mode 100644
index 00000000..27630d67
--- /dev/null
+++ b/okio/src/unixMain/kotlin/okio/UnixPosixVariant.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package okio
+
+import kotlinx.cinterop.ByteVar
+import kotlinx.cinterop.CPointer
+import kotlinx.cinterop.CValuesRef
+import kotlinx.cinterop.UnsafeNumber
+import kotlinx.cinterop.allocArray
+import kotlinx.cinterop.convert
+import kotlinx.cinterop.memScoped
+import kotlinx.cinterop.toKString
+import okio.Path.Companion.toPath
+import okio.internal.toPath
+import platform.posix.DEFFILEMODE
+import platform.posix.ENOENT
+import platform.posix.FILE
+import platform.posix.O_CREAT
+import platform.posix.O_EXCL
+import platform.posix.O_RDWR
+import platform.posix.PATH_MAX
+import platform.posix.S_IFLNK
+import platform.posix.S_IFMT
+import platform.posix.errno
+import platform.posix.fdopen
+import platform.posix.fileno
+import platform.posix.fopen
+import platform.posix.free
+import platform.posix.getenv
+import platform.posix.mkdir
+import platform.posix.open
+import platform.posix.pread
+import platform.posix.pwrite
+import platform.posix.readlink
+import platform.posix.realpath
+import platform.posix.remove
+import platform.posix.rename
+import platform.posix.stat
+import platform.posix.symlink
+import platform.posix.timespec
+
+internal actual val PLATFORM_TEMPORARY_DIRECTORY: Path
+ get() {
+ val tmpdir = getenv("TMPDIR")
+ if (tmpdir != null) return tmpdir.toKString().toPath()
+
+ return "/tmp".toPath()
+ }
+
+internal actual val PLATFORM_DIRECTORY_SEPARATOR = "/"
+
+internal actual fun PosixFileSystem.variantDelete(path: Path, mustExist: Boolean) {
+ val result = remove(path.toString())
+ if (result != 0) {
+ if (errno == ENOENT) {
+ if (mustExist) {
+ throw FileNotFoundException("no such file: $path")
+ } else {
+ return
+ }
+ }
+ throw errnoToIOException(errno)
+ }
+}
+
+@OptIn(UnsafeNumber::class)
+internal actual fun PosixFileSystem.variantMkdir(dir: Path): Int {
+ return mkdir(dir.toString(), 0b111111111u.convert() /* octal 777 */)
+}
+
+internal actual fun PosixFileSystem.variantCanonicalize(path: Path): Path {
+ // Note that realpath() fails if the file doesn't exist.
+ val fullpath = realpath(path.toString(), null)
+ ?: throw errnoToIOException(errno)
+ try {
+ return Buffer().writeNullTerminated(fullpath).toPath(normalize = true)
+ } finally {
+ free(fullpath)
+ }
+}
+
+internal actual fun PosixFileSystem.variantMove(
+ source: Path,
+ target: Path,
+) {
+ val result = rename(source.toString(), target.toString())
+ if (result != 0) {
+ throw errnoToIOException(errno)
+ }
+}
+
+internal actual fun PosixFileSystem.variantSource(file: Path): Source {
+ val openFile: CPointer<FILE> = fopen(file.toString(), "r")
+ ?: throw errnoToIOException(errno)
+ return FileSource(openFile)
+}
+
+internal actual fun PosixFileSystem.variantSink(file: Path, mustCreate: Boolean): Sink {
+ val openFile: CPointer<FILE> = fopen(file.toString(), if (mustCreate) "wx" else "w")
+ ?: throw errnoToIOException(errno)
+ return FileSink(openFile)
+}
+
+internal actual fun PosixFileSystem.variantAppendingSink(file: Path, mustExist: Boolean): Sink {
+ // There is a `r+` flag which we could have used to force existence of [file] but this flag
+ // doesn't allow opening for appending, and we don't currently have a way to move the cursor to
+ // the end of the file. We are then forcing existence non-atomically.
+ if (mustExist && !exists(file)) throw IOException("$file doesn't exist.")
+ val openFile: CPointer<FILE> = fopen(file.toString(), "a")
+ ?: throw errnoToIOException(errno)
+ return FileSink(openFile)
+}
+
+internal actual fun PosixFileSystem.variantOpenReadOnly(file: Path): FileHandle {
+ val openFile: CPointer<FILE> = fopen(file.toString(), "r")
+ ?: throw errnoToIOException(errno)
+ return UnixFileHandle(false, openFile)
+}
+
+internal actual fun PosixFileSystem.variantOpenReadWrite(
+ file: Path,
+ mustCreate: Boolean,
+ mustExist: Boolean,
+): FileHandle {
+ // Note that we're using open() followed by fdopen() rather than fopen() because this way we
+ // can pass exactly the flags we want. Note that there's no string mode that opens for reading
+ // and writing that creates if necessary. ("a+" has features but can't do random access).
+ val flags = when {
+ mustCreate && mustExist ->
+ throw IllegalArgumentException("Cannot require mustCreate and mustExist at the same time.")
+ mustCreate -> O_RDWR or O_CREAT or O_EXCL
+ mustExist -> O_RDWR
+ else -> O_RDWR or O_CREAT
+ }
+
+ val fid = open(file.toString(), flags, DEFFILEMODE)
+ if (fid == -1) throw errnoToIOException(errno)
+
+ // Use 'r+' to get reading and writing on the FILE, which is all we need.
+ val openFile = fdopen(fid, "r+") ?: throw errnoToIOException(errno)
+
+ return UnixFileHandle(true, openFile)
+}
+
+internal actual fun PosixFileSystem.variantCreateSymlink(source: Path, target: Path) {
+ if (source.parent == null || !exists(source.parent!!)) {
+ throw IOException("parent directory does not exist: ${source.parent}")
+ }
+
+ if (exists(source)) {
+ throw IOException("already exists: $source")
+ }
+
+ val result = symlink(target.toString(), source.toString())
+ if (result != 0) {
+ throw errnoToIOException(errno)
+ }
+}
+
+@OptIn(UnsafeNumber::class)
+internal fun variantPread(
+ file: CPointer<FILE>,
+ target: CValuesRef<*>,
+ byteCount: Int,
+ offset: Long,
+): Int = pread(fileno(file), target, byteCount.convert(), offset).convert()
+
+@OptIn(UnsafeNumber::class)
+internal fun variantPwrite(
+ file: CPointer<FILE>,
+ source: CValuesRef<*>,
+ byteCount: Int,
+ offset: Long,
+): Int = pwrite(fileno(file), source, byteCount.convert(), offset).convert()
+
+@OptIn(UnsafeNumber::class)
+internal val timespec.epochMillis: Long
+ get() = tv_sec * 1000L + tv_sec / 1_000_000L
+
+@OptIn(UnsafeNumber::class)
+internal fun symlinkTarget(stat: stat, path: Path): Path? {
+ if (stat.st_mode.toInt() and S_IFMT != S_IFLNK) return null
+
+ // `path` is a symlink, let's resolve its target.
+ memScoped {
+ val buffer = allocArray<ByteVar>(PATH_MAX)
+ val byteCount = readlink(path.toString(), buffer, PATH_MAX.convert())
+ if (byteCount.convert<Int>() == -1) {
+ throw errnoToIOException(errno)
+ }
+ return buffer.toKString().toPath()
+ }
+}