?
/**
@file path_tools.h
@brief Tools for working with 'struct path'
@details Copyright (c) 2024 Acronis International GmbH
@author Bruce Wang (bruce.wang@acronis.com)
@since $Id: $
*/
#pragma once
#ifdef BPF_PROGRAM
#include "compat_ebpf.h"
#else
#include "file_contexts.h"
#include "file_key_tools.h"
#include <linux/fcntl.h>
#include <linux/magic.h>
static inline void make_file_context_info_common_ino(file_context_key_t* key, const struct inode *inode)
{
make_key_from_inode(&key->file_key, inode);
#ifdef HAVE_INODE_GET_MTIME
key->i_mtime.tv_sec = inode_get_mtime(inode).tv_sec;
key->i_mtime.tv_nsec = inode_get_mtime(inode).tv_nsec;
#else
key->i_mtime.tv_sec = inode->i_mtime.tv_sec;
key->i_mtime.tv_nsec = inode->i_mtime.tv_nsec;
#endif
#ifdef HAVE_INODE_GET_CTIME
key->i_ctime.tv_sec = inode_get_ctime(inode).tv_sec;
key->i_ctime.tv_nsec = inode_get_ctime(inode).tv_nsec;
#else
key->i_ctime.tv_sec = inode->i_ctime.tv_sec;
key->i_ctime.tv_nsec = inode->i_ctime.tv_nsec;
#endif
}
static inline bool make_file_context_info_common(file_context_info_t *info
, const struct path *path
, uint64_t pid_version)
{
if (!path || !path->dentry || !path->dentry->d_inode)
{
return false;
}
make_file_context_info_common_ino(&info->msg_info.key, path->dentry->d_inode);
info->pid_key = pid_version;
return true;
}
static inline bool make_file_context_info_open(file_context_info_t *info
, const struct path *path
, uint64_t pid_version
, int flags)
{
if (!make_file_context_info_common(info, path, pid_version))
return false;
if ((flags & O_ACCMODE) <= O_RDWR) {
flags += 1;
}
info->params.open.flags = flags;
return true;
}
static inline bool make_file_context_info_rw(file_context_info_t *info
, const struct path *path
, uint64_t pid_version
, uint64_t low, uint64_t high)
{
if (!make_file_context_info_common(info, path, pid_version))
return false;
info->params.rw.low = low;
info->params.rw.high = high;
return true;
}
static inline bool make_file_context_info_modify(file_context_info_t *info, const struct inode *inode, uint64_t unique_pid)
{
if (!inode)
return false;
make_file_context_info_common_ino(&info->msg_info.key, inode);
info->pid_key = unique_pid;
return true;
}
static inline bool make_file_context_req_modify(file_context_add_cache_request_t* req, const struct inode *inode, uint64_t unique_pid)
{
if (!inode)
return false;
make_key_from_inode(&req->key.file_key, inode);
req->pid_key = unique_pid;
return true;
}
#endif
#ifndef NSFS_MAGIC
#define NSFS_MAGIC 0x6e736673
#endif
#ifndef SYSFS_MAGIC
#define SYSFS_MAGIC 0x62656572
#endif
#ifndef PROC_SUPER_MAGIC
#define PROC_SUPER_MAGIC 0x9fa0
#endif
#ifndef DEBUGFS_MAGIC
#define DEBUGFS_MAGIC 0x64626720
#endif
#ifndef SECURITYFS_MAGIC
#define SECURITYFS_MAGIC 0x73636673
#endif
#ifndef CGROUP_SUPER_MAGIC
#define CGROUP_SUPER_MAGIC 0x27e0eb
#endif
#ifndef CGROUP2_SUPER_MAGIC
#define CGROUP2_SUPER_MAGIC 0x63677270
#endif
#ifndef BPF_FS_MAGIC
#define BPF_FS_MAGIC 0xcafe4a11
#endif
#ifndef TRACEFS_MAGIC
#define TRACEFS_MAGIC 0x74726163
#endif
#ifndef FUSECTL_SUPER_MAGIC
#define FUSECTL_SUPER_MAGIC 0x65735543
#endif
#ifndef PSTOREFS_MAGIC
#define PSTOREFS_MAGIC 0x6165676C
#endif
#ifndef CONFIGFS_MAGIC
#define CONFIGFS_MAGIC 0x62656570
#endif
#ifndef HUGETLBFS_MAGIC
#define HUGETLBFS_MAGIC 0x958458f6
#endif
#ifndef MQUEUE_MAGIC
#define MQUEUE_MAGIC 0x19800202
#endif
#ifndef DEVPTS_SUPER_MAGIC
#define DEVPTS_SUPER_MAGIC 0x1cd1
#endif
#ifndef SELINUX_MAGIC
#define SELINUX_MAGIC 0xf97cff8c
#endif
static inline bool inode_is_usable(const struct inode *inode)
{
if (!inode)
{
return false;
}
if (!FP_KREAD(inode, i_sb))
{
return false;
}
return true;
}
// Checks for bare minimum requirements for 'path' to be any sort of reasonable
static inline bool path_is_usable(const struct path *path)
{
struct dentry *dentry;
if (!FP_KREAD(path, mnt))
return false;
dentry = FP_KREAD(path, dentry);
if (!dentry)
{
return false;
}
return inode_is_usable(FP_KREAD(dentry, d_inode));
}
static inline int magic_ok(unsigned long magic)
{
return magic != BPF_FS_MAGIC
&& magic != CGROUP_SUPER_MAGIC
&& magic != CGROUP2_SUPER_MAGIC
&& magic != CONFIGFS_MAGIC
&& magic != DEBUGFS_MAGIC
&& magic != DEVPTS_SUPER_MAGIC
&& magic != FUSECTL_SUPER_MAGIC
&& magic != HUGETLBFS_MAGIC
&& magic != MQUEUE_MAGIC
&& magic != NSFS_MAGIC
&& magic != PROC_SUPER_MAGIC
&& magic != PSTOREFS_MAGIC
&& magic != SECURITYFS_MAGIC
&& magic != SELINUX_MAGIC
&& magic != SYSFS_MAGIC
&& magic != TRACEFS_MAGIC
;
}
static inline int sb_ok(const struct super_block *sb)
{
return sb && magic_ok(FP_KREAD(sb, s_magic));
}
#ifndef S_IFMT
#define S_IFMT 00170000
#endif
#ifndef S_IFREG
#define S_IFREG 0100000
#endif
#ifndef S_IFDIR
#define S_IFDIR 0040000
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
static inline bool check_if_usable_inode_valid(const struct inode *inode)
{
umode_t mode;
if (!sb_ok(FP_KREAD(inode, i_sb)))
{
return false;
}
mode = FP_KREAD(inode, i_mode);
if ((S_ISREG(mode)) || (S_ISDIR(mode)))
return true;
else
return false;
}
static inline bool inode_is_valid(const struct inode *inode)
{
if (!inode_is_usable(inode))
{
return false;
}
return check_if_usable_inode_valid(inode);
}
static inline bool path_is_valid(const struct path *path)
{
if (!path_is_usable(path))
{
return false;
}
return check_if_usable_inode_valid(FP_KREAD2(path, dentry, d_inode));
}
static inline bool file_is_valid(struct file* f)
{
#ifndef BPF_PROGRAM
return path_is_valid(&f->f_path);
#else
struct path path = BPF_CORE_READ(f, f_path);
return path_is_valid(&path);
#endif
}
#ifdef BPF_PROGRAM
#define INT_MAX __INT_MAX__
#define rcu_dereference(p) (p)
#endif
static inline pid_t pid_from_dname(const struct dentry *d)
{
const unsigned char *s = (const unsigned char *)d->d_name.name;
unsigned int len = d->d_name.len;
unsigned int i;
pid_t pid = 0;
if (len <= 0 || !s)
return -1;
for (i = 0; i < len; i++)
{
unsigned char c = s[i];
if (c < '0' || c > '9')
return -1;
/* Basic overflow guard for 32-bit int. */
if (pid > (INT_MAX - (c - '0')) / 10)
return -1;
pid = pid * 10 + (c - '0');
}
return pid;
}
static inline pid_t pid_under_proc(const struct path *path)
{
if (!path || !FP_KREAD(path, mnt) || !FP_KREAD2(path, mnt, mnt_sb))
return -1;
if (FP_KREAD3(path, mnt, mnt_sb, s_magic) != PROC_SUPER_MAGIC)
return -1;
{
struct dentry *dentry = FP_KREAD(path, dentry);
struct dentry *root_dentry = FP_KREAD2(path, mnt, mnt_root);
struct dentry *parent;
pid_t pid = -1;
if (!dentry || !root_dentry)
return -1;
rcu_read_lock();
parent = rcu_dereference(dentry->d_parent);
if (!parent)
{
rcu_read_unlock();
return -1;
}
dentry = parent;
parent = rcu_dereference(dentry->d_parent);
if (!parent || parent != root_dentry)
{
rcu_read_unlock();
return -1;
}
pid = pid_from_dname(dentry);
rcu_read_unlock();
return pid;
}
return -1;
}