Перейти до вмісту

A6. Файлова система через FUSE

просунутийспирається на модуль 15

Кожна файлова система зводиться до кількох структур на диску: суперблок, бітові карти, таблиця inode, блоки даних. Модуль 15 описує їх і пояснює, чому ім’я файлу живе в каталозі, а не в inode. У цій роботі ви реалізуєте ці структури самі, змонтуєте результат через FUSE і перевірите його звичайними ls, cat і ln. Після цього жорсткі посилання перестають бути дивиною: це другий запис у каталозі з тим самим номером inode. FUSE працює в режимі користувача, тож помилка завершить лише вашу програму, система встоїть.

Після цієї роботи ви зможете:

  • розкласти образ файлової системи на суперблок, бітові карти, таблицю inode і блоки даних і пояснити, за що відповідає кожна ділянка;
  • реалізувати обробники FUSE так, щоб ls, cat, mkdir, ln і rm працювали з вашим форматом без жодних змін у цих утилітах;
  • пояснити, чому unlink не знищує дані, поки лічильник посилань не дійшов до нуля, і показати це на власній ФС через stat двох імен;
  • перевірити, що стан ФС переживає відмонтування, і знайти те, що трималося лише в пам’яті.

У роботі це трапляється, коли після обриву живлення fsck перевіряє ті самі лічильники й бітові карти, або коли ви монтуєте sshfs чи хмарне сховище: вони зроблені на тому самому FUSE.

Написати дві програми: mkfs.myfs, яка створює порожню файлову систему у файлі-образі, і myfs, яка монтує цей образ через FUSE.

Terminal window
./mkfs.myfs disk.img 64M # створити порожню ФС у файлі
./myfs disk.img /mnt/my # змонтувати
fusermount3 -u /mnt/my # відмонтувати

Що має вміти:

  • mkfs.myfs записує суперблок, обнуляє бітові карти і створює кореневий каталог із записами . і ..;
  • після монтування всередині працюють mkdir, touch, echo > file, cat, ls -l, ln, rm, stat;
  • файли читаються назад і мають правильний розмір у stat; файл на 100 КБ записується цілком;
  • rmdir відмовляє на непорожньому каталозі і видаляє порожній;
  • жорсткі посилання: ln дає друге ім’я з тим самим номером inode і лічильником 2; після видалення першого імені дані читаються за другим, а лічильник стає 1;
  • блоки повертаються у вільні після видалення файлу, і df на точці монтування це показує (обробник statfs);
  • усе перелічене переживає відмонтування й повторне монтування.

Чого робити не треба. Непрямі блоки й журнал винесені в «Далі, якщо цікаво». Символьні посилання, перейменування і права доступу автоперевірка не використовує. Тримати відкритий файл із нульовим nlink живим до закриття не обов’язково, але про це є пункт у звіті.

Обмеження. libfuse бере на себе лише обмін із ядром; розбір шляхів, записи каталогів, бітові карти і зростання файлу пишете самі за форматом нижче.

Що записати у звіт:

  • що станеться у вашій ФС, якщо видалити файл, який хтось тримає відкритим (див. Aside після етапів);
  • максимальний розмір файлу, який допускає ваш формат, і що його обмежує;
  • кількість вільних блоків до і після ста створень і видалень файлу;
  • що не пережило перемонтування з першого разу і де воно трималося лише в пам’яті.

Готово, коли:

  • ./check.sh ./myfs ./mkfs.myfs проходить усі двадцять одну перевірку в шести групах; якщо df не бачить вільного місця, перевірку звільнення скрипт пропускає, і їх лишається двадцять;
  • образ, створений mkfs.myfs, монтується вдруге з усіма файлами й посиланнями на місці;
  • у звіті є чотири пункти вище.
  • Прочитайте в модулі 15 розділи «inode», «Розподіл блоків» і «overlayfs і FUSE».
  • Потрібен пристрій /dev/fuse, якого в непривілейованому контейнері немає (чому). Беріть віртуальну машину або WSL2. У Vagrant-машині з архіву вже стоять libfuse3-dev і fuse3; на своїй системі їх поставить setup/provision.sh.
  • Розпакуйте архів курсу: перевірка лежить у labs/a6-filesystem/check.sh. Їй потрібні fusermount3 (або fusermount) і mountpoint; на монтування вона дає 10 секунд. Опис обробників є у fuse3/fuse.h, структура fuse_operations.

