Читання каталогу

 

Каталог може бути прочитаний будь-яким користувачем у кого є права для читання каталогу. Але здійснити запис у каталог може тільки ядро (для збереження працездатності файлової системи).

 

Стандартна бібліотека Linux надає два способи перерахування вмісту директорій:

перший - за допомогою функції scandir () і функцій зворотного виклику,

 

int scandir (const char * dir, struct dirent *** namelist,

int (* select) (const struct dirent *),

int ( * compar) (const struct dirent **, const struct dirent **));

 

Як функції сортування запропоновані alphasort () і versionsort (), а ось фільтр, так розумію, потрібно писати свій.

 

другий - з використанням набору функцій opendir (), readdir (), closedir ().

 

# Include <sys/types.h>

# include <dirent.h>

DIR * opendir (const char * pathname);

Return: покажчик, якщо все OK, NULL при помилку

 

struct dirent * readdir (DIR * dp);

Return: покажчик, якщо все OK, NULL якщо кінець каталогу або помилка

 

void rewinddir (DIR * dp);

int closedir (DIR * dp);

Return: 0, якщо все OK, -1 при помилці

 

Структура dirent визначена у файлі <dirent.h> і є залежною від реалізації. Але вона містить принаймні два елементи.

 

struct dirent

{int_td_ino; / / номер I-node

char d_name [NAME_MAX +1]; / / ім'я

файлу}

 

Константа NAME_MAX залежить від файлової системи в якій розміщується каталог та її значення береться з функції fpathconf (). Часте значення для BSD-систем становить 255.

 

Структура DIR це внутрішня структура, яка використовується для обробки інформації про каталог, який був прочитаний. Покажчик на структуру DIR, який повертається функцією opendir (), використовується рештою функціями.

 

Розглянемо два варіанти програми printdir, роздруковує вміст директорії, переданої їй як аргумент.

 

# Include <stdio.h>

# include <dirent.h>

 

int sel (struct dirent * d)

{return 1; / / завжди підтверджуємо}

 

int main (int argc, char ** argv)

{inti, n;

struct dirent ** entry;

if (argc! = 2)

{printf ("Використання:% s <директорія> \ n", argv [0]) ;

return 0;}

n = scandir (argv [1], & entry, sel, alphasort);

if (n <0)

{printf ("Помилка читання директорії \ n");

return 1;}

for (i = 0; i <n; i + +)

printf ("% s inode =% i \ n", entry[i]->d_name, entry [i] -> d_ino);

return 0;}

 

Функція scandir () створює список елементів зазначеної директорії. Їй необхідно передати покажчик на функцію-фільтр, яка, отримуючи дані про чергове елементі, приймає рішення, включати цей елемент в результуючий список. У нашому прикладі це функція sel (). Якщо при черговому виклику функція sel () поверне значення 0, відповідний елемент директорії не буде включений у кінцевий список. Останній параметр scandir - функція сортування елементів директорії. Ми використовуємо функцію alphasort (), сортують елементи в лексикографічному порядку.

 

Дані про елементи директорії передаються в структурах dirent. Можна було б очікувати, що структури типу dirent містять багато корисної інформації про елементи директорії, але це не так. Крім імені файлу dirent містить номер inode для цього елемента (простим програмами зазвичай не навіщо знати номери inode, але, щоб наш приклад якось відрізнявся від стандартного, ми включаємо цю інформацію). У структури dirent є ще поле d_type типу char *, але воно, як правило, містить null.

 

Функція scandir () дозволяє нам отримати повний відсортований список елементів директорії за виклик. У нас є можливість використовувати низькорівневі кошти, які можуть виявитися швидше в тому випадку, якщо сортування файлів нам не потрібна.

 

Розглянемо другий варіант програми:

# Include <stdio.h>

# include <dirent.h>

int main (int argc, char ** argv)

{DIR * d;

struct dirent * entry;

if (argc! = 2)

{printf ("Використання:% s <директорія> \ n ", argv [0]);

return 0;}

d = opendir (argv [1]);

if (d == NULL)

{printf (" Помилка читання директорії \ n ");

return 1;}

while (entry = readdir (d))

printf ("% s inode =% i \ n", entry-> d_name, entry-> d_ino);

closedir (d);

return 0;}

