📄 namei.c
字号:
int count = 0; /*取出低两位操作标志*/ acc_mode = ACC_MODE(flag); /*若不需创建文件*/ if (!(flag & O_CREAT)) { /*在进程目录文件表搜索已有文件,并把结果拷贝到该nameidata结构*/ if (path_init(pathname, lookup_flags(flag), nd)) error = path_walk(pathname, nd); /*若查找不成功则返回错误信息*/ if (error) return error; /*获取文件dentry结构*/ dentry = nd->dentry; /*执行打开操作,更新inode*/ goto ok; } /*进程文件表中搜索该文件,若不存在则创建*/ if (path_init(pathname, LOOKUP_PARENT, nd)) error = path_walk(pathname, nd); /*查找不成功返回错误信息*/ if (error) return error; /*检测查找结果是否是目录文件,是则返回*/ error = -EISDIR; if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len]) /*跳转到返回程序段*/ goto exit; /*获取文件相关目录数据返回到dentry*/ down(&dir->d_inode->i_sem); /*获得散列值并查找对应dentry结构*/ dentry = lookup_hash(&nd->last, nd->dentry);do_last: /*如果dentry是一个错误值则返回*/ error = PTR_ERR(dentry); if (IS_ERR(dentry)) { /*处理信号量*/ up(&dir->d_inode->i_sem); /*返回*/ goto exit; } /* *若与目录项相关的索引节点不存在则创建*/ if (!dentry->d_inode) { /*VFS系统新建一个inode*/ error = vfs_create(dir->d_inode, dentry, mode & ~current->fs->umask); /*处理信号量*/ up(&dir->d_inode->i_sem); dput(nd->dentry); nd->dentry = dentry; /*创建不成功返回*/ if (error) goto exit; /* 无需检查写权限*/ acc_mode = 0; flag &= ~O_TRUNC; /*执行打开操作*/ goto ok; } /* * *若与目录项相关的索引节点已存在 */ up(&dir->d_inode->i_sem); /*如果指定了O_EXCL和O_CREAT,文件存在时,出错*/ error = -EEXIST; if (flag & O_EXCL) goto exit_dput; /*检测文件是否是链接文件*/ if (d_mountpoint(dentry)) { error = -ELOOP; /*如果指定不遍历链接文件则返回*/ if (flag & O_NOFOLLOW) goto exit_dput; /*检测dentry挂载点*/ while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry)); } error = -ENOENT; /*inode不存在则返回*/ if (!dentry->d_inode) goto exit_dput; /*允许遍历链接文件则找到链接文件对应的文件*/ if (dentry->d_inode->i_op && dentry->d_inode->i_op->follow_link) goto do_link; /*将处理后的dentry复制到nd结构中,并判断其是否是目录,是则返回错误*/ dput(nd->dentry); nd->dentry = dentry; error = -EISDIR; if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) goto exit;ok: /*打开文件返回处理结果代码*/ error = -ENOENT; inode = dentry->d_inode; /*inode为空则返回*/ if (!inode) goto exit; error = -ELOOP; /*若是连接文件则返回*/ if (S_ISLNK(inode->i_mode)) goto exit; error = -EISDIR; /*是目录且仅有写权限则返回*/ if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) goto exit; /*检则该索引节点是否有acc_mode标志*/ error = permission(inode,acc_mode); if (error) goto exit; /* 如果是FIFO文件,则不允许truncate*/ if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { flag &= ~O_TRUNC; } /*如果是设备文件则不允许truncate*/ else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { error = -EACCES; /*设备文件却允许truncate则要返回错误信息*/ if (nd->mnt->mnt_flags & MNT_NODEV) goto exit; flag &= ~O_TRUNC; } else { error = -EROFS; /*如果flag标识和inode权限冲突,则返回错误*/ if (IS_RDONLY(inode) && (flag & 2)) goto exit; } /* * 如果inode只允许append方式写入,则不允许truncate和非append写入方式。 */ error = -EPERM; if (IS_APPEND(inode)) { if ((flag & FMODE_WRITE) && !(flag & O_APPEND)) goto exit; if (flag & O_TRUNC) goto exit; } /* * 若有其它进程使用该文件,则返回 */ error = get_lease(inode, flag); if (error) goto exit; /*若可以truncate*/ if (flag & O_TRUNC) { /*获取一次inode写操作权限*/ error = get_write_access(inode); if (error) goto exit; /*锁定inode*/ error = locks_verify_locked(inode); if (!error) { /*对inode执行配额初始化*/ DQUOT_INIT(inode); /*truncate dentry*/ error = do_truncate(dentry, 0); } /*释放当前写操作权限*/ put_write_access(inode); if (error) goto exit; } else /*如果有写标识,则对inode执行配额初始化*/ if (flag & FMODE_WRITE) DQUOT_INIT(inode); return 0;exit_dput: /*释放dentry*/ dput(dentry);exit: /*释放nameidata结构*/ path_release(nd); return error;do_link: error = -ELOOP; /*不允许遍历链接文件则返回错误*/ if (flag & O_NOFOLLOW) goto exit_dput; /* * This is subtle. Instead of calling do_follow_link() we do the * thing by hands. The reason is that this way we have zero link_count * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT. *link_count为0 * After that we have the parent and last component, i.e. * we are in the same situation as after the first path_walk(). * Well, almost - if the last component is normal we get its copy * stored in nd->last.name and we will have to putname() it when we * are done. Procfs-like symlinks just set LAST_BIND. */ /*更新上次访问文件的时间*/ UPDATE_ATIME(dentry->d_inode); /*调用操作函数集中follow_link函数查找指定的文件*/ error = dentry->d_inode->i_op->follow_link(dentry, nd); /*释放目录项对象*/ dput(dentry); /*若查找有错则返回错误类型*/ if (error) return error; /*若查找成功*/ if (nd->last_type == LAST_BIND) { dentry = nd->dentry; goto ok; } error = -EISDIR; /*若未成功找到目标文件则返回*/ if (nd->last_type != LAST_NORM) goto exit; /*查找到的是目录则返回*/ if (nd->last.name[nd->last.len]) { /*释放内存中nd->last.name*/ putname(nd->last.name); goto exit; } error = -ELOOP; /*若链接过多则释放资源返回*/ if (count++==32) { /*释放内存中nd->last.name*/ putname(nd->last.name); goto exit; } dir = nd->dentry; /*获取文件相关目录数据返回到dentry*/ down(&dir->d_inode->i_sem); dentry = lookup_hash(&nd->last, nd->dentry); putname(nd->last.name); goto do_last;}/*在给定目录下查找指定类型文件所对应的目录项对象未找到则创建 *查找或创建所得返回dentry地址*/static struct dentry *lookup_create(struct nameidata *nd, int is_dir){ struct dentry *dentry; /*处理信号量*/ down(&nd->dentry->d_inode->i_sem); dentry = ERR_PTR(-EEXIST); /*未找到目标文件则fail返回*/ if (nd->last_type != LAST_NORM) goto fail; /*生成散列值,并据该散列值查找相应的dentry结构,若未找到则创建该dentry*/ dentry = lookup_hash(&nd->last, nd->dentry); /*如果出错则返回*/ if (IS_ERR(dentry)) goto fail; /*找到文件类型与传入参数所指类型不匹配则释放dentry*/ if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode) goto enoent; return dentry;enoent: /*释放dentry资源*/ dput(dentry); /*返回值置入错误信息*/ dentry = ERR_PTR(-ENOENT);fail: return dentry;}/*在给定目录下,为与目录项相关的特殊文件创建一个新的磁盘索节点结点*/int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev){ int error = -EPERM; /*处理信号量*/ down(&dir->i_zombie); /*若是字符设备或块设备或不可建立设备文件节点则跳转到返回段*/ if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) goto exit_lock; /*若不可以创建则跳转到返回段*/ error = may_create(dir, dentry); if (error) goto exit_lock; error = -EPERM; /*索引操作中无mknod操作则跳转到返回段*/ if (!dir->i_op || !dir->i_op->mknod) goto exit_lock; /*磁盘配额*/ DQUOT_INIT(dir); /*上锁*/ lock_kernel(); /*在dir目录下,为与目录项相关的特殊文件创建一个新的磁盘索引节点*/ error = dir->i_op->mknod(dir, dentry, mode, dev); /*解锁*/ unlock_kernel();exit_lock: /*处理信号量*/ up(&dir->i_zombie); /*文件dir中的i_dnotify_mask定义为DN_RENAME重命名*/ if (!error) inode_dir_notify(dir, DN_CREATE); return error;}/*为特殊文件创建一个新的磁盘索引节点 */asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev){ int error = 0; char * tmp; struct dentry * dentry; struct nameidata nd; /*若是目录文件则返回错误信息*/ if (S_ISDIR(mode)) return -EPERM; /*分配内存空间*/ tmp = getname(filename); /*分配内存空间有错则返回*/ if (IS_ERR(tmp)) return PTR_ERR(tmp); /*在内存中找到或建立代表着目标文件或目录dentry结构和inode结构由old_nd反回搜索结果*/ if (path_init(tmp, LOOKUP_PARENT, &nd)) error = path_walk(tmp, &nd); if (error) goto out; /*查找指定类型文件所对应的目录项对象*/ dentry = lookup_create(&nd, 0); error = PTR_ERR(dentry); /*重设文件类型*/ mode &= ~current->fs->umask; /*若查找dentry成功*/ if (!IS_ERR(dentry)) { switch (mode & S_IFMT) { case 0: case S_IFREG: error = vfs_create(nd.dentry->d_inode,dentry,mode); break; case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: error = vfs_mknod(nd.dentry->d_inode,dentry,mode,dev); break; case S_IFDIR: error = -EPERM; break; default: error = -EINVAL; } dput(dentry); } up(&nd.dentry->d_inode->i_sem); path_release(&nd);out: putname(tmp); return error;}/*在某个目录下,为与目录项对象相关的目录创建一个新的索引节点 *创建成功则返回0,不成功则返回错误信息*/int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode){ int error; /*处理信号量*/ down(&dir->i_zombie); /*判断是否可以创建节点*/ error = may_create(dir, dentry); /*若有错,则跳转到返回段*/ if (error) goto exit_lock; error = -EPERM; /*若索引节点操作中无mkdir操作则跳转到返回段*/ if (!dir->i_op || !dir->i_op->mkdir) goto exit_lock; /*磁盘配额*/ DQUOT_INIT(dir); /*????*/ mode &= (S_IRWXUGO|S_ISVTX); /*上锁*/ lock_kernel(); /*在当前给定目录下,为与目录项对象相关的符号链创建一个新的索引节点*/ error = dir->i_op->mkdir(dir, dentry, mode); /*解锁*/ unlock_kernel();exit_lock: /*处理信号量*/ up(&dir->i_zombie); /*文件dir中的i_dnotify_mask定义为DN_RENAME重命名*/ if (!error) inode_dir_notify(dir, DN_CREATE); return error;}/*为相关的目录创建一个新的索引节点*/asmlinkage long sys_mkdir(const char * pathname, int mode){ int error = 0; char * tmp; /*分配内存空间*/ tmp = getname(pathname); error = PTR_ERR(tmp); /*分配空间成功*/ if (!IS_ERR(tmp)) { struct dentry *dentry; struct nameidata nd; /*在内存中找到或建立代表着目标文件或目录dentry结构和inode结构由old_nd反回搜索结果*/ if (path_init(tmp, LOOKUP_PARENT, &nd)) error = path_walk(tmp, &nd); if (error) goto out; /*查找指定类型文件所对应的目录项对象*/ dentry = lookup_create(&nd, 1); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { /*在某个目录下,为与目录对象相关的目录创建一个新的索引节点*/ error = vfs_mkdir(nd.dentry->d_inode, dentry, mode & ~current->fs->umask); /*释放dentry资源*/ dput(dentry); } /*处理信号量*/ up(&nd.dentry->d_inode->i_sem); /*释放资源*/ path_release(&nd);out: putname(tmp); } return error;}/*若当前dentry有两个用户时则可以提前抛弃它, *撤消其哈希值使得cache lookup无法查找到该子目录 *底层文件系统通过这样的语句调用该函数 *if (!d_unhashed(dentry)) * return -EBUSY; * We try to drop the dentry early: we should have * a usage count of 2 if we're the only user of this * dentry, and if that is true (possibly after pruning * the dcache), then we drop the dentry now. * * A low-level filesystem can, if it choses, legally * do a
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -