9 Mart 2014 Pazar

Linux Sistem Programlama - Bölüm 2

Dosya erişim kontrolü şöyle yapılır. 
  1. Önce Open fonksiyonunu çağıran prosesin root olup olmadığına bakılır. root ise hiçbir kontrol yapılmaz. Erişim kabul edilir.
  2. Değil ise ikinci erişim istenen processin etkin UserID'sinin dosyanın UserID'si ile ayni olup olmadığına bakılır. Eğer aynı ise erişim haklarından owner kısmı dikkate alınır. Değil ise 3. maddeye geçilir.
  3. Erişmek isteyen prosesin etkin grupid'sinin dosyanın grupid'si ile aynı olup olmadığına bakılır. Aynı ise grup haklar dikkate alınır. Değil ise 4. maddeye geçilir.
  4. Bu durumda dosyanın other bilgileri dikkate alınır.
Belirli bir yıldan sonra bir prosesin tek bir gruba ait olması yetersiz görülmüştür. Çünkü bir kişi birden fazla projede çalışabilmektedir. işte bu durumda  ek grupid kavramı ortaya çıkmıştır. Bugünkü sistemde bir prosesin bir tane gerçek grupid'si ve etkin grupid'si vardır. Fakat birden fazla ek gruba da sahiptir. 
Ek gruplar tamamen yukarıdaki 3. madde sırasında prosesin etkin grupid ile dosyanın grupid'si karşılaştırılırken etkin grupid ile ayni değerde karşılaştırılmaktadır. Kullanıcının ek gruplari /etc/group dosyasında belirtilmektedir.

POSIX Programlamada Kullanılan Önemli Bazı Bilgiler

POSIX fonksiyonları standart C fonksiyonları ile birlikte glibc/libc diye bilinen kütüphanenin içerisindedir ve GCC sistemi bu kütüphaneye bakmaktadır. Fakat fonksiyonların prototipleri çeşitli baslık dosyası içinde olabilir. Bunların ayrıca include edilmedi gerekmektedir.
POSIX Fonksiyonlarının çok büyük bolumu basari durumunda 0 başarısızlık durumunda -1 değerine geri dönerler. Programcı programın başarısızlığını test etmelidir. Tabi başarısızlığın da bir sebebi vardır. iste başarısızlığın nedeni int türden global errno isimli bir değişkene yerleştirilmiştir. Fonksiyon basarisiz olduğunda bu değişkenin içine bakarsak neden basarisiz olduğunu anlayabiliriz. Tüm başarısızlık değerleri errno.h dosyası içinde EXXX bicimde sembolik sabitlerle define edilmiştir. Programı isterse doğrudan bu sembolik sabitleri kullanabilir.

if( open(.........) ){
    if(errno == ENOENT)
    {
         ......
    }
}

POSIX Standartlarında her posix fonksiyonu için başarısızlık durumunda her fonksiyon için errno değişkeninin alabileceği tüm değerler listelenmiştir.
Fonksiyon basarisiz olduğunda uygun mesajın verilmesi zahmetli olabilir. Bunun için perror ve strerror fonksiyonları bulunmaktadır. perror bizden bir yazı alır önce o yazıyı yazdırır. Sonra bir iki nokta üst üste karakteri yazdırır sonra da o anki errno değerine bakarak ona karsı gelen yazıyı yazdırır (stderr dosyasina). stderror fonksiyonu ise bizden bir hata kodunu alarak. Ona karşı gelen yazıyı verir. Bu durumda tipik kontrol su şekilde yapılabilir.

if(open(...) == -1){
    perror("open");
    exit(EXIT_FAILURE);
}

Dosya İşlemleri Yapan Temel POSIX Fonksiyonları

Open Fonksiyonu

#include <fcntl.h>
int open(const char *path, int oflags, ...);

Fonksiyonun birinci parametresi açılacak olan dosyanın yolunu alır. İkinci parametresi açış modunu belirtir. Açış modu fcntl.h içerisinde tanımlanmış olan O_XXXX biçimindeki sabitlerin bit OR işlemi ile birleştirilmesi ile olur. Açış modu şunlardan yalnızca birini içermek zorundadır.
  • O_RDONLY
  • O_WRONLY
  • O_RDWR
O_CREAT, dosya yok ise yarat, var ise bir şey yapma anlamına gelmektedir. O_TRUNCO_RDONLY ya da O_RDWR ile birlikte kullanılmak zorundadır. Bu durumda dosya varsa sıfırlanır ve açılır. O_EXCLO_CREAT ile birlikte kullanılmak zorundadır. Bu birlikte kullanım sadece yeni bir dosya yaratmak ve açmak için kullanılır. Dosya var ise open fonksiyonu başarısız olur. O_APPEND, Tüm yapılan yazma işlemlerinin dosyanın sonuna eklenmesi gerektiği anlamına gelmektedir. Bu durumda dosyanın herhangi bir yerinden okuma yapılabilir. Fakat herhangi bir yerine yazılamaz. Her yazılan sonuna eklenir.
Open fonksiyonunda  ikici parametrede O_CREAT kullanılmışsa bu durumda dosyanın yaratılması gibi bir durum oluşur. Artık programcının da dosyanın erişim haklarını üçüncü parametresi belirtmesi gerekir.
Fonksiyonun 3. parametresinde belirtilecek erişim hakları sys/stat.h dosyasında belirtilen sembolik sabitlerin bit OR işlemi ile birleştirilmesi ile oluşturulur. Bu sembolik sabitlerin isimleri su şekilde oluşturulmuştur.

   

R
USR
S_I
W
GRP

X
OTH

Bu gösterimin kombinasyonları olacak şekilde olur. Örnegin;

S_IRUSR -> User'a read hakki verir
S_IXGRP -> Gruba calistirma haki verir

Örnek bir uygulama:
/*-----------------------------------------------------------------------------------
 open fonksiyonunun kullanýmý
------------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void)
{
 int fd;

 if ((fd = open("a.dat", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 printf("success...\n");

 return 0;
}

Open fonksiyonu basari durumunda dosya betimleyicisi (file descriptor) denilen bir handle değerine geri döner. Bu handle değeri diğer fonksiyonlarda parametre olarak kullanılacaktır. Başarısızlık durumunda fonksiyon -1 değerine geri döner.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcnlt.h>
#include <sys/stat.h>

int main(void)
{
    int fd;
   
    if((fd = open("a.dat", O_RDWR)) == -1){
        fprintf(stderr, "Error ")       
    }
    return 0;
}

Close Fonksiyonu

#include <unistd.h>
int close(int fd);
Açılan her dosya bir biçim de kapatılmalıdır. Çünkü açım sırasında bir takım kaynaklar tahsis edilmiş olur. Kapatma sırasında bu kaynaklar geri bırakılır. Ancak dosyayı biz hiç kapatmasak bile process kapandığında bile dosya kesinlikle kapatılmalıdır. Fakat artık kullanılmayacak olan bir dosyanın kapatılması iyi bir tekniktir.

posix fonksiyonlarının bir çoğu unistd.h dosyası içerisinde tanımlanmıştır.

Fonksiyon parametre olarak open fonksiyonu ile dosyanın betimleyicisini alır. Başarılı ise 0 değil ise -1 ile geri dönüş yapar.

/*-------------------------------------------------------------------------------------
 close fonksiyonu
-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void)
{
 int fd;

 if ((fd = open("a.dat", O_RDWR)) == -1) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 close(fd);

 return 0;
}

Dosya Göstericisi Kavramı

Dosyadaki her bir byte'in bir ofset numarası vardır. Dosya göstericisi imleç görevindedir. Bir ofset belirtir. Okuma ve yazma işleminin dosyanın hangi noktasından itibaren yapılacağını belirtir.  Dosya açıldığında dosya göstericisi 0. ofsettedir. Okunan ya da yazılan miktar kadar otomatik ilerletilir. Dosya göstericisinin dosyanın son byte’indan sonraki byte’ı gösterme durumuna EOF durumu denir. Bu durumda okuma yapılamaz ancak yazma yapılabilir. Bu dosyaya ekleme anlamına gelir.

Read ve Write Fonksiyonları

Read fonksiyonunun prototipi şu şekildedir

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

Fonksiyonun birinci parametresi dosya betimleyicisini, ikinci parametresi okunan bilgilerin yerleştirileceği adresi, üçüncü parametresi ise okunacak byte sayısını belirtir. Read fonksiyonu ile olandan daha fazla okunmak istenebilir bu durumda fonksiyon okuyabildiği kadar byte'i okur okuyabildiği byte sayısına geri döner. read IO hatası dolaysı ile hiç okuma yapamaz ise -1 değerine EOF'dan dolayı hiçbir şey okuyamaz ise 0 değerine geri döner.

/*-------------------------------------------------------------------------------------
 read fonksiyonunun kullanim ornegi
--------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void)
{
 int fd;
 char buf[30 + 1];
 ssize_t n;

 if ((fd = open("sample.c", O_RDONLY)) == -1) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 if ((n = read(fd, buf, 30)) == -1) {
  perror("read");
  exit(EXIT_FAILURE);
 }

 buf[n] = '\0';
 puts(buf);

 close(fd);

 return 0;
}

Write fonksiyonunun prototipi şu şekildedir.

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

Fonksiyonun birinci parametresi dosya betimleyicisini, ikinci parametresi dosyaya yazılacak bilgilerin bulunduğu bellek adresini, üçüncü parametresi ise yazılacak byte miktarını belirtir. Fonksiyon başarılı ise yazılabilen byte sayısına basarisiz ise -1 değerine geri döner.

/*-------------------------------------------------------------------------------------
 Dosya kopyalama
-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define BUFSIZE  8192

int main(int argc, char *argv[])
{
 int fds, fdd;
 char buf[BUFSIZE];
 ssize_t n;

 if (argc < 3) {
  fprintf(stderr, "wrong number of argument!..\n");
  fprintf(stderr, "usage: mycp <source fie> <dest file>\n");
  exit(EXIT_FAILURE);
 }

 if ((fds = open(argv[1], O_RDONLY)) < 0) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 if ((fdd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC,
                 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
  perror("open");
  exit(EXIT_FAILURE);
 }

 while ((n = read(fds, buf, BUFSIZE)) > 0)
  if (write(fdd, buf, n) < 0) {
   perror("write");
   exit(EXIT_FAILURE);
  }

 if (n < 0) {
  perror("read");
  exit(EXIT_FAILURE);
 }

 printf("1 file copied...\n");

 close(fds);
 close(fdd);

 return 0;
}
Bu örnekte okuma yaptigimiz konksiyonun yetkilerini alip hedef dosyaya eklemek icin stat fonksiyonunu kullanmak gerekiyor. 

lseek fonksiyonu

lseek fonksiyonunun prototipi su sekildedir

#include <sys/types.h> // bu dosya include edilmek zorunda deiliz. Edilirse guzel olur.
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

Fonksiyonun birinci parametresi dosya betimleyicisi ikinci parametresi konumlandırma ofseti, üçüncü parametresi konumlandırma orijinini belirtir.  Üçüncü parametre 3 değerden birini alabilir. 
SEEK_SET (0) konumlandırmanın baştan itibaren yapılacağını belirtir. Bu durumda ofset parametresi >= 0 olmak zorundadır. SEEK_CUR (1) konumlandırmanın dosyanın göstericisinin konumu nerede ise ona göre yapılacağı anlamına gelir. Bu durumda ikinci parametre  pozitif, negatif ya da 0 olabilir. Nihayet SEEK_END (2) konumlandırmanın EOF pozisyonuna göre yapılacağı anlamına gelmektedir. Bu durumda ikinci parametre  =< 0 olmak zorundadır. Fonksiyon basari durumunda dosya göstericisinin yeni konumuna başarısızlık durumunda -1 değerine geri döner.

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

int main()
{
        int file=0;
        if((file=open("testfile.txt",O_RDONLY)) < -1)
                return 1;

        char buffer[19];
        if(read(file,buffer,19) != 19)  return 1;
        printf("%s\n",buffer);

        if(lseek(file,10,SEEK_SET) < 0) return 1;

        if(read(file,buffer,19) != 19)  return 1;
        printf("%s\n",buffer);

        return 0;
}

Bu örnekde exitsys fonksiyonunu kendi yazdığımız uygulamalarda direk kullanabiliriz.
lseek komutu ile örneğin 5 bayt uzunluğunda bir dosya 15. bayttan itibaren veri yazabiliyoruz. Buna dosya deliği denilmektedir. (File hole) Bu konu ile ilgili http://www.kaanaslan.com/resource/article/display_article.php?id=64 adresinden ayrıntılı bilgi elde edilebilir.

Dosya baytlardan oluşmaktadır. Fakat halk arasında içinde yalnızca yazı olan dosyalara text dosyalar, içinde yazının dışında başka şeyler de olabilen dosyalara binary dosyalar denilmektedir. Ayrıca test dosyalarda her bir karakterin hangi tablo ile kodlandığını bilmek gerekir. Buna karakter kodlaması denilmektedir.

Unix / Linux   |    Windows 
a\nb                |    a\r\nb
LF                   |    CR/LF

Windows da test editörler aşağı satıra geçiş için CR ve LF karakterlerini (0D ve 0A) kullanır. Fakat Unix/Linux’ta yalnızca LF karakteri kullanılmaktadır. Bu da uyumsuzluğa yol açmaktadır. Bu karışıklığı çözmek için ve özellikle text okuma ve yazmaları kolaylaştırmak için C ve bazı başka dillerde dosyayı açarken text mode ve binari mode seklinde yapay seçenekler oluşturulmuştur. Bir dosyayı text modda açtığımız zaman şunlar olur;
  1. Dosyadan 1 bayt okurken eğer dosya göstericisi Windows da CR karakterinin üzerinde ise CR ve LF karakterlerinin ikisini de okur fakat bize fonksiyon LF karakteri okumuş gibi geri döner.
  2. Biz dosyaya text modda açılmışsa \n karakterini yazdırdığımızda aslında Windows da aslında CR/LF biçiminde 2 byte yazılmaktadır.
Biz dosyayı binary modda açtığımız zaman hangi karakteri yazdırırsak yalnızca onu yazar.
Görüldüğü gibi Unix/Linux sistemlerinde dosyanın text mode veya binary modda açılması arastanda hiçbir fark yoktur.

Text Mode / Binary Mode Örnekleri

/*-------------------------------------------------------------------------------------
                C'de text mode / binary mode'
-------------------------------------------------------------------------------------*/

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
                FILE *f;

                if ((f = fopen("a.txt", "w")) == NULL) {
                               fprintf(stderr, "cannot open file!\n");
                               exit(EXIT_FAILURE);
                }

                fprintf(f, "a\nb");                           /* Windows'ta 4 byte UNIX/Linux'ta 3 byte */

                fclose(f);

                return 0;
}

