X-Git-Url: https://git.distorted.org.uk/~mdw/tripe-android/blobdiff_plain/7894831e9078211df0b460c4d3dd1bc51ca46804..8eabb4ff13562f3550499ee599297f7e97fa8754:/jni.scala diff --git a/jni.scala b/jni.scala new file mode 100644 index 0000000..ea6ae76 --- /dev/null +++ b/jni.scala @@ -0,0 +1,246 @@ +/* -*-java-*- + * + * Declarations of C functions + * + * (c) 2018 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Trivial IP Encryption (TrIPE) Android app. + * + * TrIPE is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * TrIPE is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with TrIPE. If not, see . + */ + +package uk.org.distorted.tripe; package object jni { + +/*----- Imports -----------------------------------------------------------*/ + +import java.io.{Closeable, File}; +import java.util.Date; +import Magic._; + +/*----- Main code ---------------------------------------------------------*/ + +/* Import the native code library. */ +System.loadLibrary("toy"); + +/* Exception indicating that a wrapped native object has been clobbered. */ +class NativeObjectTypeException(msg: String) extends RuntimeException(msg); +type Wrapper = Array[Byte]; + +case class ErrorEntry(val tag: String, val err: Int); +@native def errtab: Array[ErrorEntry]; +@native def strerror(err: Int): CString; + +@native def hashsz(hash: String): Int; + /* Return the output hash size for the named HASH function, or -1. */ + +/* Flags for `close'. */ +val CF_CLOSERD = 1; +val CF_CLOSEWR = 2; +val CF_CLOSEMASK = CF_CLOSERD | CF_CLOSEWR; + +/* Flags for `lock'. */ +val LKF_EXCL = 1; +val LKF_WAIT = 2; + +/* Flags for `stat'. */ +val S_IFMT = 0xf000; +val S_IFIFO = 0x1000; +val S_IFCHR = 0x2000; +val S_IFDIR = 0x4000; +val S_IFBLK = 0x6000; +val S_IFREG = 0x8000; +val S_IFLNK = 0xa000; +val S_IFSOCK = 0xc000; + +object FileType extends Enumeration { + val FIFO, CHR, DIR, BLK, REG, LNK, SOCK, UNK = Value; +} +import FileType.{Value => _, _}; + +class FileInfo private[this](val devMajor: Int, val devMinor: Int, + val ino: Long, val mode: Int, val nlink: Int, + val uid: Int, val gid: Int, + _rdevMinor: Int, _rdevMajor: Int, + val size: Long, + val blksize: Int, val blocks: Long, + val atime: Date, val mtime: Date, + val ctime: Date) { + def this(devMajor: Int, devMinor: Int, ino: Long, + mode: Int, nlink: Int, uid: Int, gid: Int, + rdevMinor: Int, rdevMajor: Int, + size: Long, blksize: Int, blocks: Long, + atime: Long, mtime: Long, ctime: Long) { + this(devMajor, devMinor, ino, mode, nlink, uid, gid, + rdevMajor, rdevMinor, size, blksize, blocks, + new Date(atime), new Date(mtime), new Date(ctime)); + } + def perms: Int = mode&0xfff; + def ftype: FileType.Value = (mode&S_IFMT) match { + case S_IFIFO => FIFO + case S_IFCHR => CHR + case S_IFDIR => DIR + case S_IFBLK => BLK + case S_IFREG => REG + case S_IFLNK => LNK + case S_IFSOCK => SOCK + case _ => UNK + } + def isfifo: Boolean = ftype == FIFO + def ischr: Boolean = ftype == CHR + def isdir: Boolean = ftype == DIR + def isblk: Boolean = ftype == BLK + def isreg: Boolean = ftype == REG + def islnk: Boolean = ftype == LNK + def issock: Boolean = ftype == SOCK + def isdev: Boolean = ischr || isblk; + private[this] def mustBeDevice() { + if (!isdev) throw new IllegalArgumentException("Object is not a device"); + } + def rdevMajor: Int = { mustBeDevice(); _rdevMajor } + def rdevMinor: Int = { mustBeDevice(); _rdevMinor } +} +@native protected def unlink(path: CString); +def unlink(path: String) { unlink(path.toCString); } +def unlink(file: File) { unlink(file.getPath); } +@native protected def rmdir(path: CString); +def rmdir(path: String) { rmdir(path.toCString); } +def rmdir(file: File) { rmdir(file.getPath); } +@native protected def mkdir(path: CString, mode: Int); +def mkdir(path: String, mode: Int) { mkdir(path.toCString, mode); } +def mkdir(path: String) { mkdir(path, 0x1ff); } +def mkdir(file: File, mode: Int) { mkdir(file.getPath, mode); } +def mkdir(file: File) { mkdir(file.getPath); } +@native protected def mkfile(path: CString, mode: Int); +def mkfile(path: String, mode: Int) { mkfile(path.toCString, mode); } +def mkfile(path: String) { mkfile(path, 0x1b6); } +def mkfile(file: File, mode: Int) { mkfile(file.getPath, mode); } +def mkfile(file: File) { mkfile(file.getPath); } +@native protected def rename(from: CString, to: CString); +def rename(from: String, to: String) + { rename(from.toCString, to.toCString); } +def rename(from: File, to: File) + { rename(from.getPath, to.getPath); } +@native protected def stat(path: CString): FileInfo; +def stat(path: String): FileInfo = stat(path.toCString); +def stat(file: File): FileInfo = stat(file.getPath); +@native protected def lstat(path: CString): FileInfo; +def lstat(path: String): FileInfo = lstat(path.toCString); +def lstat(file: File): FileInfo = lstat(file.getPath); + +@native protected def opendir(path: CString): Wrapper; +@native protected def readdir(path: CString, dir: Wrapper): CString; +@native protected def closedir(path: CString, dir: Wrapper); + +abstract class BaseDirIterator[T](cpath: CString) + extends LookaheadIterator[T] with Closeable { + def this(path: String) { this(path.toCString); } + def this(dir: File) { this(dir.getPath); } + override def close() { closedir(cpath, dir); } + override protected def finalize() { super.finalize(); close(); } + private[this] val dir = opendir(cpath); + protected def mangle(file: String): T; + override protected def fetch(): Option[T] = readdir(cpath, dir) match { + case null => None + case f => f.toJString match { + case "." | ".." => fetch() + case jf => Some(mangle(jf)) + } + } +} + +class DirIterator(val path: String) extends BaseDirIterator[String](path) { + def this(dir: File) { this(dir.getPath); } + override protected def mangle(file: String): String = file; +} +def listDir(path: String): List[String] = { + val iter = new DirIterator(path); + try { iter.toList } + finally { iter.close(); } +} +def listDir(dir: File): List[String] = listDir(dir.getPath); + +class DirFilesIterator private[this](val dir: File, cpath: CString) + extends BaseDirIterator[File](cpath) { + def this(dir: File) { this(dir, dir.getPath.toCString); } + def this(path: String) { this(new File(path), path.toCString); } + override protected def mangle(file: String): File = new File(dir, file); +} +def listDirFiles(path: String): List[File] = { + val iter = new DirFilesIterator(path); + try { iter.toList } + finally { iter.close(); } +} +def listDirFiles(dir: File): List[File] = listDirFiles(dir.getPath); + +@native protected def lock(path: CString, flags: Int): Wrapper; +@native protected def unlock(lock: Wrapper); +class FileLock(path: String, flags: Int) extends Closeable { + def this(file: File, flags: Int) { this(file.getPath, flags); } + def this(path: String) { this(path, LKF_EXCL); } + def this(file: File) { this(file.getPath, LKF_EXCL); } + private[this] val lk = lock(path.toCString, flags); + override def close() { unlock(lk); } + override protected def finalize() { super.finalize(); close(); } +} +def withLock[T](path: String, flags: Int)(body: => T): T = { + val lk = new FileLock(path, flags); + try { body; } finally { lk.close(); } +} +def withLock[T](file: File, flags: Int)(body: => T): T = + withLock(file.getPath, flags) { body } +def withLock[T](path: String)(body: => T): T = + withLock(path, LKF_EXCL) { body } +def withLock[T](file: File)(body: => T): T = + withLock(file.getPath, LKF_EXCL) { body } + +@native protected def connect(path: CString): Wrapper; +@native def send(conn: Wrapper, buf: CString, + start: Int, len: Int); +@native def recv(conn: Wrapper, buf: CString, + start: Int, len: Int): Int; +@native def close(conn: Wrapper, how: Int); +class Connection(path: String) extends Closeable { + def this(file: File) { this(file.getPath); } + private[this] val conn = connect(path.toCString); + override def close() { jni.close(conn, CF_CLOSEMASK); } + override protected def finalize() { super.finalize(); close(); } + class InputStream private[Connection] extends java.io.InputStream { + override def read(): Int = { + val buf = new Array[Byte](1); + val n = read(buf, 0, 1); + if (n < 0) -1 else buf(0)&0xff; + } + override def read(buf: Array[Byte]): Int = + read(buf, 0, buf.length); + override def read(buf: Array[Byte], start: Int, len: Int) = + recv(conn, buf, start, len); + override def close() { jni.close(conn, CF_CLOSERD); } + } + lazy val input = new InputStream; + class OutputStream private[Connection] extends java.io.OutputStream { + override def write(b: Int) { write(Array[Byte](b.toByte), 0, 1); } + override def write(buf: Array[Byte]) { write(buf, 0, buf.length); } + override def write(buf: Array[Byte], start: Int, len: Int) + { send(conn, buf, start, len); } + override def close() { jni.close(conn, CF_CLOSEWR); } + } + lazy val output = new OutputStream; +} + +/*----- That's all, folks -------------------------------------------------*/ + +}