Ponizej przedstawiam znaczenie bitow slowa dostepu do pliku zapisanego w i-wezle na polu mode.
bit setuid - bit nadawania efektywnego identyfikatora uzytkownika (fsuid) dla procesu wykonujacego plik,
bit setgid - to samo dla fsgid,
bit przyklejania - gdy 1, to w pamieci zostaje kopia pliku po jego wykonaniu,
typ pliku (4 bity z lewej, ósemkowo):
001 - FIFO
002 - Character Device
004 - Directory
inne: Regular (zwykly), Block Device, Link, Socket.
Przyda sie jeszcze znajomosc czesci
struct file {
mode_t f_mode; /* tryb otwarcia pliku (pisanie/czytanie) - to nie jest ten sam tryb, co w i-wezle (patrz wyzej) */
loff_t f_pos; /* pozycja w pliku */
struct file_operations *f_op; /* operacje na pliku w konkretnym file-systemie */
unsigned short f_flags; /* czy plik ma szczególne wlasciwosci (tylko do nadpisywania, niezmienialny, do pisania synchronicznego lub inne) - dotyczy konkretnego otwarcia pliku, a nie wspolnego dla wszystkich otwarc i-wezla*/
struct file *f_next, *f_prev; /* wskaznik do poprzedniego i nastepnego pliku w tablicy plikow, ktora wcale nie jest tablica, ale lista dwukierunkowa. Jadro trzyma wskaznik do pierwszego pliku. Maksymalny rozmiar tej listy to 1024 = NR_FILE = maksymalna liczba plikow otwartych w systemie */
struct inode *f_inode; /* i-wezel odpowiadajacy plikowi */
... (i inne, dla mojego tematu nieistotne)
}
Ponizej przedstawiam funkcje (jedna) z wirtualego file-systemu. Oznacza to, ze jest ona niezalezna od file-systemu, ktory sobie zainstalowalismy.
Sluzy do pisania do pliku (w tym rowniez gniazda, gdyz gniazdo jest typem pliku).
DEFINICJA: int write(unsigned int fd, char *buf, unsigned int licznik)
WYNIK: liczba zapisanych bajtow lub blad.
DANE: deskryptor pliku (fd), wskaznik do danych do zapisania (buf), ilosc bajtow do zapisania (licznik).
Algorytm pisania:
{
Sprawdz w tablicy plikow prawo do pisania (pole file->mode);
Jezeli licznik=0, zwroc zero;
Upewnij sie, ze:
nie ma blokady na rekord do zapisania (locks_verify_area);
mozemy czytac z obszaru pamieci wskazywanego przez buf o dlugosci licznik (verify_area);
Jesli proces nie ma euid=0 (effective user id), czyli nie jest jednym z superuserow, to:
skasuj bit setuid;
jezeli bit setgid jest ustawiony, a grupa nie ma prawa wykonywania (odpowiedni bit w slowie dostepu do pliku w i-wezle jest zerem), to zostaw bit setgid, gdyz plik jest kandydatem do obowiazkowego blokowania (mandatory locking), w p.p. skasuj bit setgid (utrata nadawania efektywnego identyfikatora grupy dla procesu przy pisaniu do pliku);
Opusc semafor w i-wezle (zabezpieczenie przed jednoczesnym pisaniem);
Wywolaj write dla konkretnego file-systemu lub, jesli mamy do czynienia z gniazdem, to write dla gniazda - po prostu funkcje ze struktury file_operations;
Podnies semafor;
}
Kazdy filesystem ma swoja strukture file_operations, w której okreslone sa charakterystyczne dla niego operacje na plikach:
struct file_operations {
int (* lseek) (struct inode *, struct file *, off_t, int);
int (* read) (struct inode *, struct file *, char *, int);
int (* write) (struct inode *, struct file *, const char *, int);
int (* open) (struct inode *, struct file *);
i inne ... (plik fs.h)
}
Do struktury tej prowadzi wskaznik z kazdego pliku w tablicy plikow.
Po opuszczeniu semafora, w file-systemie Ext2 wykonywana przez jadro jest:
DEFINICJA: int ext2_file_write(inode *inode, file *file, char *buf, int licznik)
WYNIK: liczba zapisanych bajtow lub blad.
DANE: plik oraz odpowiadajacy mu i-wezel, buf i licznik - to wskaznik i rozmiar obszaru w pamieci z danymi (te same, co w funkcji write).
Algorytm pisania, c.d. :
{
Upewnij sie, ze:
file-system nie jest zamontowany read-only;
jestesmy plikiem regularnym, tzn. nie gniazdem, katalogiem, FIFO, etc.
Jesli plik jest tylko do nadpisywania (mówi o tym pole f_flags w strukturze file), to zacznij pisac na koncu pliku, w p.p. zacznij od aktualnej pozycji pliku;
Oblicz numer bloku logicznego, w którym znajduje sie pozycja w pliku, na której bedziemy pisali, oraz offset (to jest tak, ze pozycje mozna traktowac jako adres: bity mowiace o numerze bloku i o przesunieciu);
while (licznik>0)
{
Jesli wyszlismy poza pozycje nr 2GB, to zakoncz petle, a na wartosc wynikowa przypisz blad;
Pobierz bufor odpowiadajacy logicznemu blokowi i-wezla ( getblk() );
Liczba bajtów do zapisania := min{liczba bajtów od offsetu do konca bloku, licznik}. Chodzi o to, ze piszemy albo do konca bloku, albo mniej. Urzadzenie blokowe potrafi zapisac tylko caly blok, a nie pojedynczy bajt.
Odlicz od licznika liczbe bajtow pozostalych do konca bloku;
Jezli bufor nie jest aktualny i nie mamy go calego zapisac (po to obliczalismy liczbe bajtow do zapisania), to wczytaj odpowiadajacy mu blok z dysku (funkcja ll_rw_block(READ,...));
Skopiuj dane w pamieci z (argumentu funkcji) 'buf' do pobranego bufora;
Zaktualizuj Virtual Memory Cache odnosnie danego bloku - pobierz strone, zapisz do niej dane z bloku i zwolnij ja ( funkcja update_vm_cache() );
Przesun pozycje do pisania w pliku oraz miejsce pobierania danych w 'buf';
Zaznacz, ze bufor jest aktualny i nie jest brudny (brudny oznacza, ze co innego jest na dysku, niz w pamieci, oraz dane w pamieci sa lepsze);
Jesli plik jest do pisania synchronicznego, to zapisz bufor do tymczasowej tablicy i nie zapisuj go na dysk, dopoki jej nie zapelnisz (rozmiar tablicy: NBUF =16);
w p.p. (zapis nie synchroniczny) zwolnij bufor;
Jesli tablica jest juz pelna, to zapisz ja cala ( funkcja ll_rw_block(WRITE,...) ) i pozwalniaj bufory;
Zajmij sie nastepnym blokiem w pliku i ustaw w nim offset na 0.
}
Jesli zostaly bufory w tymczasowej tablicy (liczba blokow nie byla wielokrotnoscia 16), to je pozapisuj i pozwalniaj;
Jesli pozycja, w której skonczylismy pisac jest wieksza niz rozmiar pliku, to zwieksz jego rozmiar;
Ustaw w i-wezle czas ostatniej modyfikacji pliku (mtime) i i-wezla (ctime) na obecny;
Ustaw pozycje pliku w tablicy plików na ta, gdzie skonczylismy pisac;
Zaznacz, ze i-wezel jest brudny.
}
Opracowany przeze mnie kod zrodlowy - z komentarzem do prawie kazdej linijki jest tu:
Ogolne pytania pana Raczunasa:
Inne pytania:
tego samego autora: funkcja chown.