Цей варіант програми використовує функції opendir (), readdir () і closedir (), які працюють з директорією як з файлом. Функція readdir () повертає значення TRUE до тих пір, поки не будуть прочитані всі елементи директорії.

 

3. Зміна каталогу

 

int chdir (char * pathname);

pathname - ім'я нового робочого каталогу

 

Функція chdir змінює поточний робочий каталог на каталог, який визначається за pathname; pathname повинен посилатися на існуючий каталог.

 

Функція chdir повертає значення 0, якщо робочий каталог успішно змінений. Повертане значення -1 вказує на помилку; в цьому випадку errno встановлюється в ENOENT, вказуючи, що задане path-ім'я не може бути знайдене. Помилки не виникають, якщо pathname визначає поточний робочий каталог.

 

Приклад: наступний оператор змінює поточний робочий каталог на кореневій

 

1) chdir ("/"); / * Зауваження: еквівалентно chdir ("\")

chdir ("..") / * вийти вгору * /

 

2) void lookdir (char * s, int ac, char ** av)

chdir (s)

 

3)chdir ("c: / temp");

c: \ \ temp Зауважимо, треба використовувати два зворотніх слеша (\ \) для представлення oдінoчного слеша (\). Зворотний слеш - цe escape-сімвoл для C-стрoк.

 

4. Управління жорсткими посиланнями

Запис у файлі каталогу, що містить ім'я і номер індексного вузла, називається жорсткою посиланням. Жорстка посилання з'являється при створенні файлу будь-якого типу, включаючи каталоги. Можна створити додаткові жорсткі посилання на файли, які не є каталогами, за допомогою системного виклику link:

 

link - створює жорстку посилання # include <unistd.h>

int link (const char * oldpath, const char * newpath)

 

oldpath, старе ім'я файлу

newpath нове ім'я файлу

Повертає 0 у разі успіху, -1 у випадку помилки (код помилки-в змінної errno)

 

Перший аргумент (oldpath) повинен бути ім'ям існуючої жорсткої посилання - вона представляє номер використовуваного індексного вузла. Другий аргумент (newpath) завдає ім'я нової жорсткої посилання. Стара і нова посилання абсолютно рівноправні, так як UNIX не розрізняє первинні і вторинні посилання. Процес, який створює жорстку посилання, повинен мати право на запис у каталог, де вона розміщуватиметься. Ім'я посилання, представленої у другому аргументі, не повинно існувати - системний виклик link не вміє змінювати існуючі посилання.

У разі необхідності, існуюче посилання можна видалити викликом unlink.

Т.к. жорсткі посилання є звичайними файлами, операції вводу-виводу для них стандартні (read, write).

 

5. Управління символічними посиланнями

 

Існує ще один тип посилань - символічні посилання/

На відміну від жорсткоuj посилання, символічнt посилання - новий файл особого типу, який зберігає рядок повного імені об'єкта в текстовому вигляді. Символічні посилання можуть посилатися на інші символічні посилання. Зазвичай, коли системному виклику open передається символічна посилання такого роду, ядро разименовивает їх по ланцюжку до тих пір, поки не зустріне об'єкт, який не є символічною посиланням. З жорсткими посиланнями цього не відбувається, тому що вони прямо посилаються на індексний вузол, який не може бути іншої жорсткої посиланням (хоча він може бути каталогом, який містить жорстку посилання). Іншими словами, якщо шлях до об'єкта визначається жорсткої посиланням, він може розглядатися буквально. Якщо символічної посиланням, то фактичний шлях до об'єкта залежить від вмісту проміжних символічних посилань.

Створюються символічні посилання системним викликом symlink:

 

# include <unistd.h>

int symlink (const char * oldpath, const char * newpath)

 

oldpath, можливе старе повне ім'я

newpath нове повне ім'я

Повертає 0 у разі успіху, -1 у випадку помилки (код помилки - у змінній errno)

 

Видалити символічну посилання можна за допомогою системного виклику unlink.

 

readlink - читає вміст файлу символічного посилання

 

# include <unistd.h>

ssize_t readlink (const char * path, char * buf, size_t bufsize)

 

path - повне ім'я

buf повертається текст

bufsize розмір буфера;

