Pliki i systemy plików

Plik (ang. file) jest podstawową jednostką przechowywania danych w komputerach, a dokładniej: na tzw. urządzeniach pamięci masowej wykorzystywanych przez komputery. W praktyce, urządzenia te to najczęściej:

System plików (ang. filesystem) to z kolei warstwa systemu operacyjnego pośrednicząca w dostępie do informacji zapisanej na pamięci masowej, a więc do plików właśnie. Dzięki systemowi plików użytkownik (czy raczej wykorzystywane przez niego programy) nie potrzebuje troszczyć się np. o to, gdzie fizycznie na dysku zapisana jest potrzebna informacja; zamiast tego, system plików udostępnia użytkownikowi pewną strukturę logiczną (drzewo katalogów), o wiele wygodniejszą jako forma organizacji zapisu danych niż byłoby np. bezpośrednie lokalizowanie pliku przez jego adresy na dysku (numery sektorów lub t.p.). Ponadto system plików zarządza pewnymi meta-danymi o plikach i udostępnia je programom, realizując zarazem np. mechanizmy kontroli dostępu do plików.

Drzewo katalogów

Podstawową strukturą w unixowych systemach plików jest wspomniane już drzewo katalogów: katalogi (ang. directory) to też rodzaj plików, lecz o specjalnym charakterze -- służą one nie bezpośrednio do przechowywania danych, lecz do przechowywania informacji o innych plikach. Drzewowy charakter struktury polega na tym, że każdy katalog posiada swój (jedyny) katalog nadrzędny (odwołać się do niego można przez specjalną nazwę ".."), natomiast sam może ,,zawierać'' na równych prawach zarówno inne katalogi, jak i pliki ,,zwyczajne''; te ostatnie stanowią ,,liście'' drzewa katalogów. Wyjątkiem jest katalog główny, stanowiący ,,korzeń'' tego drzewa; jako jedyny nie posiada on katalogu nadrzędnego, natomiast wszystkie katalogi (i pliki) dostępne w danej chwili są osiągalne przez podanie tzw. pełnej ścieżki dostępu, specyfikującej cały łańcuch kolejnych podkatalogów przez które należy przejść, aby odnaleźć pozycję katalogową odnoszącą się do danego pliku. Nazwy kolejnych podkatalogów w ścieżce dostępu oddziela się znakiem "/". Przykładowo:

itd.

Należy pamiętać, że drzewo katalogów jest pewną abstrakcją, ,,lokalizacja'' danego pliku w obrębie drzewa katalogów nie ma a priori wiele wspólnego z lokalizacją odpowiednich danych na urządzeniu (lub urządzeniach) fizycznych (tj. dyskach). W szczególności, jednolite drzewo katalogów jest na ogół ,,zszyte'' z kawałków odpowiadających różnym urządzeniom: partycjom dysków bezpośrednio podłączonych do danego komputera, dyskom udostępnianym przez serwery sieciowe, itp.

Komendy: przykłady
% pwd
/home/staff/rjb
% cd tmp ; pwd
/home/staff/rjb/tmp
% cd /tmp ; pwd
/tmp
% cd ; pwd
/home/staff/rjb
% mkdir kuku ; ls -al kuku
total 16
drwxr-sr-x    2 rjb      rjb          4096 Nov  4 02:36 .
drwxr-sr-x   74 rjb      rjb         12288 Nov  4 02:36 ..
% rmdir kuku ; ls -al kuku
ls: kuku: No such file or directory
% cd .. ; pwd
/home/staff
% _

Nazwy plików

