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);
|
for (i = pos_in_item; i < get_ih_entry_count(&tmp_ih);
|
||||||
i++, deh++) {
|
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;
|
int ret = OK;
|
||||||
|
|
||||||
if (name) {
|
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