diff --git a/Main.java b/Main.java index 3b8673a..dc8da7f 100644 --- a/Main.java +++ b/Main.java @@ -1,4 +1,6 @@ import filesys.IFileSystem; +import filesys.Offset; +import filesys.Usuario; import java.util.Scanner; import java.io.FileNotFoundException; @@ -6,7 +8,7 @@ import exception.PermissaoException; import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; - +import exception.OperacaoInvalidaException; import filesys.FileSystem; // MENU INTERATIVO PARA O SISTEMA DE ARQUIVOS @@ -17,7 +19,6 @@ public class Main { // Para esse tipo de execução, o tamanho max do buffer de // leitura pode ser menor. private static final String ROOT_USER = "root"; - private static final String ROOT_DIR = "/"; private static final int READ_BUFFER_SIZE = 256; // Sistema de arquivos @@ -27,7 +28,7 @@ public class Main { private static Scanner scanner = new Scanner(System.in); // Usuário que está executando o programa - private static String user; + private static String username; // O sistema de arquivos é inteiramente virtual, ou seja, será reiniciado a cada execução do programa. // Logo, não é necessário salvar os arquivos em disco. O sistema será uma simulação em memória. @@ -39,7 +40,11 @@ public static void main(String[] args) { System.out.println("Usuário não fornecido"); return; } - user = args[1]; + username = args[1]; + + // Cria o sistema de arquivos + // O sistema de arquivos é inteiramente virtual, ou seja, será reiniciado a cada execução do programa. + fileSystem = new FileSystem(); // Carrega a lista de usuários do sistema a partir de arquivo // Formato do arquivo users: @@ -59,15 +64,25 @@ public static void main(String[] args) { String[] parts = line.split(" "); if (parts.length == 3) { String userListed = parts[0]; - String dir = parts[1]; + String dir = parts[1].substring(0, 1); // O primeiro caractere é o diretório String dirPermission = parts[2]; - /* A FAZER: + /* + A FAZER: * 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 + */ + + if (userListed.equals(ROOT_USER)) { + System.out.println("O usuário root já está adicionado."); + continue; + } + + Usuario user = new Usuario(userListed, dirPermission, dir); + fileSystem.addUser(user); } else { System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); @@ -77,22 +92,8 @@ public static void main(String[] args) { 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"); - return; } - - // 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?*/); - - // // 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()); - // } // Menu interativo. menu(); @@ -153,7 +154,7 @@ public static void menu() { default: System.out.println("Comando inválido!"); } - } catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException e) { + } catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException | OperacaoInvalidaException e) { System.out.println("Erro: " + e.getMessage()); } @@ -172,14 +173,14 @@ public static void chmod() throws CaminhoNaoEncontradoException, PermissaoExcept System.out.println("Insira a permissão (formato: 3 caracteres\"rwx\"):"); String permissoes = scanner.nextLine(); - fileSystem.chmod(caminho, user, usuarioAlvo, permissoes); + fileSystem.chmod(caminho, username, usuarioAlvo, permissoes); } - public static void mkdir() throws CaminhoJaExistenteException, PermissaoException { + public static void mkdir() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException, OperacaoInvalidaException { System.out.println("Insira o caminho do diretório a ser criado:"); String caminho = scanner.nextLine(); - fileSystem.mkdir(caminho, user); + fileSystem.mkdir(caminho, username); } public static void rm() throws CaminhoNaoEncontradoException, PermissaoException { @@ -188,17 +189,17 @@ public static void rm() throws CaminhoNaoEncontradoException, PermissaoException System.out.println("Remover recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); - fileSystem.rm(caminho, user, recursivo); + fileSystem.rm(caminho, username, recursivo); } - public static void touch() throws CaminhoJaExistenteException, PermissaoException { + public static void touch() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException, OperacaoInvalidaException { System.out.println("Insira o caminho do arquivo a ser criado:"); String caminho = scanner.nextLine(); - fileSystem.touch(caminho, user); + fileSystem.touch(caminho, username); } - public static void write() throws CaminhoNaoEncontradoException, PermissaoException { + public static void write() throws CaminhoNaoEncontradoException, PermissaoException, OperacaoInvalidaException { System.out.println("Insira o caminho do arquivo a ser escrito:"); String caminho = scanner.nextLine(); System.out.println("Anexar? (true/false):"); @@ -207,24 +208,37 @@ public static void write() throws CaminhoNaoEncontradoException, PermissaoExcept String content = scanner.nextLine(); byte[] buffer = content.getBytes(); - fileSystem.write(caminho, user, anexar, buffer); + fileSystem.write(caminho, username, anexar, buffer); } - public static void read() throws CaminhoNaoEncontradoException, PermissaoException { + public static void read() throws CaminhoNaoEncontradoException, PermissaoException, OperacaoInvalidaException { 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. - } + byte[] buffer = new byte[READ_BUFFER_SIZE]; + + Offset offset = new Offset(0); + int offsetAnterior; + int bytesLidos; + + do { + offsetAnterior = offset.getValue(); + fileSystem.read(caminho, username, buffer, offset); + bytesLidos = offset.getValue() - offsetAnterior; + + if (bytesLidos > 0) System.out.write(buffer, 0, bytesLidos); + } while (bytesLidos > 0); + System.out.flush(); + System.out.println(); + } + public static void mv() throws CaminhoNaoEncontradoException, PermissaoException { System.out.println("Insira o caminho do arquivo a ser movido:"); String caminhoAntigo = scanner.nextLine(); System.out.println("Insira o novo caminho do arquivo:"); String caminhoNovo = scanner.nextLine(); - fileSystem.mv(caminhoAntigo, caminhoNovo, user); + fileSystem.mv(caminhoAntigo, caminhoNovo, username); } public static void ls() throws CaminhoNaoEncontradoException, PermissaoException { @@ -233,7 +247,7 @@ public static void ls() throws CaminhoNaoEncontradoException, PermissaoException System.out.println("Listar recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); - fileSystem.ls(caminho, user, recursivo); + fileSystem.ls(caminho, username, recursivo); } public static void cp() throws CaminhoNaoEncontradoException, PermissaoException { @@ -244,6 +258,6 @@ public static void cp() throws CaminhoNaoEncontradoException, PermissaoException System.out.println("Copiar recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); - fileSystem.cp(caminhoOrigem, caminhoDestino, user, recursivo); + fileSystem.cp(caminhoOrigem, caminhoDestino, username, recursivo); } } diff --git a/exception/OperacaoInvalidaException.java b/exception/OperacaoInvalidaException.java new file mode 100644 index 0000000..9f446af --- /dev/null +++ b/exception/OperacaoInvalidaException.java @@ -0,0 +1,7 @@ +package exception; + +public class OperacaoInvalidaException extends Exception { + public OperacaoInvalidaException(String message) { + super(message); + } +} diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java new file mode 100644 index 0000000..dd59ac1 --- /dev/null +++ b/filesys/Arquivo.java @@ -0,0 +1,87 @@ +package filesys; + +import java.util.ArrayList; +import java.util.List; + +public class Arquivo extends Diretorio { + private static final int TAMANHO_BLOCO = 4096; // 4KB por bloco (ajuste conforme necessário) + private List blocos; + private long tamanho; + + public Arquivo(String nome, String permissoes, String dono) { + super(nome, permissoes, dono); + this.blocos = new ArrayList<>(); + this.tamanho = 0; + this.filhos = null; // Arquivos não podem ter filhos + } + + // Classe interna para representar blocos + public static class Bloco { + byte[] dados; + + Bloco() { + this.dados = new byte[TAMANHO_BLOCO]; + } + + public Bloco(byte[] dados2) { + this.dados = new byte[dados2.length]; + System.arraycopy(dados2, 0, this.dados, 0, Math.min(dados2.length, TAMANHO_BLOCO)); + } + + public byte[] getDados() { + return dados; + } + + } + + public void addBloco(Bloco bloco) { + this.blocos.add(bloco); + } + + public void clearBlocos() { + this.blocos.clear(); + this.tamanho = 0; + } + + public int getTamMaxBloco() { + return TAMANHO_BLOCO; + } + + public List getBlocos() { + return blocos; + } + + + + public void setBlocos(List blocos) { + this.blocos = blocos; + } + + public void incrementTamnho(long tamanho) { + this.tamanho += tamanho; + } + + public long getTamanho() { + return tamanho; + } + + @Override + public boolean isArquivo() { + return true; + } + + @Override + public void adicionarFilho(Diretorio filho) { + throw new UnsupportedOperationException("Arquivos não podem conter filhos."); + } + + @Override + public void removerFilho(String nome) { + throw new UnsupportedOperationException("Arquivos não podem conter filhos."); + } + + @Override + public String toString() { + return "A " + permissoes + " " + dono + " " + nome + " " + tamanho + " bytes"; + } +} \ No newline at end of file diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java new file mode 100644 index 0000000..18d4529 --- /dev/null +++ b/filesys/Diretorio.java @@ -0,0 +1,115 @@ +package filesys; + +import java.util.HashMap; +import java.util.Map; + +public class Diretorio { + protected String nome; + protected String permissoes; + protected String dono; + protected Diretorio pai; + protected Map filhos; + protected Map permissoesEspecificas; + + public Diretorio(String nome, String permissoes, String dono) { + this.nome = nome; + this.permissoes = permissoes; + this.dono = dono; + this.filhos = new HashMap<>(); + this.permissoesEspecificas = new HashMap<>(); + } + + public void setPermissaoUsuario(String usuario, String permissao) { + // Valida o formato da permissão (rwx com 3 caracteres) + if (permissao == null || permissao.length() != 3) { + throw new IllegalArgumentException("Permissão deve ter 3 caracteres (rwx)"); + } + permissoesEspecificas.put(usuario, permissao); + } + + public boolean temPermissao(String usuario, char permissaoNecessaria) { + // Root tem todas as permissões + if ("root".equals(usuario)) + return true; + + // Dono tem todas as permissões + if (usuario.equals(dono)) + return true; + + // Verifica permissões específicas do usuário no diretório atual + String permissoesUsuario = permissoesEspecificas.get(usuario); + if (permissoesUsuario != null && permissoesUsuario.indexOf(permissaoNecessaria) != -1) { + return true; + } + + // Se não houver permissões específicas, verifica permissões herdadas do pai + Diretorio pai = getPai(); // Método para obter o diretório pai + if (pai != null) { + return pai.temPermissao(usuario, permissaoNecessaria); + } + + return false; // Sem permissões + } + + public String getPermissoesUsuario(String usuario) { + if ("root".equals(usuario)) + return "rwx"; + if (usuario.equals(dono)) + return permissoes; + return permissoesEspecificas.getOrDefault(usuario, "---"); + } + + public String getNome() { + return nome; + } + + public String getPermissoes() { + return permissoes; + } + + public void setPermissoes(String permissoes) { + this.permissoes = permissoes; + } + + public String getDono() { + return dono; + } + + public void setNome(String nome) { + this.nome = nome; + } + + public void setDono(String dono) { + this.dono = dono; + } + + public Map getFilhos() { + return filhos; + } + + public Diretorio getPai() { + return pai; + } + + protected void setPai(Diretorio pai) { + this.pai = pai; + } + + public void adicionarFilho(Diretorio filho) { + filho.setPai(this); + filhos.put(filho.getNome(), filho); + } + + public void removerFilho(String nome) { + filhos.remove(nome); + } + + public boolean isArquivo() { + return false; // Por padrão, um `Diretorio` não é um arquivo + } + + @Override + public String toString() { + return "D " + permissoes + " " + dono + " " + nome; + } +} diff --git a/filesys/FileSystem.java b/filesys/FileSystem.java index 8d7a83b..8e2b5ba 100644 --- a/filesys/FileSystem.java +++ b/filesys/FileSystem.java @@ -2,9 +2,22 @@ import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; +import exception.OperacaoInvalidaException; import exception.PermissaoException; -// Essa classe deve servir apenas como proxy para o FileSystemImpl +/** + * Classe {@code FileSystem} atua como um proxy para a implementação real do + * sistema de arquivos {@link FileSystemImpl}. + * + *

