Як мій комп’ютер зберігає усе на диску

Коли Ви дивитесь на жорсткий диск під Unix’ом, Ви бачите дерево іменованих каталогів та файлів. Для щоденного використання, Вам не потрібно вникати глибше за таке відображення; проте потреба в цьому може виникнути, наприклад, при відновленні файлів із пошкоджених носіїв. На жаль, пояснювати організацію даних на диску, спускаючись від файлів «до низу» не зручно, тому ми підео зворотнім шляхом — від апаратури «вверх» до файлів.

Низькорівнева структура диска та файлової системи

Поверхня диска, на котрій зберігаються дані поділена подібно до дошки для гри в дартс – на концентричні доріжки, що також розділені на сектори^1 . Оскільки доріжки біля краю диска мають більшу поверхню аніж ті, що розміщені ближче його цетнру, то вони містять у собі більше секторів. Кожен сектор (або блок диска^2 ) має однаковий розмір, котрий у сучасних Юнікс-системах, як правило, є розміром з 1 двійковий кілобайт (1024 8-бітних слова). Кожен блок має унікальну адресу, або номер дискового блоку.

Юнікс ділить диск на розділи^3 . Кожен розділ є неперервною послідовністю блоків, що використовуються окремо від іншого розділу у якості файлової системи або обмінної області. Первинними причинами для створення окремих розділів на диску була необхідність відновлення після аварій у світі повільних та ненадійних вінчестерів; коли один розділ був пошкоджеий, інший залишався неушкодженим. Зараз набагато важливішим є те, що розділи можуть бути, наприклад, налаштовані як такі, з яких можна лише зчитувати дані (попереджуючи таким чином випадкову чи навмисну модифікацію системних файлів), чи відкриті для доступу по мережі. Найперший розділ на диску, як правило, є особливий завантажувальним розділом, куди Ви можете помістити ядро Вашої операційної системи.

Кожен розділ є або обмінником (що використовується для створення віртуальної оперативної пам’яті), або файловою системою^4 (що використовується для зберігання файлів). Розділи обмінників розглядаються як неперервна лінійна послідовність блоків. На відміну від них, розділи із файловою системою мусять якимось способом розподіляти блоки між різними файлами. Файли збільшуються, зменшуються та весь час змінюються, тому блоки, котрі зберігають вміст файлу, можуть бути розкидані по всьому розділу (в залежності від того, де операційна система знаходить вільний блок, коли він їй потрібен, якщо файл росте). Такий розсіюючий ефект називається фрагментацією^5 .

Назви файлів та каталогів

Всередині будь-якої файлової системи зв’язок назв файлів із їхніми блоками підтримується задопомогою структур, що називаються індексними дескрипторами файлів, або i-node’ами. Ай-ноди зберігаються знаходиться «внизу» (в найнижчих за порядком блоках) кожної файлової системи (в той час, як найперші блоки використовуються файловою системою в різних службових цілях). Кожен дескриптор описує один файл. Блоки цих файлів (включаючи каталоги) знаходяться вище, над дисктрипторами.

Кожен дескриптор містить список блоків, в котрих знаходиться його файл (фактично, так є лише із малими файлами, але решта деталей несуттєві в контексті нашого посібника). Зверніть увагу, що індексні дескриптори НЕ МІСТЯТЬ назв файлів.

Самі назви файлів знаходяться в структурі каталогів. Структура каталогів просто зв’язує назви файлів на номери іхніх дескрипторів. Ось чому в Юнікс-системах один файл може мати декілька імен (так званих жорстких посилань^6 ) — цього система досягає, створюючи в структурі каталогів додаткові записи, які вказують на той сам дескриптор.

Точки монтування

У найпростішому випадку вся Ваша файлова система знаходиться на одному розділі диску. Проте так роблять досить рідко. Значно типовішим є розділення її по декількох розділах, можливо навіть на різних фізичних дисках. Наприклад, Ваша система може мати один маленький розділ, де розташовується ядро, дещо більший для розміщення операційної системи і дуже великий для розміщення домашніх каталогів користувачів.

Єдиним розділом, до котрого Ви маєте доступ відразу після завантаження системи є кореневий розділ, котрий (майже завжди) є тим розділом, з котрого Ви завантажились. Він містить кореневий каталог файлової системи, головний вузол, до котрого приєднуються всі інші.

Інші розділи повинні бути приєднаними до кореневого для того, щоб Ви мали можливість доступитись до всієї вашої файлової системи. Приблизно на середині процесу завантаження Ваш Юнікс робить ці некореневі розділи доступними. Він монтує^7 , чи приєднує, підключає їх в каталоги кореневого розділу.

Для прикладу, якщо Ви маєте каталог, що називається /usr, найвірогідніше, це — точка монтування для розділу, що містить більшість програм, встановлених у Вашій системі, за винятком тих, які потрібні системі у процесі початкового завантаження.

Як операційна система знаходить файли

Тепер ми можемо подивитись на файлову систему згори. Коли Ви відкриваєте файл (скажімо, /home/esr/WWW/ldp/fundamentals.xml), відбувається наступне:

Ваше ядро розпочинає перегляд з кореня файлової системи (в кореневому розділі). Воно шукає каталог, що називається «home». Як правило, він є точкою монтування для великого користувацького розділу, розміщеного деінде, тож ядро прямує туди. У каталозі верхнього рівня того розділу ядро шукає каталог «esr» та виділяє номер його файлового дескриптора. Перейшовши до цього дескриптора, воно побачить, що його блок даних містить структуру каталогу, тож воно шукає в ній запис для «WWW». Виділивши цей дескриптор, ядро знову натикається на каталог, і шукає там елемент «ldp». Так воно отримує ще один дескриптор, відкривши котрий, ядро знайде нарешті дескриптор файлу «fundamentals.xml». Цей дескриптор буде вже не каталогом, а справді міститиме список блоків диска, асоційованих із відповідним файлом.

Власники файлів, права доступу та безпека

Для попередження випадкової чи навмисної зміни даних одних програм іншими у Юніксах введено поняття прав доступу. Вони задумувались для організації розділення часу щоб убезпечити багатьох користувачів одне від одного в ті часи, коли Юнікси запускались на дорогих спільних комп’ютерах.

Для того, щоб зрозуміти права доступу до файлів, Вам потрібно на хвильку пригадати те, про що ми говорили у розділі «Що відбувається, коли ви реєструєтесь у системі?». Кожен файл має власника та групу, котрим він належить. Як правило, власником є той, хто створив файл; ці атрибути можуть бути змінені командами chown та chgrp.

Основні права, що можуть бути асоційовані з файлом — це право читати дані з файлу (read), право записувати у файл (write) та право запускати його у якості програми. Кожен файл має три набори прав доступу — один для власника, один для групи та один для всіх інших користувачів. Ті привілеї, котрі Ви отримуєте під час реєстрації є просто можливостями читати, змінювати та запускати ті файли, які відповідають ID вашого користувача, групі користувачів у котрій Ви знаходитесь, а також файли, котрі дозволяють ці операції для всіх у системі.

Щоб побачити, як з правами можна взаємодіяти та як Юнікс їх відображає, давайте глянемо на який-небудь список файлів на гіпотетичній Юнікс-системі. Ось приклад (пам’ятеєте команду ls?):

snark:~$ ls -l notes
-rw-r--r--   1 esr      users         2993 Jun 17 11:00 notes

Це — звичайний файл із даними. Роздрук підказує нам, що його власником є користувач «esr», котрий знаходиться у групі «users». Мабуть машина, на котрій ми знаходимось, стандартно поміщає в цю групу всіх користувачів; іншими групами, які Ви можете побачити на машинах з роздільним доступом, можуть бути, наприклад «staff» (працівники), «admin» (адміністратори), і, наприклад, «students» (студенити) (зрозуміло, групи не є такими важливими на машинах, з якими працює лише одна людина). Ваш Юнікс може використовувати іншу групу у якості стандартної для вашого користувача, можливо навіть одноіменну із вашим користувачем.

