linux vfs 路径解析代码注释

linux版本为 v6.7

以chroot修改根目录为例,走一遍流程,重点在path_lookupat的实现。代码按逻辑组织,非真实代码顺序。由于涉及太多细节,每部分的开始会先做一个小结。

chroot

解析路径字符串,逐层进入,检查权限后将current->fs->root改为对应的路径节点,如果逐层进入子路径的过程中遇到ESTALE错误,可能是某个文件子系统的缓存失效了,这时可以尝试标记LOOKUP_REVAL对可以提示子文件系统对缓存项做及时检验,重来一次。

SYSCALL_DEFINE1(chroot, const char __user *, filename):// 进入到路径节点user_path_at();// 检查权限path_permission(&path, MAY_EXEC | MAY_CHDIR);ns_capable(current_user_ns(), CAP_SYS_CHROOT);// selinux 安全模块的 hooksecurity_path_chroot(&path);// 将current->fs->root设置为传入路径对应的节点set_fs_root(current->fs, &path);// 如果路径有问题,可能是一些文件系统的缓存导致的// 设置LOOKUP_REVAL可以在需要验证的地方对缓存项做验证if (retry_estale(error, lookup_flags)) {lookup_flags |= LOOKUP_REVAL;goto retry;}

user_path_at

先生成一个绑定struct audit_names信息的路径字符串节点 struct filename(getname_flags),再一层层进入节点(path_lookupat),并更新节点inode的最新信息(audit_inode)。

user_path_at():getname_flags():// 从缓存读取路径对应的struct filenameaudit_reusename();// 如果没有缓存没有,就取一个预分配的filename对象// 这里接受的路径最长是4k减去一个struct头的大小// 如果路径字符串超过了这个值,但小于4k,// 可以分配一个额外的 struct 头,原对象的4k空间完全用来存字符串...// 存入audit_context->names_list缓存audit_getname();filename_lookup():// 创建nameidata结构,它是用户向内核传参数的媒介set_nameidata();// 尝试进入路径节点,这里有三种尝试// 先尝试RCU方式读,如果并发修改频繁,很可能rcu在中间会读到脏数据,导致失败ECHILD// 再尝试普通读方式,需要加锁,如果访问到一些子文件系统的失效缓存,导致失败ESTALE// 最后尝试加上LOOKUP_REVAL标记,提醒子文件系统,读到缓存后先做一次校验path_lookupat();// 存一份本inode的最新信息audit_inode();// 恢复原来的current->nameidatarestore_nameidata();

path_lookupat

切换工作目录到指定目录,如果它是mount节点,要先解析切换到mount的目录。然后一层层解析路径并进入,解析中发现符号链接时需要以深度遍历的方式先解析符号链接(符号链接的路径也可能有mount节点)。到最后一层时如果发现是个mount节点,且调用者指定LOOKUP_MOUNTPOINT标记,则需要对最后一层节点做mount操作。

最后检查最后进入的位置是否合法,做一下清理工作,并返回。

path_lookupat():// 找到工作路径放在nameidata->path上,有三种可能情况:// 1、根目录:路径以'/'开头// 2、当前目录: nameidata->dfd == AT_FDCWD// 3、指定目录:nameidata->dfd 为真实的文件描述符fd,比如LOOKUP_IS_SCOPED的场景 path_init();// 有LOOKUP_DOWN标记,意味着入口可能是一个mount的节点,要解析一下mounthandle_lookup_down();step_into():// 如果工作路径是挂载节点,则一层层进入挂载点(它也可能只是普通目录)handle_mounts();// 如果遇到符号链接,最多深度链接38次,为它预先分配40个slot// 直到整个流程结束,restore_nameidata时才释放//(原本只有2个slot,存上一级和当前级目录,因为如果是普通目录// 或mount节点的话,每解析一层,就进入对应目录,只需要2个slot)pick_link();// 从工作目录一层层进入到最终节点    link_path_walk();// 有LOOKUP_MOUNTPOINT标记,说明目标节点也是mount的子系统节点,进入mount节点。handle_lookup_down();// 检验路径是否合理,有两方面:// 1、对于LOOKUP_IS_SCOPED,要判断路径是在root节点之后//(这只是兜底逻辑,实际前面每次解析节点都已经做过判断)// 2、如果有DCACHE_OP_WEAK_REVALIDATE标记,还要检验下缓存没过期complete_walk();// 如果因为错误导致的link_path_walk提前返回,则栈上的资源需要释放:// 1、调用所有路径的释放hook,比如有的文件子系统要关闭设备// 2、将栈中所有 path 的引用计数减1if (err)terminate_walk();

link_path_walk

解析路径字符串,每一层以一个或多个'/'结束,每一层可能是点,点点,或普通路径实体。调用walk_component向下走(中间可能遇到三种情况:普通路径实体、符号链接、mount节点)。直到最后一层,不再向下走。最后一层可能是普通实体节点或mount节点,不可能是link节点,并且即使是mount节点也不做解析。

 link_path_walk();while true:// 查看是否有exec权限idmap = mnt_idmap(nd->path.mnt);err = may_lookup(idmap, nd);// 解析下一层的字符串(以‘/’结尾),可能是.(当前目录)..(上层目录),实体目录hash_len = hash_name(nd->path.dentry, name); // 取一级字符串...switch(当前一层路径实体):case 是普通路径实体:// 进入下一层walk_component(WALK_MORE);case 是个 link:// 如果下一层是个 link,则放在栈里,下个循环里先解析link的路径nd->stack[depth++].name = name;name = link;case 走到了一个link的最后一个节点:// link路径走完,则回到上一个栈的位置name = nd->stack[--depth].name;walk_component(0);case 走完了全部节点(*name == nullptr && depth == 0),不再向下走:return 0;// 校验它是目录,不是文件d_can_lookup()

handle_mounts

跳过已经mount好的节点后,对本目录和下层目录拿锁,执行一层层mount挂载(如果是链式挂载的话)。这里涉及三个宏和一个函数需要简单讲一下:

1、DCACHE_MANAGE_TRANSIT:从这个文件找子目录需要特别操作,比如等待其它的依赖mount完成等。

2、DCACHE_MOUNTED:mount已经完成,在缓存的mount_hashtable哈希表中会有记录。

3、DCACHE_NEED_AUTOMOUNT:需要在第一次访问时自动挂载。

4、try_to_unlazy_next与try_to_unlazy:mount操作是需要排它执行的,虽然前面读可以用rcu,但要做mount操作的话,还是要对节点拿锁,锁的节点涉及符号链接、根目录、当前进入的目录、mount节点。加next的函数与不加的区别在于,加next的需要锁本层与下层目录,不加只锁本层目录。

