http://blogold.chinaunix.net/u2/86590/showart_2185876.html


linux内核中用d_path得到绝对路径名

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 <linux/mount.h>
#include <linux/netdevice.h>
#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 <linux/module.h>
#include <linux/fcntl.h>//for O_RDONLY
#include <linux/fs.h>//for filp_open
#include <linux/uaccess.h>//for get_fs
#include <linux/limits.h>//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/

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