Jak widać, każdy plik (a więc i katalog) posiada swoją nazwę, przy czym nazwa ta musi być unikalna ale jedynie w ramach określonego katalogu, tj. wystąpienia tej samej nazwy w różnych katalogach nie mają ze sobą a priori nic wspólnego -- odnoszą się do w zasadzie różnych plików. Jeśli chodzi o reguły tworzenia nazw plików, to zasada jest dość prosta: nazwy te są (niemal) całkowicie dowolne, mogą składać się z dowolnych znaków (właściwie bajtów), z wyjątkiem jedynie znaku "/" oraz bajtu zerowego; ponadto zastrzeżone są nazwy "." oraz "..", które pojawiają się automatycznie w każdym nowo utworzonym katalogu i odnoszą się odpowiednio do samego danego katalogu (pojedyncza kropka) i jego katalogu nadrzędnego (dwie kropki). Poza tym np. kropka jako element nazwy nie jest w żaden sposób wyróżniona, a małe i wielkie litery są rozróżnianie (i równie dozwolone) w nazwach plików. Z drugiej jednak strony, wiele programów użytkowych stosuje się do pewnych konwencji dotyczących nazw plików, a szczególnie co do tzw. końcówki nazwy -- ciągu znaków po ostatniej kropce. Np. o pliku o nazwie nazwa.c zakłada się, że zawiera kod źródłowy w języku C, podobnie nazwa.C lub nazwa.cpp powinien na mocy umowy zawierać kod źródłowy w C++ (konwencje stosowane przez kompilatory), nazwa.html zawierać powinien tekst strony WWW w języku HTML (konwencja serwera WWW), itp. Podkreślić należy jednak, że są to konwencje które nie mają wpływu na sposób traktowania tych nazw przez sam system plików.

Ważnym przypadkiem takie konwencji są nazwy zaczynające się od kropki (np. .login czy .profile). Pliki o takich nazwach to tzw. pliki ukryte; nie są one np. domyślnie uwzględniane w spisie zawartości katalogu wypisywanym przez komendę ls. Są one najczęściej wykorzystywane do zapisu osobistych ustawień użytkownika dotyczących poszczególnych używanych przez niego programów, i często są tworzone automatycznie przez te programy, a ,,ukrywane'' są po prostu dla wygody: ponieważ rzadko potrzebujemy korzystać z nich bezpośrednio, uwzględnianie ich w spisach zawartości katalogów tworzyłoby jedynie zbędny ,,szum informacyjny''. Znowu jednak sam system nie traktuje ich w żaden szczególny sposób, i w razie potrzeby możemy mieć do nich bezpośredni dostęp na ogólnych zasadach.

Długość nazwy pliku (liczba znaków z której się składa) nie jest oczywiście nieograniczona, konkretna wartość tej granicy zależy od systemu: np. typowo w Linuxie maksymalna długość nazwy pliku to 255 znaków, a maksymalna długość pełnej ścieżki dostępu do pliku to 4095 znaków (patrz /usr/include/linux/limits.h).

W praktyce, dobierając nazwy dla tworzonych przez nas plików, warto przestrzegać wspomnianych konwencji nazewniczych oraz mimo wszystko unikać stosowania w nazwach znaków ,,specjalnych'', takich jak posiadające szczególne znaczenie dla shella (interpretera komend): takimi są np. *,?,!,<,>,|,\,$ oraz spacja. ,,Bezpieczne'' są nazwy składające się ze znaków alfanumerycznych oraz niektórych znaków przestankowych, takich jak kropka, - (minus), _ (znak podkreślenia). Wprawdzie stosowanie innych znaków nie jest zakazane, lecz nazwy je zawierające mogą sprawiać kłopoty ze względu na konieczność pamiętania o tym, by ,,chronić'' je odpowiednio przed interpretacją przez shell oraz z powodu ograniczeń występujących w niektórych, nie całkiem ,,porządnie'' napisanych programach, które mogą nie traktować ich poprawnie.

Dowiązania ,,twarde'' i ,,miękkie'' (symboliczne)