Рядок «-rw-r--r--» показує біти доступу до нашого файлу. Перший мінус стоїть на позиції біта каталогу; там стояло б «d», якщо б це був каталог, або «l» у випадку символічного посилання. Після першого знака перші три позиції показують права доступу користувача-власника файлу, наступні три — групи і останніх три — всіх інших користувачів. Цей файл його власник може читати та змінювати, інші користувачі групи "users" — лише читати, і будь-хто інший — також лише читати. Це є дуже поширеним набором прав доступу для довільного файлу даних.

Тепер давайте глянемо на файл з дещо іншими правами доступу. Це — GCC, компілятор GNU для мови C.

snark:~$ ls -l /usr/bin/gcc
-rwxr-xr-x   3 root     bin         64796 Mar 21 16:41 /usr/bin/gcc

Цей файл належить користувачеві «root» з групи «bin». Він може бути змінений лише користувачем «root», але будь-ким прочитаний чи виконаний (запущений). Такий власник та права доступу є типовими для встановлених системних команд. Група «bin» існує на деяких Юніксах щоб об’єднати разом усі системні команди. Ваша система може використовувати групу «root» (хоча це — не зовсім те саме, що користувач «root»).

Root — це традиційне ім’я користувача з ідентифікатором 0 — спеціальний привілейований користувач, що може змінити чи переназначити будь-які права доступу. Використання цього реєстраційного запису є зручним, але небезпечним: помилка при вводі команди може пошкодити важливі системні файли, які та ж сама команда, виконана звичайним користувачем, не має змоги чіпати.

Саме тому, що користувач «root» є настільки повновладним, доступ до нього повинен дуже добре охоронятися. Ваш пароль root-а — це найкритичніше місце у забезпеченні захисту вашої системи та найголовніше, до чого хочуть дістатись різноманітні кракери^8 та шкідники.

Про паролі. Ніде не записуйте їх, та не використовуйте у якості паролів слова, котрі можна легко вгадати, як то ім’я Вашої дівчини/хлопця/хом’ячка. На жаль, це дуже поширена практика, завдяки котрій кракери продовжують свою діяльність. В ідеалі, взагалі не використовуйте жодного змістовного слова; існують автоматизовані методи зламування паролів, використовуючи словник, коли спеціальна програма пробує підібрати пароль, підставляючи усі підряд слова зі словника. Хорошим вибором є використання одного слова, потім цифри та іншого слова, як наприклад «shark6cider» чи «jump3joy» - це робить область пошуку зі словником занадто великою. Але не вживайте саме ці приклади — кракери можуть натрапити на них, читаючи цей посібник, та включити їх до своїх словників:)

А тепер давайте глянемо на третій випадок:

snark:~$ ls -ld ~
drwxr-xr-x  89 esr      users          9216 Jun 27 11:29 /home2/esr
snark:~$

Цей файл є каталогом (зверніть увагу на літеру «d» на першій позиції стрічки із правами доступу). Ми бачимо, що писати у нього може лише користувач «esr», але будь-хто може його проглядати та виконувати.

Право на читання дає можливість переглянути вміст каталогу — побачити імена файлів та каталогів, що містяться всередині. Якщо Ви пам’ятаєте, що каталоги містять список імен файлів та інших каталогів, це твердження набуває осмисленості.

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

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

Важливо запам’ятати, що право на читання, запис чи виконання каталогу є незалежним від прав доступу до файлів та каталогів нижчого рівня. Іншими словами, право на запис у каталог дає вам можливість створювати та знищувати файли у цьому каталозі (що, фактично, є лише операціями над самим каталогом), проте не дає можливості модифікувати зміст самих файлів.

І нарешті, давайте глянемо на права доступу програми реєстрації у системі:

snark:~$ ls -l /bin/login
-rwsr-xr-x   1 root     bin         20164 Apr 17 12:57 /bin/login

Приблизно такі ж права доступу були у звичайної системної команди за винятком літери «s» на тому місці, де має бути біт дозволу на виконання. Це — видима демонстрація спеціального біта доступу, що називається бітом «set-user-id» або бітом «встановлення-ID-користувача».

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

