diff --git a/Main.java b/Main.java index 3b8673a..ffc897b 100644 --- a/Main.java +++ b/Main.java @@ -1,5 +1,8 @@ import filesys.IFileSystem; +import model.Usuario; +import java.util.ArrayList; +import java.util.List; import java.util.Scanner; import java.io.FileNotFoundException; @@ -8,6 +11,7 @@ import exception.CaminhoNaoEncontradoException; import filesys.FileSystem; +import filesys.FileSystemImpl; // MENU INTERATIVO PARA O SISTEMA DE ARQUIVOS // SINTA-SE LIVRE PARA ALTERAR A CLASSE MAIN @@ -36,10 +40,12 @@ public static void main(String[] args) { // Para quaisquer operações que serão feitas por esse usuário em um caminho /path/**, // deve-se checar se o usuário tem permissão de escrita (r) neste caminho. if (args.length < 2) { - System.out.println("Usuário não fornecido"); + System.out.println("Usuario nao fornecido"); return; } user = args[1]; + + List usuarios = new ArrayList<>(); // Carrega a lista de usuários do sistema a partir de arquivo // Formato do arquivo users: @@ -62,21 +68,21 @@ public static void main(String[] args) { String dir = parts[1]; String dirPermission = parts[2]; - /* A FAZER: + /* FEITO: * Processar a permissão de todos os usuários existentes por diretório. * Por enquanto esse código somente imprime as permissões contidas no arquivo users. */ System.out.println(userListed + " " + dir + " " + dirPermission); // Somente imprime o usuário, diretório e permissão - + usuarios.add(new Usuario(userListed, dirPermission, dir)); } else { - System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); + System.out.println("Formato ruim no arquivo de usuarios. Linha: " + line); } } } userScanner.close(); } catch (FileNotFoundException e) { // Retorna se o arquivo de usuários não for encontrado - System.out.println("Arquivo de usuários não encontrado"); + System.out.println("Arquivo de usuarios nao encontrado"); return; } @@ -84,16 +90,15 @@ public static void main(String[] args) { // Finalmente cria o Sistema de Arquivos // Lista de usuários é imutável durante a execução do programa // Obs: Como passar a lista de usuários para o FileSystem? - fileSystem = new FileSystem(/*usuários?*/); + fileSystem = new FileSystem(usuarios); // // DESCOMENTE O BLOCO ABAIXO PARA CRIAR O DIRETÓRIO RAIZ ANTES DE RODAR O MENU // // Cria o diretório raiz do sistema. Root sempre tem permissão total "rwx" // try { // fileSystem.mkdir(ROOT_DIR, ROOT_USER); // } catch (CaminhoJaExistenteException | PermissaoException e) { - // System.out.println(e.getMessage()); + // System.out.println(e.getMessage()); // } - // Menu interativo. menu(); } @@ -103,18 +108,18 @@ public static void main(String[] args) { // mas diretamente na interface IFileSystem public static void menu() { while (true) { - System.out.println("\nComandos disponíveis:"); - System.out.println("1. chmod - Alterar permissões"); - System.out.println("2. mkdir - Criar diretório"); - System.out.println("3. rm - Remover arquivo/diretório"); - System.out.println("4. touch - Criar arquivo"); - System.out.println("5. write - Escrever em arquivo"); - System.out.println("6. read - Ler arquivo"); - System.out.println("7. mv - Mover/renomear arquivo"); - System.out.println("8. ls - Listar diretório"); - System.out.println("9. cp - Copiar arquivo"); - System.out.println("0. exit - Sair"); - System.out.print("\nDigite o comando desejado: "); + System.out.println("\nComandos disponiveis (POSIX):"); + System.out.println("1. chmod - Alterar permissoes de arquivo ou diretorio"); + System.out.println("2. mkdir - Criar diretorio (cria diretorios intermediarios automaticamente, como 'mkdir -p')"); + System.out.println("3. rm - Remover arquivo ou diretorio (pode ser recursivo)"); + System.out.println("4. touch - Criar arquivo vazio"); + System.out.println("5. write - Escrever dados em arquivo (pode anexar ou sobrescrever)"); + System.out.println("6. read - Ler dados de arquivo (leitura sequencial)"); + System.out.println("7. mv - Mover ou renomear arquivo/diretorio"); + System.out.println("8. ls - Listar conteudo de diretorio (pode ser recursivo)"); + System.out.println("9. cp - Copiar arquivo ou diretorio (pode ser recursivo)"); + System.out.println("0. exit - Sair do sistema de arquivos"); + System.out.print("\nDigite o numero do comando desejado: "); String opcao = scanner.nextLine(); try { @@ -165,25 +170,25 @@ public static void menu() { } public static void chmod() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho do arquivo ou diretório:"); + System.out.println("Insira o caminho do arquivo ou diretorio:"); String caminho = scanner.nextLine(); - System.out.println("Insira o usuário para o qual deseja alterar as permissões:"); + System.out.println("Insira o usuario para o qual deseja alterar as permissoes:"); String usuarioAlvo = scanner.nextLine(); - System.out.println("Insira a permissão (formato: 3 caracteres\"rwx\"):"); + System.out.println("Insira a permissao (formato: 3 caracteres\"rwx\"):"); String permissoes = scanner.nextLine(); fileSystem.chmod(caminho, user, usuarioAlvo, permissoes); } public static void mkdir() throws CaminhoJaExistenteException, PermissaoException { - System.out.println("Insira o caminho do diretório a ser criado:"); + System.out.println("Insira o caminho do diretorio a ser criado:"); String caminho = scanner.nextLine(); fileSystem.mkdir(caminho, user); } public static void rm() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho do diretório a ser removido:"); + System.out.println("Insira o caminho do diretorio a ser removido:"); String caminho = scanner.nextLine(); System.out.println("Remover recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); @@ -203,7 +208,7 @@ public static void write() throws CaminhoNaoEncontradoException, PermissaoExcept String caminho = scanner.nextLine(); System.out.println("Anexar? (true/false):"); boolean anexar = Boolean.parseBoolean(scanner.nextLine()); - System.out.println("Insira o conteúdo a ser escrito:"); + System.out.println("Insira o conteudo a ser escrito:"); String content = scanner.nextLine(); byte[] buffer = content.getBytes(); @@ -213,9 +218,23 @@ public static void write() throws CaminhoNaoEncontradoException, PermissaoExcept public static void read() throws CaminhoNaoEncontradoException, PermissaoException { System.out.println("Insira o caminho do arquivo a ser lido:"); String caminho = scanner.nextLine(); - byte[] buffer = new byte[READ_BUFFER_SIZE]; // Exemplo de tamanho de buffer por load/leitura . O que acontece se o Buffer for menor que o conteúdo a ser lido? - - fileSystem.read(caminho, user, buffer); // Lógica para ler arquivos maiores que o buffer deve ser implementada. + + // Descobre o tamanho real do arquivo usando o método auxiliar + int tamanho; + try { + // Acesso ao método auxiliar via cast para FileSystemImpl + FileSystem fsProxy = (FileSystem) fileSystem; + FileSystemImpl fsImpl = (FileSystemImpl) fsProxy.fileSystemImpl; + tamanho = fsImpl.getTamanhoArquivo(caminho); + } catch (Exception e) { + System.out.println("Erro ao obter tamanho do arquivo: " + e.getMessage()); + return; + } + + byte[] buffer = new byte[tamanho]; + fileSystem.read(caminho, user, buffer); + System.out.println("Conteudo do arquivo:"); + System.out.println(new String(buffer)); } public static void mv() throws CaminhoNaoEncontradoException, PermissaoException { @@ -228,7 +247,7 @@ public static void mv() throws CaminhoNaoEncontradoException, PermissaoException } public static void ls() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho do diretório a ser listado:"); + System.out.println("Insira o caminho do diretorio a ser listado:"); String caminho = scanner.nextLine(); System.out.println("Listar recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); diff --git a/filesys/FileSystem.java b/filesys/FileSystem.java index 8d7a83b..e622492 100644 --- a/filesys/FileSystem.java +++ b/filesys/FileSystem.java @@ -1,16 +1,20 @@ package filesys; +import java.util.List; + import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; +import model.Usuario; // Essa classe deve servir apenas como proxy para o FileSystemImpl final public class FileSystem implements IFileSystem { - private final IFileSystem fileSystemImpl; + public final IFileSystem fileSystemImpl; + + public FileSystem(List usuarios) { + fileSystemImpl = new FileSystemImpl(usuarios); - public FileSystem() { - fileSystemImpl = new FileSystemImpl(); } @Override diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 45fa05d..11163e2 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -3,68 +3,334 @@ import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; +import model.Arquivo; +import model.Diretorio; +import model.ElementoFS; +import model.Usuario; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; -// Implemente nesta classe o seu código do FileSystem. -// A classe pode ser alterada. -// O construtor, argumentos do construtor podem ser modificados -// e atributos & métodos privados podem ser adicionados public final class FileSystemImpl implements IFileSystem { - private static final String ROOT_USER = "root"; // pode ser necessário + private static final String ROOT_USER = "root"; + private Diretorio raiz; + private Map usuarios = new HashMap<>(); + private static Map usuariosGlobais = new HashMap<>(); + + public FileSystemImpl(List usuarios) { + this.raiz = new Diretorio("/", "rwx", ROOT_USER); + this.usuarios.put(ROOT_USER, new Usuario(ROOT_USER, "rwx", "/")); + for (Usuario usuario : usuarios) { + this.usuarios.put(usuario.getNome(), usuario); + usuariosGlobais.put(usuario.getNome(), usuario); + } + } + + public static Usuario getUsuarioGlobal(String nome) { + return usuariosGlobais.get(nome); + } - public FileSystemImpl() {} + private ElementoFS navegar(String caminho) throws CaminhoNaoEncontradoException { + if (caminho.equals("/")) return raiz; + String[] partes = caminho.split("/"); + Diretorio atual = raiz; + for (int i = 1; i < partes.length; i++) { + ElementoFS filho = atual.getFilhos().get(partes[i]); + if (filho == null) throw new CaminhoNaoEncontradoException("Caminho não encontrado: " + caminho); + if (i == partes.length - 1) return filho; + if (!filho.isArquivo()) { + atual = (Diretorio) filho; + } else { + throw new CaminhoNaoEncontradoException("Caminho não encontrado (esperado diretório): " + caminho); + } + } + return atual; + } + + public ElementoFS navegarParaTeste(String caminho) throws CaminhoNaoEncontradoException { + return navegar(caminho); + } @Override - public void mkdir(String caminho, String nome) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mkdir'"); + public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + if (caminho.equals("/")) return; + String[] partes = caminho.split("/"); + Diretorio atual = raiz; + StringBuilder caminhoAtual = new StringBuilder(); + + for (int i = 1; i < partes.length; i++) { + if (partes[i].isEmpty()) continue; + caminhoAtual.append("/").append(partes[i]); + Map filhos = atual.getFilhos(); + boolean ehUltimo = (i == partes.length - 1); + if (!filhos.containsKey(partes[i])) { + // Verifica permissão de escrita e execução no diretório atual + if (!atual.temPermissao(usuario, 'w') || !atual.temPermissao(usuario, 'x')) { + throw new PermissaoException("Sem permissão para criar em: " + caminhoAtual.substring(0, caminhoAtual.lastIndexOf("/"))); + } + // Cria o diretório intermediário + Diretorio novo = new Diretorio(partes[i], "rwx", usuario); + atual.adicionarFilho(novo); + } else if (ehUltimo) { + // Se o diretório final já existe, lança exceção + throw new CaminhoJaExistenteException("Diretório já existe: " + caminho); + } + // Avança para o próximo diretório + atual = (Diretorio) filhos.get(partes[i]); + } } @Override public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'chmod'"); + ElementoFS elem = navegar(caminho); + if (!elem.getDonoDiretorio().equals(usuario) && !usuario.equals(ROOT_USER)) { + throw new PermissaoException("Apenas o dono ou root pode alterar permissões."); + } + if (elem instanceof Diretorio) { + ((Diretorio) elem).setPermissaoUsuario(usuarioAlvo, permissao); + } else { + elem.setPermissoesPadrao(permissao); + } } + @Override public void rm(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'rm'"); + if (caminho.equals("/")) { + throw new PermissaoException("Não é permitido remover o diretório raiz."); + } + // Navega até o elemento a ser removido + ElementoFS elemento = navegar(caminho); + + // Descobre o diretório pai + int lastSlash = caminho.lastIndexOf('/'); + String caminhoPai = (lastSlash <= 0) ? "/" : caminho.substring(0, lastSlash); + Diretorio pai = (Diretorio) navegar(caminhoPai); + + // Verifica permissão de escrita no pai + if (!pai.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão para remover em: " + caminhoPai); + } + + if (!elemento.isArquivo()) { + Diretorio dir = (Diretorio) elemento; + if (!recursivo && !dir.getFilhos().isEmpty()) { + throw new PermissaoException("Diretório não está vazio. Use recursivo=true para remover tudo."); + } + // Remover recursivamente todos os filhos + if (recursivo) { + for (String filho : new java.util.ArrayList<>(dir.getFilhos().keySet())) { + rm(caminho + "/" + filho, usuario, true); + } + } + } + // Remove do pai + pai.removerFilho(elemento.getNomeDiretorio()); } @Override public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'touch'"); + if (caminho.equals("/") || caminho.endsWith("/")) throw new IllegalArgumentException("Nome de arquivo inválido."); + int lastSlash = caminho.lastIndexOf('/'); + String caminhoPai = (lastSlash <= 0) ? "/" : caminho.substring(0, lastSlash); + String nomeArquivo = caminho.substring(lastSlash + 1); + Diretorio pai; + try { + pai = (Diretorio) navegar(caminhoPai); + } catch (CaminhoNaoEncontradoException e) { + throw new PermissaoException("Diretório pai não encontrado: " + caminhoPai); + } + if (pai.getFilhos().containsKey(nomeArquivo)) throw new CaminhoJaExistenteException("Arquivo já existe: " + nomeArquivo); + if (!pai.temPermissao(usuario, 'w')) throw new PermissaoException("Sem permissão para criar arquivo em: " + caminhoPai); + pai.adicionarFilho(new Arquivo(nomeArquivo, "rw-", usuario)); + } + @Override public void write(String caminho, String usuario, boolean anexar, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'write'"); + ElementoFS elemento = navegar(caminho); + + if (!elemento.isArquivo()) { + throw new CaminhoNaoEncontradoException("O caminho não é um arquivo."); + } + Arquivo arquivo = (Arquivo) elemento; + + if (!arquivo.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita no arquivo."); + } + + final int TAMANHO_BLOCO = 4096; + + if (!anexar) { + arquivo.limparBlocos(); + } + + int pos = 0; + while (pos < buffer.length) { + int tamanhoRestante = buffer.length - pos; + int tamanhoBloco = Math.min(TAMANHO_BLOCO, tamanhoRestante); + byte[] bloco = new byte[tamanhoBloco]; + System.arraycopy(buffer, pos, bloco, 0, tamanhoBloco); + arquivo.adicionarBloco(bloco); + pos += tamanhoBloco; + } } @Override public void read(String caminho, String usuario, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'read'"); + ElementoFS elemento = navegar(caminho); + + if (!elemento.isArquivo()) { + throw new CaminhoNaoEncontradoException("O caminho não é um arquivo."); + } + Arquivo arquivo = (Arquivo) elemento; + + if (!arquivo.temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura no arquivo."); + } + + // Lê os blocos do arquivo e copia para o buffer até preencher ou acabar o arquivo + int posBuffer = 0; + for (byte[] bloco : arquivo.getBlocos()) { + int bytesParaCopiar = Math.min(bloco.length, buffer.length - posBuffer); + System.arraycopy(bloco, 0, buffer, posBuffer, bytesParaCopiar); + posBuffer += bytesParaCopiar; + if (posBuffer >= buffer.length) break; // buffer cheio + } } @Override public void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mv'"); + if (caminhoAntigo.equals("/") || caminhoNovo.equals("/")) { + throw new PermissaoException("Não é permitido mover ou renomear o diretório raiz."); + } + + // Navega até o elemento a ser movido + ElementoFS elemento = navegar(caminhoAntigo); + + // Descobre o diretório pai do antigo e do novo caminho + int lastSlashAntigo = caminhoAntigo.lastIndexOf('/'); + String caminhoPaiAntigo = (lastSlashAntigo <= 0) ? "/" : caminhoAntigo.substring(0, lastSlashAntigo); + Diretorio paiAntigo = (Diretorio) navegar(caminhoPaiAntigo); + + int lastSlashNovo = caminhoNovo.lastIndexOf('/'); + String caminhoPaiNovo = (lastSlashNovo <= 0) ? "/" : caminhoNovo.substring(0, lastSlashNovo); + Diretorio paiNovo = (Diretorio) navegar(caminhoPaiNovo); + + String nomeNovo = caminhoNovo.substring(lastSlashNovo + 1); + + // Permissão de escrita nos dois diretórios + if (!paiAntigo.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão para remover do diretório antigo: " + caminhoPaiAntigo); + } + if (!paiNovo.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão para criar no diretório novo: " + caminhoPaiNovo); + } + + // Remove do diretório antigo + paiAntigo.removerFilho(elemento.getNomeDiretorio()); + + // Renomeia se necessário + elemento.setNomeDiretorio(nomeNovo); + + // Adiciona ao novo diretório + paiNovo.adicionarFilho(elemento); } @Override public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'ls'"); + ElementoFS elemento = navegar(caminho); + + if (elemento.isArquivo()) { + if (!elemento.temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura no arquivo."); + } + System.out.println(elemento.getNomeDiretorio()); + return; + } + + Diretorio dir = (Diretorio) elemento; + if (!dir.temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura no diretório."); + } + + listarDiretorio(dir, caminho.equals("/") ? "" : caminho, usuario, recursivo, 0); + } + + private void listarDiretorio(Diretorio dir, String caminho, String usuario, boolean recursivo, int nivel) { + String prefixo = " ".repeat(nivel); + for (ElementoFS filho : dir.getFilhos().values()) { + System.out.println(prefixo + filho.getNomeDiretorio() + + (filho.isArquivo() ? "" : "/") + + " [owner=" + filho.getDonoDiretorio() + ", perms=" + filho.getPermissoesPadrao() + "]"); + if (recursivo && !filho.isArquivo()) { + listarDiretorio((Diretorio) filho, caminho + "/" + filho.getNomeDiretorio(), usuario, true, nivel + 1); + } + } } @Override public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'cp'"); + ElementoFS origem = navegar(caminhoOrigem); + + int lastSlash = caminhoDestino.lastIndexOf('/'); + String caminhoPaiDestino = (lastSlash <= 0) ? "/" : caminhoDestino.substring(0, lastSlash); + String nomeDestino = caminhoDestino.substring(lastSlash + 1); + Diretorio paiDestino = (Diretorio) navegar(caminhoPaiDestino); + + if (!origem.temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura na origem."); + } + if (!paiDestino.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita no destino."); + } + + if (origem.isArquivo()) { + // Copia arquivo + Arquivo arqOrigem = (Arquivo) origem; + Arquivo novoArquivo = new Arquivo(nomeDestino, arqOrigem.getPermissoesPadrao(), usuario); + // Copia blocos + for (byte[] bloco : arqOrigem.getBlocos()) { + novoArquivo.adicionarBloco(bloco.clone()); + } + paiDestino.adicionarFilho(novoArquivo); + } else { + // Copia diretório + if (!recursivo) { + throw new PermissaoException("Diretório só pode ser copiado com recursivo=true."); + } + Diretorio dirOrigem = (Diretorio) origem; + Diretorio novoDir = new Diretorio(nomeDestino, dirOrigem.getPermissoesPadrao(), usuario); + paiDestino.adicionarFilho(novoDir); + // Copia recursivamente os filhos + for (ElementoFS filho : dirOrigem.getFilhos().values()) { + cp(caminhoOrigem + "/" + filho.getNomeDiretorio(), + caminhoDestino + "/" + filho.getNomeDiretorio(), + usuario, true); + } + } } public void addUser(String user) { throw new UnsupportedOperationException("Método não implementado 'addUser'"); } + + public int getTamanhoArquivo(String caminho) throws CaminhoNaoEncontradoException { + ElementoFS elemento = navegar(caminho); + if (!elemento.isArquivo()) throw new CaminhoNaoEncontradoException("O caminho não é um arquivo."); + Arquivo arquivo = (Arquivo) elemento; + int tamanho = 0; + for (byte[] bloco : arquivo.getBlocos()) { + tamanho += bloco.length; + } + return tamanho; + } } diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index a0f0ce0..7b3409e 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -44,4 +44,5 @@ public interface IFileSystem { // Copia um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException; -} \ No newline at end of file +} + diff --git a/model/Arquivo.java b/model/Arquivo.java new file mode 100644 index 0000000..c9e5f0f --- /dev/null +++ b/model/Arquivo.java @@ -0,0 +1,72 @@ +package model; + +import java.util.ArrayList; +import java.util.List; + +public class Arquivo extends ElementoFS { + private List blocos; // conteúdo do arquivo + private long tamanho; + + + public Arquivo(String nome, String permissoes, String dono) { + super(nome, permissoes, dono); + this.blocos = new ArrayList<>(); + this.tamanho = 0; + } + + public void adicionarBloco(byte[] dados) { + blocos.add(dados); + tamanho += dados.length; + } + + public List getBlocos() { + return blocos; + } + + public long getTamanho() { + return tamanho; + } + + public void removerBloco(int index) { + if (index < 0 || index >= blocos.size()) { + throw new IndexOutOfBoundsException("Índice fora dos limites da lista."); + } + byte[] removed = blocos.remove(index); + tamanho -= removed.length; + } + + public void limparBlocos() { + blocos.clear(); + tamanho = 0; + } + + @Override + public boolean isArquivo() { + return true; + } + + @Override + public String toString() { + return "Arquivo: " + nomeDiretorio + " | Dono: " + donoDiretorio + " | Permissões: " + permissoesPadrao + " | Tam: " + + tamanho + " bytes"; + } + + public void adicionarFilho(ElementoFS filho) { + throw new UnsupportedOperationException("Não é possível adicionar filhos a um arquivo."); + } + + + public void removerFilho(String nomeFilho) { + throw new UnsupportedOperationException("Não é possível remover filhos de um arquivo."); + } + + @Override + public boolean temPermissao(String usuario, char tipoPermissao) { + if ("root".equals(usuario)) return true; + if (usuario.equals(donoDiretorio)) { + return permissoesPadrao.indexOf(tipoPermissao) >= 0; + } + // Arquivo não tem permissões específicas para outros usuários, só o dono pode acessar + return false; + } +} diff --git a/model/Bloco.java b/model/Bloco.java new file mode 100644 index 0000000..ae04dbd --- /dev/null +++ b/model/Bloco.java @@ -0,0 +1,19 @@ +package model; + +public class Bloco { + private static final int TAMANHO_BLOCO = 4096; // 4KB por bloco + private byte[] dados; + + + public Bloco(int tamanho) { + this.dados = new byte[TAMANHO_BLOCO]; + } + + public byte[] getDados() { + return dados; + } + + public void setDados(byte[] dados) { + this.dados = dados; + } +} \ No newline at end of file diff --git a/model/Diretorio.java b/model/Diretorio.java new file mode 100644 index 0000000..b5bcd6b --- /dev/null +++ b/model/Diretorio.java @@ -0,0 +1,84 @@ +package model; + +import java.util.HashMap; +import java.util.Map; + +import filesys.FileSystemImpl; + +public class Diretorio extends ElementoFS { + private Diretorio diretorioPai; + private Map filhos; + private Map acessosPorUsuario; // permissões específicas + + public Diretorio(String nome, String permissoes, String dono) { + super(nome, permissoes, dono); + if (permissoes == null || permissoes.length() != 3) { + throw new IllegalArgumentException("Permissões devem conter 3 caracteres (rwx)"); + } + this.filhos = new HashMap<>(); + this.acessosPorUsuario = new HashMap<>(); + } + + public void setPermissaoUsuario(String nomeUsuario, String permissoes) { + if (permissoes == null || permissoes.length() != 3) { + throw new IllegalArgumentException("Permissões devem conter 3 caracteres (rwx)"); + } + acessosPorUsuario.put(nomeUsuario, permissoes); + } + + public boolean temPermissao(String usuario, char tipoPermissao) { + if ("root".equals(usuario)) return true; + if (usuario.equals(donoDiretorio)) return permissoesPadrao.indexOf(tipoPermissao) >= 0; + String permissoesUsuario = acessosPorUsuario.get(usuario); + if (permissoesUsuario != null && permissoesUsuario.indexOf(tipoPermissao) >= 0) return true; + // Verifica permissão global do usuário (/**) + Usuario usuarioGlobal = FileSystemImpl.getUsuarioGlobal(usuario); + if (usuarioGlobal != null && usuarioGlobal.getDiretorio().equals("/**") && usuarioGlobal.getPermissao().indexOf(tipoPermissao) >= 0) { + return true; + } + if (diretorioPai != null) return diretorioPai.temPermissao(usuario, tipoPermissao); + return false; + } + + public String obterPermissoesDoUsuario(String usuario) { + if ("root".equals(usuario)) return "rwx"; + if (usuario.equals(donoDiretorio)) return permissoesPadrao; + return acessosPorUsuario.getOrDefault(usuario, "---"); + } + + public void adicionarFilho(ElementoFS filho) { + if (filhos.containsKey(filho.getNomeDiretorio())) { + throw new IllegalArgumentException("Já existe um filho com este nome: " + filho.getNomeDiretorio()); + } + if (filho instanceof Diretorio) { + ((Diretorio) filho).setDiretorioPai(this); + } + filhos.put(filho.getNomeDiretorio(), filho); + } + + public void removerFilho(String nomeFilho) { + filhos.remove(nomeFilho); + } + + public Map getFilhos() { + return filhos; + } + + public Diretorio getDiretorioPai() { + return diretorioPai; + } + + public void setDiretorioPai(Diretorio diretorioPai) { + this.diretorioPai = diretorioPai; + } + + @Override + public boolean isArquivo() { + return false; + } + + @Override + public String toString() { + return "Dir: " + nomeDiretorio + " | Owner: " + donoDiretorio + " | Perms: " + permissoesPadrao; + } +} \ No newline at end of file diff --git a/model/ElementoFS.java b/model/ElementoFS.java new file mode 100644 index 0000000..7a9045c --- /dev/null +++ b/model/ElementoFS.java @@ -0,0 +1,40 @@ +package model; + +public abstract class ElementoFS { + protected String nomeDiretorio; + protected String permissoesPadrao; + protected String donoDiretorio; + + public ElementoFS(String nomeDiretorio, String permissoesPadrao, String donoDiretorio) { + this.nomeDiretorio = nomeDiretorio; + this.permissoesPadrao = permissoesPadrao; + this.donoDiretorio = donoDiretorio; + } + + public String getNomeDiretorio() { + return nomeDiretorio; + } + + public void setNomeDiretorio(String nomeDiretorio) { + this.nomeDiretorio = nomeDiretorio; + } + + public String getPermissoesPadrao() { + return permissoesPadrao; + } + + public void setPermissoesPadrao(String permissoesPadrao) { + this.permissoesPadrao = permissoesPadrao; + } + + public String getDonoDiretorio() { + return donoDiretorio; + } + + public void setDonoDiretorio(String donoDiretorio) { + this.donoDiretorio = donoDiretorio; + } + + public abstract boolean isArquivo(); + public abstract boolean temPermissao(String usuario, char tipoPermissao); +} \ No newline at end of file diff --git a/model/Offset.java b/model/Offset.java new file mode 100644 index 0000000..9d653db --- /dev/null +++ b/model/Offset.java @@ -0,0 +1,53 @@ +package model; + +public class Offset { + private int valor; + private int max; + + public Offset() { + this.valor = 0; + this.max = Integer.MAX_VALUE; + } + + public Offset(int valorInicial) { + this.valor = valorInicial; + this.max = Integer.MAX_VALUE; + } + + public Offset(int valorInicial, int max) { + this.valor = valorInicial; + this.max = max; + } + + public int getValor() { + return valor; + } + + public void setValor(int valor) { + if (valor < 0) { + throw new IllegalArgumentException("Offset não pode ser negativo."); + } + if (valor > max) { + throw new IllegalArgumentException("Offset excede o valor máximo permitido (" + max + ")."); + } + this.valor = valor; + } + + public void incrementar(int delta) { + setValor(this.valor + delta); + } + + public void resetar() { + this.valor = 0; + } + + public int getMax() { + return max; + } + + public void setMax(int max) { + if (max < 0) throw new IllegalArgumentException("Max deve ser positivo."); + this.max = max; + if (valor > max) valor = max; + } +} diff --git a/model/Usuario.java b/model/Usuario.java new file mode 100644 index 0000000..3f41c0d --- /dev/null +++ b/model/Usuario.java @@ -0,0 +1,49 @@ +package model; + +public class Usuario { + private String nome; + private String diretorio; + private String permissao; + + public Usuario(String nome, String permissao, String diretorio) { + this.setNome(nome); + this.setPermissao(permissao); + this.setDiretorio(diretorio); + } + + public String getNome() { + return nome; + } + + public String getPermissao() { + return permissao; + } + + public void setPermissao(String permissao) { + this.permissao = permissao; + } + + public void setNome(String nome) { + this.nome = nome; + } + + public void setDiretorio(String diretorio) { + this.diretorio = diretorio; + } + + public String getDiretorio() { + return diretorio; + } + + @Override + public String toString() { + return "Usuario{" + + "nome='" + nome + '\'' + + ", permissao='" + permissao + '\'' + + ", diretorio='" + diretorio + '\'' + + '}'; + } + + + +} diff --git a/tests/FileSystemImplTest.java b/tests/FileSystemImplTest.java new file mode 100644 index 0000000..606ae3f --- /dev/null +++ b/tests/FileSystemImplTest.java @@ -0,0 +1,218 @@ +package tests; + +import exception.CaminhoJaExistenteException; +import exception.CaminhoNaoEncontradoException; +import exception.PermissaoException; +import filesys.FileSystemImpl; +import model.Usuario; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class FileSystemImplTest { + private FileSystemImpl fs; + List usuarios = new ArrayList<>(); + + @BeforeEach + void setUp() { + fs = new FileSystemImpl(usuarios); + } + + @Test + void mkdir_CriaDiretorioComSucesso() throws Exception { + fs.mkdir("/docs", "root"); + assertDoesNotThrow(() -> fs.mkdir("/docs2", "root")); + } + + @Test + void mkdir_LancaExcecaoSeDiretorioJaExiste() throws Exception { + fs.mkdir("/docs", "root"); + assertThrows(CaminhoJaExistenteException.class, () -> fs.mkdir("/docs", "root")); + } + + @Test + void mkdir_LancaExcecaoSeUsuarioSemPermissao() throws Exception { + fs.mkdir("/docs", "root"); + assertThrows(PermissaoException.class, () -> fs.mkdir("/docs/privado", "usuario")); + } + + @Test + void touch_CriaArquivoComSucesso() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + assertThrows(CaminhoJaExistenteException.class, () -> fs.touch("/docs/arquivo.txt", "root")); + } + + @Test + void touch_LancaExcecaoSeUsuarioSemPermissao() throws Exception { + fs.mkdir("/docs", "root"); + assertThrows(PermissaoException.class, () -> fs.touch("/docs/novo.txt", "usuario")); + } + + @Test + void chmod_AlteraPermissaoComSucesso() throws Exception { + fs.mkdir("/docs", "root"); + assertDoesNotThrow(() -> fs.chmod("/docs", "root", "usuario", "rwx")); + } + + @Test + void chmod_LancaExcecaoSeUsuarioSemPermissao() throws Exception { + fs.mkdir("/docs", "root"); + assertThrows(PermissaoException.class, () -> fs.chmod("/docs", "usuario", "usuario", "rwx")); + } + + @Test + void chmod_LancaExcecaoSeCaminhoNaoExiste() { + assertThrows(CaminhoNaoEncontradoException.class, () -> fs.chmod("/naoexiste", "root", "root", "rwx")); + } + + @Test + void rm_RemoveArquivoComSucesso() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + assertDoesNotThrow(() -> fs.rm("/docs/arquivo.txt", "root", false)); + // Verifica se realmente foi removido + assertThrows(exception.CaminhoNaoEncontradoException.class, () -> { + fs.rm("/docs/arquivo.txt", "root", false); + }); + } + + @Test + void rm_RemoveDiretorioRecursivo() throws Exception { + fs.mkdir("/docs", "root"); + fs.mkdir("/docs/sub", "root"); + fs.touch("/docs/sub/arquivo.txt", "root"); + assertDoesNotThrow(() -> fs.rm("/docs", "root", true)); + assertThrows(exception.CaminhoNaoEncontradoException.class, () -> { + fs.rm("/docs", "root", false); + }); + } + + @Test + void rm_LancaExcecaoSeDiretorioNaoVazioSemRecursivo() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + assertThrows(exception.PermissaoException.class, () -> fs.rm("/docs", "root", false)); + } + + @Test + void write_EscritaSimplesEmArquivo() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + byte[] dados = "Hello, World!".getBytes(); + fs.write("/docs/arquivo.txt", "root", false, dados); + model.Arquivo arq = (model.Arquivo) fs.navegarParaTeste("/docs/arquivo.txt"); + assertEquals(dados.length, arq.getTamanho()); + } + + @Test + void write_AppendEmArquivo() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + byte[] dados1 = "ABC".getBytes(); + byte[] dados2 = "DEF".getBytes(); + fs.write("/docs/arquivo.txt", "root", false, dados1); + fs.write("/docs/arquivo.txt", "root", true, dados2); + model.Arquivo arq = (model.Arquivo) fs.navegarParaTeste("/docs/arquivo.txt"); + assertEquals(6, arq.getTamanho()); + } + + @Test + void read_LeituraSimplesDeArquivo() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + byte[] dados = "Hello, World!".getBytes(); + fs.write("/docs/arquivo.txt", "root", false, dados); + + byte[] buffer = new byte[dados.length]; + fs.read("/docs/arquivo.txt", "root", buffer); + assertArrayEquals(dados, buffer); + } + + @Test + void mv_MoverArquivoComSucesso() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + fs.mkdir("/destino", "root"); + fs.mv("/docs/arquivo.txt", "/destino/novo.txt", "root"); + assertThrows(exception.CaminhoNaoEncontradoException.class, () -> fs.rm("/docs/arquivo.txt", "root", false)); + // Verifica se existe no novo local + fs.rm("/destino/novo.txt", "root", false); + } + + @Test + void ls_ListagemSimplesDeDiretorio() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo1.txt", "root"); + fs.touch("/docs/arquivo2.txt", "root"); + + // Redireciona a saída padrão para capturar o resultado do ls + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + PrintStream originalOut = System.out; + System.setOut(new PrintStream(outContent)); + + fs.ls("/docs", "root", false); + + System.setOut(originalOut); // Restaura saída padrão + + String output = outContent.toString(); + assertTrue(output.contains("arquivo1.txt")); + assertTrue(output.contains("arquivo2.txt")); + } + + @Test + void ls_ListagemRecursiva() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo1.txt", "root"); + fs.mkdir("/docs/subdir", "root"); + fs.touch("/docs/subdir/arquivo2.txt", "root"); + + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + PrintStream originalOut = System.out; + System.setOut(new PrintStream(outContent)); + + fs.ls("/docs", "root", true); + + System.setOut(originalOut); + + String output = outContent.toString(); + assertTrue(output.contains("arquivo1.txt")); + assertTrue(output.contains("subdir/")); + assertTrue(output.contains("arquivo2.txt")); + } + + @Test + void cp_CopiaArquivoComSucesso() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + byte[] dados = "abc".getBytes(); + fs.write("/docs/arquivo.txt", "root", false, dados); + + fs.cp("/docs/arquivo.txt", "/docs/copia.txt", "root", false); + + model.Arquivo arq = (model.Arquivo) fs.navegarParaTeste("/docs/copia.txt"); + assertEquals(dados.length, arq.getTamanho()); + } + + @Test + void cp_CopiaDiretorioRecursivo() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + fs.mkdir("/docs/subdir", "root"); + fs.touch("/docs/subdir/arquivo2.txt", "root"); + fs.cp("/docs", "/backup", "root", true); + + // Verifica se os arquivos foram copiados + model.Arquivo arq1 = (model.Arquivo) fs.navegarParaTeste("/backup/arquivo.txt"); + model.Arquivo arq2 = (model.Arquivo) fs.navegarParaTeste("/backup/subdir/arquivo2.txt"); + assertNotNull(arq1); + assertNotNull(arq2); + } +} diff --git a/tests/PermissionTest.java b/tests/PermissionTest.java index 6026e31..3493a0b 100644 --- a/tests/PermissionTest.java +++ b/tests/PermissionTest.java @@ -1,26 +1,97 @@ package tests; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import exception.CaminhoJaExistenteException; +import exception.CaminhoNaoEncontradoException; +import exception.PermissaoException; import filesys.FileSystemImpl; import filesys.IFileSystem; +import model.Usuario; -// Essa classe testa cenários de permissão public class PermissionTest { private static IFileSystem fileSystem; + static List usuarios = new ArrayList<>(); @BeforeAll public static void setUp() { - fileSystem = new FileSystemImpl(/*args...*/); + fileSystem = new FileSystemImpl(usuarios); } @Test public void testPermission() { - // Teste de permissão - assertTrue(true); + // Teste básico de permissão: root sempre pode criar diretório + assertDoesNotThrow(() -> fileSystem.mkdir("/test", "root")); + } + + @Test + public void testReadPermission() throws Exception { + fileSystem.mkdir("/docs", "root"); + fileSystem.touch("/docs/arquivo.txt", "root"); + + // root pode listar + assertDoesNotThrow(() -> fileSystem.ls("/docs", "root", false)); + + // chmod para negar leitura a outro usuário + fileSystem.chmod("/docs", "root", "joao", "---"); + assertThrows(PermissaoException.class, () -> fileSystem.ls("/docs", "joao", false)); + + // chmod para permitir leitura + fileSystem.chmod("/docs", "root", "joao", "r--"); + assertDoesNotThrow(() -> fileSystem.ls("/docs", "joao", false)); + } + + @Test + public void testWritePermission() throws Exception { + fileSystem.mkdir("/priv", "root"); + + // root pode criar arquivo + assertDoesNotThrow(() -> fileSystem.touch("/priv/novo.txt", "root")); + + // chmod para negar escrita a joao + fileSystem.chmod("/priv", "root", "joao", "r--"); + assertThrows(PermissaoException.class, () -> fileSystem.touch("/priv/joao.txt", "joao")); + + // chmod para permitir escrita + fileSystem.chmod("/priv", "root", "joao", "rw-"); + assertDoesNotThrow(() -> fileSystem.touch("/priv/joao2.txt", "joao")); + } + + @Test + public void testExecutePermission() throws Exception { + fileSystem.mkdir("/exec", "root"); + + // chmod para negar execução a joao + fileSystem.chmod("/exec", "root", "joao", "rw-"); + // Tenta criar subdiretório (precisa de x) + assertThrows(PermissaoException.class, () -> fileSystem.mkdir("/exec/sub", "joao")); + + // chmod para permitir execução + fileSystem.chmod("/exec", "root", "joao", "rwx"); + assertDoesNotThrow(() -> fileSystem.mkdir("/exec/sub", "joao")); + } + + @Test + public void testPermissionInheritance() throws Exception { + fileSystem.mkdir("/pai", "root"); + fileSystem.mkdir("/pai/filho", "root"); + + // Permissão só no pai + fileSystem.chmod("/pai", "root", "joao", "rwx"); + // Deve herdar permissão do pai + assertDoesNotThrow(() -> fileSystem.mkdir("/pai/filho/nieto", "joao")); + + // Remove permissão do pai, adiciona só no filho + fileSystem.chmod("/pai", "root", "joao", "---"); + fileSystem.chmod("/pai/filho", "root", "joao", "rwx"); + // Agora só pode criar dentro de filho, não no pai + assertThrows(PermissaoException.class, () -> fileSystem.mkdir("/pai/novo", "joao")); + assertDoesNotThrow(() -> fileSystem.mkdir("/pai/filho/novo", "joao")); } }