Este repositório contém um pequeno projeto educacional de sistema de arquivos
escrito em C. Ele simula um dispositivo de blocos e expõe um shell de linha de
comando para interagir com a imagem do sistema de arquivos. A parte que falta é
a implementação do sistema de arquivos estilo FAT em fat.c.
Sua tarefa é concluir o sistema de arquivos para que os comandos do shell funcionem como esperado.
cmd.c: shell interativo e comandos do usuáriods.c/ds.h: abstração de disco simulado sobre um arquivo do hospedeirofat.c/fat.h: lógica do sistema de arquivos estilo FAT a ser implementadaMakefile: alvos básicos de compilaçãoimagem-pronta: imagem de disco de exemplo
Implemente a lógica ausente em fat.c para que o programa consiga:
- formatar uma imagem de disco nova
- montar um sistema de arquivos existente
- imprimir metadados do sistema para depuração
- criar arquivos no diretório raiz
- deletar arquivos e recuperar seus blocos
- informar o tamanho dos arquivos
- ler o conteúdo dos arquivos
- escrever o conteúdo dos arquivos
- dar suporte à importação/exportação pelos comandos já existentes no shell
O layout do disco já está sugerido em fat.c:
- bloco
0: superbloco - bloco
1: diretório - bloco
2em diante: blocos da FAT
O código já define as principais estruturas em disco:
super: metadados do sistema de arquivosdir_item: entrada do diretório raizfat: tabela de alocação em memória
Também existe uma flag global mountState que deve ser usada para impedir
operações no sistema de arquivos antes de uma montagem bem-sucedida.
Conclua estas funções em fat.c:
fat_format()fat_mount()fat_debug()fat_create(char *name)fat_delete(char *name)fat_getsize(char *name)fat_read(char *name, char *buff, int length, int offset)fat_write(char *name, const char *buff, int length, int offset)
Sua implementação deve suportar o comportamento abaixo.
fat_format() deve:
- inicializar o superbloco com um número mágico válido
- calcular quantos blocos da FAT são necessários para o tamanho atual do disco
- marcar blocos reservados como indisponíveis
- limpar o diretório
- inicializar as entradas da FAT
- escrever as estruturas inicializadas no disco
fat_mount() deve:
- ler o superbloco do disco
- validar o número mágico e os metadados básicos do sistema
- alocar memória para a FAT em memória
- carregar a FAT e o diretório a partir do disco
- definir o estado de montado somente após validação bem-sucedida
fat_debug() deve imprimir informações úteis, como:
- valores do superbloco
- número de blocos da FAT
- número de entradas de diretório em uso
- cadeias de blocos alocados para os arquivos
A saída não precisa seguir um formato rígido, mas deve ser útil para verificar a correção.
fat_create() deve:
- rejeitar nomes inválidos
- rejeitar nomes duplicados
- encontrar uma entrada livre no diretório
- criar uma entrada de arquivo vazia
fat_delete() deve:
- localizar o arquivo no diretório
- liberar cada bloco de dados em sua cadeia na FAT
- limpar a entrada do diretório
- persistir os metadados atualizados no disco
fat_getsize() deve:
- retornar o tamanho do arquivo em bytes para um arquivo existente
- retornar erro para um arquivo inexistente
fat_read() deve:
- ler a partir do deslocamento de byte solicitado
- parar no fim do arquivo
- lidar corretamente com leituras que atravessem vários blocos
- retornar o número de bytes realmente lidos
- retornar erro para requisições inválidas
fat_write() deve:
- escrever a partir do deslocamento de byte solicitado
- alocar novos blocos quando o arquivo crescer
- preservar dados existentes fora da faixa escrita
- lidar corretamente com escritas que atravessem vários blocos
- atualizar o tamanho do arquivo quando necessário
- persistir FAT, diretório e blocos de dados modificados
- retornar o número de bytes realmente escritos
- Mantenha as assinaturas públicas das funções existentes.
- Preserve o tamanho de bloco atual definido em
ds.h. - O diretório raiz tem tamanho fixo de acordo com o desenho atual.
- Os nomes de arquivo são limitados por
MAX_LETTERS; aplique isso de forma consistente. - Não reprojete o projeto inteiro, a menos que seja absolutamente necessário.
- Prefira uma implementação clara e correta a uma implementação engenhosa.
- Implemente algumas funções auxiliares privadas em
fat.c. - Faça
fat_format()efat_mount()funcionarem primeiro. - Adicione auxiliares para busca no diretório e busca de entrada livre.
- Implemente auxiliares para alocação de blocos e percorrer cadeias de blocos.
- Implemente
fat_create(),fat_getsize()efat_delete(). - Implemente
fat_read()efat_write()por último. - Use
fat_debug()com frequência durante os testes.
- operações antes da montagem
- nomes de arquivo inválidos ou grandes demais
- criação duplicada de arquivo
- exclusão de arquivo vazio
- leitura além do fim do arquivo
- escrita em arquivo vazio
- extensão de arquivo por vários blocos
- falta de espaço livre
- falta de entradas livres no diretório
Depois de implementar o código, verifique o comportamento com sequências como estas:
- Inicie o programa com uma imagem nova e formate-a.
- Monte o sistema de arquivos e inspecione-o com
depurar. - Crie um arquivo e confirme que seu tamanho é
0. - Importe um pequeno arquivo de texto e exporte-o de volta.
- Leia o arquivo com
ver. - Importe um arquivo maior que um bloco.
- Delete arquivos e verifique se os blocos são reutilizados.
- Remonte a imagem e confirme que os dados persistem.
Entregue:
- o código-fonte concluído
- quaisquer pequenas funções auxiliares adicionadas a
fat.c - uma nota curta descrevendo decisões de projeto e limitações conhecidas
Esta tarefa está concluída quando:
- todos os comandos do shell apoiados pela camada FAT funcionarem corretamente
- os dados persistirem após fechar e remontar a imagem
- a alocação e a recuperação de blocos se comportarem corretamente
- a implementação for legível e razoavelmente documentada
Estes itens são opcionais e só devem ser tentados depois que os requisitos centrais estiverem funcionando:
- saída de depuração mais clara
- validação mais forte durante a montagem
- melhor consistência no tratamento de erros
- pequenos refactors internos para legibilidade
Trate isto como uma tarefa de conclusão, não como uma reescrita. O projeto já fornece o shell, a simulação de disco, as estruturas básicas e a direção pretendida. Seu trabalho é concluir a implementação do sistema de arquivos de forma limpa e correta.