?? namei.c
字號:
/** linux/fs/namei.c** (C) 1991 Linus Torvalds*//** Some corrections by tytso.*//** 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])/** comment out this line if you want names > NAME_LEN chars to be* truncated. Else they will be disallowed.*//** 如果想讓文件名長度>NAME_LEN 的字符被截掉,就將下面定義注釋掉。*//* #define NO_TRUNCATE */#define MAY_EXEC 1 // 可執行(可進入)。#define MAY_WRITE 2 // 可寫。#define MAY_READ 4 // 可讀。/** permission()** is used to check for read/write/execute permissions on a file.* I don't know if we should look at just the euid or both euid and* uid, but that should be easily changed.*//** permission()* 該函數用于檢測一個文件的讀/寫/執行權限。我不知道是否只需檢查euid,還是* 需要檢查euid 和uid 兩者,不過這很容易修改。*///// 檢測文件訪問許可權限。// 參數:inode - 文件對應的i 節點;mask - 訪問屬性屏蔽碼。// 返回:訪問許可返回1,否則返回0。static intpermission (struct m_inode *inode, int mask){ int mode = inode->i_mode; // 文件訪問屬性/* special case: not even root can read/write a deleted file *//* 特殊情況:即使是超級用戶(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 ()) return 1; return 0;}/** ok, we cannot use strncmp, as the name is not in our data space.* Thus we'll have to use match. No big problem. Match also makes* some sanity tests.** NOTE! unlike strncmp, match returns 1 for success, 0 for failure.*//** ok,我們不能使用strncmp 字符串比較函數,因為名稱不在我們的數據空間(不在內核空間)。* 因而我們只能使用match()。問題不大。match()同樣也處理一些完整的測試。** 注意!與strncmp 不同的是match()成功時返回1,失敗時返回0。*///// 指定長度字符串比較函數。// 參數:len - 比較的字符串長度;name - 文件名指針;de - 目錄項結構。// 返回:相同返回1,不同返回0。static intmatch (int len, const char *name, struct dir_entry *de){ register int same __asm__ ("ax");// 如果目錄項指針空,或者目錄項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), "S" ((long) name), "D" ((long) de->name), "c" (len):"cx", "di", "si"); return same; // 返回比較結果。}/** find_entry()** finds an entry in the specified directory with the wanted name. It* returns the cache buffer in which the entry was found, and the entry* itself (as a parameter - res_dir). It does NOT read the inode of the* entry - you'll have to do that yourself if you want to.** This also takes care of the few special cases due to '..'-traversal* over a pseudo-root and a mount point.*//** 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;/* check for '..', as we might have to do some "magic" for it *//* 檢查目錄項'..',因為可能需要對其特別處理 */ if (namelen == 2 && get_fs_byte (name) == '.' && get_fs_byte (name + 1) == '.') {/* '..' in a pseudo-root results in a faked '.' (just change namelen) *//* 偽根中的'..'如同一個假'.'(只需改變名字長度) */// 如果當前進程的根節點指針即是指定的目錄,則將文件名修改為'.', if ((*dir) == current->root) namelen = 1;// 否則如果該目錄的i 節點號等于ROOT_INO(1)的話,說明是文件系統根節點。則取文件系統的超級塊。 else if ((*dir)->i_num == ROOT_INO) {/* '..' over a mount-point results in 'dir' being exchanged for the mounteddirectory-inode. NOTE! We set mounted, so that we can iput the new dir *//* 在一個安裝點上的'..'將導致目錄交換到安裝到文件系統的目錄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()** adds a file entry to the specified directory, using the same* semantics as find_entry(). It returns NULL if it failed.** NOTE!! The inode part of 'de' is left at 0 - which means you* may not sleep between calling this and putting something into* the entry, as someone else might have used it while you slept.*//** 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)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -