Linux 内核的d_path()函数转换目录数据结构(dentry结构)到ASCII路径名字,指定的目录数据结构(dentry结构)路径返回在一个缓冲区中,这个缓冲区得内核开发人员自己申请,自己释放。
在linux2.4.18以前的内核中,指定的目录数据结构(dentry结构)路径返回在一段大小为PAGE_SIZE字节的固定缓冲区中。 这样就存在一个著名的d_path()路径截断漏洞,也就是如果提交的的目录数据结构(dentry结构)路径过长,超过PAGE_SIZE - 1长度,就会返回不正确的值,返回的路径就会导致结构条目被截断,并没有错误报告。
那么先来看一下d_path的定义:
include/linux/dcache.h(linux kernel 2.6.24)
/* write full pathname into buffer and return start of pathname */
extern char * d_path(struct dentry *, struct vfsmount *, char *, int);
第一个参数struct dentry是目录项结构,我们知道linux内核的虚拟文件系统的通用文件模型是由四个结构组成的:超级块对象(struct super_block),索引节点对象(struct inode),目录项对象(struct dentry)和文件对象(struct file).
第二个参数就是此目录结构所属于的已安装文件系统
第三个参数就是要存储绝对路径名的缓冲区
第四个参数缓冲区大小
那么怎么得到这个struct dentry和struct vfsmount呢?
--------------------------------------------------------
我们再来看一个结构struct fs_struct:
这个结构都是和进程相关的,我们知道
每一个进程都有它自己的当前工作目录和它自己的根目录(这仅仅是内核用来表示进程与文件系统相互作用所必须维护的数据中的两个例子).结构fs_struct的目的就在于记录进程和文件系统的关系.每个进程描述符的fs字段就是指向进程的fs_struct结构.
struct fs_struct {
atomic_t count;
rwlock_t lock;
int umask;
struct dentry * root,
* pwd, * altroot;
struct vfsmount * rootmnt,
* pwdmnt, * altrootmnt;
};
我们可以用这个结构中的pwd和pwdmnt来充当d_path的前两个参数,得到进程当前工作目录pwd的名字,然后再合并上在当前工作目录中打开的文件的名字,就得到了在当前工作目录下已打开文件的绝对路径名.大概流程如下:
pwd = dget(fs->pwd);
vfsmnt = mntget(fs->pwdmnt);
start = d_path(pwd,vfsmnt,path,PATH_MAX);
strcat(fullpath,start);
strcat(fullpath,"/");
strcat(fullpath,filename);
这个方法其实是首
先得到进程的当前工作路径,然后再拼接上文件名.如果已打开的文件是在进程的当前工作目录下的,那么这个方法没问题.但是
如果已打开的文件不是在进程当前的工作目录下呢?
-------------------------------------------------------------
我们知道每一个已打开的文件,内核中都有一个struct file与之对应.
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
所以,我们可以这样做,首先得到文件对应的struct file结构,然后再用结构中的f_dentry和f_vfsmnt字段充当d_path的前两个参数,这样就得到了这个文件的绝对路径了,具体步骤如下:
struct file *file = NULL;
file = filp_open(MY_FILE, O_RDWR | O_CREAT,0644);
d_path(file->f_dentry,file->f_vfsmnt,buffer,pathmax);
------------------------------------------------------------
下面给出这两种方法的具体实现(针对linux2.6.24内核)
----------
util.h
----------
#ifndef __UTIL_H__
#define __UTIL_H__
char* getfullpath(const char* filename);
char* getfullpath2(struct file* file);
void putfullpath(char *mem);
#endif // __UTIL_H_
-------------
util.c
-------------
#include
#include
#include "util.h"
char* getfullpath(const char* filename)
{
char *path=NULL, *start=NULL;
char *fullpath=NULL;
struct dentry * pwd = NULL;
struct vfsmount *vfsmnt = NULL;
struct fs_struct *fs = current->fs;
fullpath = kmalloc(PATH_MAX,GFP_KERNEL);
if(!fullpath) goto OUT;
memset(fullpath,0,PATH_MAX);
path = kmalloc(PATH_MAX,GFP_KERNEL);
if(!path) {
kfree(fullpath);
goto OUT;
}
//get the dentry and vfsmnt
read_lock(&fs->lock);
pwd = dget(fs->pwd);
vfsmnt = mntget(fs->pwdmnt);
read_unlock(&fs->lock);
//get the path
start = d_path(pwd,vfsmnt,path,PATH_MAX);
strcat(fullpath,start);
strcat(fullpath,"/");
strcat(fullpath,filename);
kfree(path);
OUT:
return fullpath;
}
char* getfullpath2(struct file* file)
{
char *path=NULL, *start=NULL;
char *fullpath=NULL;
fullpath = kmalloc(PATH_MAX,GFP_KERNEL);
if(!fullpath) goto OUT;
memset(fullpath,0,PATH_MAX);
path = kmalloc(PATH_MAX,GFP_KERNEL);
if(!path) {
kfree(fullpath);
goto OUT;
}
memset(path,0,PATH_MAX);
//get the path
start = d_path(file->f_dentry,file->f_vfsmnt,path,PATH_MAX);
strcpy(fullpath,start);
kfree(path);
OUT:
return fullpath;
}
void putfullpath(char* fullpath)
{
if(fullpath)
kfree(fullpath);
}
------------
main.c
------------
#include
#include //for O_RDONLY
#include //for filp_open
#include //for get_fs
#include //for PATH_MAX
#include "util.h"
#define MY_FILE "./abc"
#define FILE_NAME "abc"
#define TMP_FILE "/tmp/123"
void test1(void)
{
struct file *file = NULL;
char* fullpath = NULL;
//open ./abc
file = filp_open(MY_FILE, O_RDWR | O_CREAT,0644);
if (IS_ERR(file)) {
printk( "error occured while opening file %s, exiting...\n ", MY_FILE);
return;
}
//get the fullpath
fullpath = getfullpath(FILE_NAME);
if(!fullpath){
printk("Get fullpath error!\n");
return;
}
printk("FULLPATH:%s\n",fullpath);
//free mem
putfullpath(fullpath);
//close ./abc
if(file != NULL)
filp_close(file, NULL);
}
void test2(void)
{
struct file *file = NULL;
char* fullpath = NULL;
//open /tmp/123
file = filp_open(TMP_FILE, O_RDWR | O_CREAT,0644);
if (IS_ERR(file)) {
printk( "error occured while opening file %s, exiting...\n ", TMP_FILE);
return;
}
//get the fullpath
fullpath = getfullpath2(file);
if(!fullpath){
printk("Get fullpath error!\n");
return;
}
printk("FULLPATH:%s\n",fullpath);
//free mem
putfullpath(fullpath);
//close /tmp/123
if(file != NULL)
filp_close(file, NULL);
}
int init_module(void)
{
//full pathname = path + name
test1();
//full pathname = d_path()
test2();
return 0;
}
void cleanup_module(void)
{
}
MODULE_LICENSE("GPL");
-----------
Makefile
-----------
obj-m := my_test.o
my_test-objs := main.o util.o
all:
make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) modules
rm -f *.o *.mod.c .*.cmd sysenter.h Module.symvers
clean:
rm -f *.o *.ko *.mod.c .*.cmd sysenter.h Module.symvers
make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) clean
-----------------------------------------------------------------------------
执行结果:
$ make
$ sudo insmod ./my_test.ko
$ dmesg | tail
[ 5530.069194] FULLPATH:/home/xulei/Project/kernel_project/absolute_path/abc
[ 5530.069208] FULLPATH:/tmp/123
reference:
<<深入理解linux内核 第三版>>http://book.opensourceproject.org.cn/kernel/kernel3rd/
|