W rzeczywistości, katalogi nie tyle ,,zawierają'' same pliki, co pewne meta-dane opisujące te pliki (i umożliwiające dostęp do danych zapisanych w tych plikach). Fakt ten ma istotną konsekwencję praktyczną: jednemu i temu samemu plikowi (jako zbiorowi danych) może odpowiadać więcej niż jeden wpis, w tym samym lub w różnych katalogach, i sytuacja taka jest całkowicie legalna. Mówimy wtedy o ,,twardych dowiązaniach'' (ang. hard links). Wszystkie takie dowiązania, tj. różne pozycje katalogowe wskazujące na te same zbiory danych, są równoprawne; usunięcie jednego z nich nie spowoduje utraty dostępu do odpowiadających mu danych (tj. ich ,,skasowania''), dopóki nie zostanie usunięte ostatnie takie dowiązanie. Dopiero wtedy miejsce na dysku zajmowane przez te dane zostanie dołączone do ,,mapy wolnej przestrzeni'' utrzymywanej przez system, i (w trudnym do przewidzenia czasie) zostanie ponownie wykorzystane na zapis kolejnych danych.

Nie ma prostego sposobu, by ustalić jakie konkretnie twarde dowiązania do danego pliku istnieją w określonej chwili. Łatwo dostępna jest jedynie informacja o liczbie istniejących twardych dowiązań. Istnienie twardych dowiązań podlega ponadto pewnym ograniczeniom: większość systemów unixowych nie pozwala na tworzenie twardych dowiązań do katalogów (w odróżnieniu do plików ,,zwyczajnych'', a to ze względu na możliwość wytworzenia ,,pętli'' dowiązań); co ważniejsze jednak, wszystkie twarde dowiązania do danego pliku muszą znajdować się w poddrzewie drzewa katalogów odpowiadającym temu samemu fizycznemu urządzeniu (np. partycji dyskowej).

To ostatnie ograniczenie nie obowiązuje w przypadku dowiązań ,,miękkich'', zwanych też symbolicznymi (ang. soft links, symbolic links). W odróżnieniu od dowiązania twardego, dowiązanie symboliczne nie jest równoprawnym wpisem katalogowym wskazującym na dany plik, lecz specjalnym typem pliku, zawierającym jedynie informację o innym wpisie katalogowym na który wskazuje (zamiast wskazywać ,,bezpośrednio'' na plik). Usunięcie dowiązania symbolicznego w żadnym przypadku nie stanowi ,,skasowania'' odpowiednich danych, za to możliwa jest sytuacja, gdy dowiązanie symboliczne wskazuje ,,w próżnię'', tj. na nieistniejącą pozycję katalogową. Z tymi zastrzeżeniami, z dowiązania symbolicznego można korzystać tak samo jak z ,,prawdziwej'' pozycji katalogowej wskazującej na plik. W tym przypadku nie obowiązuje również zakaz tworzenia dowiązań wskazujących na katalogi. Tworzenie ,,łańcucha'' kolejnych dowiązań symbolicznych również jest legalne, lecz długość (liczba ,,ogniw'') takiego łańcucha podlega ograniczeniom.

Operacje na linkach
$ echo Hello, World > hello ; ls -l
total 4
-rw-r--r--    1 rjb      rjb            13 Nov  5 23:08 hello
$ ln hello hi ; ls -l
total 8
-rw-r--r--    2 rjb      rjb            13 Nov  5 23:08 hello
-rw-r--r--    2 rjb      rjb            13 Nov  5 23:08 hi
$ cat hi
Hello, World
$ echo It\'s a beautiful day >> hi ; cat hello
Hello, World
It's a beautiful day
$ rm hello ; ls -l
total 4
-rw-r--r--    1 rjb      rjb            34 Nov  5 23:13 hi
$ ln -s hi hello ; ls -l
total 4
lrwxrwxrwx    1 rjb      rjb             2 Nov  5 23:15 hello -> hi
-rw-r--r--    1 rjb      rjb            34 Nov  5 23:13 hi
$ echo Good night > hello ; cat hi
Good night
$ rm hi ; ls -l
total 0
lrwxrwxrwx    1 rjb      rjb             2 Nov  5 23:15 hello -> hi
$ cat hello
cat: hello: No such file or directory
$ _

Kontrola dostępu

Komputer unixowy z założenia ma umożliwiać pracę wielu użytkownikom (i to równocześnie), stąd też bierze się konieczność istnienia mechanizmów kontroli dostępu do danych zapisanych na pamięciach masowych (dyskach). Mechanizm taki w standardowej wersji, realizowanej we wszystkich unixowych systemach plików, jest dość prosty:

