From 4ec936af3ce5b6dd3d21ee1c3fac48c91e3bf4b7 Mon Sep 17 00:00:00 2001 From: mstoeck3 Date: Sun, 7 Dec 2025 16:02:57 +0100 Subject: [PATCH] ext update --- webroot/js/filesystems/ext.js | 258 ++++++++++++++++++++++++++++++++++ webroot/js/main.js | 4 +- 2 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 webroot/js/filesystems/ext.js diff --git a/webroot/js/filesystems/ext.js b/webroot/js/filesystems/ext.js new file mode 100644 index 0000000..45671e5 --- /dev/null +++ b/webroot/js/filesystems/ext.js @@ -0,0 +1,258 @@ +// EXT filesystem implementation (ext2/ext3/ext4) + +import { BaseFilesystem } from './base.js'; +import { checkDependencies, updateResultItem } from '../utils.js'; + +export class EXTFilesystem extends BaseFilesystem { + constructor() { + super('EXT', [ + { + id: 'ext', + name: 'EXT (ext2/ext3/ext4)', + constants: [ + { id: 'partitionStartEXT', label: 'Partitionsstart (Basis-Offset)', unit: 'Bytes', default: '0x0' }, + { id: 'superblockOffsetEXT', label: 'Superblock-Offset (relativ zur Partition)', unit: 'Bytes', default: '0x400' } + ], + inputs: [ + { id: 'blockSizeExpEXT', label: 'Block-Größe Exponent (Offset 0x418)', unit: 'Exponent n', placeholder: '0x0' }, + { id: 'blocksPerGroupEXT', label: 'Blöcke pro Blockgruppe (Offset 0x420)', unit: 'Anzahl', placeholder: '0x8000' }, + { id: 'inodesPerGroupEXT', label: 'Inodes pro Blockgruppe (Offset 0x428)', unit: 'Anzahl', placeholder: '0x2000' }, + { id: 'inodeSizeEXT', label: 'Inode-Größe (Offset 0x458)', unit: 'Bytes', placeholder: '0x100' }, + { id: 'totalBlocksEXT', label: 'Gesamtanzahl Blöcke (Offset 0x404)', unit: 'Anzahl', placeholder: '0x40000' }, + { id: 'groupDescSizeEXT', label: 'Größe Gruppendeskriptor (Offset 0x4FE)', unit: 'Bytes', placeholder: '0x20' }, + { id: 'inodeTableBlockGroupEXT', label: 'Inode-Tabelle Block (aus GDT, Offset 0x08)', unit: 'Block-Nummer', placeholder: '0x21' }, + { id: 'targetInodeEXT', label: 'Gesuchter Inode', unit: 'Nummer', placeholder: '0x02' }, + { id: 'targetBlockEXT', label: 'Gesuchter Block', unit: 'Nummer', placeholder: '0x100' }, + { id: 'blockgroupNumberEXT', label: 'Blockgruppen-Nummer', unit: 'Nummer', placeholder: '0x0' } + ], + resultGroups: [ + { + name: 'Basis-Berechnungen', + results: [ + { id: 'superblockPosEXT', label: 'Superblock Position', dependencies: ['partitionStartEXT', 'superblockOffsetEXT'], formula: 'Partitionsstart + 0x400' }, + { id: 'blockSizeBytesEXT', label: 'Block-Größe (Bytes)', dependencies: ['blockSizeExpEXT'], formula: '2^(10+n) Bytes' }, + { id: 'totalBlockgroupsEXT', label: 'Anzahl Blockgruppen', dependencies: ['totalBlocksEXT', 'blocksPerGroupEXT'], formula: 'Aufrunden(Gesamtblöcke ÷ Blöcke pro Gruppe)' } + ] + }, + { + name: 'Gruppendeskriptor-Tabelle', + results: [ + { id: 'gdtStartEXT', label: 'GDT Start', dependencies: ['partitionStartEXT', 'superblockOffsetEXT', 'blockSizeExpEXT'], formula: 'Partitionsstart + Block nach Superblock' }, + { id: 'gdtBlockgroupOffsetEXT', label: 'GDT-Eintrag für Blockgruppe', dependencies: ['partitionStartEXT', 'superblockOffsetEXT', 'blockSizeExpEXT', 'blockgroupNumberEXT', 'groupDescSizeEXT'], formula: 'GDT Start + (Blockgruppe × GD-Größe)' } + ] + }, + { + name: 'Inode-Berechnungen', + results: [ + { id: 'inodeBlockgroupEXT', label: 'Blockgruppe des Inodes', dependencies: ['targetInodeEXT', 'inodesPerGroupEXT'], formula: 'Abrunden((Inode - 1) ÷ Inodes pro Gruppe)' }, + { id: 'inodeRelativeIndexEXT', label: 'Relativer Index in Blockgruppe', dependencies: ['targetInodeEXT', 'inodesPerGroupEXT'], formula: '(Inode - 1) % Inodes pro Gruppe' }, + { id: 'inodeTableStartEXT', label: 'Inode-Tabelle Start', dependencies: ['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT'], formula: 'Partitionsstart + (Block × Block-Größe)' }, + { id: 'inodeOffsetEXT', label: 'Inode Offset', dependencies: ['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT', 'targetInodeEXT', 'inodesPerGroupEXT', 'inodeSizeEXT'], formula: 'Inode-Tabelle Start + (Relativer Index × Inode-Größe)' } + ] + }, + { + name: 'Block-Berechnungen', + results: [ + { id: 'blockOffsetEXT', label: 'Block Offset', dependencies: ['partitionStartEXT', 'targetBlockEXT', 'blockSizeExpEXT'], formula: 'Partitionsstart + (Block-Nummer × Block-Größe)' } + ] + }, + { + name: 'Reservierte Inodes (Systemdateien)', + results: [ + { id: 'badBlocksInodeEXT', label: 'Inode 1 - Beschädigte Blöcke', dependencies: ['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT', 'inodeSizeEXT'], formula: 'Inode-Tabelle Start + (0 × Inode-Größe)' }, + { id: 'rootDirInodeEXT', label: 'Inode 2 - Wurzelverzeichnis', dependencies: ['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT', 'inodeSizeEXT'], formula: 'Inode-Tabelle Start + (1 × Inode-Größe)' }, + { id: 'userQuotaInodeEXT', label: 'Inode 3 - Benutzer-Quotas', dependencies: ['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT', 'inodeSizeEXT'], formula: 'Inode-Tabelle Start + (2 × Inode-Größe)' }, + { id: 'groupQuotaInodeEXT', label: 'Inode 4 - Gruppen-Quotas', dependencies: ['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT', 'inodeSizeEXT'], formula: 'Inode-Tabelle Start + (3 × Inode-Größe)' }, + { id: 'bootloaderInodeEXT', label: 'Inode 5 - Bootloader', dependencies: ['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT', 'inodeSizeEXT'], formula: 'Inode-Tabelle Start + (4 × Inode-Größe)' }, + { id: 'journalInodeEXT', label: 'Inode 8 - Journal (ext3/ext4)', dependencies: ['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT', 'inodeSizeEXT'], formula: 'Inode-Tabelle Start + (7 × Inode-Größe)' } + ] + }, + { + name: 'Inode-Tabellen-Übersicht', + results: [ + { id: 'inodeTableSizeEXT', label: 'Inode-Tabelle Größe (Bytes)', dependencies: ['inodesPerGroupEXT', 'inodeSizeEXT'], formula: 'Inodes pro Gruppe × Inode-Größe' }, + { id: 'inodeTableSizeBlocksEXT', label: 'Inode-Tabelle Größe (Blöcke)', dependencies: ['inodesPerGroupEXT', 'inodeSizeEXT', 'blockSizeExpEXT'], formula: 'Aufrunden(Tabellengröße ÷ Block-Größe)' } + ] + } + ], + formulas: [ + { name: 'Superblock Position', expression: 'Partitionsstart + 0x400 (immer fix bei Offset 0x400)' }, + { name: 'Block-Größe', expression: '2^(10+n) Bytes, wobei n der Exponent aus Offset 0x418 ist (0 = 1024, 1 = 2048, 2 = 4096, etc.)' }, + { name: 'Anzahl Blockgruppen', expression: 'Aufrunden(Gesamtanzahl Blöcke ÷ Blöcke pro Blockgruppe)' }, + { name: 'GDT Start', expression: 'Partitionsstart + (Block nach Superblock × Block-Größe)' }, + { name: 'GDT-Eintrag Offset', expression: 'GDT Start + (Blockgruppen-Nummer × Gruppendeskriptor-Größe)' }, + { name: 'Blockgruppe des Inodes', expression: 'Abrunden((Inode-Nummer - 1) ÷ Inodes pro Blockgruppe)' }, + { name: 'Relativer Index', expression: '(Inode-Nummer - 1) % Inodes pro Blockgruppe' }, + { name: 'Inode-Tabelle Start', expression: 'Partitionsstart + (Inode-Tabelle Block-Nummer × Block-Größe)' }, + { name: 'Inode Offset', expression: 'Inode-Tabelle Start + (Relativer Index × Inode-Größe)' }, + { name: 'Block Offset', expression: 'Partitionsstart + (Block-Nummer × Block-Größe)' }, + { name: 'Reservierte Inodes', expression: 'Inode-Tabelle Start + ((Inode-Nummer - 1) × Inode-Größe)' }, + { name: 'Inode-Tabelle Größe', expression: 'Inodes pro Blockgruppe × Inode-Größe' } + ] + } + ]); + } + + calculate(variantId) { + if (variantId !== 'ext') return; + + const values = this.getInputValues(variantId); + const results = {}; + + this.calculateEXT(values, results); + } + + calculateEXT(values, results) { + // Superblock Position + if (checkDependencies(['partitionStartEXT', 'superblockOffsetEXT'])) { + results.superblockPos = values.partitionStartEXT + values.superblockOffsetEXT; + updateResultItem('superblockPosEXT', results.superblockPos, true, + `0x${values.partitionStartEXT.toString(16)} + 0x${values.superblockOffsetEXT.toString(16)} = 0x${results.superblockPos.toString(16)}`); + } else { + updateResultItem('superblockPosEXT', 0, false); + } + + // Block Size (2^(10+n)) + if (checkDependencies(['blockSizeExpEXT'])) { + results.blockSize = Math.pow(2, 10 + values.blockSizeExpEXT); + updateResultItem('blockSizeBytesEXT', results.blockSize, true, + `2^(10 + ${values.blockSizeExpEXT}) = 2^${10 + values.blockSizeExpEXT} = 0x${results.blockSize.toString(16)} (${results.blockSize} Bytes)`); + } else { + updateResultItem('blockSizeBytesEXT', 0, false); + } + + // Total Blockgroups + if (checkDependencies(['totalBlocksEXT', 'blocksPerGroupEXT'])) { + results.totalBlockgroups = Math.ceil(values.totalBlocksEXT / values.blocksPerGroupEXT); + updateResultItem('totalBlockgroupsEXT', results.totalBlockgroups, true, + `Aufrunden(0x${values.totalBlocksEXT.toString(16)} ÷ 0x${values.blocksPerGroupEXT.toString(16)}) = ${results.totalBlockgroups}`); + } else { + updateResultItem('totalBlockgroupsEXT', 0, false); + } + + // GDT Start (block after superblock) + if (checkDependencies(['partitionStartEXT', 'superblockOffsetEXT', 'blockSizeExpEXT']) && results.blockSize !== undefined) { + // If block size is 1024 (0x400), superblock is in block 1, GDT starts at block 2 + // Otherwise, superblock is in block 0, GDT starts at block 1 + const superblockBlock = (results.blockSize === 1024) ? 1 : 0; + const gdtBlock = superblockBlock + 1; + results.gdtStart = values.partitionStartEXT + (gdtBlock * results.blockSize); + updateResultItem('gdtStartEXT', results.gdtStart, true, + `0x${values.partitionStartEXT.toString(16)} + (Block ${gdtBlock} × 0x${results.blockSize.toString(16)}) = 0x${results.gdtStart.toString(16)}`); + } else { + updateResultItem('gdtStartEXT', 0, false); + } + + // GDT Entry for specific Blockgroup + if (checkDependencies(['blockgroupNumberEXT', 'groupDescSizeEXT']) && results.gdtStart !== undefined) { + results.gdtBlockgroupOffset = results.gdtStart + (values.blockgroupNumberEXT * values.groupDescSizeEXT); + updateResultItem('gdtBlockgroupOffsetEXT', results.gdtBlockgroupOffset, true, + `0x${results.gdtStart.toString(16)} + (0x${values.blockgroupNumberEXT.toString(16)} × 0x${values.groupDescSizeEXT.toString(16)}) = 0x${results.gdtBlockgroupOffset.toString(16)}`); + } else { + updateResultItem('gdtBlockgroupOffsetEXT', 0, false); + } + + // Inode Blockgroup + if (checkDependencies(['targetInodeEXT', 'inodesPerGroupEXT'])) { + results.inodeBlockgroup = Math.floor((values.targetInodeEXT - 1) / values.inodesPerGroupEXT); + updateResultItem('inodeBlockgroupEXT', results.inodeBlockgroup, true, + `Abrunden((0x${values.targetInodeEXT.toString(16)} - 1) ÷ 0x${values.inodesPerGroupEXT.toString(16)}) = ${results.inodeBlockgroup}`); + } else { + updateResultItem('inodeBlockgroupEXT', 0, false); + } + + // Inode Relative Index + if (checkDependencies(['targetInodeEXT', 'inodesPerGroupEXT'])) { + results.inodeRelativeIndex = (values.targetInodeEXT - 1) % values.inodesPerGroupEXT; + updateResultItem('inodeRelativeIndexEXT', results.inodeRelativeIndex, true, + `(0x${values.targetInodeEXT.toString(16)} - 1) % 0x${values.inodesPerGroupEXT.toString(16)} = 0x${results.inodeRelativeIndex.toString(16)}`); + } else { + updateResultItem('inodeRelativeIndexEXT', 0, false); + } + + // Inode Table Start + if (checkDependencies(['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT']) && results.blockSize !== undefined) { + results.inodeTableStart = values.partitionStartEXT + (values.inodeTableBlockGroupEXT * results.blockSize); + updateResultItem('inodeTableStartEXT', results.inodeTableStart, true, + `0x${values.partitionStartEXT.toString(16)} + (0x${values.inodeTableBlockGroupEXT.toString(16)} × 0x${results.blockSize.toString(16)}) = 0x${results.inodeTableStart.toString(16)}`); + } else { + updateResultItem('inodeTableStartEXT', 0, false); + } + + // Inode Offset + if (checkDependencies(['targetInodeEXT', 'inodesPerGroupEXT', 'inodeSizeEXT']) && results.inodeTableStart !== undefined && results.inodeRelativeIndex !== undefined) { + results.inodeOffset = results.inodeTableStart + (results.inodeRelativeIndex * values.inodeSizeEXT); + updateResultItem('inodeOffsetEXT', results.inodeOffset, true, + `0x${results.inodeTableStart.toString(16)} + (0x${results.inodeRelativeIndex.toString(16)} × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.inodeOffset.toString(16)}`); + } else { + updateResultItem('inodeOffsetEXT', 0, false); + } + + // Block Offset + if (checkDependencies(['partitionStartEXT', 'targetBlockEXT', 'blockSizeExpEXT']) && results.blockSize !== undefined) { + results.blockOffset = values.partitionStartEXT + (values.targetBlockEXT * results.blockSize); + updateResultItem('blockOffsetEXT', results.blockOffset, true, + `0x${values.partitionStartEXT.toString(16)} + (0x${values.targetBlockEXT.toString(16)} × 0x${results.blockSize.toString(16)}) = 0x${results.blockOffset.toString(16)}`); + } else { + updateResultItem('blockOffsetEXT', 0, false); + } + + // Reserved Inodes (always in blockgroup 0, inode table) + if (results.inodeTableStart !== undefined && checkDependencies(['inodeSizeEXT'])) { + // Inode 1 (Bad Blocks) - Index 0 + results.badBlocksInode = results.inodeTableStart + (0 * values.inodeSizeEXT); + updateResultItem('badBlocksInodeEXT', results.badBlocksInode, true, + `0x${results.inodeTableStart.toString(16)} + (0 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.badBlocksInode.toString(16)}`); + + // Inode 2 (Root Directory) - Index 1 + results.rootDirInode = results.inodeTableStart + (1 * values.inodeSizeEXT); + updateResultItem('rootDirInodeEXT', results.rootDirInode, true, + `0x${results.inodeTableStart.toString(16)} + (1 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.rootDirInode.toString(16)}`); + + // Inode 3 (User Quotas) - Index 2 + results.userQuotaInode = results.inodeTableStart + (2 * values.inodeSizeEXT); + updateResultItem('userQuotaInodeEXT', results.userQuotaInode, true, + `0x${results.inodeTableStart.toString(16)} + (2 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.userQuotaInode.toString(16)}`); + + // Inode 4 (Group Quotas) - Index 3 + results.groupQuotaInode = results.inodeTableStart + (3 * values.inodeSizeEXT); + updateResultItem('groupQuotaInodeEXT', results.groupQuotaInode, true, + `0x${results.inodeTableStart.toString(16)} + (3 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.groupQuotaInode.toString(16)}`); + + // Inode 5 (Bootloader) - Index 4 + results.bootloaderInode = results.inodeTableStart + (4 * values.inodeSizeEXT); + updateResultItem('bootloaderInodeEXT', results.bootloaderInode, true, + `0x${results.inodeTableStart.toString(16)} + (4 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.bootloaderInode.toString(16)}`); + + // Inode 8 (Journal) - Index 7 + results.journalInode = results.inodeTableStart + (7 * values.inodeSizeEXT); + updateResultItem('journalInodeEXT', results.journalInode, true, + `0x${results.inodeTableStart.toString(16)} + (7 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.journalInode.toString(16)}`); + } else { + updateResultItem('badBlocksInodeEXT', 0, false); + updateResultItem('rootDirInodeEXT', 0, false); + updateResultItem('userQuotaInodeEXT', 0, false); + updateResultItem('groupQuotaInodeEXT', 0, false); + updateResultItem('bootloaderInodeEXT', 0, false); + updateResultItem('journalInodeEXT', 0, false); + } + + // Inode Table Size (Bytes) + if (checkDependencies(['inodesPerGroupEXT', 'inodeSizeEXT'])) { + results.inodeTableSize = values.inodesPerGroupEXT * values.inodeSizeEXT; + updateResultItem('inodeTableSizeEXT', results.inodeTableSize, true, + `0x${values.inodesPerGroupEXT.toString(16)} × 0x${values.inodeSizeEXT.toString(16)} = 0x${results.inodeTableSize.toString(16)} (${results.inodeTableSize} Bytes)`); + } else { + updateResultItem('inodeTableSizeEXT', 0, false); + } + + // Inode Table Size (Blocks) + if (checkDependencies(['inodesPerGroupEXT', 'inodeSizeEXT', 'blockSizeExpEXT']) && results.blockSize !== undefined && results.inodeTableSize !== undefined) { + results.inodeTableSizeBlocks = Math.ceil(results.inodeTableSize / results.blockSize); + updateResultItem('inodeTableSizeBlocksEXT', results.inodeTableSizeBlocks, true, + `Aufrunden(0x${results.inodeTableSize.toString(16)} ÷ 0x${results.blockSize.toString(16)}) = ${results.inodeTableSizeBlocks} Blöcke`); + } else { + updateResultItem('inodeTableSizeBlocksEXT', 0, false); + } + } +} diff --git a/webroot/js/main.js b/webroot/js/main.js index 4fb4e98..f12257a 100644 --- a/webroot/js/main.js +++ b/webroot/js/main.js @@ -5,6 +5,7 @@ import { Calculator } from './calculator.js'; import { FAT12_16Filesystem, FAT32Filesystem } from './filesystems/fat.js'; import { NTFSFilesystem } from './filesystems/ntfs.js'; import { ExFATFilesystem } from './filesystems/exfat.js'; +import { EXTFilesystem } from './filesystems/ext.js'; import { initializeUXEnhancements } from './ux-enhancements.js'; class FilesystemCalculator { @@ -13,7 +14,8 @@ class FilesystemCalculator { new FAT12_16Filesystem(), new FAT32Filesystem(), new NTFSFilesystem(), - new ExFATFilesystem() + new ExFATFilesystem(), + new EXTFilesystem() ]; this.calculator = new Calculator(); this.activeTab = null;