penguins-eggs 25.12.7 → 25.12.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.oclif.manifest.json +1 -1
  2. package/README.md +74 -45
  3. package/README.pdf +13450 -14838
  4. package/addons/eggs/theme/livecd/simple.grub.main.cfg +3 -3
  5. package/conf/distros/buster/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
  6. package/conf/distros/focal/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
  7. package/conf/distros/noble/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
  8. package/conf/distros/noble/calamares/libexec/calamares-l10n-helper.sh +2 -1
  9. package/conf/distros/noble/calamares/settings.yml +1 -0
  10. package/conf/distros/trixie/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
  11. package/dist/classes/cli-autologin.d.ts +37 -4
  12. package/dist/classes/cli-autologin.js +125 -112
  13. package/dist/classes/daddy.js +4 -1
  14. package/dist/classes/incubation/fisherman-helper/initcpio.d.ts +3 -2
  15. package/dist/classes/incubation/fisherman-helper/initcpio.js +25 -20
  16. package/dist/classes/incubation/incubator.d/manjaro.js +1 -0
  17. package/dist/classes/ovary.d/edit-live-fs.d.ts +1 -1
  18. package/dist/classes/ovary.d/edit-live-fs.js +15 -122
  19. package/dist/classes/ovary.d/fertilization.js +1 -1
  20. package/dist/classes/ovary.d/luks-home.js +33 -19
  21. package/dist/classes/ovary.d/luks-root.d.ts +1 -2
  22. package/dist/classes/ovary.d/luks-root.js +46 -27
  23. package/dist/classes/ovary.d/luks-shrink.d.ts +14 -0
  24. package/dist/classes/ovary.d/luks-shrink.js +86 -0
  25. package/dist/classes/ovary.d/produce.js +63 -21
  26. package/dist/classes/ovary.d.ts +3 -1
  27. package/dist/classes/ovary.js +3 -1
  28. package/dist/classes/utils.js +1 -1
  29. package/dist/classes/yolk.js +1 -1
  30. package/dist/commands/produce.js +11 -7
  31. package/dist/interfaces/calamares/i-calamares-branding.d.ts +56 -38
  32. package/dist/interfaces/calamares/i-calamares-branding.js +10 -0
  33. package/dist/krill/classes/prepare.d/users.js +1 -1
  34. package/dist/krill/classes/sequence.d/fstab.js +1 -1
  35. package/dist/krill/classes/sequence.d/mkfs.js +1 -1
  36. package/dist/krill/classes/sequence.d/unpackfs.js +2 -2
  37. package/dist/krill/classes/sequence.d.ts +1 -5
  38. package/dist/krill/classes/sequence.js +26 -29
  39. package/dist/krill/components/finished.js +2 -2
  40. package/dist/krill/components/install.js +2 -2
  41. package/dist/krill/components/keyboard.js +2 -2
  42. package/dist/krill/components/location.js +2 -2
  43. package/dist/krill/components/network.js +2 -2
  44. package/dist/krill/components/partitions.js +2 -2
  45. package/dist/krill/components/summary.js +2 -2
  46. package/dist/krill/components/users.js +2 -2
  47. package/dist/krill/components/welcome.js +2 -2
  48. package/dist/lib/utils.d.ts +1 -0
  49. package/dist/lib/utils.js +46 -0
  50. package/manpages/doc/man/eggs.1.gz +0 -0
  51. package/manpages/doc/man/eggs.html +8 -8
  52. package/package.json +2 -2
  53. package/scripts/restore_homecrypt_krill.sh +93 -0
@@ -5,7 +5,6 @@
5
5
  * email: piero.proietti@gmail.com
6
6
  * license: MIT
7
7
  */
8
- // packages
9
8
  import fs from 'fs';
10
9
  import os from 'os';
11
10
  import path from 'node:path';
@@ -16,11 +15,11 @@ import Systemctl from '../systemctl.js';
16
15
  import { exec } from '../../lib/utils.js';
17
16
  // _dirname
18
17
  const __dirname = path.dirname(new URL(import.meta.url).pathname);