Jako że są trzy rodzaje uprawnień i trzy kategorie użytkowników, daje to w sumie dziewięć pozycji, które przyjęto reprezentować jako dziewięć bitów (bit ,,ustawiony'' czyli równy jeden oznacza oczywiście przyznanie danego prawa, a wartość zerowa odmowę), co jest równoważne trzem cyfrom układu ósemkowego. Oznaczają one kolejno: prawa właściciela (u), prawa grupy (g) oraz prawa ogólne (o), a wartość każdej z nich powstaje przez dodanie liczby cztery (jeżeli przysługuje prawo odczytu) do liczby dwa (jeżeli przysługuje prawo zapisu) i do liczby jeden (jeżeli przysługuje prawo uruchamiania); jak widać, otrzymuje się wartość między 0 a 7, gdzie 0 reprezentuje brak jakichkolwiek uprawnień a 7 -- pełne prawa.

Pewnej uwagi wymaga interpretacja praw dostępu w zastosowaniu do katalogów. Z katalogami związany jest taki sam zestaw bitów uprawnień jak ze ,,zwykłymi'' plikami, przy czym brak prawa x (uruchamiania) ma znaczenie zakazu jakiegokolwiek dostępu do zawartości danego katalogu (oraz jego podkatalogów). Należy też mieć na uwadze, że operacje utworzenia pliku czy też jego skasowania (usunięcia z katalogu) nie są operacjami na zawartości pliku, lecz na zawartości odpowiedniego katalogu, możliwość ich wykonania uwarunkowana jest więc prawami dostępu danego użytkownika nie do samego pliku, a do katalogu w którym się on znajduje.

Z dowiązaniami symbolicznymi nie są natomiast związane żadne prawa dostępu; ponieważ dowiązanie takie jest jedynie wskazaniem na jakąś inną pozycję katalogową, to obowiązują prawa dostępu określone dla pliku na który dowiązanie wskazuje. Natomiast (jak wspomniano powyżej) operacje utworzenia czy usunięcia dowiązania są operacjami dotyczącymi katalogu w którym ono się znajduje.

Prawa dostępu: przykłady
$ echo Hello, World > hello ; ls -l
total 4
-rw-r--r--    1 rjb      rjb            13 Nov  7 23:36 hello
[ standardowe prawa: odczytu i zapisu dla właściciela,
tylko odczytu dla grupy i reszty ]
$ chmod u-w hello ; ls -l
total 4
-r--r--r--    1 rjb      rjb            13 Nov  7 23:36 hello
[ odebrałem sobie prawo do zapisu, czyli modyfikacji ]
$ chmod 400 hello ; ls -l
total 4
-r--------    1 rjb      rjb            13 Nov  7 23:36 hello
[ teraz tylko ja mam prawo odczytu ]
$ chmod 666 hello ; ls -l
total 4
-rw-rw-rw-    1 rjb      rjb            13 Nov  7 23:36 hello
[ prawo odczytu i zapisu dla wszystkich ]
$ chmod go-w hello ; ls -l
total 4
-rw-r--r--    1 rjb      rjb            13 Nov  7 23:36 hello
[ przywróciłem stan początkowy ]
$ echo '#!/bin/sh' > hello ; echo 'echo Hello, World' >> hello ; cat hello
#!/bin/sh
echo Hello, World
[ stworzyłem najprostszy skrypt shellowy ]
[ a teraz zrobię z niego program wykonywalny ]
$ chmod +x hello ; ls -l
total 4
-rwxr-xr-x    1 rjb      rjb            28 Nov  7 23:47 hello
$ ./hello
Hello, World
[ i rzeczywiście, działa ! ]

Jak widać na powyższych przykładach, komenda chmod, służąca do zmiany praw dostępu, akceptuje instrukcje zarówno w postaci symbolicznej, określającej czy dane uprawnienie (r, w, x) należy przyznać (+) czy odebrać (-) i komu (u, g, o), jak i w postaci bezwzględnej, polegającej na podaniu kodu ósemkowego docelowej postaci żądanych praw dostępu do pliku.

