/* * 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])//访问模式宏。x 是include/fcntl.h 第7 行开始定义的文件访问标志
/* * 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)// 检测文件访问许可权限
{// 参数:inode - 文件对应的i 节点;mask - 访问属性屏蔽码,返回:访问许可返回1,否则返回0
int mode = inode->i_mode;
/* special case: not even root can read/write a deleted file */ if (inode->i_dev && !inode->i_nlinks)// 如果i 节点有对应的设备,但该i 节点的连接数等于0,则返回
return 0; else if (current->euid==inode->i_uid)// 否则,如果进程的有效用户id(euid)与i 节点的用户id 相同,则取文件宿主的用户访问权限
mode >>= 6; else if (current->egid==inode->i_gid)// 否则,如果进程的有效组id(egid)与i 节点的组id 相同,则取组用户的访问权限
mode >>= 3; if (((mode & mask & 0007) == mask) || suser())// 如果上面所取的的访问权限与屏蔽码相同,或者是超级用户,则返回1,否则返回0
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. */ static int match(int len,const char * name,struct dir_entry * de)// 指定长度字符串比较函数,主要用作文件或目录名的比较
{//参数:len - 比较的字符串长度;name - 文件名指针;de - 目录项结构,相同返回1,不同返回0
register int same __asm__("ax");
if (!de || !de->inode || len > NAME_LEN)// 如果目录项指针空,或者目录项i 节点等于0,或者要比较的字符串长度超过文件名长度,则返回0
return 0; if (len < NAME_LEN && de->name[len])// 如果要比较的长度len 小于NAME_LEN,但是目录项中文件名长度超过len,则返回0
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. */ static struct buffer_head * find_entry(struct m_inode ** dir, const char * name, int namelen, struct dir_entry ** res_dir)// 查找指定目录和文件名的目录项
{//参数:dir - 指定目录i 节点的指针;name - 文件名;namelen - 文件名长度,返回:本目录项的高速缓冲区指针;res_dir - 返回的目录项结构指针
int entries; int block,i; struct buffer_head * bh; struct dir_entry * de; struct super_block * sb;
#ifdef NO_TRUNCATE// 如果定义了NO_TRUNCATE,则若文件名长度超过最大长度NAME_LEN,则返回
if (namelen > NAME_LEN) return NULL; #else//如果没有定义NO_TRUNCATE,则若文件名长度超过最大长度NAME_LEN,则截短之
if (namelen > NAME_LEN) namelen = NAME_LEN; #endif entries = (*dir)->i_size / (sizeof (struct dir_entry));// 计算本目录中目录项项数entries。
*res_dir = NULL;//置空返回目录项结构指针
if (!namelen)// 如果文件名长度等于0,则返回NULL,退出
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;//则将文件名修改为'.'
else if ((*dir)->i_num == ROOT_INO) {// 如果该目录的i 节点号等于ROOT_INO(1)的话,说明是文件系统根节点
/* '..' 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) {//如果这个文件系统 安装的节点存在
iput(*dir);//则放回原I 节点
(*dir)=sb->s_imount;//取新I 节点,也就是这个文件系统的根I 节点
(*dir)->i_count++;//记得节点引有数增1
} } } if (!(block = (*dir)->i_zone[0]))// 如果该i 节点所指向的第一个直接磁盘块号为0,则返回NULL,退出
return NULL; if (!(bh = bread((*dir)->i_dev,block)))// 从节点所在设备读取指定的目录项数据块(读取了1块,1024字节),如果不成功,则返回NULL,退出
return NULL; i = 0; de = (struct dir_entry *) bh->b_data;//首先让de 指向数据块
while (i < entries) {//entries 本目录中的目录项
if ((char *)de >= BLOCK_SIZE+bh->b_data) {// 如果当前目录项数据块已经搜索完,还没有找到匹配的目录项,则释放当前目录项数据块
brelse(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)) {// 如果找到匹配的目录项的话,则返回该目录项结构指针和该目录项数据块指针,退出
*res_dir = de; return bh; } de++; i++; } brelse(bh);// 若指定目录中的所有目录项都搜索完还没有找到相应的目录项,则释放目录项数据块,返回NUL
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)// 根据指定的目录和文件名添加目录项
{//参数:dir - 指定目录的i 节点;name - 文件名;namelen - 文件名长度,返回:该目录项高速缓冲区指针;res_dir - 返回的目录项结构指针
int block,i; struct buffer_head * bh; struct dir_entry * de;
*res_dir = NULL;//置空返回目录项结构指针
#ifdef NO_TRUNCATE// 如果定义了NO_TRUNCATE,则若文件名长度超过最大长度NAME_LEN,则返回
if (namelen > NAME_LEN) return NULL; #else//如果没有定义NO_TRUNCATE,则若文件名长度超过最大长度NAME_LEN,则截短之
if (namelen > NAME_LEN) namelen = NAME_LEN; #endif if (!namelen)// 如果文件名长度等于0,则返回NULL,退出
return NULL; if (!(block = dir->i_zone[0]))// 如果该目录i 节点所指向的第一个直接磁盘块号为0,则返回NULL 退出
return NULL; if (!(bh = bread(dir->i_dev,block)))// 如果读取该磁盘块失败,则返回NULL 并退出
return NULL; i = 0; de = (struct dir_entry *) bh->b_data;//首先让de 指向数据块
while (1) { if ((char *)de >= BLOCK_SIZE+bh->b_data) {// 如果当前判别的目录项已经超出当前数据块,则释放该数据块,重新申请一块磁盘块block
brelse(bh); bh = NULL; block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);// 创建文件数据块block 在设备上对应的逻辑块,并返回设备上对应的逻辑块号,i/DIR_ENTRIES_PER_BLOCK) 当前搜索的目录项i 所在目录文件中的块号
if (!block) return NULL; if (!(bh = bread(dir->i_dev,block))) {// 如果读取磁盘块返回的指针为空,则跳过该块继续
i += DIR_ENTRIES_PER_BLOCK; continue; } de = (struct dir_entry *) bh->b_data;// 否则,让目录项结构指针de 志向该块的高速缓冲数据块开始处
} if (i*sizeof(struct dir_entry) >= dir->i_size) {// 如果当前所操作的目录项序号i*目录结构大小已经超过了该目录所指出的大小i_size,则说明该第i 个目录项还未使用,我们可以使用它
de->inode=0;//i节点号
dir->i_size = (i+1)*sizeof(struct dir_entry);// 文件大小(字节数),目录项的大小
dir->i_dirt = 1;// 已修改(脏)标志。
dir->i_ctime = CURRENT_TIME;// i 节点自身修改时间。
} if (!de->inode) {// 若该目录项的i 节点为空,则表示找到一个还未使用的目录项,极有可能是上一语句生成的
dir->i_mtime = CURRENT_TIME;// 修改时间(自1970.1.1:0 算起,秒)。
for (i=0; i < NAME_LEN ; i++)//用户数据区复制文件名到该目录项的文件名字段
de->name[i]=(i<namelen)?get_fs_byte(name+i):0;//要记住用这种方式来复制的
bh->b_dirt = 1;/* 0-clean,1-dirty *///修改标志0为未修改,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. */ static struct m_inode * get_dir(const char * pathname)// 搜寻指定路径名的目录的I 节点(这个路径名中最顶层的I节点)
{//参数:pathname - 路径名,返回:目录的i 节点指针。失败时返回NULL
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)// 如果进程没有设定根i 节点,或者该进程根i 节点的引用为0,则系统出错,死机
panic("No root inode"); if (!current->pwd || !current->pwd->i_count)// 如果进程的当前工作目录指针为空,或者该当前目录i 节点的引用计数为0,也是系统有问题,死机
panic("No cwd inode"); if ((c=get_fs_byte(pathname))=='/') {// 如果用户指定的路径名的第1 个字符是'/',则说明路径名是绝对路径名。则从根i 节点开始操作
inode = current->root;//取根目录I 节点
pathname++; } else if (c)// 否则若第一个字符是其它字符,则表示给定的是相对路径名。应从进程的当前工作目录开始操作
inode = current->pwd;//取当前目录I 节点
else// 否则表示路径名为空,出错。返回NULL,退出
return NULL; /* empty name is bad */ inode->i_count++;// 将取得的i 节点引用计数增1
while (1) { thisname = pathname; if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {// 若该i 节点不是目录节点,或者没有可进入的访问许可,则释放该i 节点,返回NULL
iput(inode);// 释放一个i 节点
return NULL; } for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) /* nothing */ ; if (!c)// 若字符是结尾符NULL,则表明已经到达指定目录,则返回该i 节点指针,退出
return inode; if (!(bh = find_entry(&inode,thisname,namelen,&de))) {//调用查找指定目录和文件名的目录项函数,在当前处理目录中寻找子目录项。如果没有找到,则释放该i 节点,并返回NULL
iput(inode); return NULL; } inr = de->inode;//i节点号
idev = inode->i_dev;// i 节点所在的设备号。
brelse(bh);//本任务释放指定的高速缓冲块区
iput(inode);// 释放一个i 节点
if (!(inode = iget(idev,inr)))//取得一个i节点内容,dev 设备号,nr节点号
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)//函数返回指定路径的最顶层目录的i 节点指针,以及最顶层的名称
{//参数:pathname - 目录路径名;namelen - 路径名长度
char c; const char * basename; struct m_inode * dir;
if (!(dir = get_dir(pathname)))// 取最顶层目录的I 节点
return NULL; basename = pathname;// 对路径名pathname 进行搜索检测,查处最后一个'/'后面的名字字符串,计算其长度,
while (c=get_fs_byte(pathname++))//取最后一个“/”字符后的字符串,也是最高层的......
if (c=='/') basename=pathname; *namelen = pathname-basename-1; *name = basename;//返回最顶层目录的 名称
return dir;//返回最顶层目录的i 节点指针
}
/* * 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)// 根据给定的目录文件名找到对应的i 节点
{ 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)))// 首先查找指定路径的最顶层目录的I 节点以及最顶层的名称,若不存在,则返回NULL
return NULL; if (!namelen) // 如果返回的最顶层名字的长度是0,则表示该路径名以一个目录名为最后一项 /* special case: '/usr/' etc */
return dir; bh = find_entry(&dir,basename,namelen,&de);// 查找指定目录和文件名的目录项,返回值存于&de中了,同时返回本目录项的高速缓冲区信息
if (!bh) { iput(dir);// 释放一个i 节点
return NULL; } inr = de->inode;//i节点号
dev = dir->i_dev;// i 节点所在的设备号。
brelse(bh);// 本任务释放指定的高速缓冲块区
iput(dir);// 释放一个i 节点
dir=iget(dev,inr);//取得一个设备中的i节点,dev 设备号,nr节点号,从设备上读取指定节点号的i 节点到内存i 节点列表中
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)//文件打开namei 函数,返回的对应文件的i 节点指针,成功返回0,否则返回出错码
{//参数:pathname - 文件路径名;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))// 如果文件访问许可模式标志是只读(0),但文件截0 标志O_TRUNC 却置位了,则改为只写标志
flag |= O_WRONLY; mode &= 0777 & ~current->umask;// 使用进程的文件访问许可屏蔽码,屏蔽掉给定模式中的相应位,并添上普通文件标志
mode |= I_REGULAR; if (!(dir = dir_namei(pathname,&namelen,&basename)))// 返回路径名中最顶层目录的i 节点,以及最顶层文件名及其长度
return -ENOENT; if (!namelen) {// 如果最顶端文件名长度为0(例如'/usr/'这种路径名的情况), /* special case: '/usr/' etc */
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {//那么若打开操作不是创建、截0,则表示打开一个目录名,直接返回该目录的i 节点,并退出
*res_inode=dir; return 0; } iput(dir);// 否则释放该i 节点,返回出错码
return -EISDIR; } bh = find_entry(&dir,basename,namelen,&de);// 在dir 节点对应的目录中取文件名对应的目录项结构de 和该目录项所在的高速缓冲区
if (!bh) {//如果该高速缓冲指针为NULL,则表示没有找到对应文件名的目录项,因此只可能是创建文件操作
if (!(flag & O_CREAT)) {// 如果不是创建文件,则释放该目录的i 节点,返回出错号退出
iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) {// 如果用户在该目录没有写的权力,则释放该目录的i 节点,返回出错号退出
iput(dir); return -EACCES; } inode = new_inode(dir->i_dev);// 在目录节点对应的设备上申请一个新i 节点,若失败,则释放目录的i 节点,并返回没有空间出错码
if (!inode) { iput(dir); return -ENOSPC; } inode->i_uid = current->euid;// 用户id(文件拥有者标识符)。
inode->i_mode = mode;// 文件类型和属性(rwx 位)。
inode->i_dirt = 1;// 已修改(脏)标志。
bh = add_entry(dir,basename,namelen,&de);// 然后在指定目录dir 中添加一新目录项
if (!bh) {// 如果返回的应该含有新目录项的高速缓冲区指针为NULL,则表示添加目录项操作失败
inode->i_nlinks--;//新i 节点的引用连接计数减1
iput(inode);//并释放该i 节点
iput(dir);//并释放目录的节点
return -ENOSPC;//返回错误码
} de->inode = inode->i_num;// 初始设置该新目录项:置i 节点号为新申请到的i 节点的号码
bh->b_dirt = 1;//并置高速缓冲区已修改标志
brelse(bh);//释放该高速缓冲区
iput(dir);//释放目录的i 节点
*res_inode = inode;//返回新目录项的i 节点指针
return 0; } inr = de->inode;// 若上面在目录中取文件名对应的目录项结构操作成功,取出该目录项的i 节点号
dev = dir->i_dev;//和其所在的设备号,这时的“该目录”是指要打开文件的上一级“目录”节点
brelse(bh); iput(dir); if (flag & O_EXCL)// 如果独占使用标志O_EXCL 置位,则返回文件已存在出错码,退出
return -EEXIST; if (!(inode=iget(dev,inr)))// 取设备上该目录项对应i 节点的操作,如果失败则返回访问出错码,退出
return -EACCES; if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || !permission(inode,ACC_MODE(flag))) {// 如果取该目录项对应i 节点的操作失败,则返回访问出错码,退出节点,返回访问权限出错码,退出
iput(inode); return -EPERM; } inode->i_atime = CURRENT_TIME;// 最后访问时间
if (flag & O_TRUNC) truncate(inode);// 如果设立了截0 标志,则将该i 节点的文件长度截为0
*res_inode = inode;// 最后返回该目录项i 节点的指针,并返回0(成功)
return 0; }
int sys_mknod(const char * filename, int mode, int dev)// 系统调用函数 - 创建一个特殊文件或普通文件节点(node)
{//参数:filename - 路径名;mode - 指定使用许可以及所创建节点的类型;dev - 设备号
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(filename,&namelen,&basename)))//函数返回指定路径中最顶层目录的i 节点指针,以及最顶层的名称
return -ENOENT; if (!namelen) {// 如果最顶端的文件名长度为0,则说明给出的路径名最后没有指定文件名,
iput(dir);//释放该目录i 节点,返回
return -ENOENT; } if (!permission(dir,MAY_WRITE)) {// 如果在该目录中没有写的权限,
iput(dir);//则释放该目录的i 节点,返回
return -EPERM; } bh = find_entry(&dir,basename,namelen,&de);// 查找指定目录和文件名的目录项,返回:本目录项的高速缓冲区指针,
if (bh) {//如果栫 在则不能创建同名的I 节点
brelse(bh); iput(dir); return -EEXIST; } inode = new_inode(dir->i_dev);// 申请一个新的i 节点
if (!inode) {//如果不成功,则释放目录的i 节点
iput(dir); return -ENOSPC; } inode->i_mode = mode;// 文件类型和属性(rwx 位)。
if (S_ISBLK(mode) || S_ISCHR(mode))//如果要创建的是块设备文件或者是字符设备文件,
inode->i_zone[0] = dev;//则令i 节点的直接块指针0 等于设备号
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; iput(inode); return -ENOSPC; } de->inode = inode->i_num;//令该目录项的i 节点字段等于新i 节点号
bh->b_dirt = 1;//置高速缓冲区已修改标志
iput(dir);//释放目录
iput(inode);//新的i 节点
brelse(bh);//释放高速缓冲区
return 0;//最后返回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)))//函数返回指定路径中最顶层目录的i 节点指针,以及最顶层的名称
return -ENOENT; if (!namelen) {// 如果最顶端的文件名长度为0,则说明给出的路径名最后没有指定文件名,释放该目录i 节点
iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) {// 如果在该目录中没有写的权限,则释放该目录的i 节点,返回访问许可出错码
iput(dir); return -EPERM; } bh = find_entry(&dir,basename,namelen,&de);// 查找指定目录和文件名的目录项,返回:本目录项的高速缓冲区指针,
if (bh) {//如果栫 在则不能创建同名的I 节点
brelse(bh); iput(dir); return -EEXIST; } inode = new_inode(dir->i_dev);// 申请一个新的i 节点
if (!inode) {//如果不成功,则释放目录的i 节点
iput(dir); return -ENOSPC; } inode->i_size = 32;//置该新i 节点对应的文件长度为32(一个目录项的大小)
inode->i_dirt = 1;//置节点已修改标志
inode->i_mtime = inode->i_atime = CURRENT_TIME;//以及节点的修改时间和访问时间
if (!(inode->i_zone[0]=new_block(inode->i_dev))) {// 为该i 节点申请一磁盘块,并令节点第一个直接块指针等于该块号
iput(dir);// 释放一个dir 节点
inode->i_nlinks--;// 文件目录项链接数。
iput(inode);// 释放一个inode节点
return -ENOSPC; } inode->i_dirt = 1;// 置该新的i 节点已修改标志
if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) {// 读新申请的磁盘块
iput(dir);// 释放一个dir 节点
free_block(inode->i_dev,inode->i_zone[0]);// 释放设备dev 上数据区中的逻辑块block
inode->i_nlinks--;// 文件目录项链接数。
iput(inode);// 释放一个inode节点
return -ERROR; } de = (struct dir_entry *) dir_block->b_data;// 令de 指向目录项数据块,,
de->inode=inode->i_num;//置该目录项的i 节点号字段等于新申请的i 节点号
strcpy(de->name,".");//名字字段等于"."
de++;//然后de 指向下一个目录项结构
de->inode = dir->i_num;//该结构用于存放上级目录的节点号
strcpy(de->name,"..");//名字字段等于".."
inode->i_nlinks = 2;// 文件目录项链接数为2
dir_block->b_dirt = 1;/* 0-clean,1-dirty *///修改标志0为未修改,1为修改
brelse(dir_block);// 本任务释放指定的高速缓冲块区
inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);// 初始化设置新i 节点的模式字段
inode->i_dirt = 1;//置该i 节点已修改标志
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; } de->inode = inode->i_num;//令该目录项的i 节点字段等于新i 节点号
bh->b_dirt = 1;//置该目录项高速缓冲区已修改标志
dir->i_nlinks++;// 文件目录项链接数。
dir->i_dirt = 1;// 已修改(脏)标志。
iput(dir);//释放一个i 节点
iput(inode);//释放一个i 节点
brelse(bh);// 本任务释放指定的高速缓冲块区
return 0; }
/* * routine to check that the specified directory is empty (for rmdir) */ static int empty_dir(struct m_inode * inode)// 检查指定目录是否是空的
{ int nr,block; int len; struct buffer_head * bh; struct dir_entry * de;
len = inode->i_size / sizeof (struct dir_entry);// 计算指定目录中现有目录项的个数(应该起码有2 个,即"."和".."两个文件目录项)
if (len<2 || !inode->i_zone[0] || !(bh=bread(inode->i_dev,inode->i_zone[0]))) {// 如果目录项个数少于2 个或者该目录i 节点的第1 个直接块没有指向任何磁盘块号,或者相应磁盘块读不出,则显示警告信息“设备dev 上目录错”,返回0(失败)
printk("warning - bad directory on dev %04x\n",inode->i_dev); return 0; } de = (struct dir_entry *) bh->b_data;// 让de 指向含有读出磁盘块数据的高速缓冲区中第1 项目录项
if (de[0].inode != inode->i_num || !de[1].inode || strcmp(".",de[0].name) || strcmp("..",de[1].name)) {// 如果第1 个目录项的i 节点号字段值不等于该目录的i 节点号,或者第2 个目录项的i 节点号字段 为零,或者两个目录项的名字字段不分别等于"."和"..",则显示出错警告信息“设备dev 上目录错"
printk("warning - bad directory on dev %04x\n",inode->i_dev); return 0; } nr = 2;// 令nr 等于目录项序号
de += 2;//de 指向第三个目录项
while (nr<len) {// 循环检测该目录中所有的目录项(len-2 个),看有没有目录项的i 节点号字段不为0(被使用)。
if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {// 如果该块磁盘块中的目录项已经检测完,则释放该磁盘块的高速缓冲区,读取下一块含有目录项的 磁盘块
brelse(bh); block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK);//inode节点,nr/DIR_ENTRIES_PER_BLOCK文件块号,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; } if (de->inode) {// 如果该目录项的i 节点号字段不等于0,则表示该目录项目前正被使用,则说明存在除"."和".."之外的第三个目录项,则说明不为空,返回0
brelse(bh); return 0; } de++; nr++; } brelse(bh);// 到这里说明该目录中没有找到已用的目录项(当然除了头两个以外),则释放缓冲区,返回1
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)))//函数返回指定路径中最顶层目录的i 节点指针,以及最顶层的名称
return -ENOENT; if (!namelen) {// 如果最顶端的文件名长度为0,则说明给出的路径名最后没有指定文件名,释放该目录i 节点,返回
iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) {// 如果在该目录中没有写的权限,则释放该目录的i 节点,返回访问许可出错码
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))) {// 取该目录项指明的i 节点
iput(dir); brelse(bh); return -EPERM; } if ((dir->i_mode & S_ISVTX) && current->euid && inode->i_uid != current->euid) {// 若该目录设置了受限删除标志并且进程的有效用户id 不等于该i 节点的用户id,则表示没有权限删除该目录
iput(dir); iput(inode); brelse(bh); return -EPERM; } if (inode->i_dev != dir->i_dev || inode->i_count>1) {// 如果要被删除的目录项的i 节点的设备号不等于包含该目录项的目录的设备号,或者该被删除目录的引用连接计数大于1(表示有符号连接等),则不能删除该目录
iput(dir); iput(inode); brelse(bh); return -EPERM; } if (inode == dir) {//如果要被删除目录的目录项i 节点的节点号等于包含该需删除目录的i 节点号,则表示试图删除"." /* we may not delete ".", but "../dir" is ok */
iput(inode); iput(dir); brelse(bh); return -EPERM; } if (!S_ISDIR(inode->i_mode)) {// 若要被删除的目录的i 节点的属性表明这不是一个目录
iput(inode); iput(dir); brelse(bh); return -ENOTDIR; } if (!empty_dir(inode)) {// 若该需被删除的目录不空
iput(inode); iput(dir); brelse(bh); return -ENOTEMPTY; } if (inode->i_nlinks != 2)// 若该需被删除目录的i 节点的连接数不等于2,
printk("empty directory has nlink!=2 (%d)",inode->i_nlinks); de->inode = 0;// 置该需被删除目录的目录项的i 节点号字段为0,表示该目录项不再使用
bh->b_dirt = 1;//并置含有该目录项的高速缓冲区已修改标志,
brelse(bh);//并释放该缓冲区
inode->i_nlinks=0;// 置被删除目录的i 节点的连接数为0,
inode->i_dirt=1;//并置i 节点已修改标志
dir->i_nlinks--;// 将包含被删除目录名的目录的i 节点引用计数减1
dir->i_ctime = dir->i_mtime = CURRENT_TIME;//修改其改变时间和修改时间为当前时间
dir->i_dirt=1;//置该节点已修改标志
iput(dir);// 最后释放包含要删除目录名的目录i 节点
iput(inode);//和该要删除目录的i 节点
return 0;//返回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)))//函数返回指定路径中最顶层目录的i 节点指针,以及最顶层的名称
return -ENOENT; if (!namelen) {// 如果最顶端的文件名长度为0,则说明给出的路径名最后没有指定文件名,释放该目录i 节点,返回
iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) {// 如果在该目录中没有写的权限,则释放该目录的i 节点,返回访问许可出错码,退出
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))) {// 取该目录项指明的i 节点
iput(dir); brelse(bh); return -ENOENT; } if ((dir->i_mode & S_ISVTX) && !suser() && current->euid != inode->i_uid && current->euid != dir->i_uid) {// 如果该目录设置了受限删除标志并且用户不是超级用户,并且进程的有效用户id 不等于被删除文件 名i 节点的用户id,并且进程的有效用户id 也不等于目录i 节点的用户id,则没有权限删除该文件
iput(dir); iput(inode); brelse(bh); return -EPERM; } if (S_ISDIR(inode->i_mode)) {// 如果该指定文件名是一个目录,则也不能删除
iput(inode); iput(dir); brelse(bh); return -EPERM; } if (!inode->i_nlinks) {// 如果该i 节点的连接数已经为0,则显示警告信息,修正其为1。
printk("Deleting nonexistent file (%04x:%d), %d\n", inode->i_dev,inode->i_num,inode->i_nlinks); inode->i_nlinks=1; } de->inode = 0;//将该文件名的目录项中的i 节点号字段置为0,表示释放该目录项
bh->b_dirt = 1;//设置包含该目录项的缓冲区已修改标志,
brelse(bh);//释放该高速缓冲区
inode->i_nlinks--;// 该i 节点的连接数减1
inode->i_dirt = 1;//置已修改标志
inode->i_ctime = CURRENT_TIME;//更新改变时间为当前时间
iput(inode);//最后释放该i 节点
iput(dir);//目录的i 节点
return 0;//成功返回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;
oldinode=namei(oldname);// 根据给定的目录文件名找到对应的i 节点
if (!oldinode) return -ENOENT; if (S_ISDIR(oldinode->i_mode)) {// 如果原路径名对应的是一个目录名,则释放该i 节点,返回出错号
iput(oldinode); return -EPERM; } dir = dir_namei(newname,&namelen,&basename);//函数返回指定路径中最顶层目录的i 节点指针,以及最顶层的名称
if (!dir) {//如果目录的i 节点没有找到则释放原路径名的i 节点,返回出错号
iput(oldinode); return -EACCES; } if (!namelen) {//// 如果新路径名中不包括文件名,则释放原路径名i 节点和新路径名目录的i 节点,返回出错号
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; } de->inode = oldinode->i_num;//初始设置该目录项的i 节点号等于原路径名的i 节点号
bh->b_dirt = 1;//置包含该新添目录项的高速缓冲区已修改标志
brelse(bh);//释放该缓冲区
iput(dir);//释放目录的i 节点
oldinode->i_nlinks++;// 将原节点的应用计数加1
oldinode->i_ctime = CURRENT_TIME;//修改其改变时间为当前时间
oldinode->i_dirt = 1;//并设置i 节点已修改标志
iput(oldinode);//最后释放原路径名的i 节点,并返回0
return 0; }
|