Linux 内核文件系统dentry_path_raw函数

news2024/11/15 9:41:22

文章目录

  • 一、简介
    • 1.1 __dentry_path
    • 1.2 prepend_name
    • 1.3 d_path
  • 二、dmeo
  • 参考资料

一、简介

// linux-5.4.18/fs/d_path.c

char *dentry_path_raw(struct dentry *dentry, char *buf, int buflen)
{
	return __dentry_path(dentry, buf, buflen);
}
EXPORT_SYMBOL(dentry_path_raw);

该函数根据给定dentry返回其文件的完整路径。使用EXPORT_SYMBOL导出,可以直接调用。
关于dentry介绍请参考:Linux文件系统 struct dentry 结构体解析

1.1 __dentry_path

/*
 * Write full pathname from the root of the filesystem into the buffer.
 */
static char *__dentry_path(struct dentry *d, char *buf, int buflen)
{
	struct dentry *dentry;
	char *end, *retval;
	int len, seq = 0;
	int error = 0;

	if (buflen < 2)
		goto Elong;

	rcu_read_lock();
restart:
	dentry = d;
	end = buf + buflen;
	len = buflen;
	prepend(&end, &len, "\0", 1);
	/* Get '/' right */
	retval = end-1;
	*retval = '/';
	read_seqbegin_or_lock(&rename_lock, &seq);
	while (!IS_ROOT(dentry)) {
		struct dentry *parent = dentry->d_parent;

		prefetch(parent);
		error = prepend_name(&end, &len, &dentry->d_name);
		if (error)
			break;

		retval = end;
		dentry = parent;
	}
	if (!(seq & 1))
		rcu_read_unlock();
	if (need_seqretry(&rename_lock, seq)) {
		seq = 1;
		goto restart;
	}
	done_seqretry(&rename_lock, seq);
	if (error)
		goto Elong;
	return retval;
Elong:
	return ERR_PTR(-ENAMETOOLONG);
}

函数 __dentry_path,用于将文件系统根目录到给定的 dentry 的完整路径名写入缓冲区。

函数的参数包括要获取路径名的 dentry,存储路径名的缓冲区 buf,以及缓冲区的长度 buflen。

static int prepend(char **buffer, int *buflen, const char *str, int namelen)
{
	*buflen -= namelen;
	if (*buflen < 0)
		return -ENAMETOOLONG;
	*buffer -= namelen;
	memcpy(*buffer, str, namelen);
	return 0;
}
dentry = d;
end = buf + buflen;
len = buflen;
prepend(&end, &len, "\0", 1);
/* Get '/' right */
retval = end-1;
*retval = '/';

缓冲区buf 末尾会有一个斜杠 (/) 和一个空字符 (\0),形成一个以斜杠结尾的字符串,以确保路径名的正确格式。

通过遍历 dentry 的父节点链来构建路径名。循环中,函数调用 prepend_name 函数将每个父节点的名称添加到路径名的前面。同时,函数更新 end 和 len 变量以反映已添加的路径名部分和剩余缓冲区长度。

循环继续,直到遍历到根节点(使用 IS_ROOT 宏判断为真):

#define IS_ROOT(x) ((x) == (x)->d_parent)

在此期间,函数通过调用 prefetch 函数预取父节点,以提高性能。

_dentry_path 函数负责构建文件系统根目录到给定 dentry 对象的完整路径名,并将其存储在提供的缓冲区中。

注意_dentry_path 函数(即dentry_path_raw)对于传入的 buf 缓冲区,是从缓冲区的末尾开始构建dentry 对象的路径名,从buf的末尾往前构建dentry 对象的路径名,因此构建完成后,我们不能使用buf缓冲区来当作路径名,应该使用该函数的返回值来当作路径名,即调用者应该使用返回的指针来使用路径名,而不是传入的缓冲区指针。缓冲区的开头留下 0 字节(如果传入的缓冲区buf初始化为0),而路径名的开始通常会从缓冲区开始的部分字节偏移处开始的。

大概的过程就是,比如目录 /home/user01/c/:

//缓冲区buf末尾添加一个斜杠 (/) 和一个空字符 (\0),形成一个以斜杠结尾的字符串,以确保路径名的正确格式。

					 /\0  
  				   /c/\0
     		/user01/c/\0
	   /home/user01/c/\0
buf    retval
					 / 
  				   /c/
     		/user01/c/
	   /home/user01/c/
buf    retval

应该使用retval作为文件名而不是buf。

这里有一个疑问?如果是获取文件的完整路径,比如文件 /home/user01/c/text.txt,那么需要在text.txt的末尾添加一个斜杠 (/)?

如下图所示:
在这里插入图片描述

1.2 prepend_name

/**
 * prepend_name - prepend a pathname in front of current buffer pointer
 * @buffer: buffer pointer
 * @buflen: allocated length of the buffer
 * @name:   name string and length qstr structure
 *
 * With RCU path tracing, it may race with d_move(). Use READ_ONCE() to
 * make sure that either the old or the new name pointer and length are
 * fetched. However, there may be mismatch between length and pointer.
 * The length cannot be trusted, we need to copy it byte-by-byte until
 * the length is reached or a null byte is found. It also prepends "/" at
 * the beginning of the name. The sequence number check at the caller will
 * retry it again when a d_move() does happen. So any garbage in the buffer
 * due to mismatched pointer and length will be discarded.
 *
 * Load acquire is needed to make sure that we see that terminating NUL.
 */
static int prepend_name(char **buffer, int *buflen, const struct qstr *name)
{
	const char *dname = smp_load_acquire(&name->name); /* ^^^ */
	u32 dlen = READ_ONCE(name->len);
	char *p;

	*buflen -= dlen + 1;
	if (*buflen < 0)
		return -ENAMETOOLONG;
	p = *buffer -= dlen + 1;
	*p++ = '/';
	while (dlen--) {
		char c = *dname++;
		if (!c)
			break;
		*p++ = c;
	}
	return 0;
}

prepend_name 静态函数用于在当前缓冲区指针的前面添加一个路径名。

函数的参数包括一个指向指针的指针 buffer,一个指向整数的指针 buflen,以及一个指向 struct qstr 的指针 name,其中 struct qstr 包含了要添加的路径名的字符串和长度信息。

函数的逻辑如下:
(1)首先,函数通过使用 smp_load_acquire 函数从 name->name 加载字符串指针,确保在读取指针和长度时不会发生竞争条件。

(2)接下来,函数使用 READ_ONCE 宏读取 name->len 的值。尽管会有指针和长度不匹配的情况,但长度不能被信任,因此需要逐字节复制,直到达到指定的长度或找到空字符为止。

(3)然后,函数根据要添加的路径名的长度将缓冲区指针向前移动,并将斜杠 / 添加到缓冲区的开头。

(4)在循环中,函数逐字节复制路径名的字符,直到达到指定的长度或找到空字符为止。

(5)最后,函数返回 0,表示添加路径名的操作成功。

该函数的目的是在当前缓冲区指针的前面添加一个路径名,并更新缓冲区指针和剩余长度。

1.3 d_path

与此函数功能相同的函数有:d_path。

/**
 * d_path - return the path of a dentry
 * @path: path to report
 * @buf: buffer to return value in
 * @buflen: buffer length
 *
 * Convert a dentry into an ASCII path name. If the entry has been deleted
 * the string " (deleted)" is appended. Note that this is ambiguous.
 *
 * Returns a pointer into the buffer or an error code if the path was
 * too long. Note: Callers should use the returned pointer, not the passed
 * in buffer, to use the name! The implementation often starts at an offset
 * into the buffer, and may leave 0 bytes at the start.
 *
 * "buflen" should be positive.
 */
char *d_path(const struct path *path, char *buf, int buflen)
{
	char *res = buf + buflen;
	struct path root;
	int error;

	/*
	 * We have various synthetic filesystems that never get mounted.  On
	 * these filesystems dentries are never used for lookup purposes, and
	 * thus don't need to be hashed.  They also don't need a name until a
	 * user wants to identify the object in /proc/pid/fd/.  The little hack
	 * below allows us to generate a name for these objects on demand:
	 *
	 * Some pseudo inodes are mountable.  When they are mounted
	 * path->dentry == path->mnt->mnt_root.  In that case don't call d_dname
	 * and instead have d_path return the mounted path.
	 */
	if (path->dentry->d_op && path->dentry->d_op->d_dname &&
	    (!IS_ROOT(path->dentry) || path->dentry != path->mnt->mnt_root))
		return path->dentry->d_op->d_dname(path->dentry, buf, buflen);

	rcu_read_lock();
	get_fs_root_rcu(current->fs, &root);
	error = path_with_deleted(path, &root, &res, &buflen);
	rcu_read_unlock();

	if (error < 0)
		res = ERR_PTR(error);
	return res;
}
EXPORT_SYMBOL(d_path);

二、dmeo

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>


static int __init hello_init(void)
{
 
    char *buffer, *path;

    buffer = (char *)__get_free_page(GFP_KERNEL);
    if (!buffer)
        return 0;

    path = dentry_path_raw(current->mm->exe_file->f_path.dentry, buffer, PAGE_SIZE);
    if (IS_ERR(path)){
        return 0;   
    }

    printk("path = %s\n", path);

    free_page((unsigned long)buffer);

    return 0;
}
 
static void __exit hello_exit(void)
{

}
 
module_init(hello_init);
module_exit(hello_exit);
 
MODULE_LICENSE("GPL");

需要注意的是,调用者调用dentry_path_raw应该使用返回的指针来作为路径名,而不是传入的缓冲区指针。路径名通常是从缓冲区的部分字节偏移处开始的,缓冲区的开头部分字节是无意义的。

			/usr/bin/kmod
buffer		path

应当使用返回的指针path来作为路径名,而不是传入的缓冲区指针buffer。

# dmesg -c
path = /usr/bin/kmod

参考资料

Linux 5.4.18

https://blog.csdn.net/qq_42931917/article/details/119803534

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

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

相关文章

语雀停服8小时,P0级事故,故障原因和补偿来了。

昨天互联网圈子里发生了一件大事&#xff0c;那就是语雀的 P0 级事故&#xff0c;前后足足停服了 7 个多小时&#xff0c;放眼整个互联网的发展史&#xff0c;都是相当炸裂的表现。 语雀是技术大牛玉伯在蚂蚁金服内部孵化出来的一个云端知识库&#xff0c;整体的界面非常清爽&…

[SQL开发笔记]IN操作符: 在WHERE子句中规定多个值

上一实例我们在where子句使用(year2022 or year2020)&#xff0c;如果我们需要在WHERE子句中规定多个值呢&#xff1f;这时我们将学习IN操作符 一、功能描述&#xff1a; 在WHERE子句中规定多个值。 二、IN操作符语法详解&#xff1a; IN操作符语法&#xff1a; SELECT col…

【Linux】MAC帧协议 + ARP协议

文章目录 &#x1f4d6; 前言1. 数据链路层2. MAC帧格式3. 再谈局域网4. ARP协议4.1 路由器的转发过程&#xff1a;4.2 ARP协议格式&#xff1a; 5. 如何获得目的MAC地址 &#x1f4d6; 前言 在学完网络层IP协议之后&#xff0c;本章我们将继续向下沉一层&#xff0c;进入到数…

STM32-通用定时器

通用定时器 通用定时器由一个可编程预分频器驱动的16位自动重新加载计数器组成。应用&#xff1a;测量输入的脉冲长度信号&#xff08;输入捕获&#xff09;、产生输出波形&#xff08;输出比较和PWM&#xff09;。 脉冲长度和波形周期可以从几微秒调制到几毫秒&#xff0c;使用…

记录隐藏挖矿木马rcu_tasked的查杀

记录一次项目中挖矿病毒的经历 这是黑客使用的批量蔓延病毒的工具&#xff0c;通过如下脚本 [rootServer .cfg]# cat /home/pischi/.bash_history cd /root/ nvidia-smi;ls -a;cd .cfg;ls -a;wc -l ip ./key 20 -f ip pass 22 "nproc;nvidia-smi;rm -rf .cfg;mkdir .cfg…

怎么做好网络软文推广?媒介盒子为你揭秘

不管是初创公司还是成熟公司&#xff0c;都需要打响品牌知名度&#xff0c;而有些公司在网络推广中的预算不是很高&#xff0c;这个时候就可以利用软文进行推广&#xff0c;今天媒介盒子就来告诉大家&#xff0c;如何写好网络推广软文。 一、 明确推广目标 确定推广目标有助于…

【备考网络工程师】如何备考2023年网络工程师之常见考点篇(1)

文章目录 写在前面涉及知识点1、NSLOOKUP命令设置的几个类型&#xff08;DNS服务器资源记录类型&#xff09;2、结构化综合布线系统的组成3、xDSL及相关概念4、私有地址及A-E类地址4.1 、私有地址4.2 、A-E类地址 总结 写在前面 其实做模拟或真题时候&#xff0c;总是会在关键…

建联合作1000+达人,如何高效管理?

随着社交媒体的发展&#xff0c;达人营销已成为品牌营销重要的方式之一&#xff0c;甚至可以说是必选项。 对于很多品牌商家来说&#xff0c;一次合作几百个不同类型、不同社媒平台的达人&#xff0c;已屡见不鲜。在电商大促前、主推单品爆品时&#xff0c;同时合作上千个达人&…

通过js来实现用身份证号来判断性别和出生年月

html: <input type"text" id"shenfenzhenghao" oninput"hao()" placeholder"证件号"><input type"text" id"xingbie" disabled"disabled" placeholder"性别"><input type&qu…

CVPR 2024 延期!

点击下方卡片&#xff0c;关注“CVer”公众号 AI/CV重磅干货&#xff0c;第一时间送达 点击进入—>【计算机视觉和Transformer】交流群 扫码加入CVer知识星球&#xff0c;可以最快学习到最新顶会顶刊上的论文idea和CV从入门到精通资料&#xff0c;以及最前沿项目和应用&…

『第五章』二见痴心:初识小雨燕(中)

在本篇博文中,您将学到如下内容: 7. 值类型与引用类型8. OOP与POP9. 协议与扩展10. 函数与闭包11. 泛型和宏总结相逢情便深,恨不相逢早 玲珑骰子安红豆,入骨相思知不知。 7. 值类型与引用类型 在 Swift 中我们可以将之前介绍的那些数据类型分为两大类:值类型和引用类型。 …

做好微信私域,一定要知道的5大触点

越来越多人意识到私域的重要性&#xff0c;也有越来越多人在私域中获益。 把用户私有化&#xff0c;只是第一步。 我们还要和用户建立联系&#xff0c;要一直互动。 也就是说&#xff0c;我们必须高频地和用户互动&#xff0c;要不断去触达他们。而触达&#xff0c;就需要触点。…

科大讯飞发布讯飞星火 3.0;开源AI的现状

&#x1f680; 科大讯飞发布讯飞星火 3.0&#xff0c;综合能力超越ChatGPT&#xff08;非GPT-4版&#xff09; 摘要&#xff1a;科大讯飞在2023全球1024开发者节上宣布讯飞星火 3.0正式发布&#xff0c;号称综合能力已超越ChatGPT。据介绍&#xff0c;星火认知大模型 V3.0在文…

2023年中国临床决策支持系统发展趋势分析:综合性决策系统将成市场主流[图]

临床决策支持系统是一种利用人工智能原理与方法为医疗工作者提供合理解决方案的计算机系统。通过将医疗信息处理、解决方案智能推荐等AI技术与多年临床经验、循证医学有机结合&#xff0c;临床决策支持系统能够在疾病诊断、医学检查、疗法推荐、用药监测等各个环节提供决策支持…

禁止拷贝文件到U盘的解决办法

禁止拷贝文件到U盘的解决办法 安企神U盘管理系统下载使用 说到这问题&#xff0c;大多情况下是企业的需求&#xff0c;很多公司电脑中都保存着极为重要的数据&#xff0c;这些数据往往是不能传播的&#xff0c;所以此时就需要禁止拷贝文件到U盘来防止公司数据被泄密。 禁止拷…

Go学习第八章——面向“对象”编程(结构体与方法)

Go面向“对象”编程&#xff08;结构体与方法&#xff09; 1 结构体1.1 快速入门1.2 内存解析1.3 创建结构体四种方法1.4 注意事项和使用细节 2 方法2.1 方法的声明和调用2.2 快速入门案例2.3 调用机制和传参原理2.4 注意事项和细节2.5 方法和函数区别 3 工厂模式 Golang语言面…

代码随想录打卡第五十天|198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

198.打家劫舍 题目&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 …

MySQL——九、SQL编程

MySQL 一、触发器1、触发器简介2、创建触发器3、一些常见示例 二、存储过程1、什么是存储过程或者函数2、优点3、存储过程创建与调用 三、存储函数1、存储函数创建和调用2、修改存储函数3、删除存储函数 四、游标1、声明游标2、打开游标3、使用游标4、关闭游标游标案例 一、触发…

算法通关村第三关|青铜|线性表数组热身

1.线性表 1.1 线性表 线性表就是具有相同特征数据元素的一个有限序列。 1.2 数组存储元素的初始化 数组初始化时必须对从前向后的连续空间初始化&#xff0c;不可以出现空缺的情况。写 0 也是初始化&#xff0c;也要从前向后写。 2.热身-单调数组 判断是否为单调数组&am…

【Gensim概念】02/3 NLP玩转 word2vec

第二部分 句法 六、句法模型&#xff08;类对象和参数&#xff09; 6.1 数据集的句子查看 classgensim.models.word2vec.BrownCorpus(dirname) Bases: object 迭代句子 Brown corpus (part of NLTK data). 6.2 数据集的句子和gram classgensim.models.word2vec.Heapitem(c…