Tutorial: Criando um Sistema de Arquivos Simples na Memória (lkcampfs)¶
Este tutorial tem como objetivo demonstrar os conceitos básicos para a criação de um sistema de arquivos. Não há implementação de funções para criação ou remoção de arquivos.
Visão Geral¶
Neste projeto, você irá:
-
Criar um sistema de arquivos simples em memória (
lkcampfs) -
Implementar as funções básicas do VFS (Virtual File System)
-
Compilar e testar o módulo no QEMU
Etapa 1: Preparando o Ambiente¶
1. Acesse o diretório de filesystems do kernel:¶
cd linux/fs
2. Crie um novo diretório para o seu sistema de arquivos:¶
mkdir lkcampfs
cd lkcampfs
3. Copie o Makefile do ramfs:¶
cp ../ramfs/Makefile .
4. Crie o arquivo inode.c:¶
touch inode.c
Obs: Caso queiram investigar um exemplo de filesystem completo, que faz a escrita no disco, temos o Minix, que consegue rodar um SO inteiro em cima dele.
5. Modifique o Makefile de lkcampfs:¶
obj-m += lkcampfs.o
lkcampfs-objs += inode.o
6. Atualize o Makefile de linux/fs:¶
obj-y += lkcampfs/
Etapa 2: Inicialização do Módulo¶
No inode.c, adicione:
#include <linux/init.h>
#include <linux/module.h>
static int __init lkcampfs_init(void)
{
pr_info("lkcampfs was successfully loaded\n");
return 0;
}
static void __exit lkcampfs_exit(void)
{
pr_info("lkcampfs was successfully unloaded\n");
}
module_init(lkcampfs_init);
module_exit(lkcampfs_exit);
MODULE_AUTHOR("LKCAMP");
MODULE_DESCRIPTION("A simple filesystem implementation");
MODULE_LICENSE("GPL");
Etapa 3: Compilação e Teste Inicial¶
Compile o kernel:
make -j$(nproc)
Execute o kernel com QEMU:
qemu-system-x86_64 \
-drive file=../my_disk.raw,format=raw,index=0,media=disk \
-m 4G -nographic \
-kernel ./arch/x86_64/boot/bzImage \
-append "root=/dev/sda rw console=ttyS0 loglevel=6 nokaslr" \
-fsdev local,id=fs1,path=.,security_model=none \
-device virtio-9p-pci,fsdev=fs1,mount_tag=shared_folder \
--enable-kvm \
-s
Carregue o módulo na VM:
insmod host_folder/fs/lkcampfs/lkcampfs.ko
Verifique com dmesg:
lkcampfs was successfully loaded
Descarregue o módulo:
rmmod host_folder/fs/lkcampfs/lkcampfs.ko
Etapa 4: Registrando o Filesystem¶
1. Inclua no início do inode.c:¶
#include <linux/fs.h>
2. Declare as funções:¶
static void lkcampfs_kill_sb(struct super_block *s);
static struct dentry *lkcampfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data);
3. Defina a estrutura do filesystem:¶
static struct file_system_type lkcamp_fs_type = {
.owner = THIS_MODULE, // para o kernel não precisar carregar 2x o módulo na memória
.name = "lkcampfs", // para identificar o filesystem
.mount = lkcampfs_mount, // nossa função de mount
.kill_sb = lkcampfs_kill_sb, // função para desmount
.fs_flags = FS_USERNS_MOUNT,
};
Etapa 5: Superbloco e Inodes¶
Para montar um filesystem, precisamos ler um superbloco e a partir da informação lida, criamos um inode e registramos uma dentry para a raiz do novo filesystem.
A função get_inode() recebe o superbloco, o diretório onde o inode existe e as flags de permissão do arquivo.
get_inode()¶
static struct inode *lkcampfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode)
{
struct inode *inode = new_inode(sb); // aloca um inode
if (inode) {
inode->i_ino = get_next_ino(); // aloca um número para nosso inode
inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
simple_inode_init_ts(inode); //inicializa o inode
switch (mode & S_IFMT) { //verificar o tipo setado pro inode
case S_IFREG: //alocar o inode para um arquivo regular
break;
case S_IFDIR: //alocar o inode para um diretório
inode->i_op = &simple_dir_inode_operations; //define a tabela de funções do inode
inode->i_fop = &simple_dir_operations;
inc_nlink(inode); //incrementa o número de links para o novo inode
break;
default:
pr_err("lkcampfs: unsupported inode type\n");
return NULL;
}
}
return inode;
}
fill_super()¶
A função fill_super() é chamada dentro da função mount(). Ela cria um superbloco e um inode com a função de get_inode().
Depois, apontamos a raiz do inode do nosso filesystem para esse inode criado.
#define LKCAMPFS_MAGIC 0x4D43353034
static int lkcampfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *root_inode;
sb->s_magic = LKCAMPFS_MAGIC; //define o tipo do filesystem que está sendo montado (em hex
sb->s_blocksize = PAGE_SIZE; //define o tamanho do bloco do sistema
sb->s_blocksize_bits = PAGE_SHIFT; //define o número de bits do bloco
root_inode = lkcampfs_get_inode(sb, NULL, S_IFDIR);
sb->s_root = d_make_root(root_inode);
return sb->s_root ? 0 : -ENOMEM;
}
mount()¶
A função mount() é chamada pelo filesystem a partir do comando mount do usuário no terminal.
static struct dentry *lkcampfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data)
{
struct dentry *ret = mount_bdev(fs_type, flags, dev_name, data, lkcampfs_fill_super);
if (IS_ERR(ret)) {
pr_err("lkcampfs: failed to mount\n");
} else {
pr_info("lkcampfs: mounted successfully on [%s]\n", dev_name);
}
return ret;
}
kill_sb()¶
Pra finalizar, o código da função kill_sb(), que destrói o superbloco.
Como nosso filesystem só existe na memória, seu código é bem simples e serve somente para que a struct lkcamp_fs_type tenha um ponteiro pruma função de kill superblock.
static void lkcampfs_kill_sb(struct super_block *s)
{
kill_litter_super(s);
pr_info("lkcampfs: unmounted successfully\n");
}
Etapa 6: Registro no Kernel¶
Atualizamos as funções de inicialização e finalização:
static int __init lkcampfs_init(void)
{
int ret = register_filesystem(&lkcamp_fs_type);
if (ret)
pr_err("lkcampfs: registration failed\n");
else
pr_info("lkcampfs: registered successfully\n");
return ret;
}
static void __exit lkcampfs_exit(void)
{
int ret = unregister_filesystem(&lkcamp_fs_type);
if (ret)
pr_err("lkcampfs: unregistration failed\n");
else
pr_info("lkcampfs: unregistered successfully\n");
}
Etapa Final: Teste Completo¶
-
Compile o novo código:
make -j$(nproc) -
Carregue o módulo na VM:
insmod host_folder/fs/lkcampfs/lkcampfs.ko -
Crie uma imagem (neste comando, a imagem é criada com 10Mb):
dd if=/dev/zero of=a.img bs=1M count=10 -
Monte o sistema na nova imagem:
mount -t lkcampfs -o loop a.img /mnt -
Verifique com
dmesg:lkcampfs: mounted successfully on [...]