3.Apelurile de sistem pentru lucrul cu fişiere şi directoare
3.1. Descriptori de fişier
Sistemul de operare ataşează intern fiecărui fişier deschis un descriptor sau identificator de fişier (în principiu, un număr întreg pozitiv). La deschiderea unui fişier sau la crearea unui fişier nou, sistemul returnează un descriptor de fişier procesului care a executat operaţia. Fiecare proces îşi are proprii descriptori de fişier. Prin convenţie, primii trei descriptori de fişier ai fiecărui proces sunt alocaţi automat la crearea lui. Descriptorul de fişier 0 este asociat intrării standard (tastatura), 1 ieşirii standard (ecranul), iar 2 ieşirii standard de eroare (ecranul). Ceilalţi descriptori sunt folosiţi de proces pentru deschiderea de fişiere ordinare, pipe, speciale sau directoare. Există cinci apeluri sistem care generează descriptori de fişiere: creat, open, fcntl, dup şi pipe.
3.2. Apeluri sistem pentru lucrul cu fişiere
3.2.1. Apelul sistem OPEN
Deschiderea sau crearea unui fişier se poate face prin apelul sistem open. Sintaxa acestui apel este:
#include
#include
#include
int open(const char *cale,
int optiuni, mode_t permisiuni);
Funcţia returnează un descriptor de fişier sau -1 în caz de eroare. La apelul acestei funcţii se pot specifica două sau trei argumente, al treilea argument fiind folosit doar la crearea de fişiere noi. Apelul cu două argumente este folosit pentru deschiderea fişierelor existente. Funcţia returnează cel mai mic descriptor de fişier disponibil. Acesta va fi utilizat în apelurile sistem ulterioare: read, write, lseek şi close. Procesul care execută apelul sistem open trebuie să aibă drepturi de citire şi/sau scriere asupra fişierului pe care încearcă să-l deschidă, în funcţie de valoarea argumentului optiuni. Pointerul din fişier (poziţia curentă relativ la care se vor efectua operaţiile de citire şi scriere) este poziţionat pe primul octet din fişier. Argumentul optiuni se formează printr-un SAU pe biţi între următoarele constante, definite în fişierul fcntl.h:
O_RDONLY Fişierul este deschis doar pentru citire.
O_WRONLY Fişierul este deschis doar pentru scriere.
O_RDWR Fişierul este deschis pentru citire şi scriere.
O_APPEND Scrierile în fişier se fac întotdeauna la sfârşitul fişierului. Acest lucru este asigurat automat de către sistemul de operare, ca şi cum procesul ar poziţiona anterior scrierii pointerul în fişier la sfârşitul fişierului.
O_CREAT Dacă fişierul nu există el este creat. Dacă există, este trunchiat.
O_EXCL Dacă fişierul există şi este specificată şi opţiunea O_CREAT, apelul open nu se execută cu succes.
O_NDELAY
O_NONBLOCK La fişiere pipe şi cele speciale pe bloc sau caracter cauzează trecerea în modul fără blocare atât pentru apelul open, cât şi pentru operaţiile viitoare de I/E.
O_TRUNC Dacă fişierul există îi şterge conţinutul.
O_SYNC Forţează scrierea efectivă pe disc prin write. Întârzie mult întregul sistem, dar e eficace în cazuri critice.
Argumentul al treilea, permisiuni, poate fi o combinaţie de SAU pe biţi între următoarele constante predefinite:
S_IRUSR, S_IWUSR, S_IXUSR Proprietar: read, write, execute.
S_IRGRP, S_IWGRP, S_IXGRP Group: read, write, execute.
S_IROTH, S_IWOTH, S_IXOTH Alţii: read, write, execute.
Aceste constante definesc drepturile de acces asupra unui fişier şi sunt definite în fişierul sys/stat.h.
3.2.2. Apelul sistem CREAT
Un fişier nou este creat cu ajutorul apelului sistem creat, a cărui sintaxă este:
#include
#include
#include
int creat(const char *cale, mode_t permisiuni);
Funcţia creat returnează descriptorul de fişier sau -1 în caz de eroare. Apelul funcţiei creat este echivalent cu apelul funcţiei open în forma:
open(path, O_WRONLY | O_CREAT | O_TRUNC, mod);
Argumentul cale specifică calea şi numele fişierului, iar permisiuni drepturile de acces. Dacă fişierul creat nu există, este alocat un nou i-node şi o legătură spre el este plasată în directorul unde acesta a fost creat. Proprietarul procesului (dat de UID-ul efectiv şi GUID-ul efectiv) care execută acest apel trebuie să aibă permisiunea de scriere în directorul unde se creează fişierul. Fişierul deschis va avea drepturile de acces specificate de argumentul al doilea din apel (vezi şi umask). Apelul întoarce cel mai mic descriptor de fişier disponibil. Fişierul este deschis în scriere, iar dimensiunea sa iniţială este 0. Timpii de acces şi modificare din i-node sunt actualizaţi. Dacă fişierul există (este nevoie de permisiunea de căutare în director) conţinutul lui este şters, fişierul este deschis în scriere, dar nu se modifică proprietarul sau drepturile de acces aspra lui. În acest ultim caz al doilea argument este ignorat.
3.2.3. Apelul sistem READ
Pentru a citi un anumit număr de octeţi dintr-un fişier de la poziţia curentă, se foloseşte apelul sistem read. Sintaxa lui este:
#include
ssize_t read(int fd, void* buf, size_t noct);
Funcţia returnează numărul de octeţi citiţi efectiv, 0 pentru sfârşit de fişier (EOF) şi -1 în caz de eroare. Se încearcă citirea a noct octeţi din fişierul deschis referit de descriptorul fd şi se depun la adresa de memorie indicată de parametrul buf. Pointerul (poziţia curentă) în fişier este incrementat automat după o operaţie de citire cu numărul de octeţi citiţi. Se revine din funcţia read doar după ce datele citite de pe disc (din fişier) sunt transferate în memorie. Acest tip de funcţionalitate se numeşte sincronă.
3.2.4. Apelul sistem WRITE
Pentru a scrie un anumit număr de octeţi într-un fişier la poziţia curentă, se foloseşte apelul sistem write. Sintaxa lui este:
#include
ssize_t write(int fd, const void* buf, size_t noct);
Funcţia returnează numărul de octeţi scrişi si -1 în caz de eroare. Apelul scrie noct octeţi preluaţi de la adresa de memorie indicată de parametrul buf în fişierul al cărui descriptor este fd. Interesant de remarcat referitor la acest apel este faptul că scrierea fizică pe disc este întârziată. Ea se efectuează la iniţiativa sistemului de operare fără ca utilizatorul să fie informat. Dacă procesul care a efectuat apelul sau un alt proces citeşte datele care încă nu au fost scrise pe disc, sistemul le citeşte înapoi din bufferele cache. Scrierea întârziată este mai rapidă, dar are trei dezavantaje:
-
eroare pe disc sau căderea sistemului duce la pierderea datelor;
-
un proces care a iniţiat o operaţie de scriere nu poate fi informat în cazul apariţiei unei erori de scriere;
-
ordinea scrierilor fizice nu poate fi controlată.
Pentru a elimina aceste dezavantaje, în anumite cazuri se foloseşte opţiunea O_SYNC specificată în momentul deschiderii fişierului. Dar cum aceasta scade viteza sistemului şi având în vedere fiabilitatea sistemelor de astăzi se preferă mecanismul de lucru cu tampoane cache.
3.2.5. Apelul sistem CLOSE
Pentru închiderea unui fişier şi, implicit, eliberarea descriptorului ataşat, se foloseşte apelul sistem close. Sintaxa lui este:
#include
int close(int fd);
Funcţia returnează 0 în caz de succes şi -1 în caz de eroare. Un fişier deschis este oricum închis automat la terminarea procesului.
3.2.6. Apelul sistem LSEEK
Pentru poziţionarea absolută sau relativă a indicatorului poziţiei curente într-un fişier se foloseşte apelul sistem lseek. Următoarele operaţiile de citire din şi scriere în fişier se vor efectua relativ la noua poziţie curentă în fişier. Sintaxa funcţiei lseek este următoarea:
#include
#include
off_t lseek(int fd, off_t salt, int relativLa);
Funcţia returnează deplasamentul faţă de începutul fişierului al noii poziţii curente din fişier sau -1 în caz de eroare. Nu se efectuează nici o operaţie de I/O şi nu se trimite nici o comandă controlerului de disc. Saltul relativ la punctul indicat de parametrul relativLa se face cu numărul de octeţi indicaţi de parametrul salt. Parametrul relativLa poate avea doar una dintre următoarele valori predefinite:
SEEK_SET repoziţionarea (saltul) se face relativ la începutul fişierului (primul octet din fişier este la deplasament zero).
SEEK_CUR repoziţionarea se face relativ la poziţia curentă.
SEEK_END repoziţionarea se face relativ la sfârşitul fişierului.
Apelurile sistem open, creat, write şi read execută implicit lseek. Dacă un fişier este deschis folosind constanta simbolică O_APPEND, se efectuează un apel lseek la sfârşitul fişierului înaintea unei operaţii de scriere.
3.2.7. Apelul sistem LINK
Pentru a lega un fişier existent la un alt director (sau acelaşi) se foloseşte apelul sistem link. Stabilirea unei legături înseamnă de fapt stabilirea unui nou nume sau a unei noi căi de acces spre un fişier existent. Sintaxa acestui apelul sistem este:
#include
int link(const char* vecheaCale, const char* NouaCale);
Funcţia returnează 0 în caz de succes şi -1 în caz contrar. Argumentul vecheaCale trebuie să indice un fişier existent. Doar root-ul are dreptul de a stabili legături spre un director.
3.2.8. Apelul sistem UNLINK
Pentru a şterge o legătură (cale) dintr-un director se foloseşte apelul sistem unlink. Sintaxa lui este:
#include
int unlink(const char* cale);
Funcţia returnează 0 în caz de succes şi -1 în caz contrar. Apelul unlink decrementează contorul de legături fizice din i-node-ul fişierului specificat şi şterge intrarea din director corespunzătoare fişierului şters. Dacă numărul de legături ale unui fişier devine 0, spaţiul ocupat de fişierul în cauză şi i-node-ul său este eliberat. Doar root-ul poate să şteargă un director folosind acest apel sistem. Altfel, apelul sistem rmdir poate fi utilizat pentru a şterge un director.
3.2.9. Apelurile sistem STAT, LSTAT şi FSTAT
Pentru a obţine informaţii detaliate despre un fişier se pot folosi apelurile sistem stat, lstat sau fstat.
#include
#include
int stat(const char* cale, struct stat* buf);
int lstat(const char* cale, struct stat* buf);
int fstat(int fd, struct stat* buf);
Cele trei funcţii returnează 0 în caz de succes şi -1 în caz de eroare. Primele două primesc ca parametru calea şi numele spre un fişier şi completează structura de la adresa buf cu informaţii din i-node-ul fişierului. Apelul fstat e similar, dar funcţionează pentru fişiere deschise cărora li se cunoaşte descriptorul. Diferenţa între stat şi lstat apare doar în cazul unui fişier legătură simbolică, caz în care stat returnează informaţii despre fişierul referit (legat), pe când lstat returnează informaţii despre fişierul legătură. Structura struct stat e definită în fişierul sys/stat.h şi conţine câmpurile:
struct stat {
mode_t st_mode; /* tip fişier & drepturi */
ino_t st_ino; /* i-node */
dev_t st_dev; /* număr de dispozitiv (SF) */
nlink_t st_nlink; /* numărul de legaturi */
uid_t st_uid; /* ID proprietar */
gid_t st_gid; /* ID grup */
off_t st_size; /* dim. fişiere ordinare */
time_t st_atime; /* timpul ultimului acces */
time_t st_mtime; /* timpul ultimei modificări */
time_t st_ctime; /* timpul schimbării stării */
dev_t st_rdev; /* nr. dispozitiv */
/* pt. fişiere speciale /
long st_blksize; /* dimensiunea optima */
/* a blocului de I/E */
long st_blocks; /* număr de blocuri */
/* de 512 octeti alocate */
};
Comanda Linux care foloseşte cel mai des acest apel sistem este ls. Declaraţiile de tipuri pentru membrii structurii se găsesc în fişierul sys/types.h. Tipul fişierului este codificat, alături de drepturile de acces, în câmpul st_mode şi poate fi determinat folosind următoarele macrouri:
Tabelul 1. Macrouri pentru obţinerea tipului unui fişier
|
Macro
|
Semnificaţie
|
S_ISREG(st_mode)
|
Fişier obişnuit
|
S_ISDIR(st_mode)
|
Fişier director
|
S_ISCHR(st_mode)
|
Dispozitiv special de tip caracter
|
S_ISBLK(st_mode)
|
Dispozitiv special de tip bloc
|
S_ISFIFO(st_mode)
|
Fişier pipe sau fifo
|
S_ISLNK(st_mode)
|
Legătura simbolică
|
Decodificarea informaţiilor din st_mode poate fi făcută testând rezultatul operaţiei de „ŞI pe biţi” (&) între câmpul st_mode şi una dintre constantele (măşti de biţi): S_IFIFO, S_IFCHR, S_IFBLK, S_IFDIR,S_IFREG, S_IFLNK, S_ISUID (setat bitul suid), S_ISGID (setat bitul sgid), S_ISVTX (setat bitul sticky), S_IRUSR (drept de citire pentru proprietar), S_IWUSR (drept de scriere pentru proprietar), S_IWUSR(drept de execuţie pentru proprietar) etc.
3.2.10. Apelul sistem ACCESS
În momentul accesului unui fişier de către un proces sistemul de operare verifică permisiunea de acces la fişier a acelui proces în funcţie de UID-ul şi GID-ul său efectiv. Există situaţii când este nevoie să se testeze drepturile de acces bazându-se pe UID-ul şi GID-ul real. O situaţie în care acest lucru este util este atunci când un proces se execută, datorită setării biţilor suid sau sgid ai fişierului executabil, cu alte drepturi decât cele ale utilizatorului care a lansat procesul, dar se doreşte să se verifice dacă utilizatorul real poate accesa fişierul. Apelul sistem access permite testarea acestui lucru. Sintaxa lui este:
#include
int access(const char* cale, int tipAcces);
Funcţia returnează 0 dacă există permisiunea şi -1 în caz contrar. Parametrul tipAcces poate fi o combinaţie de tipul „ŞI pe biţi” între următoarele constante predefinite: R_OK (dreptul de citire), W_OK(dreptul de scriere), X_OK (dreptul de execuţie), F_OK (existenţa fişierului).
3.2.11. Apelul sistem UMASK
Pentru a îmbunătăţii securitatea sistemului de fişiere, sistemul de operare Linux permite stabilirea pentru fiecare proces a unei măşti (filtru) de biţi ce indică resetarea automată a unor drepturi de acces la crearea fişierelor. Structura pe biţi a acestei măşti este similară cu structura câmpului din i-node-ul fişierelor care codifică pe biţi permisiunile de acces. Biţii din mască poziţionaţi pe 1 invalidează, la crearea unui fişier, biţii corespunzători din argumentul care precizează drepturile de acces. Masca nu afectează apelul sistem chmod, astfel încât procesele au posibilitatea de a-şi fixa explicit drepturile de acces indiferent de valoarea măştii stabilite prin umask. Sintaxa apelului este:
#include
#include
mode_t umask(mode_t mask);
Funcţia returnează valoarea măştii anterioare. Efectul apelului este ilustrat în exemplul de mai jos:
main()
{
int fd;
umask(022);
if ((fd=creat("temp", 0622))==-1)
{ perror("creat"); exit(0); }
system("ls -l temp");
}
Rezultatul afişat va fi de forma:
-rw------- temp
în care se observă resetarea automată a drepturilor de scriere pentru grup şi alţi utilizatori decât proprietarul.
3.2.12. Apelul sistem CHMOD
Pentru a modifica drepturile de acces asupra unui fişier existent se poate folosi apelul sistem chmod, a cărui sintaxă este:
#include
#include
int chmod(const char* cale, mode_t permisiuni);
Funcţia returnează 0 în caz de succes şi -1 în caz contrar. Funcţia chmod modifică drepturile de acces ale fişierului specificat de parametrul cale în conformitate cu valoarea argumentului permisiuni. Pentru a putea modifica drepturile de acces, UID-ul efectiv al procesului trebuie să fie egal cu cel al proprietarului fişierului sau procesul trebuie să aibă drepturi de root.
Argumentul permisiuni poate fi specificat printr-una dintre constantele descrise mai jos şi definite în sys/stat.h. Se poate obţine un efectul cumulat al lor folosind operatorul SAU pe biţi:
Tabelul 2. Măşti pe biţi pentru setarea drepturilor de acces la un fişier
|
Mod
|
Descriere
|
S_ISUID
|
Setează bitul suid
|
S_ISGID
|
Setează bitul sgid
|
S_ISVTX
|
Setează bitul sticky
|
S_IRWXU
|
Drept de citire, scriere şi execuţie pentru proprietar obţinut din:S_IRUSR | S_IWUSR | S_IXUSR
|
S_IRWXG
|
Drept de citire, scriere şi execuţie pentru grup, obţinut din: S_IRGRP | S_IWGRP | S_IXGRP
|
S_IRWXO
|
Drept de citire, scriere şi execuţie pentru alţii, obţinut din: S_IROTH | S_IWOTH | S_IXOTH
|
3.2.13. Apelul sistem CHOWN
Acest apel sistem este utilizat în scopul modificării proprietarului (UID) şi al grupului (GID) căruia îi aparţine un fişier. Sintaxa funcţiei este:
#include
#include
int chown(const char* cale,
uid_t proprietare, gid_t grup);
Funcţia returnează 0 în caz de succes şi -1 în caz de eroare. Apelul ei schimbă proprietarul şi grupul fişierului precizat de parametrul cale, la noul proprietar specificat de parametrul proprietar şi la noul grup specificat de parametrul grup. În afară de root, un utilizator obişnuit nu poate schimba proprietarul fişierelor altor utilizatori, dar poate schimba GID-ul pentru fişierele proprii, dar numai pentru acele grupuri din care face parte.
3.2.14. Apelul sistem UTIME
În structura stat există trei membri care se referă la timp, conform tabelului de mai jos.
Tabelul 3. Informaţii de timp asociate unui fişier
|
Câmp
|
Descriere
|
Operaţie
|
st_atime
|
Ultimul acces la datele fişierului
|
read
|
st_mtime
|
Ultima modificare a datelor fişierului
|
write
|
st_ctime
|
Schimbarea stării i-node-ului
|
chmod, chown
|
Diferenţa între timpul de modificare al fişierului şi cel de schimbare a stării i-nod-ului constă în faptul că primul se referă la momentul în care conţinutul fişierului a fost modificat, iar cel de-al doilea la momentul în care informaţia din i-node a fost modificată. Acest lucru se datorează faptului că informaţia din i-node este memorată separat de conţinutul fişierului. Apelurile sistem care modifică i-node-ul sunt cele care modifică drepturile de acces asupra unui fişier, cele care schimba UID-ul, numărul de legaturi, etc. Sistemul de operare nu reţine ultimul acces la i-node. Acesta este motivul pentru care apelurile sistem accessşi stat nu schimbă nici unul dintre aceşti timpi.
Timpii de acces şi de modificare ai unui fişier de orice tip pot fi schimbaţi printr-unul dintre apelurile sistem de mai jos:
#include
int utimes(const char* cale,
const struct timeval* timpi);
int lutimes(const char* cale,
const struct timeval* timpi);
int futimes(int fd, const struct timeval* timpi);
Funcţiile returnează 0 în caz de succes şi -1 în caz contrar. Doar proprietarul unui fişier sau root-ul pot modifica timpii asociaţi unui fişier. Parametrul timpi reprezintă adresa (pointer) unui şir de două structuritimeval, care corespund timpului de acces şi, respectiv, de modificare. Câmpurile structurii timeval sunt descrise mai jos:
struct timeval {
long tv_sec; // sec. trecute din 1.01.1970
suseconds_t tv_usec; // microsecunde
};
Pentru obţinerea timpului curent în forma cerută de structura timeval poate fi folosită funcţia gettimeofday (a se vedea pagina de manual). Pentru diferite conversii între forma normală a specificării unei date şi ore şi cea specifică structurii timeval se poate folosi funcţia ctime sau o alta din aceeaşi familie (a se vedea pagina de manual).
Dostları ilə paylaş: |