+ * Essa classe encapsula a lógica interna e fornece uma interface padronizada + * para operações em sistemas de arquivos, + * delegando todas as chamadas de método à instância de {@code FileSystemImpl}. + * + *

+ * Inicializa com um diretório raiz padrão ("/") e um usuário padrão ("root") + * com permissões completas. + */ final public class FileSystem implements IFileSystem { private final IFileSystem fileSystemImpl; @@ -20,7 +33,8 @@ public void chmod(String caminho, String usuario, String usuarioAlvo, String per } @Override - public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + public void mkdir(String caminho, String usuario) + throws CaminhoJaExistenteException, PermissaoException, OperacaoInvalidaException { fileSystemImpl.mkdir(caminho, usuario); } @@ -31,20 +45,21 @@ public void rm(String caminho, String usuario, boolean recursivo) } @Override - public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException, + CaminhoNaoEncontradoException, OperacaoInvalidaException { fileSystemImpl.touch(caminho, usuario); } @Override public void write(String caminho, String usuario, boolean anexar, byte[] buffer) - throws CaminhoNaoEncontradoException, PermissaoException { + throws CaminhoNaoEncontradoException, PermissaoException, OperacaoInvalidaException { fileSystemImpl.write(caminho, usuario, anexar, buffer); } @Override - public void read(String caminho, String usuario, byte[] buffer) - throws CaminhoNaoEncontradoException, PermissaoException { - fileSystemImpl.read(caminho, usuario, buffer); + public void read(String caminho, String usuario, byte[] buffer, Offset offset) + throws CaminhoNaoEncontradoException, PermissaoException, OperacaoInvalidaException { + fileSystemImpl.read(caminho, usuario, buffer, offset); } @Override @@ -64,4 +79,14 @@ public void cp(String caminhoOrigem, String caminhoDestino, String usuario, bool throws CaminhoNaoEncontradoException, PermissaoException { fileSystemImpl.cp(caminhoOrigem, caminhoDestino, usuario, recursivo); } + + @Override + public void addUser(Usuario user) { + fileSystemImpl.addUser(user); + } + + @Override + public void removeUser(String username) { + fileSystemImpl.removeUser(username); + } } \ No newline at end of file diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 45fa05d..80eeac5 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -2,69 +2,265 @@ import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; +import exception.OperacaoInvalidaException; import exception.PermissaoException; +import filesys.util.ArquivoUtil; +import filesys.util.DiretorioUtil; +import filesys.util.NavegacaoUtil; +import filesys.util.UsuarioUtil; +import filesys.util.VerificacaoUtil; -// 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 +import java.util.HashSet; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * Classe {@code FileSystemImpl} implementa a interface {@link IFileSystem} e + * fornece a lógica para um sistema de arquivos + * hierárquico com suporte a permissões de usuários, manipulação de arquivos e + * diretórios. + * + *

+ * Essa classe gerencia um conjunto de usuários, um diretório raiz e oferece + * métodos para operações como criação, + * remoção, leitura, escrita, cópia e movimentação de arquivos e diretórios. + * Além disso, permite a configuração de + * permissões específicas para usuários em diferentes caminhos do sistema de + * arquivos. + * + *

+ * O sistema de arquivos é inicializado com um diretório raiz ("/") e um usuário + * padrão ("root") com permissões + * completas. Usuários adicionais podem ser adicionados e gerenciados + * dinamicamente. + * + *

+ * Principais funcionalidades: + *

    + *
  • Gerenciamento de usuários e permissões.
  • + *
  • Criação e remoção de arquivos e diretórios.
  • + *
  • Leitura e escrita em arquivos.
  • + *
  • Movimentação e cópia de arquivos e diretórios.
  • + *
  • Listagem de conteúdo de diretórios com suporte a recursividade.
  • + *
+ * + *

+ * Exceções específicas são lançadas para tratar erros como permissões + * insuficientes, caminhos inexistentes, + * ou tentativas de operações inválidas. + * + *

+ * Essa classe é imutável no sentido de que não permite a modificação direta de + * seus atributos internos, + * mas fornece métodos para manipular o estado do sistema de arquivos de forma + * controlada. + */ 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 Set users = new HashSet<>(); + private Diretorio root; + + public FileSystemImpl() { + users.add(new Usuario(ROOT_USER, "rwx", "/")); + root = new Diretorio("/", "rwx", ROOT_USER); + } - public FileSystemImpl() {} + @Override + public void addUser(Usuario user) { + if (users.stream().anyMatch(u -> u.getNome().equals(user.getNome()))) { + throw new IllegalArgumentException("Usuário com o mesmo nome já existe: " + user.getNome()); + } + users.add(user); + try { + // Navegar usando root para setar permissão do novo usuário + NavegacaoUtil.navegar(user.getDir(), root, ROOT_USER) + .setPermissaoUsuario(user.getNome(), user.getPermissao()); + } catch (CaminhoNaoEncontradoException e) { + try { + // Criar o diretório usando root + mkdir(user.getDir(), ROOT_USER); + // Navegar usando root para setar permissão do novo usuário + NavegacaoUtil.navegar(user.getDir(), root, ROOT_USER) + .setPermissaoUsuario(user.getNome(), user.getPermissao()); + } catch (Exception ex) { + throw new RuntimeException("Erro ao criar diretório para o usuário: " + user.getNome(), ex); + } + } catch (PermissaoException e) { + throw new RuntimeException("Erro de permissão inesperado ao adicionar usuário: " + user.getNome(), e); + } + } + + @Override + public void removeUser(String username) { + Usuario user = users.stream() + .filter(u -> u.getNome().equals(username)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Usuário não encontrado: " + username)); + if (user.getNome().equals(ROOT_USER)) + throw new IllegalArgumentException("Não é possível remover o usuário root."); + users.remove(user); + } @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, OperacaoInvalidaException { + if (caminho.equals("/")) + return; + StringTokenizer tokenizer = new StringTokenizer(caminho, "/"); + Diretorio atual = root; + StringBuilder caminhoAtual = new StringBuilder(); + while (tokenizer.hasMoreTokens()) { + String parte = tokenizer.nextToken(); + caminhoAtual.append("/").append(parte); + VerificacaoUtil.verificarPermissaoExecucao(atual, usuario, caminhoAtual.toString()); + + if (!existeFilho(atual, parte)) { + atual = DiretorioUtil.criarDiretorioFilho(atual, parte, caminhoAtual.toString(), usuario, users); + } else { + atual = DiretorioUtil.avancarParaDiretorioFilho(atual, parte); + } + } + + } + + private boolean existeFilho(Diretorio atual, String nome) { + return atual.getFilhos().containsKey(nome); } @Override public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'chmod'"); + Diretorio dir = NavegacaoUtil.navegar(caminho, root, usuario); + VerificacaoUtil.verificarPermissaoEscrita(dir, usuario, caminho); + dir.setPermissaoUsuario(usuarioAlvo, 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 é possível remover o diretório raiz."); + } + + Diretorio pai = NavegacaoUtil.obterDiretorioPai(caminho, root, usuario); + String nomeAlvo = NavegacaoUtil.obterNomeAlvo(caminho); + Diretorio alvo = NavegacaoUtil.obterAlvo(pai, nomeAlvo, caminho); + VerificacaoUtil.verificarPermissaoEscrita(alvo, usuario, caminho); + if (!alvo.isArquivo()) { + VerificacaoUtil.verificarRemocaoDiretorio(alvo, recursivo); + if (recursivo) { + DiretorioUtil.removerRecursivo(alvo, usuario); + } + } + pai.removerFilho(nomeAlvo); } + @Override - public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'touch'"); + public void touch(String caminho, String usuario) + throws CaminhoJaExistenteException, PermissaoException, + CaminhoNaoEncontradoException, OperacaoInvalidaException { + + VerificacaoUtil.verificarCaminhoArquivo(caminho); + String caminhoPai = NavegacaoUtil.extrairCaminhoPai(caminho); + String nomeArquivo = DiretorioUtil.extrairNomeArquivo(caminho); + + mkdir(caminhoPai, usuario); + Diretorio parent = NavegacaoUtil.navegar(caminhoPai, root, usuario); + VerificacaoUtil.verificarPermissaoEscrita(parent, usuario, caminhoPai); + VerificacaoUtil.verificarSeEhArquivo(parent); + + VerificacaoUtil.verificarSeArquivoOuDiretorioExiste(parent, nomeArquivo); + Usuario user = UsuarioUtil.buscarUsuario(usuario, users); + Arquivo novoArquivo = ArquivoUtil.criarNovoArquivo(nomeArquivo, user); + + parent.adicionarFilho(novoArquivo); } @Override public void write(String caminho, String usuario, boolean anexar, byte[] buffer) - throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'write'"); + throws CaminhoNaoEncontradoException, PermissaoException, OperacaoInvalidaException { + Diretorio dir = NavegacaoUtil.navegar(caminho, root,usuario); + VerificacaoUtil.verificarEscritaArquivo(dir, usuario); + ArquivoUtil.escreverBufferNoArquivo((Arquivo) dir, buffer, anexar); } @Override - public void read(String caminho, String usuario, byte[] buffer) - throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'read'"); + public void read(String caminho, String usuario, byte[] buffer, Offset offset) + throws CaminhoNaoEncontradoException, PermissaoException, OperacaoInvalidaException { + Diretorio dir = NavegacaoUtil.navegar(caminho, root,usuario); + VerificacaoUtil.verificarLeituraArquivo(dir, usuario); + Arquivo arquivo = (Arquivo) dir; + ArquivoUtil.atualizarOffsetLimite(offset, arquivo.getTamanho()); + if (offset.getValue() >= arquivo.getTamanho()) + return; + ArquivoUtil.lerDadosDoArquivo(arquivo, buffer, offset); } @Override public void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mv'"); + VerificacaoUtil.verificarMovimentacaoPermitida(caminhoAntigo, caminhoNovo); + + Diretorio paiAntigo = NavegacaoUtil.obterDiretorioPai(caminhoAntigo, root, usuario); + String nomeAntigo = DiretorioUtil.extrairNomeArquivo(caminhoAntigo); + Diretorio alvo = NavegacaoUtil.obterAlvo(paiAntigo, nomeAntigo, caminhoAntigo); + + VerificacaoUtil.verificarPermissaoEscrita(alvo, usuario, caminhoAntigo); + + boolean destinoExiste = false; + Diretorio destino; + try { + destino = NavegacaoUtil.navegar(caminhoNovo, root, usuario); + destinoExiste = true; + } catch (CaminhoNaoEncontradoException e) { + destinoExiste = false; + destino = null; + } + + if (destinoExiste && !destino.isArquivo()) { + // Se destino é um diretório existente, mova para dentro dele + VerificacaoUtil.verificarPermissaoEscrita(destino, usuario, caminhoNovo); + if (destino.getFilhos().containsKey(nomeAntigo)) { + throw new PermissaoException("Já existe um item com o mesmo nome no diretório de destino."); + } + ArquivoUtil.executarMovimentacao(paiAntigo, destino, nomeAntigo, nomeAntigo, alvo); + } else { + // Caso seja renomeação ou mudança para novo caminho + Diretorio paiNovo = NavegacaoUtil.obterDiretorioPai(caminhoNovo, root, usuario); + String nomeNovo = DiretorioUtil.extrairNomeArquivo(caminhoNovo); + + VerificacaoUtil.verificarPermissaoEscrita(paiNovo, usuario, caminhoNovo); + VerificacaoUtil.verificarDestinoDisponivel(paiNovo, nomeNovo); + + ArquivoUtil.executarMovimentacao(paiAntigo, paiNovo, nomeAntigo, nomeNovo, alvo); + } } @Override - public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'ls'"); + public void ls(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { + Diretorio dir = NavegacaoUtil.navegar(caminho, root, usuario); + VerificacaoUtil.verificarPermissaoLeitura(dir, usuario, caminho); + String output = DiretorioUtil.listar(dir, caminho, recursivo, usuario); + System.out.print(output); } @Override public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'cp'"); - } + Diretorio origem = NavegacaoUtil.navegar(caminhoOrigem, root, usuario); + Diretorio destino = NavegacaoUtil.navegar(caminhoDestino, root, usuario); + + VerificacaoUtil.verificarPermissoesParaCopia(origem, destino, usuario); - public void addUser(String user) { - throw new UnsupportedOperationException("Método não implementado 'addUser'"); + if (origem.isArquivo()) { + ArquivoUtil.copiarArquivo((Arquivo) origem, destino); + } else { + if (!recursivo) { + throw new PermissaoException("Diretório precisa de recursividade para cópia."); + } + ArquivoUtil.copiarDiretorio((Diretorio) origem, destino, usuario); + } } } diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index a0f0ce0..a31e31f 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -2,46 +2,167 @@ import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; +import exception.OperacaoInvalidaException; import exception.PermissaoException; -// Apenas modifique essa interface caso seja EXTREMAMENTE necessário. -// Documente & Justifique TODAS asalterações feitas. +/** + * Interface para as operações de um sistema de arquivos virtual. + * + *

IFyleSystem é implementada por FileSystemImpl, que contém a lógica de manipulação de arquivos e diretórios e FyleSystem atua como um proxy que delega as chamadas para a implementação real. + *

Essa interface define os métodos necessários para gerenciar usuários, permissões e operações de arquivos. + *

O sistema de arquivos é inicializado com um diretório raiz padrão ("/") e um usuário padrão ("root") + *

Não é necessário passar parâmetros para o construtor e nem adicionar o diretório e o usuário root + *

Importante: O usuário root não pode ser removido. + */ public interface IFileSystem { - // Altera as permissões de um arquivo ou diretório. - // Configura a permissao do caminho para o usuarioAlvo. - // Apenas o usuario root ou que tenha permissão de rw do caminho podem alterar as permissões. - void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException; - - // Cria um novo diretório. Se o diretório já existir, será lançada uma exceção. - // Se o usuario não tiver permissão para criar o diretório, será lançada uma exceção. - void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException; - - // Remove um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. - // Caso recursivo seja true, o diretório será removido recursivamente. - void rm(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException; - - // Cria um novo arquivo. Atenção: Se o arquivo já existir, será lançada uma exceção. - void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException; - - // Escreve dados em um arquivo. Se o diretório não existir, será lançada uma exceção. - // Caso o diretório exista, o arquivo será criado ou sobrescrito. - // Caso anexar (append) seja true, os dados serão adicionados ao final do arquivo. - // Escrita sequencial. - void write(String caminho, String usuario, boolean anexar, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException; - - // Lê dados de um arquivo. Se o arquivo não existir, será lançada uma exceção. - // Leitura sequencial - todo o conteudo do arquivo sera lido e armazenado no buffer. - void read(String caminho, String usuario, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException; - - // Move ou renomeia um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. - // Se o diretório já existir, será sobrescrito. - // mv é naturalmente recursivo. - void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException; - - // Lista o conteúdo de um diretório. Se o diretório não existir, será lançada uma exceção. - // Caso recursivo seja true, todo o conteúdo do diretório será listado recursivamente. - void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException; - - // 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 + + /** + * Adiciona um novo usuário ao sistema. + * + * @param user Usuário a ser adicionado. + */ + void addUser(Usuario user); + + /** + * Remove um usuário do sistema. + * + * Importante: O usuário root não pode ser removido. + * + * @param username Nome do usuário a ser removido. + */ + void removeUser(String username); + + /** + * Altera as permissões de um caminho para um usuário específico. + * + *

+ * Apenas o usuário root ou aquele com permissão rw no caminho pode alterar permissões. + *

+ * + * @param caminho Caminho do arquivo ou diretório. + * @param usuario Usuário que está realizando a operação. + * @param usuarioAlvo Usuário cujas permissões serão alteradas. + * @param permissao Novas permissões (ex: "rwx"). + * @throws CaminhoNaoEncontradoException Se o caminho não existir. + * @throws PermissaoException Se o usuário não tiver permissão para modificar. + */ + void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) + throws CaminhoNaoEncontradoException, PermissaoException; + + /** + * Cria um novo diretório. + * + * @param caminho Caminho completo do diretório a ser criado. + * @param usuario Usuário que está criando o diretório. + * @throws CaminhoJaExistenteException Se o diretório já existir. + * @throws PermissaoException Se o usuário não tiver permissão para criar o diretório. + * @throws CaminhoNaoEncontradoException Se o caminho pai não existir. + * @throws OperacaoInvalidaException + */ + void mkdir(String caminho, String usuario) + throws CaminhoJaExistenteException, PermissaoException, OperacaoInvalidaException; + + /** + * Remove um arquivo ou diretório. + * + * @param caminho Caminho do arquivo ou diretório a ser removido. + * @param usuario Usuário que realiza a remoção. + * @param recursivo Se true, remove diretórios recursivamente. + * @throws CaminhoNaoEncontradoException Se o caminho não existir. + * @throws PermissaoException Se o usuário não tiver permissão de remoção. + */ + void rm(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException; + + /** + * Cria um novo arquivo vazio. + * + * Atenção: Se o arquivo já existir, será lançada uma exceção. + * + * @param caminho Caminho do arquivo. + * @param usuario Usuário que está criando o arquivo. + * @throws CaminhoJaExistenteException Se o arquivo já existir. + * @throws PermissaoException Se o usuário não tiver permissão de criação. + * @throws CaminhoNaoEncontradoException Se o caminho pai não existir. + * @throws OperacaoInvalidaException + */ + void touch(String caminho, String usuario) + throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException, OperacaoInvalidaException; + + /** + * Escreve dados em um arquivo. + * + *

+ * Caso o arquivo não exista, será lançado um erro. + * Se anexar for true, os dados serão adicionados ao final do arquivo. + *

+ * + * @param caminho Caminho do arquivo. + * @param usuario Usuário que está escrevendo. + * @param anexar Se true, realiza operação de append. + * @param buffer Dados a serem escritos. + * @throws CaminhoNaoEncontradoException Se o arquivo não existir. + * @throws PermissaoException Se o usuário não tiver permissão de escrita. + * @throws OperacaoInvalidaException + */ + void write(String caminho, String usuario, boolean anexar, byte[] buffer) + throws CaminhoNaoEncontradoException, PermissaoException, OperacaoInvalidaException; + + /** + * Lê dados de um arquivo sequencialmente. + * + *

Todo o conteúdo do arquivo será lido a partir de um Offset e armazenado no buffer. + *

+ * + * @param caminho Caminho do arquivo. + * @param usuario Usuário que está lendo. + * @param buffer Buffer onde os dados serão armazenados. + * @param offset Objeto que representa o deslocamento atual da leitura. + * @throws CaminhoNaoEncontradoException Se o arquivo não existir. + * @throws PermissaoException Se o usuário não tiver permissão de leitura. + * @throws OperacaoInvalidaException + */ + void read(String caminho, String usuario, byte[] buffer, Offset offset) + throws CaminhoNaoEncontradoException, PermissaoException, OperacaoInvalidaException; + + /** + * Move ou renomeia um arquivo ou diretório. + * + *

+ * A operação é recursiva. Se o destino já existir, será sobrescrito. + *

+ * + * @param caminhoAntigo Caminho original. + * @param caminhoNovo Novo caminho. + * @param usuario Usuário que está realizando a operação. + * @throws CaminhoNaoEncontradoException Se o caminho antigo não existir. + * @throws PermissaoException Se o usuário não tiver permissão. + */ + void mv(String caminhoAntigo, String caminhoNovo, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException; + + /** + * Lista o conteúdo de um diretório. + * + * @param caminho Caminho do diretório. + * @param usuario Usuário que está acessando. + * @param recursivo Se true, lista recursivamente os subdiretórios. + * @throws CaminhoNaoEncontradoException Se o diretório não existir. + * @throws PermissaoException Se o usuário não tiver permissão de leitura. + */ + void ls(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException; + + /** + * Copia um arquivo ou diretório. + * + * @param caminhoOrigem Caminho de origem. + * @param caminhoDestino Caminho de destino. + * @param usuario Usuário que está realizando a operação. + * @param recursivo Se true, copia diretórios recursivamente. + * @throws CaminhoNaoEncontradoException Se a origem não existir. + * @throws PermissaoException Se o usuário não tiver permissão. + */ + void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException; +} \ No newline at end of file diff --git a/filesys/Offset.java b/filesys/Offset.java new file mode 100644 index 0000000..56430a8 --- /dev/null +++ b/filesys/Offset.java @@ -0,0 +1,53 @@ +package filesys; + +public class Offset { + private int value; + private int max; + + public Offset() { + this.setValue(0); + this.setMax(Integer.MAX_VALUE); + } + + public Offset(int initialValue) { + this.setValue(initialValue); + this.setMax(Integer.MAX_VALUE); + } + + public Offset(int initialValue, int max) { + this.setValue(initialValue); + this.setMax(max); + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + if (value < 0) { + throw new IllegalArgumentException("Offset não pode ser negativo."); + } + if (value > max) { + throw new IllegalArgumentException("Offset excede o valor máximo permitido (" + max + ")."); + } + this.value = value; + } + + public void add(int delta) { + setValue(this.value + delta); + } + + public void reset() { + this.value = 0; + } + + public void setMax(int max) { + if (max < 0) throw new IllegalArgumentException("Max deve ser positivo."); + this.max = max; + if (value > max) value = max; // ajusta valor atual se necessário + } + + public int getMax() { + return max; + } +} diff --git a/filesys/Usuario.java b/filesys/Usuario.java new file mode 100644 index 0000000..ea93b5d --- /dev/null +++ b/filesys/Usuario.java @@ -0,0 +1,46 @@ +package filesys; + +public class Usuario { + private String nome; + private String permissao; + private String dir; + + public Usuario(String nome, String permissao, String dir) { + this.setNome(nome); + this.setPermissao(permissao); + this.setDir(dir); + } + + public String getNome() { + return nome; + } + + public void setNome(String nome) { + this.nome = nome; + } + + public String getPermissao() { + return permissao; + } + + public void setPermissao(String permissao) { + this.permissao = permissao; + } + + public String getDir() { + return dir; + } + + public void setDir(String dir) { + this.dir = dir; + } + + @Override + public String toString() { + return "Usuario{" + + "nome='" + nome + '\'' + + ", permissao='" + permissao + '\'' + + ", dir='" + dir + '\'' + + '}'; + } +} diff --git a/filesys/util/ArquivoUtil.java b/filesys/util/ArquivoUtil.java new file mode 100644 index 0000000..7379674 --- /dev/null +++ b/filesys/util/ArquivoUtil.java @@ -0,0 +1,96 @@ +package filesys.util; + +import exception.PermissaoException; +import filesys.Arquivo; +import filesys.Diretorio; +import filesys.Offset; +import filesys.Usuario; + +public class ArquivoUtil { + + private ArquivoUtil() { + } + + public static Arquivo criarNovoArquivo(String nomeArquivo, Usuario user) { + return new Arquivo(nomeArquivo, user.getPermissao(), user.getNome()); + } + + public static void escreverBufferNoArquivo(Arquivo arquivo, byte[] buffer, boolean anexar) { + if (!anexar) { + arquivo.clearBlocos(); + } + + int offset = 0; + int tamMax = arquivo.getTamMaxBloco(); + + while (offset < buffer.length) { + int length = Math.min(buffer.length - offset, tamMax); + byte[] dados = new byte[length]; + System.arraycopy(buffer, offset, dados, 0, length); + + arquivo.addBloco(new Arquivo.Bloco(dados)); + arquivo.incrementTamnho(length); + offset += length; + } + } + + public static void atualizarOffsetLimite(Offset offset, long fileSize) { + offset.setMax((int) fileSize); + } + + public static void lerDadosDoArquivo(Arquivo arquivo, byte[] buffer, Offset offset) { + int arquivoOffset = offset.getValue(); + int bufferOffset = 0; + + for (Arquivo.Bloco bloco : arquivo.getBlocos()) { + if (arquivoOffset >= bloco.getDados().length) { + arquivoOffset -= bloco.getDados().length; + continue; + } + + int bytesDisponiveis = bloco.getDados().length - arquivoOffset; + int bytesParaLer = Math.min(buffer.length - bufferOffset, bytesDisponiveis); + System.arraycopy(bloco.getDados(), arquivoOffset, buffer, bufferOffset, bytesParaLer); + + bufferOffset += bytesParaLer; + arquivoOffset = 0; + + if (bufferOffset >= buffer.length) + break; + } + + offset.add(bufferOffset); + } + + public static void executarMovimentacao(Diretorio paiAntigo, Diretorio paiNovo, String nomeAntigo, String nomeNovo, + Diretorio alvo) { + paiAntigo.removerFilho(nomeAntigo); + alvo.setNome(nomeNovo); + paiNovo.adicionarFilho(alvo); + } + + public static void copiarArquivo(Arquivo origem, Diretorio destino) { + Arquivo copia = new Arquivo(origem.getNome(), origem.getPermissoes(), origem.getDono()); + for (Arquivo.Bloco bloco : origem.getBlocos()) { + copia.addBloco(bloco); + copia.incrementTamnho(bloco.getDados().length); + } + destino.adicionarFilho(copia); + } + + public static void copiarDiretorio(Diretorio origem, Diretorio destino, String usuario) throws PermissaoException { + Diretorio copia = new Diretorio(origem.getNome(), origem.getPermissoes(), origem.getDono()); + destino.adicionarFilho(copia); + for (Diretorio filho : origem.getFilhos().values()) { + if (filho.isArquivo()) { + copiarArquivo((Arquivo) filho, copia); + } else { + copiarDiretorio(filho, copia, usuario); + } + } + } + + + + +} diff --git a/filesys/util/DiretorioUtil.java b/filesys/util/DiretorioUtil.java new file mode 100644 index 0000000..a7f9dfe --- /dev/null +++ b/filesys/util/DiretorioUtil.java @@ -0,0 +1,85 @@ +package filesys.util; + +import java.util.Map; +import java.util.Set; +import exception.OperacaoInvalidaException; +import exception.PermissaoException; +import filesys.Diretorio; +import filesys.Usuario; + +public class DiretorioUtil { + + private DiretorioUtil() { + } + + public static Diretorio criarDiretorioFilho(Diretorio atual, String nome, String caminhoAtual, String usuario, + Set users) + throws PermissaoException { + VerificacaoUtil.verificarPermissaoEscrita(atual, usuario, caminhoAtual); + Usuario user = UsuarioUtil.buscarUsuario(usuario, users); + Diretorio novo = new Diretorio(nome, atual.getPermissoes(), user.getNome()); + atual.adicionarFilho(novo); + return novo; + } + + public static Diretorio avancarParaDiretorioFilho(Diretorio atual, String nome) + throws OperacaoInvalidaException, PermissaoException { + Diretorio filho = atual.getFilhos().get(nome); + + if (filho == null) { + throw new OperacaoInvalidaException("Diretório não encontrado: " + nome); + } + + if (filho.isArquivo()) { + throw new OperacaoInvalidaException("Não é possível criar um diretório dentro de um arquivo."); + } + VerificacaoUtil.verificarPermissaoExecucao(filho, filho.getDono(), nome); + + return filho; + } + + public static void removerRecursivo(Diretorio dir, String usuario) throws PermissaoException { + for (Diretorio filho : dir.getFilhos().values()) { + VerificacaoUtil.verificarPermissaoEscrita(filho, usuario, dir.getNome()); + if (!filho.isArquivo()) { + removerRecursivo(filho, usuario); + } + } + dir.getFilhos().clear(); + } + + public static String extrairNomeArquivo(String caminho) { + return caminho.substring(caminho.lastIndexOf('/') + 1); + } + + private static void listarDiretorioAtual(Diretorio dir, String caminho, StringBuilder output) { + output.append(caminho).append(":\n"); + for (Map.Entry entry : dir.getFilhos().entrySet()) { + Diretorio filho = entry.getValue(); + output.append(" ").append(filho.toString()).append("\n"); + } + } + + private static void listarRecursivamente(Diretorio dir, String caminho, StringBuilder output, String usuario) { + for (Map.Entry entry : dir.getFilhos().entrySet()) { + Diretorio filho = entry.getValue(); + if (!filho.isArquivo()) { + String novoCaminho = caminho.equals("/") ? "/" + filho.getNome() : caminho + "/" + filho.getNome(); + listarDiretorioAtual(filho, novoCaminho, output); + listarRecursivamente(filho, novoCaminho, output, usuario); + } + } + } + + public static String listar(Diretorio dir, String caminho, boolean recursivo, String usuario) { + StringBuilder output = new StringBuilder(); + listarDiretorioAtual(dir, caminho, output); + + if (recursivo) { + listarRecursivamente(dir, caminho, output, usuario); + } + + return output.toString(); + } + +} diff --git a/filesys/util/NavegacaoUtil.java b/filesys/util/NavegacaoUtil.java new file mode 100644 index 0000000..f9d1481 --- /dev/null +++ b/filesys/util/NavegacaoUtil.java @@ -0,0 +1,63 @@ +package filesys.util; + +import java.util.StringTokenizer; + +import exception.CaminhoNaoEncontradoException; +import exception.PermissaoException; +import filesys.Diretorio; + +public class NavegacaoUtil { + private NavegacaoUtil() { + } + + public static Diretorio navegar(String caminho, Diretorio root, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException { + + if (caminho.equals("/") || caminho.isEmpty()) { + VerificacaoUtil.verificarPermissaoExecucao(root, usuario, caminho); + return root; + } + + Diretorio atual = root; + StringTokenizer tokenizer = new StringTokenizer(caminho, "/"); + StringBuilder caminhoAtual = new StringBuilder("/"); + + while (tokenizer.hasMoreTokens()) { + String parte = tokenizer.nextToken(); + + if (!atual.getFilhos().containsKey(parte)) { + throw new CaminhoNaoEncontradoException("Caminho não encontrado: " + caminho); + } + + atual = atual.getFilhos().get(parte); + caminhoAtual.append(parte).append("/"); + VerificacaoUtil.verificarPermissaoExecucao(atual, usuario, caminhoAtual.toString()); + } + + return atual; + } + + public static Diretorio obterDiretorioPai(String caminho, Diretorio root, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException { + String caminhoPai = caminho.substring(0, caminho.lastIndexOf('/')); + return navegar(caminhoPai.isEmpty() ? "/" : caminhoPai, root, usuario); + } + + public static Diretorio obterAlvo(Diretorio pai, String nomeAlvo, String caminho) + throws CaminhoNaoEncontradoException { + Diretorio alvo = pai.getFilhos().get(nomeAlvo); + if (alvo == null) { + throw new CaminhoNaoEncontradoException("Caminho não encontrado: " + caminho); + } + return alvo; + } + + public static String extrairCaminhoPai(String caminho) { + int lastSlash = caminho.lastIndexOf('/'); + return (lastSlash <= 0) ? "/" : caminho.substring(0, lastSlash); + } + + public static String obterNomeAlvo(String caminho) { + return caminho.substring(caminho.lastIndexOf('/') + 1); + } +} diff --git a/filesys/util/UsuarioUtil.java b/filesys/util/UsuarioUtil.java new file mode 100644 index 0000000..accba65 --- /dev/null +++ b/filesys/util/UsuarioUtil.java @@ -0,0 +1,19 @@ +package filesys.util; + +import java.util.Set; + +import exception.PermissaoException; +import filesys.Usuario; + +public class UsuarioUtil { + + private UsuarioUtil() { + } + + public static Usuario buscarUsuario(String nome, Set users) throws PermissaoException { + return users.stream() + .filter(u -> u.getNome().equals(nome)) + .findFirst() + .orElseThrow(() -> new PermissaoException("Usuário não encontrado: " + nome)); + } +} diff --git a/filesys/util/VerificacaoUtil.java b/filesys/util/VerificacaoUtil.java new file mode 100644 index 0000000..fabbe75 --- /dev/null +++ b/filesys/util/VerificacaoUtil.java @@ -0,0 +1,108 @@ +package filesys.util; + +import exception.CaminhoJaExistenteException; +import exception.OperacaoInvalidaException; +import exception.PermissaoException; +import filesys.Diretorio; + +public class VerificacaoUtil { + + private VerificacaoUtil() { + } + + private static void verificarPermissao(Diretorio dir, String usuario, char tipoPermissao, String mensagemErro) + throws PermissaoException { + if (!dir.temPermissao(usuario, tipoPermissao)) { + throw new PermissaoException(mensagemErro); + } + } + + public static void verificarPermissaoEscrita(Diretorio diretorio, String usuario, String caminhoAtual) + throws PermissaoException { + verificarPermissao(diretorio, usuario, 'w', "Sem permissão de escrita para acessar: " + caminhoAtual); + } + + public static void verificarPermissaoExecucao(Diretorio diretorio, String usuario, String caminhoAtual) + throws PermissaoException { + verificarPermissao(diretorio, usuario, 'x', "Sem permissão de execução para acessar: " + caminhoAtual); + } + + public static void verificarPermissaoLeitura(Diretorio dir, String usuario, String caminho) + throws PermissaoException { + verificarPermissao(dir, usuario, 'r', "Sem permissão de leitura para acessar: " + caminho); + } + + public static void verificarRemocaoDiretorio(Diretorio alvo, boolean recursivo) throws PermissaoException { + if (!recursivo && !alvo.getFilhos().isEmpty()) { + throw new PermissaoException("Diretório não está vazio. Use recursivo=true."); + } + } + + public static void verificarCaminhoArquivo(String caminho) throws OperacaoInvalidaException { + if (caminho.equals("/") || caminho.endsWith("/")) { + throw new OperacaoInvalidaException("Nome de arquivo inválido."); + } + } + + public static void verificarSeEhArquivo(Diretorio parent) throws OperacaoInvalidaException { + if (parent.isArquivo()) { + throw new OperacaoInvalidaException("Não é possível criar um arquivo dentro de um arquivo."); + } + } + + public static void verificarSeArquivoOuDiretorioExiste(Diretorio parent, String nomeArquivo) + throws CaminhoJaExistenteException, OperacaoInvalidaException { + + Diretorio existente = parent.getFilhos().get(nomeArquivo); + if (existente != null) { + if (existente.isArquivo()) { + throw new CaminhoJaExistenteException("Arquivo já existe: " + nomeArquivo); + } else { + throw new OperacaoInvalidaException( + "Não é possível criar um arquivo: já existe um diretório com o nome '" + nomeArquivo + "'"); + } + } + } + + public static void verificarEscritaArquivo(Diretorio dir, String usuario) + throws OperacaoInvalidaException, PermissaoException { + if (!dir.isArquivo()) { + throw new OperacaoInvalidaException("Não é possível escrever em um diretório."); + } + if (!dir.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita."); + } + } + + public static void verificarLeituraArquivo(Diretorio dir, String usuario) + throws OperacaoInvalidaException, PermissaoException { + if (!dir.isArquivo()) + throw new OperacaoInvalidaException("Não é possível ler de um diretório."); + if (!dir.temPermissao(usuario, 'r')) + throw new PermissaoException("Sem permissão de leitura."); + } + + public static void verificarMovimentacaoPermitida(String origem, String destino) throws PermissaoException { + if (origem.equals("/") || destino.equals("/")) { + throw new PermissaoException("Não é possível mover o diretório raiz."); + } + } + + public static void verificarDestinoDisponivel(Diretorio paiDestino, String nomeDestino) + throws PermissaoException { + if (paiDestino.getFilhos().containsKey(nomeDestino)) { + throw new PermissaoException("Já existe um item no destino com esse nome."); + } + } + + public static void verificarPermissoesParaCopia(Diretorio origem, Diretorio destino, String usuario) + throws PermissaoException { + if (!origem.temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura na origem."); + } + if (!destino.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita no destino."); + } + } + +} diff --git a/tests/FileSystemTest.java b/tests/FileSystemTest.java new file mode 100644 index 0000000..58c53e7 --- /dev/null +++ b/tests/FileSystemTest.java @@ -0,0 +1,95 @@ +package tests; + +import exception.CaminhoJaExistenteException; +import exception.CaminhoNaoEncontradoException; +import exception.OperacaoInvalidaException; +import exception.PermissaoException; +import filesys.*; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FileSystemTest { + + private static IFileSystem fileSystem; + private static Usuario user; + + @BeforeAll + static void setUp() { + fileSystem = new FileSystem(); + } + + @Test + void testChmod() throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException, OperacaoInvalidaException { + fileSystem.mkdir("/chmodTest", "root"); + fileSystem.addUser(new Usuario("alvim", "r-x", "/chmodTest")); + assertDoesNotThrow(() -> fileSystem.chmod("/chmodTest", "root", "alvim", "rw-")); + } + + @Test + void testMkdir() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + assertDoesNotThrow(() -> fileSystem.mkdir("/mkdirTest", "root")); + assertDoesNotThrow(() -> fileSystem.mkdir("/mkdirTest2/subdir", "root")); + } + + @Test + void testRm() throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException, OperacaoInvalidaException { + fileSystem.mkdir("/rmTest", "root"); + assertDoesNotThrow(() -> fileSystem.rm("/rmTest", "root", true)); + } + + @Test + void testTouch() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException, OperacaoInvalidaException { + fileSystem.mkdir("/touchDir", "root"); + assertDoesNotThrow(() -> fileSystem.touch("/touchDir/file.txt", "root")); + assertDoesNotThrow(() -> fileSystem.touch("/touchDir2/file2.txt", "root")); + } + + @Test + void testWriteAndRead() throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException, OperacaoInvalidaException { + fileSystem.mkdir("/rwDir", "root"); + fileSystem.touch("/rwDir/file", "root"); + byte[] buffer = new byte[]{1, 2, 3}; + fileSystem.write("/rwDir/file", "root", true, buffer); + + byte[] readBuffer = new byte[3]; + Offset offset = new Offset(0); + fileSystem.read("/rwDir/file", "root", readBuffer, offset); + assertArrayEquals(buffer, readBuffer); + } + + @Test + void testMv() throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException, OperacaoInvalidaException { + fileSystem.mkdir("/mvDir", "root"); + fileSystem.mv("/mvDir", "/mvDirRenamed", "root"); + } + + @Test + void testLs() throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException, OperacaoInvalidaException { + fileSystem.mkdir("/lsTest", "root"); + assertDoesNotThrow(() -> fileSystem.ls("/lsTest", "root", true)); + } + + @Test + void testCp() throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException, OperacaoInvalidaException { + fileSystem.mkdir("/cpSrc", "root"); + fileSystem.touch("/cpSrc/file.txt", "root"); + fileSystem.mkdir("/cpDest", "root"); + fileSystem.cp("/cpSrc", "/cpDest", "root", true); + // poderia testar se "/cpDest" existe se houvesse método acessor + } + + @Test + void testAddUser() { + assertDoesNotThrow(() -> fileSystem.addUser(new Usuario("markids", "rwx", "/"))); + } + + @Test + void testRemoveUser() {; + user = new Usuario("marceneiro", "---", "/"); + fileSystem.addUser(user); + assertDoesNotThrow(() -> fileSystem.removeUser(user.getNome())); + } +} diff --git a/tests/PermissionTest.java b/tests/PermissionTest.java index 6026e31..1adee1f 100644 --- a/tests/PermissionTest.java +++ b/tests/PermissionTest.java @@ -1,26 +1,105 @@ package tests; -import static org.junit.Assert.assertTrue; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.BeforeAll; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import exception.CaminhoJaExistenteException; +import exception.CaminhoNaoEncontradoException; +import exception.OperacaoInvalidaException; +import exception.PermissaoException; import filesys.FileSystemImpl; import filesys.IFileSystem; +import filesys.Usuario; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -// Essa classe testa cenários de permissão public class PermissionTest { - private static IFileSystem fileSystem; - - @BeforeAll - public static void setUp() { - fileSystem = new FileSystemImpl(/*args...*/); - } - - @Test - public void testPermission() { - // Teste de permissão - assertTrue(true); - } -} + private static IFileSystem fileSystem; + + @BeforeAll + public static void setUp() + throws CaminhoJaExistenteException, + PermissaoException, + CaminhoNaoEncontradoException, + OperacaoInvalidaException { + fileSystem = new FileSystemImpl(); + fileSystem.addUser(new Usuario("completo", "rwx", "/")); + fileSystem.addUser(new Usuario("leitura_execucao", "r-x", "/")); + fileSystem.addUser(new Usuario("so_leitura", "r--", "/")); + fileSystem.addUser(new Usuario("so_escrita", "-w-", "/")); + fileSystem.addUser(new Usuario("so_execucao", "--x", "/")); + fileSystem.addUser(new Usuario("nenhuma", "---", "/")); + + fileSystem.mkdir("/bin", "completo"); + fileSystem.touch("/bin/arquivo.txt", "completo"); + } + + @Test + public void testPermissaoLeitura() throws Exception { + // Usuário com r pode ler + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + fileSystem.ls("/bin", "leitura_execucao", false); + assertTrue(outContent.toString().contains("arquivo.txt")); + outContent.reset(); + + // Usuário só com r NÃO pode ler porque não tem x para navegar + assertThrows(PermissaoException.class, () -> fileSystem.ls("/bin", "so_leitura", false)); + + // Usuário só com x NÃO pode ler porque não tem r + assertThrows(PermissaoException.class, () -> fileSystem.ls("/bin", "so_execucao", false)); + + // Usuário sem r nem x não pode ler + assertThrows(PermissaoException.class, () -> fileSystem.ls("/bin", "so_escrita", false)); + assertThrows(PermissaoException.class, () -> fileSystem.ls("/bin", "nenhuma", false)); + System.setOut(System.out); + } + + @Test + public void testPermissaoEscrita() throws Exception { + // Usuário com w pode criar arquivos/diretórios + fileSystem.touch("/bin/novo.txt", "completo"); + + // Usuário sem w não pode criar arquivos/diretórios + assertThrows(PermissaoException.class, () -> fileSystem.touch("/bin/somenter.txt", "so_escrita")); + assertThrows(PermissaoException.class, () -> fileSystem.touch("/bin/semw.txt", "so_leitura")); + assertThrows(PermissaoException.class, () -> fileSystem.touch("/bin/semw2.txt", "so_execucao")); + assertThrows(PermissaoException.class, () -> fileSystem.touch("/bin/semw3.txt", "nenhuma")); + } + + @Test + public void testPermissaoExecucaoNavegacao() throws Exception { + // Usuário com x pode navegar/criar subdiretórios + fileSystem.mkdir("/bin/subx", "completo"); + + // Usuário sem x não pode navegar/criar subdiretórios + assertThrows(PermissaoException.class, () -> fileSystem.mkdir("/bin/somentex", "so_execucao")); + assertThrows(PermissaoException.class, () -> fileSystem.mkdir("/bin/semx", "so_leitura")); + assertThrows(PermissaoException.class, () -> fileSystem.mkdir("/bin/semx2", "so_escrita")); + assertThrows(PermissaoException.class, () -> fileSystem.mkdir("/bin/semx3", "nenhuma")); + } + + @Test + public void testPermissaoExecucaoLeituraArquivo() throws Exception { + // Usuário sem x não pode nem ler arquivos em subdiretórios + assertThrows( + PermissaoException.class, () -> fileSystem.ls("/bin", "so_leitura", false)); // sem x + assertThrows( + PermissaoException.class, () -> fileSystem.ls("/bin", "so_escrita", false)); // sem x + assertThrows(PermissaoException.class, () -> fileSystem.ls("/bin", "nenhuma", false)); // sem x + + // Usuário com x mas sem r NÃO pode ler (deve lançar exceção) + assertThrows(PermissaoException.class, () -> fileSystem.ls("/bin", "so_execucao", false)); + } + + @Test + public void testNenhumaPermissao() { + // Usuário sem nenhuma permissão não pode fazer nada + assertThrows(PermissaoException.class, () -> fileSystem.ls("/", "nenhuma", false)); + assertThrows( + PermissaoException.class, () -> fileSystem.touch("/bin/arquivo_novo.txt", "nenhuma")); + assertThrows(PermissaoException.class, () -> fileSystem.mkdir("/bin/dir_novo", "nenhuma")); + } +} \ No newline at end of file diff --git a/users/users b/users/users index 58c2992..1d72a42 100644 --- a/users/users +++ b/users/users @@ -1,7 +1,9 @@ root /** rwx maria /** rw- joao /** rw- -pedro /** rw- +pedro /** rwx tiago /** rw- -luzia /** rwx -carla /** r-- \ No newline at end of file +carla /** r-- +alvim /** rwx +marcos /** r-x +luisa /** -wx \ No newline at end of file