/*-----------------------------------------------------------------------------------
                C'de text mode / binary mode'
------------------------------------------------------------------------------------*/

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
                FILE *f;

                if ((f = fopen("a.txt", "wb")) == NULL) {
                               fprintf(stderr, "cannot open file!\n");
                               exit(EXIT_FAILURE);
                }

                fprintf(f, "a\nb");                           /* Windows'ta 3 byte UNIX/Linux'ta 3 byte */

                fclose(f);

                return 0;
}

Dosya Sistemine İlişkin Yardımcı POSIX Fonksiyonları

chmod ve fchmod fonksiyonları

#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

Yaratılmış bir dosyanın erişim hakları open fonksiyonunda ilk kez belirlenir. Ancak bu fonksiyonlarla daha sonra değiştirilebilir. chmod fonksiyonu yol ifadesinden hareketle fchmod fonksiyonu dosya betimleyicisinden hareketle modu değiştirir.
Bir prosesin bir dosyanın erişim haklarını değiştirebilmesi için ya root olması ya da prosesin etkin userid'sinin dosyanın sahibi ile ayni olması gerekir.

root hakkı, ya hep ya hiç biçiminde bir haktır. Yani root proses her şeyi yapabilir. Diğerleri sadece kendilerine yönelik şeyleri yapabilir. Bu nedenle bazı sistemlerde proses root olmadığı halde bazı işlemleri yapabilecek durum oluşturulmuştur. Prosesin bu yetkilendirme bilgisi ilgili sistemlerde proses kontrol bloğunda saklanır. Fakat her sistem bunu desteklememektedir.
> Ayrıca komut satırında chmod isimli bir komutta vardır.

/*--------------------------------------------------------------------------------------
 chmod örneği
-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>

void exitsys(const char *msg);

int main(int argc, char *argv[])
{
 int arights;
 int i, k;
 int aflags[] = {S_IXOTH, S_IWOTH, S_IROTH, S_IXGRP, S_IWGRP,
                     S_IRGRP, S_IXUSR, S_IWUSR, S_IRUSR};
 int mflag;

 if (argc < 3) {
  fprintf(stderr, "wrong number of arguments!..\n");
  fprintf(stderr, "usage: mychmod <access rights> <file>\n");
  exit(EXIT_FAILURE);
 }

 sscanf(argv[1], "%o", &arights);

 for (i = 2; i < argc; ++i) {
  mflag = 0;
  for (k = 0; k < 9; ++k)
   if (arights & aflags[k])
    mflag |= aflags[k];
  if (chmod(argv[i], mflag) < 0)
   exitsys("chmod");
 }

 return 0;
}

void exitsys(const char *msg)
{
 perror(msg);
 exit(EXIT_FAILURE);
}

Hiç yorum yok:

Yorum Gönder