📄 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 + -