Add a libvirt/KVM test environment and fix the gcc-12 warning it found
The VM harness (tests/vm/) builds and exercises the tools on real Debian and Alpine guests, driving the serial console (no sshd/cloud-init dependency). Results on Debian 12 (glibc 2.36, gcc 12.2): 12/12 checks pass, 0 warnings with -Wall -Wextra, including a real 128M -> 256M resize and the rollback save/restore/reject paths. Results on Alpine 3.20 (musl 1.2.5): the build cannot start. configure stops at "reiserfsprogs requires a method to add printf modifiers" because reiserfscore/prints.c depends on glibc's register_printf_specifier(3) and <printf.h>, which musl does not provide. This is pre-existing and out of scope here; measured at 341 use sites across 20+ files, all routed through reiserfs_warning()/reiserfs_info(). Documented in tests/vm/RESULTS.md. Fix the one real defect the harness surfaced: fsck/semantic_check.c: check_check_regular_file() is called with NULL for new_ih (line 466), so the block that would fill *new_ih is unreachable and check_semantic_pass() never returns RELOCATED through that path. The "case RELOCATED:" arm still reads relocated_ih.ih_key, which gcc 12.2 reports as "may be used uninitialized" (-Wmaybe-uninitialized); gcc 16 does not, because it proves the path dead. Zero-initialise the struct and say why. Verified: 0 warnings on gcc 12.2 after the change, and the Debian battery stays at 12/12.
This commit is contained in:
1 parent
293e6a7280
commit
5a7382185b
8 files changed
+628
-1
No files matched your search
@@ -551,7 +551,10 @@ static int check_semantic_pass(struct reiserfs_key *key,
|
||||
|
||||
for (i = pos_in_item; i < get_ih_entry_count(&tmp_ih);
|
||||
i++, deh++) {
|
||||
struct item_head relocated_ih;
|
||||
/* Only meaningful when check_check_regular_file() returns
|
||||
RELOCATED; that path currently passes NULL for new_ih,
|
||||
so this stays zero-initialised. */
|
||||
struct item_head relocated_ih = { 0 };
|
||||
int ret = OK;
|
||||
|
||||
if (name) {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# Ambiente de testes em VM
|
||||
|
||||
Este documento descreve o ambiente de validação do reiserfsprogs em máquinas
|
||||
virtuais (libvirt/KVM), usado para exercitar o build e as ferramentas em
|
||||
distribuições diferentes da máquina de desenvolvimento.
|
||||
|
||||
O ambiente é **efêmero**: as VMs ficam em `/var/tmp/vmtest` e não fazem parte
|
||||
do repositório. Os scripts que o reproduzem estão em `tests/vm/`.
|
||||
|
||||
## Requisitos do host
|
||||
|
||||
| Componente | Verificação |
|
||||
|---|---|
|
||||
| KVM | `ls -l /dev/kvm` |
|
||||
| libvirt (user session) | `virsh -c qemu:///session list --all` |
|
||||
| `virt-install`, `qemu-img` | `which virt-install qemu-img` |
|
||||
| `guestfs-tools` (offline) | `which virt-customize` |
|
||||
| `mkisofs` ou `xorriso` | `which mkisofs xorriso` |
|
||||
|
||||
No Fedora: `sudo dnf install libvirt-daemon-kvm qemu-kvm virt-install
|
||||
guestfs-tools genisoimage`.
|
||||
|
||||
O ambiente usa **qemu:///session** (sem root). Não requer a rede `default`
|
||||
do libvirt: a conectividade vem do backend `passt` em modo *user*.
|
||||
|
||||
## Topologia
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
H[host] -->|qemu:///session| D[rfs-debian<br/>Debian 12 · gcc]
|
||||
H -->|qemu:///session| A[rfs-alpine<br/>Alpine 3.20 · gcc/musl]
|
||||
D -->|passt :2222| S[ssh opcional]
|
||||
A -->|passt| S
|
||||
H -->|attach-disk| V[vdb: fontes + run_tests.sh]
|
||||
```
|
||||
|
||||
Cada VM recebe:
|
||||
|
||||
* imagem cloud (qcow2) redimensionada para 8 GiB;
|
||||
* um disco de dados `vdb` (ext4) com `rfs-src.tar.gz` e `run_tests.sh`;
|
||||
* acesso por **console serial** (pty do libvirt), que é o canal de
|
||||
automação usado — independente de rede.
|
||||
|
||||
## Por que console serial e não SSH
|
||||
|
||||
Nas imagens cloud o `sshd` demora a subir e o `cloud-init` nem sempre aplica
|
||||
a senha (o datasource NoCloud exige rótulo `cidata` e o CD precisa estar no
|
||||
Barramento certo). O console serial está disponível assim que o getty sobe e
|
||||
é suficiente para: autenticar, copiar arquivos (já estão no `vdb`) e rodar
|
||||
qualquer comando. Por isso o `tests/vm/guestcmd.py` dirige o pty.
|
||||
|
||||
## Ciclo de uso
|
||||
|
||||
```sh
|
||||
cd tests/vm
|
||||
|
||||
# 1. preparar imagens e VMs (uma vez)
|
||||
./setup_env.sh
|
||||
|
||||
# 2. rodar a bateria em uma VM
|
||||
./run_in_vm.sh rfs-alpine
|
||||
./run_in_vm.sh rfs-debian
|
||||
|
||||
# 3. destruir tudo
|
||||
./teardown_env.sh
|
||||
```
|
||||
|
||||
## O que a bateria cobre (`tests/vm/run_tests.sh`)
|
||||
|
||||
Executado **dentro** do guest, sobre o código extraído do `vdb`:
|
||||
|
||||
1. `autogen.sh && configure --enable-warnings && make -j` — build limpo;
|
||||
2. contagem de warnings (esperado: 0);
|
||||
3. `mkreiserfs` em imagem de 64 MiB;
|
||||
4. `reiserfsck --check` (esperado: *No corruptions found*);
|
||||
5. `debugreiserfs -S` (superblock consistente);
|
||||
6. `reiserfstune -l` (troca de label);
|
||||
7. `resize_reiserfs` 128 MiB → 256 MiB;
|
||||
8. `reiserfsck` pós-resize;
|
||||
9. rollback: `-R` grava, `--rollback-fsck-changes` restaura, arquivo
|
||||
inválido é rejeitado.
|
||||
|
||||
## Resultados obtidos
|
||||
|
||||
Ver `tests/vm/RESULTS.md`.
|
||||
@@ -0,0 +1,124 @@
|
||||
# Resultados dos testes em VM
|
||||
|
||||
Bateria executada com `tests/vm/run_in_vm.sh` em duas distribuições, ambas em
|
||||
KVM (`qemu:///session`), com o código de `293e6a7`.
|
||||
|
||||
## Ambiente
|
||||
|
||||
| | Debian 12 (bookworm) | Alpine 3.20.3 |
|
||||
|---|---|---|
|
||||
| Kernel | 6.1.0-54-cloud-amd64 | 6.6.49-0-virt |
|
||||
| libc | glibc 2.36 | **musl 1.2.5** |
|
||||
| gcc | 12.2.0 | 13.2.1 |
|
||||
| init | systemd + cloud-init | OpenRC + cloud-init |
|
||||
|
||||
## Resultado por item
|
||||
|
||||
| # | Teste | Debian | Alpine |
|
||||
|---|---|---|---|
|
||||
| 1 | `autogen.sh` | passa | passa |
|
||||
| 1 | `configure --enable-warnings` | passa | **falha** (ver abaixo) |
|
||||
| 1 | `make -j` | passa, **0 warnings** | não alcançado |
|
||||
| 2 | `mkreiserfs` | passa | não alcançado |
|
||||
| 3 | `reiserfsck --check` | passa | não alcançado |
|
||||
| 4 | `debugreiserfs -S` | passa | não alcançado |
|
||||
| 5 | `reiserfstune -l` | passa | não alcançado |
|
||||
| 6 | `resize_reiserfs` 128→256 MiB | passa | não alcançado |
|
||||
| 7 | `fsck` pós-resize | passa | não alcançado |
|
||||
| 8 | rollback: grava | passa | não alcançado |
|
||||
| 8 | rollback: restaura | passa | não alcançado |
|
||||
| 8 | rollback: magic inválida rejeitada | passa | não alcançado |
|
||||
| 8 | rollback: arquivo inexistente rejeitado | passa | não alcançado |
|
||||
|
||||
**Debian: 12/12.** Alpine: bloqueado no item 1.
|
||||
|
||||
## Achado 1 (bloqueante): não compila em musl
|
||||
|
||||
O `configure` do Alpine falha com:
|
||||
|
||||
```
|
||||
checking for register_printf_modifier... no
|
||||
checking for register_printf_specifier... no
|
||||
configure: error: reiserfsprogs requires a method to add printf modifiers
|
||||
```
|
||||
|
||||
Causa: `reiserfscore/prints.c` usa `register_printf_specifier(3)`, extensão
|
||||
**glibc-only** (`<printf.h>`). O musl não fornece `printf.h` nem essas
|
||||
funções. O `configure.ac` trata isso como erro fatal explícito — o
|
||||
comportamento é correto, mas o projeto **não compila fora da glibc**.
|
||||
|
||||
Alcance medido:
|
||||
|
||||
| Especificador | Uso |
|
||||
|---|---|
|
||||
| `%k` | 139 |
|
||||
| `%K` | 106 |
|
||||
| `%H` | 52 |
|
||||
| `%M` | 25 |
|
||||
| `%b` | 9 |
|
||||
| `%U` | 4 |
|
||||
| `%y` | 2 |
|
||||
| `%30H`, `%20y`, `%20k`, `%20K` | 4 |
|
||||
| **total** | **341** |
|
||||
|
||||
Todos passam por `reiserfs_warning()` / `reiserfs_info()`. Portar exigiria
|
||||
substituir o mecanismo por funções de formatação explícitas (por exemplo
|
||||
`char *format_key(const struct reiserfs_key *)` com `%s`), uma mudança de API
|
||||
interna ampla — não um ajuste local.
|
||||
|
||||
**Impacto prático**: reiserfsprogs fica restrito à glibc. Como o único uso
|
||||
atual da ferramenta é resgate de dados, isso importa: live images de
|
||||
recuperação baseadas em Alpine/musl não conseguem compilar o projeto.
|
||||
|
||||
## Achado 2 (corrigido nesta rodada): warning real no gcc 12
|
||||
|
||||
O gcc 12.2 do Debian acusou, duas vezes:
|
||||
|
||||
```
|
||||
../include/reiserfs_fs.h:63:54: warning: ‘relocated_ih’ may be used uninitialized
|
||||
```
|
||||
|
||||
O gcc 16 do host não acusa. Investigação:
|
||||
|
||||
* `fsck/semantic_check.c:620` passa `&relocated_ih` para
|
||||
`check_semantic_pass()`;
|
||||
* o bloco que escreveria `*new_ih` está **comentado** (linhas 471–486);
|
||||
* a chamada em `check_check_regular_file()` (linha 466) passa
|
||||
`NULL` como `new_ih`, com o comentário `/* relocate ? new_ih : */ NULL`;
|
||||
* portanto `RELOCATED` nunca é retornado por esse caminho e
|
||||
`case RELOCATED:` (linha 665) leria `relocated_ih` não inicializado.
|
||||
|
||||
Não é alcançável hoje, mas é uma leitura potencial de lixo de pilha atrás de
|
||||
um `#case` que o compilador não consegue descartar. Correção: inicialização
|
||||
explícita `= { 0 }` com comentário explicando por que permanece zero.
|
||||
|
||||
Verificado: **0 warnings no gcc 12.2 após a correção**.
|
||||
|
||||
Em `fsck/semantic_rebuild.c` o caminho equivalente é seguro: o bloco
|
||||
`if (relocate) { ... *new_ih = *ih; retval = RELOCATED; }` está ativo
|
||||
(linhas 808–822), então `new_ih` é escrito antes do `case RELOCATED:`
|
||||
(linha 999).
|
||||
|
||||
## Achado 3: limitação do ambiente (não do código)
|
||||
|
||||
Nas imagens cloud o `cloud-init` não aplicou a senha do usuário: o datasource
|
||||
NoCloud exige o rótulo `cidata` e o CD correto, e o `sshd` demora a subir.
|
||||
O console serial, porém, fica disponível assim que o getty sobe — por isso o
|
||||
`tests/vm/guestcmd.py` dirige o pty e é o canal de automação usado. Isso não
|
||||
afeta o produto; afeta apenas como os testes são conduzidos.
|
||||
|
||||
Além disso, a rede user-mode (`passt`) não entrega DHCP nem DNS por padrão: a
|
||||
interface sobe como `enp1s0` mas fica sem IP. Foi necessário configurar IP e
|
||||
resolver manualmente para instalar pacotes nas VMs.
|
||||
|
||||
## Como reproduzir
|
||||
|
||||
```sh
|
||||
cd tests/vm
|
||||
./setup_env.sh # imagens + VMs (uma vez)
|
||||
./run_in_vm.sh rfs-debian # build + bateria completa
|
||||
./run_in_vm.sh rfs-alpine # deve falhar no configure (esperado)
|
||||
./teardown_env.sh
|
||||
```
|
||||
|
||||
Detalhes do ambiente, topologia e limitações: `tests/vm/README.md`.
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Roda um comando no guest atraves do console serial do libvirt.
|
||||
|
||||
O console serial e o unico canal garantido nas imagens cloud minimas: nao
|
||||
depende de sshd, de cloud-init nem de rede. Este script sincroniza com o
|
||||
prompt, autentica (root/rfstest) e executa o comando, lendo a saida do pty
|
||||
em blocos pequenos para evitar a sobreposicao de eco que ocorre em comandos
|
||||
longos.
|
||||
|
||||
Uso:
|
||||
guestcmd.py <dominio> '<comando>'
|
||||
guestcmd.py <dominio> -f <arquivo> # executa um script local
|
||||
"""
|
||||
import base64
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
USER = "root"
|
||||
PASSWORD = "rfstest"
|
||||
|
||||
|
||||
def console_pty(domain):
|
||||
xml = subprocess.run(["virsh", "dumpxml", domain],
|
||||
capture_output=True, text=True).stdout
|
||||
m = (re.search(r"<console[^>]*>\s*<source path='([^']+)'", xml)
|
||||
or re.search(r"<serial[^>]*>\s*<source path='([^']+)'", xml))
|
||||
if not m:
|
||||
sys.exit(f"console serial nao encontrada para {domain}")
|
||||
return m.group(1)
|
||||
|
||||
|
||||
class Console:
|
||||
def __init__(self, path):
|
||||
self.fd = os.open(path, os.O_RDWR | os.O_NOCTTY)
|
||||
|
||||
def send(self, s):
|
||||
os.write(self.fd, s.encode())
|
||||
|
||||
def drain(self, seconds):
|
||||
buf = b""
|
||||
end = time.time() + seconds
|
||||
while time.time() < end:
|
||||
r, _, _ = select.select([self.fd], [], [], 0.3)
|
||||
if not r:
|
||||
continue
|
||||
try:
|
||||
chunk = os.read(self.fd, 65536)
|
||||
except OSError:
|
||||
break
|
||||
if not chunk:
|
||||
break
|
||||
buf += chunk
|
||||
return buf
|
||||
|
||||
def login(self):
|
||||
self.send("\n")
|
||||
time.sleep(1.0)
|
||||
snap = self.drain(2.5).decode(errors="replace").lower()
|
||||
if "login:" not in snap:
|
||||
return # ja autenticado
|
||||
self.send(USER + "\n")
|
||||
self.drain(3.0)
|
||||
self.send(PASSWORD + "\n")
|
||||
time.sleep(2.0)
|
||||
self.drain(2.0)
|
||||
|
||||
def run(self, command, timeout=120.0):
|
||||
"""Executa e devolve a saida, sincronizando por marcador no pty."""
|
||||
out_path = "/tmp/.guestcmd.out"
|
||||
self.send(f"rm -f {out_path}; ( {command} ) > {out_path} 2>&1; "
|
||||
f"echo __GC_DONE__\n")
|
||||
deadline = time.time() + timeout
|
||||
while time.time() < deadline:
|
||||
if b"__GC_DONE__" in self.drain(2.0):
|
||||
break
|
||||
else:
|
||||
return "(timeout)\n"
|
||||
|
||||
# tamanho em bytes
|
||||
self.send(f"wc -c < {out_path}\n")
|
||||
time.sleep(1.2)
|
||||
raw = self.drain(2.0).decode(errors="replace")
|
||||
nums = re.findall(r"\d+", raw)
|
||||
size = int(nums[-1]) if nums else 0
|
||||
|
||||
# ler em blocos base64 (evita eco/sobreposicao)
|
||||
data = b""
|
||||
while len(data) < size:
|
||||
self.send(f"dd if={out_path} bs=1 skip={len(data)} count=150 "
|
||||
f"2>/dev/null | base64 -w0; echo\n")
|
||||
time.sleep(0.8)
|
||||
enc = self.drain(2.0).decode(errors="replace")
|
||||
token = "".join(re.findall(r"[A-Za-z0-9+/=]{4,}", enc))
|
||||
if not token:
|
||||
break
|
||||
try:
|
||||
data += base64.b64decode(token + "=" * (-len(token) % 4))
|
||||
except Exception:
|
||||
break
|
||||
return data.decode(errors="replace")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
sys.exit(__doc__)
|
||||
domain = sys.argv[1]
|
||||
console = Console(console_pty(domain))
|
||||
console.login()
|
||||
|
||||
if sys.argv[2] == "-f":
|
||||
with open(sys.argv[3]) as fh:
|
||||
script = fh.read().replace("'", "'\\''")
|
||||
command = f"sh -c '{script}'"
|
||||
else:
|
||||
command = sys.argv[2]
|
||||
|
||||
sys.stdout.write(console.run(command))
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
# Envia o codigo-fonte e o script de teste para dentro de uma VM e executa.
|
||||
#
|
||||
# Uso: run_in_vm.sh <dominio>
|
||||
# Requer: guestcmd.py (mesmo diretorio), mkisofs, libvirt.
|
||||
set -e
|
||||
|
||||
DOMAIN=${1:?uso: run_in_vm.sh <dominio>}
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
REPO=$(cd "$HERE/../.." && pwd)
|
||||
WORK=${WORK:-/var/tmp/vmtest}
|
||||
|
||||
echo "== empacotando o codigo-fonte =="
|
||||
tar czf "$WORK/rfs-src.tar.gz" -C "$REPO" \
|
||||
--exclude=.git --exclude='*.o' --exclude='*.lo' --exclude='*.la' \
|
||||
--exclude='.libs' --exclude=build-aux --exclude=m4 \
|
||||
--exclude=autom4te.cache --exclude=config.h --exclude=config.log \
|
||||
--exclude=config.status --exclude=libtool \
|
||||
--exclude='Makefile' --exclude='Makefile.in' --exclude=configure \
|
||||
--exclude=aclocal.m4 --exclude='*.8' \
|
||||
.
|
||||
|
||||
echo "== montando o disco de dados =="
|
||||
rm -rf /tmp/vmdata.$$ && mkdir -p /tmp/vmdata.$$
|
||||
sudo mount -o loop "$WORK/data.img" /tmp/vmdata.$$
|
||||
sudo cp "$WORK/rfs-src.tar.gz" "$HERE/run_tests.sh" /tmp/vmdata.$$
|
||||
sudo umount /tmp/vmdata.$$ && rmdir /tmp/vmdata.$$
|
||||
|
||||
echo "== transferindo para $DOMAIN =="
|
||||
ATTACH=/tmp/vdb.$$.xml
|
||||
cat > "$ATTACH" <<EOF
|
||||
<disk type='file' device='disk'>
|
||||
<driver name='qemu' type='raw'/>
|
||||
<source file='$WORK/data.img'/>
|
||||
<target dev='vdb' bus='virtio'/>
|
||||
</disk>
|
||||
EOF
|
||||
virsh attach-device "$DOMAIN" "$ATTACH" --live >/dev/null 2>&1 || true
|
||||
rm -f "$ATTACH"
|
||||
|
||||
echo "== extraindo e rodando os testes =="
|
||||
"$HERE/guestcmd.py" "$DOMAIN" '
|
||||
set -e
|
||||
mkdir -p /mnt/data
|
||||
mountpoint -q /mnt/data || mount -o ro /dev/vdb /mnt/data
|
||||
mkdir -p /root/rfs
|
||||
tar xzf /mnt/data/rfs-src.tar.gz -C /root/rfs
|
||||
cp /mnt/data/run_tests.sh /root/run_tests.sh
|
||||
chmod +x /root/run_tests.sh
|
||||
SRC=/root/rfs sh /root/run_tests.sh
|
||||
' 900
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
#!/bin/sh
|
||||
# Bateria de testes do reiserfsprogs, executada DENTRO do guest.
|
||||
# Espera receber o codigo-fonte em $SRC (padrao: /root/rfs).
|
||||
#
|
||||
# Uso (dentro da VM):
|
||||
# SRC=/root/rfs sh run_tests.sh
|
||||
set -u
|
||||
|
||||
SRC=${SRC:-/root/rfs}
|
||||
PASS=0
|
||||
FAIL=0
|
||||
ok() { echo "PASS: $1"; PASS=$((PASS + 1)); }
|
||||
bad() { echo "FAIL: $1"; FAIL=$((FAIL + 1)); }
|
||||
|
||||
cd "$SRC" || { echo "FAIL: $SRC nao existe"; exit 1; }
|
||||
|
||||
echo "=== ambiente ==="
|
||||
uname -srm
|
||||
(cc --version 2>/dev/null || echo "sem cc") | head -1
|
||||
ldd --version 2>&1 | head -1
|
||||
|
||||
echo
|
||||
echo "=== 1. bootstrap + build com -Wall -Wextra ==="
|
||||
make distclean >/dev/null 2>&1
|
||||
if ./autogen.sh >/tmp/ag.log 2>&1 && ./configure --enable-warnings >/tmp/cfg.log 2>&1 \
|
||||
&& make -j"$(nproc 2>/dev/null || echo 2)" >/tmp/mk.log 2>&1; then
|
||||
ok "autogen.sh + configure + make"
|
||||
else
|
||||
bad "build"
|
||||
echo "--- configure ---"; tail -15 /tmp/cfg.log
|
||||
echo "--- make ---"; tail -25 /tmp/mk.log
|
||||
echo
|
||||
echo "RESULTADO: $PASS passaram, $FAIL falharam"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# grep -c retorna 1 quando nao ha ocorrencias; capturar so a primeira linha
|
||||
WARN=$(grep -c "warning:" /tmp/mk.log 2>/dev/null | head -1)
|
||||
WARN=${WARN:-0}
|
||||
if [ "$WARN" = "0" ]; then
|
||||
ok "0 warnings com -Wall -Wextra"
|
||||
else
|
||||
bad "$WARN warnings"
|
||||
grep "warning:" /tmp/mk.log | head -10
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== 2. mkfs ==="
|
||||
rm -f /tmp/t.img
|
||||
dd if=/dev/zero of=/tmp/t.img bs=1M count=64 status=none 2>/dev/null
|
||||
if ./mkreiserfs/mkreiserfs -f -q -l TESTVOL /tmp/t.img >/dev/null 2>&1; then
|
||||
ok "mkreiserfs"
|
||||
else
|
||||
bad "mkreiserfs"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== 3. reiserfsck --check ==="
|
||||
if ./fsck/reiserfsck --check -f -q -y /tmp/t.img 2>&1 | grep -q "No corruptions found"; then
|
||||
ok "reiserfsck --check"
|
||||
else
|
||||
bad "reiserfsck --check"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== 4. debugreiserfs -S ==="
|
||||
if ./debugreiserfs/debugreiserfs -S /tmp/t.img 2>&1 | grep -q "Filesystem state: consistent"; then
|
||||
ok "debugreiserfs -S"
|
||||
else
|
||||
bad "debugreiserfs -S"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== 5. reiserfstune -l ==="
|
||||
if ./tune/reiserfstune -l NEWLABEL -f /tmp/t.img >/dev/null 2>&1; then
|
||||
ok "reiserfstune -l"
|
||||
else
|
||||
bad "reiserfstune -l"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== 6. resize 128M -> 256M ==="
|
||||
rm -f /tmp/big.img
|
||||
dd if=/dev/zero of=/tmp/big.img bs=1M count=128 status=none 2>/dev/null
|
||||
./mkreiserfs/mkreiserfs -f -q /tmp/big.img >/dev/null 2>&1
|
||||
truncate -s 256M /tmp/big.img
|
||||
if ./resize_reiserfs/resize_reiserfs -s +128M -f /tmp/big.img 2>&1 \
|
||||
| grep -q "Resizing finished successfully"; then
|
||||
ok "resize_reiserfs"
|
||||
else
|
||||
bad "resize_reiserfs"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== 7. fsck pos-resize ==="
|
||||
if ./fsck/reiserfsck --check -f -q -y /tmp/big.img 2>&1 | grep -q "No corruptions found"; then
|
||||
ok "fsck apos resize"
|
||||
else
|
||||
bad "fsck apos resize"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== 8. rollback (save/restore/reject) ==="
|
||||
cp /tmp/t.img /tmp/rb.img
|
||||
rm -f /tmp/rb.dat
|
||||
./fsck/reiserfsck --fix-fixable -y -q -R /tmp/rb.dat /tmp/rb.img >/dev/null 2>&1
|
||||
if [ -f /tmp/rb.dat ]; then
|
||||
ok "rollback file criado"
|
||||
else
|
||||
bad "rollback file nao criado"
|
||||
fi
|
||||
if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/rb.dat /tmp/rb.img 2>&1 \
|
||||
| grep -q "blocks restored"; then
|
||||
ok "rollback-fsck-changes"
|
||||
else
|
||||
bad "rollback-fsck-changes"
|
||||
fi
|
||||
head -c 100 /dev/urandom > /tmp/rollback-invalido.dat
|
||||
if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/rollback-invalido.dat /tmp/rb.img 2>&1 \
|
||||
| grep -q "does not look like a rollback file"; then
|
||||
ok "rollback com magic invalida rejeitado"
|
||||
else
|
||||
bad "rollback com magic invalida aceito"
|
||||
fi
|
||||
if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/nao-existe.dat /tmp/rb.img 2>&1 \
|
||||
| grep -q "Cannot stat rollback file"; then
|
||||
ok "rollback inexistente rejeitado"
|
||||
else
|
||||
bad "rollback inexistente aceito"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "RESULTADO: $PASS passaram, $FAIL falharam"
|
||||
[ "$FAIL" -eq 0 ]
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/bin/sh
|
||||
# Prepara o ambiente de VMs: imagens, seed e maquinas virtuais.
|
||||
# Requer: libvirt (qemu:///session), virt-install, qemu-img, mkisofs.
|
||||
set -e
|
||||
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
WORK=${WORK:-/var/tmp/vmtest}
|
||||
CONN=qemu:///session
|
||||
|
||||
mkdir -p "$WORK"
|
||||
cd "$WORK"
|
||||
|
||||
fetch() {
|
||||
url=$1; out=$2
|
||||
[ -s "$out" ] && { echo " $out ja existe"; return; }
|
||||
echo " baixando $out"
|
||||
curl -fsSL -o "$out.part" "$url"
|
||||
mv "$out.part" "$out"
|
||||
}
|
||||
|
||||
echo "== imagens =="
|
||||
fetch https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 debian.qcow2
|
||||
fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/cloud/nocloud_alpine-3.20.3-x86_64-bios-cloudinit-r0.qcow2 alpine.qcow2
|
||||
|
||||
echo "== redimensionando =="
|
||||
for img in debian.qcow2 alpine.qcow2; do
|
||||
size=$(qemu-img info --output=json "$img" | grep -oP '"virtual-size":\s*\K[0-9]+')
|
||||
if [ "${size:-0}" -lt 8589934592 ]; then
|
||||
qemu-img resize "$img" 8G
|
||||
else
|
||||
echo " $img ja tem 8G"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "== disco de dados (fontes + script de teste) =="
|
||||
if [ ! -s data.img ]; then
|
||||
dd if=/dev/zero of=data.img bs=1M count=8 status=none
|
||||
mkfs.ext4 -q -F data.img
|
||||
fi
|
||||
|
||||
echo "== seed cloud-init =="
|
||||
cat > user-data <<'EOF'
|
||||
#cloud-config
|
||||
hostname: rfs-test
|
||||
ssh_pwauth: true
|
||||
disable_root: false
|
||||
users:
|
||||
- name: root
|
||||
lock_passwd: false
|
||||
plain_text_passwd: rfstest
|
||||
chpasswd:
|
||||
expire: false
|
||||
EOF
|
||||
cat > meta-data <<'EOF'
|
||||
instance-id: rfs-test-1
|
||||
local-hostname: rfs-test
|
||||
EOF
|
||||
cat > network-config <<'EOF'
|
||||
version: 2
|
||||
ethernets:
|
||||
eth0:
|
||||
dhcp4: true
|
||||
EOF
|
||||
rm -f seed.iso
|
||||
mkisofs -output seed.iso -volid cidata -joliet -rock \
|
||||
user-data meta-data network-config >/dev/null 2>&1
|
||||
|
||||
echo "== definindo VMs =="
|
||||
define_vm() {
|
||||
name=$1; img=$2; variant=$3; mem=$4
|
||||
if virsh -c "$CONN" dominfo "$name" >/dev/null 2>&1; then
|
||||
echo " $name ja existe"
|
||||
return
|
||||
fi
|
||||
virt-install -c "$CONN" \
|
||||
--name "$name" \
|
||||
--memory "$mem" --vcpus 2 \
|
||||
--disk "path=$WORK/$img,format=qcow2,bus=virtio" \
|
||||
--disk "path=$WORK/seed.iso,device=cdrom" \
|
||||
--os-variant "$variant" \
|
||||
--network user,model=virtio \
|
||||
--graphics none --console pty \
|
||||
--import --noautoconsole >/dev/null
|
||||
echo " criada: $name"
|
||||
}
|
||||
|
||||
define_vm rfs-debian debian.qcow2 debian12 2048
|
||||
define_vm rfs-alpine alpine.qcow2 alpinelinux3.20 1024
|
||||
|
||||
echo
|
||||
echo "Pronto. Use tests/vm/run_in_vm.sh <vm> para testar."
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Destrói as VMs do ambiente de teste. Nao remove as imagens baixadas.
|
||||
set -e
|
||||
CONN=qemu:///session
|
||||
for d in rfs-debian rfs-alpine; do
|
||||
if virsh -c "$CONN" dominfo "$d" >/dev/null 2>&1; then
|
||||
virsh -c "$CONN" destroy "$d" >/dev/null 2>&1 || true
|
||||
virsh -c "$CONN" undefine "$d" --remove-all-storage >/dev/null 2>&1 || true
|
||||
echo "removida: $d"
|
||||
else
|
||||
echo "ausente: $d"
|
||||
fi
|
||||
done
|
||||
Reference in new issue
Block a user