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/myfs/test-myfs.sh
</code>
Statisticile afișate pentru sistemul de fișiere sunt minimale, întrucât informațiile sunt furnizate de către funcția simple_statfs.
În continuare vom implementa bazele unui sistem de fișiere foarte simplu, denumit minfs, cu suport pe disc. Vom folosi un disc din cadrul mașinii virtuale pe care îl vom formata și monta cu sistemul de fișiere minfs.
Pentru aceasta vom accesa directorul minfs/kernel din scheletul laboratorului și vom folosi scheletul de cod minfs.c. La fel ca la myfs nu vom implementa operații de lucru cu inode-urile, limitându-ne doar la operațiile de lucru cu superblocul și, așadar, la montare. Restul operațiilor le vom implementa în laboratorul următor.
Urmăriți diagrama de mai jos pentru a clarifica rolul structurilor din cadrul sistemului de fișiere minfs.
dd if=/dev/zero of=qemu/mydisk.img bs=1M count=100
și adăugați argumentul -drive file=qemu/mydisk.img,if=virtio,format=raw comenzii qemu, în fișierul qemu/Makefile (în variabila QEMU_OPTS). Noul argument pentru comanda qemu trebuie sa fie adăugat după cel corespunzător discului existent (YOCTO_IMAGE).
Pentru înregistrarea și deînregistrarea sistemului de fișiere va trebui să completați, în fișierul minfs.c, structura minfs_fs_type și funcția minfs_mount. Urmăriți indicațiile marcate cu TODO 1.
minfs_mount prezentă în scheletul de cod. În cadrul acestei funcții apelați funcția aferentă pentru montarea unui sistem de fișiere cu suport de disc (vedeți secțiunea Funcțiile mount, kill_sb; este vorba de funcția mount_bdev).
Alegeți funcția cea mai potrivită pentru distrugerea superblocului (efectuată la demontare); țineți cont de faptul că este un sistem de fișiere cu suport pe disc. Este vorba de funcția kill_block_super.
Inițializați câmpul fs_flags al structurii minfs_fs_type cu valoarea corespunzătoare pentru un sistem de fișiere cu suport pe disc. Vedeți secțiunea Înregistrarea și deînregistrarea sistemelor de fișiere.
Funcția de completare a superblocului este minfs_fill_super.
După completarea secțiunilor marcate cu TODO 1, compilați modulul, copiați-l în mașina virtuală QEMU și porniți mașina virtuală. Încărcați modulul în kernel și apoi verificați prezența sistemului de fișiere minfs în cadrul fișierului /proc/filesystems.
Pentru a putea testa montarea sistemului de fișiere va trebui să formatăm discul cu structura acestuia. Formatarea necesită utilitarul de formatare mkfs.minfs, din directorul minfs/user. Utilitarul se compilează automat la comanda
make build
și se copiază pe mașina virtuală la
make copy
.
După compilare, copiere și pornirea mașinii virtuale, formatăm discul /dev/vdb folosind utilitarul de formatare:
# ./mkfs.minfs /dev/vdb
Încărcăm modulul de kernel
# insmod minfs.ko
creăm punctul de montare /mnt/minfs/
# mkdir -p /mnt/minfs/
și montăm sistemul de fișiere
# mount -t minfs /dev/vdb /mnt/minfs/
Operaţia eşuează din cauză că inode-ul rădăcină nu este iniţializat.
Pentru a putea monta sistemul de fișiere va trebui să completați superblocul (adică o structură de tip struct super_block) în cadrul funcției minfs_fill_super; este vorba de argumentul s al funcției. Structura de operații pe superbloc este definită: minfs_ops. Urmăriți indicațiile marcate cu TODO 2. Puteți urmări implementarea funcției minix_fill_super.
minfs.h.
Pentu informații legate de lucrul cu buffere, parcurgeți secțiunea Buffer cache-ul.
Citiți primul bloc de pe disc (blocul cu indexul 0). Pentru a citi blocul, folosiți funcția sb_bread. Convertiți datele citite (câmpul b_data din struct buffer_head) la structura de tip informație de superbloc de pe disc: struct minfs_super_block, definită în fișierul cod sursă.
Structura struct minfs_super_block deține informații specifice sistemului de fișiere, care nu se regăsesc în structura generică struct super_block (în cazul de față doar versiunea). Acele informații suplimentare (care se găsesc în struct minfs_super_block (disc) dar nu în struct super_block (VFS)) vor fi stocate în structura struct minfs_sb_info.
Pentru verificarea funcționalității avem nevoie de o funcţie pentru citirea inode-ului rădăcină. Pe moment folosiți funcția myfs_get_inode de la exercițiile cu sistemul de fișiere myfs. Copiați funcția în codul sursă şi apelaţi-o la fel ca în cazul myfs. Al doilea argument în cazul apelării funcției myfs_get_inode îl reprezintă permisiunile de creare ale inode-ului, similar exercițiului cu sistem de fișiere virtual (myfs).
Validaţi implementarea executând comenzile de la exerciţiul anterior.
Pentru montare avem nevoie de inițializarea inode-ului rădăcină, iar pentru inițializarea inode-ului rădăcină avem nevoie de implementarea funcțiilor de lucru cu inode-uri. Adică trebuie să implementați funcțiile minfs_alloc_inode și minfs_destroy_inode. Urmăriții indicațiile marcate cu TODO 3. Puteți folosi ca model funcțiile minix_alloc_inode și minix_destroy_inode.
În implementare urmăriți macro-urile și structurile din fișierul minfs.h.
minfs_alloc_inode și minfs_destroy_inode recomandăm folosirea kzalloc și kfree.
În funcția minfs_alloc_inode alocați inode-uri de tip minfs_inode_info, dar returnați doar structuri de tip struct inode, adică cele date de câmpul vfs_inode.
În funcția de minfs_alloc_inode apelați funcția inode_init_once pentru inițializarea inode-ului.
În funcția destroy_inode. să puteți accesa structura de tip struct minfs_inode_info folosiți macro-ul container_of.
minfs_alloc_inode şi minfs_destroy_inode, dar ele nu sunt încă apelate. Corectitudinea implementării o veţi verifica la sfârşitul exerciţiului următor.
Inițializarea inode-ului rădăcină este necesară pentru montarea sistemului de fișiere. Pentru aceasta va trebui să completați structura minfs_ops cu funcțiile minfs_alloc_inode și minfs_destroy_inode și să completați funcția minfs_iget. Funcția minfs_iget este funcția apelată pentru alocarea unui inode de tip VFS (adică struct inode) și completarea acestuia cu informații specifice inode-ului minfs de pe disc (adică struct minfs_inode). Urmăriții indicațiile marcate cu TODO 4.
Completați câmpurile alloc_inode și destroy_inode ale structurii struct super_operations cu funcțiile implementate la pasul anterior.
Informațiile despre inode-ul rădăcină se găsesc în al doilea bloc de pe disc (inode-ul cu indexul 1). Realizați, în cadrul funcției minfs_iget citirea inode-ului rădăcină de tip minfs de pe disc (struct minfs_inode) și completarea inode-ului VFS (struct inode).
În cadrul funcției minfs_fill_super înlocuiți apelul funcției myfs_get_inode cu apelul funcției minfs_iget.
minfs_iget urmăriți implementarea funcției V1_minix_iget.
Pentru a citi un bloc folosiți funcția sb_bread. Convertiți datele citite (câmpul b_data al structurii struct buffer_head) la inode-ul de tip minfs de pe disc (struct minfs_inode).
Câmpurile i_uid, i_gid, i_mode, i_size le completați în cadrul inode-ului VFS cu valorile din inode-ul de pe disc. Pentru initializarea câmpurilor i_uid și i_gid, folosiți funcțiile i_uid_write, și, respectiv, i_gid_write.
Inițializați câmpurile i_atime, i_ctime și i_mtime ale inode-ului VFS la valoarea întoarsă de funcția current_time.
Va trebui să inițializați operațiile pentru inode-ul de tip director. Pentru aceasta urmați pașii:
i_op și i_fop, folosiți funcții din kernel deja implementate:i_op: simple_dir_inode_operations.i_fop: simple_dir_operations
Acum putem să montăm sistemul de fișiere. Urmați pașii indicați mai sus pentru a compila modulul de kernel, copia pe mașina virtuală și porni mașina virtuală și apoi inserați modulul de kernel, creați punctul de montare /mnt/minfs/ și montați sistemul de fișiere. Verificăm că sistemul de fișiere a fost montat investigând fișierul /proc/mounts.
Verificăm că totul este în regulă prin listarea conținutului punctului de montare /mnt/minfs/:
# ls /mnt/minfs/
După montare și verificare, demontăm sistemul de fișiere și descărcăm modulul din kernel.
test-minfs.sh:
# ./test-minfs.sh
Scriptul este copiat pe mașina virtuală la rularea comenzii make copy doar dacă este executabil.
student@workstation:~/so2/linux/tools/labs$ chmod +x skels/filesystems/minfs/user/test-minfs.sh