文件可能比较长,呵呵。
/*
* linux/fs/namei.c
*
* (C) 1991 Linus Torvalds
*/
/*
* Some corrections by tytso.
*/
#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;
// 如果进程的有效用户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;
// 如果上面所取的的访问权限与屏蔽码相同,或者是超级用户
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.
*/
/* 指定长度字符串比较函数,match()成功时返回1,失败时返回0 */
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;
__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 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.
*/
/* 从一个目录中搜索制定的目录项,在指定的目录下,查找相应的文件inode */
/* 标志。例如在/etc/目录下,查找某个文件,返回的是该文件的inode标志 */
/* 函数的返回值放在参数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;
// 得到namelen
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#endif
// linux中目录也是文件
// 计算本目录中目录项项数entries
entries = (*dir)->i_size / (sizeof (struct dir_entry));
*res_dir = 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 mounted
directory-inode. NOTE! We set mounted, so that we can iput the new dir */
// 取文件系统的超级块
sb=get_super((*dir)->i_dev);
if (sb->s_imount) // 如果被安装到的i 节点存在
{
iput(*dir); // 释放原i 节点
// 让*dir 指向该被安装到的i 节点
(*dir)=sb->s_imount; // 对被安装到的i 节点进行处理
(*dir)->i_count++; // i_count加加
}
}
}
// 如果该i 节点所指向的第一个直接磁盘块号为0
if (!(block = (*dir)->i_zone[0]))
// 返回NULL,退出
return NULL;
// 从节点所在设备读取指定的目录项数据块
if (!(bh = bread((*dir)->i_dev,block)))
return NULL;
i = 0;
// 让de 指向数据块(de -- struct dir_entry)
de = (struct dir_entry *) bh->b_data;
while (i < entries) // 不超过目录中目录项数
{
// 如果当前目录项数据块已经搜索完
if ((char *)de >= BLOCK_SIZE+bh->b_data)
{
brelse(bh); // 释放当前目录项数据块
bh = NULL;
// 在读入下一目录项数据块,首先是使用*dir来得到设备上的
// 逻辑块号,然后使用函数bread来读取设备上的数据
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; // 就让de 指向该目录项数据块
}
// 如果找到匹配的目录项的话,则返回该目录项结构指针和该目录项数据块指针
if (match(namelen,name,de))
{
*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;
// 得到namelen
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#endif
if (!namelen)
return NULL;
// 如果该目录i 节点所指向的第一个直接磁盘块号为0
if (!(block = dir->i_zone[0]))
return NULL;
if (!(bh = bread(dir->i_dev,block))) // 读取该磁盘块
// bh中存储的是目录项struct dir_entry
return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data;
while (1)
{
// 当前判别的目录项已经超出当前数据块?
if ((char *)de >= BLOCK_SIZE+bh->b_data)
{
brelse(bh); // 释放该数据块
bh = NULL;
// 重新申请一块磁盘块block
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个目录项还未使用,我们可以使用
// 它。对该目录项进行设置
if (i*sizeof(struct dir_entry) >= dir->i_size)
{
de->inode=0; // 置该目录项的i 节点指针为空
// 更新该目录的长度值
dir->i_size = (i+1)*sizeof(struct dir_entry);
// 设置目录的i 节点已修改标志
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++;
}
// 执行不到这里??
brelse(bh);
return NULL;
}
/*
* get_dir()
*
* Getdir traverses the pathname until it hits the topmost directory.
* It returns NULL on failure.
*/
/* 通过参数pathname来得到i节点m_inode */
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 个字符是'/',则说明路径名是绝对路径名
if ((c=get_fs_byte(pathname))=='/')
{
inode = current->root; // 从根i 节点开始操作
pathname++;
}
else if (c) // 否则若第一个字符是其它字符,则表示给定的是相对路径名
inode = current->pwd; // 应从进程的当前工作目录开始操作
else // 表示路径名为空,出错。返回NULL
return NULL; /* empty name is bad */
inode->i_count++; // // 将取得的i 节点引用计数增1
while (1)
{
thisname = pathname;.
// 若该i 节点不是目录节点,或者没有可进入的访问许可
if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC))
{
iput(inode); // 释放该i 节点
return NULL;
}
// 得到当前处理目录长度namelen
for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
/* nothing */ ;
if (!c) // 若字符是结尾符NULL
return inode; // 表明已经到达指定目录,返回该inode
// 在上面找到的目录下查找thisname文件名
if (!(bh = find_entry(&inode,thisname,namelen,&de))) // 失败
{
iput(inode); // 释放i节点
return NULL; // 返回NULL指针
}
// 通过de,取该子目录项的i 节点号inr 和设备号idev
inr = de->inode;
idev = inode->i_dev;
brelse(bh);
iput(inode); // 释放该i 节点
// 取节点号inr 的i 节点信息,若失败,则返回NULL,退
// 出。否则继续以该子目录的i 节点进行操作
if (!(inode = iget(idev,inr)))
return NULL;
}
}
/*
* dir_namei()
*
* dir_namei() returns the inode of the directory of the
* specified name, and the name within that directory.
*/
/* 函数dir_namei和函数get_dir大致上是相同的,但是函数dir_namei返回的信息 */
/* 比较get_dir而言,新增加了namelen和name返回值,注意的是namelen的值是除去目录*/
/* 的长度。例如/etc/inittab,返回的是inittab的长度,但是如果是/etc/inittab/的话 */
/* 返回的就是0 */
static struct m_inode * dir_namei(const char * pathname,
int * namelen, const char ** name)
{
char c;
const char * basename;
struct m_inode * dir;
// 取指定路径名最顶层目录的i 节点,若出错则返回NULL
if (!(dir = get_dir(pathname)))
return NULL;
basename = pathname;
// 得到namelen
while (c=get_fs_byte(pathname++))
if (c=='/')
basename=pathname;
*namelen = pathname-basename-1;
// 得到name值
*name = basename;
return dir; // 不仅返回dir,还包括namelen和name字段
}
/*
* 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.
*/
/* 取指定路径名的i 节点,其中参数pathname是路径 */
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;
// 查找指定路径的最顶层目录的目录名及其i 节点
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return NULL; // 失败
if (!namelen) /* special case: '/usr/' etc */
// 如果返回的最顶层名字的长度是0
return dir;
// 在该目录节点下查找该文件名的inode节点
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) // 失败
{
iput(dir); // 释放i节点
return NULL;
}
// 得到i节点信息
inr = de->inode; // i节点号
dev = dir->i_dev; // 设备号
brelse(bh); // 释放buffer_head缓冲区
iput(dir); // 得到该inode节点
dir=iget(dev,inr);
if (dir) // 成功
{
dir->i_atime = CURRENT_TIME;
dir->i_dirt=1;
}
return dir; // 返回该inode节点
}
/*
* open_namei()
*
* namei for open - this is in fact almost the whole open-routine.
*/
/* 文件打开namei 函数 */
/* 该函数实现的功能大体上是根据参数pathname来查找该文件对应的i节点,但是 */
/* 和函数namei不相同的是在该函数加入了文件权限的检查等 */
int open_namei(const char * pathname, int flag, int mode,
struct m_inode ** res_inode)
{
// flag - 文件打开标志;mode - 文件访问许可属性
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 |= O_WRONLY;
mode &= 0777 & ~current->umask;
mode |= I_REGULAR;
// 根据路径名寻找到对应的i 节点
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT;
if (!namelen)
{ /* special case: '/usr/' etc */
// 若打开操作不是创建、截0
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC)))
{
// 则表示打开一个目录名,直接返回该目录的i 节点,并退出
*res_inode=dir;
return 0;
}
iput(dir);
return -EISDIR;
}
// // 在dir 节点对应的目录中取文件名对应的目录项结构de 和
// 该目录项所在的高速缓冲区
bh = find_entry(&dir,basename,namelen,&de);
// 如果该高速缓冲指针为NULL,则表示没有找到对应文件名的目录项
// 因此只可能是创建文件操作
if (!bh)
{
// 如果不是创建文件,则释放该目录的i 节点,返回出错号退出
if (!(flag & O_CREAT))
{
iput(dir);
return -ENOENT;
}
// 如果用户在该目录没有写的权力,则释放该目录的i 节点,返
// 回出错号退出。即是该用户在该目录下内有权限去建立新的文件
if (!permission(dir,MAY_WRITE))
{
iput(dir); // 释放i节点
return -EACCES; // 返回出错code
}
// 在目录节点对应的设备上申请一个新i 节点
inode = new_inode(dir->i_dev);
if (!inode) // 失败?
{
iput(dir);
return -ENOSPC;
}
// 否则使用该新i 节点,初始化该i节点
inode->i_uid = current->euid;
inode->i_mode = mode;
inode->i_dirt = 1;
// 然后在指定目录dir 中添加一新目录项
bh = add_entry(dir,basename,namelen,&de);
if (!bh) // 失败
{
// 新i 节点的引用连接计数减1;并释放该i 节点与目录的i 节点
inode->i_nlinks--;
iput(inode);
iput(dir);
return -ENOSPC;
}
de->inode = inode->i_num; // 置i 节点号为新申请到的i 节点的号码
bh->b_dirt = 1; // 并置高速缓冲区已修改标志
brelse(bh); // 释放该高速缓冲区
iput(dir); // 释放目录的i 节点
// 返回新目录项的i 节点指针
*res_inode = inode;
return 0;
}
// bh = find_entry(&dir,basename,namelen,&de);成功,即是在该目录下找到了
// 指定的文件,即是bh不是空值
inr = de->inode;
dev = dir->i_dev;
brelse(bh);
iput(dir);
// 如果独占使用标志O_EXCL 置位,则返回文件已存在出错码
if (flag & O_EXCL)
return -EEXIST;
if (!(inode=iget(dev,inr))) // 取该目录项对应i 节点
return -EACCES;
// 1.i 节点是一个目录的节点 2.访问模式是只读或只写
// 3. 没有访问的许可权限
if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
!permission(inode,ACC_MODE(flag)))
{
iput(inode);
return -EPERM;
}
inode->i_atime = CURRENT_TIME;
if (flag & O_TRUNC)
truncate(inode);
*res_inode = inode; // 最后返回该目录项i 节点的指针
return 0; // 成功,返回0
}
/* 创建一个特殊文件或普通文件节点,该文件可能是普通文件,设备特殊文件 */
/* 或者是管道文件,有参数mode来指定 */
int sys_mknod(const char * filename, int mode, int dev)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!suser()) // 需要超级用户权限
return -EPERM;
// 查找文件上级的目录项i节点
if (!(dir = dir_namei(filename,&namelen,&basename)))
return -ENOENT;
if (!namelen) // 此处不能是下面的情况/etc/XXX/
{
iput(dir); // 释放该节点
return -ENOENT; // 返回错误代码
}
if (!permission(dir,MAY_WRITE)) // 该目录下盖用户没有的建立文件的权限
{
iput(dir);
return -EPERM;
}
// 在该目录项下,新建文件名为basename的文件,下面的函数一定要返回NULL
bh = find_entry(&dir,basename,namelen,&de);
if (bh) // 需要建立的文件已经存在
{
brelse(bh);
iput(dir);
return -EEXIST;
}
inode = new_inode(dir->i_dev); // 申请一个新的i 节点
if (!inode)
{
iput(dir);
return -ENOSPC;
}
inode->i_mode = mode; // 设置该i 节点的属性模式
if (S_ISBLK(mode) || S_ISCHR(mode)) // 是块设备文件或者是字符设备文件
inode->i_zone[0] = dev; // 令i 节点的直接块指针0 等于设备号
// 设置该i 节点的修改时间、访问时间为当前时间
inode->i_mtime = inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
// 在目录中新添加一个目录项
bh = add_entry(dir,basename,namelen,&de);
if (!bh)
{
iput(dir);
inode->i_nlinks=0; // 所申请的i 节点引用连接计数复位
iput(inode);
return -ENOSPC;
}
de->inode = inode->i_num; // 令该目录项的i 节点字段等于新i 节点号
bh->b_dirt = 1; // 置高速缓冲区已修改标志
// 事后处理工作
iput(dir);
iput(inode);
brelse(bh);
return 0;
}
/* 系统调用函数,实现建立目录 */
int sys_mkdir(const char * pathname, int mode)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh, *dir_block;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT;
if (!namelen) // 下面的情况不行/etc/XXX/
{
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) // 需要有在该目录下建立文件夹的能力
{
iput(dir);
return -EPERM;
}
// 在该目录下,查找相应的目录节点,下面的函数应该返回NULL
bh = find_entry(&dir,basename,namelen,&de);
if (bh)
{
brelse(bh);
iput(dir);
return -EEXIST;
}
// 重新申请一个inode节点
inode = new_inode(dir->i_dev);
if (!inode)
{
iput(dir);
return -ENOSPC;
}
// 设置新生成的inode节点
inode->i_size = 32;
inode->i_dirt = 1;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
// 为该i 节点申请一磁盘块
if (!(inode->i_zone[0]=new_block(inode->i_dev)))
{
iput(dir);
inode->i_nlinks--;
iput(inode);
return -ENOSPC;
}
// 置该新的i 节点已修改标志
inode->i_dirt = 1;
// 读新申请的磁盘块,其实其中全部是0,在下面进行的初始化
if (!(dir_block=bread(inode->i_dev,inode->i_zone[0])))
{
iput(dir); // 释放对应目录的i 节点
// 释放申请的磁盘块
free_block(inode->i_dev,inode->i_zone[0]);
inode->i_nlinks--;
iput(inode);
return -ERROR;
}
// 令de 指向目录项数据块,其实是使用de指向dir_block->b_data,初始化
// de指定的内容,就是将 dir_block->b_data中的数据初始化
de = (struct dir_entry *) dir_block->b_data;
// 置该目录项的i 节点号字段等于新申请的i 节点号
de->inode=inode->i_num;
strcpy(de->name,"."); // 名字字段等于"."
de++; // // 然后de 指向下一个目录项结构
de->inode = dir->i_num;
strcpy(de->name,"..");
inode->i_nlinks = 2;
// 然后设置该高速缓冲区已修改标志,并释放该缓冲区
dir_block->b_dirt = 1;
brelse(dir_block);
// 初始化设置新i 节点的模式字段,并置该i 节点已修改标志
inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
inode->i_dirt = 1;
// 在新建的文件夹上层新增entry
bh = add_entry(dir,basename,namelen,&de);
if (!bh)
{
iput(dir);
free_block(inode->i_dev,inode->i_zone[0]);
inode->i_nlinks=0;
iput(inode);
return -ENOSPC;
}
// 令该目录项的i 节点字段等于新i 节点号,置高速缓冲区已修改标志
de->inode = inode->i_num;
bh->b_dirt = 1;
dir->i_nlinks++;
dir->i_dirt = 1;
// 释放目录和新的i 节点
iput(dir);
iput(inode);
brelse(bh); // 高速缓冲区
return 0;
/*
* 函数主要是实现在一个给定的目录下建立文件夹,例如在Test目录下
* 建立文件夹a,首先得到Test的inode,然后在Test中寻找a文件夹所
* 对应的节点,显然没有找到,需要建立,首先是生成该文件夹,申请
* 磁盘逻辑块,将.和..目录的inode写入到其中新申请的磁盘逻辑块上。
* 在a文件夹建立之后,需要将a的文件件表项添加到Test文件件下
*/
}
/*
* routine to check that the specified directory is empty (for rmdir)
*/
/* 检查指定目录是否是空的(同时检查目录下是否有文件被使用) */
/* 返回:0 - 是空的;1 - 不空 */
static int empty_dir(struct m_inode * inode)
{
int nr,block;
int len;
struct buffer_head * bh;
struct dir_entry * de;
// 计算指定目录中现有目录项的个数(应该起码有2 个,即"."和".."两个文件目录项)
len = inode->i_size / sizeof (struct dir_entry);
// 如果目录项个数少于2 个或者该目录i 节点的第1 个直接块没有指向任何磁盘块号,
// 或者相应磁盘块读不出
if (len<2 || !inode->i_zone[0] ||
!(bh=bread(inode->i_dev,inode->i_zone[0]))) {
printk("warning - bad directory on dev %04x\n",inode->i_dev);
return 0;
}
// bh->b_data的数据在上面的函数sys_mkdir中初始化
de = (struct dir_entry *) bh->b_data;
// 如果第1 个目录项的i 节点号字段值不等于该目录的i 节点号,或者第2 个目录项的i 节点号字段
// 为零,或者两个目录项的名字字段不分别等于"."和"..",则显示出错警告信息“设备dev 上目录错”
// 并返回0。
if (de[0].inode != inode->i_num || !de[1].inode ||
strcmp(".",de[0].name) || strcmp("..",de[1].name))
{
printk("warning - bad directory on dev %04x\n",inode->i_dev);
return 0;
}
nr = 2; // 令nr 等于目录项序号
de += 2; // de 指向第三个目录项
// 看有没有目录项的i 节点号字段不为0(被使用)
while (nr<len)
{
// 如果该块磁盘块中的目录项已经检测完,则释放该磁盘块的高速缓冲区,读取下一块含有目录项的
// 磁盘块。若相应块没有使用(或已经不用,如文件已经删除等),则继续读下一块,若读不出,则出
// 错,返回0。否则让de 指向读出块的首个目录项。
if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE))
{
brelse(bh);
block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK);
if (!block) {
nr += DIR_ENTRIES_PER_BLOCK;
continue;
}
if (!(bh=bread(inode->i_dev,block)))
return 0;
de = (struct dir_entry *) bh->b_data;
}
// 如果该目录项的i 节点号字段不等于0,则表示该目录项目前正被使用,则释放该高速缓冲区,
// 返回0,退出
if (de->inode) {
brelse(bh);
return 0;
}
// 否则,若还没有查询完该目录中的所有目录项,则继续检测
de++;
nr++;
}
// 到这里说明该目录中没有找到已用的目录项(当然除了头两个以外),则返回缓冲区,返回1。
brelse(bh);
return 1;
}
/* 删除指定名称的目录 */
int sys_rmdir(const char * name)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(name,&namelen,&basename)))
return -ENOENT;
if (!namelen) // 不能是/etc/XXX/(for example)
{
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) // 写权限?
{
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (!bh)
{
iput(dir);
return -ENOENT;
}
if (!(inode = iget(dir->i_dev, de->inode))) {
iput(dir);
brelse(bh);
return -EPERM;
}
// 若该目录设置了受限删除标志并且进程的有效用户id 不等于该i 节点的用户id
// 则表示没有权限删除该目录
if ((dir->i_mode & S_ISVTX) && current->euid &&
inode->i_uid != current->euid) {
iput(dir);
iput(inode);
brelse(bh);
return -EPERM;
}
// 如果要被删除的目录项的i 节点的设备号不等于包含该目录项的目录的设备号
// 或者该被删除目录的引用连接计数大于1(表示有符号连接等),则不能删除该目录
if (inode->i_dev != dir->i_dev || inode->i_count>1) {
iput(dir);
iput(inode);
brelse(bh);
return -EPERM;
}
// 试图删除当前目录'.'
if (inode == dir) { /* we may not delete ".", but "../dir" is ok */
iput(inode);
iput(dir);
brelse(bh);
return -EPERM;
}
// 若要被删除的目录的i 节点的属性表明这不是一个目录
if (!S_ISDIR(inode->i_mode)) {
iput(inode);
iput(dir);
brelse(bh);
return -ENOTDIR;
}
// 若该需被删除的目录不空
if (!empty_dir(inode)) {
iput(inode);
iput(dir);
brelse(bh);
return -ENOTEMPTY;
}
// 若该需被删除目录的i 节点的连接数不等于2('.'和'..'),则显示警告信息
if (inode->i_nlinks != 2)
printk("empty directory has nlink!=2 (%d)",inode->i_nlinks);
// 置该需被删除目录的目录项的i 节点号字段为0,表示该目录项不再使用,并置含有该目录项的高速
// 缓冲区已修改标志,并释放该缓冲区。
/*************删除目录项**************/
de->inode = 0;
bh->b_dirt = 1;
brelse(bh);
/*************************************/
// 置被删除目录的i 节点的连接数为0,并置i 节点已修改标志。
inode->i_nlinks=0;
inode->i_dirt=1;
// 将包含被删除目录名的目录的i 节点引用计数减1,修改其改变时间和修改时间为当前时间,并置
// 该节点已修改标志
dir->i_nlinks--;
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt=1;
// 最后释放包含要删除目录名的目录i 节点和该要删除目录的i 节点
iput(dir);
iput(inode);
return 0;
}
/* 从文件系统删除一个名字。如果是一个文件的最后一个连接,并且没有进程正打 */
/* 开该文件,则该文件 也将被删除,并释放所占用的设备空间 */
int sys_unlink(const char * name)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(name,&namelen,&basename)))
return -ENOENT;
if (!namelen)
{
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
iput(dir);
return -ENOENT;
}
if (!(inode = iget(dir->i_dev, de->inode))) {
iput(dir);
brelse(bh);
return -ENOENT;
}
// 如果该目录设置了受限删除标志并且用户不是超级用户,并且进程的有效用户id 不等于被删除文件
// 名i 节点的用户id,并且进程的有效用户id 也不等于目录i 节点的用户id,则没有权限删除该文件
// 名。则释放该目录i 节点和该文件名目录项的i 节点,释放包含该目录项的缓冲区,返回出错号。
if ((dir->i_mode & S_ISVTX) && !suser() &&
current->euid != inode->i_uid &&
current->euid != dir->i_uid) {
iput(dir);
iput(inode);
brelse(bh);
return -EPERM;
}
// 如果该指定文件名是一个目录,则也不能删除
if (S_ISDIR(inode->i_mode)) {
iput(inode);
iput(dir);
brelse(bh);
return -EPERM;
}
// 如果该i 节点的连接数已经为0,则显示警告信息
if (!inode->i_nlinks) {
printk("Deleting nonexistent file (%04x:%d), %d\n",
inode->i_dev,inode->i_num,inode->i_nlinks);
inode->i_nlinks=1;
}
// 将该文件名的目录项中的i 节点号字段置为0,表示释放该目录项,并设置包含该目录项的缓冲区
// 已修改标志,释放该高速缓冲区。
de->inode = 0;
bh->b_dirt = 1;
brelse(bh);
// 该i 节点的连接数减1,置已修改标志,更新改变时间为当前时间。最后释放该i 节点和目录的i 节
// 点,返回0(成功)。
inode->i_nlinks--;
inode->i_dirt = 1;
inode->i_ctime = CURRENT_TIME;
iput(inode);
iput(dir);
return 0;
}
/* 为文件建立一个文件名,为一个已经存在的文件创建一个新连接 */
int sys_link(const char * oldname, const char * newname)
{
struct dir_entry * de;
struct m_inode * oldinode, * dir;
struct buffer_head * bh;
const char * basename;
int namelen;
// 取原文件路径名对应的i 节点oldinode
oldinode=namei(oldname);
if (!oldinode) // 失败
return -ENOENT;
// 如果原路径名对应的是一个目录名
if (S_ISDIR(oldinode->i_mode))
{
iput(oldinode);
return -EPERM;
}
dir = dir_namei(newname,&namelen,&basename);
if (!dir) {
iput(oldinode);
return -EACCES;
}
if (!namelen) {
iput(oldinode);
iput(dir);
return -EPERM;
}
// 如果新路径名目录的设备号与原路径名的设备号不一样,则也不能建立连接
if (dir->i_dev != oldinode->i_dev) {
iput(dir);
iput(oldinode);
return -EXDEV;
}
if (!permission(dir,MAY_WRITE)) { // 写的权限
iput(dir);
iput(oldinode);
return -EACCES;
}
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
brelse(bh);
iput(dir);
iput(oldinode);
return -EEXIST;
}
/**************************************/
bh = add_entry(dir,basename,namelen,&de);
/***************************************/
if (!bh) {
iput(dir);
iput(oldinode);
return -ENOSPC;
}
// 否则初始设置该目录项的i 节点号等于原路径名的i 节点号,并置包含该新添目录项的高速缓冲区
// 已修改标志,释放该缓冲区,释放目录的i 节点。
// 存盘,保存了相应的信息
de->inode = oldinode->i_num;
bh->b_dirt = 1;
brelse(bh);
iput(dir);
// 将原节点的应用计数加1,修改其改变时间为当前时间,并设置i 节点已修改标志,最后释放原
// 路径名的i 节点,并返回0(成功)。
oldinode->i_nlinks++;
oldinode->i_ctime = CURRENT_TIME;
oldinode->i_dirt = 1;
iput(oldinode);
return 0;
}