📄 namei.c
字号:
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <const.h>
#include <sys/stat.h>
#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.
*/
/* #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.
*/
static int permission(struct m_inode * inode,int mask) //权限检查
{
int mode = inode->i_mode;
/* special case: not even root can read/write a deleted file */
if (inode->i_dev && !inode->i_nlinks)
return 0;
if (!(current->uid && current->euid)) //uid和euid都为0,表示最高权限
mode=0777;
else if (current->uid==inode->i_uid || current->euid==inode->i_uid) //用户权限检查
mode >>= 6;
else if (current->gid==inode->i_gid || current->egid==inode->i_gid) //用户组权限检查
mode >>= 3;
return mode & mask & 0007;
}
/*
* 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.
*/
static int match(int len,const char * name,struct dir_entry * de)
{
register int same __asm__("ax");
if (!de || !de->inode || len > NAME_LEN) //如果目录指针为空或节点不存在或长度太长
return 0;
if (len < NAME_LEN && de->name[len])
return 0;
//下面内嵌汇编的意思是:
// movl 0 %eax
// movl name %esi
// movl de->name %edi
// cld
// repe //ZF=1
// cmpsb
// setz %al
// movl %eax same
__asm__("cld\n\t"
"fs ; repe ; cmpsb\n\t"
"setz %%al"
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
:"cx","di","si");
return same;
}
/*
* find_entry()
*
* finds and 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.
*/
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;
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#endif
entries = dir->i_size / (sizeof (struct dir_entry)); //该目录里包含几个入口
*res_dir = NULL;
if (!namelen)
return NULL;
if (!(block = dir->i_zone[0])) //此目录里有没有元素
return NULL;
if (!(bh = bread(dir->i_dev,block))) //从硬盘读取该目录
return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data; //de指向目录文件数据区的开始
while (i < entries) {
if ((char *)de >= BLOCK_SIZE+bh->b_data) { //如果该目录文件中一个块读完仍未找到,就读该目录文件的下一个块
brelse(bh); //释放bh
bh = NULL;
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)) { //对比目录文件中的一个元素,返回1是找到
*res_dir = de; //指向所找数据的偏移量
return bh;
}
de++;
i++;
}
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.
*/
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;
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#endif
if (!namelen) //如果光是目录,没有文件名,返回空
return NULL;
if (!(block = dir->i_zone[0])) //取得目录文件的第一个块号
return NULL;
if (!(bh = bread(dir->i_dev,block))) //读取目录文件的第一块
return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data; //de指向第一块的数据区
while (1) {
if ((char *)de >= BLOCK_SIZE+bh->b_data) { //如果de指向了该块的末尾
brelse(bh);
bh = NULL;
block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK); //取得目录文件的下一个块号,如果需要,就创建新块
if (!block)
return NULL;
if (!(bh = bread(dir->i_dev,block))) { //将此块读入buffer
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data; //de指向该块的数据区
}
if (i*sizeof(struct dir_entry) >= dir->i_size) { //在目录文件的末尾创建新的节点,初始化为0,并修改该目录文件的尺寸
de->inode=0;
dir->i_size = (i+1)*sizeof(struct dir_entry);
dir->i_dirt = 1;
dir->i_ctime = CURRENT_TIME;
}
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; //将文件名写入de->name
bh->b_dirt = 1;
*res_dir = de; //这时de是一个inode=0和name=文件名的结构
return bh;
}
de++;
i++;
}
brelse(bh);
return NULL;
}
/*
* get_dir()
*
* Getdir traverses the pathname until it hits the topmost directory.
* It returns NULL on failure.
*/
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;
if (!current->root || !current->root->i_count)
panic("No root inode");
if (!current->pwd || !current->pwd->i_count)
panic("No cwd inode");
if ((c=get_fs_byte(pathname))=='/') { //取第一个字节
inode = current->root;
pathname++;
} else if (c)
inode = current->pwd;
else
return NULL; /* empty name is bad */
inode->i_count++;
while (1) {
thisname = pathname;
if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { //如果不是目录或没有权限
iput(inode); //释放该节点
return NULL;
}
for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) //取得目录长度
/* nothing */ ;
if (!c)
return inode; //取完全部目录返回,循环出口
if (!(bh = find_entry(inode,thisname,namelen,&de))) {
iput(inode);
return NULL;
}
inr = de->inode; //取得目录的inode
idev = inode->i_dev;
brelse(bh);
iput(inode);
if (!(inode = iget(idev,inr))) //取得该目录的内存inode,为寻找下一个子目录做准备
return NULL;
}
}
/*
* dir_namei()
*
* dir_namei() returns the inode of the directory of the
* specified name, and the name within that directory.
*/
static struct m_inode * dir_namei(const char * pathname,
int * namelen, const char ** name)
{
char c;
const char * basename;
struct m_inode * dir;
if (!(dir = get_dir(pathname))) //取得该目录的最后子目录的内存inode
return NULL;
basename = pathname; //两个指针指向同一地址,即文件全名的起始地址
while (c=get_fs_byte(pathname++))
if (c=='/')
basename=pathname; //basename最终将是要运行的程序名
*namelen = pathname-basename-1;
*name = basename;
return dir;
}
/*
* namei()
*
* is used by most simple commands to get the inode of a specified name.
* Open, link etc use their own routines, but this is enough for things
* like 'chmod' etc.
*/
struct m_inode * namei(const char * pathname)
{
const char * basename;
int inr,dev,namelen;
struct m_inode * dir;
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(pathname,&namelen,&basename))) //取得目录的内存inode
return NULL;
if (!namelen) /* special case: '/usr/' etc */
return dir;
bh = find_entry(dir,basename,namelen,&de); //取得需要运行或打开的inode
if (!bh) { //如果没有这个文件,释放该目录的buffer
iput(dir);
return NULL;
}
inr = de->inode; //取得该文件的硬盘inode
dev = dir->i_dev;
brelse(bh);
iput(dir);
dir=iget(dev,inr); //取得该文件的内存inode
if (dir) {
dir->i_atime=CURRENT_TIME;
dir->i_dirt=1;
}
return dir;
}
/*
* open_namei()
*
* namei for open - this is in fact almost the whole open-routine.
*/
int open_namei(const char * pathname, int flag, int mode,
struct m_inode ** res_inode)
{
const char * basename;
int inr,dev,namelen;
struct m_inode * dir, *inode;
struct buffer_head * bh;
struct dir_entry * de;
if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) //如果flag是可修剪的或可访问的
flag |= O_WRONLY; //那么加上可写位
mode &= 0777 & ~current->umask; //mode必须接受当前进程掩码的约束
mode |= I_REGULAR;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT;
if (!namelen) { /* special case: '/usr/' etc */ //如果pathname只是目录,而没有文件名
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
*res_inode=dir;
return 0;
}
iput(dir);
return -EISDIR;
}
bh = find_entry(dir,basename,namelen,&de);
if (!bh) { //如果在目录中没有发现该文件的入口点
if (!(flag & O_CREAT)) { //如果flag中没有建立位,返回
iput(dir);
return -ENOENT;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -