#include #include #include #include #include #include #include #include #define N_DIRECT_BLOCKS 5 #define TYPE_FREE 0 #define TYPE_FILE 1 #define TYPE_DIR 2 #define KBYTES 1024 #define CHECK_NUMBER 2003 #define INODE_SIZE (sizeof(inode)) #define BLOCK_SIZE (sb->block_size) #define INODES_PER_BLOCK (BLOCK_SIZE / INODE_SIZE) #define N_BLOCKS (1 + sb->num_inodes / INODES_PER_BLOCK + sb->num_inodes) #define FS_SIZE (BLOCK_SIZE * N_BLOCKS) typedef struct superblock_entry { int check_number; // número que permite identificar o sistema de ficheiros como válido int block_size; // tamanho de cada bloco do sistema {1, 2(default) ou 4 Kbytes} int num_inodes; // o número de inodes no sistema de ficheiros int root_inode; // o numero do inode a que corresponde o directório root int free_inode; // o número do primeiro inode da lista de inodes não utilizados int free_block; // o número do primeiro bloco da lista de blocos de dados não utilizados } superblock; typedef struct inode_entry { int type; // o tipo do inode {TYPE_FILE, TYPE_DIR, TYPE_FREE} int size_free; // tamanho em bytes do ficheiro (se TYPE_FILE) // tamanho em bytes do directório (se TYPE_DIR) // próximo inode não utilizado (se TYPE_FREE) int direct_block[N_DIRECT_BLOCKS]; // índices dos blocos onde estão guardados os dados int indirect_block; // índice do bloco de índices para outros blocos } inode; // variáveis globais superblock *sb; // superblock do sistema de ficheiros inode *inodes; // apontador para a região dos inodes char *blocks; // apontador para a região dos dados // declaração das funções void parse_argv(int argc, char *argv[]); void my_format(int fsd, int block_size, int filesystem_size); int main(int argc, char *argv[]) { char *linha; parse_argv(argc, argv); for (;;) { // obter o próximo comando do utilizador while (strlen(linha = readline("virtual_fs$ ")) == 0) { free(linha); } add_history(linha); // parsing e execução do comando ... parsing do comando ... ... execução do comando ... free(linha); } return; } void parse_argv(int argc, char *argv[]) { int i, fsd, block_size, fs_size; block_size = 2 * KBYTES; // valor por defeito fs_size = 0; for (i = 1; i < argc - 1; i++) { if (argv[i][0] == '-') { if (argv[i][1] == 'b') { block_size = atoi(&argv[i][2]); if (block_size != 1 && block_size != 2 && block_size != 4) { printf("virtual_fs: invalid block size (%d)\n", block_size); printf("Usage: virtual_fs [-b[1|2|4]] [-sSIZE] FILESYSTEM\n"); exit(1); } block_size *= KBYTES; } else if (argv[i][1] == 's') { fs_size = atoi(&argv[i][2]); if (fs_size <= 0) { printf("virtual_fs: invalid filesystem size (%d)\n", fs_size); printf("Usage: virtual_fs [-b[1|2|4]] [-sSIZE] FILESYSTEM\n"); exit(1); } fs_size *= KBYTES; } else { printf("virtual_fs: invalid argument (%s)\n", argv[i]); printf("Usage: virtual_fs [-b[1|2|4]] [-sSIZE] FILESYSTEM\n"); exit(1); } } else { printf("virtual_fs: invalid argument (%s)\n", argv[i]); printf("Usage: virtual_fs [-b[1|2|4]] [-sSIZE] FILESYSTEM\n"); exit(1); } } if ((fsd = open(argv[argc-1], O_RDWR)) == -1) { // o sistema de ficheiros não existe --> é necessário criá-lo if (fs_size == 0) { printf("virtual_fs: missing filesystem size\n"); printf("Usage: virtual_fs [-b[1|2|4]] [-sSIZE] FILESYSTEM\n"); exit(1); } // cria um novo sistema de ficheiros ... if ((fsd = open(argv[argc-1], O_CREAT | O_TRUNC | O_RDWR, S_IRWXU)) == -1) { printf("virtual_fs: cannot create filesystem (%s)\n", argv[argc-1]); printf("Usage: virtual_fs [-b[1|2|4]] [-sSIZE] FILESYSTEM\n"); exit(1); } // e formata-o my_format(fsd, block_size, fs_size); } else { // faz o mapeamento do sistema de ficheiros e inicia as variáveis globais struct stat buf; stat(argv[argc-1], &buf); if ((sb = (superblock *) mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fsd, 0)) == MAP_FAILED) { printf("virtual_fs: cannot map filesystem (mmap error)\n"); close(fsd); exit(1); } inodes = (inode *) ((int) sb + BLOCK_SIZE); blocks = (char *) ((int) inodes + sb->num_inodes * INODE_SIZE); // testa se o sistema de ficheiros é válido if (sb->check_number != CHECK_NUMBER || buf.st_size != FS_SIZE) { printf("virtual_fs: invalid filesystem (%s)\n", argv[argc-1]); printf("Usage: virtual_fs [-b[1|2|4]] [-sSIZE] FILESYSTEM\n"); munmap(sb, buf.st_size); close(fsd); exit(1); } } close(fsd); return; } void my_format(int fsd, int block_size, int filesystem_size) { int n; // ajusta o tamanho do sistema de ficheiros para um valor válido n = (filesystem_size / block_size - 1) / (1 + block_size / INODE_SIZE); if (n == 0 || block_size * (1 + n * (1 + block_size / INODE_SIZE)) != filesystem_size) n++; filesystem_size = block_size * (1 + n * (1 + block_size / INODE_SIZE)); printf("virtual_fs: formatting virtual filesystem (%d bytes) ... please wait\n", filesystem_size); // extender o sistema de ficheiros para o tamanho desejado lseek(fsd, filesystem_size - 1, SEEK_SET); write(fsd, "", 1); // faz o mapeamento do sistema de ficheiros e inicia as variáveis globais if ((sb = (superblock *) mmap(NULL, filesystem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fsd, 0)) == MAP_FAILED) { printf("virtual_fs: cannot map filesystem (mmap error)\n"); close(fsd); exit(1); } // inicia o superblock sb->check_number = CHECK_NUMBER; sb->block_size = block_size; sb->free_block = 0; sb->num_inodes = n * INODES_PER_BLOCK; sb->root_inode = 0; sb->free_inode = 1; // inicia os inodes inodes = (inode *) ((int) sb + block_size); ... iniciar os inodes ... // inicia os data blocks blocks = (char *) ((int) inodes + sb->num_inodes * INODE_SIZE); ... iniciar os data_blocks ... return; }