Pentru rezolvarea laboratorului, vom lucra în același director din care pornim mașina virtuală (~/so2/linux/tools/labs
).
Pașii de rezolvare sunt următorii:
Scheletul de laborator este generat din sursele din directorul tools/labs/templates
. Putem genera scheletele pentru toate laboratoarele folosind următoarea comanda:
tools/labs $ make skels
Pentru a genera scheletul pentru un singur laborator, vom folosi variabila de mediu LABS
:
tools/labs $ make clean tools/labs $ LABS=<lab name> make skels
filesystems
.
Similar, putem genera și scheletul pentru un singur exercițiu, atribuind valoarea <lab_name>/<task_name>
variabilei LABS
.
tools/labs/skels
.
Comanda make build
compilează toate modulele din directorul skels
.
student@eg106:~/so2/linux/tools/labs$ make build echo "# autogenerated, do not edit " > skels/Kbuild echo "ccflags-y += -Wno-unused-function -Wno-unused-label -Wno-unused-variable " >> skels/Kbuild for i in ./filesystems/minfs/kernel ./filesystems/myfs; do echo "obj-m += $i/" >> skels/Kbuild; done
Putem copia modulele generate pe mașina virtuală folosind target-ul copy
al comenzii make, atunci când mașina virtuală este oprită.
student@eg106:~/so2/linux/tools/labs$ make copy student@eg106:~/so2/linux/tools/labs$ make boot
Alternativ, putem copia fișierele prin scp
, pentru e evita repornirea mașinii virtuale. Pentru detalii despre folosirea interacțiunea prin rețea cu mașina virtuală citiți Interacțiunea cu mașina virtuală.
Modulele generate sunt copiate pe mașina virtuală în directorul /home/root/skels/<lab_name>
.
root@qemux86:~/skels/filesystems# ls minfs myfs root@qemux86:~/skels/filesystems# ls myfs/ myfs.ko test-myfs.sh
După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU (sau în minicom
) pentru a încărca și descărca modulul de kernel:
root@qemux86:~# insmod skels/<lab_name>/<task_name>/<module_name>.ko root@qemux86:~# rmmod skels/<lab_name>/<task_name>/<module_name>.ko
Ctrl+Shift+t
. Cele trei tab-uri de terminal îndeplinesc următoarele roluri:
~/so2/linux/tools/labs
.~/so2/linux/
cu sursele nucleului unde putem folosi Vim și cscope pentru parcurgerea codului sursă.student@eg106-pc:~ chmod +x skels/filesystems/minfs/user/test-minfs*.sh </code>
După ce pornim mașina, formatăm discul /dev/vdb
, creăm punctul de montare și montăm sistemul de fișiere:
# ./mkfs.minfs /dev/vdb # mkdir -p /mnt/minfs # mount -t minfs /dev/vdb /mnt/minfs
Acum putem lista conținutul directorului rădăcină:
# ls -l /mnt/minfs
Observăm că există deja un fișier (a.txt
); acesta este creat de utilitarul de formatare.
Observăm, de asemenea, că nu ne este permisă afișerea de informații folosind comanda ls
. Aceasta se întâmplă pentru că nu avem implementată funcția de lookup
. O vom implementa în exercițiul următor.
Pentru testarea funcționalităților oferite de modul putem folosi scriptul dedicat:
# ./test-minfs-0.sh # ./test-minfs-1.sh
Dacă implementarea este validă, nu vor fi afișate mesaje de eroare în urma rulării scripturilor de mai sus.
==== 4. [1.5p] Operația lookup (minfs) ====
Pentru listarea corespunzătoare a conținutului unui director este nevoie să implementăm funcționalitatea de căutare, adică operația lookup
. Operația lookup
este un câmp în cadrul structurii minfs_dir_inode_operations
(de tipul inode_operations) și este implementată de funcția minfs_lookup
. Această funcție (minfs_lookup
) trebuie să o implementăm. De fapt vom implementa funcția minfs_find_entry
apelată de funcția minfs_lookup
.
Urmăriți indicațiile marcate cu TODO 6
care vă vor indica pașii pe care trebuie să îi faceți.
Ca punct de plecare, urmăriti funcțiile qnx6_find_entry și minix_find_entry.
În cadrul funcției minfs_find_entry
, parcurgeți directorul în care se află intrarea dentry
: dentry->d_parent->d_inode
. Parcurgere înseamnă parcurgerea intrărilor din blocul de date al directorului și localizarea, dacă există, a intrării căutate.
struct minfs_inode_info
aferent directorului, aflați indexul blocului și citiți-l. Veți accesa conținutul blocului folosind construcția bh->b_data
.
Blocul de date al directorului conține un vector de cel mult MINFS_NUM_ENTRIES
intrări de tipul struct minfs_dir_entry
. Folosiți aritmetică de pointeri pentru a obține intrări de tipul struct minfs_dir_entry
din blocul de date (bh->b_data
).
Verificați prezența numelui (stocat în variabila locală name
) în director; adică dacă există o intrare în blocul de date al cărei nume să un șir egal cu șirul name
. Folosiți strcmp
pentru verificare.
Ignorați dentry-urile care au câmpul ino
egal cu 0. Acele dentry-uri reprezintă slot-uri liber în lista de dentry-uri a directorului.
Rețineți în variabila final_de
dentry-ul găsit. Dacă nu găsiți nici un dentry, atunci variabila final_de
va avea valoarea NULL
, valoare cu care a fost inițializată.
Actualizați câmpul lookup
al structurii minfs_dir_inode_operations
, structură de tipul inode_operations.
=== Testare ===
Pentru testare, folosim pașii descriși la exercițiul anterior. La listarea în format lung (ls -l
) a conținutului unui director (directorul rădăcină) vor fi afișate permiuni și alte informații specifice fișierului:
# ls -l /mnt/minfs
Pentru testarea funcționalităților oferite de modul putem folosi scriptul dedicat:
# ./test-minfs-0.sh # ./test-minfs-1.sh
Dacă implementarea este validă, nu vor fi afișate mesaje de eroare în urma rulării scripturilor de mai sus.
# mount -t minfs /dev/vdb /mnt/minfs
încercăm să creăm un fișier folosind comanda
# touch /mnt/minfs/peanuts.txt
Observăm că primi eroare pentru că nu am implementat operațiile de lucru pe directoare care permit crearea unui fișier. Vom face acest lucru la exercițiul următor.
==== 5. [3.5p] Operația create (minfs) ====
Pentru a permite crearea unui fișier într-un director trebuie să implementăm operația de tip create
. Operația create
este un câmp în cadrul structurii minfs_dir_inode_operations
(de tipul inode_operations și este implmenentată de funcția minfs_create
. Trebuie să o implementăm această funcție. De fapt vom implementa funcțiile minfs_new_inode
(care creează și inițializează un inode) și minfs_add_link
care adaugă un link (sau nume sau dentry) inode-ului creat.
Urmăriți indicațiile marcate cu TODO 7
care vă vor ghida pașii pe care trebuie să îi faceți.
Parcurgeți codul funcției minfs_create
și scheletul funcțiilor minfs_new_inode
și minfs_add_link
.
Completați funcțiile minfs_readdir
și minfs_find_entry
cu implementarea de la exercițiul anterior.
Implementați funcția minfs_new_inode
. În cadrul funcției veți crea (new_inode) și veți inițializa un inode. Inițializarea se face cu datele de pe disc.
Găsiți primul inode liber din imap (sbi->imap
). Folosiți operații de lucru pe biți (find_first_zero_bit și set_bit). Parcurgeți secțiunea Operații pe bitmap-uri.
Buffer-ul aferent supernodului (sbi->sbh
) trebuie marcat dirty.
Trebuie să inițializați câmpurile uzuale, așa cum este inițializat și pentru sistemul de fișiere myfs
. Inițializați câmpul i_mode
la 0. Va fi inițializat ulterior în apelant.
Implementați funcția minfs_add_link
. Funcția adaugă un nou dentry (struct minfs_dir_entry
) în blocul de date al directorului părinte (dentry->d_parent->d_inode
).
În cadrul funcției minfs_add_link
doriți să găsiți primul loc liber pentru dentry. Pentru aceasta veți parcurge blocul de date aferente directorului și veți găsi primul loc liber, adică acel dentry pentru care câmpul ino
are valoarea 0
.
struct minfs_inode_info
aferente directorului părinte (inode-ul dir
). Nu folosiți inode-ul inode
pentru obținerea struct_minfs_inode_info
; acel inode este inode-ul fișierului, nu al directorului părinte (cum este dir
) în conținutul căruia trebuie să adăugați link-ul/dentry-ul. Pentru obținerea structurii struct minfs_inode_info
folosiți container_of.
Structura struct minfs_inode_info
este utilă pentru a afla blocul de date al directorului (cel indicat de inode-ul dentry->d_parent->d_inode
, adică de variabila dir
). Din cadrul acestei structuri obțineți câmpul data_block
, reprezentând indexul blocului de date pe disc. Acest bloc conține intrări în director. Folosiți sb_bread pentru citirea blocului dat și apoi construcția bh->b_data
pentru a referi datele din bloc. Blocul conține un vector de cel mult MINFS_NUM_ENTRIES
intrări de tipul struct minfs_dir_entry
.
Dacă toate intrările sunt ocupate, se întoarce -ENOSPC
.
Obțineți numele intrării în forma unui șir de caractere (char *
) în variabila name
.
Parcurgeți intrările din bloc folosind variabila de
și extrageți prima intrare liberă (cea pentru care câmpul ino
este 0
). Dacă ați găsit loc liber, completați intrarea corespunzătoare: câmpul ino
și câmpul name
din variabila de
. Puteți folosi strcpy
sau memcpy
pentru inițializarea numelui la conținutul variabilei name
.
=== Testare ===
Pentru testare, folosim pașii descriși la exercițiul anterior. Acum vom putea crea fișiere în cadrul sistemului de fișiere:
# touch /mnt/minfs/peanuts.txt
Pentru testarea funcționalităților oferite de modul putem folosi scriptul dedicat:
# ./test-minfs-2.sh
Dacă implementarea este validă, nu vor fi afișate mesaje de eroare în urma rulării scriptului de mai sus.
minfs
. Pentru a fi completă, implementarea are nevoie de funcții de șters fișiere, create și șters directoare, redumit intrări și alterat conținutul unui fișier.
===== Soluții =====