Мінімальний, але справжній:

Ділянка Що містить
Суперблок сигнатура, розмір блока, кількість inode і блоків, зміщення решти ділянок
Бітова карта inode по біту на inode: зайнятий чи ні
Бітова карта блоків по біту на блок даних
Таблиця inode тип, права, розмір, лічильник посилань, часи, вказівники на блоки
Блоки даних вміст файлів і записи каталогів

Каталог зберігається як звичайний файл, вміст якого є послідовністю записів «довжина імені, номер inode, ім’я».

  1. mkfs. Створити образ, записати суперблок, обнулити бітові карти, створити кореневий каталог із записами . і ...

  2. Монтування й читання. Реалізувати getattr, readdir, open, read. На цьому етапі ФС уже видно в ls, хоч записувати ще не можна.

    getattr найважливіша з них: без неї не працює нічого, і на ній зазвичай застрягають.

  3. Запис. create, write, truncate, unlink. Тут з’являється робота з бітовою картою блоків і зростання файлу.

  4. Каталоги. mkdir, rmdir. rmdir має відмовляти на непорожньому каталозі; переконайтеся, що ваша перевірка не рахує . і ...

  5. Жорсткі посилання. link збільшує лічильник, unlink зменшує; inode звільняється лише на нулі.

    Обов’язкова перевірка: створити файл, зробити на нього посилання, видалити оригінал, прочитати за другим іменем. Дані мають бути на місці.

  6. Стійкість до перезапуску. Відмонтуйте, змонтуйте знову, переконайтеся, що все на місці. Якщо ні, значить, ви щось тримали лише в пам’яті.

Перевірка лежить в архіві з файлами курсу, і команди нижче виконуються з розпакованого каталогу.

Terminal window
cd labs/a6-filesystem
./check.sh ./myfs ./mkfs.myfs

Скрипт створює образ, монтує його у тимчасовий каталог, проганяє сценарії з файлами, каталогами й посиланнями, відмонтовує й монтує знову, щоб перевірити збереження стану.

Жорсткі посилання «не працюють», хоча код правильний. Найдорожча пастка цієї роботи, і вона не у вашому коді. За замовчуванням libfuse підставляє власні номери inode замість тих, що ви повертаєте з getattr, і кешує атрибути на секунду. Тому stat показує два різні inode для двох імен одного файлу й застарілий nlink.

Лікується обробником init:

static void *fs_init(struct fuse_conn_info *conn, struct fuse_config *cfg) {
cfg->use_ino = 1; // брати st_ino з нашого getattr
cfg->attr_timeout = 0; // не кешувати атрибути
cfg->entry_timeout = 0;
return NULL;
}

Кеш можна лишити ввімкненим у готовій ФС, але поки ви її налагоджуєте, він приховує половину помилок.

getattr повертає неправильний розмір. Тоді cat читає зайве або обрізає файл. Розмір беруть з inode; кількість блоків його не визначає.

Каталог не має . і ... На них розраховує чимало утиліт, тож ls почне поводитися дивно.

Блоки не звільняються. Місце тікатиме при кожному видаленні. Перевірити просто: створіть і видаліть файл сто разів, а потім подивіться на вільні блоки.

Немає перевірки меж. Ім’я, довше за максимальне, або запис за межу файлу зіпсують сусідні структури. Це та сама помилка, що й у A4, тільки на диску.

Додати непрямі блоки й подивитися, як зростає максимальний розмір файлу (модуль 15). Або додати журнал і перевірити, що ФС переживає обрив на середині запису.