?? namei.c
字號:
/* passed
* linux/fs/namei.c
*
* (C) 1991 Linus Torvalds
*/
#include <set_seg.h>
/*
* tytso 作了一些糾正。
*/
#include <linux/sched.h>// 調度程序頭文件,定義了任務結構task_struct、初始任務0 的數據,
// 還有一些有關描述符參數設置和獲取的嵌入式匯編函數宏語句。
#include <linux/kernel.h>// 內核頭文件。含有一些內核常用函數的原形定義。
#include <asm/segment.h>// 段操作頭文件。定義了有關段寄存器操作的嵌入式匯編函數。
#include <string.h>// 字符串頭文件。主要定義了一些有關字符串操作的嵌入函數。
#include <fcntl.h>// 文件控制頭文件。用于文件及其描述符的操作控制常數符號的定義。
#include <errno.h>// 錯誤號頭文件。包含系統中各種出錯號。(Linus 從minix 中引進的)。
#include <const.h>// 常數符號頭文件。目前僅定義了i 節點中i_mode 字段的各標志位。
#include <sys/stat.h>// 文件狀態頭文件。含有文件或文件系統狀態結構stat{}和常量。
// 訪問模式宏。x 是include/fcntl.h 第7 行開始定義的文件訪問標志。
// 根據x 值索引對應數值(數值表示rwx 權限: r, w, rw, wxrwxrwx)(數值是8 進制)。
#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
/*
* 如果想讓文件名長度>NAME_LEN 的字符被截掉,就將下面定義注釋掉。
*/
/* #define NO_TRUNCATE */
#define MAY_EXEC 1 // 可執行(可進入)。
#define MAY_WRITE 2 // 可寫。
#define MAY_READ 4 // 可讀。
/*
* permission()
*
* 該函數用于檢測一個文件的讀/寫/執行權限。我不知道是否只需檢查euid,還是
* 需要檢查euid 和uid 兩者,不過這很容易修改。
*/
//// 檢測文件訪問許可權限。
// 參數:inode - 文件對應的i 節點;mask - 訪問屬性屏蔽碼。
// 返回:訪問許可返回1,否則返回0。
static int permission(struct m_inode * inode,int mask)
{
int mode = inode->i_mode;// 文件訪問屬性
/* 特殊情況:即使是超級用戶(root)也不能讀/寫一個已被刪除的文件 */
// 如果i 節點有對應的設備,但該i 節點的連接數等于0,則返回。
if (inode->i_dev && !inode->i_nlinks)
return 0;
// 否則,如果進程的有效用戶id(euid)與i 節點的用戶id 相同,則取文件宿主的用戶訪問權限。
else if (current->euid==inode->i_uid)
mode >>= 6;
// 否則,如果進程的有效組id(egid)與i 節點的組id 相同,則取組用戶的訪問權限。
else if (current->egid==inode->i_gid)
mode >>= 3;
// 如果上面所取的的訪問權限與屏蔽碼相同,或者是超級用戶,則返回1,否則返回0。
if (((mode & mask & 0007) == mask) || suser()) // suser()在linux/kernel.h。
return 1;
return 0;
}
/*
* ok,我們不能使用strncmp 字符串比較函數,因為名稱不在我們的數據空間(不在內核空間)。
* 因而我們只能使用match()。問題不大。match()同樣也處理一些完整的測試。
*
* 注意!與strncmp 不同的是match()成功時返回1,失敗時返回0。
*/
//// 指定長度字符串比較函數。
// 參數:len - 比較的字符串長度;name - 文件名指針;de - 目錄項結構。
// 返回:相同返回1,不同返回0。
static int match(int len,const char * name,struct dir_entry * de)
{
register int same; //__asm__("ax")
char *de_name;
// 如果目錄項指針空,或者目錄項i 節點等于0,或者要比較的字符串長度超過文件名長度,則返回0。
if (!de || !de->inode || len > NAME_LEN)
return 0;
// 如果要比較的長度len 小于NAME_LEN,但是目錄項中文件名長度超過len,則返回0。
if (len < NAME_LEN && de->name[len])
return 0;
// 下面嵌入匯編語句,在用戶數據空間(fs)執行字符串的比較操作。
// %0 - eax(比較結果same);%1 - eax(eax 初值0);%2 - esi(名字指針);%3 - edi(目錄項名指針);
// %4 - ecx(比較的字節長度值len)。
/* __asm__("cld\n\t" // 清方向位。
"fs ; repe ; cmpsb\n\t" // 用戶空間執行循環比較[esi++]和[edi++]操作,
"setz %%al" // 若比較結果一樣(z=0)則設置al=1(same=eax)。
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
:"cx","di","si");*/
de_name = de->name;
_asm{
pushf
xor eax,eax
mov esi,name
mov edi,de_name
mov ecx,len
cld // 清方向位。
// 用戶空間執行循環比較[esi++]和[edi++]操作,
repe cmps byte ptr fs:[edi],[esi]
//上語句應該是錯誤的,但我不知道怎么改。還好系統可以運行:)
setz al // 若比較結果一樣(z=0)則設置al=1(same=eax)。
mov same,eax
popf
}
return same; // 返回比較結果。
}
/*
* find_entry()
*
* 在指定的目錄中尋找一個與名字匹配的目錄項。返回一個含有找到目錄項的高速
* 緩沖區以及目錄項本身(作為一個參數- res_dir)。并不讀目錄項的i 節點- 如
* 果需要的話需自己操作。
*
* '..'目錄項,操作期間也會對幾種特殊情況分別處理- 比如橫越一個偽根目錄以
* 及安裝點。
*/
//// 查找指定目錄和文件名的目錄項。
// 參數:dir - 指定目錄i 節點的指針;name - 文件名;namelen - 文件名長度;
// 返回:高速緩沖區指針;res_dir - 返回的目錄項結構指針;
static struct buffer_head * find_entry(struct m_inode ** dir,
const char * name, int namelen, struct dir_entry ** res_dir)
{
int entries;
int block,i;
struct buffer_head * bh;
struct dir_entry * de;
struct super_block * sb;
// 如果定義了NO_TRUNCATE,則若文件名長度超過最大長度NAME_LEN,則返回。
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
//如果沒有定義NO_TRUNCATE,則若文件名長度超過最大長度NAME_LEN,則截短之。
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#endif
// 計算本目錄中目錄項項數entries。置空返回目錄項結構指針。
entries = (*dir)->i_size / (sizeof (struct dir_entry));
*res_dir = NULL;
// 如果文件名長度等于0,則返回NULL,退出。
if (!namelen)
return NULL;
/* 檢查目錄項'..',因為可能需要對其特別處理 */
if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
/* 偽根中的'..'如同一個假'.'(只需改變名字長度) */
// 如果當前進程的根節點指針即是指定的目錄,則將文件名修改為'.',
if ((*dir) == current->root)
namelen=1;
// 否則如果該目錄的i 節點號等于ROOT_INO(1)的話,說明是文件系統根節點。則取文件系統的超級塊。
else if ((*dir)->i_num == ROOT_INO) {
/* 在一個安裝點上的'..'將導致目錄交換到安裝到文件系統的目錄i 節點。
注意!由于設置了mounted 標志,因而我們能夠取出該新目錄 */
sb=get_super((*dir)->i_dev);
// 如果被安裝到的i 節點存在,則先釋放原i 節點,然后對被安裝到的i 節點進行處理。
// 讓*dir 指向該被安裝到的i 節點;該i 節點的引用數加1。
if (sb->s_imount) {
iput(*dir);
(*dir)=sb->s_imount;
(*dir)->i_count++;
}
}
}
// 如果該i 節點所指向的第一個直接磁盤塊號為0,則返回NULL,退出。
if (!(block = (*dir)->i_zone[0]))
return NULL;
// 從節點所在設備讀取指定的目錄項數據塊,如果不成功,則返回NULL,退出。
if (!(bh = bread((*dir)->i_dev,block)))
return NULL;
// 在目錄項數據塊中搜索匹配指定文件名的目錄項,首先讓de 指向數據塊,并在不超過目錄中目錄項數
// 的條件下,循環執行搜索。
i = 0;
de = (struct dir_entry *) bh->b_data;
while (i < entries) {
// 如果當前目錄項數據塊已經搜索完,還沒有找到匹配的目錄項,則釋放當前目錄項數據塊。
if ((char *)de >= BLOCK_SIZE+bh->b_data) {
brelse(bh);
bh = NULL;
// 在讀入下一目錄項數據塊。若這塊為空,則只要還沒有搜索完目錄中的所有目錄項,就跳過該塊,
// 繼續讀下一目錄項數據塊。若該塊不空,就讓de 指向該目錄項數據塊,繼續搜索。
if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
!(bh = bread((*dir)->i_dev,block))) {
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data;
}
// 如果找到匹配的目錄項的話,則返回該目錄項結構指針和該目錄項數據塊指針,退出。
if (match(namelen,name,de)) {
*res_dir = de;
return bh;
}
// 否則繼續在目錄項數據塊中比較下一個目錄項。
de++;
i++;
}
// 若指定目錄中的所有目錄項都搜索完還沒有找到相應的目錄項,則釋放目錄項數據塊,返回NULL。
brelse(bh);
return NULL;
}
/*
* add_entry()
*
* 使用與find_entry()同樣的方法,往指定目錄中添加一文件目錄項。
* 如果失敗則返回NULL。
*
* 注意!!'de'(指定目錄項結構指針)的i 節點部分被設置為0 - 這表示
* 在調用該函數和往目錄項中添加信息之間不能睡眠,因為若睡眠那么其它
* 人(進程)可能會已經使用了該目錄項。
*/
//// 根據指定的目錄和文件名添加目錄項。
// 參數:dir - 指定目錄的i 節點;name - 文件名;namelen - 文件名長度;
// 返回:高速緩沖區指針;res_dir - 返回的目錄項結構指針;
static struct buffer_head * add_entry(struct m_inode * dir,
const char * name, int namelen, struct dir_entry ** res_dir)
{
int block,i;
struct buffer_head * bh;
struct dir_entry * de;
*res_dir = NULL;
// 如果定義了NO_TRUNCATE,則若文件名長度超過最大長度NAME_LEN,則返回。
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
//如果沒有定義NO_TRUNCATE,則若文件名長度超過最大長度NAME_LEN,則截短之。
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#endif
// 如果文件名長度等于0,則返回NULL,退出。
if (!namelen)
return NULL;
// 如果該目錄i 節點所指向的第一個直接磁盤塊號為0,則返回NULL 退出。
if (!(block = dir->i_zone[0]))
return NULL;
// 如果讀取該磁盤塊失敗,則返回NULL 并退出。
if (!(bh = bread(dir->i_dev,block)))
return NULL;
// 在目錄項數據塊中循環查找最后未使用的目錄項。首先讓目錄項結構指針de 指向高速緩沖的數據塊
// 開始處,也即第一個目錄項。
i = 0;
de = (struct dir_entry *) bh->b_data;
while (1) {
// 如果當前判別的目錄項已經超出當前數據塊,則釋放該數據塊,重新申請一塊磁盤塊block。如果
// 申請失敗,則返回NULL,退出。
if ((char *)de >= BLOCK_SIZE+bh->b_data) {
brelse(bh);
bh = NULL;
block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
if (!block)
return NULL;
// 如果讀取磁盤塊返回的指針為空,則跳過該塊繼續。
if (!(bh = bread(dir->i_dev,block))) {
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
// 否則,讓目錄項結構指針de 志向該塊的高速緩沖數據塊開始處。
de = (struct dir_entry *) bh->b_data;
}
// 如果當前所操作的目錄項序號i*目錄結構大小已經超過了該目錄所指出的大小i_size,則說明該第i
// 個目錄項還未使用,我們可以使用它。于是對該目錄項進行設置(置該目錄項的i 節點指針為空)。并
// 更新該目錄的長度值(加上一個目錄項的長度,設置目錄的i 節點已修改標志,再更新該目錄的改變時
// 間為當前時間。
if (i*sizeof(struct dir_entry) >= dir->i_size) {
de->inode=0;
dir->i_size = (i+1)*sizeof(struct dir_entry);
dir->i_dirt = 1;
dir->i_ctime = CURRENT_TIME;
}
// 若該目錄項的i 節點為空,則表示找到一個還未使用的目錄項。于是更新目錄的修改時間為當前時間。
// 并從用戶數據區復制文件名到該目錄項的文件名字段,置相應的高速緩沖塊已修改標志。返回該目錄
// 項的指針以及該高速緩沖區的指針,退出。
if (!de->inode) {
dir->i_mtime = CURRENT_TIME;
for (i=0; i < NAME_LEN ; i++)
de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
bh->b_dirt = 1;
*res_dir = de;
return bh;
}
// 如果該目錄項已經被使用,則繼續檢測下一個目錄項。
de++;
i++;
}
// 執行不到這里。也許Linus 在寫這段代碼時是先復制了上面find_entry()的代碼,而后修改的:)
brelse(bh);
return NULL;
}
/*
* get_dir()
*
* 該函數根據給出的路徑名進行搜索,直到達到最頂端的目錄。
* 如果失敗則返回NULL。
*/
//// 搜尋指定路徑名的目錄。
// 參數:pathname - 路徑名。
// 返回:目錄的i 節點指針。失敗時返回NULL。
static struct m_inode * get_dir(const char * pathname)
{
char c;
const char * thisname;
struct m_inode * inode;
struct buffer_head * bh;
int namelen,inr,idev;
struct dir_entry * de;
// 如果進程沒有設定根i 節點,或者該進程根i 節點的引用為0,則系統出錯,死機。
if (!current->root || !current->root->i_count)
panic("No root inode");
// 如果進程的當前工作目錄指針為空,或者該當前目錄i 節點的引用計數為0,也是系統有問題,死機。
if (!current->pwd || !current->pwd->i_count)
panic("No cwd inode");
// 如果用戶指定的路徑名的第1 個字符是'/',則說明路徑名是絕對路徑名。則從根i 節點開始操作。
if ((c=get_fs_byte(pathname))=='/') {
inode = current->root;
pathname++;
// 否則若第一個字符是其它字符,則表示給定的是相對路徑名。應從進程的當前工作目錄開始操作。
// 則取進程當前工作目錄的i 節點。
} else if (c)
inode = current->pwd;
// 否則表示路徑名為空,出錯。返回NULL,退出。
else
return NULL; /* 空的路徑名是錯誤的 */
// 將取得的i 節點引用計數增1。
inode->i_count++;
while (1) {
// 若該i 節點不是目錄節點,或者沒有可進入的訪問許可,則釋放該i 節點,返回NULL,退出。
thisname = pathname;
if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
iput(inode);
return NULL;
}
// 從路徑名開始起搜索檢測字符,直到字符已是結尾符(NULL)或者是'/',此時namelen 正好是當前處理
// 目錄名的長度。如果最后也是一個目錄名,但其后沒有加'/',則不會返回該最后目錄的i 節點!
// 比如:/var/log/httpd,將只返回log/目錄的i 節點。
for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
/* nothing */ ;
// 若字符是結尾符NULL,則表明已經到達指定目錄,則返回該i 節點指針,退出。
if (!c)
return inode;
// 調用查找指定目錄和文件名的目錄項函數,在當前處理目錄中尋找子目錄項。如果沒有找到,
// 則釋放該i 節點,并返回NULL,退出。
if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
iput(inode);
return NULL;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -