From 7cdfc32c31b210111b55e8ace40f115a08afc602 Mon Sep 17 00:00:00 2001 From: Hardcore Sushi Date: Fri, 23 Sep 2022 20:58:16 +0200 Subject: [PATCH] Direct encrypted files read/write & More compliant EncryptedVolumeDataSource --- app/libcryfs | 2 +- .../sushi/hardcore/droidfs/CameraActivity.kt | 2 +- .../sushi/hardcore/droidfs/ConstValues.kt | 1 - .../file_operations/FileOperationService.kt | 8 ++-- .../file_viewers/EncryptedVolumeDataSource.kt | 45 ++++++++++--------- .../droidfs/file_viewers/TextEditor.kt | 13 +----- .../droidfs/filesystems/CryfsVolume.kt | 12 ++--- .../droidfs/filesystems/EncryptedVolume.kt | 21 ++++----- .../droidfs/filesystems/GocryptfsVolume.kt | 14 +++--- app/src/main/native/gocryptfs_jni.c | 17 ++++--- app/src/main/native/libcryfs.c | 20 ++++++--- 11 files changed, 73 insertions(+), 82 deletions(-) diff --git a/app/libcryfs b/app/libcryfs index c0600c2..dc82772 160000 --- a/app/libcryfs +++ b/app/libcryfs @@ -1 +1 @@ -Subproject commit c0600c262480f6fb3d31e2b500ba04714f1ff342 +Subproject commit dc82772e5eb8063bace8415193eebc52cedcec64 diff --git a/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt index ec6bee8..5c8d34c 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt @@ -459,7 +459,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { videoCapture?.startRecording(VideoCapture.OutputFileOptions(object : SeekableWriter { var offset = 0L override fun write(byteArray: ByteArray) { - offset += encryptedVolume.write(fileHandle, offset, byteArray, byteArray.size) + offset += encryptedVolume.write(fileHandle, offset, byteArray, 0, byteArray.size.toLong()) } override fun seek(offset: Long) { this.offset = offset diff --git a/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt b/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt index ece664b..8924e01 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt @@ -9,7 +9,6 @@ object ConstValues { const val CRYFS_LOCAL_STATE_DIR = "cryfsLocalState" const val SORT_ORDER_KEY = "sort_order" val FAKE_URI: Uri = Uri.parse("fakeuri://droidfs") - const val MAX_KERNEL_WRITE = 128*1024 const val WIPE_PASSES = 2 const val IO_BUFF_SIZE = 16384 const val SLIDESHOW_DELAY: Long = 4000 diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_operations/FileOperationService.kt b/app/src/main/java/sushi/hardcore/droidfs/file_operations/FileOperationService.kt index bae0b3d..66a8548 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_operations/FileOperationService.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_operations/FileOperationService.kt @@ -131,10 +131,10 @@ class FileOperationService : Service() { if (dstFileHandle != -1L) { var offset: Long = 0 val ioBuffer = ByteArray(ConstValues.IO_BUFF_SIZE) - var length: Int - while (remoteEncryptedVolume.read(srcFileHandle, ioBuffer, offset).also { length = it } > 0) { - val written = encryptedVolume.write(dstFileHandle, offset, ioBuffer, length).toLong() - if (written == length.toLong()) { + var length: Long + while (remoteEncryptedVolume.read(srcFileHandle, offset, ioBuffer, 0, ioBuffer.size.toLong()).also { length = it.toLong() } > 0) { + val written = encryptedVolume.write(dstFileHandle, offset, ioBuffer, 0, length).toLong() + if (written == length) { offset += written } else { success = false diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/EncryptedVolumeDataSource.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/EncryptedVolumeDataSource.kt index 0827f2a..859694a 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/EncryptedVolumeDataSource.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/EncryptedVolumeDataSource.kt @@ -1,23 +1,29 @@ package sushi.hardcore.droidfs.file_viewers import android.net.Uri +import com.google.android.exoplayer2.C import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DataSpec import com.google.android.exoplayer2.upstream.TransferListener import sushi.hardcore.droidfs.ConstValues import sushi.hardcore.droidfs.filesystems.EncryptedVolume -import kotlin.math.ceil import kotlin.math.min class EncryptedVolumeDataSource(private val encryptedVolume: EncryptedVolume, private val filePath: String): DataSource { private var fileHandle = -1L - private var fileSize: Long = -1 private var fileOffset: Long = 0 + private var bytesRemaining: Long = -1 + override fun open(dataSpec: DataSpec): Long { - fileOffset = dataSpec.position fileHandle = encryptedVolume.openFile(filePath) - fileSize = encryptedVolume.getAttr(filePath)!!.size - return fileSize + fileOffset = dataSpec.position + val fileSize = encryptedVolume.getAttr(filePath)!!.size + bytesRemaining = if (dataSpec.length == C.LENGTH_UNSET.toLong()) { + fileSize - fileOffset + } else { + min(fileSize, dataSpec.length) + } + return bytesRemaining } override fun getUri(): Uri { @@ -33,23 +39,18 @@ class EncryptedVolumeDataSource(private val encryptedVolume: EncryptedVolume, pr } override fun read(buffer: ByteArray, offset: Int, readLength: Int): Int { - if (fileOffset >= fileSize){ - return -1 - } - var totalRead = 0 - for (i in 0 until ceil(readLength.toDouble()/ConstValues.MAX_KERNEL_WRITE).toInt()){ - val tmpReadLength = min(readLength-totalRead, ConstValues.MAX_KERNEL_WRITE) - val tmpBuff = if (fileOffset+tmpReadLength > fileSize){ - ByteArray((fileSize-fileOffset).toInt()) - } else { - ByteArray(tmpReadLength) - } - val read = encryptedVolume.read(fileHandle, tmpBuff, fileOffset) - System.arraycopy(tmpBuff, 0, buffer, offset+totalRead, read) - fileOffset += read - totalRead += read - } - return totalRead + val originalOffset = fileOffset + while (fileOffset < originalOffset+readLength && encryptedVolume.read( + fileHandle, + fileOffset, + buffer, + offset+(fileOffset-originalOffset), + (originalOffset+readLength)-fileOffset + ).also { fileOffset += it } > 0 + ) {} + val totalRead = fileOffset-originalOffset + bytesRemaining -= totalRead + return totalRead.toInt() } class Factory(private val encryptedVolume: EncryptedVolume, private val filePath: String): DataSource.Factory { diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt index a875ba0..483c621 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt @@ -70,23 +70,12 @@ class TextEditor: FileViewerActivity() { val content = editor.text.toString().toByteArray() val fileHandle = encryptedVolume.openFile(filePath) if (fileHandle != -1L) { - val buff = ByteArrayInputStream(content) var offset: Long = 0 - val ioBuffer = ByteArray(ConstValues.IO_BUFF_SIZE) - var length: Int - while (buff.read(ioBuffer).also { length = it } > 0) { - val written = encryptedVolume.write(fileHandle, offset, ioBuffer, length).toLong() - if (written == length.toLong()) { - offset += written - } else { - break - } - } + while (offset < content.size && encryptedVolume.write(fileHandle, offset, content, offset, content.size.toLong()).also { offset += it } > 0) {} if (offset == content.size.toLong()){ success = encryptedVolume.truncate(filePath, offset) } encryptedVolume.closeFile(fileHandle) - buff.close() } if (success){ Toast.makeText(this, getString(R.string.file_saved), Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/sushi/hardcore/droidfs/filesystems/CryfsVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/filesystems/CryfsVolume.kt index 6f408f2..249cabc 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/filesystems/CryfsVolume.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/filesystems/CryfsVolume.kt @@ -33,8 +33,8 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() { ): Boolean private external fun nativeCreate(fusePtr: Long, path: String, mode: Int): Long private external fun nativeOpen(fusePtr: Long, path: String, flags: Int): Long - private external fun nativeRead(fusePtr: Long, fileHandle: Long, buffer: ByteArray, offset: Long): Int - private external fun nativeWrite(fusePtr: Long, fileHandle: Long, offset: Long, buffer: ByteArray, size: Int): Int + private external fun nativeRead(fusePtr: Long, fileHandle: Long, fileOffset: Long, buffer: ByteArray, dstOffset: Long, length: Long): Int + private external fun nativeWrite(fusePtr: Long, fileHandle: Long, fileOffset: Long, buffer: ByteArray, srcOffset: Long, length: Long): Int private external fun nativeTruncate(fusePtr: Long, path: String, size: Long): Boolean private external fun nativeDeleteFile(fusePtr: Long, path: String): Boolean private external fun nativeCloseFile(fusePtr: Long, fileHandle: Long): Boolean @@ -101,12 +101,12 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() { } } - override fun read(fileHandle: Long, buffer: ByteArray, offset: Long): Int { - return nativeRead(fusePtr, fileHandle, buffer, offset) + override fun read(fileHandle: Long, fileOffset: Long, buffer: ByteArray, dstOffset: Long, length: Long): Int { + return nativeRead(fusePtr, fileHandle, fileOffset, buffer, dstOffset, length) } - override fun write(fileHandle: Long, offset: Long, buffer: ByteArray, size: Int): Int { - return nativeWrite(fusePtr, fileHandle, offset, buffer, size) + override fun write(fileHandle: Long, fileOffset: Long, buffer: ByteArray, srcOffset: Long, length: Long): Int { + return nativeWrite(fusePtr, fileHandle, fileOffset, buffer, srcOffset, length) } override fun truncate(path: String, size: Long): Boolean { diff --git a/app/src/main/java/sushi/hardcore/droidfs/filesystems/EncryptedVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/filesystems/EncryptedVolume.kt index 49a7af1..19eb99d 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/filesystems/EncryptedVolume.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/filesystems/EncryptedVolume.kt @@ -74,8 +74,8 @@ abstract class EncryptedVolume: Parcelable { override fun describeContents() = 0 abstract fun openFile(path: String): Long - abstract fun read(fileHandle: Long, buffer: ByteArray, offset: Long): Int - abstract fun write(fileHandle: Long, offset: Long, buffer: ByteArray, size: Int): Int + abstract fun read(fileHandle: Long, fileOffset: Long, buffer: ByteArray, dstOffset: Long, length: Long): Int + abstract fun write(fileHandle: Long, fileOffset: Long, buffer: ByteArray, srcOffset: Long, length: Long): Int abstract fun closeFile(fileHandle: Long): Boolean // Due to gocryptfs internals, truncate requires the file to be open before it is called abstract fun truncate(path: String, size: Long): Boolean @@ -96,7 +96,7 @@ abstract class EncryptedVolume: Parcelable { var offset: Long = 0 val ioBuffer = ByteArray(ConstValues.IO_BUFF_SIZE) var length: Int - while (read(fileHandle, ioBuffer, offset).also { length = it } > 0) { + while (read(fileHandle, offset, ioBuffer, 0, ioBuffer.size.toLong()).also { length = it } > 0) { os.write(ioBuffer, 0, length) offset += length.toLong() } @@ -132,10 +132,10 @@ abstract class EncryptedVolume: Parcelable { var success = true var offset: Long = 0 val ioBuffer = ByteArray(ConstValues.IO_BUFF_SIZE) - var length: Int - while (inputStream.read(ioBuffer).also { length = it } > 0) { - val written = write(dstfileHandle, offset, ioBuffer, length).toLong() - if (written == length.toLong()) { + var length: Long + while (inputStream.read(ioBuffer).also { length = it.toLong() } > 0) { + val written = write(dstfileHandle, offset, ioBuffer, 0, length).toLong() + if (written == length) { offset += written } else { inputStream.close() @@ -174,12 +174,7 @@ abstract class EncryptedVolume: Parcelable { Pair(null, 3) } else { var offset: Long = 0 - val ioBuffer = ByteArray(ConstValues.IO_BUFF_SIZE) - var length: Int - while (read(fileHandle, ioBuffer, offset).also { length = it } > 0) { - System.arraycopy(ioBuffer, 0, fileBuff, offset.toInt(), length) - offset += length.toLong() - } + while (offset < fileSize && read(fileHandle, offset, fileBuff, offset, fileSize-offset).also { offset += it } > 0) {} closeFile(fileHandle) if (offset == fileBuff.size.toLong()) { Pair(fileBuff, 0) diff --git a/app/src/main/java/sushi/hardcore/droidfs/filesystems/GocryptfsVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/filesystems/GocryptfsVolume.kt index aa7e67d..c5c58f3 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/filesystems/GocryptfsVolume.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/filesystems/GocryptfsVolume.kt @@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.filesystems import android.os.Parcel import sushi.hardcore.droidfs.explorers.ExplorerElement +import kotlin.math.min class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() { private external fun native_close(sessionID: Int) @@ -9,8 +10,8 @@ class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() { 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, offset: Long, buff: ByteArray): Int - private external fun native_write_file(sessionID: Int, handleID: Int, offset: Long, buff: ByteArray, buff_size: 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 @@ -22,6 +23,7 @@ class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() { companion object { const val KeyLen = 32 const val ScryptDefaultLogN = 16 + const val MAX_KERNEL_WRITE = 128*1024 const val CONFIG_FILE_NAME = "gocryptfs.conf" external fun createVolume(root_cipher_dir: String, password: ByteArray, plainTextNames: Boolean, xchacha: Int, logN: Int, creator: String, returnedHash: ByteArray?): Boolean private external fun nativeInit(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): Int @@ -53,8 +55,8 @@ class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() { return native_open_write_mode(sessionID, path, 0).toLong() } - override fun read(fileHandle: Long, buffer: ByteArray, offset: Long): Int { - return native_read_file(sessionID, fileHandle.toInt(), offset, buffer) + 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? { @@ -91,8 +93,8 @@ class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() { return true } - override fun write(fileHandle: Long, offset: Long, buffer: ByteArray, size: Int): Int { - return native_write_file(sessionID, fileHandle.toInt(), offset, buffer, size) + 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 { diff --git a/app/src/main/native/gocryptfs_jni.c b/app/src/main/native/gocryptfs_jni.c index d834158..dde718c 100644 --- a/app/src/main/native/gocryptfs_jni.c +++ b/app/src/main/native/gocryptfs_jni.c @@ -293,12 +293,12 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1open_1write_1mod JNIEXPORT jint JNICALL Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1write_1file(JNIEnv *env, jobject thiz, - jint sessionID, jint handleID, jlong offset, - jbyteArray jbuff, jint buff_size) { + jint sessionID, jint handleID, jlong file_offset, + jbyteArray jbuff, jlong src_offset, jint length) { jbyte* buff = (*env)->GetByteArrayElements(env, jbuff, NULL); - GoSlice go_buff = {buff, buff_size, buff_size}; + GoSlice go_buff = {buff+src_offset, length, length}; - int written = gcf_write_file(sessionID, handleID, offset, go_buff); + int written = gcf_write_file(sessionID, handleID, file_offset, go_buff); (*env)->ReleaseByteArrayElements(env, jbuff, buff, 0); @@ -307,13 +307,12 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1write_1file(JNIE JNIEXPORT jint JNICALL Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1read_1file(JNIEnv *env, jobject thiz, - jint sessionID, jint handleID, jlong offset, - jbyteArray jbuff) { - const size_t buff_size = (*env)->GetArrayLength(env, jbuff); + jint sessionID, jint handleID, jlong file_offset, + jbyteArray jbuff, jlong dst_offset, jint length) { jbyte* buff = (*env)->GetByteArrayElements(env, jbuff, NULL); - GoSlice go_buff = {buff, buff_size, buff_size}; + GoSlice go_buff = {buff+dst_offset, length, length}; - int read = gcf_read_file(sessionID, handleID, offset, go_buff); + int read = gcf_read_file(sessionID, handleID, file_offset, go_buff); (*env)->ReleaseByteArrayElements(env, jbuff, buff, 0); return read; diff --git a/app/src/main/native/libcryfs.c b/app/src/main/native/libcryfs.c index 9239e81..b765118 100644 --- a/app/src/main/native/libcryfs.c +++ b/app/src/main/native/libcryfs.c @@ -35,10 +35,15 @@ Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeOpen(JN } JNIEXPORT jint JNICALL -Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeRead(JNIEnv *env, jobject thiz, - jlong fuse_ptr, jlong file_handle, - jbyteArray buffer, jlong offset) { - return cryfs_read(env, fuse_ptr, file_handle, buffer, offset); +Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeRead(JNIEnv *env, + jobject thiz, + jlong fuse_ptr, + jlong file_handle, + jlong file_offset, + jbyteArray buffer, + jlong dst_offset, + jlong length) { + return cryfs_read(env, fuse_ptr, file_handle, file_offset, buffer, dst_offset, length); } JNIEXPORT jint JNICALL @@ -46,10 +51,11 @@ Java_sushi_hardcore_droidfs_filesystems_CryfsVolume_00024Companion_nativeWrite(J jobject thiz, jlong fuse_ptr, jlong file_handle, - jlong offset, + jlong file_offset, jbyteArray buffer, - jint size) { - return cryfs_write(env, fuse_ptr, file_handle, offset, buffer, size); + jlong src_offset, + jlong length) { + return cryfs_write(env, fuse_ptr, file_handle, file_offset, buffer, src_offset, length); } JNIEXPORT jboolean JNICALL