Повертає 0 у разі успіху, -1 у випадку помилки (код помилки-у змінній errno)

 

Системний виклик readlink повертає повне ім'я об'єкта посилання, але без завершальним символом «0», який в UNIX зазвичай служить маркером кінця рядка. Тому в якості третьої аргументу слід передавати число, на 1 менше фактичного розміру буфера, а після отримання заповненого буфера необхідно додати в нього символ «\ 0»:

 

6. Перейменування файлів і каталогів

 

Операція перейменування увазі зміну назви елемента каталогу. Потрібно створити нову жорстку посилання викликом link і видалити стару посилання виклику вом unlink.

Але в цьому випадку виникає ряд проблем:

  1. Файл може бути каталогом, для якого не можна створити другу жорстку посилання.
  2. Перейменований об'єкт може опинитися в іншому каталозі. Якщо це каталог тієї ж файлової системи, то проблем не виникає, але якщо інший, то виникне конфлікт, тому що link може створювати жорсткі посилання тільки в межах однієї файлової системи. Зрозуміло, можна створити символічну посилання в новому каталозі, але це не «перейменувати».
  3. Якщо необхідно «перейменувати» (фактично - «перемістити») каталог з однієї файлової системи в іншу, то доведеться перемістити ціле дерево підкаталогів з усім їхнім вмістом. Перемістити лише порожні каталоги буде недостатньо;
  4. В межах однієї файлової системи можна створити кілька жорстких посилань на один і той же файл - всі вони посилаються на один і той же індексний вузол, який не змінюється. Якщо буде необхідно перемістити файл в іншу файлову систему, то виникає наступна проблема. Створюється нова жорстка посилання, видаляється стара, але при цьому «стара» копія файлу залишиться на місці, так як файл не можна видалити, поки на нього є хоч одне посилання. Таким чином, замість «перейменування» виходить копіювання.
  5. Якщо змінити повний шлях до файлу, що міститься в символічній посиланням, то в результаті отримаємо неправильну посилання.

І т.д.

 

Тобто операція перейменування не така проста, як здається на перший погляд.

Як перейменувати каталог?

1. Можна було б копіювати каталог і вкладені підкаталоги з усім їхнім змістом, а потім видаляти старий каталог, навіть якщо він просто перейменовується, оста ваясь в тому ж самому батьківському каталозі. - Не підходить тому це занадто великий обсяг роботи.

2. Використовувати системний виклик link для каталогів, нехай навіть виконувати його може тільки суперкористувач. - Відкинутий POSIX і замінений на rename.

3. Використовувати новий системний виклик, який буде в змозі виконувати переміщення каталогів всередині файлової системи. - Це єдиний спосіб перейменувати каталог без повного копіювання його вмісту.

 

rename - перейменовує файл «include <stdio.h>

int rename (const char * oldpath, const char * newpath)

 

oldpath, старе повне ім'я

newpath нове повне ім'я

Повертає 0 у разі успіху, -1 у випадку помилки (код помилки-у змінній errno)

 

Виклик rename виконує приблизно таку послідовність дій:

1. Якщо newpath існує, то він віддаляється за допомогою unlink або rmdir.

2. Виконується link (oldpath, newpath), навіть якщо oldpath є каталогом.

3. Викликом unlink або rmdir видаляється oldpath.

 

Крім того, він має свої особливості та обмеження.

1. Крок 2 працює і з каталогами, навіть якщо процес не має права суперкористувача (процесу достатньо мати право на запис у батьківський каталог newpath).

2. Якщо newpath існує, тоді й newpath, і oldpath повинні бути одного типу, або файли, або каталоги.

3. Якщо файл буде перенесено в новий каталог, потрібно вказати ім'я файлу в новому каталозі явно

4. Якщо newpath існує і є каталогом, він повинен бути порожній (аналогічне правило існує і у rmdir). На кроці 3, якщо oldpath - каталог, він віддаляється, навіть якщо це непорожній каталог, так як його вміст уже мається на newpath.

5. Якщо oldpath є символічною посиланням, то rename працює з нею, а не з тим, на що вона посилається.

6. Якщо rename зазнає невдачі, то все залишається без змін.

 

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

На основі системного виклику rename створена утиліта mv, яка може виконувати більш широкий діапазон дій.