19
- export async function editLiveFs(clone = false) {
18
+ export async function editLiveFs() {
20
19
  if (this.verbose)
21
20
  console.log('Ovary: editLiveFs');
22
21
  const workDir = this.settings.work_dir.merged;
23
- if (clone) {
22
+ if (this.clone || this.homecrypt || this.fullcrypt) {
24
23
  await exec(`touch ${workDir}/etc/penguins-eggs.d/is_clone`, this.echo);
25
24
  }
26
25
  if (Pacman.packageIsInstalled('epoptes')) {
@@ -38,15 +37,6 @@ export async function editLiveFs(clone = false) {
38
37
  await exec(cmd, this.echo);
39
38
  cmd = `find ${workDir}/var/log/ -type f -exec truncate -s 0 {} \\;`;
40
39
  await exec(cmd, this.echo);
41
- // =========================================================================
42
- // FIX DEFINITIVO PER DEVUAN/SYSVINIT (Init Script Hardening)
43
- // =========================================================================
44
- if (Utils.isSysvinit()) {
45
- if (this.verbose)
46
- console.log('SysVinit detected: Hardening init scripts...');
47
- await patchInitScripts(workDir, this.verbose);
48
- }
49
- // =========================================================================
50
40
  // Fix Symlinks /var/run e /var/lock
51
41
  const varRun = `${workDir}/var/run`;
52
42
  if (fs.existsSync(varRun) && !fs.lstatSync(varRun).isSymbolicLink()) {
@@ -85,16 +75,20 @@ export async function editLiveFs(clone = false) {
85
75
  if (fs.existsSync(`${workDir}/etc/crypttab`)) {
86
76
  await exec(`rm ${workDir}/etc/crypttab`, this.echo);
87
77
  }
88
- // 🔧 [MODIFICA 1] Machine ID cleanup
89
- // Cancelliamo SEMPRE il machine-id, anche su SysVinit.
90
- // Questo garantisce che lo script patchato (dbus) trovi il file mancante e lo rigeneri.
91
- if (fs.existsSync(`${workDir}/etc/machine-id`)) {
92
- await exec(`rm ${workDir}/etc/machine-id`, this.echo);
93
- await exec(`touch ${workDir}/etc/machine-id`, this.echo); // Lo ricreiamo vuoto
78
+ /**
79
+ * machine-id
80
+ */
81
+ await exec(`rm -f ${workDir}/etc/machine-id`);
82
+ await exec(`rm -f ${workDir}/var/lib/dbus/machine-id`);
83
+ if (Utils.isSysvinit()) {
84
+ await exec(`chroot ${workDir} dbus-uuidgen --ensure=/etc/machine-id`);
85
+ await exec(`ln -sf /etc/machine-id ${workDir}/var/lib/dbus/machine-id`);
86
+ // const machineId = crypto.randomBytes(16).toString('hex')
87
+ // fs.writeFileSync(`${workDir}/etc/machine-id`, machineId + '\n')
88
+ // fs.writeFileSync(`${workDir}/var/lib/dbus/machine-id`, machineId + '\n')
94
89
  }
95
- // Rimuoviamo anche quello in /var/lib/dbus per forzare la rigenerazione
96
- if (fs.existsSync(`${workDir}/var/lib/dbus/machine-id`)) {
97
- await exec(`rm ${workDir}/var/lib/dbus/machine-id`, this.echo);
90
+ else if (Utils.isSystemd()) {
91
+ await exec(`touch ${workDir}/etc/machine-id`);
98
92
  }
99
93
  if (fs.existsSync(`${workDir}/boot/grub/fonts/unicode.pf2`)) {
100
94
  shx.cp(`${workDir}/boot/grub/fonts/unicode.pf2`, `${workDir}/boot/grub/fonts/UbuntuMono16.pf2`);
@@ -166,104 +160,3 @@ export async function editLiveFs(clone = false) {
166
160
  await exec(`chmod 1777 ${workDir}/tmp`, this.echo);
167
161
  }
168
162
  }
169
- /**
170
- * Patcha direttamente gli script di init in /etc/init.d/
171
- */
172
- async function patchInitScripts(workDir, verbose) {
173
- // 1. PATCH DBUS (Il più importante)
174
- const dbusScript = `${workDir}/etc/init.d/dbus`;
175
- if (fs.existsSync(dbusScript)) {
176
- if (verbose)
177
- console.log(`Patching ${dbusScript} to fix missing directories and RO fs...`);
178
- let content = fs.readFileSync(dbusScript, 'utf8');
179
- let modified = false;
180
- // PATCH A: Header con gestione Ramdisk Fallback e Mount Proc
181
- // Questa intestazione viene inserita all'inizio e prova a montare tmpfs se non può scrivere
182
- const dbusFix = `
183
- ### EGGS-FIX-START
184
- # 1. Assicuriamoci che /proc sia montato (necessario per leggere uuid kernel)
185
- if ! mountpoint -q /proc/; then
186
- mount -t proc proc /proc
187
- fi
188
-
189
- # 2. Gestione Filesystem Read-Only (RO)
190
- # Se non possiamo scrivere in /var/lib/dbus, montiamo un tmpfs (RAM) sopra.
191
- # Questo bypassa il blocco dell'overlayfs non ancora pronto tipico delle build AppImage.
192
- if ! mkdir -p /var/lib/dbus 2>/dev/null || ! touch /var/lib/dbus/.rw_check 2>/dev/null; then
193
- echo "EGGS-DEBUG: Filesystem Read-Only detected! Mounting tmpfs on /var/lib/dbus" > /dev/console
194
- mount -t tmpfs -o size=1m tmpfs /var/lib/dbus
195
- fi
196
- rm -f /var/lib/dbus/.rw_check
197
-
198
- # 3. Creazione Directory Runtime
199
- mkdir -p /var/run/dbus /var/lib/dbus
200
- chmod 755 /var/run/dbus /var/lib/dbus
201
-
202
- # 4. Generazione Machine ID (Non-Blocking Strategy)
203
- # Se il file non esiste (o è vuoto), lo creiamo.
204
- if [ ! -s /var/lib/dbus/machine-id ]; then
205
- # Prova A: Usa UUID del kernel (Istantaneo, non blocca)
206
- if [ -f /proc/sys/kernel/random/uuid ]; then
207
- cat /proc/sys/kernel/random/uuid | tr -d '-' > /var/lib/dbus/machine-id
208
- # Prova B: Fallback statico
209
- else
210
- echo "00000000000000000000000000000001" > /var/lib/dbus/machine-id
211
- fi
212
- chmod 644 /var/lib/dbus/machine-id
213
- fi
214
-
215
- # 5. Sync /etc/machine-id (Best effort, ignora errori se /etc è RO)
216
- if [ ! -s /etc/machine-id ] && [ -s /var/lib/dbus/machine-id ]; then
217
- cp /var/lib/dbus/machine-id /etc/machine-id 2>/dev/null || true
218
- fi
219
- ### EGGS-FIX-END
220
- `;
221
- if (!content.includes('EGGS-FIX-START')) {
222
- content = content.replace('#!/bin/sh', `#!/bin/sh\n${dbusFix}`);
223
- modified = true;
224
- }
225
- // PATCH B: SABOTAGE PREVENTION (Questa è quella che mancava!)
226
- // Impediamo che create_machineid cancelli il file che abbiamo appena creato
227
- // perché l'uptime è basso.
228
- if (content.includes('rm -f "${MACHINEID}"')) {
229
- if (verbose)
230
- console.log('Patching dbus: disabling auto-delete of machine-id on boot');
231
- content = content.replace('rm -f "${MACHINEID}"', ': # EGGS-PATCH: Prevent deletion of valid machine-id');
232
- modified = true;
233
- }
234
- if (modified) {
235
- fs.writeFileSync(dbusScript, content, 'utf8');
236
- }
237
- }
238
- // 2. PATCH RSYSLOG
239
- const rsyslogScript = `${workDir}/etc/init.d/rsyslog`;
240
- if (fs.existsSync(rsyslogScript)) {
241
- let content = fs.readFileSync(rsyslogScript, 'utf8');
242
- const fix = `
243
- ### EGGS-FIX-START
244
- mkdir -p /var/spool/rsyslog
245
- chmod 755 /var/spool/rsyslog
246
- ### EGGS-FIX-END
247
- `;
248
- if (!content.includes('EGGS-FIX-START')) {
249
- content = content.replace('#!/bin/sh', `#!/bin/sh\n${fix}`);
250
- fs.writeFileSync(rsyslogScript, content, 'utf8');
251
- }
252
- }
253
- // 3. PATCH CRON
254
- const cronScript = `${workDir}/etc/init.d/cron`;
255
- if (fs.existsSync(cronScript)) {
256
- let content = fs.readFileSync(cronScript, 'utf8');
257
- const fix = `
258
- ### EGGS-FIX-START
259
- mkdir -p /var/spool/cron/crontabs
260
- chmod 1730 /var/spool/cron/crontabs
261
- chown root:crontab /var/spool/cron/crontabs 2>/dev/null || true
262
- ### EGGS-FIX-END
263
- `;
264
- if (!content.includes('EGGS-FIX-START')) {
265
- content = content.replace('#!/bin/sh', `#!/bin/sh\n${fix}`);
266
- fs.writeFileSync(cronScript, content, 'utf8');
267
- }
268
- }
269
- }
@@ -21,7 +21,7 @@ export async function fertilization(snapshot_prefix = '', snapshot_basename = ''
21
21
  this.familyId = distro.familyId;
22
22
  this.distroId = distro.distroId;
23
23
  this.distroLike = distro.distroLike;
24
- this.distroLliveMediumPath = distro.liveMediumPath;
24
+ this.distroLiveMediumPath = distro.liveMediumPath;
25
25
  this.settings = new Settings();
26
26
  if (await this.settings.load()) {
27
27
  await this.settings.loadRemix(this.theme);
@@ -27,7 +27,7 @@ export async function luksHome(clone = false, homecrypt = false) {
27
27
  try {
28
28
  /**
29
29
  * this.luksMappedName = 'home.img';
30
- * this.luksFile = `/tmp/${luksMappedName}`
30
+ * this.luksFile = `/var/tmp/${luksMappedName}`
31
31
  * this.luksDevice = `/dev/mapper/${luksMappedName}`
32
32
  * this.luksMountpoint = `/tmp/mnt/${luksMappedName}`
33
33
  * this.luksPassword = '0'
@@ -42,15 +42,21 @@ export async function luksHome(clone = false, homecrypt = false) {
42
42
  // Utils.warning('1. Calculation of space requirements...')
43
43
  let sizeString = (await exec('du -sb --exclude=/home/eggs /home', { capture: true })).data.trim().split(/\s+/)[0];
44
44
  let size = Number.parseInt(sizeString, 10);
45
- const luksSize = Math.ceil(size * 2);
46
- /**
47
- * E' più precisa ma equivalente grazie
48
- * al truncate
49
- */
50
- // const fsOverhead = Math.max(size * 0.1, 100 * 1024 * 1024)
51
- // const luksSize = size * 1.25 + fsOverhead // +25%
52
- warning(`homes size: ${bytesToGB(size)}`);
53
- warning(`partition LUKS ${this.luksFile} size: ${bytesToGB(luksSize)}`);
45
+ const fsOverhead = Math.ceil(size * 0.05);
46
+ const luksHeader = 32 * 1024 * 1024;
47
+ const safetyBuffer = 100 * 1024 * 1024;
48
+ let calculatedSize = size + fsOverhead + luksHeader + safetyBuffer;
49
+ const minSize = 64 * 1024 * 1024;
50
+ if (calculatedSize < minSize)
51
+ calculatedSize = minSize;
52
+ const alignment = 4 * 1024 * 1024;
53
+ const luksSize = Math.ceil(calculatedSize / alignment) * alignment;
54
+ warning(`------------------------------------------`);
55
+ warning(`HOME CRYPT CALCULATION (Read-Only):`);
56
+ warning(` Data Payload: ${bytesToGB(size)}`);
57
+ warning(` Overhead+Buf: ${bytesToGB(luksSize - size)}`);
58
+ warning(` TOTAL SIZE: ${bytesToGB(luksSize)}`);
59
+ warning(`------------------------------------------`);
54
60
  warning(`creating partition LUKS: ${this.luksFile}`);
55
61
  await this.luksExecuteCommand('truncate', ['--size', `${luksSize}`, this.luksFile]);
56
62
  warning(`formatting ${this.luksFile} as a LUKS volume...`);
@@ -59,7 +65,7 @@ export async function luksHome(clone = false, homecrypt = false) {
59
65
  await this.luksExecuteCommand('cryptsetup', luksFormatArgs, `${this.luksPassword}\n`);
60
66
  warning(`opening the LUKS volume. It will be mapped to ${this.luksDevice}`);
61
67
  await this.luksExecuteCommand('cryptsetup', ['luksOpen', this.luksFile, this.luksMappedName], `${this.luksPassword}\n`);
62
- warning(`formatting c ext4 `);
68
+ warning(`formatting ext4 `);
63
69
  await exec(`mkfs.ext4 -L live-home ${this.luksDevice}`, this.echo);
64
70
  warning(`mounting ${this.luksDevice} on ${this.luksMountpoint}`);
65
71
  if (fs.existsSync(this.luksMountpoint)) {
@@ -74,17 +80,21 @@ export async function luksHome(clone = false, homecrypt = false) {
74
80
  await exec(`mount /dev/mapper/${this.luksMappedName} ${this.luksMountpoint}`, this.echo);
75
81
  warning(`copying /home on ${this.luksMountpoint}`);
76
82
  await exec(`rsync -ah --exclude='eggs' /home/ ${this.luksMountpoint}`, this.echo);
83
+ /**
84
+ * utenti e gruppi in .system-backup
85
+ */
77
86
  warning(`saving user accounts info...`);
78
- // Crea directory per backup system files
79
87
  await exec(`mkdir -p ${this.luksMountpoint}/.system-backup`, this.echo);
80
- // Filtra solo utenti con UID >= 1000
88
+ // passwd/shadow: solo utenti con UID >= 1000
81
89
  await exec(`awk -F: '$3 >= 1000 {print}' /etc/passwd > ${this.luksMountpoint}/.system-backup/passwd`, this.echo);
82
90
  await exec(`awk -F: '$3 >= 1000 {print}' /etc/shadow > ${this.luksMountpoint}/.system-backup/shadow`, this.echo);
83
- // Per i gruppi: salva TUTTI (non filtrare per GID)
91
+ // group/gshadow TUTTI
84
92
  // Gli utenti possono appartenere a gruppi di sistema (sudo, audio, video, etc.)
85
93
  await exec(`cp /etc/group ${this.luksMountpoint}/.system-backup/group`, this.echo);
86
94
  await exec(`cp /etc/gshadow ${this.luksMountpoint}/.system-backup/gshadow`, this.echo);
87
- // saving display manager (autologin) configs...
95
+ /**
96
+ * saving display manager (autologin) configs...
97
+ */
88
98
  warning(`saving display manager configuration...`);
89
99
  // GDM (gdm3 è comune su Debian/Ubuntu)
90
100
  await exec(`[ -e /etc/gdm3 ] && cp -a /etc/gdm3 ${this.luksMountpoint}/.system-backup/`, this.echo);
@@ -95,10 +105,10 @@ export async function luksHome(clone = false, homecrypt = false) {
95
105
  // SDDM (sia file .conf che directory .conf.d)
96
106
  await exec(`[ -e /etc/sddm.conf ] && cp -a /etc/sddm.conf ${this.luksMountpoint}/.system-backup/`, this.echo);
97
107
  await exec(`[ -e /etc/sddm.conf.d ] && cp -a /etc/sddm.conf.d ${this.luksMountpoint}/.system-backup/`, this.echo);
98
- warning(`unmount ${this.luksDevice}`);
99
- await exec(`umount ${this.luksMountpoint}`, this.echo);
100
- warning(`closing LUKS volume ${this.luksMappedName}.`);
101
- await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]);
108
+ warning(`Syncing filesystem on ${this.luksMountpoint}...`);
109
+ await exec('sync', this.echo); // Forza scrittura dati su disco
110
+ // Shrink()
111
+ await this.luksShrink();
102
112
  warning(`moving ${this.luksMappedName} to (ISO)/live/.`);
103
113
  await exec(`mv ${this.luksFile} ${this.settings.iso_work}/live`, this.echo);
104
114
  warning('encryption process successfully completed!');
@@ -117,6 +127,10 @@ export async function luksHome(clone = false, homecrypt = false) {
117
127
  if (fs.existsSync(this.luksDevice)) {
118
128
  await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]).catch(() => { });
119
129
  }
130
+ if (fs.existsSync(this.luksFile)) {
131
+ Utils.warning(`Removing temporary container: ${this.luksFile}`);
132
+ fs.unlinkSync(this.luksFile);
133
+ }
120
134
  await Utils.pressKeyToExit();
121
135
  process.exit(1);
122
136
  }
@@ -8,8 +8,7 @@
8
8
  import Ovary from '../ovary.js';
9
9
  /**
10
10
  * luksRoot()
11
- *
12
- * create a container LUKS with the entire
11
+ * * create a container LUKS with the entire
13
12
  * filesystem.squashfs
14
13
  */
15
14
  export declare function luksRoot(this: Ovary): Promise<void>;
@@ -12,8 +12,7 @@ import { exec } from '../../lib/utils.js';
12
12
  const noop = () => { };
13
13
  /**
14
14
  * luksRoot()
15
- *
16
- * create a container LUKS with the entire
15
+ * * create a container LUKS with the entire
17
16
  * filesystem.squashfs
18
17
  */
19
18
  export async function luksRoot() {
@@ -29,7 +28,7 @@ export async function luksRoot() {
29
28
  try {
30
29
  /**
31
30
  * this.luksMappedName = 'root.img';
32
- * this.luksFile = `/tmp/${luksMappedName}`
31
+ * this.luksFile = `/var/tmp/${luksMappedName}`
33
32
  * this.luksDevice = `/dev/mapper/${luksMappedName}`
34
33
  * this.luksMountpoint = `/tmp/mnt/${luksMappedName}`
35
34
  * this.luksPassword = '0'
@@ -45,11 +44,29 @@ export async function luksRoot() {
45
44
  throw new Error(`filesystem.squashfs not found at: ${live_fs}`);
46
45
  }
47
46
  const stats = fs.statSync(live_fs);
48
- let size = stats.size; // Dimensione REALE del file in Byte
49
- // Add overhead * 1.25 per più sicurezza con file grandi
50
- const luksSize = Math.ceil(size * 1.25);
51
- warning(`filesystem.squashfs size: ${bytesToGB(size)}`);
52
- warning(`partition LUKS ${this.luksFile} size: ${bytesToGB(luksSize)}`);
47
+ const size = stats.size; // Dimensione REALE del file in Byte
48
+ // -------------------------------------------------------------
49
+ // CALCOLO DIMENSIONE OTTIMIZZATA (No shrink, safe allocation)
50
+ // -------------------------------------------------------------
51
+ // 1. Overhead Ext4: Inode, bitmap, superblocchi. ~3-4% è standard senza journal.
52
+ const fsOverhead = Math.ceil(size * 0.04);
53
+ // 2. Header LUKS: Solitamente 16MB per LUKS2, usiamo 32MB per sicurezza.
54
+ const luksHeader = 32 * 1024 * 1024;
55
+ // 3. Margine di sicurezza (Buffer): 120MB fissi.
56
+ // Questo spazio evita errori di "Disk full" durante la copia dei metadati.
57
+ // Viene compensato dall'uso di "-m 0" nella formattazione.
58
+ const safetyBuffer = 120 * 1024 * 1024;
59
+ // 4. Somma totale
60
+ let calculatedSize = size + fsOverhead + luksHeader + safetyBuffer;
61
+ // 5. Allineamento a 4MB (Performance storage)
62
+ const alignment = 4 * 1024 * 1024;
63
+ const luksSize = Math.ceil(calculatedSize / alignment) * alignment;
64
+ warning(`------------------------------------------`);
65
+ warning(`SAFE SIZE CALCULATION (No Shrink):`);
66
+ warning(` Payload (SquashFS): ${bytesToGB(size)}`);
67
+ warning(` Calc. Overhead: ${bytesToGB(luksSize - size)}`);
68
+ warning(` TOTAL CONTAINER: ${bytesToGB(luksSize)}`);
69
+ warning(`------------------------------------------`);
53
70
  warning(`creating partition LUKS: ${this.luksFile}`);
54
71
  await this.luksExecuteCommand('truncate', ['--size', `${luksSize}`, this.luksFile]);
55
72
  warning(`formatting ${this.luksFile} as a LUKS volume...`);
@@ -57,8 +74,13 @@ export async function luksRoot() {
57
74
  await this.luksExecuteCommand('cryptsetup', luksFormatArgs, `${this.luksPassword}\n`);
58
75
  warning(`opening the LUKS volume. It will be mapped to ${this.luksDevice}`);
59
76
  await this.luksExecuteCommand('cryptsetup', ['luksOpen', this.luksFile, this.luksMappedName], `${this.luksPassword}\n`);
60
- warning(`formatting ext4 (without journal)...`);
61
- await exec(`mkfs.ext4 -O ^has_journal -L live-root ${this.luksDevice}`, this.echo);
77
+ // -------------------------------------------------------------
78
+ // FORMATTAZIONE EXT4
79
+ // -m 0 : Imposta i blocchi riservati a 0% (recupera spazio)
80
+ // -O ^has_journal : Disabilita il journal (risparmia spazio e scritture)
81
+ // -------------------------------------------------------------
82
+ warning(`formatting ext4 (without journal, 0% reserved)...`);
83
+ await exec(`mkfs.ext4 -m 0 -O ^has_journal -L live-root ${this.luksDevice}`, this.echo);
62
84
  warning(`mounting ${this.luksDevice} on ${this.luksMountpoint}`);
63
85
  if (fs.existsSync(this.luksMountpoint)) {
64
86
  if (!Utils.isMountpoint(this.luksMountpoint)) {
@@ -74,25 +96,18 @@ export async function luksRoot() {
74
96
  await exec(`mv ${live_fs} ${this.luksMountpoint}/filesystem.squashfs`, this.echo);
75
97
  warning(`Syncing filesystem on ${this.luksMountpoint}...`);
76
98
  await exec('sync', this.echo); // Forza scrittura dati su disco
77
- warning(`Attempting unmount ${this.luksMountpoint}...`);
78
- try {
79
- await exec(`umount ${this.luksMountpoint}`, this.echo);
80
- success(`Unmounted ${this.luksMountpoint} successfully.`);
81
- }
82
- catch (umountError) {
83
- Utils.error(`Failed to unmount ${this.luksMountpoint}! Trying force unmount...`);
84
- // Tenta un unmount forzato/lazy come ultima risorsa
85
- await exec(`umount -lf ${this.luksMountpoint}`).catch((forceError) => {
86
- Utils.error(`Force unmount also failed: ${forceError}`);
87
- // Considera se lanciare un errore qui per fermare il processo
88
- });
89
- // Lancia comunque l'errore originale per segnalare il problema
90
- throw umountError;
91
- }
92
- warning(`closing LUKS volume ${this.luksFile}.`);
99
+ /**
100
+ * SHRINK DISABILITATO
101
+ * Non eseguiamo this.luksShrink() per evitare corruzione dati.
102
+ * Abbiamo calcolato la dimensione corretta all'inizio.
103
+ */
104
+ // Procedura di chiusura manuale (sostituisce quella dentro shrink)
105
+ warning(`Unmounting ${this.luksMountpoint}...`);
106
+ await exec(`umount ${this.luksMountpoint}`, this.echo);
107
+ warning(`Closing LUKS volume ${this.luksMappedName}...`);
93
108
  await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]);
94
109
  warning(`moving ${this.luksMappedName} on (ISO)/live.`);
95
- await exec(`mv ${this.luksFile} ${this.settings.iso_work}/live`, this.echo);
110
+ await exec(`mv ${this.luksFile} ${this.settings.iso_work}/live/root.img`, this.echo);
96
111
  }
97
112
  catch (error) {
98
113
  if (error instanceof Error) {
@@ -108,6 +123,10 @@ export async function luksRoot() {
108
123
  if (fs.existsSync(this.luksDevice)) {
109
124
  await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]).catch(() => { });
110
125
  }
126
+ if (fs.existsSync(this.luksFile)) {
127
+ Utils.warning(`Removing temporary container: ${this.luksFile}`);
128
+ fs.unlinkSync(this.luksFile);
129
+ }
111
130
  await Utils.pressKeyToExit();
112
131
  process.exit(1);
113
132
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * ./src/classes/ovary.d/luks-shrink.ts
3
+ * penguins-eggs v.25.10.x / ecmascript 2020
4
+ * author: Piero Proietti
5
+ * email: piero.proietti@gmail.com
6
+ * license: MIT
7
+ */
8
+ import Ovary from '../ovary.js';
9
+ /**
10
+ * luksShrink
11
+ * Riduce in sicurezza un volume LUKS (root o home) minimizzando lo spazio
12
+ * ma garantendo l'integrità dei dati e l'allineamento dei blocchi.
13
+ */
14
+ export declare function luksShrink(this: Ovary): Promise<void>;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * ./src/classes/ovary.d/luks-shrink.ts
3
+ * penguins-eggs v.25.10.x / ecmascript 2020
4
+ * author: Piero Proietti
5
+ * email: piero.proietti@gmail.com
6
+ * license: MIT
7
+ */
8
+ import Utils from '../utils.js';
9
+ import { exec } from '../../lib/utils.js';
10
+ const noop = () => { };
11
+ /**
12
+ * luksShrink
13
+ * Riduce in sicurezza un volume LUKS (root o home) minimizzando lo spazio
14
+ * ma garantendo l'integrità dei dati e l'allineamento dei blocchi.
15
+ */
16
+ export async function luksShrink() {
17
+ const loggers = {
18
+ log: this.hidden ? noop : console.log,
19
+ warning: this.hidden ? noop : Utils.warning,
20
+ success: this.hidden ? noop : Utils.success,
21
+ info: this.hidden ? noop : Utils.info,
22
+ };
23
+ const { log, warning, success, info } = loggers;
24
+ warning(`Unmounting ${this.luksMountpoint} to perform shrinking...`);
25
+ // Usa -l (lazy) se necessario, ma meglio umount pulito
26
+ await exec(`umount ${this.luksMountpoint}`, this.echo);
27
+ // 1. Controllo integrità (CRUCIALE per homecrypt: ripara eventuali errori prima di ridimensionare)
28
+ warning(`Checking filesystem integrity...`);
29
+ await exec(`e2fsck -f -y ${this.luksDevice}`, this.echo);
30
+ // 2. Riduzione del filesystem al minimo
31
+ warning(`Shrinking filesystem to minimum size...`);
32
+ // resize2fs -M è sicuro se poi lasciamo margine nel contenitore
33
+ await exec(`resize2fs -M ${this.luksDevice}`, this.echo);
34
+ // 3. Calcolo della nuova dimensione del filesystem
35
+ warning(`Calculating new sizes...`);
36
+ const tuneOutput = (await exec(`tune2fs -l ${this.luksDevice}`, { capture: true })).data;
37
+ const blockSizeMatch = tuneOutput.match(/Block size:\s+(\d+)/);
38
+ const blockCountMatch = tuneOutput.match(/Block count:\s+(\d+)/);
39
+ if (!blockSizeMatch || !blockCountMatch) {
40
+ throw new Error("Could not determine filesystem size from tune2fs");
41
+ }
42
+ const blockSize = parseInt(blockSizeMatch[1], 10);
43
+ const blockCount = parseInt(blockCountMatch[1], 10);
44
+ const fsSizeBytes = blockSize * blockCount;
45
+ warning(`Actual Ext4 payload size: ${bytesToGB(fsSizeBytes)}`);
46
+ // 4. Calcolo dell'offset LUKS (Header size)
47
+ const statusOutput = (await exec(`cryptsetup status ${this.luksMappedName}`, { capture: true })).data;
48
+ const offsetMatch = statusOutput.match(/offset:\s+(\d+)\s+sectors/);
49
+ let luksHeaderBytes = 0;
50
+ if (offsetMatch && offsetMatch[1]) {
51
+ luksHeaderBytes = parseInt(offsetMatch[1], 10) * 512;
52
+ warning(`Detected LUKS header: ${bytesToGB(luksHeaderBytes)}`);
53
+ }
54
+ else {
55
+ luksHeaderBytes = 32 * 1024 * 1024; // Fallback 32MB
56
+ warning(`Could not detect LUKS offset, using safe fallback: 32MB`);
57
+ }
58
+ // 5. Calcolo dimensione finale SICURA
59
+ // Usiamo 200MB di margine. Su una ISO da 4GB è il 5%, un prezzo onesto per la stabilità.
60
+ // Questo spazio extra protegge sia lo squashfs (root) che i metadati sparsi (home).
61
+ const safetyMargin = 200 * 1024 * 1024;
62
+ let rawFinalSize = fsSizeBytes + luksHeaderBytes + safetyMargin;
63
+ // ALLINEAMENTO A 1MB: Fondamentale per evitare errori di I/O su device fisici o loop
64
+ const alignBlock = 1024 * 1024;
65
+ const finalFileSize = Math.ceil(rawFinalSize / alignBlock) * alignBlock;
66
+ warning(`------------------------------------------------`);
67
+ warning(`SAFE SHRINK CALCULATION:`);
68
+ warning(` FS Payload: ${bytesToGB(fsSizeBytes)}`);
69
+ warning(` LUKS Header: ${bytesToGB(luksHeaderBytes)}`);
70
+ warning(` SafetyMargin: ${bytesToGB(safetyMargin)}`);
71
+ warning(` Alignment: 1 MB`);
72
+ warning(` FINAL SIZE: ${bytesToGB(finalFileSize)}`);
73
+ warning(`------------------------------------------------`);
74
+ // 6. Chiusura volume
75
+ warning(`Closing LUKS volume ${this.luksMappedName}...`);
76
+ await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]);
77
+ // 7. Truncate finale
78
+ warning(`Truncating ${this.luksFile} to release unused space...`);
79
+ await exec(`truncate -s ${finalFileSize} ${this.luksFile}`, this.echo);
80
+ }
81
+ function bytesToGB(bytes) {
82
+ if (bytes === 0)
83
+ return '0.00 GB';
84
+ const gigabytes = bytes / (1024 * 1024 * 1024);
85
+ return gigabytes.toFixed(2) + ' GB';
86
+ }
@@ -58,7 +58,7 @@ export async function produce(kernel = '', clone = false, homecrypt = false, ful
58
58
  else if (this.fullcrypt) {
59
59
  this.luksMappedName = 'root.img';
60
60
  }
61
- this.luksFile = `/tmp/${this.luksMappedName}`;
61
+ this.luksFile = `/var/tmp/${this.luksMappedName}`;
62
62
  this.luksMappedName = this.luksMappedName;
63
63
  this.luksMountpoint = `/tmp/mnt/${this.luksMappedName}`;
64
64
  this.luksDevice = `/dev/mapper/${this.luksMappedName}`;
@@ -131,8 +131,6 @@ export async function produce(kernel = '', clone = false, homecrypt = false, ful
131
131
  * homecrypt/fullcrypt/clone/standard
132
132
  */
133
133
  if (this.homecrypt) {
134
- this.settings.config.user_opt = 'live'; // patch for humans
135
- this.settings.config.user_opt_passwd = 'evolution';
136
134
  Utils.warning("eggs will SAVE users and users' data ENCRYPTED on the live (ISO)/live/home.img");
137
135
  }
138
136
  else if (this.fullcrypt) {
@@ -195,10 +193,10 @@ export async function produce(kernel = '', clone = false, homecrypt = false, ful
195
193
  /**
196
194
  * installer
197
195
  */
198
- this.incubator = new Incubator(this.settings.remix, this.settings.distro, this.settings.config.user_opt, this.theme, this.clone || this.fullcrypt, verbose);
196
+ this.incubator = new Incubator(this.settings.remix, this.settings.distro, this.settings.config.user_opt, this.theme, this.clone || this.fullcrypt || this.homecrypt, verbose);
199
197
  await this.incubator.config(release);
200
- // Qua inseriamo uno sleep di un minuto
201
- await Utils.sleep(5000); // 5 secondo
198
+ // 5 secondi di sleep
199
+ await Utils.sleep(5000);
202
200
  await this.bindLiveFs();
203
201
  await this.bindVfs();
204
202
  /**
@@ -228,36 +226,80 @@ export async function produce(kernel = '', clone = false, homecrypt = false, ful
228
226
  await this.initrdDracut();
229
227
  }
230
228
  }
231
- // We dont' need more
229
+ // We don't need more
232
230
  await this.ubindVfs();
233
- const cleanSystem = !(this.clone || this.fullcrypt);
234
- if (cleanSystem) {
235
- /**
236
- * SOLO per homecrypt e standard
237
- */
231
+ // mistero della Fede
232
+ await this.editLiveFs();
233
+ /**
234
+ * Autologin solse per non clone
235
+ */
236
+ if (!(this.clone || this.homecrypt || this.fullcrypt)) {
238
237
  await this.usersRemove();
239
238
  await this.userCreateLive();
240
239
  if (Pacman.isInstalledGui()) {
240
+ // add GUI autologin
241
241
  await this.createXdgAutostart(this.settings.config.theme, myAddons, myLinks, noicons);
242
- /**
243
- * GUI installed but NOT Desktop Manager: just create motd and issue
244
- */
245
- // if (displaymanager().length > 0) {
246
- // this.cliAutologin.addIssue(this.settings.distro.distroId, this.settings.distro.codenameId, this.settings.config.user_opt, this.settings.config.user_opt_passwd, this.settings.config.root_passwd, this.settings.work_dir.merged)
247
- // this.cliAutologin.addMotd(this.settings.distro.distroId, this.settings.distro.codenameId, this.settings.config.user_opt, this.settings.config.user_opt_passwd, this.settings.config.root_passwd, this.settings.work_dir.merged)
248
- // }
249
242
  }
250
243
  else {
244
+ // add cli-autologin
251
245
  this.cliAutologin.add(this.settings.distro.distroId, this.settings.distro.codenameId, this.settings.config.user_opt, this.settings.config.user_opt_passwd, this.settings.config.root_passwd, this.settings.work_dir.merged);
252
246
  }
253
247
  }
254
- await this.editLiveFs(clone);
248
+ /**
249
+ * configurazione homecrytp
250
+ */
255
251
  if (this.homecrypt) {
252
+ // Occorre forzare il login CLI
253
+ if (Utils.isSystemd()) {
254
+ const systemdDir = `${this.settings.work_dir.merged}/etc/systemd/system`;
255
+ // remove eventuali autologin
256
+ if (fs.existsSync(`${systemdDir}/getty@tty1.service.d`)) {
257
+ await exec(`rm -rf ${systemdDir}/getty@tty1.service.d/*`);
258
+ }
259
+ else {
260
+ await exec(`mkdir -p ${systemdDir}/getty@tty1.service.d`);
261
+ }
262
+ let content = ``;
263
+ content += `[Service]\n`;
264
+ content += `ExecStart=\n`;
265
+ content += `ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear %I \$TERM\n`;
266
+ fs.writeFileSync(`${systemdDir}/getty@tty1.service.d/no-autologin.conf`, content);
267
+ }
268
+ else if (Utils.isSysvinit()) {
269
+ const inittabPath = `${this.settings.work_dir.merged}/etc/inittab`;
270
+ if (fs.existsSync(inittabPath)) {
271
+ // Non funziona per Devuan
272
+ let content = fs.readFileSync(inittabPath, 'utf-8');
273
+ // La riga standard per Devuan/Debian SysVinit.
274
+ // 1: ID
275
+ // 2345: Runlevels in cui attivarlo (Nota che il tuo boot è in runlevel 2)
276
+ // respawn: Riavvia getty se muore
277
+ // /sbin/getty 38400 tty1: Il comando
278
+ const tty1Line = '1:2345:respawn:/sbin/agetty --noclear tty1 linux';
279
+ // Rimuoviamo vecchie definizioni di tty1 (anche se commentate o diverse)
280
+ // Cerca righe che iniziano con "1:"
281
+ const regex = /^1:.*$/gm;
282
+ if (regex.test(content)) {
283
+ // Sostituisce la riga esistente
284
+ content = content.replace(regex, tty1Line);
285
+ console.log('Fixed tty1 in inittab (replaced)');
286
+ }
287
+ else {
288
+ // Se non c'è, la aggiunge in fondo (dopo i commenti iniziali)
289
+ content += `\n${tty1Line}\n`;
290
+ console.log('Fixed tty1 in inittab (appended)');
291
+ }
292
+ // FIX CRITICO PER --cryptedhome / LIVE:
293
+ // A volte le live commentano tutte le tty per usare i propri hook.
294
+ // Assicurati che non ci siano altre righe strane che confliggono.
295
+ fs.writeFileSync(inittabPath, content);
296
+ }
297
+ }
256
298
  /**
257
299
  * homecrypt: installa il supporto
258
300
  */
259
301
  const squashfsRoot = this.settings.work_dir.merged;
260
- const homeImgPath = this.distroLliveMediumPath + 'live/home.img';
302
+ const homeImgPath = this.distroLiveMediumPath + 'live/home.img';
261
303
  this.installHomecryptSupport(squashfsRoot, homeImgPath);
262
304
  }
263
305
  mksquashfsCmd = await this.makeSquashfs(scriptOnly, includeRootHome);