UNPKG

penguins-eggs

Version:

A remaster system tool, compatible with Almalinux, Alpine, Arch, Debian, Devuan, Fedora, Manjaro, Opensuse, Ubuntu and derivatives

87 lines (86 loc) 4.21 kB
/** * ./src/classes/ovary.d/luks-shrink.ts * penguins-eggs v.25.10.x / ecmascript 2020 * author: Piero Proietti * email: piero.proietti@gmail.com * license: MIT */ import { exec } from '../../lib/utils.js'; import Utils from '../utils.js'; const noop = () => { }; /** * luksShrink * Riduce in sicurezza un volume LUKS (root o home) minimizzando lo spazio * ma garantendo l'integrità dei dati e l'allineamento dei blocchi. */ export async function luksShrink() { const loggers = { info: this.hidden ? noop : Utils.info, log: this.hidden ? noop : console.log, success: this.hidden ? noop : Utils.success, warning: this.hidden ? noop : Utils.warning }; const { info, log, success, warning } = loggers; warning(`Unmounting ${this.luksMountpoint} to perform shrinking...`); // Usa -l (lazy) se necessario, ma meglio umount pulito await exec(`umount ${this.luksMountpoint}`, this.echo); // 1. Controllo integrità (CRUCIALE per homecrypt: ripara eventuali errori prima di ridimensionare) warning(`Checking filesystem integrity...`); await exec(`e2fsck -f -y ${this.luksDevice}`, this.echo); // 2. Riduzione del filesystem al minimo warning(`Shrinking filesystem to minimum size...`); // resize2fs -M è sicuro se poi lasciamo margine nel contenitore await exec(`resize2fs -M ${this.luksDevice}`, this.echo); // 3. Calcolo della nuova dimensione del filesystem warning(`Calculating new sizes...`); const tuneOutput = (await exec(`tune2fs -l ${this.luksDevice}`, { capture: true })).data; const blockSizeMatch = tuneOutput.match(/Block size:\s+(\d+)/); const blockCountMatch = tuneOutput.match(/Block count:\s+(\d+)/); if (!blockSizeMatch || !blockCountMatch) { throw new Error('Could not determine filesystem size from tune2fs'); } const blockSize = Number.parseInt(blockSizeMatch[1], 10); const blockCount = Number.parseInt(blockCountMatch[1], 10); const fsSizeBytes = blockSize * blockCount; warning(`Actual Ext4 payload size: ${bytesToGB(fsSizeBytes)}`); // 4. Calcolo dell'offset LUKS (Header size) const statusOutput = (await exec(`cryptsetup status ${this.luksMappedName}`, { capture: true })).data; const offsetMatch = statusOutput.match(/offset:\s+(\d+)\s+sectors/); let luksHeaderBytes = 0; if (offsetMatch && offsetMatch[1]) { luksHeaderBytes = Number.parseInt(offsetMatch[1], 10) * 512; warning(`Detected LUKS header: ${bytesToGB(luksHeaderBytes)}`); } else { luksHeaderBytes = 32 * 1024 * 1024; // Fallback 32MB warning(`Could not detect LUKS offset, using safe fallback: 32MB`); } // 5. Calcolo dimensione finale SICURA // Usiamo 200MB di margine. Su una ISO da 4GB è il 5%, un prezzo onesto per la stabilità. // Questo spazio extra protegge sia lo squashfs (root) che i metadati sparsi (home). const safetyMargin = 200 * 1024 * 1024; const rawFinalSize = fsSizeBytes + luksHeaderBytes + safetyMargin; // ALLINEAMENTO A 1MB: Fondamentale per evitare errori di I/O su device fisici o loop const alignBlock = 1024 * 1024; const finalFileSize = Math.ceil(rawFinalSize / alignBlock) * alignBlock; warning(`------------------------------------------------`); warning(`SAFE SHRINK CALCULATION:`); warning(` FS Payload: ${bytesToGB(fsSizeBytes)}`); warning(` LUKS Header: ${bytesToGB(luksHeaderBytes)}`); warning(` SafetyMargin: ${bytesToGB(safetyMargin)}`); warning(` Alignment: 1 MB`); warning(` FINAL SIZE: ${bytesToGB(finalFileSize)}`); warning(`------------------------------------------------`); // 6. Chiusura volume warning(`Closing LUKS volume ${this.luksMappedName}...`); await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]); // 7. Truncate finale warning(`Truncating ${this.luksFile} to release unused space...`); await exec(`truncate -s ${finalFileSize} ${this.luksFile}`, this.echo); } function bytesToGB(bytes) { if (bytes === 0) return '0.00 GB'; const gigabytes = bytes / (1024 * 1024 * 1024); return gigabytes.toFixed(2) + ' GB'; }