− переміщати окремі файли і каталоги між файловими системами.

− переміщувати групи файлів і каталогів.

 

7. Індівідуальні завдання

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

2. Читати вміст зазначеного каталогу в файл. Якщо каталог порожній, видати на екран повідомлення. Ім'я каталогу вводиться з клавіатури.

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

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

5. Вивести для певного каталогу імена текстових файлів, для яких дозволена запис. Ім'я каталогу задається через параметр командного рядка.

6. Вивести для каталогу (ім'я каталогу вводиться з клавіатури) список файлів, для яких дозволені виконання і читання.

7. Створити резервні копії текстових файлів, що мають атрибут дозволу для запису.

8. Читати вміст зазначеного каталогу в файл. Якщо каталог не порожній, видати на екран повідомлення. Ім'я каталогу передається через параметр командного рядка.

9. Роздрукувати з поточного каталогу містять цифри імена всіх файлів з розширеннями "*. c" і "*.cpp".

10. Створити в каталозі "./Links" символічні посилання на всі файли поточного каталогу з додаванням до імені файлу ".Link".

11. Копіювати в каталог, ім'я якого вводиться з клавіатури, файли, у яких ім'я починається з літер "a" або "z", якщо ці файли не є каталогами.

 

Лабораторна робота №7. Одержання і відображення метаданих файлу.

 

Мета:Оволодіння технологією отримання метаданих файлів різних типів. Застосування отриманих знань для написання програм.

 

Завдання для самостійної підготовки:

Вивчити:

− поняття метеданих файлів ОС UNIX;

− побудову та призначення системних стуктур зберігання метаданих;

− зв’язок між різними типами структурами;

− системні виклики отримання метаданих файлів.

 

Методичні вказівки

 

1. Механізми управління файлами ОС UNIX

 

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

 

1. Для управління файлами, які зберігаються но жорсткому діску, система має структуру inode.

Загальним елементом у всіх 4-х типах файлів є те, що вони управляються через INODE, що є інформаційним ядром для файлів будь-якого типу, містить всі необхідні інформаційні дані й використовується системою.

Файли і їхні імена жорстко пов'язані з INODE. З одним INODE може бути зв'язано кілька імен, але кожн активний INODE зв'язується тільки з одним файлом. Це забезпечує доступ до одного фізичного файлу через кілька імен, пов'язаних із загальним INODE.

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

 

Вміст INODE:

di_mode унікальний номер

file mode прапори прав доступу

di_count ідентифікатор користувача

di_uid ідентифікатор власника

di_gid ідентифікатор групи

di_size розмір файлу

di_addr[0]. адреса блоку даних

di_addr[n] адреса блоку даних

di_atime час останнього звертання до файлу

di_mtime час останньої модифікації файлу

di_ctime час останньої модифікації індексного

дескриптора

 

У кодах ядра посилання на INODE завжди починаються з посилання "di_". Вся інформація в INODE має тип "integer".

file mode - 2-х байтовый прапор, де перших 9 байтів задають право доступу (read/write) або дозвіл на виконання (файл типу exe). Ця інформація представляється у вигляді 3-х бітових полів, що задають зазначені права, відповідно для власника файлу, для груп директорій або для всіх директорій файлової системи. Наступний байт містить 4 біти, що визначають вид здійсненного (exe) файлу й тип файлу ( щовипливають 4 біти).

link count - поле, що вказує скільки директорій посилається на даний файл. Якщо в цьому полі коштує 0, то INODE не приєднаний ні до якого файлу, тобто файлу немає. Для директорій ця величина завжди => 2 (для батьківської директорії й для себе принаймні). Ця величина инкрементируется, якщо INODE одержує ім'я-синонім і декрементируется, якщо такоеимя вбирається.

group id - групи ідентифікаторів директорій, пов'язаних з файлом.

file size - розмір файлу в байтах, або 0 для спеціальних файлів.

Інші параметри ясні по своєму змісту.

Ядро UNIX містить таблицю i_list – список всіх активних INODE's. Таблиця i_list розташована на жорсткому диску і зберігається після виключення комп'ютера.

 

2. Для управління відкритими файлами ОС UNIX надає два базисні механізми: дескриптори (описувачі) файлу і потоки (файлові покажчики).