UNIX/Linux ve Windows Sistemlerinde Dosyaların ve Dizinlerin Silinmesi Kaan Aslan 23 Temuz 2007 UNIX/Linux sistemlerinde bir dosyanın katı bağ sayısı 1 ise dosyayı tek bir dizin girişi göstermektedir. Bu durumda dosyanın silinmesi hem dizin girişinin hem de düğüm bilgisinin (dolayısıyla da dosya bloklarının) silinmesi anlamına gelir. Bu gerçek bir silme işlemidir. Fakat silinecek dosyanın katı bağ sayısı 1'den fazlaysa silme işlemi yalnızca dizin girişinin silinmesine yol açar. Dosyayı kullanan başka girişler olduğu için dosya gerçek anlamda silinmez. Dosya silme işleminde unlink isimli POSIX fonksiyonu kullanılmaktadır: #include <unistd.h> int unlink(const char *path); Fonksiyonun parametresi silinecek dosyaya ilişkin yol ifadesidir. Fonksiyon başarı durumunda sıfır değerine başarısızlık durumunda -1 değerine geri döner. Başarısızlık durumunda errno değişkeninin aldığı önemli değerler şunlardır: EACCESS : Yol bileşenlerine ilişkin dizinlerden en az birine ‘x’ hakkı yoktur ya da dosyanın bulunduğu dizine yazma hakkı yoktur. ENOENT : Yol bileşenlerinden biri yoktur ya da yol ifadesi boş bir stringten oluşmaktadır. ENOTDIR : Yol bileşenlerinden biri dizin olması gerekirken dizin değildir. … : … unlink fonksiyonu dizin girişini siler ve dosyanın katı bağ sayısını 1 azaltır. Eğer katı bağ sayacı sıfıra düşmüşse düğüm elemanını ve dosyanın disk bloklarını siler. Dosyanın silinebilmesi için fonksiyonu çağıran prosesin yol ifadesindeki tüm dizinlere ‘x’ hakkının ve dizin girişinin içinde bulunduğu dizine ‘w’ hakkının olması gerekir. Silme işlemi için prosesin dizin girişinin içinde bulunduğu dizine ‘r’ hakkının olması gerekmemektedir. POSIX standartlarına göre unlink fonksiyonu ile dizinlerin silinip silinemeyeceği işletim sistemini yazanların isteğine bırakılmıştır. Eğer işletim sistemi unlink fonksiyonuyla dizinin silinmesine izin veriyorsa fonksiyonu çağıran prosesin etkin 1 Kaan Aslan Makale Arşivi – www.kaanaslan.net kullanıcı id'sinin 0 olması gerekir. Fakat yaygın olarak kullanılan UNIX türevi sistemler dizinlerin bu fonksiyonla silinmesine olanak vermiyorlar. unlink fonksiyonu dosyanın içerisinde bulunduğu dizinde değişiklik yaptığı için dosyanın içinde bulunduğu dizine ilişkin düğümün (i-node elemanının) st_ctime ve st_mtime elemanlarını günceller. Tabi eğer unlink işlemi sonucunda yalnızca dizin girişi silinmişse (katı bağ sayısının düğüm elemanının içinde olduğunu anımsayınız) silinmek istenen dosyanın st_ctime elemanı da güncellenecektir. unlink fonksiyonu dosya açıkken de uygulanabilir. Bu durumda dizin girişi yok edilir ve dosyanın katı bağ sayısı bir azaltılır. Fakat dosyanın katı bağ sayısı sıfıra düşse bile silme işlemi hemen yapılmaz. Gerçek silme işlemi dosya kapatıldığında yapılır. Örneğin: #include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <fcntl.h> <sys/types.h> <sys/stat.h> int main(vodid) { int fd; if ((fd = open("test.dat", O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR)) < 0) { perror("open"); exit(EXIT_FAILURE); } if (unlink("test.dat") < 0) { perror("unlink"); exit(EXIT_FAILURE); } /* Dizin girişinden silindi ancak gerçek anlamda silinmedi */ printf("Press ENTER to continue. . . "); getchar(); /* Dosyanın kapatılmasıyla dosya gerçek anlamda silinecek */ close(fd); return 0; } Yukarıdaki programda dosya yaratılmış ve hemen silinmeye çalışılmıştır. Bu durumda unlink fonksiyonu başarılı olur. Fakat gerçek silme işlemi close fonksiyonu çağrıldığında yapılacaktır. 2 Kaan Aslan Makale Arşivi – www.kaanaslan.net Dosyaların silinmesinde remove isimli fonksiyon da kullanılabilir. remove aynı zamanda bir standart C fonksiyonu olduğu için yalnızca UNIX/Linux sistemlerinde değil tüm C derleyicilerinde bulunmaktadır:[1 #include <stdio.h> int remove(const char *path); POSIX standartlarına göre remove fonksiyonu ile hem dosyalar hem de dizinler silinebilirken unlink fonksiyonuyla yalnızca normal dosyalar silinebilmektedir. Fakat C standartlarında remove fonksiyonunun dizin silmede de kullanılabileceğine ilişkin bir ifadenin olmadığını belirtelim. (Ayrıca yukarıda da belirttiğimiz gibi POSIX fonksiyonları unlink fonksiyonuyla dizinlerin silinip silinmeyeceğini işletim sistemini yazanların isteğine bırakmıştır.) Windows sistemlerinde dosya silmek için DeleteFile API fonksiyonu kullanılmaktadır. Windows 95, 98 ve ME sistemlerinde dosya açıkken silme yapılması salık verilmez. Çünkü DeleteFile bu sistemlerde gerçek anlamda silme yapmaktadır. Bu durumda önce dosyanın kapatılıp silinmesi uygun ve doğru tekniktir. DeleteFile fonksiyonunun Windows 200 ve XP sistemlerindeki davranışı POSIX sistemlerindeki gibidir. Yani dosyanın silinmesi gecikmeli olarak CloseHandle fonksiyonu ile kapatıldığında gerçekleştirilir. Windows CE sistemlerinde dosya açıkken silinmeye çalışılırsa DeleteFile fonksiyonu başarısızlıkla geri döner. Dizinlerin silinmesi için POSIX sistemlerinde rmdir fonksiyonu kullanılmaktadır. Fonksiyonun protoipini inceleyiniz: #include <unistd.h> int rmdir(const char *path); Fonksiyon parametere olarak dizine ilişkin yol ifadesini alır. İşlem başarılıysa sıfır değerine başarısızsa -1 değerine geri döner. Bir dizinin silinmesi için dizinin boş olması gerekir. Dizinin boş olması dizinde "." ve ".." dışında hiçbir dizin girişinin olmaması anlamına geliyor. Ayrıca bir prosesin çalışma dizininin rmdir fonksiyonuyla silinip silinemeyeceği POSIX standartlarında işletim sistemini yazanların isteğine bırakılmıştır. Ancak pek çok sistemin buna izin verdiğini görüyoruz. Kök dizinin silinmeye çalışılması ise normal bir durum değildir. Bu durumun da mümkün olup olamayacağı işletim sistemini yazanların isteğine bırakılmıştır. Sistemlerin neredeyse hepsinde bu yasaklanmış durumda. Son olarak dizininlerin ilk iki elemanı olan “.” ve “..” dizinlerinin silinmeye çalışılmasının da geçerli olmadığını belirtelim.[2] rmdir fonksiyonu da unlink ve remove fonksiyonları gibi katı bağ sayacını dikkate alarak çalışmaktadır. Yani bu fonksiyon da önce dizinin katı bağ sayacını düşürür, eğer dizinin katı bağ sayacı sıfıra düşmüşse dizini gerçek anlamda siler. Dizin boşsa ve bağ sayacı 2 ise dizin "." ve ".." girişleriyle birlikte silinmektedir. Eğer dizin boş olduğu halde bağ sayacı 2'den büyük ise dizinler için katı bağ oluşturmaya izin verilen bir sistem söz konusudur. Bu durumda rmdir fonksiyonu yalnızca dizine ilişkin katı bağ girişini siler ve dizinin bağ sayacını 1 eksiltir. Son olarak dizinin bağ sayacının hiçbir zaman 1 olmayacağını bir kez daha vurgulayalım. Silinebilecek durumdaki bir dizine ilişkin bağ sayacı en az 2 olabilir. 3 Kaan Aslan Makale Arşivi – www.kaanaslan.net Katı bağ sayacı 2 olan bir dizin herhangi bir ya da birden fazla proses tarafından opendir fonksiyonuyla açılmış olabilir. Bu durumda rmdir fonksiyonu “.” ve ”..” dizinlerini hemen siler. Ancak dizinin gerçek anlamda silinmesi son proses dizini kapattığında gerçekleştirilecektir. rmdir fonksiyonu başarısız olduğunda errno değişkenine yerleştirilen önemli değerlerden bazıları şunlardır: EACCESS : Yol bileşenlerine ilişkin dizinlerden en az birine “x” hakkı yoktur ya da dizinin içinde bulunduğu dizine yazma hakkı yoktur. ENOENT : Yol bileşenlerinden biri yoktur ya da yol ifadesi boş bir stringten oluşmaktadır. ENOTDIR : Yol bileşenlerinden biri dizin olması gerekirken dizin değildir. EBUSY : Dizin sistem tarafından ya da proses tarafından kullanıyor durumdadır. Örneğin dizin bir prosesin çalışma dizinidir. EEXIST ENOEMPTY : Silinmeye çalışılan dizin boş değildir. … : … Dizin silindiğinde dizinin üst dizinine ilişkin düğüm elemanının st_ctime ve st_mtime elemanları güncellenmektedir. İşletim sistemlerinin çoğu tek hamlede dizin ağacının silinmesine olanak sağlayacak bir sistem fonksiyonu barındırmıyor. Bunun nedenlerinden biri dizin ağacının silinmesinin yüksek seviyeli bir işlem olarak görülmesidir. Çünkü işletim sisteminin sunduğu temel sistem fonksiyonları kullanılarak dizin ağacının silinmesi kullanıcı modunda yapılabilir. Ayrıca dizin ağacının tek hamlede tümden silinmesi sistem tasarımcıları tarafından tehlikeli bir işlem olarak da ele alınmaktadır. Çok sayıda dosyanın bir hamlede silinmesi geri dönülemez önemli kayıpların oluşmasına yol açabilmektedir. Ağaç silinirken bir noktada pürüz çıkarsa bir kısmı silinmiş olan bir ağacın durumu ne olacaktır? Dizin ağacını silmek için aşağıdaki gibi bir özyinelemeli algoritma kullanılabilir. Bu özyinelemeli algoritma, dizinin içerisine girilince dizindeki dosyaların hepsini silen, dizinden çıkıldığında ise dizini silen bir yapıdadır 4 Kaan Aslan Makale Arşivi – www.kaanaslan.net /* deltree.c */ #include #include #include #include #include #include #include #include <stdio.h> <stdlib.h> <fcntl.h> <unistd.h> <sys/types.h> <sys/stat.h> <dirent.h> <errno.h> #define MAX_PATH_LEN 2048 void err_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } void delwalk(const char *dirpath) { DIR *dir; struct dirent *ent; struct stat finfo; int fdcwd; static int level; if ((dir = opendir(dirpath)) == NULL) { perror("opendir"); return; } if ((fdcwd = open(".", O_RDONLY)) < 0) err_sys("getcwd"); if (chdir(dirpath) < 0) { perror("chdir"); goto NOT_SEARCHABLE; } while (errno = 0, (ent = readdir(dir)) != NULL) { if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) continue; if (lstat(ent->d_name, &finfo) < 0) err_sys("lstat"); if (S_ISDIR(finfo.st_mode)) delwalk(ent->d_name); else if (remove(ent->d_name) < 0) err_sys("remove"); } if (errno) err_sys("readdir"); 5 Kaan Aslan Makale Arşivi – www.kaanaslan.net if (fchdir(fdcwd) < 0) err_sys("fchdir"); if (rmdir(dirpath) < 0) err_sys("rmdir"); NOT_SEARCHABLE: close(fdcwd); closedir(dir); } int main(int argc, char *argv[]) { if (argc != 2) err_usr("Wrong number of arguments\n"); delwalk(argv[1]); return 0; } Derleme işlemini şöyle yapabilirsiniz: gcc –o deltree deltree.c Windows sistemlerinde dizin ağacını tek hamlede silen bir çekirdek API fonksiyonu yoktur. Ancak bu işlem SHFileOperation isimli bir kabuk (shell) fonksiyonuyla yapılabilir. Bildiğiniz gibi kabuk fonksiyonları daha yüksek seviyelidir ve kendi içerisinde çekirdek fonksiyonlarını çağırarak işlevlerini gerçekleştirirler. SHFileOperation fonksiyonunu kullanarak bir dizini aşağıdaki gibi bir kodla silebilirsiniz /* deltree.c */ #include <stdio.h> #include <windows.h> #include <shellapi.h> int main(int argc, char *argv[]) { SHFILEOPSTRUCT shfo; char path[MAX_PATH]; int result; if (argc != 2) { fprintf(stderr, "Wrong number of arguments!..\n"); exit(EXIT_FAILURE); } strcpy(path, argv[1]); path[strlen(path) + 1] = '\0'; 6 Kaan Aslan Makale Arşivi – www.kaanaslan.net ZeroMemory(&shfo, sizeof(SHFILEOPSTRUCT)); shfo.hwnd = NULL; shfo.wFunc = FO_DELETE; shfo.lpszProgressTitle = "Test"; shfo.fFlags = FOF_NOERRORUI; shfo.pFrom = path; if ((result = SHFileOperation(&shfo)) != 0) { fprintf(stderr, "Cannot delete folder or file, " "ErrCode = %X\n", result); exit(EXIT_FAILURE); } printf("Success...\n"); return 0; } Derleme işlemini şöyle yapabilirsiniz: cl deltree.c shell32.lib SHFILEOPSTRUCT yapısının pFrom elemanına atanacak yazının sonunda çift null karakterin bulunması gerekmektedir. SHFileOperation pek çok işlemi yapabilen geniş bir fonksiyondur. Fonksiyonun kullanımına ilişkin ayrıntılı bilgileri MSDN yardım sisteminden elde edebilirsiniz. [3] [1] C'de ve C++'ta standartlara uyum "hosted" ve "free standing" biçiminde iki gruba ayrılmıştır. "Hosted" uyum katı uyumdur. Bu uyuma sahip standart C derleyicilerinde tüm standart C fonksiyonları bulunmak zorundadır. "Free standing" uyumda ise kütüphanenin küçük bir bölümünün bulunma zorunluluğu vardır. [2] POSIX standartları "." ve ".." dizinleri konusunda esnek davranmıştır. Bu iki dizinin üst dizinin ilk iki girişinde bulunması zorunlu değildir. Taşınabilir programların bunu göz önüne alması gerekebilir. Fakat uygulamada hemen hemen tüm sistemlerde bu iki dizin girişi üst dizinin ilk iki elemanında bulunuyorlar. [3] Windows Shell programlama için Dino Esposito'nun "Visual C++ Windows Shell Programming" isimli kitabından faydalanabilirsiniz. 7 Kaan Aslan Makale Arşivi – www.kaanaslan.net