现在的位置: 首页 > 综合 > 正文

linux 0.11 内核学习 — namei.c。

2013年04月23日 ⁄ 综合 ⁄ 共 19231字 ⁄ 字号 评论关闭

文件可能比较长,呵呵。

 

/*

 *  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;

}

参考《linux内核完全注释》和网上文章

抱歉!评论已关闭.