⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 namei.c

📁 linux0.11原码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* 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 + -