📄 ul_namei.cpp
字号:
/*
* 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 + -