Linux 驱动根据struct file获取全路径和文件名
一、背景
在对内核vfs层函数进行hook的时候,需要对struct pt_regs相关数据进行处理,插桩vfs_read/vfs_write时需要得到被读写文件的全路径或者文件名。
二、代码实现
根据拿到的struct file来获得文件名和对应的全路径。
//hook vfs_write
struct file *filep = NULL;
char *full_path, *temp;
char file_name = NULL;
filep = (struct file *)regs->di;
full_path = (char *)kmalloc(PATH_MAX, GFP_KERNEL);
if (full_path == NULL) {
printk("%s: malloc memory failed\n", __func__);
return -1;
}
memset(full_path, 0, PATH_MAX);
//Get file name
filename = filep->f_path.dentry->d_iname;
//Get full path
temp = dentry_path_raw(filep->f_path.dentry, full_path, PATH_MAX);
printk("%s\n", temp);
//根据file指针获取文件全路径
char *file_path(struct file *filep, char *buf, int buflen)
{
return d_path(&filep->f_path, buf, buflen);
}
//最后要记得释放申请的内存
dentry_path_raw()函数只能获取到挂载点之后的路径!!!d_path()才能获取全路径!!!
需要特别注意的是,d_path函数有可能返回error code,需要对返回的指针做判断,不然有可能发生非法地址访问导致内核OOPS。
//函数原型,为内核导出函数,放心使用
/**
* 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);
//内核函数使用实例
static int do_proc_readlink(struct path *path, char __user *buffer, int buflen)
{
char *tmp = (char *)__get_free_page(GFP_KERNEL);
char *pathname;
int len;
if (!tmp)
return -ENOMEM;
pathname = d_path(path, tmp, PAGE_SIZE);
len = PTR_ERR(pathname);
if (IS_ERR(pathname))
goto out;
len = tmp + PAGE_SIZE - 1 - pathname;
if (len > buflen)
len = buflen;
if (copy_to_user(buffer, pathname, len))
len = -EFAULT;
out:
free_page((unsigned long)tmp);
return len;
}
char *ret_path = NULL;
char buff[256] = {0};
ret_path = d_path(path, buff, sizeof(buff));
if (IE_ERR(ret_path) {
pr_err("Get path failed\n");
return -1;
}