Як і реєстраційний запис адміністратора («root»), такі права доступу є зручними, але небезпечними. Кожен, хто зможе змінити таку програму, власником якої є root, може використати її щоб отримати оболонку з привілеями адміністратора. Тому право на запис для таких програм на більшості Юнікс-систем автоматично вимикає її біт встановлення ID користувача. Багато атак на безпеку Юніксів базуються саме на пошуку вразливостей setuid-програм. Досвідчені системні адміністратори дуже уважно працюють з такими програмами та дуже неохоче встановлюють нові такі програми.

Під час розгляду прав доступу ми пропустили декілька важливих речей; наприклад, як задається група та права, коли файл створюється. З групою простіше, бо хоча кожен користувач може бути членом одночасно багатьох груп, але лише одна з них, визначена у файлі /etc/passwd, є стандартною групою користувача, і саме вона стає призначається файлові.

Із початковими бітами доступу — дещо цікавіше. Програма, що створює файл, задає йому такі права доступу, щоб вона могла з ним працювати. Але вони будуть змінені змінною середовища umask. Вона визначає, які біти будуть вимкнені під час створення файлу; найбільш поширеним значенням, і стандартом для більшості систем є «-------w-» (або «0002» в числовій нотації), що вимикає біт запису для невласників. Зверніться до документації команди umask за більш детальною інформацією.

Визначення групи-власника для каталогу також дещо складніше. В деяких Юніксах новий каталог приймає групу по замовчування користувача (це домовленість ще із старої «System V»); в інших береться група-власник батьківського каталогу (таки прийтяно у BSD-Юніксах). На деяких сучасних Юніксах, включаючи Лінукс, може бути ця друга поведінка, шляхом встановленням біта «set-group-ID» для каталогу (chmod g+s).

Як можуть виникнути помилки

Раніше ми згадували, що файлові системи можуть бути вразливими. Тепер Ви знаєте, що для того, щоб прочитати якийсь файл з диска, ядро повинно пройти через порівняно довгий ланцюжок i-node’в. Тепер уявіть собі, що Ваш жорсткий диск зазнав пошкодження в певному місці.

У кращому випадку, постраждали лише деякі файли з даними. Проте, з таким самим успіхом могла пошкодитись структура каталогів або номер файлового дискриптора, і ціла гілка Вашої файлової системи могла стати недоступною. Або ще гірше — могла утворитись пошкоджена структура, що кілька разів вказує на один і той сам блок диска чи i-node. Така помилкова структура може швидко завдати шкоди іншим зонам на диску, якщо продовжувати нормальну роботу з такою файловою системою.

На щастя, такий тип пошкоджень став дуже рідкісним завдяки зростанню надійності апаратного забезпечення. Попри те, Ваш Юнікс періодично перевіряє файлову систему щоб впевнитись, що вона в порядку. Сучасні Юнікси роблять швидку перевірку під час завантаження, безпосередньо перед монтуванням файлової системи. Час від часу, також при завантаженні, система роблять більш грунтовну перевірку, що займає трохи більше часу.

Якщо це все виглядає так, ніби Юнікс є дуже складною та схильною до помилок системою, то варто знати, що ці перевірки під час завантаження відловлюють та коректують типові помилки до того як вони зможуть пошкодити дані. Насупне твердження потребує експертної оцінки чи перевірки на актуальність: Інші операційні системи не роблять цього, що дещо прискорює процес завантаження, але залишає Вас наодинці з небезпекою пошкодження даних з наступною перспективою їх відновлення вручну (припускаю, що копія Norton Utilities або подібного пакету завжди лежить у вас під руками).

Однією з тенденцій розвитку сучасних Юніксів є файлові системи з журналом^9 . Вони компонують потік даних до диска таким чином, щоб, у разі аварії чи раптової втрати живлення, при наступному запуску програма перевірки файлової системи могла легко з’ясувати, які саме дії здійснювались перед аварією, і значно точніше виправити можливі помилки. Крім того, це значно пришвидшує перевірку дискових розділів під час завантаження.