Este guia ter por objetivo dar uma visão básica sobre como o FUSE funciona e compartilhar materiais que possuam documentação sobre o mesmo. Caso você deseje adicionar qualquer coisa ou corrigir por favor faça um comentário abaixo.
O guia surgiu prioritariamente para ajudar meu grupo, mas decidi disponitilizá-lo para caso alguém precise.
GIT
Foi comentado durante a aula que muitas pessoas não tinham nenhum conhecimento sobre GIT e por isso estavam tendo alguma dificuldade. Vou deixar alguns links bem interessantes para caso você queira aprender e algumas dicas para ajudar você a desenvolver.
Por quê git é importante?
Ele vai permitir você escrever código colaborativo com seus colegas de forma muito mais fácil e sem conflitos.
Esqueça problemas para saber em qual versão vocês estão, pessoas sobrescrevendo código um do outro ou até mesmo ter que procurar em uma lista enorme de backups uma versão estável do seu projeto no qual tudo ainda funcionava.
O git vai resolver tudo isso pra você com pouco esforço e dentro de poucas horas você provavelmente já estará dominando o suficiente para desenvolver sem problemas.
Claro que pra usufruir das vantagens você vai ter que seguir algumas boas práticas e dominar a ferramenta.
Tutoriais interessantes de GIT
Uma base teórica legal pode ser encontrada nos seguintes links:
Escreva commits com mensagens descritivas, isso vai ajudar você quando precisar voltar para uma versão onde o sistema estava funcionando, além de documentar o projeto.
Tente escrever commits que contenham poucas mudanças e que representem uma versão estável do seu sistema, ou seja, evite commitar quando nada compila ou existe algum bug.
Seja descritivo nas mensagens de commit.
Trabalhe em BRANCHS com seus colegas, isso vai evitar conflitos e irá garantir que a versão da master do projeto é a mais atualizada e estável.
Comandos úteis
Adicionar arquivos para o commit (Staging)
git add path_para_o_arquivo
Caso deseje adicionar tudo que modificou pode usar o atalho
git add -A
Verificar o que foi adicionado para commit e o que foi modificado
git status
Arquivos que estão adicionados para o próximo commit (staged) irão aparecer em uma lista em verde logo após o texto
"Changes to be committed:".
Arquivos modificados irão aparecer em uma lista em vermelho após o texto "Untracked files:"
Criar nova branch
git checkout -b nomedabranch
ou para mudar para uma branch já existente
git checkout nomedabranch
Realizar um novo commit (após adicionar mudanças)
git commit -m "escreva aqui seu texto"
ou
git commit (isso irá abrir o editor padrão do seu computador)
Subir mudanças realizadas em uma branch para o repositório remoto
Quando retornar algum desses erros em sua função lembre-se de retornar o mesmo com um um sinal de menos na frente, é assim que o fuse espera tratar os erros retornados pela sua função.
Exemplo: -ENOSPC
O fuse faz o intermédio entre o seu sistema de arquivo o SO e a interface de usuário. Isso significa que toda chamada que envolva operações do sistema de arquivos irá primeiro passar pelo fuse.
Exemplo:
Usuário digita stat nome_do_arquivo.txt no terminal, a partir de então os seguintes eventos acontecem:
Fuse intercepta a chamada, parsea os argumentos e chama uma a função que você mapeou para o a stat.
Você recebe a chamada do FUSE, faz as atualizações necessárias no seu file system e retorna ao fuse o conjunto
de dados que é necessário para que ele converse com o SO e/ou apresente algo ao usuário.
Fuse recebe os parâmetros que você passou e repassa a interface do usuário, nesse caso listando todos os arquivos.
Exemplificando com código:
Digite no seu terminal:
stat nomedoarquivo.txt
Observe que no seu console de debug algo parecido com isso será printado:
O que acontece aqui é que o fuse recebeu uma chamada para getattr para seu arquivo e esses logs ficaram gravados
no console de debug.
Então primeiro a chamada ao getattr do fuse aconteceu e depois ele chamou a função presente no seu file system passando
alguns argumentos que irei comentar a seguir.
Exemplo sobre o que deve ser feito em 2 com código no repositório base:
static int getattr_brisafs(const char *path, struct stat *stbuf) {
memset(stbuf, 0, sizeof(struct stat));
//Diretório raiz
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
return 0;
}
//Busca arquivo na lista de inodes
for (int i = 0; i < MAX_FILES; i++) {
if (superbloco[i].bloco != 0 //Bloco sendo usado
&& compara_nome(superbloco[i].nome, path)) { //Nome bate
stbuf->st_mode = S_IFREG | superbloco[i].direitos;
stbuf->st_nlink = 1;
stbuf->st_size = superbloco[i].tamanho;
return 0; //OK, arquivo encontrado
}
}
//Erro arquivo não encontrado
return -ENOENT;
}
O parâmetro mais importante recebido na sua função é a struct do tipo stat (link para documentação: http://pubs.opengroup.org/onlinepubs/007908799/xsh/sysstat.h.html), essa struct é utilizada para passar os metadados do seu INode (arquivo) para o fuse, então todos os metadados que você desejar que sejam apresentados ao usuário devem ser preenchidos nessa estrutra.
Mas ok, como o fuse irá pegar esses dados de volta?
Bom, repare que stbuf é apenas um ponteiro, então ao final da execução da sua função todos os dados que você preencher nessa estrutura estarão disponíveis para a função do fuse que chamou sua função. A partir daí como já disse, o fuse irá se encarregar de mostrar esses dados ao usuário.
Esse padrão de passar um ponteiro para sua função irá se repetir diversas vezes em outras funções que você implementar, recomendo sempre procurar a documentação dos tipos que o fuse está passando para sua função junto com a documentação do fuse.h. Isso irá facilitar a você entender o que o fuse espera que você faça
Último ponto a se destacar é que o fuse espera que suas funções retornem 0 quando tudo ocorreu como esperado ou o código de erro correspondente ao problema encontrado como -ENOENT (não se esqueça do negativo), caso um arquivo não seja encontrado.
O que fazer quando quero implementar uma nova funcionalidade?
Primeiro pense qual função do sistema operacional seria responsável por aquilo, por exemplo escrever em um arquivo certamente deve corresponder a write.
Depois disso lembre de criar sua função no qual o fuse irá chamar, mas como saber qual e quais parâmetros ela deve receber?
Bom, pra isso recomendo olhar em fuse.h lá você vai encontrar a assinatura que todas as suas funções devem ter para que elas sejam chamadas corretamente, exemplo:
Guia sobre FUSE
Objetivos
Este guia ter por objetivo dar uma visão básica sobre como o FUSE funciona e compartilhar materiais que possuam documentação sobre o mesmo. Caso você deseje adicionar qualquer coisa ou corrigir por favor faça um comentário abaixo.
O guia surgiu prioritariamente para ajudar meu grupo, mas decidi disponitilizá-lo para caso alguém precise.
GIT
Foi comentado durante a aula que muitas pessoas não tinham nenhum conhecimento sobre GIT e por isso estavam tendo alguma dificuldade. Vou deixar alguns links bem interessantes para caso você queira aprender e algumas dicas para ajudar você a desenvolver.
Por quê git é importante?
O git vai resolver tudo isso pra você com pouco esforço e dentro de poucas horas você provavelmente já estará dominando o suficiente para desenvolver sem problemas.
Claro que pra usufruir das vantagens você vai ter que seguir algumas boas práticas e dominar a ferramenta.
Tutoriais interessantes de GIT
Uma base teórica legal pode ser encontrada nos seguintes links:
Link com um tutorial iterativo desde o básico até algumas coisa mais avançadas. (RECOMENDO)
Se souber de mais links deixe um comentário
Dicas (volte quando estiver entendendo)
Escreva commits com mensagens descritivas, isso vai ajudar você quando precisar voltar para uma versão onde o sistema estava funcionando, além de documentar o projeto.
Tente escrever commits que contenham poucas mudanças e que representem uma versão estável do seu sistema, ou seja, evite commitar quando nada compila ou existe algum bug.
Seja descritivo nas mensagens de commit.
Trabalhe em BRANCHS com seus colegas, isso vai evitar conflitos e irá garantir que a versão da master do projeto é a mais atualizada e estável.
Comandos úteis
Caso deseje adicionar tudo que modificou pode usar o atalho
Arquivos que estão adicionados para o próximo commit (staged) irão aparecer em uma lista em verde logo após o texto "Changes to be committed:".
Arquivos modificados irão aparecer em uma lista em vermelho após o texto "Untracked files:"
Criar nova branch
ou para mudar para uma branch já existente
Realizar um novo commit (após adicionar mudanças)
para verificar qual nome do repositório remoto do seu projeto basta digitar
Caso queira completar e aprimorar esse guia deixe um comentário.
Fuse
Links úteis para entendimento:
Documentação explicativa básica sobre fuse: https://www.cs.hmc.edu/~geoff/classes/hmc.cs135.201001/homework/fuse/fuse_doc.html
Documentação explicando como implementar uma função em fuse http://www.maastaar.net/fuse/linux/filesystem/c/2016/05/21/writing-a-simple-filesystem-using-fuse/
Documentação sobre alguns erros que aparecem no file system cedido pelo professor como ENOSPC. Caso queira entender um pouco mais sobre a semântica e o que representam, basta acessar o link: https://www.gnu.org/software/libc/manual/html_node/Error-Codes.html
DICA:
Quando retornar algum desses erros em sua função lembre-se de retornar o mesmo com um um sinal de menos na frente, é assim que o fuse espera tratar os erros retornados pela sua função. Exemplo: -ENOSPC
Documentação sobre a struct muito utilizada nas funções do fuse chamada stat, recomendo sua leitura toda vez que for necessário transmitir algum metadado sobre seus INodes: http://pubs.opengroup.org/onlinepubs/007908799/xsh/sysstat.h.html
Documentação do FUSE.h, nele você vai encontrar um mapeamento de todas as funções que você pode implementar junto com uma breve descrição sobre elas. https://github.com/libfuse/libfuse/blob/master/include/fuse.h
Como o fuse funciona:
O fuse faz o intermédio entre o seu sistema de arquivo o SO e a interface de usuário. Isso significa que toda chamada que envolva operações do sistema de arquivos irá primeiro passar pelo fuse.
Exemplo:
Usuário digita
stat nome_do_arquivo.txt
no terminal, a partir de então os seguintes eventos acontecem:Exemplificando com código:
Digite no seu terminal:
Observe que no seu console de debug algo parecido com isso será printado:
e no seu terminal irá aparecer os metadados do arquivo
O que acontece aqui é que o fuse recebeu uma chamada para getattr para seu arquivo e esses logs ficaram gravados no console de debug.
Então primeiro a chamada ao getattr do fuse aconteceu e depois ele chamou a função presente no seu file system passando alguns argumentos que irei comentar a seguir.
Exemplo sobre o que deve ser feito em 2 com código no repositório base:
O parâmetro mais importante recebido na sua função é a struct do tipo stat (link para documentação: http://pubs.opengroup.org/onlinepubs/007908799/xsh/sysstat.h.html), essa struct é utilizada para passar os metadados do seu INode (arquivo) para o fuse, então todos os metadados que você desejar que sejam apresentados ao usuário devem ser preenchidos nessa estrutra.
Mas ok, como o fuse irá pegar esses dados de volta?
Bom, repare que stbuf é apenas um ponteiro, então ao final da execução da sua função todos os dados que você preencher nessa estrutura estarão disponíveis para a função do fuse que chamou sua função. A partir daí como já disse, o fuse irá se encarregar de mostrar esses dados ao usuário.
Esse padrão de passar um ponteiro para sua função irá se repetir diversas vezes em outras funções que você implementar, recomendo sempre procurar a documentação dos tipos que o fuse está passando para sua função junto com a documentação do fuse.h. Isso irá facilitar a você entender o que o fuse espera que você faça
Último ponto a se destacar é que o fuse espera que suas funções retornem 0 quando tudo ocorreu como esperado ou o código de erro correspondente ao problema encontrado como -ENOENT (não se esqueça do negativo), caso um arquivo não seja encontrado.
O que fazer quando quero implementar uma nova funcionalidade?
Primeiro pense qual função do sistema operacional seria responsável por aquilo, por exemplo escrever em um arquivo certamente deve corresponder a write. Depois disso lembre de criar sua função no qual o fuse irá chamar, mas como saber qual e quais parâmetros ela deve receber?
Bom, pra isso recomendo olhar em fuse.h lá você vai encontrar a assinatura que todas as suas funções devem ter para que elas sejam chamadas corretamente, exemplo:
Depois disso mapeie a função do fuse para sua função na struct fuse_operations:
Isso irá dizer ao fuse qual função chamar quando a função write for invocada.
Após isso implemente a sua função write_brisafs e tá pronto o sorvetinho.
Espero que tenha ajudado, é tudo bem básico mas se alguém ainda estiver completamente perdido já é um começo.
Sugestões, dúvidas ou correções basta me contactar.
Obrigado,