handle_mounts():// 跳过普通目录或已经mount好的节点__follow_mount_rcu():// 如果节点无需要auto mount直接返回,需要的话,先验证目录有效,加锁,才进一步mount// (验证link, mnt,path,root依然有效,则增加锁引用,这是退出rcu模式前要做的)try_to_unlazy_next();// 一层层进入挂载点执行mount操作(follow_automount)traverse_mounts();/* --------------------------------------- */// 跳过普通目录或已经mount好的节点
__follow_mount_rcu():while true:// 从这个文件找子目录需要特别操作//(比如等待其它的依赖mount完成,如果rcu访问的话不用等)if (unlikely(flags & DCACHE_MANAGE_TRANSIT)) {int res = dentry->d_op->d_manage(path, true);if (res)return res == -EISDIR; // 它只是个普通的目录文件}// 检查是否已经mounted,如果是,则进入这个目录if (flags & DCACHE_MOUNTED) {struct mount *mounted = __lookup_mnt(path->mnt, dentry);if (mounted) {path->dentry = dget(mounted->mnt_root); continue; // 继续查这个被mount的目录是否需要mount}}// 没有mount过,返回是否无需auto mountreturn !(flags & DCACHE_NEED_AUTOMOUNT);/* --------------------------------------- */// 一层层mount,这里省略了修改mnt计数相关逻辑
__traverse_mounts():while (flags & DCACHE_MANAGED_DENTRY) {// 首先调用 manage hook做准备(比如等其它依赖完成)if (flags & DCACHE_MANAGE_TRANSIT) {ret = path->dentry->d_op->d_manage(path, false);if (res)return res; // 它只是个普通的目录文件}// 检查是否已经mounted,如果是,则进入这个目录if (flags & DCACHE_MOUNTED) {struct mount *mounted = __lookup_mnt(path->mnt, dentry);if (mounted) {path->dentry = dget(mounted->mnt_root); continue; // 继续查这个被mount的目录是否需要mount}}// 无需 auto mount,可直接返回// 目录没有被mount,但有可能不需要auto mount,// 或auto mount 发现是个普通目录,则找到这里就可以返回了if (!(flags & DCACHE_NEED_AUTOMOUNT) || follow_automount() == -EISDIR) {return 0;}}

pick_link

默认的栈只有两层,用来存平层与上一层,但link的情况导致可能栈会有多层,举例比如:

用户输入 /a/b/c 目录,解析到b时,栈顶0为空,但发现b是link到了 /a2/b2/c2 目录,于是栈变成了

0:b

走到c2发现它link到了/a3/b3/c3,则栈变成了

0:b,1:c2

如果link没填充过,在填充link时要先拿锁退出rcu。

pick_link返回的是link的路径字符串,如果开始是'/',则要先跳到根节点,再返回后面的字符串。

pick_link// 默认的栈只有两层,但link的情况导致可能栈会有多层,// 要额外分配,在回到terminate_walk时才释放栈reserve_stack():// 先以rcu模式分配MAXSYMLINKS(40)个栈nd_alloc_stack();// 如果不成功,加锁后再重新分配legitimize_path();nd_alloc_stack();// 如果开启sysctl_protected_symlinks,则做检查权限may_follow_link():// 三种情况有权限:// 1、用户是link所有者// 2、父路径没有设置只有目录所有者和具有写权限的用户可以删除或重命名该目录中的文件// (S_ISVTX: 只目录所有者,S_IWOTH:只有写权限的用户)// 3、自己是父目录的所有者...// 如果失败,则读审计日志audit_inode() // 获取inode信息audit_log_path_denied("follow_link") // 记follow_link错误日志// 更新访问时间atime_needs_update// selinux安全子模块的 hooksecurity_inode_follow_link();// 如果有inode->i_link直接用,没有则获取try_to_unlazy();inode->i_op->get_link();// 如果链接字符串是'/',则跳到根节点,并返回link后面的字符串nd_jump_root();

audit_inode

找到需要审计的规则,删除因为路径上inode节点变化导致的已过时的审计信息chunk,更新最新的路径审计信息。

audit_inode():// 遍历audit_filter_list[AUDIT_FILTER_FS]中的每个规则,// 找到审计的inode满足规则涉及的field,如果规则上指示的是AUDIT_NEVER,// 则无需存这个inode的缓存。list_for_each_entry_rcu(e, list, list) {for (i = 0; i < e->rule.field_count; i++) if (audit_comparator(inode->i_sb->s_magic,f->op, f->val))return;}// 如果已经有审计项,且发生过rename或drop,则后面的项可以清理掉,然后更新这个缓存handle_path();audit_copy_inode();// 如果没有就分配,然后更新这个缓存audit_alloc_name();handle_path();audit_copy_inode();/* ----------------------- */// 如果路径上有节点变了,需要更新审计节点。
handle_path():
retry: // 找到drop的path或rename的path的审计块,做清理(audit_put_chunk)while true:// 一层层向上找inode,查看inode是否有审计标记i_fsnotify_marksif (inode && unlikely(inode->i_fsnotify_marks)) {// 有的话,说明关心这个inode的审计,找出审计块chunk = audit_tree_lookup(inode);if (chunk) {if (unlikely(!put_tree_ref(context, chunk))) {audit_put_chunk(drop);unroll_tree_refs():for(节点后面的每个chunk) audit_put_chunk();goto retry;}}parent = d->d_parent;if (parent == d) {// 发生了renameif (read_seqretry(&rename_lock, seq)) {unroll_tree_refs():for(节点后面的每个chunk) audit_put_chunk();goto retry;}return; // 正常扫描到了根节点}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/220940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C/C++ 表达式求值(含多位数)

个人主页&#xff1a;仍有未知等待探索_C语言疑难,数据结构,算法-CSDN博客 专题分栏&#xff1a;算法_仍有未知等待探索的博客-CSDN博客 目录 一、前言 二、解析 分析 最后直接上代码&#xff01; 一、前言 表达式求值是一个比较基础的代码关于栈的使用。在写的时候充分锻炼…

WEB 3D技术 以vue3+vite环境为例 讲解vue项目中使用three

上文 WEB 3D 技术&#xff0c;通过node环境创建一个three案例 中 我们打造了自己的第一个Web 3D界面 那么 今天 我们就来结合vue来开发我们的3D界面 这里 我们先创建一个文件夹 作为文件目录 千万不要放C盘 我们 依旧是在终端执行命令 npm init vitelatest输入一下项目名称 …

同城线下社交搭子,同城圈子交友系统

简介:打破传统耗时耗力的交友模式&#xff0c;实现1对1,点对点的快速即时交友模式&#xff0c;线上线下 整合&#xff0c;可在线查看状态以及距离远近&#xff0c;可自行设置每单的收益提成以及代理的分佣提成。 结构: TINKPHP框架 公众号H5;系统开源&#xff0c;方便二次开发…

自动机器学习是什么?概念及应用

自动机器学习 (Auto Machine Learning) 的应用和方法 随着众多企业在大量场景中开始采用机器学习&#xff0c;前后期处理和优化的数据量及规模指数级增长。企业很难雇用充足的人手来完成与高级机器学习模型相关的所有工作&#xff0c;因此机器学习自动化工具是未来人工智能 (A…

【INTEL(ALTERA)】 quartus版本 21使用SDI II IP出现错误:无法生成示例设计example_design

项目场景&#xff1a; quartus版本 21SDI II FPGA IP 设计示例生成失败怎么办 原因分析&#xff1a; 适用于 Windows* 的英特尔 Quartus Prime Pro Edition 软件版本 21.3 和版本 21.4 以及英特尔 Quartus Prime Standard Edition 软件版本 22.1 中存在问题&#xff0c;SDI I…

ICC2:low power与pg strategy(pg_macro_conn_pattern)

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 创建hard macro上的stripe,参考示例: set pd_list{{DEFAULT_VA VDD_DIG VDD_DIG VSS} {PD_DSP VDD_DIG VDD_DSP VSS} } ;#两个电源域,DEFAULT_VA和PD_DSP是对应voltage area名字,其中DEFAULT_…

机器学习可重复性危机下,创建复杂数据系统的挑战

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 数据科学系统已成为众多研究领域的关键性工具&#xff0c;其开发者群体呈现出多元化的背景特征。在过去十年中&#xff0c;尽管数据科学与机器学习的强…

[论文笔记] 大模型主流Benchmark测试集介绍

自然语言处理(NLP)的进步往往通过在各种benchmark测试集上的表现来衡量。随着多语言和跨语言NLP研究的兴起,越来越多的多语言测试集被提出以评估模型在不同语言和文化背景下的泛化能力。在这篇文章中,我们将介绍几个主流的多语言NLP benchmark测试集,包括ARC Challenge、H…

Android hwcomposer服务启动流程

Android hwcomposer服务启动流程 客户端 binder远程调用 服务端 surfaceflinger --binder--> hwcomposer .hal文件编译时生成支持binder进程间远程调用通信的cpp文件 在out/soong/.intermediates/hardware/interfaces/graphics/composer/2.1/ 目录下找…

测试用例设计方法:功能图

1 引言 前面几篇文章为我们讲述了因果图、判定表、正交试验等几种方法&#xff0c;主要是针对于不同条件输入输出的组合进行测试&#xff0c;但在实际需求中&#xff0c;我们也常会遇到需要对被测对象的状态流转进行验证的情况&#xff0c;此时前面几种方法将不再适用&#xf…

005 本地安全策略

一、本地安全策略 1、概念 主要是对登录计算机的账户进行一些安全设置主要影响是本地计算机安全设置 2、打开方式 开始菜单->管理工具->本地安全策略使用命令secpol.msc从本地组策略进去&#xff0c;使用命令gpedit.msc 二、账户策略 1、密码策略&#xff08;默认情…

Java安全工具Jar包加密

jar包加密有很多种方式&#xff0c;我这边记录一下使用mavenClassFinal的方式,classFinal可以通过jar包/maven的方式来使用&#xff0c;因为maven使用较为简单&#xff0c;我仅记录使用maven的方式 在需要打包的启动程序的pom文件中添加如下plugin 1.plugin需要加在spring-boot…

Windows本地的RabbitMQ服务怎么在Docker for Windows的容器中使用

1. 进入管理界面 windows安装过程请访问&#xff1a;Windows安装RabbitMQ、添加PHP的AMQP扩展 浏览器访问&#xff1a;http://127.0.0.1:15672/ 2. 创建虚拟主机 上面访问的是 RabbitMQ 的管理界面&#xff0c;可以在这个界面上进行一些操作&#xff0c;比如创建虚拟主机、…

P1000 超级玛丽游戏

题目背景 本题是洛谷的试机题目&#xff0c;可以帮助了解洛谷的使用。 建议完成本题目后继续尝试 P1001、P1008。 另外强烈推荐新用户必读贴 题目描述 超级玛丽是一个非常经典的游戏。请你用字符画的形式输出超级玛丽中的一个场景。 ******** ************ …

找到字符串中所有字母异位词-中等

leetcode ****1111 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 示例 1:输入: s "cbaebaba…

CSS彩色发光液体玻璃

效果展示 CSS 知识点 animation 综合运用animation-delay 综合运用filter 的 hue-rotate 属性运用 页面整体布局 <section><div class"glass" style"--i: 1"><div class"inner"><div class"liquid"></d…

LeetCode---374周赛

题目列表 2951. 找出峰值 2952. 需要添加的硬币的最小数量 2953. 统计完全子字符串 2954. 统计感冒序列的数目 一、找到峰值 这个简单的模拟&#xff0c;代码如下 class Solution { public:vector<int> findPeaks(vector<int>& mountain) {int nmountain…

Springboot整合阿里云短信服务

目录 1.注册登录用户 2.点击AccessKey管理&#xff0c;开通使用子用户AccessKey 2.1点击进入AccessKey管理 2.2点击用户创建用户 2.3选择控制台创建 2.4权限修改 3.短信服务 4.创建Springboot项目使用SDK 4.1创建一个springboot项目 4.2导入阿里云短信Maven依赖 4.3…

N体问题-MATLAB 中的数值模拟

一、说明 万有引力是宇宙普适真理&#xff0c;当计算两个物体的引力、质量、距离的关系是经典万有引力物理定律&#xff0c;然而面向复杂问题&#xff0c;比如出现三个以上物体的相互作用&#xff0c;随时间的运动力学&#xff0c;这种数学模型将是更高级的思维方法。本文将阐述…

gin使用自签名SSL证书与自签名证书不受信任方法解决

文章目录 1. X.509 V3证书介绍2、使用openssl生成自签名证书和解决不受信任问题2.1、生成根证书2.2、为域名生成证书申请文件2.3、为域名创建证书的扩展描述文件2.4、为域名创建证书 3、Go应用中使用自签名证书3.1、gin框架调用实现3.2、运行效果 4、使用java的bouncycastle生成…