📄 namei.c.bak
字号:
/* * 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()) 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{
xor eax,eax
mov esi,name
mov edi,de_name
mov ecx,len
cld // 清方向位。
repe cmpsb // 用户空间执行循环比较[esi++]和[edi++]操作,
setz al // 若比较结果一样(z=0)则设置al=1(same=eax)。
mov same,eax
} 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; }
// 取该子目录项的i 节点号inr 和设备号idev,释放包含该目录项的高速缓冲块和该i 节点。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -