Чтобы получить dev_t
для устройства, на котором смонтирована ваша файловая система root, вы можете использовать стратегию, аналогичную той, которую выполняет системный вызов stat()
. Единственным исключением является то, что вы не работаете с __user
буферами, поэтому вам нужно будет использовать немного другие API.
Короче говоря, вы хотите:
- Call
kern_path()
, чтобы получить struct path
для пути "/"
. - Вызов
vfs_getattr()
, проходящий этот путь, чтобы получить struct kstat
. - Проверьте
->dev
член struct kstat
, который является устройством root (dev_t
). - Если вы хотите, Вы также можете использовать
bdget()
, чтобы найти блочное устройство, соответствующее полученному dev_t
, затем используйте bdevname()
, чтобы получить его имя (например, sda1
).
Это означает следующее:
struct path root_path;
struct kstat root_stat;
struct block_device *root_device;
char root_device_name[BDEVNAME_SIZE];
kern_path("/", 0, &root_path);
// KERNEL > 4.10
// vfs_getattr(&root_path, &root_stat, STATX_BASIC_STATS, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
// KERNEL <= 4.10
vfs_getattr(&root_path, &root_stat);
pr_info("root device number is 0x%08x; major = %d, minor = %d\n", root_stat.dev, MAJOR(root_stat.dev), MINOR(root_stat.dev));
root_device = bdget(root_stat.dev);
bdevname(root_device, root_device_name);
pr_info("root device name: %s, path: /dev/%s\n", root_device_name, root_device_name);
bdput(root_device);
path_put(&root_path);
Некоторые примечания:
Я не делал ничего проверка ошибок в приведенном выше примере, но вы, безусловно, должны! См. Полный пример ниже.
kern_path()
, кажется, принимает LOOKUP_*
флаги, определенные в linux/namei.h
в качестве второго аргумента, но передача значения по умолчанию 0
также приемлемо.
Аналогично, vfs_getattr()
принимает STATX_*
флагов, определенных в linux/stat.h
в качестве третьего аргумента. Системный вызов stat()
проходит STATX_BASIC_STATS
, но передача 0
здесь тоже должна быть в порядке, поскольку нам не нужно знать ничего, кроме устройства (->dev
field), которое кажется для заполнения независимо от флагов. Я не смог проверить это, хотя мое ядро <= 4.10, и эти флаги необходимы только для ядра> 4.10.
Рабочий пример
Вот полный пример работающий модуль ядра, который выполняет вышеупомянутое, также применяя надлежащую проверку ошибок.
// SPDX-License-Identifier: GPL-3.0
#include <linux/kernel.h> // printk(), pr_*()
#include <linux/module.h> // THIS_MODULE, MODULE_VERSION, ...
#include <linux/init.h> // module_{init,exit}
#include <linux/types.h> // dev_t
#include <linux/kdev_t.h> // MAJOR(), MINOR(), MKDEV()
#include <linux/path.h> // struct path
#include <linux/namei.h> // kern_path(), path_put()
#include <linux/stat.h> // struct kstat, STATX_*
#include <linux/fs.h> // vfs_getattr(), struct block_device, bdget(),...
#include <uapi/linux/fcntl.h> // AT_*
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
static int __init findrootdev_init(void)
{
struct path root_path;
struct kstat root_stat;
struct block_device *root_device;
char root_device_name[BDEVNAME_SIZE];
int err = 0;
pr_info("init\n");
err = kern_path("/", 0, &root_path);
if (err) {
pr_err("kern_path error %d\n", err);
goto kern_path_fail;
}
// KERNEL > 4.10
// err = vfs_getattr(&root_path, &root_stat, STATX_BASIC_STATS,
// AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW);
// KERNEL <= 4.10
err = vfs_getattr(&root_path, &root_stat);
if (err) {
pr_err("vfs_getattr error %d\n", err);
goto vfs_getattr_fail;
}
pr_info("root device number is 0x%08x; major = %d, minor = %d\n",
root_stat.dev, MAJOR(root_stat.dev), MINOR(root_stat.dev));
root_device = bdget(root_stat.dev);
if (!root_device) {
pr_err("bdget failed\n");
err = -1;
goto bdget_fail;
}
if (!bdevname(root_device, root_device_name)) {
pr_err("bdevname failed\n");
err = -1;
goto bdevname_fail;
}
pr_info("root device name: %s, path: /dev/%s\n",
root_device_name, root_device_name);
bdevname_fail:
bdput(root_device);
bdget_fail:
vfs_getattr_fail:
path_put(&root_path);
kern_path_fail:
return err;
}
static void __exit findrootdev_exit(void)
{
// This function is only needed to be able to unload the module.
pr_info("exit\n");
}
module_init(findrootdev_init);
module_exit(findrootdev_exit);
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("Silly test module to find the root device and its name.");
MODULE_AUTHOR("Marco Bonelli");
MODULE_LICENSE("GPL");
Компиляция и загрузка вышеупомянутого модуля с insmod
генерирует этот вывод в dmesg
:
[12664.685699] findrootdev: init
[12664.685703] findrootdev: root device number is 0x00800003; major = 8, minor = 3
[12664.685706] findrootdev: root device name: sda3, path: /dev/sda3
[12671.671038] findrootdev: exit