# 文件系統,第 4 部分:使用目錄
> 原文:<https://github.com/angrave/SystemProgramming/wiki/File-System%2C-Part-4%3A-Working-with-directories>
## 如何確定文件(inode)是常規文件還是目錄?
使用`S_ISDIR`宏檢查 stat 結構中的模式位:
```c
struct stat s;
stat("/tmp", &s);
if (S_ISDIR(s.st_mode)) { ...
```
注意,稍后我們將編寫健壯的代碼來驗證 stat 調用是否成功(返回 0);如果`stat`調用失敗,我們應該假設 stat 結構內容是任意的。
## 我如何遞歸到子目錄?
首先是一個難題 - 您可以在以下代碼中找到多少個錯誤?
```c
void dirlist(char *path) {
struct dirent *dp;
DIR *dirp = opendir(path);
while ((dp = readdir(dirp)) != NULL) {
char newpath[strlen(path) + strlen(dp->d_name) + 1];
sprintf(newpath,"%s/%s", newpath, dp->d_name);
printf("%s\n", dp->d_name);
dirlist(newpath);
}
}
int main(int argc, char **argv) { dirlist(argv[1]); return 0; }
```
你找到了所有 5 個錯誤嗎?
```c
// Check opendir result (perhaps user gave us a path that can not be opened as a directory
if (!dirp) { perror("Could not open directory"); return; }
// +2 as we need space for the / and the terminating 0
char newpath[strlen(path) + strlen(dp->d_name) + 2];
// Correct parameter
sprintf(newpath,"%s/%s", path, dp->d_name);
// Perform stat test (and verify) before recursing
if (0 == stat(newpath,&s) && S_ISDIR(s.st_mode)) dirlist(newpath)
// Resource leak: the directory file handle is not closed after the while loop
closedir(dirp);
```
## 什么是符號鏈接?他們是如何工作的?我怎么做一個?
```c
symlink(const char *target, const char *symlink);
```
要在 shell 中創建符號鏈接,請使用`ln -s`
要將鏈接的內容作為文件使用`readlink`
```
$ readlink myfile.txt
../../dir1/notes.txt
```
要讀取符號鏈接的元(stat)信息,請使用`lstat`而不是`stat`
```c
struct stat s1, s2;
stat("myfile.txt", &s1); // stat info about the notes.txt file
lstat("myfile.txt", &s2); // stat info about the symbolic link
```
## 符號鏈接的優點
* 可以引用尚不存在的文件
* 與硬鏈接不同,可以引用目錄以及常規文件
* 可以引用當前文件系統之外的文件(和目錄)
主要缺點:比常規文件和目錄慢。讀取鏈接內容時,必須將它們解釋為目標文件的新路徑。
## 什么是`/dev/null`以及何時使用?
文件`/dev/null`是存儲您永遠不需要閱讀的位的好地方!發送到`/dev/null/`的字節永遠不會被存儲 - 它們被簡單地丟棄。 `/dev/null`的一個常見用途是丟棄標準輸出。例如,
```
$ ls . >/dev/null
```
## 為什么我要設置目錄的粘性位?
當目錄的粘滯位僅設置文件的所有者時,目錄的所有者和 root 用戶可以重命名(或刪除)該文件。當多個用戶具有對公共目錄的寫訪問權時,這非常有用。
粘滯位的常見用途是共享和可寫`/tmp`目錄。
## 為什么 shell 和腳本程序以`#!/usr/bin/env python`開頭?
答:為了便攜性!雖然可以將完全限定的路徑寫入 python 或 perl 解釋器,但這種方法不可移植,因為您可能已將 python 安裝在不同的目錄中。
為了克服這種情況,`env`實用程序用于在用戶路徑上查找和執行程序。 env 實用程序本身歷來存儲在`/usr/bin`中 - 必須使用絕對路徑指定。
## 如何制作“隱藏”文件,即“ls”未列出?我如何列出它們?
簡單!創建以“。”開頭的文件(或目錄)。 - 然后(默認情況下)標準工具和實用程序不顯示它們。
這通常用于隱藏用戶主目錄中的配置文件。例如,`ssh`將其首選項存儲在名為`.sshd`的目錄中
要列出包括常用隱藏條目在內的所有文件,請使用帶有`-a`選項的`ls`
```
$ ls -a
. a.c myls
.. a.out other.txt
.secret
```
## 如果我關閉目錄上的執行位會發生什么?
目錄的執行位用于控制目錄內容是否可列表。
```
$ chmod ugo-x dir1
$ ls -l
drw-r--r-- 3 angrave staff 102 Nov 10 11:22 dir1
```
但是,在嘗試列出目錄的內容時,
```
$ ls dir1
ls: dir1: Permission denied
```
換句話說,目錄本身是可發現的,但無法列出其內容。
## 什么是文件通配(以及誰做)?
在執行程序之前,shell 會將參數擴展為匹配的文件名。例如,如果當前目錄有三個以 my(my1.txt mytext.txt myomy)開頭的文件名,那么
```
$ echo my*
```
擴展到
```
$ echo my1.txt mytext.txt myomy
```
這稱為文件通配,在執行命令之前處理。即命令的參數與手動鍵入每個匹配的文件名相同。
## 創建安全目錄
假設您在/ tmp 中創建了自己的目錄,然后設置權限,以便只有您可以使用該目錄(見下文)。這樣安全嗎?
```
$ mkdir /tmp/mystuff
$ chmod 700 /tmp/mystuff
```
創建目錄和更改權限之間有一個機會窗口。這會導致多個漏洞基于競爭條件(攻擊者在刪除權限之前以某種方式修改目錄)。一些例子包括:
另一個用戶將`mystuff`替換為第二個用戶擁有的現有文件或目錄的硬鏈接,然后他們就能夠讀取和控制`mystuff`目錄的內容。哦不 - 我們的秘密不再是秘密!
但是在此特定示例中,`/tmp`目錄設置了粘滯位,因此其他用戶可能不會刪除`mystuff`目錄,并且上述簡單攻擊情形是不可能的。這并不意味著創建目錄然后將目錄設為私有是安全的!更好的版本是從一開始就以原子方式創建具有正確權限的目錄 -
```
$ mkdir -m 700 /tmp/mystuff
```
## 如何自動創建父目錄?
```
$ mkdir -p d1/d2/d3
```
如果它們不存在,將自動創建 d1 和 d2。
## 我的默認 umask 022;這是什么意思?
umask _ 從 777 中減去 _(減少)權限位,并在 open,mkdir 等創建新文件和新目錄時使用。因此`022`(八進制)表示組和其他權限不包括可寫位。每個進程(包括 shell)都有一個當前的 umask 值。在分叉時,子進程繼承父進程的 umask 值。
例如,通過在 shell 中將 umask 設置為 077,確保將來的文件和目錄創建只能由當前用戶訪問,
```
$ umask 077
$ mkdir secretdir
```
作為代碼示例,假設使用`open()`和模式位`666`(用戶,組和其他的寫入和讀取位)創建新文件:
```c
open("myfile", O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
```
如果 umask 是八進制 022,那么創建的文件的權限將是 0666&amp; ?022 即。
```c
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
```
## 如何將字節從一個文件復制到另一個文件?
使用通用`dd`命令。例如,以下命令將文件`/dev/urandom`中的 1 MB 數據復制到文件`/dev/null`。數據被復制為 1024 塊塊大小為 1024 字節。
```
$ dd if=/dev/urandom of=/dev/null bs=1k count=1024
```
上例中的輸入和輸出文件都是虛擬的 - 它們不存在于磁盤上。這意味著傳輸速度不受硬件電源的影響。相反,它們是`dev`文件系統的一部分,后者是內核提供的虛擬文件系統。虛擬文件`/dev/urandom`提供無限的隨機字節流,而 virtal 文件`/dev/null`忽略寫入它的所有字節。 `/dev/null`的一個常見用法是丟棄命令的輸出,
```
$ myverboseexecutable > /dev/null
```
另一個常用的/ dev 虛擬文件是`/dev/zero`,它提供無限的零字節流。例如,我們可以將內核中讀取流零字節的操作系統性能基準測試到進程內存,并將字節寫回內核而不需要任何磁盤 I / O.請注意,吞吐量(~20GB / s)很大程度上取決于 blocksize。對于小塊大小,額外`read`和`write`系統調用的開銷將占主導地位。
```
$ dd if=/dev/zero of=/dev/null bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 0.0539153 s, 19.9 GB/s
```
## 觸摸文件時會發生什么?
如果文件不存在,`touch`可執行文件會創建文件,并將文件的上次修改時間更新為當前時間。例如,我們可以使用當前時間創建一個新的私有文件:
```
$ umask 077 # all future new files will maskout all r,w,x bits for group and other access
$ touch file123 # create a file if it does not exist, and update its modified time
$ stat file123
File: `file123'
Size: 0 Blocks: 0 IO Block: 65536 regular empty file
Device: 21h/33d Inode: 226148 Links: 1
Access: (0600/-rw-------) Uid: (395606/ angrave) Gid: (61019/ ews)
Access: 2014-11-12 13:42:06.000000000 -0600
Modify: 2014-11-12 13:42:06.001787000 -0600
Change: 2014-11-12 13:42:06.001787000 -0600
```
觸摸的一個示例用法是強制 make 在修改 makefile 中的編譯器選項后重新編譯未更改的文件。記住 make 是'懶惰的' - 它會將源文件的修改時間與相應的輸出文件進行比較,以查看是否需要重新編譯該文件
```
$ touch myprogram.c # force my source file to be recompiled
$ make
```
[轉到文件系統:第 5 部分](https://github.com/angrave/SystemProgramming/wiki/File-System,-Part-5:-Virtual-file-systems)
- UIUC CS241 系統編程中文講義
- 0. 簡介
- #Informal 詞匯表
- #Piazza:何時以及如何尋求幫助
- 編程技巧,第 1 部分
- 系統編程短篇小說和歌曲
- 1.學習 C
- C 編程,第 1 部分:簡介
- C 編程,第 2 部分:文本輸入和輸出
- C 編程,第 3 部分:常見問題
- C 編程,第 4 部分:字符串和結構
- C 編程,第 5 部分:調試
- C 編程,復習題
- 2.進程
- 進程,第 1 部分:簡介
- 分叉,第 1 部分:簡介
- 分叉,第 2 部分:Fork,Exec,等等
- 進程控制,第 1 部分:使用信號等待宏
- 進程復習題
- 3.內存和分配器
- 內存,第 1 部分:堆內存簡介
- 內存,第 2 部分:實現內存分配器
- 內存,第 3 部分:粉碎堆棧示例
- 內存復習題
- 4.介紹 Pthreads
- Pthreads,第 1 部分:簡介
- Pthreads,第 2 部分:實踐中的用法
- Pthreads,第 3 部分:并行問題(獎金)
- Pthread 復習題
- 5.同步
- 同步,第 1 部分:互斥鎖
- 同步,第 2 部分:計算信號量
- 同步,第 3 部分:使用互斥鎖和信號量
- 同步,第 4 部分:臨界區問題
- 同步,第 5 部分:條件變量
- 同步,第 6 部分:實現障礙
- 同步,第 7 部分:讀者編寫器問題
- 同步,第 8 部分:環形緩沖區示例
- 同步復習題
- 6.死鎖
- 死鎖,第 1 部分:資源分配圖
- 死鎖,第 2 部分:死鎖條件
- 死鎖,第 3 部分:餐飲哲學家
- 死鎖復習題
- 7.進程間通信&amp;調度
- 虛擬內存,第 1 部分:虛擬內存簡介
- 管道,第 1 部分:管道介紹
- 管道,第 2 部分:管道編程秘密
- 文件,第 1 部分:使用文件
- 調度,第 1 部分:調度過程
- 調度,第 2 部分:調度過程:算法
- IPC 復習題
- 8.網絡
- POSIX,第 1 部分:錯誤處理
- 網絡,第 1 部分:簡介
- 網絡,第 2 部分:使用 getaddrinfo
- 網絡,第 3 部分:構建一個簡單的 TCP 客戶端
- 網絡,第 4 部分:構建一個簡單的 TCP 服務器
- 網絡,第 5 部分:關閉端口,重用端口和其他技巧
- 網絡,第 6 部分:創建 UDP 服務器
- 網絡,第 7 部分:非阻塞 I O,select()和 epoll
- RPC,第 1 部分:遠程過程調用簡介
- 網絡復習題
- 9.文件系統
- 文件系統,第 1 部分:簡介
- 文件系統,第 2 部分:文件是 inode(其他一切只是數據...)
- 文件系統,第 3 部分:權限
- 文件系統,第 4 部分:使用目錄
- 文件系統,第 5 部分:虛擬文件系統
- 文件系統,第 6 部分:內存映射文件和共享內存
- 文件系統,第 7 部分:可擴展且可靠的文件系統
- 文件系統,第 8 部分:從 Android 設備中刪除預裝的惡意軟件
- 文件系統,第 9 部分:磁盤塊示例
- 文件系統復習題
- 10.信號
- 過程控制,第 1 部分:使用信號等待宏
- 信號,第 2 部分:待處理的信號和信號掩碼
- 信號,第 3 部分:提高信號
- 信號,第 4 部分:信號
- 信號復習題
- 考試練習題
- 考試主題
- C 編程:復習題
- 多線程編程:復習題
- 同步概念:復習題
- 記憶:復習題
- 管道:復習題
- 文件系統:復習題
- 網絡:復習題
- 信號:復習題
- 系統編程笑話