Uwaga: prawa dostępu dotyczą samego pliku, a nie danej pozycji katalogowej. Oznacza to tyle, że w sytuacji istnienia wielu twardych dowiązań do jednego pliku prawa dostępu nie będą się dla nich różniły.

Limit miejsca czyli quota

Limitowanie przestrzeni dyskowej nie tylko zapobiega ,,antyspołecznym'' zachowaniom użytkowników; jest ono również zabezpieczeniem przed sytuacją, gdy błędnie zachowujący się program zapełnia całe dostępne miejsce na dysku bezużytecznymi wynikami lub komunikatami o błędach. Trzeba jednak przyznać, że stosowanie (dość niskich) limitów przestrzeni dyskowej na użytkownika wynika zazwyczaj głównie z braku środków na zakup większych dysków.

Limity zapisu czyli quota dotyczą każdego urządzenia pamięci masowej (partycji dyskowej) osobno, i zazwyczaj stosowane są przede wszystkim do partycji będącej nośnikiem poddrzewa /home, zawierającego katalogi własne użytkowników. Zresztą zazwyczaj ,,szeregowemu'' użytkownikowi prawo do zapisu plików przysługuje jedynie w obrębie jego katalogu własnego oraz katalogów takich, jak /tmp czy /var/tmp, przeznaczonych do zapisu plików tymczasowych.

Limity zapisu są dwojakiego rodzaju: tzw. miękki limit (ang. quota), którego ,,miękkość'' polega na tym, że może on być przekroczony, choć jedynie przez ograniczony czas (zazwyczaj tydzień). Po przekroczeniu tego czasu dalsze operacje zapisu zlecane przez danego użytkownika zostają zablokowane, dopóki nie zmniejszy on wykorzystania przestrzeni dyskowej poniżej wartości quoty. Limit ,,twardy'' (ang. limit) z kolei nie może zostać przekroczony: system od razu odmawia wykonania operacji zapisu powodującej przekroczenie limitu.

Ponadto zarówno quota jak i limit mogą dotyczyć dwóch różnych wielkości: ilości zajętego miejsca na dysku (liczonej np. w kB), oraz liczby zajętych inodów (w uproszczeniu, chodzi o liczbę plików wytworzonych przez użytkownika, niezależnie od ich wielkości). Zazwyczaj praktyczne znaczenie ma ograniczenie w kilobajtach, przekroczenie granicy liczby inodów jest w praktyce mało prawdopodobne.

Aktualnie w OKWF quota dla kont studenckich wynosi standardowo 20 MB, natomiast limit -- 25 Mb.

Do ustalenia stanu wykorzystania swojej quoty służy komenda quota. Wywołana bez opcji wypisze informację jedynie jeżeli quota jest aktualnie przekroczona, natomiast w postaci quota -v poinformuje użytkownika o stanie wykorzystania jego ,,przydziału'' w każdym przypadku.

Najczęstsze przyczyny bezwiednego przekroczenia limitów dyskowych to:

Jedną z przykrych konsekwencji przekroczenia limitu dyskowego jest niemożliwość otwarcia sesji użytkownika na terminalu graficznym (X Window System). Zainicjowanie sesji wiąże się bowiem z zapisaniem paru plików w katalogu użytkownika (co nie ma miejsca w przypadku otwierania sesji na terminalu znakowym). Rozwiązanie polega więc na otwarciu sesji na terminalu znakowym i usunięciu plików powodujących przekroczenie limitu. Na PC linuxowych dostęp do trybu terminala znakowego uzyskuje się zazwyczaj przez kombinację klawiszy Ctrl-Alt-F[1-6], tj. należy wcisnąć równocześnie klawisze Ctrl, Alt (lewy) oraz jeden z klawiszy oznaczonych F1, F2, ...F6. Powrót do trybu graficznego następuje przez naciśnięcie Alt-F7.

Czytane razy.