📄 namei.c
字号:
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// 个目录项还未使用,我们可以使用它。于是对该目录项进行设置(置该目录项的i 节点指针为空)。并// 更新该目录的长度值(加上一个目录项的长度,设置目录的i 节点已修改标志,再更新该目录的改变时// 间为当前时间。 if (i * sizeof (struct dir_entry) >= dir->i_size) { de->inode = 0; dir->i_size = (i + 1) * sizeof (struct dir_entry); 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++; }// 执行不到这里。也许Linus 在写这段代码时是先复制了上面find_entry()的代码,而后修改的?。 brelse (bh); return NULL;}/** get_dir()** Getdir traverses the pathname until it hits the topmost directory.* It returns NULL on failure.*//** get_dir()* 该函数根据给出的路径名进行搜索,直到达到最顶端的目录。* 如果失败则返回NULL。*///// 搜寻指定路径名的目录。// 参数:pathname - 路径名。// 返回:目录的i 节点指针。失败时返回NULL。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 个字符是'/',则说明路径名是绝对路径名。则从根i 节点开始操作。 if ((c = get_fs_byte (pathname)) == '/') { inode = current->root; pathname++;// 否则若第一个字符是其它字符,则表示给定的是相对路径名。应从进程的当前工作目录开始操作。// 则取进程当前工作目录的i 节点。 } else if (c) inode = current->pwd;// 否则表示路径名为空,出错。返回NULL,退出。 else return NULL; /* empty name is bad *//* 空的路径名是错误的 */// 将取得的i 节点引用计数增1。 inode->i_count++; while (1) {// 若该i 节点不是目录节点,或者没有可进入的访问许可,则释放该i 节点,返回NULL,退出。 thisname = pathname; if (!S_ISDIR (inode->i_mode) || !permission (inode, MAY_EXEC)) { iput (inode); return NULL; }// 从路径名开始起搜索检测字符,直到字符已是结尾符(NULL)或者是'/',此时namelen 正好是当前处理// 目录名的长度。如果最后也是一个目录名,但其后没有加'/',则不会返回该最后目录的i 节点!// 比如:/var/log/httpd,将只返回log/目录的i 节点。 for (namelen = 0; (c = get_fs_byte (pathname++)) && (c != '/'); namelen++)/* nothing */ ;// 若字符是结尾符NULL,则表明已经到达指定目录,则返回该i 节点指针,退出。 if (!c) return inode;// 调用查找指定目录和文件名的目录项函数,在当前处理目录中寻找子目录项。如果没有找到,则释放// 该i 节点,并返回NULL,退出。 if (!(bh = find_entry (&inode, thisname, namelen, &de))) { iput (inode); return NULL; }// 取该子目录项的i 节点号inr 和设备号idev,释放包含该目录项的高速缓冲块和该i 节点。 inr = de->inode; idev = inode->i_dev; brelse (bh); iput (inode);// 取节点号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()* dir_namei()函数返回指定目录名的i 节点指针,以及在最顶层目录的名称。*/// 参数:pathname - 目录路径名;namelen - 路径名长度。// 返回:指定目录名最顶层目录的i 节点指针和最顶层目录名及其长度。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;// 对路径名pathname 进行搜索检测,查处最后一个'/'后面的名字字符串,计算其长度,并返回最顶// 层目录的i 节点指针。 basename = pathname; while (c = get_fs_byte (pathname++)) if (c == '/') basename = pathname; *namelen = pathname - basename - 1; *name = basename; return dir;}/** namei()** is used by most simple commands to get the inode of a specified name.* Open, link etc use their own routines, but this is enough for things* like 'chmod' etc.*//** namei()* 该函数被许多简单的命令用于取得指定路径名称的i 节点。open、link 等则使用它们* 自己的相应函数,但对于象修改模式'chmod'等这样的命令,该函数已足够用了。*///// 取指定路径名的i 节点。// 参数:pathname - 路径名。// 返回:对应的i 节点。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 节点,若不存在,则返回NULL,退出。 if (!(dir = dir_namei (pathname, &namelen, &basename))) return NULL;// 如果返回的最顶层名字的长度是0,则表示该路径名以一个目录名为最后一项。 if (!namelen) /* special case: '/usr/' etc */ return dir; /* 对应于'/usr/'等情况 */// 在返回的顶层目录中寻找指定文件名的目录项的i 节点。因为如果最后也是一个目录名,但其后没// 有加'/',则不会返回该最后目录的i 节点!比如:/var/log/httpd,将只返回log/目录的i 节点。// 因此dir_namei()将不以'/'结束的最后一个名字当作一个文件名来看待。因此这里需要单独对这种// 情况使用寻找目录项i 节点函数find_entry()进行处理。 bh = find_entry (&dir, basename, namelen, &de); if (!bh) { iput (dir); return NULL; }// 取该目录项的i 节点号和目录的设备号,并释放包含该目录项的高速缓冲区以及目录i 节点。 inr = de->inode; dev = dir->i_dev; brelse (bh); iput (dir);// 取对应节号的i 节点,修改其被访问时间为当前时间,并置已修改标志。最后返回该i 节点指针。 dir = iget (dev, inr); 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.*//** open_namei()* open()所使用的namei 函数 - 这其实几乎是完整的打开文件程序。*///// 文件打开namei 函数。// 参数:pathname - 文件路径名;flag - 文件打开标志;mode - 文件访问许可属性;// 返回:成功返回0,否则返回出错码;res_inode - 返回的对应文件路径名的的i 节点指针。intopen_namei (const char *pathname, int flag, int mode, struct m_inode **res_inode){ const char *basename; int inr, dev, namelen; struct m_inode *dir, *inode; struct buffer_head *bh; struct dir_entry *de;// 如果文件访问许可模式标志是只读(0),但文件截0 标志O_TRUNC 却置位了,则改为只写标志。 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;// 如果最顶端文件名长度为0(例如'/usr/'这种路径名的情况),那么若打开操作不是创建、截0,// 则表示打开一个目录名,直接返回该目录的i 节点,并退出。 if (!namelen) { /* special case: '/usr/' etc */ if (!(flag & (O_ACCMODE | O_CREAT | O_TRUNC))) { *res_inode = dir; return 0; }// 否则释放该i 节点,返回出错码。 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); return -EACCES; }// 在目录节点对应的设备上申请一个新i 节点,若失败,则释放目录的i 节点,并返回没有空间出错码。 inode = new_inode (dir->i_dev); if (!inode) { iput (dir); return -ENOSPC; }// 否则使用该新i 节点,对其进行初始设置:置节点的用户id;对应节点访问模式;置已修改标志。 inode->i_uid = current->euid; inode->i_mode = mode; inode->i_dirt = 1;// 然后在指定目录dir 中添加一新目录项。 bh = add_entry (dir, basename, namelen, &de);// 如果返回的应该含有新目录项的高速缓冲区指针为NULL,则表示添加目录项操作失败。于是将该// 新i 节点的引用连接计数减1;并释放该i 节点与目录的i 节点,返回出错码,退出。 if (!bh) { inode->i_nlinks--; iput (inode); iput (dir); return -ENOSPC; }// 初始设置该新目录项:置i 节点号为新申请到的i 节点的号码;并置高速缓冲区已修改标志。然后// 释放该高速缓冲区,释放目录的i 节点。返回新目录项的i 节点指针,退出。 de->inode = inode->i_num;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -