Programarea Calculatoarelor 2 - Laborator 8

Să se scrie un program care afișează și modifică datele din tabela de partiții a unui disc, similar cu programul fdisk sub DOS/Windows sau UNIX.

Un sector de disc conține 0x200 (512) octeți. Tabela de partiții se află în primul sector al discului, începând cu deplasamentul (octetul) 0x1BE. Ea conține 4 intrări a câte 16 octeți. (Cei doi octeți rămași la sfârșitul sectorului sunt ocupați de "semnătura" convențională 0x55 0xAA). Conținutul unei intrări din tabela de partiții este următorul:
- un octet care indică dacă partiția e activă (0x80) sau nu (0), adică dacă de pe ea se poate lansa ("boot") spre încărcare sistemul de operare.
- o secvență de 3 octeți care indică poziția primului sector fizic al partiției (în format HSC - head, sector, cylinder - descris ulterior)
- un octet identificator pentru tipul de partiție (0 pentru partiție goală, 7 pentru NTFS, 0x83 pentru Linux, etc.)
- o altă secvență de 3 octeți în format HSC indicând poziția ultimului sector fizic al partiției
- un întreg fără semn pe 4 octeți indicând numărul logic al primului sector al partiției (numărul de sectoare care-l preced pe disc)
- un întreg fără semn pe 4 octeți indicând dimensiunea partiției în sectoare
Formatul HSC conține:
- în primul octet, numărul capului de citire (numerotat de la zero, cel mult 255)
- în cei 6 biți inferiori ai octetului al doilea, numărul sectorului în cadrul unei piste (numerotat de la 1, cel mult 63)
- în octetul al treilea, și biții 7 și 6 din octetul al doilea (considerați ca biți superiori, de rang 9 și 8), numărul cilindrului de pe disc (începând cu 0, cel mult 1023).
(Se constată că formatul HSC e limitat la 24 de biți, deci discuri de cca 8GB. Dincolo de această limită se folosește doar numărul logic de sector în cadrul discului; pentru simplificare nu vom trata acest caz.)

Scrieți un program care acceptă următoarele comenzi (câte o literă mică):
d șterge o partiție: cere un număr între 1 și 4, pune identificatorul și toți octeții din intrarea respectivă pe zero.
m afișează meniul de comenzi
n creează o partiție nouă: solicită un număr între 1 și 4; un cilindru de început de partiție; un cilindru de sfârșit de partiție; un tip (identificator) de partiție. Partiția nu trebuie să se suprapună peste cele existente.
p afișează tabela de partiții (un tabel cu câte un rând pentru fiecare partiție existentă)
q iese din program, ignoră modificările făcute
w iese din program, scrie modificările pe disc

Convențional, fiecare partiție e aliniată de regulă la un cilindru nou (începe la sectorul 1 al capului 0 de pe acel cilindru). Excepție face începutul discului, unde se rezervă o pistă specială (cilindrul 0, capul 0, toate sectoarele) care include și primul sector unde se află chiar tabela de partiții.

Pentru implementare, vor trebui manipulate zonele corespunzătoare (intrările în tabela de partiții) dintr-un tablou de 512 octeți reprezentând conținutul unui sector. E elegantă și utilă definirea unui tip structură care reprezintă o intrare din tabela de partiții. Se poate defini un tip și pentru formatul HSC, eventual cu câmpuri pe biți, deși cu ordonarea dată a biților nu se poate trata "dintr-o bucată" câmpul de cilindri. Pentru întregii pe dimensiune fixă în octeți, soluția portabilă în standardul C99 este folosirea tipurilor definite în stdint.h, în acest caz tipul uint32_t. (Pentru gcc/x86/linux acesta e definit ca unsigned int; vom învăța cum să dăm o definiție portabilă cu directive de compilare condițională, în funcție de valoarea dată de sizeof).

Pentru simplificare, și întrucât citirea/scrierea din fișiere nu s-a tratat încă la curs, s-au implementat funcții ajutătoare, în fișierul rwsect.c:
funcția size_t readsect(void *ptr) citește un sector (512 octeți) la adresa ptr unde se presupune că există suficient loc.
funcția size_t writesect(const void *ptr) scrie un sector (512 octeți) de la adresa ptr. Funcțiile lucrează implicit cu fișierul mbr.bin aflat în catalogul curent și nu se vor apela de mai multe ori în execuția programului (deoarece implicit deschid și închid fișierul respectiv). Ele returnează numărul octeților citiți/scriși cu succes, și setează variabila standard errno în caz de eroare.
În realitate, programul fdisk poate afla din hardware informațiile despre geometria discului (număr de cilindri, capete, sectoare). Pentru problema dată, considerăm C=528, H=64, S=63, valori consistente cu structura dată în fișierul de test.
Când folosim structuri pentru a defini formate cu număr precizat de octeți, e bine să ne asigurăm că deplasamentul fiecărui câmp în cadrul structurii este cel dorit, modul de aliniere a câmpurilor în memorie fiind dependent de implementare (compilator). Dacă x are tipul struct s și f e un câmp, atunci expresia (char *)&x.f - (char *)&x indică deplasamentul câmpului f în cadrul structurii. În C99 există și macro-ul offsetof din stddef.h care utilizat sub forma offsetof(struct s, f) dă o constantă întreagă cu același rezultat.


Marius Minea
Last modified: Wed Nov 26 07:23:19 EET 2003