Chinaunix首页 | 论坛 | 博客
  • 博客访问: 566930
  • 博文数量: 137
  • 博客积分: 4040
  • 博客等级: 上校
  • 技术积分: 1584
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-08 13:05
文章分类

全部博文(137)

文章存档

2011年(10)

2010年(23)

2009年(104)

分类: LINUX

2009-08-17 17:15:58

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

阅读(1181) | 评论(0) | 转发(0) |
0

上一篇:supper.c

下一篇:file_table.c

给主人留下些什么吧!~~