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

📄 ul_namei.cpp

📁 类似Linux操作系统0.11版文件系统的文件系统设计和Windows下的操作程序
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/*
 * UNIX Like (UL)文件系统 目录 处理模块
 */

#include "stdafx.h"
#include "UL_NameI.h"
#include "UL_Buffer.h"
#include "UL_FileSys.h"
#include "UL_Inode.h"
#include "UL_Super.h"
#include "UL_Block.h"
#include "UL_File.h"

#define NO_TRUNCATE

#define MAY_EXEC 1		// 可执行(可进入)。
#define MAY_WRITE 2		// 可写。
#define MAY_READ 4		// 可读。

//// 检测文件访问许可权限。
// 参数:inode - 文件对应的i 节点;mask - 访问属性屏蔽码。
// 返回:访问许可返回1,否则返回0。
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;

	return 1;
}

// 指定长度字符串比较函数。
// 参数:len - 比较的字符串长度;name - 文件名指针;de - 目录项结构。
// 返回:相同返回1,不同返回0。
int match (int len, const char *name, struct dir_entry *de)
{
	// 如果目录项指针空,或者目录项i 节点等于0,或者要比较的字符串长度超过文件名长度,则返回0。
	if (!de || !de->inode || len > NAME_LEN)
		return 0;

	if (len < NAME_LEN && de->name[len])
		return 0;

	if( strncmp(name, de->name, len) )
		return 0;
	return 1;
}
//// 查找指定目录和文件名的目录项。
// 参数:dir - 指定目录i 节点的指针;name - 文件名;namelen - 文件名长度;
// 返回:高速缓冲区指针;res_dir - 返回的目录项结构指针;
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_byte (name) == '.'
		&& get_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 mounted
			directory-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;
}

//// 根据指定的目录和文件名添加目录项。
// 参数:dir - 指定目录的i 节点;name - 文件名;namelen - 文件名长度;
// 返回:高速缓冲区指针;res_dir - 返回的目录项结构指针;
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)
		{
			printk ("add_entry: i=%d , dir->i_size=%d\n", i, dir->i_size);
			dir->i_mtime = CURRENT_TIME;
			for (i = 0; i < NAME_LEN; i++)
				de->name[i] = (i < namelen) ? get_byte (name + i) : 0;
			bh->b_dirt = 1;
			*res_dir = de;
			return bh;
		}
		// 如果该目录项已经被使用,则继续检测下一个目录项。
		de++;
		i++;
	}
	// 执行不到这里。也许Linus 在写这段代码时是先复制了上面find_entry()的代码,而后修改的?。
	brelse (bh);
	return NULL;
}

//// 搜寻指定路径名的目录。
// 参数:pathname - 路径名。
// 返回:目录的i 节点指针。失败时返回NULL。
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_byte (pathname)) == '/')
	{
		inode = current_root;
		pathname++;
		// 否则若第一个字符是其它字符,则表示给定的是相对路径名。应从进程的当前工作目录开始操作。
		// 则取进程当前工作目录的i 节点。
	}
	else if (c)
		inode = current_pwd;
	// 否则表示路径名为空,出错。返回NULL,退出。
	else
		return NULL;		/* empty name is bad *//* 空的路径名是错误的 */
	// 将取得的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_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 节点。
		inr = de->inode;
		idev = inode->i_dev;
		brelse (bh);
		iput (inode);
		// 取节点号inr 的i 节点信息,若失败,则返回NULL,退出。否则继续以该子目录的i 节点进行操作。
		if (!(inode = iget (idev, inr)))

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -