All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jtransc.vfs.tar.kt Maven / Gradle / Ivy

package com.jtransc.vfs

import com.jtransc.error.noImpl
import com.jtransc.io.stringz
import com.jtransc.io.stringzTrim
import com.jtransc.numeric.nextMultipleOf
import com.jtransc.numeric.toInt
import com.jtransc.vfs.node.FileNodeIO
import com.jtransc.vfs.node.FileNodeTree
import com.jtransc.vfs.node.FileNodeType
import java.io.ByteArrayInputStream
import java.io.File
import java.util.*

fun TarVfs(file: File): SyncVfsFile = TarSyncVfs(file.readBytes()).root()
fun TarVfs(file: ByteArray): SyncVfsFile = TarSyncVfs(file).root()

private class TarSyncVfs(val tarData: ByteArray) : BaseTreeVfs(FileNodeTree()) {
	var currentOffset = 0

	private fun readOne(): Boolean {
		val nodeHeaderOffset = currentOffset
		val header = ByteArrayInputStream(tarData, currentOffset, 0x200)
		val fileName = header.stringzTrim(100)
		if (fileName.isEmpty()) return false

		val fileMode = header.stringzTrim(8)
		val ownerNumeric = header.stringzTrim(8).toInt(8, 0)
		val ownerGroup = header.stringzTrim(8).toInt(8, 0)
		val fileSize = header.stringzTrim(12)
		val lastModification = header.stringzTrim(12).toInt(8, 0)
		val checksum = header.stringzTrim(8)
		val linkIndicator = header.stringzTrim(1)
		val nameOfLinkedFile = header.stringzTrim(100)

		val fileSizeLong = java.lang.Long.parseLong(fileSize, 8)

		val isSymLink = nameOfLinkedFile.isNotEmpty()

		//0	100	File name
		//100	8	File mode
		//108	8	Owner's numeric user ID
		//116	8	Group's numeric user ID
		//124	12	File size in bytes (octal base)
		//136	12	Last modification time in numeric Unix time format (octal)
		//148	8	Checksum for header record
		//156	1	Link indicator (file type)
		//157	100	Name of linked file

		//0	156	(several fields, same as in old format)
		//156	1	Type flag
		//157	100	(same field as in old format)
		//257	6	UStar indicator "ustar" then NUL
		//263	2	UStar version "00"
		//265	32	Owner user name
		//297	32	Owner group name
		//329	8	Device major number
		//337	8	Device minor number
		//345	155	Filename prefix

		if (fileName.isNotEmpty()) {
			//println("$fileName : $fileMode : $fileSize")
			//println("$fileName -> $nameOfLinkedFile")

			val node = tree.root[fileName, true]
			node.type = if (fileName.endsWith("/")) {
				FileNodeType.DIRECTORY
			} else if (isSymLink) {
				FileNodeType.SYMLINK
			} else {
				FileNodeType.FILE
			}

			fun readBytes() = Arrays.copyOfRange(tarData, nodeHeaderOffset + 0x200, nodeHeaderOffset + 0x200 + fileSizeLong.toInt())

			node.io = object : FileNodeIO() {
				override fun readLink(): String? = nameOfLinkedFile
				override fun read(): ByteArray {
					return if (isSymLink) {
						try {
							[email protected]()[fileName].parent[nameOfLinkedFile].read()
						} catch (t:Throwable) {
							readBytes()
						}
					} else {
						readBytes()
					}
				}
				override fun write(data: ByteArray) = noImpl("Writting not implemented on tar files")
				override fun size(): Long = fileSizeLong
				override fun mtime(): Date = Date(lastModification.toLong() * 1000L)
				override fun mode(): FileMode = FileMode.fromOctal(fileMode)
			}
		}
		currentOffset = (currentOffset + 0x200 + fileSizeLong.toInt()).nextMultipleOf(0x200)
		return true
	}

	init {
		while (currentOffset < tarData.size) {
			if (!readOne()) break
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy