package sushi.hardcore.droidfs.filesystems import android.os.Parcel import android.util.Log import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.explorers.ExplorerElement import sushi.hardcore.droidfs.util.ObjRef import kotlin.math.min class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() { private external fun native_close(sessionID: Int) private external fun native_is_closed(sessionID: Int): Boolean private external fun native_list_dir(sessionID: Int, dir_path: String): MutableList? private external fun native_open_read_mode(sessionID: Int, file_path: String): Int private external fun native_open_write_mode(sessionID: Int, file_path: String, mode: Int): Int private external fun native_read_file(sessionID: Int, handleID: Int, fileOffset: Long, buff: ByteArray, dstOffset: Long, length: Int): Int private external fun native_write_file(sessionID: Int, handleID: Int, fileOffset: Long, buff: ByteArray, srcOffset: Long, length: Int): Int private external fun native_truncate(sessionID: Int, path: String, offset: Long): Boolean private external fun native_close_file(sessionID: Int, handleID: Int) private external fun native_remove_file(sessionID: Int, file_path: String): Boolean private external fun native_mkdir(sessionID: Int, dir_path: String, mode: Int): Boolean private external fun native_rmdir(sessionID: Int, dir_path: String): Boolean private external fun native_get_attr(sessionID: Int, file_path: String): Stat? private external fun native_rename(sessionID: Int, old_path: String, new_path: String): Boolean companion object { const val KeyLen = 32 private const val ScryptDefaultLogN = 16 private const val VOLUME_CREATOR = "DroidFS" private const val MAX_KERNEL_WRITE = 128*1024 const val CONFIG_FILE_NAME = "gocryptfs.conf" private external fun nativeCreateVolume( root_cipher_dir: String, password: ByteArray, plainTextNames: Boolean, xchacha: Int, logN: Int, creator: String, returnedHash: ByteArray?, ): Int private external fun nativeInit(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): Int external fun changePassword( root_cipher_dir: String, currentPassword: ByteArray?, givenHash: ByteArray?, newPassword: ByteArray, returnedHash: ByteArray? ): Boolean fun createAndOpenVolume( root_cipher_dir: String, password: ByteArray, plainTextNames: Boolean, xchacha: Int, returnedHash: ByteArray?, volume: ObjRef ): Boolean { return when (val result = nativeCreateVolume( root_cipher_dir, password, plainTextNames, xchacha, ScryptDefaultLogN, VOLUME_CREATOR, returnedHash, )) { -1 -> { Log.e("gocryptfs", "Failed to open volume after creation") true } -2 -> false else -> { volume.value = GocryptfsVolume(result) true } } } fun init(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): InitResult { val sessionId = nativeInit(root_cipher_dir, password, givenHash, returnedHash) val result = InitResult.Builder() if (sessionId < 0) { result.errorCode = sessionId result.errorStringId = when (sessionId) { -1 -> R.string.config_load_error -2 -> { result.worthRetry = true R.string.wrong_password } else -> 0 } } else { result.volume = GocryptfsVolume(sessionId) } return result.build() } init { System.loadLibrary("gocryptfs_jni") } } constructor(parcel: Parcel) : this(parcel.readInt()) override fun openFileReadMode(path: String): Long { return native_open_read_mode(sessionID, path).toLong() } override fun openFileWriteMode(path: String): Long { return native_open_write_mode(sessionID, path, 384).toLong() // 0600 } override fun read(fileHandle: Long, fileOffset: Long, buffer: ByteArray, dstOffset: Long, length: Long): Int { return native_read_file(sessionID, fileHandle.toInt(), fileOffset, buffer, dstOffset, min(length.toInt(), MAX_KERNEL_WRITE)) } override fun readDir(path: String): MutableList? { return native_list_dir(sessionID, path) } override fun getAttr(path: String): Stat? { return native_get_attr(sessionID, path) } override fun writeToParcel(parcel: Parcel, flags: Int) = with(parcel) { writeByte(GOCRYPTFS_VOLUME_TYPE) writeInt(sessionID) } override fun close() { native_close(sessionID) } override fun isClosed(): Boolean { return native_is_closed(sessionID) } override fun mkdir(path: String): Boolean { return native_mkdir(sessionID, path, 448) // 0700 } override fun rmdir(path: String): Boolean { return native_rmdir(sessionID, path) } override fun closeFile(fileHandle: Long): Boolean { native_close_file(sessionID, fileHandle.toInt()) return true } override fun write(fileHandle: Long, fileOffset: Long, buffer: ByteArray, srcOffset: Long, length: Long): Int { return native_write_file(sessionID, fileHandle.toInt(), fileOffset, buffer, srcOffset, min(length.toInt(), MAX_KERNEL_WRITE)) } override fun truncate(path: String, size: Long): Boolean { return native_truncate(sessionID, path, size) } override fun deleteFile(path: String): Boolean { return native_remove_file(sessionID, path) } override fun rename(srcPath: String, dstPath: String): Boolean { return native_rename(sessionID, srcPath, dstPath) } }