summaryrefslogtreecommitdiff
path: root/libs/samba/src/main/java/org
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2024-08-23 00:24:18 +0200
committerJoel Klinghed <the_jk@spawned.biz>2024-08-23 00:24:18 +0200
commit16d2b1750a78527a5524bfc3171a42f67c323508 (patch)
tree1defa7bb7f44a089103582b0777662394ef1ba56 /libs/samba/src/main/java/org
parenta95264b5273748330c3126632277fd7a0db8ec91 (diff)
samba: Add support for read/write file
Diffstat (limited to 'libs/samba/src/main/java/org')
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt51
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt3
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt93
3 files changed, 145 insertions, 2 deletions
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt
index 1863e22..c951d37 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt
@@ -27,6 +27,7 @@ internal object NativeSamba {
fun removeDir(path: String): Boolean
fun unlink(path: String): Boolean
fun readLink(path: String): String?
+ fun openFile(path: String, mode: OpenMode): File?
}
interface Url : Object {
@@ -53,6 +54,25 @@ internal object NativeSamba {
fun list(): Array<DirEntry>
}
+ enum class OpenMode(val value: Int) {
+ READ(0),
+ WRITE_CREATE_TRUNCATE(1),
+ }
+
+ enum class SeekWhence(val value: Int) {
+ SET(0),
+ CURRENT(1),
+ }
+
+ interface File : Object {
+ val path: String
+
+ fun read(bytes: ByteArray, offset: Int, length: Int): Int
+ fun seek(offset: Long, whence: SeekWhence): Long
+
+ fun write(bytes: ByteArray, offset: Int, length: Int): Int
+ }
+
private class NativeContext(private var ptr: Long): Context {
override fun destroy() {
if (ptr == 0L) return
@@ -97,6 +117,11 @@ internal object NativeSamba {
override fun readLink(path: String): String? {
return nativeContextReadLink(ptr, path)
}
+
+ override fun openFile(path: String, mode: NativeSamba.OpenMode): File? {
+ val file = nativeContextOpenFile(ptr, path, mode.value)
+ return if (file != 0L) NativeFile(path, file) else null
+ }
}
private class NativeUrl(private var ptr: Long): Url {
@@ -123,6 +148,26 @@ internal object NativeSamba {
}
}
+ private class NativeFile(override val path: String, private var ptr: Long): File {
+ override fun read(bytes: ByteArray, offset: Int, length: Int): Int {
+ return nativeFileRead(ptr, bytes, offset, length)
+ }
+
+ override fun seek(offset: Long, whence: SeekWhence): Long {
+ return nativeFileSeek(ptr, offset, whence.value)
+ }
+
+ override fun write(bytes: ByteArray, offset: Int, length: Int): Int {
+ return nativeFileWrite(ptr, bytes, offset, length)
+ }
+
+ override fun destroy() {
+ if (ptr == 0L) return
+ nativeFileDestroy(ptr)
+ ptr = 0L
+ }
+ }
+
init {
System.loadLibrary("samba")
}
@@ -154,10 +199,16 @@ internal object NativeSamba {
private external fun nativeContextRemoveDir(ptr: Long, path: String): Boolean
private external fun nativeContextUnlink(ptr: Long, path: String): Boolean
private external fun nativeContextReadLink(otr: Long, path: String): String?
+ private external fun nativeContextOpenFile(ptr: Long, path: String, mode: Int): Long
private external fun nativeUrlDestroy(ptr: Long)
private external fun nativeUrlPath(ptr: Long): String
private external fun nativeDirDestroy(ptr: Long)
private external fun nativeDirList(ptr: Long): Array<DirEntry>
+
+ private external fun nativeFileDestroy(ptr: Long)
+ private external fun nativeFileRead(ptr: Long, bytes: ByteArray, offset: Int, length: Int): Int
+ private external fun nativeFileSeek(ptr: Long, offset: Long, whence: Int): Long
+ private external fun nativeFileWrite(ptr: Long, bytes: ByteArray, offset: Int, length: Int): Int
}
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
index d7302cd..6eda092 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
@@ -40,6 +40,9 @@ internal class SambaConnection(uri: String, credentials: SambaCredentials) {
fun readLink(path: String): String? =
if (connected) context.readLink(join(url!!.path(), path)) else null
+ fun openFile(path: String, read: NativeSamba.OpenMode): NativeSamba.File? =
+ if (connected) context.openFile(join(url!!.path(), path), read) else null
+
companion object {
fun join(a: String, b: String): String {
if (a.isEmpty() || b.startsWith("/")) return b
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt
index 817c1bf..9913035 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt
@@ -15,11 +15,96 @@ internal class SambaFile(
private var cacheEndOfLife: Instant = Instant.now().plusSeconds(60),
) : ModifiableFile {
override fun write(): OutputStream {
- TODO("Not yet implemented")
+ val file = conn.openFile(path, NativeSamba.OpenMode.WRITE_CREATE_TRUNCATE)
+ ?: throw IOException(conn.error)
+ return object : OutputStream() {
+ override fun write(b: Int) {
+ val buffer = ByteArray(1)
+ buffer[0] = b.toByte()
+ if (file.write(buffer, 0, 1) != 1) throw IOException(conn.error)
+ }
+
+ override fun write(b: ByteArray?) = write(b, 0, b?.size ?: 0)
+
+ override fun write(b: ByteArray?, off: Int, len: Int) {
+ if (b == null) throw NullPointerException("b == null")
+ if (off < 0) throw IndexOutOfBoundsException("off < 0")
+ if (len < 0) throw java.lang.IndexOutOfBoundsException("len < 0")
+ if (off + len > b.size) throw IndexOutOfBoundsException("off + len > b.size")
+ if (file.write(b, off, len) != len) throw IOException(conn.error)
+ }
+
+ override fun flush() {
+ clearCache()
+ }
+
+ override fun close() {
+ file.destroy()
+ clearCache()
+ }
+ }
}
override fun read(): InputStream {
- TODO("Not yet implemented")
+ val file = conn.openFile(path, NativeSamba.OpenMode.READ) ?: throw IOException(conn.error)
+ return object : InputStream() {
+ private var markedPosition = 0L
+
+ override fun read(): Int {
+ val buffer = ByteArray(1)
+ val got = file.read(buffer, 0, 1)
+ if (got == 0) return -1
+ if (got < 0) throw IOException(conn.error)
+ return buffer[0].toInt()
+ }
+
+ override fun read(b: ByteArray?) = read(b, 0, b?.size ?: 0)
+
+ override fun read(b: ByteArray?, off: Int, len: Int): Int {
+ if (b == null) throw NullPointerException("b == null")
+ if (off < 0) throw IndexOutOfBoundsException("off < 0")
+ if (len < 0) throw java.lang.IndexOutOfBoundsException("len < 0")
+ if (off + len > b.size) throw IndexOutOfBoundsException("off + len > b.size")
+ if (len == 0) return 0
+ val got = file.read(b, off, len)
+ if (got == 0) return -1
+ if (got < 0) throw IOException(conn.error)
+ return got
+ }
+
+ override fun skip(n: Long): Long {
+ if (n <= 0) return 0
+ val offset = file.seek(n, NativeSamba.SeekWhence.CURRENT)
+ if (offset < 0) throw IOException(conn.error)
+ return offset - n
+ }
+
+ override fun available(): Int {
+ val current = file.seek(0, NativeSamba.SeekWhence.CURRENT)
+ if (current < 0) throw IOException(conn.error)
+ val total = size
+ if (current.toULong() >= total) return 0
+ val left = total - current.toULong()
+ if (left >= Int.MAX_VALUE.toULong()) return Int.MAX_VALUE
+ return left.toInt()
+ }
+
+ override fun close() {
+ file.destroy()
+ }
+
+ override fun mark(readlimit: Int) {
+ markedPosition = file.seek(0, NativeSamba.SeekWhence.CURRENT)
+ }
+
+ override fun markSupported() = true
+
+ override fun reset() {
+ if (file.seek(markedPosition, NativeSamba.SeekWhence.SET) != markedPosition) {
+ throw IOException(conn.error)
+ }
+ }
+ }
}
override val size: ULong get() {
@@ -37,4 +122,8 @@ internal class SambaFile(
private fun useCached(): Boolean {
return Instant.now().isBefore(cacheEndOfLife)
}
+
+ private fun clearCache() {
+ cacheEndOfLife = Instant.EPOCH
+ }
}