From 0ff9f0a613d46411627edb9550b0a048524d934f Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Mon, 2 Jun 2025 19:17:05 -0300 Subject: [PATCH 01/32] teste --- filesys/IFileSystem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index a0f0ce0..035d221 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -36,6 +36,7 @@ public interface IFileSystem { // 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. + // arlindo 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. From ff2374773dbf6cf3b028b0ac8a3037a7c1c6291f Mon Sep 17 00:00:00 2001 From: JoaquimGCVS Date: Mon, 2 Jun 2025 19:19:58 -0300 Subject: [PATCH 02/32] teste 2 --- filesys/IFileSystem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index 035d221..2e38668 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -41,6 +41,7 @@ public interface IFileSystem { // 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. + //joaquim 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. From 1c86c8672831cbab7b1e308c2a9469a0a321f60c Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Mon, 2 Jun 2025 19:37:55 -0300 Subject: [PATCH 03/32] Estrutura do projeto --- filesys/Arquivo.java | 52 ++++++++++++++++++++++++++++ filesys/Bloco.java | 30 ++++++++++++++++ filesys/Diretorio.java | 45 ++++++++++++++++++++++++ filesys/FileSys.java | 15 ++++++++ filesys/MetaDados.java | 77 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 219 insertions(+) create mode 100644 filesys/Arquivo.java create mode 100644 filesys/Bloco.java create mode 100644 filesys/Diretorio.java create mode 100644 filesys/FileSys.java create mode 100644 filesys/MetaDados.java diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java new file mode 100644 index 0000000..7c34644 --- /dev/null +++ b/filesys/Arquivo.java @@ -0,0 +1,52 @@ +package filesys; + +public class Arquivo { + private MetaDados metaDados; + private Bloco[] arquivo; + + public Arquivo(String nome, String dono, int tamanhoBloco) { + this.metaDados = new MetaDados(nome, 0, dono); + this.arquivo = new Bloco[1]; // Inicializa com um bloco + this.arquivo[0] = new Bloco(tamanhoBloco); + } + + public MetaDados getMetaDados() { + return metaDados; + } + + public void setMetaDados(MetaDados metaDados) { + this.metaDados = metaDados; + } + + public Bloco[] getArquivo() { + return arquivo; + } + + public void setArquivo(Bloco[] arquivo) { + this.arquivo = arquivo; + } + + public void addBloco(Bloco bloco) { + Bloco[] novoArquivo = new Bloco[this.arquivo.length + 1]; + System.arraycopy(this.arquivo, 0, novoArquivo, 0, this.arquivo.length); + novoArquivo[this.arquivo.length] = bloco; + this.arquivo = novoArquivo; + this.metaDados.setTamanho(this.metaDados.getTamanho() + bloco.getTamanho()); + } + + public void removeBloco(int index) { + if (index < 0 || index >= this.arquivo.length) { + throw new IndexOutOfBoundsException("Índice fora dos limites do arquivo"); + } + Bloco[] novoArquivo = new Bloco[this.arquivo.length - 1]; + System.arraycopy(this.arquivo, 0, novoArquivo, 0, index); + System.arraycopy(this.arquivo, index + 1, novoArquivo, index, this.arquivo.length - index - 1); + this.arquivo = novoArquivo; + this.metaDados.setTamanho(this.metaDados.getTamanho() - this.arquivo[index].getTamanho()); + } + + public void clear() { + this.arquivo = new Bloco[0]; + this.metaDados.setTamanho(0); + } +} diff --git a/filesys/Bloco.java b/filesys/Bloco.java new file mode 100644 index 0000000..c1d3608 --- /dev/null +++ b/filesys/Bloco.java @@ -0,0 +1,30 @@ +package filesys; + +public class Bloco { + private byte[] dados; + private int tamanho; + + public Bloco(int tamanho) { + if (tamanho <= 0) { + throw new IllegalArgumentException("Tamanho do bloco deve ser maior que zero"); + } + this.tamanho = tamanho; + this.dados = new byte[tamanho]; + } + + public byte[] getDados() { + return dados; + } + + public void setDados(byte[] dados) { + if (dados.length <= tamanho) { + this.dados = dados; + } else { + throw new IllegalArgumentException("Dados excedem o tamanho do bloco"); + } + } + + public int getTamanho() { + return tamanho; + } +} diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java new file mode 100644 index 0000000..59cd503 --- /dev/null +++ b/filesys/Diretorio.java @@ -0,0 +1,45 @@ +package filesys; + +public class Diretorio { + + private MetaDados metaDados; + private Diretorio[] subDiretorios; + private Arquivo[] arquivos; + + public Diretorio(String nome, String dono) { + this.metaDados = new MetaDados(nome, 0, dono); + this.subDiretorios = new Diretorio[0]; + this.arquivos = new Arquivo[0]; + } + + public MetaDados getMetaDados() { + return metaDados; + } + + public void setMetaDados(MetaDados metaDados) { + this.metaDados = metaDados; + } + + public Diretorio[] getSubDiretorios() { + return subDiretorios; + } + + public Arquivo[] getArquivos() { + return arquivos; + } + + public void addSubDiretorio(Diretorio subDiretorio) { + Diretorio[] novoSubDiretorios = new Diretorio[this.subDiretorios.length + 1]; + System.arraycopy(this.subDiretorios, 0, novoSubDiretorios, 0, this.subDiretorios.length); + novoSubDiretorios[this.subDiretorios.length] = subDiretorio; + this.subDiretorios = novoSubDiretorios; + } + + public void addArquivo(Arquivo arquivo) { + Arquivo[] novoArquivos = new Arquivo[this.arquivos.length + 1]; + System.arraycopy(this.arquivos, 0, novoArquivos, 0, this.arquivos.length); + novoArquivos[this.arquivos.length] = arquivo; + this.arquivos = novoArquivos; + this.metaDados.setTamanho(this.metaDados.getTamanho() + arquivo.getMetaDados().getTamanho()); + } +} diff --git a/filesys/FileSys.java b/filesys/FileSys.java new file mode 100644 index 0000000..a3af10e --- /dev/null +++ b/filesys/FileSys.java @@ -0,0 +1,15 @@ +package filesys; + +public class FileSys { + private Diretorio raiz; + + public FileSys() { + this.raiz = new Diretorio("Raiz", "root"); + } + public Diretorio getRaiz() { + return raiz; + } + public void setRaiz(Diretorio raiz) { + this.raiz = raiz; + } +} diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java new file mode 100644 index 0000000..61abf82 --- /dev/null +++ b/filesys/MetaDados.java @@ -0,0 +1,77 @@ +package filesys; + +import java.util.HashMap; + +public class MetaDados { + private String nome; + private int tamanho; + private String dono; + private HashMap permissoes; + + public MetaDados(String nome, int tamanho, String dono) { + this.setNome(nome); + this.setTamanho(tamanho); + this.setDono(dono); + this.permissoes = new HashMap<>(); + } + + public String getNome() { + return nome; + } + + public void setNome(String nome) { + this.nome = nome; + } + + public int getTamanho() { + return tamanho; + } + + public void setTamanho(int tamanho) { + this.tamanho = tamanho; + } + + public String getDono() { + return dono; + } + + public void setDono(String dono) { + this.dono = dono; + } + + public HashMap getPermissoes() { + return permissoes; + } + + public void setPermissoes(HashMap permissoes) { + this.permissoes = permissoes; + } + + public void addPermissao(String usuario, String permissao) { + this.permissoes.put(usuario, permissao); + } + + public String getPermissao(String usuario) { + return this.permissoes.getOrDefault(usuario, "nenhuma"); + } + + public boolean hasPermissao(String usuario, String permissao) { + String perm = this.permissoes.get(usuario); + return perm != null && (perm.equals("leitura") || perm.equals("escrita") || perm.equals("leitura-escrita")); + } + + public boolean isDono(String usuario) { + return this.dono.equals(usuario); + } + + @Override + public String toString() { + return "MetaDados{" + + "nome='" + nome + '\'' + + ", tamanho=" + tamanho + + ", dono='" + dono + '\'' + + ", permissoes=" + permissoes + + '}'; + } + +} From bae4a983caa9b43428ee7a2ee29f98aced00af06 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Mon, 2 Jun 2025 20:19:27 -0300 Subject: [PATCH 04/32] implementado cp --- filesys/Arquivo.java | 11 +- filesys/FileSystemImpl.java | 195 +++++++++++++++++++++++++++++++++++- 2 files changed, 200 insertions(+), 6 deletions(-) diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java index 7c34644..34a254b 100644 --- a/filesys/Arquivo.java +++ b/filesys/Arquivo.java @@ -4,12 +4,20 @@ public class Arquivo { private MetaDados metaDados; private Bloco[] arquivo; + // Construtor original public Arquivo(String nome, String dono, int tamanhoBloco) { this.metaDados = new MetaDados(nome, 0, dono); this.arquivo = new Bloco[1]; // Inicializa com um bloco this.arquivo[0] = new Bloco(tamanhoBloco); } + // *** Novo construtor que permite criar um 'Arquivo' a partir de + // um MetaDados já pronto e de um array de Bloco[]. *** + public Arquivo(MetaDados metaDados, Bloco[] arquivo) { + this.metaDados = metaDados; + this.arquivo = arquivo; + } + public MetaDados getMetaDados() { return metaDados; } @@ -41,8 +49,9 @@ public void removeBloco(int index) { Bloco[] novoArquivo = new Bloco[this.arquivo.length - 1]; System.arraycopy(this.arquivo, 0, novoArquivo, 0, index); System.arraycopy(this.arquivo, index + 1, novoArquivo, index, this.arquivo.length - index - 1); - this.arquivo = novoArquivo; + // Ajusta tamanho total subtraindo o bloco removido this.metaDados.setTamanho(this.metaDados.getTamanho() - this.arquivo[index].getTamanho()); + this.arquivo = novoArquivo; } public void clear() { diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 45fa05d..f0c233c 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -1,5 +1,9 @@ package filesys; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Objects; + import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; @@ -10,8 +14,15 @@ // 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 FileSys fileSys; - public FileSystemImpl() {} + public FileSys getFileSys() { + return fileSys; + } + + public FileSystemImpl() { + this.fileSys = new FileSys(); + } @Override public void mkdir(String caminho, String nome) throws CaminhoJaExistenteException, PermissaoException { @@ -54,17 +65,191 @@ public void mv(String caminhoAntigo, String caminhoNovo, String usuario) } @Override - public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { + public void ls(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { throw new UnsupportedOperationException("Método não implementado 'ls'"); } + public void addUser(String user) { + throw new UnsupportedOperationException("Método não implementado 'addUser'"); + } + @Override public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'cp'"); + // -- (este código permanece igual ao exemplo anterior) -- + + // Localiza origem + Object origemObj = buscarPorCaminho(caminhoOrigem); + if (origemObj == null) { + throw new CaminhoNaoEncontradoException("Origem não encontrada: " + caminhoOrigem); + } + + // Localiza destino (obrigatoriamente Diretório) + Object destinoObj = buscarPorCaminho(caminhoDestino); + if (!(destinoObj instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Destino inválido (não é diretório): " + caminhoDestino); + } + Diretorio dirDestino = (Diretorio) destinoObj; + + // Verificação de permissões (leitura na origem e escrita no destino) + // Esse trecho garante que, independente de origemObj ser um arquivo ou diretório, + // você sempre obtém o objeto MetaDados correspondente, que será usado para checar permissões de leitura na origem. + MetaDados mdOrigem = (origemObj instanceof Arquivo) + ? ((Arquivo) origemObj).getMetaDados() + : ((Diretorio) origemObj).getMetaDados(); + + if (!mdOrigem.hasPermissao(usuario, "r")) { + throw new PermissaoException("Sem permissão de leitura em: " + caminhoOrigem); + } + if (!dirDestino.getMetaDados().hasPermissao(usuario, "w")) { + throw new PermissaoException("Sem permissão de escrita em: " + caminhoDestino); + } + + // Decide se é Arquivo ou Diretorio + if (origemObj instanceof Arquivo) { + Arquivo arqOrig = (Arquivo) origemObj; + cpArquivo(arqOrig, dirDestino, arqOrig.getMetaDados().getNome()); + } else if (origemObj instanceof Diretorio) { + Diretorio dirOrig = (Diretorio) origemObj; + if (!recursivo) { + throw new UnsupportedOperationException( + "Cópia de diretório requer recursivo=true: " + caminhoOrigem); + } + cpDiretorio(dirOrig, dirDestino, dirOrig.getMetaDados().getNome()); + } else { + throw new UnsupportedOperationException("Tipo de objeto desconhecido em origem."); + } } - public void addUser(String user) { - throw new UnsupportedOperationException("Método não implementado 'addUser'"); + // (NÃO esqueça de incluir também o método buscarPorCaminho(), exatamente como + // no exemplo anterior.) + + /** + * Copia um Arquivo (deep‑copy de metadados e blocos) para dentro de 'destino', + * com o nome 'novoNome'. + */ + private void cpArquivo(Arquivo origem, Diretorio destino, String novoNome) { + MetaDados mdOrig = origem.getMetaDados(); + + // 1) Cria novo MetaDados com nome=novoNome, mesmo dono e mesmo tamanho + MetaDados mdNovo = new MetaDados(novoNome, mdOrig.getTamanho(), mdOrig.getDono()); + // 2) Copiamos permissões + mdNovo.setPermissoes(new HashMap<>(mdOrig.getPermissoes())); + + // 3) Deep‑copy do array de Bloco[] + Bloco[] blocosOrig = origem.getArquivo(); + Bloco[] blocosNovo = new Bloco[blocosOrig.length]; + for (int i = 0; i < blocosOrig.length; i++) { + byte[] dadosOrigPrim = blocosOrig[i].getDados(); + byte[] copiaDados = Arrays.copyOf(dadosOrigPrim, dadosOrigPrim.length); + blocosNovo[i] = new Bloco(dadosOrigPrim.length); + blocosNovo[i].setDados(copiaDados); + } + + // 4) Cria novo Arquivo usando o construtor que recebemos (MetaDados + Bloco[]) + Arquivo copia = new Arquivo(mdNovo, blocosNovo); + // 5) Adiciona ao diretório destino + destino.addArquivo(copia); + } + + /** + * Copia um Diretório inteiro (recursivamente) para dentro de 'destino', com o + * nome 'novoNome'. + */ + private void cpDiretorio(Diretorio origem, Diretorio destino, String novoNome) { + MetaDados mdOrig = origem.getMetaDados(); + + // 1) Cria MetaDados para o diretório-cópia + MetaDados mdNovo = new MetaDados(novoNome, 0, mdOrig.getDono()); + mdNovo.setPermissoes(new HashMap<>(mdOrig.getPermissoes())); + + // 2) Cria o novo Diretorio com nome/dono + Diretorio copiaDir = new Diretorio(mdNovo.getNome(), mdNovo.getDono()); + copiaDir.setMetaDados(mdNovo); + + // 3) Copiar todos os arquivos diretos (usa cpArquivo) + for (Arquivo arq : origem.getArquivos()) { + cpArquivo(arq, copiaDir, arq.getMetaDados().getNome()); + } + + // 4) Copiar recursivamente cada subdiretório + for (Diretorio sub : origem.getSubDiretorios()) { + cpDiretorio(sub, copiaDir, sub.getMetaDados().getNome()); + } + + // 5) Adiciona o diretório-cópia ao destino + destino.addSubDiretorio(copiaDir); } + + /** + * Busca recursivamente um Arquivo ou Diretório a partir de um caminho POSIX + * absoluto + * (por exemplo: "/usr/docs/projeto.txt"). + * Retorna a instância encontrada (Arquivo ou Diretorio) ou lança + * CaminhoNaoEncontradoException se algum componente não existir. + */ + private Object buscarPorCaminho(String caminho) throws CaminhoNaoEncontradoException { + String path = caminho.trim(); + if (!path.startsWith("/")) { + throw new CaminhoNaoEncontradoException("Caminho deve ser absoluto: " + caminho); + } + // Se for exatamente "/", retorna o diretório raiz + if (path.equals("/")) { + return fileSys.getRaiz(); + } + + // Divide o caminho em componentes, ignorando a primeira string vazia que surge + // antes da primeira barra + String[] partes = path.split("/"); + // Começamos a navegação a partir da raiz + Diretorio atual = fileSys.getRaiz(); + + // Para cada componente após a raiz + for (int i = 1; i < partes.length; i++) { + String nome = partes[i]; + boolean ultimo = (i == partes.length - 1); + + if (ultimo) { + // Se for o último componente, pode ser arquivo OU diretório + + // 1) Tenta encontrar um subdiretório com nome "nome" + for (Diretorio sub : atual.getSubDiretorios()) { + if (sub.getMetaDados().getNome().equals(nome)) { + return sub; // Encontrou como diretório + } + } + // 2) Se não achou o diretório, tenta encontrar um arquivo com nome "nome" + for (Arquivo arq : atual.getArquivos()) { + if (arq.getMetaDados().getNome().equals(nome)) { + return arq; // Encontrou como arquivo + } + } + // 3) Se não encontrou nenhum, lança exceção + throw new CaminhoNaoEncontradoException("Componente não encontrado: " + nome); + } + + // Se não for o último componente, então obrigatoriamente esse nome deve ser um + // subdiretório + boolean achouDir = false; + for (Diretorio sub : atual.getSubDiretorios()) { + if (sub.getMetaDados().getNome().equals(nome)) { + atual = sub; + achouDir = true; + break; + } + } + if (!achouDir) { + throw new CaminhoNaoEncontradoException("Diretório não encontrado no caminho: " + nome); + } + } + + // Em teoria não chegamos aqui, pois ou retornamos algo ou lançamos exceção + throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); + } + + + + + } From ec1002c70d433712045f43e69e4f36516aa383d0 Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 09:18:28 -0300 Subject: [PATCH 05/32] update : Trocar o uso de --- filesys/Diretorio.java | 69 ++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index 59cd503..a5118de 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -1,15 +1,18 @@ package filesys; +import java.util.ArrayList; +import java.util.List; + public class Diretorio { - + private MetaDados metaDados; - private Diretorio[] subDiretorios; - private Arquivo[] arquivos; + // Lista de arquivos neste diretório + private List arquivos = new ArrayList<>(); + // Lista de subdiretórios (filhos) + private List subDirs = new ArrayList<>(); - public Diretorio(String nome, String dono) { - this.metaDados = new MetaDados(nome, 0, dono); - this.subDiretorios = new Diretorio[0]; - this.arquivos = new Arquivo[0]; + public Diretorio(MetaDados metaDados) { + this.metaDados = metaDados; } public MetaDados getMetaDados() { @@ -20,26 +23,48 @@ public void setMetaDados(MetaDados metaDados) { this.metaDados = metaDados; } - public Diretorio[] getSubDiretorios() { - return subDiretorios; + public List getArquivos() { + return arquivos; } - public Arquivo[] getArquivos() { - return arquivos; + public List getSubDirs() { + return subDirs; } - public void addSubDiretorio(Diretorio subDiretorio) { - Diretorio[] novoSubDiretorios = new Diretorio[this.subDiretorios.length + 1]; - System.arraycopy(this.subDiretorios, 0, novoSubDiretorios, 0, this.subDiretorios.length); - novoSubDiretorios[this.subDiretorios.length] = subDiretorio; - this.subDiretorios = novoSubDiretorios; + /** + * Procura um subdiretório de nome exato dentro deste diretório. + * Retorna o objeto Diretorio se encontrado, ou null caso não exista. + */ + public Diretorio pegarSubDirPeloNome(String nome) { + for (Diretorio d : subDirs) { + if (d.getMetaDados().getNome().equals(nome)) { + return d; + } + } + return null; + } + + /** + * Adiciona um subdiretório filho a este diretório. + */ + public void adicionarSubDir(Diretorio novo) { + subDirs.add(novo); + } + + /** + * Adiciona um arquivo a este diretório. + */ + + + public void adicionarArquivo(Arquivo novo) { + arquivos.add(novo); } - public void addArquivo(Arquivo arquivo) { - Arquivo[] novoArquivos = new Arquivo[this.arquivos.length + 1]; - System.arraycopy(this.arquivos, 0, novoArquivos, 0, this.arquivos.length); - novoArquivos[this.arquivos.length] = arquivo; - this.arquivos = novoArquivos; - this.metaDados.setTamanho(this.metaDados.getTamanho() + arquivo.getMetaDados().getTamanho()); + /** + * Remove um arquivo deste diretório. + * Retorna true se o arquivo foi encontrado e removido, ou false se não existia. + */ + public boolean removerArquivo(Arquivo arquivo) { + return arquivos.remove(arquivo); } } From 743cd2fb1fd2980a4ce5ff524815a8045da2efaf Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 09:20:32 -0300 Subject: [PATCH 06/32] add construtores com String --- filesys/Diretorio.java | 4 ++++ filesys/MetaDados.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index a5118de..d88cf10 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -15,6 +15,10 @@ public Diretorio(MetaDados metaDados) { this.metaDados = metaDados; } + public Diretorio(String nome, String dono) { + this.metaDados = new MetaDados(nome, dono); + } + public MetaDados getMetaDados() { return metaDados; } diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java index 61abf82..d4590fd 100644 --- a/filesys/MetaDados.java +++ b/filesys/MetaDados.java @@ -15,6 +15,10 @@ public MetaDados(String nome, int tamanho, String dono) { this.permissoes = new HashMap<>(); } + public MetaDados(String nome, String dono) { + this(nome, 0, dono); + } + public String getNome() { return nome; } From a9f9ef808325bfeea47eb529ea8748bc5f2c1acf Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 09:30:48 -0300 Subject: [PATCH 07/32] feat : MKDIR --- filesys/Diretorio.java | 8 ++--- filesys/FileSystemImpl.java | 70 ++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index d88cf10..2e2adf9 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -51,16 +51,14 @@ public Diretorio pegarSubDirPeloNome(String nome) { /** * Adiciona um subdiretório filho a este diretório. */ - public void adicionarSubDir(Diretorio novo) { - subDirs.add(novo); + public void addSubDiretorio(Diretorio subDir) { + this.subDirs.add(subDir); // Assuming subDirs is a List } /** * Adiciona um arquivo a este diretório. */ - - - public void adicionarArquivo(Arquivo novo) { + public void addArquivo(Arquivo novo) { arquivos.add(novo); } diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index f0c233c..5d33e1d 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -2,7 +2,6 @@ import java.util.Arrays; import java.util.HashMap; -import java.util.Objects; import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; @@ -25,8 +24,57 @@ public FileSystemImpl() { } @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 nome) + throws CaminhoJaExistenteException, PermissaoException { + // 1) Tenta localizar o diretório “pai” (o local onde vamos criar o subdir) + Diretorio dirPai; + try { + Object o = buscarPorCaminho(caminho); + if (!(o instanceof Diretorio)) { + // Se o objeto encontrado não for um Diretório, não faz sentido criar dentro + throw new CaminhoJaExistenteException( + "Caminho especificado não é um diretório: " + caminho); + } + dirPai = (Diretorio) o; + } catch (CaminhoNaoEncontradoException e) { + // Como a assinatura de mkdir não permite lançar CaminhoNaoEncontradoException, + // reaproveitamos CaminhoJaExistenteException para indicar que o caminho não + // existe. + throw new CaminhoJaExistenteException( + "Caminho não encontrado: " + caminho); + } + + // 2) Verifica permissão de escrita (“w”) no dirPai para o usuário ROOT_USER + MetaDados mdPai = dirPai.getMetaDados(); + // hasPermissao(u, “w”) deve retornar true somente se o mapa de permissões + // contiver “w” para aquele usuário. Se não tiver, lançamos PermissaoException. + if (!mdPai.hasPermissao(ROOT_USER, "w")) { + throw new PermissaoException( + "Usuário '" + ROOT_USER + + "' não tem permissão de escrita em: " + + caminho); + } + + // 3) Verifica se já existe um subdiretório com o mesmo nome em dirPai + for (Diretorio sub : dirPai.getSubDirs()) { + if (sub.getMetaDados().getNome().equals(nome)) { + throw new CaminhoJaExistenteException( + "Já existe um diretório chamado '" + nome + + "' em: " + caminho); + } + } + + // 4) Cria o MetaDados do novo diretório + MetaDados metaNovo = new MetaDados(nome, 0, ROOT_USER); + // Concede “rwx” apenas para o dono (“root”) e nenhuma permissão para os demais + HashMap permissoes = new HashMap<>(); + permissoes.put(ROOT_USER, "rwx"); + metaNovo.setPermissoes(permissoes); + + // 5) Cria o novo Diretorio e o adiciona na lista de filhos do dirPai + Diretorio novoDir = new Diretorio(nome, ROOT_USER); + novoDir.setMetaDados(metaNovo); + dirPai.addSubDiretorio(novoDir); } @Override @@ -93,8 +141,10 @@ public void cp(String caminhoOrigem, String caminhoDestino, String usuario, bool Diretorio dirDestino = (Diretorio) destinoObj; // Verificação de permissões (leitura na origem e escrita no destino) - // Esse trecho garante que, independente de origemObj ser um arquivo ou diretório, - // você sempre obtém o objeto MetaDados correspondente, que será usado para checar permissões de leitura na origem. + // Esse trecho garante que, independente de origemObj ser um arquivo ou + // diretório, + // você sempre obtém o objeto MetaDados correspondente, que será usado para + // checar permissões de leitura na origem. MetaDados mdOrigem = (origemObj instanceof Arquivo) ? ((Arquivo) origemObj).getMetaDados() : ((Diretorio) origemObj).getMetaDados(); @@ -174,7 +224,7 @@ private void cpDiretorio(Diretorio origem, Diretorio destino, String novoNome) { } // 4) Copiar recursivamente cada subdiretório - for (Diretorio sub : origem.getSubDiretorios()) { + for (Diretorio sub : origem.getSubDirs()) { cpDiretorio(sub, copiaDir, sub.getMetaDados().getNome()); } @@ -214,7 +264,7 @@ private Object buscarPorCaminho(String caminho) throws CaminhoNaoEncontradoExcep // Se for o último componente, pode ser arquivo OU diretório // 1) Tenta encontrar um subdiretório com nome "nome" - for (Diretorio sub : atual.getSubDiretorios()) { + for (Diretorio sub : atual.getSubDirs()) { if (sub.getMetaDados().getNome().equals(nome)) { return sub; // Encontrou como diretório } @@ -232,7 +282,7 @@ private Object buscarPorCaminho(String caminho) throws CaminhoNaoEncontradoExcep // Se não for o último componente, então obrigatoriamente esse nome deve ser um // subdiretório boolean achouDir = false; - for (Diretorio sub : atual.getSubDiretorios()) { + for (Diretorio sub : atual.getSubDirs()) { if (sub.getMetaDados().getNome().equals(nome)) { atual = sub; achouDir = true; @@ -248,8 +298,4 @@ private Object buscarPorCaminho(String caminho) throws CaminhoNaoEncontradoExcep throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); } - - - - } From 42c9019f31f2efd741476f925079c1f5f60f1b6a Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 09:40:33 -0300 Subject: [PATCH 08/32] feat: modificando metodo cp --- filesys/FileSystemImpl.java | 133 ++++++++++++------------------------ 1 file changed, 42 insertions(+), 91 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 5d33e1d..4533773 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.HashMap; +import java.util.Map; import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; @@ -123,28 +124,20 @@ public void addUser(String user) { } @Override - public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) + public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - // -- (este código permanece igual ao exemplo anterior) -- - // Localiza origem Object origemObj = buscarPorCaminho(caminhoOrigem); if (origemObj == null) { throw new CaminhoNaoEncontradoException("Origem não encontrada: " + caminhoOrigem); } - // Localiza destino (obrigatoriamente Diretório) Object destinoObj = buscarPorCaminho(caminhoDestino); if (!(destinoObj instanceof Diretorio)) { throw new CaminhoNaoEncontradoException("Destino inválido (não é diretório): " + caminhoDestino); } Diretorio dirDestino = (Diretorio) destinoObj; - // Verificação de permissões (leitura na origem e escrita no destino) - // Esse trecho garante que, independente de origemObj ser um arquivo ou - // diretório, - // você sempre obtém o objeto MetaDados correspondente, que será usado para - // checar permissões de leitura na origem. MetaDados mdOrigem = (origemObj instanceof Arquivo) ? ((Arquivo) origemObj).getMetaDados() : ((Diretorio) origemObj).getMetaDados(); @@ -156,145 +149,103 @@ public void cp(String caminhoOrigem, String caminhoDestino, String usuario, bool throw new PermissaoException("Sem permissão de escrita em: " + caminhoDestino); } - // Decide se é Arquivo ou Diretorio if (origemObj instanceof Arquivo) { - Arquivo arqOrig = (Arquivo) origemObj; - cpArquivo(arqOrig, dirDestino, arqOrig.getMetaDados().getNome()); - } else if (origemObj instanceof Diretorio) { + cpArquivo((Arquivo) origemObj, dirDestino); + } else { Diretorio dirOrig = (Diretorio) origemObj; if (!recursivo) { throw new UnsupportedOperationException( "Cópia de diretório requer recursivo=true: " + caminhoOrigem); } - cpDiretorio(dirOrig, dirDestino, dirOrig.getMetaDados().getNome()); - } else { - throw new UnsupportedOperationException("Tipo de objeto desconhecido em origem."); + cpDiretorio(dirOrig, dirDestino); } } - // (NÃO esqueça de incluir também o método buscarPorCaminho(), exatamente como - // no exemplo anterior.) - - /** - * Copia um Arquivo (deep‑copy de metadados e blocos) para dentro de 'destino', - * com o nome 'novoNome'. - */ - private void cpArquivo(Arquivo origem, Diretorio destino, String novoNome) { + private void cpArquivo(Arquivo origem, Diretorio destino) { MetaDados mdOrig = origem.getMetaDados(); + MetaDados mdNovo = clonarMetaDados(mdOrig, mdOrig.getNome(), mdOrig.getTamanho(), mdOrig.getDono()); - // 1) Cria novo MetaDados com nome=novoNome, mesmo dono e mesmo tamanho - MetaDados mdNovo = new MetaDados(novoNome, mdOrig.getTamanho(), mdOrig.getDono()); - // 2) Copiamos permissões - mdNovo.setPermissoes(new HashMap<>(mdOrig.getPermissoes())); - - // 3) Deep‑copy do array de Bloco[] Bloco[] blocosOrig = origem.getArquivo(); Bloco[] blocosNovo = new Bloco[blocosOrig.length]; for (int i = 0; i < blocosOrig.length; i++) { - byte[] dadosOrigPrim = blocosOrig[i].getDados(); - byte[] copiaDados = Arrays.copyOf(dadosOrigPrim, dadosOrigPrim.length); - blocosNovo[i] = new Bloco(dadosOrigPrim.length); - blocosNovo[i].setDados(copiaDados); + byte[] dadosOrigem = blocosOrig[i].getDados(); + byte[] copiaDados = Arrays.copyOf(dadosOrigem, dadosOrigem.length); + Bloco novoBloco = new Bloco(dadosOrigem.length); + novoBloco.setDados(copiaDados); + blocosNovo[i] = novoBloco; } - // 4) Cria novo Arquivo usando o construtor que recebemos (MetaDados + Bloco[]) Arquivo copia = new Arquivo(mdNovo, blocosNovo); - // 5) Adiciona ao diretório destino destino.addArquivo(copia); } - /** - * Copia um Diretório inteiro (recursivamente) para dentro de 'destino', com o - * nome 'novoNome'. - */ - private void cpDiretorio(Diretorio origem, Diretorio destino, String novoNome) { + private void cpDiretorio(Diretorio origem, Diretorio destino) { MetaDados mdOrig = origem.getMetaDados(); + MetaDados mdNovo = clonarMetaDados(mdOrig, mdOrig.getNome(), 0, mdOrig.getDono()); - // 1) Cria MetaDados para o diretório-cópia - MetaDados mdNovo = new MetaDados(novoNome, 0, mdOrig.getDono()); - mdNovo.setPermissoes(new HashMap<>(mdOrig.getPermissoes())); - - // 2) Cria o novo Diretorio com nome/dono - Diretorio copiaDir = new Diretorio(mdNovo.getNome(), mdNovo.getDono()); - copiaDir.setMetaDados(mdNovo); - - // 3) Copiar todos os arquivos diretos (usa cpArquivo) - for (Arquivo arq : origem.getArquivos()) { - cpArquivo(arq, copiaDir, arq.getMetaDados().getNome()); + Diretorio copiaDir = new Diretorio(mdNovo); + for (Arquivo arqFilho : origem.getArquivos()) { + cpArquivo(arqFilho, copiaDir); } - - // 4) Copiar recursivamente cada subdiretório - for (Diretorio sub : origem.getSubDirs()) { - cpDiretorio(sub, copiaDir, sub.getMetaDados().getNome()); + for (Diretorio subOrig : origem.getSubDirs()) { + cpDiretorio(subOrig, copiaDir); } - - // 5) Adiciona o diretório-cópia ao destino destino.addSubDiretorio(copiaDir); } - /** - * Busca recursivamente um Arquivo ou Diretório a partir de um caminho POSIX - * absoluto - * (por exemplo: "/usr/docs/projeto.txt"). - * Retorna a instância encontrada (Arquivo ou Diretorio) ou lança - * CaminhoNaoEncontradoException se algum componente não existir. - */ + private MetaDados clonarMetaDados(MetaDados original, String nome, int tamanho, String dono) { + MetaDados copia = new MetaDados(nome, tamanho, dono); + Map permissoesOrig = original.getPermissoes(); + if (permissoesOrig != null) { + copia.setPermissoes(new HashMap<>(permissoesOrig)); + } + return copia; + } + private Object buscarPorCaminho(String caminho) throws CaminhoNaoEncontradoException { - String path = caminho.trim(); - if (!path.startsWith("/")) { + if (caminho == null || !caminho.startsWith("/")) { throw new CaminhoNaoEncontradoException("Caminho deve ser absoluto: " + caminho); } - // Se for exatamente "/", retorna o diretório raiz + String path = caminho.trim(); if (path.equals("/")) { return fileSys.getRaiz(); } - // Divide o caminho em componentes, ignorando a primeira string vazia que surge - // antes da primeira barra String[] partes = path.split("/"); - // Começamos a navegação a partir da raiz Diretorio atual = fileSys.getRaiz(); - // Para cada componente após a raiz for (int i = 1; i < partes.length; i++) { - String nome = partes[i]; + String nomeComponente = partes[i]; boolean ultimo = (i == partes.length - 1); if (ultimo) { - // Se for o último componente, pode ser arquivo OU diretório - - // 1) Tenta encontrar um subdiretório com nome "nome" for (Diretorio sub : atual.getSubDirs()) { - if (sub.getMetaDados().getNome().equals(nome)) { - return sub; // Encontrou como diretório + if (sub.getMetaDados().getNome().equals(nomeComponente)) { + return sub; } } - // 2) Se não achou o diretório, tenta encontrar um arquivo com nome "nome" for (Arquivo arq : atual.getArquivos()) { - if (arq.getMetaDados().getNome().equals(nome)) { - return arq; // Encontrou como arquivo + if (arq.getMetaDados().getNome().equals(nomeComponente)) { + return arq; } } - // 3) Se não encontrou nenhum, lança exceção - throw new CaminhoNaoEncontradoException("Componente não encontrado: " + nome); + throw new CaminhoNaoEncontradoException("Componente não encontrado: " + nomeComponente); } - // Se não for o último componente, então obrigatoriamente esse nome deve ser um - // subdiretório - boolean achouDir = false; + boolean achou = false; for (Diretorio sub : atual.getSubDirs()) { - if (sub.getMetaDados().getNome().equals(nome)) { + if (sub.getMetaDados().getNome().equals(nomeComponente)) { atual = sub; - achouDir = true; + achou = true; break; } } - if (!achouDir) { - throw new CaminhoNaoEncontradoException("Diretório não encontrado no caminho: " + nome); + if (!achou) { + throw new CaminhoNaoEncontradoException( + "Diretório não encontrado no caminho: " + nomeComponente); } } - // Em teoria não chegamos aqui, pois ou retornamos algo ou lançamos exceção throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); } From e54975f08d894029fb991930848cf205d49d3109 Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 09:51:38 -0300 Subject: [PATCH 09/32] feat : TOUCH --- filesys/FileSystemImpl.java | 91 ++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 4533773..c5bf76a 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -90,9 +90,96 @@ public void rm(String caminho, String usuario, boolean recursivo) throw new UnsupportedOperationException("Método não implementado 'rm'"); } + /** + * Método touch: cria um arquivo vazio em 'caminho'. + * Exemplo: touch("/usr/local/meuarquivo.txt", "alice") + * -> pai = "/usr/local", nome = "meuarquivo.txt" + */ @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 + { + // 1) Separar o caminho em 'pai' e 'nomeDoArquivo' + String path = caminho.trim(); + if (!path.startsWith("/")) { + // Para simplificar, consideramos que caminhos devem ser absolutos + throw new CaminhoJaExistenteException("Caminho inválido (deve começar com '/'): " + caminho); + } + if (path.equals("/")) { + // Não faz sentido chamar touch("/") → não se pode criar arquivo com nome vazio + throw new CaminhoJaExistenteException("Não é possível criar arquivo na raiz sem nome: " + caminho); + } + + // Exemplo: "/usr/local/meuarquivo.txt" + // indexSlash = posição da última barra antes do nome do arquivo + int indexSlash = path.lastIndexOf("/"); + // Se a última barra for a própria raiz, então pai = "/" + String paiPath = (indexSlash == 0) ? "/" : path.substring(0, indexSlash); + String nomeArquivo = path.substring(indexSlash + 1); + if (nomeArquivo.isEmpty()) { + throw new CaminhoJaExistenteException("Nome de arquivo vazio em: " + caminho); + } + + // 2) Localizar o diretório pai + Diretorio dirPai; + try { + Object o = buscarPorCaminho(paiPath); + if (!(o instanceof Diretorio)) { + // Se não for diretório (por exemplo, for um arquivo), não podemos criar dentro + throw new CaminhoJaExistenteException( + "Caminho pai não é um diretório: " + paiPath + ); + } + dirPai = (Diretorio) o; + } + catch (CaminhoNaoEncontradoException e) { + // Se pai não existe, relançamos como CaminhoJaExistenteException + throw new CaminhoJaExistenteException("Caminho não encontrado: " + paiPath); + } + + // 3) Verificar permissão de escrita no dirPai para o usuário informado + MetaDados mdPai = dirPai.getMetaDados(); + if (!mdPai.hasPermissao(usuario, "w")) { + throw new PermissaoException( + "Usuário '" + usuario + + "' não tem permissão de escrita em: " + + paiPath + ); + } + + // 4) Verificar se já existe um arquivo ou diretório com esse nome em dirPai + // 4.1) Checa subdiretórios + for (Diretorio sub : dirPai.getSubDirs()) { + if (sub.getMetaDados().getNome().equals(nomeArquivo)) { + throw new CaminhoJaExistenteException( + "Já existe arquivo ou diretório chamado '" + nomeArquivo + + "' em: " + paiPath + ); + } + } + // 4.2) Checa arquivos + for (Arquivo arq : dirPai.getArquivos()) { + if (arq.getMetaDados().getNome().equals(nomeArquivo)) { + throw new CaminhoJaExistenteException( + "Já existe arquivo ou diretório chamado '" + nomeArquivo + + "' em: " + paiPath + ); + } + } + + // 5) Se chegamos aqui, podemos criar o novo arquivo vazio + // 5.1) Criar MetaDados com tamanho = 0 + MetaDados metaArq = new MetaDados(nomeArquivo, 0, usuario); + // conceder permissão “rw” para o dono + HashMap mapaPerm = new HashMap<>(); + mapaPerm.put(usuario, "rw"); + metaArq.setPermissoes(mapaPerm); + + // 5.2) Criar o objeto Arquivo com 0 blocos (array vazio) + Arquivo novoArq = new Arquivo(metaArq, new Bloco[0]); + + // 5.3) Adicionar ao diretório pai + dirPai.addArquivo(novoArq); } @Override From 6166bcbb88f92baf129025c8b34fcd966e655319 Mon Sep 17 00:00:00 2001 From: JoaquimGCVS Date: Tue, 3 Jun 2025 10:10:29 -0300 Subject: [PATCH 10/32] Metodo ls implementado --- filesys/FileSystemImpl.java | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index c5bf76a..3069b83 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -201,9 +201,39 @@ public void mv(String caminhoAntigo, String caminhoNovo, String usuario) } @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 { + // Busca o objeto (diretório ou arquivo) pelo caminho fornecido + Object obj = buscarPorCaminho(caminho); + + // Se não for um diretório, lança exceção + if (!(obj instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Não é um diretório: " + caminho); + } + Diretorio dir = (Diretorio) obj; + MetaDados meta = dir.getMetaDados(); + + // Verifica se o usuário tem permissão de leitura, ou se é dono, ou se é root + if (!meta.getPermissao(usuario).contains("r") && !meta.isDono(usuario) && !usuario.equals(ROOT_USER)) { + throw new PermissaoException("Sem permissão de leitura em: " + caminho); + } + + // Chama o método auxiliar para listar o conteúdo do diretório + listarConteudo(dir, caminho, recursivo, ""); + } + + private void listarConteudo(Diretorio dir, String caminho, boolean recursivo, String prefixo) { + // Lista todos os arquivos do diretório atual + for (Arquivo arq : dir.getArquivos()) { + System.out.println(prefixo + arq.getMetaDados().getNome()); + } + // Lista todos os subdiretórios do diretório atual + for (Diretorio sub : dir.getSubDirs()) { + System.out.println(prefixo + sub.getMetaDados().getNome() + "/"); // "/" indica diretório + // Se for recursivo, chama novamente para o subdiretório, aumentando o prefixo (indentação) + if (recursivo) { + listarConteudo(sub, caminho + "/" + sub.getMetaDados().getNome(), true, prefixo + " "); + } + } } public void addUser(String user) { From e790f8af3eee83a0967e6d297343cf97f5fda0c9 Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 10:13:07 -0300 Subject: [PATCH 11/32] feat : CHMOD --- filesys/FileSystemImpl.java | 61 +++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 3069b83..54f6e49 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -78,10 +78,67 @@ public void mkdir(String caminho, String nome) dirPai.addSubDiretorio(novoDir); } + /** + * chmod: altera a permissão de 'usuarioAlvo' no arquivo ou diretório em 'caminho'. + * + * @param caminho caminho absoluto para um arquivo OU diretório (ex: "/usr/bin/arquivo.txt" ou "/usr/bin") + * @param usuario quem está executando o comando (deve ser root OU dono do objeto) + * @param usuarioAlvo usuário cujas permissões serão ajustadas + * @param permissao string de 3 caracteres, cada um em { 'r', 'w', 'x' } ou '-' + * Ex.: "rwx", "r--", "-w-", "---" + * + * @throws CaminhoNaoEncontradoException se não encontrar o arquivo/diretório em 'caminho' + * @throws PermissaoException se 'usuario' não for root nem dono do objeto + */ @Override public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) - throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'chmod'"); + throws CaminhoNaoEncontradoException, PermissaoException + { + // 1) Validar string de permissão: deve ter exatamente 3 caracteres, cada um seja 'r','w','x' ou '-' + if (permissao == null || permissao.length() != 3) { + throw new IllegalArgumentException("Permissão inválida (deve ter 3 chars): " + permissao); + } + for (char c : permissao.toCharArray()) { + if (c != 'r' && c != 'w' && c != 'x' && c != '-') { + throw new IllegalArgumentException("Permissão contém caractere inválido: " + c); + } + } + + // 2) Localizar o objeto (Arquivo ou Diretório) em 'caminho' + Object objAlvo = buscarPorCaminho(caminho); + // buscarPorCaminho lança CaminhoNaoEncontradoException se não existir + + MetaDados mdAlvo; + if (objAlvo instanceof Arquivo) { + mdAlvo = ((Arquivo) objAlvo).getMetaDados(); + } else if (objAlvo instanceof Diretorio) { + mdAlvo = ((Diretorio) objAlvo).getMetaDados(); + } else { + // Nunca deveria acontecer, mas para garantir: + throw new CaminhoNaoEncontradoException("Caminho encontrado não é arquivo nem diretório: " + caminho); + } + + // 3) Verificar se 'usuario' tem permissão para executar chmod: + // - Se for ROOT_USER, sempre permitido. + // - Caso contrário, somente se for dono do objeto + String dono = mdAlvo.getDono(); + if (!usuario.equals(ROOT_USER) && !usuario.equals(dono)) { + throw new PermissaoException( + "Usuário '" + usuario + "' não tem permissão para alterar direitos em: " + + caminho + ); + } + + // 4) Alterar (ou inserir) a permissão de 'usuarioAlvo' para 'permissao' + // Se o usuárioAlvo for "root", deixamos que ele fique “rwx” obrigatoriamente, + // independentemente do que se passe em 'permissao'? + // Depende do requisito, mas aqui vamos aceitar qualquer string para qualquer usuárioAlvo, + // pois, se o root quiser revogar até de si mesmo, é opção dele. + // + // Simplesmente atualizamos o HashMap: + HashMap mapaPerm = mdAlvo.getPermissoes(); + mapaPerm.put(usuarioAlvo, permissao); + mdAlvo.setPermissoes(mapaPerm); } @Override From 5a9a239c9e15fa9069aadd8410976c3f472c035a Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 10:20:49 -0300 Subject: [PATCH 12/32] refactor: improve code readability and structure in Main.java --- Main.java | 205 +++++++++++++++++++++++++++--------------------------- 1 file changed, 103 insertions(+), 102 deletions(-) diff --git a/Main.java b/Main.java index 3b8673a..1e327d9 100644 --- a/Main.java +++ b/Main.java @@ -1,21 +1,22 @@ import filesys.IFileSystem; +import filesys.FileSystem; import java.util.Scanner; import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; import exception.PermissaoException; import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; -import filesys.FileSystem; - -// MENU INTERATIVO PARA O SISTEMA DE ARQUIVOS -// SINTA-SE LIVRE PARA ALTERAR A CLASSE MAIN +/* + MENU INTERATIVO PARA O SISTEMA DE ARQUIVOS + SINTA-SE LIVRE PARA ALTERAR A CLASSE MAIN +*/ public class Main { - // Constantes úteis para a versão interativa. - // Para esse tipo de execução, o tamanho max do buffer de - // leitura pode ser menor. + // Constantes úteis para a versão interativa. private static final String ROOT_USER = "root"; private static final String ROOT_DIR = "/"; private static final int READ_BUFFER_SIZE = 256; @@ -29,78 +30,96 @@ public class Main { // Usuário que está executando o programa private static String user; - // 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. + // Lista interna para guardar, temporariamente, cada permissão lida do arquivo + private static class EntryPermissao { + String usuario; + String dir; + String permissoes; + EntryPermissao(String u, String d, String p) { + this.usuario = u; + this.dir = d; + this.permissoes = p; + } + } + public static void main(String[] args) { - // Usuário que está executando o programa. - // 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"); return; } user = args[1]; - - // Carrega a lista de usuários do sistema a partir de arquivo - // Formato do arquivo users: - // username dir permission - // Exemplo: - // maria /** rw- - // luzia /** rwx - // Essa permissão vale para o diretório raiz e sub diretórios. - // A partir do momento que um usuário cria outro diretório ou arquivo, - // a permissão desse usuário é de leitura, escrita e execução nesse novo diretório/arquivo, - // e sempre será rwx para o usuário root. + + // Lista onde guardaremos as linhas do arquivo 'users/users' + List entradas = new ArrayList<>(); + + // 1) Ler o arquivo users/users e armazenar cada tripla em 'entradas' try { Scanner userScanner = new Scanner(new java.io.File("users/users")); while (userScanner.hasNextLine()) { String line = userScanner.nextLine().trim(); - if (!line.isEmpty()) { - String[] parts = line.split(" "); - if (parts.length == 3) { - String userListed = parts[0]; - String dir = parts[1]; - String dirPermission = parts[2]; - - /* 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 - - - } else { - System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); - } + if (line.isEmpty()) continue; + + String[] parts = line.split("\\s+"); + if (parts.length == 3) { + String userListed = parts[0]; // Ex: "maria" + String dir = parts[1]; // Ex: "/**" + String perm = parts[2]; // Ex: "rw-" + + // Armazena temporariamente para aplicar via chmod mais tarde + entradas.add(new EntryPermissao(userListed, dir, perm)); + } + else { + System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); } } userScanner.close(); - } catch (FileNotFoundException e) { // Retorna se o arquivo de usuários não for encontrado + } + catch (FileNotFoundException e) { 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. + + // 2) Cria o FileSystem (virtual, em memória) + // (aqui poderíamos passar lista de usuários se quisesse, mas vamos usar + // a própria chamada chmod para inicializar as permissões) + fileSystem = new FileSystem(); + + // 3) “Processar” cada entrada: chamar chmod em cada tripla (usuário=root) + for (EntryPermissao ep : entradas) { + // Traduzimos "/**" como "/" no nosso FileSystem virtual + String caminhoParaChmod = ep.dir; + if (caminhoParaChmod.equals("/**")) { + caminhoParaChmod = ROOT_DIR; + } + try { + // ROOT_USER tem permissão para alterar tudo + fileSystem.chmod(caminhoParaChmod, ROOT_USER, ep.usuario, ep.permissoes); + } + catch (CaminhoNaoEncontradoException | PermissaoException ex) { + // Geralmente não deve acontecer, pois sabemos que o ROOT_DIR ("/") existe + // Mas, se o usuário pediu permissão para um diretório que não existe, + // imprimimos uma mensagem de advertência e continuamos + System.out.println("Aviso: não foi possível aplicar permissão em '" + + ep.dir + "' para '" + ep.usuario + + "': " + ex.getMessage()); + } + } + + // Se você quiser garantir que a raiz foi criada (caso FileSystem não faça sozinho), + // descomente o bloco abaixo: + /* + try { + fileSystem.mkdir(ROOT_DIR, ROOT_USER); + } catch (CaminhoJaExistenteException | PermissaoException e) { + System.out.println(e.getMessage()); + } + */ + + // 4) Inicia o menu interativo menu(); } - // Menu interativo para fins de teste. - // Os testes junit não são feitos com esse menu, - // mas diretamente na interface IFileSystem + // … o resto de Main fica inalterado, igual ao que você já tinha … public static void menu() { while (true) { System.out.println("\nComandos disponíveis:"); @@ -119,41 +138,23 @@ public static void menu() { String opcao = scanner.nextLine(); try { switch (opcao) { - case "1": - chmod(); - break; - case "2": - mkdir(); - break; - case "3": - rm(); - break; - case "4": - touch(); - break; - case "5": - write(); - break; - case "6": - read(); - break; - case "7": - mv(); - break; - case "8": - ls(); - break; - case "9": - cp(); - break; + case "1": chmod(); break; + case "2": mkdir(); break; + case "3": rm(); break; + case "4": touch(); break; + case "5": write(); break; + case "6": read(); break; + case "7": mv(); break; + case "8": ls(); break; + case "9": cp(); break; case "0": System.out.println("Encerrando..."); - return; default: System.out.println("Comando inválido!"); - } - } catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException e) { + } + } + catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException e) { System.out.println("Erro: " + e.getMessage()); } @@ -169,16 +170,16 @@ public static void chmod() throws CaminhoNaoEncontradoException, PermissaoExcept String caminho = scanner.nextLine(); System.out.println("Insira o usuário para o qual deseja alterar as permissões:"); String usuarioAlvo = scanner.nextLine(); - System.out.println("Insira a permissão (formato: 3 caracteres\"rwx\"):"); + System.out.println("Insira a permissão (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:"); String caminho = scanner.nextLine(); - + fileSystem.mkdir(caminho, user); } @@ -187,14 +188,14 @@ public static void rm() throws CaminhoNaoEncontradoException, PermissaoException String caminho = scanner.nextLine(); System.out.println("Remover recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); - + fileSystem.rm(caminho, user, recursivo); } public static void touch() throws CaminhoJaExistenteException, PermissaoException { System.out.println("Insira o caminho do arquivo a ser criado:"); String caminho = scanner.nextLine(); - + fileSystem.touch(caminho, user); } @@ -206,16 +207,16 @@ public static void write() throws CaminhoNaoEncontradoException, PermissaoExcept System.out.println("Insira o conteúdo a ser escrito:"); String content = scanner.nextLine(); byte[] buffer = content.getBytes(); - + fileSystem.write(caminho, user, anexar, buffer); } 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. + byte[] buffer = new byte[READ_BUFFER_SIZE]; + + fileSystem.read(caminho, user, buffer); } public static void mv() throws CaminhoNaoEncontradoException, PermissaoException { @@ -223,7 +224,7 @@ public static void mv() throws CaminhoNaoEncontradoException, PermissaoException String caminhoAntigo = scanner.nextLine(); System.out.println("Insira o novo caminho do arquivo:"); String caminhoNovo = scanner.nextLine(); - + fileSystem.mv(caminhoAntigo, caminhoNovo, user); } @@ -232,7 +233,7 @@ public static void ls() throws CaminhoNaoEncontradoException, PermissaoException String caminho = scanner.nextLine(); System.out.println("Listar recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); - + fileSystem.ls(caminho, user, recursivo); } @@ -243,7 +244,7 @@ public static void cp() throws CaminhoNaoEncontradoException, PermissaoException String caminhoDestino = scanner.nextLine(); System.out.println("Copiar recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); - + fileSystem.cp(caminhoOrigem, caminhoDestino, user, recursivo); } } From 361dfd883ca80e4ab8c2f2c7499ad3acfab38320 Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 10:22:43 -0300 Subject: [PATCH 13/32] test: add JUnit tests for chmod, mkdir, and touch methods in FileSystemImpl --- tests/FileSysImplChmodTest.java | 165 +++++++++++++++++++++++++++++ tests/FileSysImplMkdirTest.java | 134 +++++++++++++++++++++++ tests/FileSysImplTouchTest.java | 181 ++++++++++++++++++++++++++++++++ 3 files changed, 480 insertions(+) create mode 100644 tests/FileSysImplChmodTest.java create mode 100644 tests/FileSysImplMkdirTest.java create mode 100644 tests/FileSysImplTouchTest.java diff --git a/tests/FileSysImplChmodTest.java b/tests/FileSysImplChmodTest.java new file mode 100644 index 0000000..0d4eb14 --- /dev/null +++ b/tests/FileSysImplChmodTest.java @@ -0,0 +1,165 @@ +package tests; +import filesys.Arquivo; +import filesys.Diretorio; +import filesys.FileSystemImpl; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import exception.CaminhoNaoEncontradoException; +import exception.PermissaoException; + +class FileSystemImplChmodTest { + + private FileSystemImpl fsImpl; + + @BeforeEach + void setUp() { + fsImpl = new FileSystemImpl(); + // Opcional: podemos já criar uma estrutura de diretórios com subpastas e arquivos + // para os testes. Por enquanto, deixamos vazio e cada teste monta o que precisa. + } + + /** + * 1) Sucesso no chmod quando quem executa é root (criaremos /d1 e um arquivo lá): + * - mkdir("/", "d1") + * - touch("/d1/arquivo.log", "root") + * - chmod("/d1/arquivo.log", "root", "alice", "r--") + * → Verifica que em /d1/arquivo.log existe permissão "r--" para "alice" + */ + @Test + void testChmodByRootSuccess() throws Exception { + // Cria diretório e arquivo + fsImpl.mkdir("/", "d1"); + fsImpl.touch("/d1/arquivo.log", "root"); + + // Executa chmod como root, conferindo permissão de 'r--' para usuário 'alice' + fsImpl.chmod("/d1/arquivo.log", "root", "alice", "r--"); + + // Localiza o Arquivo para verificar MetaDados + Diretorio d1 = (Diretorio) fsImpl.getFileSys().getRaiz() + .getSubDirs().stream() + .filter(d -> d.getMetaDados().getNome().equals("d1")) + .findFirst().orElse(null); + assertNotNull(d1, "Diretório /d1 deveria existir."); + + Arquivo arqLog = d1.getArquivos().stream() + .filter(a -> a.getMetaDados().getNome().equals("arquivo.log")) + .findFirst().orElse(null); + assertNotNull(arqLog, "Arquivo /d1/arquivo.log deveria existir."); + + // Verifica permissão para "alice" + assertTrue(arqLog.getMetaDados().hasPermissao("alice", "r"), + "Alice deve ter permissão de leitura ('r')."); + assertFalse(arqLog.getMetaDados().hasPermissao("alice", "w"), + "Alice não deve ter permissão de escrita ('w')."); + assertFalse(arqLog.getMetaDados().hasPermissao("alice", "x"), + "Alice não deve ter permissão de execução ('x')."); + } + + /** + * 2) Sucesso no chmod quando quem executa é o dono do objeto: + * - mkdir("/", "d2") + * - touch("/d2/file.dat", "bob") + * - chmod("/d2/file.dat", "bob", "charlie", "rw-") + * → Verifica que 'charlie' tem agora permissão "rw-" + */ + @Test + void testChmodByOwnerSuccess() throws Exception { + // Cria d2 e, como usuário 'bob', cria o arquivo dentro + fsImpl.mkdir("/", "d2"); + fsImpl.touch("/d2/file.dat", "bob"); + + // Agora 'bob' altera permissão de 'charlie' para "rw-" + fsImpl.chmod("/d2/file.dat", "bob", "charlie", "rw-"); + + // Localiza file.dat + Diretorio d2 = (Diretorio) fsImpl.getFileSys().getRaiz() + .getSubDirs().stream() + .filter(d -> d.getMetaDados().getNome().equals("d2")) + .findFirst().orElse(null); + assertNotNull(d2, "Diretório /d2 deveria existir."); + + Arquivo fileDat = d2.getArquivos().stream() + .filter(a -> a.getMetaDados().getNome().equals("file.dat")) + .findFirst().orElse(null); + assertNotNull(fileDat, "Arquivo /d2/file.dat deveria existir."); + + // Verifica permissão de charlie + assertTrue(fileDat.getMetaDados().hasPermissao("charlie", "r"), + "Charlie deve ter permissão de leitura ('r')."); + assertTrue(fileDat.getMetaDados().hasPermissao("charlie", "w"), + "Charlie deve ter permissão de escrita ('w')."); + assertFalse(fileDat.getMetaDados().hasPermissao("charlie", "x"), + "Charlie não deve ter permissão de execução ('x')."); + } + + /** + * 3) Falha se o caminho não existir → CaminhoNaoEncontradoException + * - chmod("/inexistente/arquivo", "root", "any", "rwx") + */ + @Test + void testChmodPathNotFound() { + CaminhoNaoEncontradoException ex = assertThrows( + CaminhoNaoEncontradoException.class, + () -> fsImpl.chmod("/inexistente/arquivo", "root", "any", "rwx") + ); + assertTrue(ex.getMessage().contains("Componente não encontrado") || + ex.getMessage().contains("Caminho inválido"), + "Ao tentar chmod em caminho inexistente, deve lançar CaminhoNaoEncontradoException."); + } + + /** + * 4) Falha se usuário não for root e nem dono → PermissaoException + * - mkdir("/", "d3") + * - touch("/d3/foo", "alice") + * - chmod("/d3/foo", "bob", "someUser", "r-x") // 'bob' não é root nem dono + */ + @Test + void testChmodNoPermission() throws Exception { + // Cria o diretório /d3 e o arquivo como 'alice' + fsImpl.mkdir("/", "d3"); + fsImpl.touch("/d3/foo", "alice"); + + // Agora 'bob' (nem root, nem dono) tenta alterar: + PermissaoException pe = assertThrows( + PermissaoException.class, + () -> fsImpl.chmod("/d3/foo", "bob", "eve", "r-x") + ); + assertEquals( + "Usuário 'bob' não tem permissão para alterar direitos em: /d3/foo", + pe.getMessage(), + "Quando usuário não é root nem dono, deve lançar PermissaoException." + ); + } + + /** + * 5) Se a string de permissão for inválida (ex: "abx" ou tamanho != 3), deve lançar IllegalArgumentException + * - mkdir("/", "d4") + * - touch("/d4/bar", "alice") + * - chmod("/d4/bar", "alice", "mallory", "abx") + */ + @Test + void testChmodInvalidPermissionString() throws Exception { + // Cria /d4/bar + fsImpl.mkdir("/", "d4"); + fsImpl.touch("/d4/bar", "alice"); + + IllegalArgumentException iae = assertThrows( + IllegalArgumentException.class, + () -> fsImpl.chmod("/d4/bar", "alice", "mallory", "abx") + ); + assertTrue(iae.getMessage().contains("Permissão inválida"), + "Quando a string de permissão conter chars inválidos, deve lançar IllegalArgumentException."); + + // Também testa tamanho diferente de 3 + IllegalArgumentException iae2 = assertThrows( + IllegalArgumentException.class, + () -> fsImpl.chmod("/d4/bar", "alice", "mallory", "rw") + ); + assertTrue(iae2.getMessage().contains("Permissão inválida"), + "Quando a string de permissão tiver tamanho != 3, deve lançar IllegalArgumentException."); + } +} diff --git a/tests/FileSysImplMkdirTest.java b/tests/FileSysImplMkdirTest.java new file mode 100644 index 0000000..a7425c0 --- /dev/null +++ b/tests/FileSysImplMkdirTest.java @@ -0,0 +1,134 @@ +package tests; + +import filesys.Diretorio; +import filesys.FileSystemImpl; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.HashMap; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import exception.CaminhoJaExistenteException; +import exception.PermissaoException; + +/** + * Classe de testes JUnit 5 para o método mkdir(...) de FileSystemImpl. + * + * Aborda os seguintes cenários: + * 1) Criação bem‐sucessida de um diretório. + * 2) Caminho‐pai não existe → CaminhoJaExistenteException. + * 3) Já existe subdiretório com mesmo nome → CaminhoJaExistenteException. + * 4) Permissão negada (sem 'w' no MetaDados) → PermissaoException. + */ +class FileSystemImplMkdirTest { + + private FileSystemImpl fsImpl; + + @BeforeEach + void setUp() { + // Inicializa um FileSystemImpl “vazio” antes de cada teste + fsImpl = new FileSystemImpl(); + } + + /** + * 1) Teste de criação bem‐sucessido: + * mkdir("/", "usr") deve criar o subdiretório “usr” imediatamente abaixo da raiz, sem lançar exceção. + */ + @Test + void testMkdirSuccess() throws Exception { + // Raiz inicialmente só tem “/” e nenhuma subpasta + Diretorio raiz = fsImpl.getFileSys().getRaiz(); + assertTrue(raiz.getSubDirs().isEmpty(), "Antes de criar, raiz não deveria ter subdiretórios."); + + // Cria "/usr" + fsImpl.mkdir("/", "usr"); + + // Após mkdir, deve existir um subdiretório com nome “usr” em raiz + boolean encontrou = false; + for (Diretorio d : raiz.getSubDirs()) { + if (d.getMetaDados().getNome().equals("usr")) { + encontrou = true; + break; + } + } + assertTrue(encontrou, "Espere‐se que o diretório '/usr' tenha sido criado com sucesso."); + } + + /** + * 2) Quando o caminho‐pai não existir, deve lançar CaminhoJaExistenteException com a mensagem correta. + * Ex.: mkdir("/inexistente", "dirnovo") → “Caminho não encontrado: /inexistente” + */ + @Test + void testMkdirParentNotFound() { + CaminhoJaExistenteException ex = assertThrows( + CaminhoJaExistenteException.class, + () -> fsImpl.mkdir("/inexistente", "dirnovo") + ); + assertEquals( + "Caminho não encontrado: /inexistente", + ex.getMessage(), + "Deve lançar CaminhoJaExistenteException dizendo que o caminho não foi encontrado." + ); + } + + /** + * 3) Se já existir um subdiretório com o mesmo nome, deve lançar CaminhoJaExistenteException. + * Primeiro criamos "/abc", depois tentamos criar novamente "/abc". + */ + @Test + void testMkdirAlreadyExists() throws Exception { + // Cria o diretório “/abc” + fsImpl.mkdir("/", "abc"); + + // Agora, tentar criar de novo “/abc” deve falhar + CaminhoJaExistenteException ex = assertThrows( + CaminhoJaExistenteException.class, + () -> fsImpl.mkdir("/", "abc") + ); + assertEquals( + "Já existe um diretório chamado 'abc' em: /", + ex.getMessage(), + "Quando já existe, a mensagem deve indicar que o diretório já existe." + ); + } + + /** + * 4) Permissão negada: se removermos a permissão 'w' do usuário ROOT + * em algum subdiretório, mkdir dentro dele deve lançar PermissaoException. + */ + @Test + void testMkdirPermissionDenied() throws Exception { + // 1) Criamos um subdiretório “/noPerm” + fsImpl.mkdir("/", "noPerm"); + + // 2) Localiza o diretório criado + Diretorio raiz = fsImpl.getFileSys().getRaiz(); + Diretorio dirNoPerm = null; + for (Diretorio d : raiz.getSubDirs()) { + if (d.getMetaDados().getNome().equals("noPerm")) { + dirNoPerm = d; + break; + } + } + assertNotNull(dirNoPerm, "O diretório '/noPerm' deveria existir."); + + // 3) Remove explicitamente a permissão 'w' do usuário "root" neste diretório: + // (MetaDados.getPermissoes() retorna o HashMap que guarda ) + HashMap mapaPerm = dirNoPerm.getMetaDados().getPermissoes(); + // Substitui permissão “rwx” por apenas “rx” (sem 'w') + mapaPerm.put("root", "rx"); + // Agora root só pode ler/executar, não pode escrever. + + // 4) Tenta criar subdiretório dentro de “/noPerm” → deve lançar PermissaoException + PermissaoException pe = assertThrows( + PermissaoException.class, + () -> fsImpl.mkdir("/noPerm", "subdir") + ); + assertEquals( + "Usuário 'root' não tem permissão de escrita em: /noPerm", + pe.getMessage(), + "Quando não há permissão de escrita deve lançar PermissaoException com a mensagem adequada." + ); + } +} diff --git a/tests/FileSysImplTouchTest.java b/tests/FileSysImplTouchTest.java new file mode 100644 index 0000000..5f3638e --- /dev/null +++ b/tests/FileSysImplTouchTest.java @@ -0,0 +1,181 @@ +package tests; +import filesys.Arquivo; +import filesys.Diretorio; +import filesys.FileSystemImpl; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.HashMap; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import exception.CaminhoJaExistenteException; +import exception.PermissaoException; + +class FileSystemImplTouchTest { + + private FileSystemImpl fsImpl; + + @BeforeEach + void setUp() { + // Inicializar um FileSystemImpl limpo antes de cada teste + fsImpl = new FileSystemImpl(); + } + + /** + * 1) Teste de criação bem‐sucedida: + * - Cria primeiro um diretório "/docs" + * - Executa touch("/docs/arquivo.txt", "root") + * - Verifica que "arquivo.txt" exista na lista de arquivos de "/docs" + */ + @Test + void testTouchSuccess() throws Exception { + // (1) Cria o diretório "/docs" + fsImpl.mkdir("/", "docs"); + + // (2) Garante que "/docs" existe como diretório + Diretorio raiz = fsImpl.getFileSys().getRaiz(); + Diretorio dirDocs = null; + for (Diretorio d : raiz.getSubDirs()) { + if (d.getMetaDados().getNome().equals("docs")) { + dirDocs = d; + break; + } + } + assertNotNull(dirDocs, "Diretório '/docs' deveria existir após mkdir."); + + // (3) Executa touch dentro de "/docs" + fsImpl.touch("/docs/arquivo.txt", "root"); + + // (4) Verifica que "arquivo.txt" apareceu em dirDocs.getArquivos() + boolean encontrou = false; + for (Arquivo arq : dirDocs.getArquivos()) { + if (arq.getMetaDados().getNome().equals("arquivo.txt")) { + encontrou = true; + // Também podemos checar que tamanho == 0 + assertEquals(0, arq.getMetaDados().getTamanho(), + "Arquivo recém‐criado deve ter tamanho 0."); + // E que o dono é "root" + assertEquals("root", arq.getMetaDados().getDono(), + "Dono do arquivo deve ser 'root'."); + // E que há permissão "rw" para "root" + assertTrue(arq.getMetaDados().hasPermissao("root", "r"), + "Deve ter permissão de leitura (r) para 'root'."); + assertTrue(arq.getMetaDados().hasPermissao("root", "w"), + "Deve ter permissão de escrita (w) para 'root'."); + break; + } + } + assertTrue(encontrou, "Esperava‐se que '/docs/arquivo.txt' estivesse presente após touch()."); + } + + /** + * 2) Quando o diretório‐pai não existir, deve lançar CaminhoJaExistenteException + * com mensagem "Caminho não encontrado: " + */ + @Test + void testTouchParentNotFound() { + CaminhoJaExistenteException ex = assertThrows( + CaminhoJaExistenteException.class, + () -> fsImpl.touch("/naoExiste/arquivo.txt", "root") + ); + assertEquals( + "Caminho não encontrado: /naoExiste", + ex.getMessage(), + "Deve lançar CaminhoJaExistenteException informando que '/naoExiste' não existe." + ); + } + + /** + * 3) Se já existir um diretório com mesmo nome (por exemplo, tocar em "/dupdir"), + * deve lançar CaminhoJaExistenteException. + * + * Explicação: chamar touch("/dupdir", "root") equivale a: + * - pai = "/" + * - nomeArquivo = "dupdir" + * Se já houver subdiretório chamado "dupdir" em "/", então deve falhar. + */ + @Test + void testTouchAlreadyExistsAsDirectory() throws Exception { + // (1) Cria um diretório "/dupdir" + fsImpl.mkdir("/", "dupdir"); + + // (2) Tenta tocar em "/dupdir" → deveria entrar nessa checagem de conflito com diretório + CaminhoJaExistenteException ex = assertThrows( + CaminhoJaExistenteException.class, + () -> fsImpl.touch("/dupdir", "root") + ); + assertEquals( + "Já existe arquivo ou diretório chamado 'dupdir' em: /", + ex.getMessage(), + "Ao tocar em caminho que conflita com um diretório, deve lançar CaminhoJaExistenteException." + ); + } + + /** + * 4) Se já existir um arquivo com mesmo nome, deve lançar CaminhoJaExistenteException. + * + * Primeiro criamos "/docs2" e dentro dele um arquivo "a.txt", depois + * chamamos touch("/docs2/a.txt", "root") novamente. + */ + @Test + void testTouchAlreadyExistsAsFile() throws Exception { + // (1) Cria diretório "/docs2" + fsImpl.mkdir("/", "docs2"); + + // (2) Executa touch("/docs2/a.txt", "root") para criar o arquivo + fsImpl.touch("/docs2/a.txt", "root"); + + // (3) Agora tentar touch("/docs2/a.txt", "root") novamente → já existe arquivo + CaminhoJaExistenteException ex = assertThrows( + CaminhoJaExistenteException.class, + () -> fsImpl.touch("/docs2/a.txt", "root") + ); + assertEquals( + "Já existe arquivo ou diretório chamado 'a.txt' em: /docs2", + ex.getMessage(), + "Quando o arquivo já existe, deve lançar CaminhoJaExistenteException." + ); + } + + /** + * 5) Permissão negada: se removermos a permissão 'w' do usuário no diretório, touch deve falhar. + * + * - Cria "/noPermDir" + * - Remove permissão "w" de "root" dentro de "/noPermDir" + * - Chama touch("/noPermDir/novoArq", "root") → PermissaoException + */ + @Test + void testTouchPermissionDenied() throws Exception { + // (1) Cria o diretório "/noPermDir" + fsImpl.mkdir("/", "noPermDir"); + + // (2) Localiza o objeto Diretório "/noPermDir" + Diretorio raiz = fsImpl.getFileSys().getRaiz(); + Diretorio dirNoPerm = null; + for (Diretorio d : raiz.getSubDirs()) { + if (d.getMetaDados().getNome().equals("noPermDir")) { + dirNoPerm = d; + break; + } + } + assertNotNull(dirNoPerm, "O diretório '/noPermDir' deveria existir."); + + // (3) Retira permissão 'w' de "root" naquele diretório + HashMap mapaPerm = dirNoPerm.getMetaDados().getPermissoes(); + // Substitui "rwx" por "rx" (sem o 'w') + mapaPerm.put("root", "rx"); + + // (4) Agora tentar criar um arquivo dentro de "/noPermDir" deve falhar + PermissaoException pe = assertThrows( + PermissaoException.class, + () -> fsImpl.touch("/noPermDir/novoArq.txt", "root") + ); + assertEquals( + "Usuário 'root' não tem permissão de escrita em: /noPermDir", + pe.getMessage(), + "Quando não há permissão de escrita, touch deve lançar PermissaoException." + ); + } +} From ede67e2836a03f1df9f4252011358d7c8cbb1d60 Mon Sep 17 00:00:00 2001 From: JoaquimGCVS Date: Tue, 3 Jun 2025 10:30:41 -0300 Subject: [PATCH 14/32] feat: implement ls method to list directory contents with optional recursion --- filesys/FileSystemImpl.java | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 54f6e49..cb0aea9 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -257,27 +257,48 @@ public void mv(String caminhoAntigo, String caminhoNovo, String usuario) throw new UnsupportedOperationException("Método não implementado 'mv'"); } + + /** + * ls: lista o conteúdo de um diretório. + * + * @param caminho caminho absoluto para um diretório (ex: "/usr/bin") + * @param usuario quem está executando o comando + * @param recursivo se true, lista recursivamente subdiretórios + * + * @throws CaminhoNaoEncontradoException se não encontrar o diretório em 'caminho' + * @throws PermissaoException se 'usuario' não tiver permissão de leitura + */ @Override public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - // Busca o objeto (diretório ou arquivo) pelo caminho fornecido + // 1) Busca o objeto (diretório ou arquivo) pelo caminho fornecido Object obj = buscarPorCaminho(caminho); - // Se não for um diretório, lança exceção + // 2) Se não for um diretório, lança exceção if (!(obj instanceof Diretorio)) { throw new CaminhoNaoEncontradoException("Não é um diretório: " + caminho); } Diretorio dir = (Diretorio) obj; MetaDados meta = dir.getMetaDados(); - // Verifica se o usuário tem permissão de leitura, ou se é dono, ou se é root + // 3) Verifica se o usuário tem permissão de leitura, ou se é dono, ou se é root if (!meta.getPermissao(usuario).contains("r") && !meta.isDono(usuario) && !usuario.equals(ROOT_USER)) { throw new PermissaoException("Sem permissão de leitura em: " + caminho); } - // Chama o método auxiliar para listar o conteúdo do diretório + // 4) Chama o método auxiliar para listar o conteúdo do diretório listarConteudo(dir, caminho, recursivo, ""); } + /** + * Método auxiliar para listar o conteúdo de um diretório. + * Imprime todos os arquivos e subdiretórios do diretório atual. + * Se recursivo=true, lista também o conteúdo dos subdiretórios, com indentação. + * + * @param dir diretório a ser listado + * @param caminho caminho atual (não usado para exibição, mas pode ser útil para recursão) + * @param recursivo se true, lista recursivamente subdiretórios + * @param prefixo string usada para identação visual (ex: " " para subníveis) + */ private void listarConteudo(Diretorio dir, String caminho, boolean recursivo, String prefixo) { // Lista todos os arquivos do diretório atual for (Arquivo arq : dir.getArquivos()) { From 4c118b6a9b9ab3d013224bb463a44c2132d9eb47 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 14:23:59 -0300 Subject: [PATCH 15/32] =?UTF-8?q?feat:=20crianco=20m=C3=A9todo=20mv,=20get?= =?UTF-8?q?ParentePath=20e=20getNameFromPath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/FileSystemImpl.java | 195 +++++++++++++++++++++++++----------- 1 file changed, 138 insertions(+), 57 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index cb0aea9..ad6da31 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -79,22 +79,28 @@ public void mkdir(String caminho, String nome) } /** - * chmod: altera a permissão de 'usuarioAlvo' no arquivo ou diretório em 'caminho'. + * chmod: altera a permissão de 'usuarioAlvo' no arquivo ou diretório em + * 'caminho'. * - * @param caminho caminho absoluto para um arquivo OU diretório (ex: "/usr/bin/arquivo.txt" ou "/usr/bin") - * @param usuario quem está executando o comando (deve ser root OU dono do objeto) - * @param usuarioAlvo usuário cujas permissões serão ajustadas - * @param permissao string de 3 caracteres, cada um em { 'r', 'w', 'x' } ou '-' - * Ex.: "rwx", "r--", "-w-", "---" + * @param caminho caminho absoluto para um arquivo OU diretório (ex: + * "/usr/bin/arquivo.txt" ou "/usr/bin") + * @param usuario quem está executando o comando (deve ser root OU dono do + * objeto) + * @param usuarioAlvo usuário cujas permissões serão ajustadas + * @param permissao string de 3 caracteres, cada um em { 'r', 'w', 'x' } ou + * '-' + * Ex.: "rwx", "r--", "-w-", "---" * - * @throws CaminhoNaoEncontradoException se não encontrar o arquivo/diretório em 'caminho' - * @throws PermissaoException se 'usuario' não for root nem dono do objeto + * @throws CaminhoNaoEncontradoException se não encontrar o arquivo/diretório em + * 'caminho' + * @throws PermissaoException se 'usuario' não for root nem dono do + * objeto */ @Override public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) - throws CaminhoNaoEncontradoException, PermissaoException - { - // 1) Validar string de permissão: deve ter exatamente 3 caracteres, cada um seja 'r','w','x' ou '-' + throws CaminhoNaoEncontradoException, PermissaoException { + // 1) Validar string de permissão: deve ter exatamente 3 caracteres, cada um + // seja 'r','w','x' ou '-' if (permissao == null || permissao.length() != 3) { throw new IllegalArgumentException("Permissão inválida (deve ter 3 chars): " + permissao); } @@ -119,23 +125,23 @@ public void chmod(String caminho, String usuario, String usuarioAlvo, String per } // 3) Verificar se 'usuario' tem permissão para executar chmod: - // - Se for ROOT_USER, sempre permitido. - // - Caso contrário, somente se for dono do objeto + // - Se for ROOT_USER, sempre permitido. + // - Caso contrário, somente se for dono do objeto String dono = mdAlvo.getDono(); if (!usuario.equals(ROOT_USER) && !usuario.equals(dono)) { throw new PermissaoException( - "Usuário '" + usuario + "' não tem permissão para alterar direitos em: " - + caminho - ); + "Usuário '" + usuario + "' não tem permissão para alterar direitos em: " + + caminho); } // 4) Alterar (ou inserir) a permissão de 'usuarioAlvo' para 'permissao' - // Se o usuárioAlvo for "root", deixamos que ele fique “rwx” obrigatoriamente, - // independentemente do que se passe em 'permissao'? - // Depende do requisito, mas aqui vamos aceitar qualquer string para qualquer usuárioAlvo, - // pois, se o root quiser revogar até de si mesmo, é opção dele. + // Se o usuárioAlvo for "root", deixamos que ele fique “rwx” obrigatoriamente, + // independentemente do que se passe em 'permissao'? + // Depende do requisito, mas aqui vamos aceitar qualquer string para qualquer + // usuárioAlvo, + // pois, se o root quiser revogar até de si mesmo, é opção dele. // - // Simplesmente atualizamos o HashMap: + // Simplesmente atualizamos o HashMap: HashMap mapaPerm = mdAlvo.getPermissoes(); mapaPerm.put(usuarioAlvo, permissao); mdAlvo.setPermissoes(mapaPerm); @@ -150,12 +156,11 @@ public void rm(String caminho, String usuario, boolean recursivo) /** * Método touch: cria um arquivo vazio em 'caminho'. * Exemplo: touch("/usr/local/meuarquivo.txt", "alice") - * -> pai = "/usr/local", nome = "meuarquivo.txt" + * -> pai = "/usr/local", nome = "meuarquivo.txt" */ @Override - public void touch(String caminho, String usuario) - throws CaminhoJaExistenteException, PermissaoException - { + public void touch(String caminho, String usuario) + throws CaminhoJaExistenteException, PermissaoException { // 1) Separar o caminho em 'pai' e 'nomeDoArquivo' String path = caminho.trim(); if (!path.startsWith("/")) { @@ -184,12 +189,10 @@ public void touch(String caminho, String usuario) if (!(o instanceof Diretorio)) { // Se não for diretório (por exemplo, for um arquivo), não podemos criar dentro throw new CaminhoJaExistenteException( - "Caminho pai não é um diretório: " + paiPath - ); + "Caminho pai não é um diretório: " + paiPath); } dirPai = (Diretorio) o; - } - catch (CaminhoNaoEncontradoException e) { + } catch (CaminhoNaoEncontradoException e) { // Se pai não existe, relançamos como CaminhoJaExistenteException throw new CaminhoJaExistenteException("Caminho não encontrado: " + paiPath); } @@ -198,10 +201,9 @@ public void touch(String caminho, String usuario) MetaDados mdPai = dirPai.getMetaDados(); if (!mdPai.hasPermissao(usuario, "w")) { throw new PermissaoException( - "Usuário '" + usuario - + "' não tem permissão de escrita em: " - + paiPath - ); + "Usuário '" + usuario + + "' não tem permissão de escrita em: " + + paiPath); } // 4) Verificar se já existe um arquivo ou diretório com esse nome em dirPai @@ -209,33 +211,31 @@ public void touch(String caminho, String usuario) for (Diretorio sub : dirPai.getSubDirs()) { if (sub.getMetaDados().getNome().equals(nomeArquivo)) { throw new CaminhoJaExistenteException( - "Já existe arquivo ou diretório chamado '" + nomeArquivo - + "' em: " + paiPath - ); + "Já existe arquivo ou diretório chamado '" + nomeArquivo + + "' em: " + paiPath); } } // 4.2) Checa arquivos for (Arquivo arq : dirPai.getArquivos()) { if (arq.getMetaDados().getNome().equals(nomeArquivo)) { throw new CaminhoJaExistenteException( - "Já existe arquivo ou diretório chamado '" + nomeArquivo - + "' em: " + paiPath - ); + "Já existe arquivo ou diretório chamado '" + nomeArquivo + + "' em: " + paiPath); } } // 5) Se chegamos aqui, podemos criar o novo arquivo vazio - // 5.1) Criar MetaDados com tamanho = 0 + // 5.1) Criar MetaDados com tamanho = 0 MetaDados metaArq = new MetaDados(nomeArquivo, 0, usuario); - // conceder permissão “rw” para o dono + // conceder permissão “rw” para o dono HashMap mapaPerm = new HashMap<>(); mapaPerm.put(usuario, "rw"); metaArq.setPermissoes(mapaPerm); - // 5.2) Criar o objeto Arquivo com 0 blocos (array vazio) + // 5.2) Criar o objeto Arquivo com 0 blocos (array vazio) Arquivo novoArq = new Arquivo(metaArq, new Bloco[0]); - // 5.3) Adicionar ao diretório pai + // 5.3) Adicionar ao diretório pai dirPai.addArquivo(novoArq); } @@ -254,22 +254,101 @@ public void read(String caminho, String usuario, byte[] buffer) @Override public void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mv'"); + // Não permite mover a raiz + if (caminhoAntigo.equals("/") || caminhoNovo.equals("/")) { + throw new PermissaoException("Não é possível mover o diretório raiz."); + } + + // 1) Localiza o pai e o objeto de origem + String caminhoPaiAntigo = getParentPath(caminhoAntigo); + String nomeAntigo = getNameFromPath(caminhoAntigo); + Diretorio paiAntigo = (Diretorio) buscarPorCaminho(caminhoPaiAntigo); + + Object alvo = null; + for (Diretorio d : paiAntigo.getSubDirs()) { + if (d.getMetaDados().getNome().equals(nomeAntigo)) { + alvo = d; + break; + } + } + if (alvo == null) { + for (Arquivo a : paiAntigo.getArquivos()) { + if (a.getMetaDados().getNome().equals(nomeAntigo)) { + alvo = a; + break; + } + } + } + if (alvo == null) + throw new CaminhoNaoEncontradoException("Origem não encontrada: " + caminhoAntigo); + + // 2) Verifica permissão de escrita no pai da origem + if (!paiAntigo.getMetaDados().hasPermissao(usuario, "w")) { + throw new PermissaoException("Sem permissão de escrita no diretório de origem: " + caminhoPaiAntigo); + } + + // 3) Localiza o pai do destino e o nome novo + String caminhoPaiNovo = getParentPath(caminhoNovo); + String nomeNovo = getNameFromPath(caminhoNovo); + Diretorio paiNovo = (Diretorio) buscarPorCaminho(caminhoPaiNovo); + + // 4) Verifica permissão de escrita no pai do destino + if (!paiNovo.getMetaDados().hasPermissao(usuario, "w")) { + throw new PermissaoException("Sem permissão de escrita no diretório de destino: " + caminhoPaiNovo); + } + + // 5) Verifica se já existe item com o nome novo no destino + for (Diretorio d : paiNovo.getSubDirs()) { + if (d.getMetaDados().getNome().equals(nomeNovo)) { + throw new PermissaoException("Já existe um diretório no destino com esse nome."); + } + } + for (Arquivo a : paiNovo.getArquivos()) { + if (a.getMetaDados().getNome().equals(nomeNovo)) { + throw new PermissaoException("Já existe um arquivo no destino com esse nome."); + } + } + + // 6) Remove do pai antigo, renomeia e adiciona ao novo pai + if (alvo instanceof Diretorio) { + paiAntigo.getSubDirs().remove(alvo); + ((Diretorio) alvo).getMetaDados().setNome(nomeNovo); + paiNovo.addSubDiretorio((Diretorio) alvo); + } else if (alvo instanceof Arquivo) { + paiAntigo.getArquivos().remove(alvo); + ((Arquivo) alvo).getMetaDados().setNome(nomeNovo); + paiNovo.addArquivo((Arquivo) alvo); + } else { + throw new CaminhoNaoEncontradoException("Origem não é arquivo nem diretório."); + } + } + + // Funções auxiliares privadas + private String getParentPath(String caminho) { + int idx = caminho.lastIndexOf("/"); + return (idx == 0) ? "/" : caminho.substring(0, idx); + } + + private String getNameFromPath(String caminho) { + int idx = caminho.lastIndexOf("/"); + return caminho.substring(idx + 1); } - /** * ls: lista o conteúdo de um diretório. * - * @param caminho caminho absoluto para um diretório (ex: "/usr/bin") - * @param usuario quem está executando o comando - * @param recursivo se true, lista recursivamente subdiretórios + * @param caminho caminho absoluto para um diretório (ex: "/usr/bin") + * @param usuario quem está executando o comando + * @param recursivo se true, lista recursivamente subdiretórios * - * @throws CaminhoNaoEncontradoException se não encontrar o diretório em 'caminho' - * @throws PermissaoException se 'usuario' não tiver permissão de leitura + * @throws CaminhoNaoEncontradoException se não encontrar o diretório em + * 'caminho' + * @throws PermissaoException se 'usuario' não tiver permissão de + * leitura */ @Override - public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { + public void ls(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { // 1) Busca o objeto (diretório ou arquivo) pelo caminho fornecido Object obj = buscarPorCaminho(caminho); @@ -294,10 +373,11 @@ public void ls(String caminho, String usuario, boolean recursivo) throws Caminho * Imprime todos os arquivos e subdiretórios do diretório atual. * Se recursivo=true, lista também o conteúdo dos subdiretórios, com indentação. * - * @param dir diretório a ser listado - * @param caminho caminho atual (não usado para exibição, mas pode ser útil para recursão) - * @param recursivo se true, lista recursivamente subdiretórios - * @param prefixo string usada para identação visual (ex: " " para subníveis) + * @param dir diretório a ser listado + * @param caminho caminho atual (não usado para exibição, mas pode ser útil + * para recursão) + * @param recursivo se true, lista recursivamente subdiretórios + * @param prefixo string usada para identação visual (ex: " " para subníveis) */ private void listarConteudo(Diretorio dir, String caminho, boolean recursivo, String prefixo) { // Lista todos os arquivos do diretório atual @@ -307,7 +387,8 @@ private void listarConteudo(Diretorio dir, String caminho, boolean recursivo, St // Lista todos os subdiretórios do diretório atual for (Diretorio sub : dir.getSubDirs()) { System.out.println(prefixo + sub.getMetaDados().getNome() + "/"); // "/" indica diretório - // Se for recursivo, chama novamente para o subdiretório, aumentando o prefixo (indentação) + // Se for recursivo, chama novamente para o subdiretório, aumentando o prefixo + // (indentação) if (recursivo) { listarConteudo(sub, caminho + "/" + sub.getMetaDados().getNome(), true, prefixo + " "); } @@ -319,7 +400,7 @@ public void addUser(String user) { } @Override - public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) + public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { Object origemObj = buscarPorCaminho(caminhoOrigem); From 9e8517ac0622934fc3e0e4dab0fe043dc785d6e8 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 14:26:58 -0300 Subject: [PATCH 16/32] =?UTF-8?q?feat:=20criando=20a=20fun=C3=A7=C3=A3o=20?= =?UTF-8?q?de=20read?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/FileSystemImpl.java | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index ad6da31..900018c 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -248,7 +248,33 @@ public void write(String caminho, String usuario, boolean anexar, byte[] buffer) @Override public void read(String caminho, String usuario, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'read'"); + // 1) Localiza o arquivo + Object obj = buscarPorCaminho(caminho); + if (!(obj instanceof Arquivo)) { + throw new CaminhoNaoEncontradoException("Arquivo não encontrado: " + caminho); + } + Arquivo arquivo = (Arquivo) obj; + + // 2) Verifica permissão de leitura + MetaDados meta = arquivo.getMetaDados(); + if (!meta.hasPermissao(usuario, "r") && !meta.isDono(usuario) && !usuario.equals(ROOT_USER)) { + throw new PermissaoException("Sem permissão de leitura em: " + caminho); + } + + // 3) Lê sequencialmente os blocos do arquivo para o buffer + Bloco[] blocos = arquivo.getArquivo(); + int bufferPos = 0; + for (Bloco bloco : blocos) { + byte[] dados = bloco.getDados(); + int bytesParaLer = Math.min(dados.length, buffer.length - bufferPos); + if (bytesParaLer <= 0) + break; + System.arraycopy(dados, 0, buffer, bufferPos, bytesParaLer); + bufferPos += bytesParaLer; + if (bufferPos >= buffer.length) + break; + } + // Se quiser, pode retornar quantos bytes foram lidos (bufferPos) } @Override From a09e165f0747f081f43863c5e105e925a9a1e566 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 17:45:24 -0300 Subject: [PATCH 17/32] feat: alterando pastas e refazendo metodo read --- Main.java | 146 ++++++++++--------- filesys/Arquivo.java | 94 ++++++------ filesys/Bloco.java | 21 +-- filesys/Diretorio.java | 99 +++++++------ filesys/ElementoFS.java | 39 +++++ filesys/FileSys.java | 15 -- filesys/FileSystem.java | 6 +- filesys/FileSystemImpl.java | 249 +++++--------------------------- filesys/MetaDados.java | 81 ----------- filesys/Permissao.java | 31 ++++ filesys/Usuario.java | 45 ++++++ tests/FileSysImplChmodTest.java | 165 --------------------- tests/FileSysImplMkdirTest.java | 134 ----------------- tests/FileSysImplTouchTest.java | 181 ----------------------- tests/PermissionTest.java | 8 +- 15 files changed, 346 insertions(+), 968 deletions(-) create mode 100644 filesys/ElementoFS.java delete mode 100644 filesys/FileSys.java delete mode 100644 filesys/MetaDados.java create mode 100644 filesys/Permissao.java create mode 100644 filesys/Usuario.java delete mode 100644 tests/FileSysImplChmodTest.java delete mode 100644 tests/FileSysImplMkdirTest.java delete mode 100644 tests/FileSysImplTouchTest.java diff --git a/Main.java b/Main.java index 1e327d9..5a883eb 100644 --- a/Main.java +++ b/Main.java @@ -1,4 +1,5 @@ import filesys.IFileSystem; +import filesys.Usuario; import filesys.FileSystem; import java.util.Scanner; @@ -17,6 +18,8 @@ public class Main { // Constantes úteis para a versão interativa. + // 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; @@ -30,92 +33,74 @@ public class Main { // Usuário que está executando o programa private static String user; - // Lista interna para guardar, temporariamente, cada permissão lida do arquivo - private static class EntryPermissao { - String usuario; - String dir; - String permissoes; - EntryPermissao(String u, String d, String p) { - this.usuario = u; - this.dir = d; - this.permissoes = p; - } - } - + // 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. public static void main(String[] args) { + // Usuário que está executando o programa. + // 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"); return; } user = args[1]; - // Lista onde guardaremos as linhas do arquivo 'users/users' - List entradas = new ArrayList<>(); - - // 1) Ler o arquivo users/users e armazenar cada tripla em 'entradas' + // Carrega a lista de usuários do sistema a partir de arquivo + // Formato do arquivo users: + // username dir permission + // Exemplo: + // maria /** rw- + // luzia /** rwx + // Essa permissão vale para o diretório raiz e sub diretórios. + // A partir do momento que um usuário cria outro diretório ou arquivo, + // a permissão desse usuário é de leitura, escrita e execução nesse novo + // diretório/arquivo, + // e sempre será rwx para o usuário root. + List usuarios = new ArrayList<>(); try { Scanner userScanner = new Scanner(new java.io.File("users/users")); while (userScanner.hasNextLine()) { String line = userScanner.nextLine().trim(); - if (line.isEmpty()) continue; - - String[] parts = line.split("\\s+"); - if (parts.length == 3) { - String userListed = parts[0]; // Ex: "maria" - String dir = parts[1]; // Ex: "/**" - String perm = parts[2]; // Ex: "rw-" - - // Armazena temporariamente para aplicar via chmod mais tarde - entradas.add(new EntryPermissao(userListed, dir, perm)); - } - else { - System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); + if (!line.isEmpty()) { + String[] parts = line.split(" "); + if (parts.length == 3) { + String userListed = parts[0]; + String dir = parts[1]; + String dirPermission = parts[2]; + + usuarios.add(new Usuario(userListed, dirPermission, dir)); + + } else { + System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); + } } } userScanner.close(); - } - catch (FileNotFoundException e) { + } 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; } - // 2) Cria o FileSystem (virtual, em memória) - // (aqui poderíamos passar lista de usuários se quisesse, mas vamos usar - // a própria chamada chmod para inicializar as permissões) - fileSystem = new FileSystem(); - - // 3) “Processar” cada entrada: chamar chmod em cada tripla (usuário=root) - for (EntryPermissao ep : entradas) { - // Traduzimos "/**" como "/" no nosso FileSystem virtual - String caminhoParaChmod = ep.dir; - if (caminhoParaChmod.equals("/**")) { - caminhoParaChmod = ROOT_DIR; - } - try { - // ROOT_USER tem permissão para alterar tudo - fileSystem.chmod(caminhoParaChmod, ROOT_USER, ep.usuario, ep.permissoes); - } - catch (CaminhoNaoEncontradoException | PermissaoException ex) { - // Geralmente não deve acontecer, pois sabemos que o ROOT_DIR ("/") existe - // Mas, se o usuário pediu permissão para um diretório que não existe, - // imprimimos uma mensagem de advertência e continuamos - System.out.println("Aviso: não foi possível aplicar permissão em '" - + ep.dir + "' para '" + ep.usuario - + "': " + ex.getMessage()); - } - } + // 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(usuarios); - // Se você quiser garantir que a raiz foi criada (caso FileSystem não faça sozinho), - // descomente o bloco abaixo: - /* + // ! JA ESTA IMPLEMENTADO NO FILESYSTEM + // // 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()); } - */ - // 4) Inicia o menu interativo + // Menu interativo. menu(); } @@ -138,23 +123,40 @@ public static void menu() { String opcao = scanner.nextLine(); try { switch (opcao) { - case "1": chmod(); break; - case "2": mkdir(); break; - case "3": rm(); break; - case "4": touch(); break; - case "5": write(); break; - case "6": read(); break; - case "7": mv(); break; - case "8": ls(); break; - case "9": cp(); break; + case "1": + chmod(); + break; + case "2": + mkdir(); + break; + case "3": + rm(); + break; + case "4": + touch(); + break; + case "5": + write(); + break; + case "6": + read(); + break; + case "7": + mv(); + break; + case "8": + ls(); + break; + case "9": + cp(); + break; case "0": System.out.println("Encerrando..."); return; default: System.out.println("Comando inválido!"); } - } - catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException e) { + } catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException e) { System.out.println("Erro: " + e.getMessage()); } diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java index 34a254b..337f19c 100644 --- a/filesys/Arquivo.java +++ b/filesys/Arquivo.java @@ -1,61 +1,73 @@ package filesys; -public class Arquivo { - private MetaDados metaDados; - private Bloco[] arquivo; +import java.util.ArrayList; +import java.util.List; - // Construtor original - public Arquivo(String nome, String dono, int tamanhoBloco) { - this.metaDados = new MetaDados(nome, 0, dono); - this.arquivo = new Bloco[1]; // Inicializa com um bloco - this.arquivo[0] = new Bloco(tamanhoBloco); +public class Arquivo extends ElementoFS { + private List blocos; + private long tamanho; + + public Arquivo(String nome, String permissoes, String dono) { + super(nome, permissoes, dono); + // this.arquivo = new Bloco[0]; + this.blocos = new ArrayList<>(); + this.tamanho = 0; } - // *** Novo construtor que permite criar um 'Arquivo' a partir de - // um MetaDados já pronto e de um array de Bloco[]. *** - public Arquivo(MetaDados metaDados, Bloco[] arquivo) { - this.metaDados = metaDados; - this.arquivo = arquivo; + /* + * public Bloco[] getArquivo() { + * return arquivo; + * } + * + * public void setArquivo(Bloco[] arquivo) { + * this.arquivo = arquivo; + * } + */ + + public void adicionarBloco(byte[] dados) { + blocos.add(dados); + tamanho += dados.length; } - public MetaDados getMetaDados() { - return metaDados; + public List getBlocos() { + return blocos; } - public void setMetaDados(MetaDados metaDados) { - this.metaDados = metaDados; + public long getTamanho() { + return tamanho; } - public Bloco[] getArquivo() { - return arquivo; + 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 setArquivo(Bloco[] arquivo) { - this.arquivo = arquivo; + public void limparBlocos() { + blocos.clear(); + tamanho = 0; } - public void addBloco(Bloco bloco) { - Bloco[] novoArquivo = new Bloco[this.arquivo.length + 1]; - System.arraycopy(this.arquivo, 0, novoArquivo, 0, this.arquivo.length); - novoArquivo[this.arquivo.length] = bloco; - this.arquivo = novoArquivo; - this.metaDados.setTamanho(this.metaDados.getTamanho() + bloco.getTamanho()); + @Override + public boolean isArquivo() { + return true; } - public void removeBloco(int index) { - if (index < 0 || index >= this.arquivo.length) { - throw new IndexOutOfBoundsException("Índice fora dos limites do arquivo"); - } - Bloco[] novoArquivo = new Bloco[this.arquivo.length - 1]; - System.arraycopy(this.arquivo, 0, novoArquivo, 0, index); - System.arraycopy(this.arquivo, index + 1, novoArquivo, index, this.arquivo.length - index - 1); - // Ajusta tamanho total subtraindo o bloco removido - this.metaDados.setTamanho(this.metaDados.getTamanho() - this.arquivo[index].getTamanho()); - this.arquivo = novoArquivo; + @Override + public String toString() { + return "Arquivo: " + nomeDiretorio + " | Dono: " + donoDiretorio + " | Permissões: " + permissoesPadrao + " | Tam: " + + tamanho + " bytes"; } - public void clear() { - this.arquivo = new Bloco[0]; - this.metaDados.setTamanho(0); + 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."); } -} + +} \ No newline at end of file diff --git a/filesys/Bloco.java b/filesys/Bloco.java index c1d3608..87d8cf9 100644 --- a/filesys/Bloco.java +++ b/filesys/Bloco.java @@ -1,15 +1,12 @@ package filesys; public class Bloco { + private static final int TAMANHO_BLOCO = 4096; // 4KB por bloco private byte[] dados; - private int tamanho; + public Bloco(int tamanho) { - if (tamanho <= 0) { - throw new IllegalArgumentException("Tamanho do bloco deve ser maior que zero"); - } - this.tamanho = tamanho; - this.dados = new byte[tamanho]; + this.dados = new byte[TAMANHO_BLOCO]; } public byte[] getDados() { @@ -17,14 +14,6 @@ public byte[] getDados() { } public void setDados(byte[] dados) { - if (dados.length <= tamanho) { - this.dados = dados; - } else { - throw new IllegalArgumentException("Dados excedem o tamanho do bloco"); - } - } - - public int getTamanho() { - return tamanho; + this.dados = dados; } -} +} \ No newline at end of file diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index 2e2adf9..90ae5a4 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -1,72 +1,77 @@ package filesys; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; -public class Diretorio { +public class Diretorio extends ElementoFS { + private Diretorio diretorioPai; + private Map filhos; + private Map acessosPorUsuario; // permissões específicas - private MetaDados metaDados; - // Lista de arquivos neste diretório - private List arquivos = new ArrayList<>(); - // Lista de subdiretórios (filhos) - private List subDirs = new ArrayList<>(); + 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 Diretorio(MetaDados metaDados) { - this.metaDados = metaDados; + 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 Diretorio(String nome, String dono) { - this.metaDados = new MetaDados(nome, dono); + 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; + if (diretorioPai != null) return diretorioPai.temPermissao(usuario, tipoPermissao); + return false; } - public MetaDados getMetaDados() { - return metaDados; + public String obterPermissoesDoUsuario(String usuario) { + if ("root".equals(usuario)) return "rwx"; + if (usuario.equals(donoDiretorio)) return permissoesPadrao; + return acessosPorUsuario.getOrDefault(usuario, "---"); } - public void setMetaDados(MetaDados metaDados) { - this.metaDados = metaDados; + 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 List getArquivos() { - return arquivos; + public void removerFilho(String nomeFilho) { + filhos.remove(nomeFilho); } - public List getSubDirs() { - return subDirs; + public Map getFilhos() { + return filhos; } - /** - * Procura um subdiretório de nome exato dentro deste diretório. - * Retorna o objeto Diretorio se encontrado, ou null caso não exista. - */ - public Diretorio pegarSubDirPeloNome(String nome) { - for (Diretorio d : subDirs) { - if (d.getMetaDados().getNome().equals(nome)) { - return d; - } - } - return null; + public Diretorio getDiretorioPai() { + return diretorioPai; } - /** - * Adiciona um subdiretório filho a este diretório. - */ - public void addSubDiretorio(Diretorio subDir) { - this.subDirs.add(subDir); // Assuming subDirs is a List + public void setDiretorioPai(Diretorio diretorioPai) { + this.diretorioPai = diretorioPai; } - /** - * Adiciona um arquivo a este diretório. - */ - public void addArquivo(Arquivo novo) { - arquivos.add(novo); + @Override + public boolean isArquivo() { + return false; } - /** - * Remove um arquivo deste diretório. - * Retorna true se o arquivo foi encontrado e removido, ou false se não existia. - */ - public boolean removerArquivo(Arquivo arquivo) { - return arquivos.remove(arquivo); + @Override + public String toString() { + return "Dir: " + nomeDiretorio + " | Owner: " + donoDiretorio + " | Perms: " + permissoesPadrao; } } diff --git a/filesys/ElementoFS.java b/filesys/ElementoFS.java new file mode 100644 index 0000000..f2e53f2 --- /dev/null +++ b/filesys/ElementoFS.java @@ -0,0 +1,39 @@ +package filesys; + +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(); +} diff --git a/filesys/FileSys.java b/filesys/FileSys.java deleted file mode 100644 index a3af10e..0000000 --- a/filesys/FileSys.java +++ /dev/null @@ -1,15 +0,0 @@ -package filesys; - -public class FileSys { - private Diretorio raiz; - - public FileSys() { - this.raiz = new Diretorio("Raiz", "root"); - } - public Diretorio getRaiz() { - return raiz; - } - public void setRaiz(Diretorio raiz) { - this.raiz = raiz; - } -} diff --git a/filesys/FileSystem.java b/filesys/FileSystem.java index 8d7a83b..3c3ea61 100644 --- a/filesys/FileSystem.java +++ b/filesys/FileSystem.java @@ -1,5 +1,7 @@ package filesys; +import java.util.List; + import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; @@ -9,8 +11,8 @@ final public class FileSystem implements IFileSystem { private final IFileSystem fileSystemImpl; - public FileSystem() { - fileSystemImpl = new FileSystemImpl(); + public FileSystem(List usuarios) { + fileSystemImpl = new FileSystemImpl(usuarios); } @Override diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 900018c..12aa14c 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import exception.CaminhoJaExistenteException; @@ -14,14 +15,38 @@ // 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 FileSys fileSys; + private Diretorio raiz; + private Map usuarios = new HashMap<>(); - public FileSys getFileSys() { - return fileSys; + public FileSystemImpl(List u) { + this.raiz = new Diretorio("/", "rwx", ROOT_USER); + usuarios.put(ROOT_USER, new Usuario(ROOT_USER, "rwx", "/")); + for (Usuario usuario : u) { + if (!usuario.getNome().equalsIgnoreCase("root")) + usuarios.put(usuario.getNome(), usuario); + } } - public FileSystemImpl() { - this.fileSys = new FileSys(); + // TODO: Validar se o método navegar cobre todos os casos de caminhos relativos + // e absolutos + 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; } @Override @@ -249,115 +274,37 @@ public void write(String caminho, String usuario, boolean anexar, byte[] buffer) public void read(String caminho, String usuario, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { // 1) Localiza o arquivo - Object obj = buscarPorCaminho(caminho); + Object obj = navegar(caminho); if (!(obj instanceof Arquivo)) { throw new CaminhoNaoEncontradoException("Arquivo não encontrado: " + caminho); } Arquivo arquivo = (Arquivo) obj; // 2) Verifica permissão de leitura - MetaDados meta = arquivo.getMetaDados(); - if (!meta.hasPermissao(usuario, "r") && !meta.isDono(usuario) && !usuario.equals(ROOT_USER)) { + // Aqui, supondo que permissoesPadrao seja do tipo "rw-" ou "r--" + if (!usuario.equals(ROOT_USER) && !arquivo.donoDiretorio.equals(usuario) + && !arquivo.permissoesPadrao.contains("r")) { throw new PermissaoException("Sem permissão de leitura em: " + caminho); } // 3) Lê sequencialmente os blocos do arquivo para o buffer - Bloco[] blocos = arquivo.getArquivo(); int bufferPos = 0; - for (Bloco bloco : blocos) { - byte[] dados = bloco.getDados(); - int bytesParaLer = Math.min(dados.length, buffer.length - bufferPos); + for (byte[] bloco : arquivo.getBlocos()) { + int bytesParaLer = Math.min(bloco.length, buffer.length - bufferPos); if (bytesParaLer <= 0) break; - System.arraycopy(dados, 0, buffer, bufferPos, bytesParaLer); + System.arraycopy(bloco, 0, buffer, bufferPos, bytesParaLer); bufferPos += bytesParaLer; if (bufferPos >= buffer.length) break; } - // Se quiser, pode retornar quantos bytes foram lidos (bufferPos) + // buffer agora contém o conteúdo lido (até buffer.length ou fim do arquivo) } @Override public void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException { - // Não permite mover a raiz - if (caminhoAntigo.equals("/") || caminhoNovo.equals("/")) { - throw new PermissaoException("Não é possível mover o diretório raiz."); - } - - // 1) Localiza o pai e o objeto de origem - String caminhoPaiAntigo = getParentPath(caminhoAntigo); - String nomeAntigo = getNameFromPath(caminhoAntigo); - Diretorio paiAntigo = (Diretorio) buscarPorCaminho(caminhoPaiAntigo); - - Object alvo = null; - for (Diretorio d : paiAntigo.getSubDirs()) { - if (d.getMetaDados().getNome().equals(nomeAntigo)) { - alvo = d; - break; - } - } - if (alvo == null) { - for (Arquivo a : paiAntigo.getArquivos()) { - if (a.getMetaDados().getNome().equals(nomeAntigo)) { - alvo = a; - break; - } - } - } - if (alvo == null) - throw new CaminhoNaoEncontradoException("Origem não encontrada: " + caminhoAntigo); - - // 2) Verifica permissão de escrita no pai da origem - if (!paiAntigo.getMetaDados().hasPermissao(usuario, "w")) { - throw new PermissaoException("Sem permissão de escrita no diretório de origem: " + caminhoPaiAntigo); - } - - // 3) Localiza o pai do destino e o nome novo - String caminhoPaiNovo = getParentPath(caminhoNovo); - String nomeNovo = getNameFromPath(caminhoNovo); - Diretorio paiNovo = (Diretorio) buscarPorCaminho(caminhoPaiNovo); - - // 4) Verifica permissão de escrita no pai do destino - if (!paiNovo.getMetaDados().hasPermissao(usuario, "w")) { - throw new PermissaoException("Sem permissão de escrita no diretório de destino: " + caminhoPaiNovo); - } - // 5) Verifica se já existe item com o nome novo no destino - for (Diretorio d : paiNovo.getSubDirs()) { - if (d.getMetaDados().getNome().equals(nomeNovo)) { - throw new PermissaoException("Já existe um diretório no destino com esse nome."); - } - } - for (Arquivo a : paiNovo.getArquivos()) { - if (a.getMetaDados().getNome().equals(nomeNovo)) { - throw new PermissaoException("Já existe um arquivo no destino com esse nome."); - } - } - - // 6) Remove do pai antigo, renomeia e adiciona ao novo pai - if (alvo instanceof Diretorio) { - paiAntigo.getSubDirs().remove(alvo); - ((Diretorio) alvo).getMetaDados().setNome(nomeNovo); - paiNovo.addSubDiretorio((Diretorio) alvo); - } else if (alvo instanceof Arquivo) { - paiAntigo.getArquivos().remove(alvo); - ((Arquivo) alvo).getMetaDados().setNome(nomeNovo); - paiNovo.addArquivo((Arquivo) alvo); - } else { - throw new CaminhoNaoEncontradoException("Origem não é arquivo nem diretório."); - } - } - - // Funções auxiliares privadas - private String getParentPath(String caminho) { - int idx = caminho.lastIndexOf("/"); - return (idx == 0) ? "/" : caminho.substring(0, idx); - } - - private String getNameFromPath(String caminho) { - int idx = caminho.lastIndexOf("/"); - return caminho.substring(idx + 1); } /** @@ -429,126 +376,6 @@ public void addUser(String user) { public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - Object origemObj = buscarPorCaminho(caminhoOrigem); - if (origemObj == null) { - throw new CaminhoNaoEncontradoException("Origem não encontrada: " + caminhoOrigem); - } - - Object destinoObj = buscarPorCaminho(caminhoDestino); - if (!(destinoObj instanceof Diretorio)) { - throw new CaminhoNaoEncontradoException("Destino inválido (não é diretório): " + caminhoDestino); - } - Diretorio dirDestino = (Diretorio) destinoObj; - - MetaDados mdOrigem = (origemObj instanceof Arquivo) - ? ((Arquivo) origemObj).getMetaDados() - : ((Diretorio) origemObj).getMetaDados(); - - if (!mdOrigem.hasPermissao(usuario, "r")) { - throw new PermissaoException("Sem permissão de leitura em: " + caminhoOrigem); - } - if (!dirDestino.getMetaDados().hasPermissao(usuario, "w")) { - throw new PermissaoException("Sem permissão de escrita em: " + caminhoDestino); - } - - if (origemObj instanceof Arquivo) { - cpArquivo((Arquivo) origemObj, dirDestino); - } else { - Diretorio dirOrig = (Diretorio) origemObj; - if (!recursivo) { - throw new UnsupportedOperationException( - "Cópia de diretório requer recursivo=true: " + caminhoOrigem); - } - cpDiretorio(dirOrig, dirDestino); - } - } - - private void cpArquivo(Arquivo origem, Diretorio destino) { - MetaDados mdOrig = origem.getMetaDados(); - MetaDados mdNovo = clonarMetaDados(mdOrig, mdOrig.getNome(), mdOrig.getTamanho(), mdOrig.getDono()); - - Bloco[] blocosOrig = origem.getArquivo(); - Bloco[] blocosNovo = new Bloco[blocosOrig.length]; - for (int i = 0; i < blocosOrig.length; i++) { - byte[] dadosOrigem = blocosOrig[i].getDados(); - byte[] copiaDados = Arrays.copyOf(dadosOrigem, dadosOrigem.length); - Bloco novoBloco = new Bloco(dadosOrigem.length); - novoBloco.setDados(copiaDados); - blocosNovo[i] = novoBloco; - } - - Arquivo copia = new Arquivo(mdNovo, blocosNovo); - destino.addArquivo(copia); - } - - private void cpDiretorio(Diretorio origem, Diretorio destino) { - MetaDados mdOrig = origem.getMetaDados(); - MetaDados mdNovo = clonarMetaDados(mdOrig, mdOrig.getNome(), 0, mdOrig.getDono()); - - Diretorio copiaDir = new Diretorio(mdNovo); - for (Arquivo arqFilho : origem.getArquivos()) { - cpArquivo(arqFilho, copiaDir); - } - for (Diretorio subOrig : origem.getSubDirs()) { - cpDiretorio(subOrig, copiaDir); - } - destino.addSubDiretorio(copiaDir); - } - - private MetaDados clonarMetaDados(MetaDados original, String nome, int tamanho, String dono) { - MetaDados copia = new MetaDados(nome, tamanho, dono); - Map permissoesOrig = original.getPermissoes(); - if (permissoesOrig != null) { - copia.setPermissoes(new HashMap<>(permissoesOrig)); - } - return copia; - } - - private Object buscarPorCaminho(String caminho) throws CaminhoNaoEncontradoException { - if (caminho == null || !caminho.startsWith("/")) { - throw new CaminhoNaoEncontradoException("Caminho deve ser absoluto: " + caminho); - } - String path = caminho.trim(); - if (path.equals("/")) { - return fileSys.getRaiz(); - } - - String[] partes = path.split("/"); - Diretorio atual = fileSys.getRaiz(); - - for (int i = 1; i < partes.length; i++) { - String nomeComponente = partes[i]; - boolean ultimo = (i == partes.length - 1); - - if (ultimo) { - for (Diretorio sub : atual.getSubDirs()) { - if (sub.getMetaDados().getNome().equals(nomeComponente)) { - return sub; - } - } - for (Arquivo arq : atual.getArquivos()) { - if (arq.getMetaDados().getNome().equals(nomeComponente)) { - return arq; - } - } - throw new CaminhoNaoEncontradoException("Componente não encontrado: " + nomeComponente); - } - - boolean achou = false; - for (Diretorio sub : atual.getSubDirs()) { - if (sub.getMetaDados().getNome().equals(nomeComponente)) { - atual = sub; - achou = true; - break; - } - } - if (!achou) { - throw new CaminhoNaoEncontradoException( - "Diretório não encontrado no caminho: " + nomeComponente); - } - } - - throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); } } diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java deleted file mode 100644 index d4590fd..0000000 --- a/filesys/MetaDados.java +++ /dev/null @@ -1,81 +0,0 @@ -package filesys; - -import java.util.HashMap; - -public class MetaDados { - private String nome; - private int tamanho; - private String dono; - private HashMap permissoes; - - public MetaDados(String nome, int tamanho, String dono) { - this.setNome(nome); - this.setTamanho(tamanho); - this.setDono(dono); - this.permissoes = new HashMap<>(); - } - - public MetaDados(String nome, String dono) { - this(nome, 0, dono); - } - - public String getNome() { - return nome; - } - - public void setNome(String nome) { - this.nome = nome; - } - - public int getTamanho() { - return tamanho; - } - - public void setTamanho(int tamanho) { - this.tamanho = tamanho; - } - - public String getDono() { - return dono; - } - - public void setDono(String dono) { - this.dono = dono; - } - - public HashMap getPermissoes() { - return permissoes; - } - - public void setPermissoes(HashMap permissoes) { - this.permissoes = permissoes; - } - - public void addPermissao(String usuario, String permissao) { - this.permissoes.put(usuario, permissao); - } - - public String getPermissao(String usuario) { - return this.permissoes.getOrDefault(usuario, "nenhuma"); - } - - public boolean hasPermissao(String usuario, String permissao) { - String perm = this.permissoes.get(usuario); - return perm != null && (perm.equals("leitura") || perm.equals("escrita") || perm.equals("leitura-escrita")); - } - - public boolean isDono(String usuario) { - return this.dono.equals(usuario); - } - - @Override - public String toString() { - return "MetaDados{" + - "nome='" + nome + '\'' + - ", tamanho=" + tamanho + - ", dono='" + dono + '\'' + - ", permissoes=" + permissoes + - '}'; - } - -} diff --git a/filesys/Permissao.java b/filesys/Permissao.java new file mode 100644 index 0000000..3cdc360 --- /dev/null +++ b/filesys/Permissao.java @@ -0,0 +1,31 @@ +package filesys; + +import java.util.HashMap; +import java.util.Map; + +public class Permissao { + //quem pode fazer o quê é definido aqui + private Map permissoesPorUsuario; + + public Permissao() { + this.permissoesPorUsuario = new HashMap<>(); + } + + public void definirPermissao(String usuario, String permissao) { + permissoesPorUsuario.put(usuario, permissao); // ex: "rw", "r", "-" + } + + public boolean podeLer(String usuario) { + String p = permissoesPorUsuario.getOrDefault(usuario, ""); + return p.contains("r"); + } + + public boolean podeEscrever(String usuario) { + String p = permissoesPorUsuario.getOrDefault(usuario, ""); + return p.contains("w"); + } + + public String getPermissao(String usuario) { + return permissoesPorUsuario.getOrDefault(usuario, "-"); + } +} diff --git a/filesys/Usuario.java b/filesys/Usuario.java new file mode 100644 index 0000000..c61c7af --- /dev/null +++ b/filesys/Usuario.java @@ -0,0 +1,45 @@ +package filesys; + +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; + } + + @Override + public String toString() { + return "Usuario{" + + "nome='" + nome + '\'' + + ", permissao='" + permissao + '\'' + + ", diretorio='" + diretorio + '\'' + + '}'; + } + + + +} \ No newline at end of file diff --git a/tests/FileSysImplChmodTest.java b/tests/FileSysImplChmodTest.java deleted file mode 100644 index 0d4eb14..0000000 --- a/tests/FileSysImplChmodTest.java +++ /dev/null @@ -1,165 +0,0 @@ -package tests; -import filesys.Arquivo; -import filesys.Diretorio; -import filesys.FileSystemImpl; - -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import exception.CaminhoNaoEncontradoException; -import exception.PermissaoException; - -class FileSystemImplChmodTest { - - private FileSystemImpl fsImpl; - - @BeforeEach - void setUp() { - fsImpl = new FileSystemImpl(); - // Opcional: podemos já criar uma estrutura de diretórios com subpastas e arquivos - // para os testes. Por enquanto, deixamos vazio e cada teste monta o que precisa. - } - - /** - * 1) Sucesso no chmod quando quem executa é root (criaremos /d1 e um arquivo lá): - * - mkdir("/", "d1") - * - touch("/d1/arquivo.log", "root") - * - chmod("/d1/arquivo.log", "root", "alice", "r--") - * → Verifica que em /d1/arquivo.log existe permissão "r--" para "alice" - */ - @Test - void testChmodByRootSuccess() throws Exception { - // Cria diretório e arquivo - fsImpl.mkdir("/", "d1"); - fsImpl.touch("/d1/arquivo.log", "root"); - - // Executa chmod como root, conferindo permissão de 'r--' para usuário 'alice' - fsImpl.chmod("/d1/arquivo.log", "root", "alice", "r--"); - - // Localiza o Arquivo para verificar MetaDados - Diretorio d1 = (Diretorio) fsImpl.getFileSys().getRaiz() - .getSubDirs().stream() - .filter(d -> d.getMetaDados().getNome().equals("d1")) - .findFirst().orElse(null); - assertNotNull(d1, "Diretório /d1 deveria existir."); - - Arquivo arqLog = d1.getArquivos().stream() - .filter(a -> a.getMetaDados().getNome().equals("arquivo.log")) - .findFirst().orElse(null); - assertNotNull(arqLog, "Arquivo /d1/arquivo.log deveria existir."); - - // Verifica permissão para "alice" - assertTrue(arqLog.getMetaDados().hasPermissao("alice", "r"), - "Alice deve ter permissão de leitura ('r')."); - assertFalse(arqLog.getMetaDados().hasPermissao("alice", "w"), - "Alice não deve ter permissão de escrita ('w')."); - assertFalse(arqLog.getMetaDados().hasPermissao("alice", "x"), - "Alice não deve ter permissão de execução ('x')."); - } - - /** - * 2) Sucesso no chmod quando quem executa é o dono do objeto: - * - mkdir("/", "d2") - * - touch("/d2/file.dat", "bob") - * - chmod("/d2/file.dat", "bob", "charlie", "rw-") - * → Verifica que 'charlie' tem agora permissão "rw-" - */ - @Test - void testChmodByOwnerSuccess() throws Exception { - // Cria d2 e, como usuário 'bob', cria o arquivo dentro - fsImpl.mkdir("/", "d2"); - fsImpl.touch("/d2/file.dat", "bob"); - - // Agora 'bob' altera permissão de 'charlie' para "rw-" - fsImpl.chmod("/d2/file.dat", "bob", "charlie", "rw-"); - - // Localiza file.dat - Diretorio d2 = (Diretorio) fsImpl.getFileSys().getRaiz() - .getSubDirs().stream() - .filter(d -> d.getMetaDados().getNome().equals("d2")) - .findFirst().orElse(null); - assertNotNull(d2, "Diretório /d2 deveria existir."); - - Arquivo fileDat = d2.getArquivos().stream() - .filter(a -> a.getMetaDados().getNome().equals("file.dat")) - .findFirst().orElse(null); - assertNotNull(fileDat, "Arquivo /d2/file.dat deveria existir."); - - // Verifica permissão de charlie - assertTrue(fileDat.getMetaDados().hasPermissao("charlie", "r"), - "Charlie deve ter permissão de leitura ('r')."); - assertTrue(fileDat.getMetaDados().hasPermissao("charlie", "w"), - "Charlie deve ter permissão de escrita ('w')."); - assertFalse(fileDat.getMetaDados().hasPermissao("charlie", "x"), - "Charlie não deve ter permissão de execução ('x')."); - } - - /** - * 3) Falha se o caminho não existir → CaminhoNaoEncontradoException - * - chmod("/inexistente/arquivo", "root", "any", "rwx") - */ - @Test - void testChmodPathNotFound() { - CaminhoNaoEncontradoException ex = assertThrows( - CaminhoNaoEncontradoException.class, - () -> fsImpl.chmod("/inexistente/arquivo", "root", "any", "rwx") - ); - assertTrue(ex.getMessage().contains("Componente não encontrado") || - ex.getMessage().contains("Caminho inválido"), - "Ao tentar chmod em caminho inexistente, deve lançar CaminhoNaoEncontradoException."); - } - - /** - * 4) Falha se usuário não for root e nem dono → PermissaoException - * - mkdir("/", "d3") - * - touch("/d3/foo", "alice") - * - chmod("/d3/foo", "bob", "someUser", "r-x") // 'bob' não é root nem dono - */ - @Test - void testChmodNoPermission() throws Exception { - // Cria o diretório /d3 e o arquivo como 'alice' - fsImpl.mkdir("/", "d3"); - fsImpl.touch("/d3/foo", "alice"); - - // Agora 'bob' (nem root, nem dono) tenta alterar: - PermissaoException pe = assertThrows( - PermissaoException.class, - () -> fsImpl.chmod("/d3/foo", "bob", "eve", "r-x") - ); - assertEquals( - "Usuário 'bob' não tem permissão para alterar direitos em: /d3/foo", - pe.getMessage(), - "Quando usuário não é root nem dono, deve lançar PermissaoException." - ); - } - - /** - * 5) Se a string de permissão for inválida (ex: "abx" ou tamanho != 3), deve lançar IllegalArgumentException - * - mkdir("/", "d4") - * - touch("/d4/bar", "alice") - * - chmod("/d4/bar", "alice", "mallory", "abx") - */ - @Test - void testChmodInvalidPermissionString() throws Exception { - // Cria /d4/bar - fsImpl.mkdir("/", "d4"); - fsImpl.touch("/d4/bar", "alice"); - - IllegalArgumentException iae = assertThrows( - IllegalArgumentException.class, - () -> fsImpl.chmod("/d4/bar", "alice", "mallory", "abx") - ); - assertTrue(iae.getMessage().contains("Permissão inválida"), - "Quando a string de permissão conter chars inválidos, deve lançar IllegalArgumentException."); - - // Também testa tamanho diferente de 3 - IllegalArgumentException iae2 = assertThrows( - IllegalArgumentException.class, - () -> fsImpl.chmod("/d4/bar", "alice", "mallory", "rw") - ); - assertTrue(iae2.getMessage().contains("Permissão inválida"), - "Quando a string de permissão tiver tamanho != 3, deve lançar IllegalArgumentException."); - } -} diff --git a/tests/FileSysImplMkdirTest.java b/tests/FileSysImplMkdirTest.java deleted file mode 100644 index a7425c0..0000000 --- a/tests/FileSysImplMkdirTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package tests; - -import filesys.Diretorio; -import filesys.FileSystemImpl; -import static org.junit.jupiter.api.Assertions.*; - -import java.util.HashMap; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import exception.CaminhoJaExistenteException; -import exception.PermissaoException; - -/** - * Classe de testes JUnit 5 para o método mkdir(...) de FileSystemImpl. - * - * Aborda os seguintes cenários: - * 1) Criação bem‐sucessida de um diretório. - * 2) Caminho‐pai não existe → CaminhoJaExistenteException. - * 3) Já existe subdiretório com mesmo nome → CaminhoJaExistenteException. - * 4) Permissão negada (sem 'w' no MetaDados) → PermissaoException. - */ -class FileSystemImplMkdirTest { - - private FileSystemImpl fsImpl; - - @BeforeEach - void setUp() { - // Inicializa um FileSystemImpl “vazio” antes de cada teste - fsImpl = new FileSystemImpl(); - } - - /** - * 1) Teste de criação bem‐sucessido: - * mkdir("/", "usr") deve criar o subdiretório “usr” imediatamente abaixo da raiz, sem lançar exceção. - */ - @Test - void testMkdirSuccess() throws Exception { - // Raiz inicialmente só tem “/” e nenhuma subpasta - Diretorio raiz = fsImpl.getFileSys().getRaiz(); - assertTrue(raiz.getSubDirs().isEmpty(), "Antes de criar, raiz não deveria ter subdiretórios."); - - // Cria "/usr" - fsImpl.mkdir("/", "usr"); - - // Após mkdir, deve existir um subdiretório com nome “usr” em raiz - boolean encontrou = false; - for (Diretorio d : raiz.getSubDirs()) { - if (d.getMetaDados().getNome().equals("usr")) { - encontrou = true; - break; - } - } - assertTrue(encontrou, "Espere‐se que o diretório '/usr' tenha sido criado com sucesso."); - } - - /** - * 2) Quando o caminho‐pai não existir, deve lançar CaminhoJaExistenteException com a mensagem correta. - * Ex.: mkdir("/inexistente", "dirnovo") → “Caminho não encontrado: /inexistente” - */ - @Test - void testMkdirParentNotFound() { - CaminhoJaExistenteException ex = assertThrows( - CaminhoJaExistenteException.class, - () -> fsImpl.mkdir("/inexistente", "dirnovo") - ); - assertEquals( - "Caminho não encontrado: /inexistente", - ex.getMessage(), - "Deve lançar CaminhoJaExistenteException dizendo que o caminho não foi encontrado." - ); - } - - /** - * 3) Se já existir um subdiretório com o mesmo nome, deve lançar CaminhoJaExistenteException. - * Primeiro criamos "/abc", depois tentamos criar novamente "/abc". - */ - @Test - void testMkdirAlreadyExists() throws Exception { - // Cria o diretório “/abc” - fsImpl.mkdir("/", "abc"); - - // Agora, tentar criar de novo “/abc” deve falhar - CaminhoJaExistenteException ex = assertThrows( - CaminhoJaExistenteException.class, - () -> fsImpl.mkdir("/", "abc") - ); - assertEquals( - "Já existe um diretório chamado 'abc' em: /", - ex.getMessage(), - "Quando já existe, a mensagem deve indicar que o diretório já existe." - ); - } - - /** - * 4) Permissão negada: se removermos a permissão 'w' do usuário ROOT - * em algum subdiretório, mkdir dentro dele deve lançar PermissaoException. - */ - @Test - void testMkdirPermissionDenied() throws Exception { - // 1) Criamos um subdiretório “/noPerm” - fsImpl.mkdir("/", "noPerm"); - - // 2) Localiza o diretório criado - Diretorio raiz = fsImpl.getFileSys().getRaiz(); - Diretorio dirNoPerm = null; - for (Diretorio d : raiz.getSubDirs()) { - if (d.getMetaDados().getNome().equals("noPerm")) { - dirNoPerm = d; - break; - } - } - assertNotNull(dirNoPerm, "O diretório '/noPerm' deveria existir."); - - // 3) Remove explicitamente a permissão 'w' do usuário "root" neste diretório: - // (MetaDados.getPermissoes() retorna o HashMap que guarda ) - HashMap mapaPerm = dirNoPerm.getMetaDados().getPermissoes(); - // Substitui permissão “rwx” por apenas “rx” (sem 'w') - mapaPerm.put("root", "rx"); - // Agora root só pode ler/executar, não pode escrever. - - // 4) Tenta criar subdiretório dentro de “/noPerm” → deve lançar PermissaoException - PermissaoException pe = assertThrows( - PermissaoException.class, - () -> fsImpl.mkdir("/noPerm", "subdir") - ); - assertEquals( - "Usuário 'root' não tem permissão de escrita em: /noPerm", - pe.getMessage(), - "Quando não há permissão de escrita deve lançar PermissaoException com a mensagem adequada." - ); - } -} diff --git a/tests/FileSysImplTouchTest.java b/tests/FileSysImplTouchTest.java deleted file mode 100644 index 5f3638e..0000000 --- a/tests/FileSysImplTouchTest.java +++ /dev/null @@ -1,181 +0,0 @@ -package tests; -import filesys.Arquivo; -import filesys.Diretorio; -import filesys.FileSystemImpl; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.HashMap; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import exception.CaminhoJaExistenteException; -import exception.PermissaoException; - -class FileSystemImplTouchTest { - - private FileSystemImpl fsImpl; - - @BeforeEach - void setUp() { - // Inicializar um FileSystemImpl limpo antes de cada teste - fsImpl = new FileSystemImpl(); - } - - /** - * 1) Teste de criação bem‐sucedida: - * - Cria primeiro um diretório "/docs" - * - Executa touch("/docs/arquivo.txt", "root") - * - Verifica que "arquivo.txt" exista na lista de arquivos de "/docs" - */ - @Test - void testTouchSuccess() throws Exception { - // (1) Cria o diretório "/docs" - fsImpl.mkdir("/", "docs"); - - // (2) Garante que "/docs" existe como diretório - Diretorio raiz = fsImpl.getFileSys().getRaiz(); - Diretorio dirDocs = null; - for (Diretorio d : raiz.getSubDirs()) { - if (d.getMetaDados().getNome().equals("docs")) { - dirDocs = d; - break; - } - } - assertNotNull(dirDocs, "Diretório '/docs' deveria existir após mkdir."); - - // (3) Executa touch dentro de "/docs" - fsImpl.touch("/docs/arquivo.txt", "root"); - - // (4) Verifica que "arquivo.txt" apareceu em dirDocs.getArquivos() - boolean encontrou = false; - for (Arquivo arq : dirDocs.getArquivos()) { - if (arq.getMetaDados().getNome().equals("arquivo.txt")) { - encontrou = true; - // Também podemos checar que tamanho == 0 - assertEquals(0, arq.getMetaDados().getTamanho(), - "Arquivo recém‐criado deve ter tamanho 0."); - // E que o dono é "root" - assertEquals("root", arq.getMetaDados().getDono(), - "Dono do arquivo deve ser 'root'."); - // E que há permissão "rw" para "root" - assertTrue(arq.getMetaDados().hasPermissao("root", "r"), - "Deve ter permissão de leitura (r) para 'root'."); - assertTrue(arq.getMetaDados().hasPermissao("root", "w"), - "Deve ter permissão de escrita (w) para 'root'."); - break; - } - } - assertTrue(encontrou, "Esperava‐se que '/docs/arquivo.txt' estivesse presente após touch()."); - } - - /** - * 2) Quando o diretório‐pai não existir, deve lançar CaminhoJaExistenteException - * com mensagem "Caminho não encontrado: " - */ - @Test - void testTouchParentNotFound() { - CaminhoJaExistenteException ex = assertThrows( - CaminhoJaExistenteException.class, - () -> fsImpl.touch("/naoExiste/arquivo.txt", "root") - ); - assertEquals( - "Caminho não encontrado: /naoExiste", - ex.getMessage(), - "Deve lançar CaminhoJaExistenteException informando que '/naoExiste' não existe." - ); - } - - /** - * 3) Se já existir um diretório com mesmo nome (por exemplo, tocar em "/dupdir"), - * deve lançar CaminhoJaExistenteException. - * - * Explicação: chamar touch("/dupdir", "root") equivale a: - * - pai = "/" - * - nomeArquivo = "dupdir" - * Se já houver subdiretório chamado "dupdir" em "/", então deve falhar. - */ - @Test - void testTouchAlreadyExistsAsDirectory() throws Exception { - // (1) Cria um diretório "/dupdir" - fsImpl.mkdir("/", "dupdir"); - - // (2) Tenta tocar em "/dupdir" → deveria entrar nessa checagem de conflito com diretório - CaminhoJaExistenteException ex = assertThrows( - CaminhoJaExistenteException.class, - () -> fsImpl.touch("/dupdir", "root") - ); - assertEquals( - "Já existe arquivo ou diretório chamado 'dupdir' em: /", - ex.getMessage(), - "Ao tocar em caminho que conflita com um diretório, deve lançar CaminhoJaExistenteException." - ); - } - - /** - * 4) Se já existir um arquivo com mesmo nome, deve lançar CaminhoJaExistenteException. - * - * Primeiro criamos "/docs2" e dentro dele um arquivo "a.txt", depois - * chamamos touch("/docs2/a.txt", "root") novamente. - */ - @Test - void testTouchAlreadyExistsAsFile() throws Exception { - // (1) Cria diretório "/docs2" - fsImpl.mkdir("/", "docs2"); - - // (2) Executa touch("/docs2/a.txt", "root") para criar o arquivo - fsImpl.touch("/docs2/a.txt", "root"); - - // (3) Agora tentar touch("/docs2/a.txt", "root") novamente → já existe arquivo - CaminhoJaExistenteException ex = assertThrows( - CaminhoJaExistenteException.class, - () -> fsImpl.touch("/docs2/a.txt", "root") - ); - assertEquals( - "Já existe arquivo ou diretório chamado 'a.txt' em: /docs2", - ex.getMessage(), - "Quando o arquivo já existe, deve lançar CaminhoJaExistenteException." - ); - } - - /** - * 5) Permissão negada: se removermos a permissão 'w' do usuário no diretório, touch deve falhar. - * - * - Cria "/noPermDir" - * - Remove permissão "w" de "root" dentro de "/noPermDir" - * - Chama touch("/noPermDir/novoArq", "root") → PermissaoException - */ - @Test - void testTouchPermissionDenied() throws Exception { - // (1) Cria o diretório "/noPermDir" - fsImpl.mkdir("/", "noPermDir"); - - // (2) Localiza o objeto Diretório "/noPermDir" - Diretorio raiz = fsImpl.getFileSys().getRaiz(); - Diretorio dirNoPerm = null; - for (Diretorio d : raiz.getSubDirs()) { - if (d.getMetaDados().getNome().equals("noPermDir")) { - dirNoPerm = d; - break; - } - } - assertNotNull(dirNoPerm, "O diretório '/noPermDir' deveria existir."); - - // (3) Retira permissão 'w' de "root" naquele diretório - HashMap mapaPerm = dirNoPerm.getMetaDados().getPermissoes(); - // Substitui "rwx" por "rx" (sem o 'w') - mapaPerm.put("root", "rx"); - - // (4) Agora tentar criar um arquivo dentro de "/noPermDir" deve falhar - PermissaoException pe = assertThrows( - PermissaoException.class, - () -> fsImpl.touch("/noPermDir/novoArq.txt", "root") - ); - assertEquals( - "Usuário 'root' não tem permissão de escrita em: /noPermDir", - pe.getMessage(), - "Quando não há permissão de escrita, touch deve lançar PermissaoException." - ); - } -} diff --git a/tests/PermissionTest.java b/tests/PermissionTest.java index 6026e31..5b7f809 100644 --- a/tests/PermissionTest.java +++ b/tests/PermissionTest.java @@ -2,20 +2,22 @@ import static org.junit.Assert.assertTrue; +import java.util.Collections; + import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeAll; import filesys.FileSystemImpl; import filesys.IFileSystem; +import filesys.Usuario; // Essa classe testa cenários de permissão public class PermissionTest { - private static IFileSystem fileSystem; + private static IFileSystem fileSystem; @BeforeAll public static void setUp() { - fileSystem = new FileSystemImpl(/*args...*/); + fileSystem = new FileSystemImpl(Collections.singletonList(new Usuario("root", "rwx", "/"))); } @Test From 2c82161fc798ce0e50d8d86615d9ed5cc184e419 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 17:52:15 -0300 Subject: [PATCH 18/32] feat: refazendo metodo cp --- filesys/FileSystemImpl.java | 83 +++++++++++++++++++++++++++++++++++++ tests/PermissionTest.java | 2 +- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 12aa14c..0366747 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -375,7 +375,90 @@ public void addUser(String user) { @Override public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { + ElementoFS origem = navegar(caminhoOrigem); + // Verifica permissão de leitura na origem + if (origem.isArquivo()) { + if (!usuario.equals(ROOT_USER) && !origem.donoDiretorio.equals(usuario) + && !origem.permissoesPadrao.contains("r")) { + throw new PermissaoException("Sem permissão de leitura em: " + caminhoOrigem); + } + try { + copiarArquivo((Arquivo) origem, caminhoDestino, usuario); + } catch (CaminhoJaExistenteException e) { + throw new PermissaoException("Já existe arquivo ou diretório em: " + caminhoDestino); + } + } else { + Diretorio dirOrigem = (Diretorio) origem; + if (!usuario.equals(ROOT_USER) && !dirOrigem.donoDiretorio.equals(usuario) + && !dirOrigem.permissoesPadrao.contains("r")) { + throw new PermissaoException("Sem permissão de leitura em: " + caminhoOrigem); + } + if (!recursivo) { + throw new PermissaoException("Cópia de diretório requer opção recursiva."); + } + try { + copiarDiretorioRecursivo(dirOrigem, caminhoDestino, usuario); + } catch (CaminhoJaExistenteException e) { + throw new PermissaoException("Já existe arquivo ou diretório em: " + caminhoDestino); + } + } + } + + private void copiarArquivo(Arquivo origem, String caminhoDestino, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + // Verifica se já existe algo no destino + try { + navegar(caminhoDestino); + throw new CaminhoJaExistenteException("Já existe arquivo ou diretório em: " + caminhoDestino); + } catch (CaminhoNaoEncontradoException e) { + // ok, pode criar + } + + // Descobre diretório pai e nome do novo arquivo + int idx = caminhoDestino.lastIndexOf("/"); + String paiPath = (idx == 0) ? "/" : caminhoDestino.substring(0, idx); + String nomeArquivo = caminhoDestino.substring(idx + 1); + + ElementoFS pai = navegar(paiPath); + if (!(pai instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + paiPath); + } + Diretorio dirPai = (Diretorio) pai; + + // Cria novo arquivo com mesmo conteúdo e permissões do usuário + Arquivo novoArq = new Arquivo(nomeArquivo, origem.permissoesPadrao, usuario); + for (byte[] bloco : origem.getBlocos()) { + novoArq.adicionarBloco(bloco.clone()); + } + dirPai.adicionarFilho(novoArq); + } + + private void copiarDiretorioRecursivo(Diretorio origem, String caminhoDestino, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + // Descobre diretório pai e nome do novo diretório + int idx = caminhoDestino.lastIndexOf("/"); + String paiPath = (idx == 0) ? "/" : caminhoDestino.substring(0, idx); + String nomeDir = caminhoDestino.substring(idx + 1); + + ElementoFS pai = navegar(paiPath); + if (!(pai instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + paiPath); + } + Diretorio dirPai = (Diretorio) pai; + + // Cria novo diretório + Diretorio novoDir = new Diretorio(nomeDir, origem.permissoesPadrao, usuario); + dirPai.adicionarFilho(novoDir); + + // Copia arquivos + for (ElementoFS filho : origem.getFilhos().values()) { + if (filho.isArquivo()) { + copiarArquivo((Arquivo) filho, caminhoDestino + "/" + filho.nomeDiretorio, usuario); + } else { + copiarDiretorioRecursivo((Diretorio) filho, caminhoDestino + "/" + filho.nomeDiretorio, usuario); + } + } } } diff --git a/tests/PermissionTest.java b/tests/PermissionTest.java index 5b7f809..81561bb 100644 --- a/tests/PermissionTest.java +++ b/tests/PermissionTest.java @@ -13,7 +13,7 @@ // Essa classe testa cenários de permissão public class PermissionTest { - private static IFileSystem fileSystem; + private static IFileSystem fileSystem; @BeforeAll public static void setUp() { From 7a32f8606e7a1052da05416cc88030568ef99600 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 17:54:59 -0300 Subject: [PATCH 19/32] feat: refazendo metodo mv --- filesys/FileSystemImpl.java | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 0366747..51076f9 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -304,7 +304,45 @@ public void read(String caminho, String usuario, byte[] buffer) @Override public void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException { + // 1) Localiza o elemento a ser movido + ElementoFS elemento = navegar(caminhoAntigo); + + // 2) Verifica permissão de escrita no diretório pai do antigo e do novo caminho + String antigoPaiPath = caminhoAntigo.substring(0, caminhoAntigo.lastIndexOf("/")); + if (antigoPaiPath.isEmpty()) + antigoPaiPath = "/"; + Diretorio dirPaiAntigo = (Diretorio) navegar(antigoPaiPath); + if (!dirPaiAntigo.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita no diretório de origem: " + antigoPaiPath); + } + + int idxNovo = caminhoNovo.lastIndexOf("/"); + String novoPaiPath = (idxNovo == 0) ? "/" : caminhoNovo.substring(0, idxNovo); + String novoNome = caminhoNovo.substring(idxNovo + 1); + Diretorio dirPaiNovo = (Diretorio) navegar(novoPaiPath); + if (!dirPaiNovo.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita no diretório de destino: " + novoPaiPath); + } + + // 3) Não sobrescreve arquivos/diretórios existentes + if (dirPaiNovo.getFilhos().containsKey(novoNome)) { + throw new CaminhoNaoEncontradoException( + "Já existe arquivo ou diretório com esse nome no destino: " + caminhoNovo); + } + // 4) Remove do diretório antigo + dirPaiAntigo.removerFilho(elemento.getNomeDiretorio()); + + // 5) Atualiza nome se for renomeação + elemento.setNomeDiretorio(novoNome); + + // 6) Adiciona ao novo diretório + dirPaiNovo.adicionarFilho(elemento); + + // 7) Se for diretório, atualiza referência de pai + if (!elemento.isArquivo() && elemento instanceof Diretorio) { + ((Diretorio) elemento).setDiretorioPai(dirPaiNovo); + } } /** From f4c87c09dd00116df4e5cf8b6e75a02e34d668da Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 17:57:16 -0300 Subject: [PATCH 20/32] feat: refazendo metodo ls --- filesys/FileSystemImpl.java | 50 ++++++++----------------------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 51076f9..e95fa55 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -345,63 +345,33 @@ public void mv(String caminhoAntigo, String caminhoNovo, String usuario) } } - /** - * ls: lista o conteúdo de um diretório. - * - * @param caminho caminho absoluto para um diretório (ex: "/usr/bin") - * @param usuario quem está executando o comando - * @param recursivo se true, lista recursivamente subdiretórios - * - * @throws CaminhoNaoEncontradoException se não encontrar o diretório em - * 'caminho' - * @throws PermissaoException se 'usuario' não tiver permissão de - * leitura - */ @Override public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - // 1) Busca o objeto (diretório ou arquivo) pelo caminho fornecido - Object obj = buscarPorCaminho(caminho); + // 1) Busca o objeto pelo caminho fornecido + Object obj = navegar(caminho); // 2) Se não for um diretório, lança exceção if (!(obj instanceof Diretorio)) { throw new CaminhoNaoEncontradoException("Não é um diretório: " + caminho); } Diretorio dir = (Diretorio) obj; - MetaDados meta = dir.getMetaDados(); - // 3) Verifica se o usuário tem permissão de leitura, ou se é dono, ou se é root - if (!meta.getPermissao(usuario).contains("r") && !meta.isDono(usuario) && !usuario.equals(ROOT_USER)) { + // 3) Verifica permissão de leitura + if (!dir.temPermissao(usuario, 'r')) { throw new PermissaoException("Sem permissão de leitura em: " + caminho); } - // 4) Chama o método auxiliar para listar o conteúdo do diretório + // 4) Lista o conteúdo do diretório listarConteudo(dir, caminho, recursivo, ""); } - /** - * Método auxiliar para listar o conteúdo de um diretório. - * Imprime todos os arquivos e subdiretórios do diretório atual. - * Se recursivo=true, lista também o conteúdo dos subdiretórios, com indentação. - * - * @param dir diretório a ser listado - * @param caminho caminho atual (não usado para exibição, mas pode ser útil - * para recursão) - * @param recursivo se true, lista recursivamente subdiretórios - * @param prefixo string usada para identação visual (ex: " " para subníveis) - */ + // Método auxiliar para listar conteúdo private void listarConteudo(Diretorio dir, String caminho, boolean recursivo, String prefixo) { - // Lista todos os arquivos do diretório atual - for (Arquivo arq : dir.getArquivos()) { - System.out.println(prefixo + arq.getMetaDados().getNome()); - } - // Lista todos os subdiretórios do diretório atual - for (Diretorio sub : dir.getSubDirs()) { - System.out.println(prefixo + sub.getMetaDados().getNome() + "/"); // "/" indica diretório - // Se for recursivo, chama novamente para o subdiretório, aumentando o prefixo - // (indentação) - if (recursivo) { - listarConteudo(sub, caminho + "/" + sub.getMetaDados().getNome(), true, prefixo + " "); + for (ElementoFS filho : dir.getFilhos().values()) { + System.out.println(prefixo + filho.getNomeDiretorio()); + if (recursivo && !filho.isArquivo()) { + listarConteudo((Diretorio) filho, caminho + "/" + filho.getNomeDiretorio(), true, prefixo + " "); } } } From 33fcc64383cf2d62834dd3c822058550ccfe7509 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 18:00:33 -0300 Subject: [PATCH 21/32] feat: refazendo metodo touch --- filesys/FileSystemImpl.java | 55 +++++++++---------------------------- 1 file changed, 13 insertions(+), 42 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index e95fa55..4f49c7a 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -189,18 +189,13 @@ public void touch(String caminho, String usuario) // 1) Separar o caminho em 'pai' e 'nomeDoArquivo' String path = caminho.trim(); if (!path.startsWith("/")) { - // Para simplificar, consideramos que caminhos devem ser absolutos throw new CaminhoJaExistenteException("Caminho inválido (deve começar com '/'): " + caminho); } if (path.equals("/")) { - // Não faz sentido chamar touch("/") → não se pode criar arquivo com nome vazio throw new CaminhoJaExistenteException("Não é possível criar arquivo na raiz sem nome: " + caminho); } - // Exemplo: "/usr/local/meuarquivo.txt" - // indexSlash = posição da última barra antes do nome do arquivo int indexSlash = path.lastIndexOf("/"); - // Se a última barra for a própria raiz, então pai = "/" String paiPath = (indexSlash == 0) ? "/" : path.substring(0, indexSlash); String nomeArquivo = path.substring(indexSlash + 1); if (nomeArquivo.isEmpty()) { @@ -210,58 +205,34 @@ public void touch(String caminho, String usuario) // 2) Localizar o diretório pai Diretorio dirPai; try { - Object o = buscarPorCaminho(paiPath); + Object o = navegar(paiPath); if (!(o instanceof Diretorio)) { - // Se não for diretório (por exemplo, for um arquivo), não podemos criar dentro - throw new CaminhoJaExistenteException( - "Caminho pai não é um diretório: " + paiPath); + throw new CaminhoJaExistenteException("Caminho pai não é um diretório: " + paiPath); } dirPai = (Diretorio) o; } catch (CaminhoNaoEncontradoException e) { - // Se pai não existe, relançamos como CaminhoJaExistenteException throw new CaminhoJaExistenteException("Caminho não encontrado: " + paiPath); } // 3) Verificar permissão de escrita no dirPai para o usuário informado - MetaDados mdPai = dirPai.getMetaDados(); - if (!mdPai.hasPermissao(usuario, "w")) { + if (!dirPai.temPermissao(usuario, 'w')) { throw new PermissaoException( - "Usuário '" + usuario - + "' não tem permissão de escrita em: " - + paiPath); + "Usuário '" + usuario + "' não tem permissão de escrita em: " + paiPath); } // 4) Verificar se já existe um arquivo ou diretório com esse nome em dirPai - // 4.1) Checa subdiretórios - for (Diretorio sub : dirPai.getSubDirs()) { - if (sub.getMetaDados().getNome().equals(nomeArquivo)) { - throw new CaminhoJaExistenteException( - "Já existe arquivo ou diretório chamado '" + nomeArquivo - + "' em: " + paiPath); - } - } - // 4.2) Checa arquivos - for (Arquivo arq : dirPai.getArquivos()) { - if (arq.getMetaDados().getNome().equals(nomeArquivo)) { - throw new CaminhoJaExistenteException( - "Já existe arquivo ou diretório chamado '" + nomeArquivo - + "' em: " + paiPath); - } + if (dirPai.getFilhos().containsKey(nomeArquivo)) { + throw new CaminhoJaExistenteException( + "Já existe arquivo ou diretório chamado '" + nomeArquivo + "' em: " + paiPath); } - // 5) Se chegamos aqui, podemos criar o novo arquivo vazio - // 5.1) Criar MetaDados com tamanho = 0 - MetaDados metaArq = new MetaDados(nomeArquivo, 0, usuario); - // conceder permissão “rw” para o dono - HashMap mapaPerm = new HashMap<>(); - mapaPerm.put(usuario, "rw"); - metaArq.setPermissoes(mapaPerm); - - // 5.2) Criar o objeto Arquivo com 0 blocos (array vazio) - Arquivo novoArq = new Arquivo(metaArq, new Bloco[0]); + // 5) Cria o arquivo vazio (sem blocos) + // Permissões padrão: "rw-" para o dono + Arquivo novoArq = new Arquivo(nomeArquivo, "rw-", usuario); + // Nenhum bloco é adicionado (arquivo vazio) - // 5.3) Adicionar ao diretório pai - dirPai.addArquivo(novoArq); + // 6) Adiciona ao diretório pai + dirPai.adicionarFilho(novoArq); } @Override From 38ffaee6119ec7dd1cc8e92ad58119b551167070 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 18:09:58 -0300 Subject: [PATCH 22/32] feat: refazendo chmod --- filesys/FileSystemImpl.java | 58 ++++++++++--------------------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 4f49c7a..74af601 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -103,29 +103,11 @@ public void mkdir(String caminho, String nome) dirPai.addSubDiretorio(novoDir); } - /** - * chmod: altera a permissão de 'usuarioAlvo' no arquivo ou diretório em - * 'caminho'. - * - * @param caminho caminho absoluto para um arquivo OU diretório (ex: - * "/usr/bin/arquivo.txt" ou "/usr/bin") - * @param usuario quem está executando o comando (deve ser root OU dono do - * objeto) - * @param usuarioAlvo usuário cujas permissões serão ajustadas - * @param permissao string de 3 caracteres, cada um em { 'r', 'w', 'x' } ou - * '-' - * Ex.: "rwx", "r--", "-w-", "---" - * - * @throws CaminhoNaoEncontradoException se não encontrar o arquivo/diretório em - * 'caminho' - * @throws PermissaoException se 'usuario' não for root nem dono do - * objeto - */ @Override public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException { // 1) Validar string de permissão: deve ter exatamente 3 caracteres, cada um - // seja 'r','w','x' ou '-' + // 'r','w','x' ou '-' if (permissao == null || permissao.length() != 3) { throw new IllegalArgumentException("Permissão inválida (deve ter 3 chars): " + permissao); } @@ -136,40 +118,30 @@ public void chmod(String caminho, String usuario, String usuarioAlvo, String per } // 2) Localizar o objeto (Arquivo ou Diretório) em 'caminho' - Object objAlvo = buscarPorCaminho(caminho); - // buscarPorCaminho lança CaminhoNaoEncontradoException se não existir + Object objAlvo = navegar(caminho); - MetaDados mdAlvo; + // 3) Verificar se 'usuario' tem permissão para executar chmod: + // - Se for ROOT_USER, sempre permitido. + // - Caso contrário, somente se for dono do objeto + String dono; if (objAlvo instanceof Arquivo) { - mdAlvo = ((Arquivo) objAlvo).getMetaDados(); + dono = ((Arquivo) objAlvo).getDonoDiretorio(); } else if (objAlvo instanceof Diretorio) { - mdAlvo = ((Diretorio) objAlvo).getMetaDados(); + dono = ((Diretorio) objAlvo).getDonoDiretorio(); } else { - // Nunca deveria acontecer, mas para garantir: throw new CaminhoNaoEncontradoException("Caminho encontrado não é arquivo nem diretório: " + caminho); } - - // 3) Verificar se 'usuario' tem permissão para executar chmod: - // - Se for ROOT_USER, sempre permitido. - // - Caso contrário, somente se for dono do objeto - String dono = mdAlvo.getDono(); - if (!usuario.equals(ROOT_USER) && !usuario.equals(dono)) { + if (!usuario.equals("root") && !usuario.equals(dono)) { throw new PermissaoException( - "Usuário '" + usuario + "' não tem permissão para alterar direitos em: " - + caminho); + "Usuário '" + usuario + "' não tem permissão para alterar direitos em: " + caminho); } // 4) Alterar (ou inserir) a permissão de 'usuarioAlvo' para 'permissao' - // Se o usuárioAlvo for "root", deixamos que ele fique “rwx” obrigatoriamente, - // independentemente do que se passe em 'permissao'? - // Depende do requisito, mas aqui vamos aceitar qualquer string para qualquer - // usuárioAlvo, - // pois, se o root quiser revogar até de si mesmo, é opção dele. - // - // Simplesmente atualizamos o HashMap: - HashMap mapaPerm = mdAlvo.getPermissoes(); - mapaPerm.put(usuarioAlvo, permissao); - mdAlvo.setPermissoes(mapaPerm); + if (objAlvo instanceof Arquivo) { + ((Arquivo) objAlvo).setPermissoesPadrao(permissao); + } else if (objAlvo instanceof Diretorio) { + ((Diretorio) objAlvo).setPermissaoUsuario(usuarioAlvo, permissao); + } } @Override From 56045518e9e42b4c3eba2d7900777965e7b405e0 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 18:12:14 -0300 Subject: [PATCH 23/32] feat: implementando mkdir, faltando apenas mkdir -p --- filesys/FileSystemImpl.java | 72 ++++++++++--------------------------- 1 file changed, 18 insertions(+), 54 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 74af601..b5554bb 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -1,6 +1,5 @@ package filesys; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,8 +26,6 @@ public FileSystemImpl(List u) { } } - // TODO: Validar se o método navegar cobre todos os casos de caminhos relativos - // e absolutos private ElementoFS navegar(String caminho) throws CaminhoNaoEncontradoException { if (caminho.equals("/")) return raiz; @@ -50,57 +47,24 @@ private ElementoFS navegar(String caminho) throws CaminhoNaoEncontradoException } @Override - public void mkdir(String caminho, String nome) - throws CaminhoJaExistenteException, PermissaoException { - // 1) Tenta localizar o diretório “pai” (o local onde vamos criar o subdir) - Diretorio dirPai; - try { - Object o = buscarPorCaminho(caminho); - if (!(o instanceof Diretorio)) { - // Se o objeto encontrado não for um Diretório, não faz sentido criar dentro - throw new CaminhoJaExistenteException( - "Caminho especificado não é um diretório: " + caminho); - } - dirPai = (Diretorio) o; - } catch (CaminhoNaoEncontradoException e) { - // Como a assinatura de mkdir não permite lançar CaminhoNaoEncontradoException, - // reaproveitamos CaminhoJaExistenteException para indicar que o caminho não - // existe. - throw new CaminhoJaExistenteException( - "Caminho não encontrado: " + caminho); - } - - // 2) Verifica permissão de escrita (“w”) no dirPai para o usuário ROOT_USER - MetaDados mdPai = dirPai.getMetaDados(); - // hasPermissao(u, “w”) deve retornar true somente se o mapa de permissões - // contiver “w” para aquele usuário. Se não tiver, lançamos PermissaoException. - if (!mdPai.hasPermissao(ROOT_USER, "w")) { - throw new PermissaoException( - "Usuário '" + ROOT_USER - + "' não tem permissão de escrita em: " - + caminho); - } - - // 3) Verifica se já existe um subdiretório com o mesmo nome em dirPai - for (Diretorio sub : dirPai.getSubDirs()) { - if (sub.getMetaDados().getNome().equals(nome)) { - throw new CaminhoJaExistenteException( - "Já existe um diretório chamado '" + nome - + "' em: " + caminho); - } - } - - // 4) Cria o MetaDados do novo diretório - MetaDados metaNovo = new MetaDados(nome, 0, ROOT_USER); - // Concede “rwx” apenas para o dono (“root”) e nenhuma permissão para os demais - HashMap permissoes = new HashMap<>(); - permissoes.put(ROOT_USER, "rwx"); - metaNovo.setPermissoes(permissoes); - - // 5) Cria o novo Diretorio e o adiciona na lista de filhos do dirPai - Diretorio novoDir = new Diretorio(nome, ROOT_USER); - novoDir.setMetaDados(metaNovo); - dirPai.addSubDiretorio(novoDir); + public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + if (caminho.equals("/")) + return; + String[] partes = caminho.split("/"); + Diretorio atual = raiz; + for (int i = 1; i < partes.length - 1; i++) { + ElementoFS filho = atual.getFilhos().get(partes[i]); + if (filho == null || filho.isArquivo()) + throw new PermissaoException("Diretório intermediário não existe: " + partes[i]); + atual = (Diretorio) filho; + } + String nomeNovo = partes[partes.length - 1]; + if (atual.getFilhos().containsKey(nomeNovo)) + throw new CaminhoJaExistenteException("Já existe: " + caminho); + if (!atual.temPermissao(usuario, 'w')) + throw new PermissaoException("Sem permissão para criar em: " + caminho); + atual.adicionarFilho(new Diretorio(nomeNovo, "rwx", usuario)); + // TODO: Permitir criar diretórios recursivamente (mkdir -p) se necessário } @Override From 158720b1b890644d3ee905e08750e5aad14bc55a Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Tue, 3 Jun 2025 18:29:02 -0300 Subject: [PATCH 24/32] testando: touch, write e read --- Main.java | 18 +++++++- exception/OperacaoInvalidaException.java | 7 ++++ filesys/FileSystem.java | 4 +- filesys/FileSystemImpl.java | 51 +++++++++++++++++++---- filesys/IFileSystem.java | 2 +- filesys/Offset.java | 53 ++++++++++++++++++++++++ 6 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 exception/OperacaoInvalidaException.java create mode 100644 filesys/Offset.java diff --git a/Main.java b/Main.java index 5a883eb..5ab39ae 100644 --- a/Main.java +++ b/Main.java @@ -1,4 +1,5 @@ import filesys.IFileSystem; +import filesys.Offset; import filesys.Usuario; import filesys.FileSystem; @@ -217,8 +218,21 @@ public static void read() throws CaminhoNaoEncontradoException, PermissaoExcepti System.out.println("Insira o caminho do arquivo a ser lido:"); String caminho = scanner.nextLine(); byte[] buffer = new byte[READ_BUFFER_SIZE]; - - fileSystem.read(caminho, user, buffer); + + Offset offset = new Offset(0); + int offsetAnterior; + int bytesLidos; + + do { + offsetAnterior = offset.getValue(); + fileSystem.read(caminho, user, 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 { 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/FileSystem.java b/filesys/FileSystem.java index 3c3ea61..bc92c7d 100644 --- a/filesys/FileSystem.java +++ b/filesys/FileSystem.java @@ -44,9 +44,9 @@ public void write(String caminho, String usuario, boolean anexar, byte[] buffer) } @Override - public void read(String caminho, String usuario, byte[] buffer) + public void read(String caminho, String usuario, byte[] buffer, Offset offset) throws CaminhoNaoEncontradoException, PermissaoException { - fileSystemImpl.read(caminho, usuario, buffer); + fileSystemImpl.read(caminho, usuario, buffer, offset); } @Override diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index b5554bb..67d0e7a 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -174,11 +174,38 @@ public void touch(String caminho, String 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'"); + // 1) Localiza o arquivo + Object obj = navegar(caminho); + if (!(obj instanceof Arquivo)) { + throw new CaminhoNaoEncontradoException("Arquivo não encontrado: " + caminho); + } + Arquivo arquivo = (Arquivo) obj; + + // 2) Verifica permissão de escrita + if (!usuario.equals(ROOT_USER) && !arquivo.donoDiretorio.equals(usuario) + && !arquivo.permissoesPadrao.contains("w")) { + throw new PermissaoException("Sem permissão de escrita em: " + caminho); + } + + // 3) Se não for append, sobrescreve o arquivo (limpa blocos) + if (!anexar) { + arquivo.limparBlocos(); + } + + // 4) Escreve o buffer em blocos de 4096 bytes (tamanho do bloco) + int TAMANHO_BLOCO = 4096; + int bufferOffset = 0; + while (bufferOffset < buffer.length) { + int bytesParaEscrever = Math.min(TAMANHO_BLOCO, buffer.length - bufferOffset); + byte[] bloco = new byte[bytesParaEscrever]; + System.arraycopy(buffer, bufferOffset, bloco, 0, bytesParaEscrever); + arquivo.adicionarBloco(bloco); + bufferOffset += bytesParaEscrever; + } } @Override - public void read(String caminho, String usuario, byte[] buffer) + public void read(String caminho, String usuario, byte[] buffer, Offset offset) throws CaminhoNaoEncontradoException, PermissaoException { // 1) Localiza o arquivo Object obj = navegar(caminho); @@ -188,24 +215,34 @@ public void read(String caminho, String usuario, byte[] buffer) Arquivo arquivo = (Arquivo) obj; // 2) Verifica permissão de leitura - // Aqui, supondo que permissoesPadrao seja do tipo "rw-" ou "r--" if (!usuario.equals(ROOT_USER) && !arquivo.donoDiretorio.equals(usuario) && !arquivo.permissoesPadrao.contains("r")) { throw new PermissaoException("Sem permissão de leitura em: " + caminho); } - // 3) Lê sequencialmente os blocos do arquivo para o buffer + int readOffset = (offset != null) ? offset.getValue() : 0; int bufferPos = 0; + int filePos = 0; + for (byte[] bloco : arquivo.getBlocos()) { - int bytesParaLer = Math.min(bloco.length, buffer.length - bufferPos); + if (filePos + bloco.length <= readOffset) { + // Pula blocos até chegar no offset + filePos += bloco.length; + continue; + } + int blocoOffset = Math.max(0, readOffset - filePos); + int bytesParaLer = Math.min(bloco.length - blocoOffset, buffer.length - bufferPos); if (bytesParaLer <= 0) break; - System.arraycopy(bloco, 0, buffer, bufferPos, bytesParaLer); + System.arraycopy(bloco, blocoOffset, buffer, bufferPos, bytesParaLer); bufferPos += bytesParaLer; + filePos += bloco.length; + readOffset += bytesParaLer; if (bufferPos >= buffer.length) break; } - // buffer agora contém o conteúdo lido (até buffer.length ou fim do arquivo) + if (offset != null) + offset.setValue(readOffset); } @Override diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index 2e38668..8a448ff 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -31,7 +31,7 @@ public interface IFileSystem { // 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; + void read(String caminho, String usuario, byte[] buffer, Offset offset) 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. 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; + } +} From 54c49915c196804cb2a86822079f7ff89c7073b2 Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 18:54:21 -0300 Subject: [PATCH 25/32] feat: implement mkdir with recursive directory creation - flag -p --- filesys/FileSystemImpl.java | 79 ++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 67d0e7a..5c60e3c 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -46,25 +46,76 @@ private ElementoFS navegar(String caminho) throws CaminhoNaoEncontradoException return atual; } + /** + * Cria diretórios no caminho especificado, incluindo diretórios intermediários + * caso necessário, + * semelhante ao comando `mkdir -p` do Linux. + * + * @param caminho Caminho absoluto do diretório a ser criado. + * @param usuario Usuário que está realizando a operação. + * @throws CaminhoJaExistenteException Se houver um arquivo no meio do caminho + * com o mesmo nome. + * @throws PermissaoException Se o usuário não tiver permissão de + * escrita em algum diretório do caminho. + */ @Override public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { - if (caminho.equals("/")) + if (caminho == null || caminho.isEmpty() || caminho.equals("/")) return; + String[] partes = caminho.split("/"); Diretorio atual = raiz; - for (int i = 1; i < partes.length - 1; i++) { - ElementoFS filho = atual.getFilhos().get(partes[i]); - if (filho == null || filho.isArquivo()) - throw new PermissaoException("Diretório intermediário não existe: " + partes[i]); - atual = (Diretorio) filho; - } - String nomeNovo = partes[partes.length - 1]; - if (atual.getFilhos().containsKey(nomeNovo)) - throw new CaminhoJaExistenteException("Já existe: " + caminho); - if (!atual.temPermissao(usuario, 'w')) - throw new PermissaoException("Sem permissão para criar em: " + caminho); - atual.adicionarFilho(new Diretorio(nomeNovo, "rwx", usuario)); - // TODO: Permitir criar diretórios recursivamente (mkdir -p) se necessário + + for (int i = 1; i < partes.length; i++) { + String nomeDir = partes[i]; + if (nomeDir.isEmpty()) + continue; // Ignora barras duplas // + + ElementoFS filho = atual.getFilhos().get(nomeDir); + + if (filho == null) { + // Verifica permissão de escrita antes de criar + if (!atual.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão para criar em: " + getCaminhoCompleto(atual, nomeDir)); + } + + Diretorio novoDir = new Diretorio(nomeDir, "rwx", usuario); + atual.adicionarFilho(novoDir); + atual = novoDir; + } else if (filho.isArquivo()) { + throw new CaminhoJaExistenteException( + "Já existe arquivo com esse nome: " + getCaminhoCompleto(atual, nomeDir)); + } else { + atual = (Diretorio) filho; + } + } + } + + /** + * Monta o caminho completo de um diretório, adicionando opcionalmente o nome de + * um novo elemento. + * + * @param atual Diretório de referência. + * @param nomeNovo Nome do novo diretório ou arquivo a ser incluído no final do + * caminho. + * @return String com o caminho completo. + */ + private String getCaminhoCompleto(Diretorio atual, String nomeNovo) { + StringBuilder sb = new StringBuilder(); + + while (atual != null && atual.getNomeDiretorio() != null && !atual.getNomeDiretorio().equals("/")) { + sb.insert(0, "/" + atual.getNomeDiretorio()); + atual = atual.getDiretorioPai(); + } + sb.insert(0, "/"); + + if (nomeNovo != null && !nomeNovo.isEmpty()) { + if (sb.length() > 1) + sb.append("/"); + sb.append(nomeNovo); + } + + return sb.toString().replaceAll("//+", "/"); } @Override From 56138f64a4f5cfd6ae9a59ed8b955fb95d45233a Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 18:59:25 -0300 Subject: [PATCH 26/32] feat: implement rm method with recursive directory removal and permission checks --- filesys/FileSystemImpl.java | 54 ++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 5c60e3c..c9583d6 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -162,7 +162,59 @@ public void chmod(String caminho, String usuario, String usuarioAlvo, String per @Override public void rm(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'rm'"); + if (caminho == null || caminho.equals("/") || caminho.isEmpty()) { + throw new CaminhoNaoEncontradoException("Não é permitido remover a raiz ou caminho vazio."); + } + + // Descobre diretório pai e nome do elemento a remover + int idx = caminho.lastIndexOf("/"); + String paiPath = (idx == 0) ? "/" : caminho.substring(0, idx); + String nome = caminho.substring(idx + 1); + + ElementoFS paiElem = navegar(paiPath); + if (!(paiElem instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + paiPath); + } + Diretorio dirPai = (Diretorio) paiElem; + + // Verifica permissão de escrita no diretório pai + if (!dirPai.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita em: " + paiPath); + } + + ElementoFS alvo = dirPai.getFilhos().get(nome); + if (alvo == null) { + throw new CaminhoNaoEncontradoException("Arquivo ou diretório não encontrado: " + caminho); + } + + if (!alvo.isArquivo()) { + Diretorio dirAlvo = (Diretorio) alvo; + if (!recursivo && !dirAlvo.getFilhos().isEmpty()) { + throw new PermissaoException("Diretório não está vazio. Use o modo recursivo para remover: " + caminho); + } + if (recursivo) { + removerDiretorioRecursivo(dirAlvo, usuario); + } else if (dirAlvo.getFilhos().isEmpty()) { + dirPai.removerFilho(nome); + } + } else { + dirPai.removerFilho(nome); + } + } + + // Método auxiliar para remoção recursiva de diretórios + private void removerDiretorioRecursivo(Diretorio dir, String usuario) throws PermissaoException { + for (ElementoFS filho : dir.getFilhos().values().toArray(new ElementoFS[0])) { + if (!filho.isArquivo()) { + removerDiretorioRecursivo((Diretorio) filho, usuario); + } + dir.removerFilho(filho.getNomeDiretorio()); + } + // Após remover todos os filhos, remove o próprio diretório do pai + Diretorio pai = dir.getDiretorioPai(); + if (pai != null) { + pai.removerFilho(dir.getNomeDiretorio()); + } } /** From 96d753bbf701d26d60cb0b02651e02dc342c18b0 Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 19:05:53 -0300 Subject: [PATCH 27/32] refactor: remove unsupported addUser method from FileSystemImpl --- filesys/FileSystemImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index c9583d6..ae020d8 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -423,10 +423,6 @@ private void listarConteudo(Diretorio dir, String caminho, boolean recursivo, St } } - public void addUser(String user) { - throw new UnsupportedOperationException("Método não implementado 'addUser'"); - } - @Override public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { From 1031f227608194344fb80bca159d3fac8a7503a6 Mon Sep 17 00:00:00 2001 From: VitorCostaVianna Date: Tue, 3 Jun 2025 19:10:24 -0300 Subject: [PATCH 28/32] test: add comprehensive tests for FileSystemImpl methods --- tests/FileSystemImplTest.java | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 tests/FileSystemImplTest.java diff --git a/tests/FileSystemImplTest.java b/tests/FileSystemImplTest.java new file mode 100644 index 0000000..4bb4d45 --- /dev/null +++ b/tests/FileSystemImplTest.java @@ -0,0 +1,111 @@ +package tests; + +import exception.CaminhoJaExistenteException; +import exception.CaminhoNaoEncontradoException; +import exception.PermissaoException; +import filesys.FileSystemImpl; +import filesys.Offset; +import filesys.Usuario; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.*; + +class FileSystemImplTest { + + private FileSystemImpl fs; + private Usuario root; + + @BeforeEach + void setUp() { + root = new Usuario("root", "rwx", "/"); + fs = new FileSystemImpl(Arrays.asList(root)); + } + + @Test + void testMkdirAndTouch() throws Exception { + fs.mkdir("/home/root/docs", "root"); + fs.touch("/home/root/docs/file.txt", "root"); + assertDoesNotThrow(() -> fs.mkdir("/home/root/docs", "root")); + } + + @Test + void testTouchNoWritePermission() throws Exception { + fs.mkdir("/docs", "root"); + fs.chmod("/docs", "root", "root", "---"); + assertThrows(PermissaoException.class, () -> fs.touch("/docs/file.txt", "root")); + } + + @Test + void testWriteAndReadFile() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + byte[] data = "hello world".getBytes(); + fs.write("/docs/file.txt", "root", false, data); + + byte[] buffer = new byte[20]; + Offset offset = new Offset(); + fs.read("/docs/file.txt", "root", buffer, offset); + String read = new String(buffer, 0, "hello world".length()); + assertEquals("hello world", read); + } + + @Test + void testWriteNoPermission() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.chmod("/docs/file.txt", "root", "root", "r--"); + assertThrows(PermissaoException.class, () -> fs.write("/docs/file.txt", "root", false, "fail".getBytes())); + } + + @Test + void testReadNoPermission() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.write("/docs/file.txt", "root", false, "abc".getBytes()); + fs.chmod("/docs/file.txt", "root", "root", "---"); + assertThrows(PermissaoException.class, () -> fs.read("/docs/file.txt", "root", new byte[10], new Offset())); + } + + @Test + void testRmFileAndDirectory() throws Exception { + fs.mkdir("/tmp", "root"); + fs.touch("/tmp/file.txt", "root"); + fs.rm("/tmp/file.txt", "root", false); + fs.rm("/tmp", "root", false); + assertThrows(CaminhoNaoEncontradoException.class, () -> fs.rm("/tmp", "root", false)); + } + + @Test + void testRmNonEmptyDirWithoutRecursive() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file.txt", "root"); + assertThrows(PermissaoException.class, () -> fs.rm("/dir", "root", false)); + } + + @Test + void testRmNonEmptyDirWithRecursive() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file.txt", "root"); + fs.rm("/dir", "root", true); + assertThrows(CaminhoNaoEncontradoException.class, () -> fs.rm("/dir", "root", false)); + } + + @Test + void testChmodAndPermissions() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.chmod("/docs/file.txt", "root", "root", "rw-"); + fs.write("/docs/file.txt", "root", false, "ok".getBytes()); + } + + @Test + void testLsListsContents() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file1.txt", "root"); + fs.touch("/dir/file2.txt", "root"); + assertDoesNotThrow(() -> fs.ls("/dir", "root", false)); + } +} \ No newline at end of file From 7cded696d3a68404c09088d0fa1174697c6cdfce Mon Sep 17 00:00:00 2001 From: JoaquimGCVS Date: Wed, 4 Jun 2025 17:46:14 -0300 Subject: [PATCH 29/32] test: add tests for mv and cp commands, including directory handling --- tests/FileSystemImplTest.java | 92 +++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/tests/FileSystemImplTest.java b/tests/FileSystemImplTest.java index 4bb4d45..90de632 100644 --- a/tests/FileSystemImplTest.java +++ b/tests/FileSystemImplTest.java @@ -24,6 +24,10 @@ void setUp() { fs = new FileSystemImpl(Arrays.asList(root)); } + /** + * Testa a criação de diretórios e arquivos. + * Verifica se mkdir e touch funcionam e se mkdir é idempotente. + */ @Test void testMkdirAndTouch() throws Exception { fs.mkdir("/home/root/docs", "root"); @@ -31,6 +35,9 @@ void testMkdirAndTouch() throws Exception { assertDoesNotThrow(() -> fs.mkdir("/home/root/docs", "root")); } + /** + * Testa se o touch lança exceção ao tentar criar arquivo sem permissão de escrita. + */ @Test void testTouchNoWritePermission() throws Exception { fs.mkdir("/docs", "root"); @@ -38,6 +45,10 @@ void testTouchNoWritePermission() throws Exception { assertThrows(PermissaoException.class, () -> fs.touch("/docs/file.txt", "root")); } + /** + * Testa escrita e leitura de arquivos. + * Verifica se o conteúdo lido é igual ao escrito. + */ @Test void testWriteAndReadFile() throws Exception { fs.mkdir("/docs", "root"); @@ -52,6 +63,9 @@ void testWriteAndReadFile() throws Exception { assertEquals("hello world", read); } + /** + * Testa se a escrita falha quando não há permissão de escrita. + */ @Test void testWriteNoPermission() throws Exception { fs.mkdir("/docs", "root"); @@ -60,6 +74,9 @@ void testWriteNoPermission() throws Exception { assertThrows(PermissaoException.class, () -> fs.write("/docs/file.txt", "root", false, "fail".getBytes())); } + /** + * Testa se a leitura falha quando não há permissão de leitura. + */ @Test void testReadNoPermission() throws Exception { fs.mkdir("/docs", "root"); @@ -69,6 +86,10 @@ void testReadNoPermission() throws Exception { assertThrows(PermissaoException.class, () -> fs.read("/docs/file.txt", "root", new byte[10], new Offset())); } + /** + * Testa remoção de arquivos e diretórios. + * Verifica se a remoção de um diretório inexistente lança exceção. + */ @Test void testRmFileAndDirectory() throws Exception { fs.mkdir("/tmp", "root"); @@ -78,6 +99,9 @@ void testRmFileAndDirectory() throws Exception { assertThrows(CaminhoNaoEncontradoException.class, () -> fs.rm("/tmp", "root", false)); } + /** + * Testa se a remoção de diretório não vazio sem recursão lança exceção. + */ @Test void testRmNonEmptyDirWithoutRecursive() throws Exception { fs.mkdir("/dir", "root"); @@ -85,6 +109,9 @@ void testRmNonEmptyDirWithoutRecursive() throws Exception { assertThrows(PermissaoException.class, () -> fs.rm("/dir", "root", false)); } + /** + * Testa remoção recursiva de diretório não vazio. + */ @Test void testRmNonEmptyDirWithRecursive() throws Exception { fs.mkdir("/dir", "root"); @@ -93,6 +120,9 @@ void testRmNonEmptyDirWithRecursive() throws Exception { assertThrows(CaminhoNaoEncontradoException.class, () -> fs.rm("/dir", "root", false)); } + /** + * Testa chmod e permissões de escrita. + */ @Test void testChmodAndPermissions() throws Exception { fs.mkdir("/docs", "root"); @@ -101,6 +131,9 @@ void testChmodAndPermissions() throws Exception { fs.write("/docs/file.txt", "root", false, "ok".getBytes()); } + /** + * Testa se ls lista o conteúdo do diretório sem lançar exceção. + */ @Test void testLsListsContents() throws Exception { fs.mkdir("/dir", "root"); @@ -108,4 +141,63 @@ void testLsListsContents() throws Exception { fs.touch("/dir/file2.txt", "root"); assertDoesNotThrow(() -> fs.ls("/dir", "root", false)); } + + /** + * Testa o comando mv para mover e renomear arquivos. + */ + @Test + void testMvFile() throws Exception { + // Cria diretório e arquivo + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + // Move arquivo para novo nome + fs.mv("/docs/file.txt", "/docs/file2.txt", "root"); + // Verifica se o arquivo antigo não existe mais e o novo existe + assertThrows(CaminhoNaoEncontradoException.class, () -> fs.ls("/docs/file.txt", "root", false)); + assertDoesNotThrow(() -> fs.ls("/docs/file2.txt", "root", false)); + } + + /** + * Testa o comando cp para copiar arquivos. + */ + @Test + void testCpFile() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.write("/docs/file.txt", "root", false, "abc".getBytes()); + fs.cp("/docs/file.txt", "/docs/file2.txt", "root", false); + + byte[] buffer = new byte[10]; + Offset offset = new Offset(); + fs.read("/docs/file2.txt", "root", buffer, offset); + String read = new String(buffer, 0, 3); + assertEquals("abc", read); + } + + /** + * Testa o comando cp para copiar diretórios recursivamente. + */ + @Test + void testCpDirectoryRecursive() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file.txt", "root"); + fs.write("/dir/file.txt", "root", false, "xyz".getBytes()); + fs.cp("/dir", "/dir2", "root", true); + + byte[] buffer = new byte[10]; + Offset offset = new Offset(); + fs.read("/dir2/file.txt", "root", buffer, offset); + String read = new String(buffer, 0, 3); + assertEquals("xyz", read); + } + + /** + * Testa se a cópia de diretório sem recursão lança exceção. + */ + @Test + void testCpDirectoryWithoutRecursiveThrows() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file.txt", "root"); + assertThrows(PermissaoException.class, () -> fs.cp("/dir", "/dir2", "root", false)); + } } \ No newline at end of file From 1bb0f4d05f2aa23f7829a4bc85f33700662d267d Mon Sep 17 00:00:00 2001 From: JoaquimGCVS Date: Thu, 19 Jun 2025 17:14:07 -0300 Subject: [PATCH 30/32] test: corrigindo erro nos testes de permissao com o usuario root --- filesys/FileSystemImpl.java | 6 +++--- tests/FileSystemImplTest.java | 31 +++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index ae020d8..ec51f24 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -317,9 +317,9 @@ public void read(String caminho, String usuario, byte[] buffer, Offset offset) } Arquivo arquivo = (Arquivo) obj; - // 2) Verifica permissão de leitura - if (!usuario.equals(ROOT_USER) && !arquivo.donoDiretorio.equals(usuario) - && !arquivo.permissoesPadrao.contains("r")) { + System.out.println("DEBUG: Permissão atual do arquivo: " + arquivo.permissoesPadrao); + + if (!usuario.equals(ROOT_USER) && !arquivo.permissoesPadrao.contains("r")) { throw new PermissaoException("Sem permissão de leitura em: " + caminho); } diff --git a/tests/FileSystemImplTest.java b/tests/FileSystemImplTest.java index 90de632..93117c8 100644 --- a/tests/FileSystemImplTest.java +++ b/tests/FileSystemImplTest.java @@ -42,7 +42,8 @@ void testMkdirAndTouch() throws Exception { void testTouchNoWritePermission() throws Exception { fs.mkdir("/docs", "root"); fs.chmod("/docs", "root", "root", "---"); - assertThrows(PermissaoException.class, () -> fs.touch("/docs/file.txt", "root")); + // Tenta criar arquivo como maria (não-root) + assertThrows(PermissaoException.class, () -> fs.touch("/docs/file.txt", "maria")); } /** @@ -70,21 +71,31 @@ void testWriteAndReadFile() throws Exception { void testWriteNoPermission() throws Exception { fs.mkdir("/docs", "root"); fs.touch("/docs/file.txt", "root"); - fs.chmod("/docs/file.txt", "root", "root", "r--"); - assertThrows(PermissaoException.class, () -> fs.write("/docs/file.txt", "root", false, "fail".getBytes())); + fs.chmod("/docs/file.txt", "root", "root", "r--"); // remove permissão de escrita + + // Tenta escrever como maria (não-root, não-dono) + assertThrows(PermissaoException.class, () -> { + fs.write("/docs/file.txt", "maria", false, "fail".getBytes()); + }); } /** * Testa se a leitura falha quando não há permissão de leitura. */ @Test - void testReadNoPermission() throws Exception { - fs.mkdir("/docs", "root"); - fs.touch("/docs/file.txt", "root"); - fs.write("/docs/file.txt", "root", false, "abc".getBytes()); - fs.chmod("/docs/file.txt", "root", "root", "---"); - assertThrows(PermissaoException.class, () -> fs.read("/docs/file.txt", "root", new byte[10], new Offset())); - } + void testReadNoPermission() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.write("/docs/file.txt", "root", false, "abc".getBytes()); + + // Remove todas as permissões do arquivo + fs.chmod("/docs/file.txt", "root", "root", "---"); + + // Tenta ler como um usuário não-root (deve lançar PermissaoException) + assertThrows(PermissaoException.class, () -> { + fs.read("/docs/file.txt", "maria", new byte[10], new Offset()); + }); +} /** * Testa remoção de arquivos e diretórios. From 647c118607c67d0710c9c5ac9690adc739d97110 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Fri, 20 Jun 2025 12:08:56 -0300 Subject: [PATCH 31/32] =?UTF-8?q?feat:=20faltando=20corrigir=20permiss?= =?UTF-8?q?=C3=B5es=20para=20outros=20usuario,=20testes=20funcionando?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/FileSystemImpl.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index ec51f24..906c116 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -317,8 +317,6 @@ public void read(String caminho, String usuario, byte[] buffer, Offset offset) } Arquivo arquivo = (Arquivo) obj; - System.out.println("DEBUG: Permissão atual do arquivo: " + arquivo.permissoesPadrao); - if (!usuario.equals(ROOT_USER) && !arquivo.permissoesPadrao.contains("r")) { throw new PermissaoException("Sem permissão de leitura em: " + caminho); } @@ -398,6 +396,13 @@ public void ls(String caminho, String usuario, boolean recursivo) // 1) Busca o objeto pelo caminho fornecido Object obj = navegar(caminho); + // **Novo**: se for arquivo e não for recursivo, imprime o próprio nome e + // retorna + if (obj instanceof Arquivo && !recursivo) { + System.out.println(((Arquivo) obj).getNomeDiretorio()); + return; + } + // 2) Se não for um diretório, lança exceção if (!(obj instanceof Diretorio)) { throw new CaminhoNaoEncontradoException("Não é um diretório: " + caminho); @@ -418,7 +423,10 @@ private void listarConteudo(Diretorio dir, String caminho, boolean recursivo, St for (ElementoFS filho : dir.getFilhos().values()) { System.out.println(prefixo + filho.getNomeDiretorio()); if (recursivo && !filho.isArquivo()) { - listarConteudo((Diretorio) filho, caminho + "/" + filho.getNomeDiretorio(), true, prefixo + " "); + listarConteudo((Diretorio) filho, + caminho + "/" + filho.getNomeDiretorio(), + true, + prefixo + " "); } } } From a4f2e59e155e54bc7da44764d38c7f33959d8c00 Mon Sep 17 00:00:00 2001 From: Arlindo Junior Date: Fri, 27 Jun 2025 11:45:24 -0300 Subject: [PATCH 32/32] implementando para novo arquivo --- Main.java | 43 ++++++++------------ filesys/FileSystemImpl.java | 81 ++++++++++++++++++++++++++++++------- filesys/Usuario.java | 6 +++ users/userMkdir | 13 ++++++ 4 files changed, 102 insertions(+), 41 deletions(-) create mode 100644 users/userMkdir diff --git a/Main.java b/Main.java index 5ab39ae..4f51918 100644 --- a/Main.java +++ b/Main.java @@ -39,10 +39,6 @@ public class Main { // Logo, não é necessário salvar os arquivos em disco. O sistema será uma // simulação em memória. public static void main(String[] args) { - // Usuário que está executando o programa. - // 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"); return; @@ -50,19 +46,9 @@ public static void main(String[] args) { user = args[1]; // Carrega a lista de usuários do sistema a partir de arquivo - // Formato do arquivo users: - // username dir permission - // Exemplo: - // maria /** rw- - // luzia /** rwx - // Essa permissão vale para o diretório raiz e sub diretórios. - // A partir do momento que um usuário cria outro diretório ou arquivo, - // a permissão desse usuário é de leitura, escrita e execução nesse novo - // diretório/arquivo, - // e sempre será rwx para o usuário root. List usuarios = new ArrayList<>(); try { - Scanner userScanner = new Scanner(new java.io.File("users/users")); + Scanner userScanner = new Scanner(new java.io.File("users/userMkdir")); while (userScanner.hasNextLine()) { String line = userScanner.nextLine().trim(); if (!line.isEmpty()) { @@ -71,34 +57,37 @@ public static void main(String[] args) { String userListed = parts[0]; String dir = parts[1]; String dirPermission = parts[2]; - usuarios.add(new Usuario(userListed, dirPermission, dir)); - } else { System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); } } } userScanner.close(); - } catch (FileNotFoundException e) { // Retorna se o arquivo de usuários não for encontrado + } catch (FileNotFoundException e) { 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? + // Inicializa o sistema de arquivos com a lista de usuários fileSystem = new FileSystem(usuarios); - // ! JA ESTA IMPLEMENTADO NO FILESYSTEM - // // 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" + // Cria os diretórios e aplica as permissões conforme o arquivo + for (Usuario usuario : usuarios) { + try { + fileSystem.mkdir(usuario.getDiretorio(), usuario.getNome()); + // Aplica a permissão do arquivo para o usuário no diretório criado + fileSystem.chmod(usuario.getDiretorio(), "root", usuario.getNome(), usuario.getPermissao()); + } catch (CaminhoJaExistenteException | PermissaoException | CaminhoNaoEncontradoException e) { + // Se já existe, não tem permissão ou não encontra, apenas ignore + } + } + + // Cria o diretório raiz do sistema (caso não exista) try { fileSystem.mkdir(ROOT_DIR, ROOT_USER); } catch (CaminhoJaExistenteException | PermissaoException e) { - System.out.println(e.getMessage()); + // Pode ignorar se já existe } // Menu interativo. diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 906c116..1a90b19 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -18,8 +18,10 @@ public final class FileSystemImpl implements IFileSystem { private Map usuarios = new HashMap<>(); public FileSystemImpl(List u) { - this.raiz = new Diretorio("/", "rwx", ROOT_USER); - usuarios.put(ROOT_USER, new Usuario(ROOT_USER, "rwx", "/")); + this.raiz = new Diretorio("/", "rwx", ROOT_USER); // adicionando um diretorio raiz, usuário ROOT tendo todas as + // permissões + usuarios.put(ROOT_USER, new Usuario(ROOT_USER, "rwx", "/")); // criando um usuário com seu diretorio e colocando + // dentro de usuarios for (Usuario usuario : u) { if (!usuario.getNome().equalsIgnoreCase("root")) usuarios.put(usuario.getNome(), usuario); @@ -27,22 +29,44 @@ public FileSystemImpl(List u) { } private ElementoFS navegar(String caminho) throws CaminhoNaoEncontradoException { + // Se o caminho for apenas "/", retorna o diretório raiz if (caminho.equals("/")) return raiz; + + // Divide o caminho pelos separadores "/" + // Ex: "/home/user/docs" -> ["", "home", "user", "docs"] String[] partes = caminho.split("/"); + + // Começa a navegação a partir do diretório raiz Diretorio atual = raiz; + + // Itera pelas partes do caminho (ignorando a primeira que é vazia) for (int i = 1; i < partes.length; i++) { + // Tenta obter o filho (arquivo ou diretório) com o nome da parte atual ElementoFS filho = atual.getFilhos().get(partes[i]); + + // Se não existir esse filho, lança exceção indicando que o caminho não foi + // encontrado if (filho == null) throw new CaminhoNaoEncontradoException("Caminho não encontrado: " + caminho); + + // Se esta é a última parte do caminho, retorna o elemento encontrado (pode ser + // arquivo ou diretório) if (i == partes.length - 1) return filho; + + // Se ainda há partes para percorrer e o elemento atual não for um arquivo (ou + // seja, é um diretório), continua a navegação if (!filho.isArquivo()) { atual = (Diretorio) filho; } else { + // Se encontrou um arquivo no meio do caminho, lança exceção (pois não pode + // navegar dentro de um arquivo) throw new CaminhoNaoEncontradoException("Caminho não encontrado (esperado diretório): " + caminho); } } + + // Retorna o último diretório acessado (caso o caminho terminasse em diretório) return atual; } @@ -60,9 +84,12 @@ private ElementoFS navegar(String caminho) throws CaminhoNaoEncontradoException */ @Override public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + // ignora casos inválidos if (caminho == null || caminho.isEmpty() || caminho.equals("/")) return; + // divide o caminho nas partes + // começa da raiz o sistema de arquivos String[] partes = caminho.split("/"); Diretorio atual = raiz; @@ -71,6 +98,7 @@ public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteExcep if (nomeDir.isEmpty()) continue; // Ignora barras duplas // + // busca se o filho ja possui diretorios com esse nome ElementoFS filho = atual.getFilhos().get(nomeDir); if (filho == null) { @@ -79,6 +107,7 @@ public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteExcep throw new PermissaoException("Sem permissão para criar em: " + getCaminhoCompleto(atual, nomeDir)); } + // criando o diretorio e adicionando Diretorio novoDir = new Diretorio(nomeDir, "rwx", usuario); atual.adicionarFilho(novoDir); atual = novoDir; @@ -296,8 +325,12 @@ public void write(String caminho, String usuario, boolean anexar, byte[] buffer) } // 4) Escreve o buffer em blocos de 4096 bytes (tamanho do bloco) - int TAMANHO_BLOCO = 4096; - int bufferOffset = 0; + // buffer -> conteudo completo a ser salvo + int TAMANHO_BLOCO = 4096; // define o tamanho de cada bloco + // evita escrever tudo de uma vez no buffer + // se diminuir o tamanho do bloco, o desperdício cai, porém a memória para + // manter metadados cresce + int bufferOffset = 0; // se o bufferOffset não tem controler de pro onde continuar a leitura do buffer while (bufferOffset < buffer.length) { int bytesParaEscrever = Math.min(TAMANHO_BLOCO, buffer.length - bufferOffset); byte[] bloco = new byte[bytesParaEscrever]; @@ -321,27 +354,47 @@ public void read(String caminho, String usuario, byte[] buffer, Offset offset) throw new PermissaoException("Sem permissão de leitura em: " + caminho); } - int readOffset = (offset != null) ? offset.getValue() : 0; - int bufferPos = 0; - int filePos = 0; + int readOffset = (offset != null) ? offset.getValue() : 0; // onde dentro do arquivo vc deve começar a ler + int bufferPos = 0; // indica em que posição do buffer de destino você ja escreveu dados lidos + int filePos = 0; // marca qual posição de leitura do arquivo vc ja consumiu for (byte[] bloco : arquivo.getBlocos()) { + // 1) Se todo o bloco ainda está antes do offset, pule-o por inteiro: if (filePos + bloco.length <= readOffset) { - // Pula blocos até chegar no offset - filePos += bloco.length; - continue; + filePos += bloco.length; // avança a posição no arquivo + continue; // vai para o próximo bloco } + + // 2) Determina onde, dentro do bloco atual, começar a copiar: + // - Se readOffset > filePos, significa que já consumimos parte desse bloco int blocoOffset = Math.max(0, readOffset - filePos); - int bytesParaLer = Math.min(bloco.length - blocoOffset, buffer.length - bufferPos); + + // 3) Quantos bytes desse bloco cabem no buffer restante? + int bytesParaLer = Math.min( + bloco.length - blocoOffset, // do bloco a partir de blocoOffset + buffer.length - bufferPos // até encher o buffer destino + ); + + // Se não há mais nada a ler (buffer cheio ou bloco já totalmente antes do + // offset), encerra: if (bytesParaLer <= 0) break; + + // 4) Copia bytesParaLer do bloco para o buffer: + // de bloco[blocoOffset … blocoOffset+bytesParaLer-1] + // para buffer[bufferPos … bufferPos+bytesParaLer-1] System.arraycopy(bloco, blocoOffset, buffer, bufferPos, bytesParaLer); - bufferPos += bytesParaLer; - filePos += bloco.length; - readOffset += bytesParaLer; + + // 5) Atualiza índices: + bufferPos += bytesParaLer; // avançou no buffer destino + filePos += bloco.length; // bloco todo foi “lido” em termos de filePos + readOffset += bytesParaLer; // avança a posição real de leitura + + // 6) Se o buffer ficou cheio, pare de ler: if (bufferPos >= buffer.length) break; } + if (offset != null) offset.setValue(readOffset); } diff --git a/filesys/Usuario.java b/filesys/Usuario.java index c61c7af..14bffac 100644 --- a/filesys/Usuario.java +++ b/filesys/Usuario.java @@ -31,6 +31,8 @@ public void setDiretorio(String diretorio) { this.diretorio = diretorio; } + + @Override public String toString() { return "Usuario{" + @@ -40,6 +42,10 @@ public String toString() { '}'; } + public String getDiretorio() { + return diretorio; + } + } \ No newline at end of file diff --git a/users/userMkdir b/users/userMkdir new file mode 100644 index 0000000..8de4353 --- /dev/null +++ b/users/userMkdir @@ -0,0 +1,13 @@ +joao / rwx +joao /users rwx +joao /users/joao rwx +joao /users/joao/documentos rwx +joao /users/joao/documentos/viagem rwx +joao /users/joao/documentos/viagem/2024 rwx +joao /users/joao/documentos/viagem/2024/dezembro rwx +joao /users/joao/documentos/viagem/2024/dezembro/fotossíntese rwx +joao /users/joao/documentos/viagem/2024/dezembro/fotos rwx +joao /users/joao/documentos/viagem/2024/janeiro rwx +joao /users/joao/documentos/viagem/2023 rwx +joao /users/joao/documentos/viagem/2023/janeiro rwx +joao /users/joao/documentos/viagem/2023/janeiro/fotos rwx \ No newline at end of file