From 1bdc0b610b4fe9bbfa70ca8aa986b5cdbfbdac12 Mon Sep 17 00:00:00 2001 From: Mateus Araujo Date: Tue, 3 Jun 2025 09:54:17 -0300 Subject: [PATCH 01/30] =?UTF-8?q?adicionando=20o=20comando=20da=20quest?= =?UTF-8?q?=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...3\241tico_SO___Sistema_de_Arquivos (2).pdf" | Bin 0 -> 180664 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "Trabalho_Pr\303\241tico_SO___Sistema_de_Arquivos (2).pdf" diff --git "a/Trabalho_Pr\303\241tico_SO___Sistema_de_Arquivos (2).pdf" "b/Trabalho_Pr\303\241tico_SO___Sistema_de_Arquivos (2).pdf" new file mode 100644 index 0000000000000000000000000000000000000000..49a0581d4aca0a8742ad7d93d5111726f554c17e GIT binary patch literal 180664 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
PQS;@RAb7p!)WPw}x^og6oZM=a2132m& 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 literal 0 HcmV?d00001 From 1336f24d7a82c7e8b158e64ddff9ac8175e33f59 Mon Sep 17 00:00:00 2001 From: Mateus Date: Fri, 6 Jun 2025 14:55:39 -0300 Subject: [PATCH 02/30] criando classes bases --- filesys/Arquivo.java | 5 +++++ filesys/Bloco.java | 5 +++++ filesys/Diretorio.java | 5 +++++ filesys/MetaDados.java | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 filesys/Arquivo.java create mode 100644 filesys/Bloco.java create mode 100644 filesys/Diretorio.java create mode 100644 filesys/MetaDados.java diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java new file mode 100644 index 0000000..0fe1ec9 --- /dev/null +++ b/filesys/Arquivo.java @@ -0,0 +1,5 @@ +package filesys; + +public class Arquivo { + +} diff --git a/filesys/Bloco.java b/filesys/Bloco.java new file mode 100644 index 0000000..8992628 --- /dev/null +++ b/filesys/Bloco.java @@ -0,0 +1,5 @@ +package filesys; + +public class Bloco { + +} diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java new file mode 100644 index 0000000..8d7a8f6 --- /dev/null +++ b/filesys/Diretorio.java @@ -0,0 +1,5 @@ +package filesys; + +public class Diretorio { + +} diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java new file mode 100644 index 0000000..6d01243 --- /dev/null +++ b/filesys/MetaDados.java @@ -0,0 +1,5 @@ +package filesys; + +public class MetaDados { + +} From 7c6ff5d9f2b019fec5491453350fa433717fdfff Mon Sep 17 00:00:00 2001 From: Mateus Date: Sat, 7 Jun 2025 15:57:03 -0300 Subject: [PATCH 03/30] criando classe bloco --- filesys/Bloco.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/filesys/Bloco.java b/filesys/Bloco.java index 8992628..28e9eac 100644 --- a/filesys/Bloco.java +++ b/filesys/Bloco.java @@ -1,5 +1,17 @@ package filesys; public class Bloco { - + byte[] bytes; + + public Bloco(){ + bytes = new byte[4096]; + } + + public byte[] getDados(){ + return bytes; + } + + public void setBytes(byte[] bytes){ + this.bytes = bytes; + } } From d8d450c26aa2962884b402bbcb176785b2f30fdd Mon Sep 17 00:00:00 2001 From: Mateus Date: Sat, 7 Jun 2025 16:20:29 -0300 Subject: [PATCH 04/30] criando usuario --- filesys/Usuario.java | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 filesys/Usuario.java diff --git a/filesys/Usuario.java b/filesys/Usuario.java new file mode 100644 index 0000000..648b5a2 --- /dev/null +++ b/filesys/Usuario.java @@ -0,0 +1,40 @@ +package filesys; + +public class Usuario { + private String nome; + private String diretorio; + private String permissao; + + public String getNome() { + return nome; + } + public void setNome(String nome) { + this.nome = nome; + } + public String getDiretorio() { + return diretorio; + } + public void setDiretorio(String diretorio) { + this.diretorio = diretorio; + } + public String getPermissao() { + return permissao; + } + public void setPermissao(String permissao) { + this.permissao = permissao; + } + + public Usuario(String nome, String diretorio, String permissao) { + setDiretorio(diretorio); + setNome(nome); + setPermissao(permissao); + } + + @Override + public String toString(){ + return("Nome: "+ nome +"\n" + + "permissão: " + permissao + "\n" + + "diretorio: " + diretorio + "\n"); + } + +} From bf074f2727ebfa563d1ac32f89a4d03ec5983554 Mon Sep 17 00:00:00 2001 From: Mateus Date: Sat, 7 Jun 2025 17:32:15 -0300 Subject: [PATCH 05/30] =?UTF-8?q?criando=20classe=20diretorio=20e=20os=20r?= =?UTF-8?q?espectivos=20m=C3=A9todos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/Diretorio.java | 122 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index 8d7a8f6..c28c09b 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -1,5 +1,127 @@ package filesys; +import java.util.Map; + +import exception.PermissaoException; + public class Diretorio { + protected String nome; + protected String dono; + protected String permissoesA; //permissões rank A + + protected Diretorio pai; + + protected Map filhos; + protected Map permissoesS; //permissões rank S + + /* + * verifica se o usuário é root, caso sim, retorna todas as permissões em formato de string (rwx) + * verifica se o usuário é dono, caso sim, retorna as respectivas permissoes + * se nenhuma das condições for satisfeita, retorna permissoes de valor default: "nnn" (todas null) + */ + public String getPermissoesDeUsuario(String usuario){ + if(usuario.equals("root")) + return "rwx"; + else if(usuario.equals(dono)) + return permissoesA; + return permissoesS.getOrDefault(usuario, "nnn"); + } + + /* + * seta permissões para o usuário utilizando 3 tipos de caracteres: "w", "r" e "x" + * caso as permissões forem diferentes de 3 lança exceção + * para sempre ter 3 letras, caso não houver o usuário não tiver uma permissão, é utilizado o n (null) + */ + public void setPermissoesUsuario(String usuario, String permissoes){ + if(permissoes == null || permissoes.length() != 3) + throw new PermissaoException("Ocorreu um erro quanto as exceções"); + + permissoesS.put(usuario, permissoes); + } + /* + * verifica se o usuário é root ou dono, caso sim retorna true porque os dois tem as permissões + * se não, ele continua e verifica as permissões do usuário no diretório + * o código continua se nenhuma das condições tiver sido satisfeita e procura as permissões herdadas do pai + * retorna falso se o usuário não tem a permissão + */ + public boolean temPermissao(String usuario, char permissaoRequerida){ + + if(usuario.equals("root") || usuario.equals(dono)) + return true; + + String permissoesDoUsuario = permissoesS.get(usuario); + + if(permissoesDoUsuario != null && permissoesDoUsuario.indexOf(permissaoRequerida) != -1) + return true; + + Diretorio pai = getPai(); + if(pai != null){ + return pai.temPermissao(usuario, permissaoRequerida); + } + return false; + } + + public void addFilho(Diretorio filho){ + filho.setPai(this); + filhos.put(filho.getNome(), filho); + } + + public Diretorio removerFilho(String nome){ + return filhos.remove(nome); + } + + public String getNome() { + return nome; + } + public void setNome(String nome) { + this.nome = nome; + } + public String getDono() { + return dono; + } + public void setDono(String dono) { + this.dono = dono; + } + public String getPermissoesA() { + return permissoesA; + } + public void setPermissoesA(String permissoesA) { + this.permissoesA = permissoesA; + } + public Diretorio getPai() { + return pai; + } + public void setPai(Diretorio pai) { + this.pai = pai; + } + public Map getFilhos() { + return filhos; + } + public void setFilhos(Map filhos) { + this.filhos = filhos; + } + public Map getPermissoesS() { + return permissoesS; + } + public void setPermissoesS(Map permissoesS) { + this.permissoesS = permissoesS; + } + + public boolean isFile(){ + return false; + } + + + public Diretorio(String nome, String dono, String permissoesA, Diretorio pai, Map filhos, + Map permissoesS) { + setNome(nome); + setDono(dono); + setPermissoesA(permissoesA); + setPai(pai); + setFilhos(filhos); + setPermissoesS(permissoesS); + } + + } From f87762079099b9ef079948ea0767912f01e66078 Mon Sep 17 00:00:00 2001 From: Mateus Date: Sat, 7 Jun 2025 17:32:24 -0300 Subject: [PATCH 06/30] =?UTF-8?q?adicionando=20exce=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exception/BlocoVazioException.java | 7 +++++++ exception/PermissaoException.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 exception/BlocoVazioException.java diff --git a/exception/BlocoVazioException.java b/exception/BlocoVazioException.java new file mode 100644 index 0000000..83987ec --- /dev/null +++ b/exception/BlocoVazioException.java @@ -0,0 +1,7 @@ +package exception; + +public class BlocoVazioException extends RuntimeException { + public BlocoVazioException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/exception/PermissaoException.java b/exception/PermissaoException.java index f8800c1..2d78a60 100644 --- a/exception/PermissaoException.java +++ b/exception/PermissaoException.java @@ -1,6 +1,6 @@ package exception; -public class PermissaoException extends Exception { +public class PermissaoException extends RuntimeException { public PermissaoException(String message) { super(message); } From 76cd192b25ebec97d0fa87eda5b16883add81e4e Mon Sep 17 00:00:00 2001 From: Mateus Date: Sat, 7 Jun 2025 17:38:26 -0300 Subject: [PATCH 07/30] mudando metadados para deslocamento --- filesys/Deslocamento.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 filesys/Deslocamento.java diff --git a/filesys/Deslocamento.java b/filesys/Deslocamento.java new file mode 100644 index 0000000..b2962ec --- /dev/null +++ b/filesys/Deslocamento.java @@ -0,0 +1,5 @@ +package filesys; + +public class Deslocamento { + +} From 99dcafec661a6fa37dcd472b3e1589767f50e5b2 Mon Sep 17 00:00:00 2001 From: Mateus Date: Sat, 7 Jun 2025 17:38:39 -0300 Subject: [PATCH 08/30] deleting metadados --- filesys/MetaDados.java | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 filesys/MetaDados.java diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java deleted file mode 100644 index 6d01243..0000000 --- a/filesys/MetaDados.java +++ /dev/null @@ -1,5 +0,0 @@ -package filesys; - -public class MetaDados { - -} From 0f8fe821b9f3fa766d23b97af4b7673c30c64ea2 Mon Sep 17 00:00:00 2001 From: Mateus Date: Sat, 7 Jun 2025 17:49:32 -0300 Subject: [PATCH 09/30] criando deslocamento --- filesys/Deslocamento.java | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/filesys/Deslocamento.java b/filesys/Deslocamento.java index b2962ec..5c148ba 100644 --- a/filesys/Deslocamento.java +++ b/filesys/Deslocamento.java @@ -1,5 +1,42 @@ package filesys; public class Deslocamento { + private int valorMax; + private int valor; + + public Deslocamento(int valor) { + setValor(valor); + setValorMax(Integer.MAX_VALUE); + } + + + public int getValorMax() { + return valorMax; + } + + + public void setValorMax(int valorMax) { + if(valorMax < 0) + throw new IllegalArgumentException("Valor não pode ser menor que 0"); + else if(this.valor > valorMax) + throw new IllegalArgumentException("Valor padrão não pode ser maior que o limite"); + + this.valorMax = valorMax; + } + + + public int getValor() { + return valor; + } + + + public void setValor(int valor) { + if(valorMax < 0) + throw new IllegalArgumentException("Valor não pode ser menor que 0"); + else if(this.valor > valorMax) + throw new IllegalArgumentException("Valor padrão não pode ser maior que o limite: " + valorMax); + } + + } From 1782f2c7a9306a5f0108f6102b5a2b6a2d34a1ec Mon Sep 17 00:00:00 2001 From: Mateus Date: Sat, 7 Jun 2025 17:50:37 -0300 Subject: [PATCH 10/30] =?UTF-8?q?criando=20o=20rascunho=20do=20arquivo,=20?= =?UTF-8?q?ainda=20falta=20implementa=C3=A7=C3=A3o=20de=20diretorio=20e=20?= =?UTF-8?q?outros=20m=C3=A9todos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/Arquivo.java | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java index 0fe1ec9..7153f54 100644 --- a/filesys/Arquivo.java +++ b/filesys/Arquivo.java @@ -1,5 +1,42 @@ package filesys; +import exception.BlocoVazioException; +import java.util.List; + +public class Arquivo extends Diretorio { + List blocos; + private long tamanho; + + public void adicionarBloco(Bloco bloco){ + blocos.add(bloco); + tamanho++; + } + + public Bloco removerBlocoPos(int pos){ + if(blocos.isEmpty()){ + throw new BlocoVazioException("Não é possível remover pois o bloco já está vazio"); + } + tamanho--; + return blocos.remove(pos); + } + + public void limparArquivo(){ + if(blocos.isEmpty()) + throw new BlocoVazioException("Não é possível limpar pois o bloco já está vazio"); + blocos.clear(); + tamanho = 0; + } + + public List getAllBlocos(){ + return blocos; + } + + public void setBlocos(List blocos){ + this.blocos = blocos; + } + + public long getTamanhoAtual(){ + return tamanho; + } -public class Arquivo { } From 8168757f7967f18df852d6f2bda2c136788ab05c Mon Sep 17 00:00:00 2001 From: Mateus Date: Sun, 8 Jun 2025 09:54:37 -0300 Subject: [PATCH 11/30] criando arquivo --- filesys/Arquivo.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java index 7153f54..5d62e9c 100644 --- a/filesys/Arquivo.java +++ b/filesys/Arquivo.java @@ -2,15 +2,17 @@ import exception.BlocoVazioException; import java.util.List; -public class Arquivo extends Diretorio { +public class Arquivo extends MetaDados { + List blocos; private long tamanho; + //adiciona bloco na lista e incrementa o tamanho atual public void adicionarBloco(Bloco bloco){ blocos.add(bloco); tamanho++; } - + //remove blocos da lista por posição passada e decrementa o tamanho atual public Bloco removerBlocoPos(int pos){ if(blocos.isEmpty()){ throw new BlocoVazioException("Não é possível remover pois o bloco já está vazio"); @@ -18,7 +20,7 @@ public Bloco removerBlocoPos(int pos){ tamanho--; return blocos.remove(pos); } - + //limpa o arquivo deletando tudo que está lá e iguala tamanho atual a 0 public void limparArquivo(){ if(blocos.isEmpty()) throw new BlocoVazioException("Não é possível limpar pois o bloco já está vazio"); @@ -37,6 +39,12 @@ public void setBlocos(List blocos){ public long getTamanhoAtual(){ return tamanho; } + public Arquivo(String nome, String permissoesBasicas, String dono, List blocos, long tamanho) { + super(nome, permissoesBasicas, dono); + this.blocos = blocos; + this.tamanho = tamanho; + } + } From c4b23292cfeaaabbc5011c53582f052a81f9d96e Mon Sep 17 00:00:00 2001 From: Mateus Date: Sun, 8 Jun 2025 09:54:45 -0300 Subject: [PATCH 12/30] criando diretorio --- filesys/Diretorio.java | 68 ++++++++++++------------------------------ 1 file changed, 19 insertions(+), 49 deletions(-) diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index c28c09b..8b3bb3f 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -4,15 +4,20 @@ import exception.PermissaoException; -public class Diretorio { - protected String nome; - protected String dono; - protected String permissoesA; //permissões rank A +public class Diretorio extends MetaDados{ - protected Diretorio pai; + public Diretorio(String nome, String permissoesBasicas, String dono, + Diretorio pai, Map filhos, + Map permissoesAvançadas) { + super(nome, permissoesBasicas, dono); + setPai(pai); + setFilhos(filhos); + setPermissoesAvancadas(permissoesAvançadas); + } + protected Diretorio pai; protected Map filhos; - protected Map permissoesS; //permissões rank S + protected Map permissoesAvancadas; /* * verifica se o usuário é root, caso sim, retorna todas as permissões em formato de string (rwx) @@ -23,8 +28,8 @@ public String getPermissoesDeUsuario(String usuario){ if(usuario.equals("root")) return "rwx"; else if(usuario.equals(dono)) - return permissoesA; - return permissoesS.getOrDefault(usuario, "nnn"); + return permissoesBasicas; + return permissoesAvancadas.getOrDefault(usuario, "nnn"); } /* @@ -36,7 +41,7 @@ public void setPermissoesUsuario(String usuario, String permissoes){ if(permissoes == null || permissoes.length() != 3) throw new PermissaoException("Ocorreu um erro quanto as exceções"); - permissoesS.put(usuario, permissoes); + permissoesAvancadas.put(usuario, permissoes); } /* * verifica se o usuário é root ou dono, caso sim retorna true porque os dois tem as permissões @@ -49,7 +54,7 @@ public boolean temPermissao(String usuario, char permissaoRequerida){ if(usuario.equals("root") || usuario.equals(dono)) return true; - String permissoesDoUsuario = permissoesS.get(usuario); + String permissoesDoUsuario = permissoesAvancadas.get(usuario); if(permissoesDoUsuario != null && permissoesDoUsuario.indexOf(permissaoRequerida) != -1) return true; @@ -70,24 +75,6 @@ public Diretorio removerFilho(String nome){ return filhos.remove(nome); } - public String getNome() { - return nome; - } - public void setNome(String nome) { - this.nome = nome; - } - public String getDono() { - return dono; - } - public void setDono(String dono) { - this.dono = dono; - } - public String getPermissoesA() { - return permissoesA; - } - public void setPermissoesA(String permissoesA) { - this.permissoesA = permissoesA; - } public Diretorio getPai() { return pai; } @@ -100,28 +87,11 @@ public Map getFilhos() { public void setFilhos(Map filhos) { this.filhos = filhos; } - public Map getPermissoesS() { - return permissoesS; + public Map getPermissoesAvancadas() { + return permissoesAvancadas; } - public void setPermissoesS(Map permissoesS) { - this.permissoesS = permissoesS; + public void setPermissoesAvancadas(Map permissoesS) { + this.permissoesAvancadas = permissoesS; } - - public boolean isFile(){ - return false; - } - - - public Diretorio(String nome, String dono, String permissoesA, Diretorio pai, Map filhos, - Map permissoesS) { - setNome(nome); - setDono(dono); - setPermissoesA(permissoesA); - setPai(pai); - setFilhos(filhos); - setPermissoesS(permissoesS); - } - - } From 4ffcf7f70cf45a42ce93b7712788283449e1dde3 Mon Sep 17 00:00:00 2001 From: Mateus Date: Sun, 8 Jun 2025 09:54:52 -0300 Subject: [PATCH 13/30] criando metadados --- filesys/MetaDados.java | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 filesys/MetaDados.java diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java new file mode 100644 index 0000000..6c2d768 --- /dev/null +++ b/filesys/MetaDados.java @@ -0,0 +1,35 @@ +package filesys; + +/*classe base que tem o nome, permissões básicas e o dono, tanto Diretorio quanto Arquivo a implementam */ +public class MetaDados { + + protected String nome; + protected String permissoesBasicas; + protected String dono; + + public String getNome() { + return nome; + } + public void setNome(String nome) { + this.nome = nome; + } + public String getPermissoesBasicas() { + return permissoesBasicas; + } + public void setPermissoesBasicas(String permissoesBasicas) { + this.permissoesBasicas = permissoesBasicas; + } + public String getDono() { + return dono; + } + public void setDono(String dono) { + this.dono = dono; + } + + public MetaDados(String nome, String permissoesBasicas, String dono) { + setNome(nome); + setPermissoesBasicas(permissoesBasicas); + setDono(dono); + } + +} From 5e4e59f31ec71334d30bed7e2552be4f640bc67f Mon Sep 17 00:00:00 2001 From: Mateus Date: Tue, 10 Jun 2025 11:12:26 -0300 Subject: [PATCH 14/30] =?UTF-8?q?criando=20m=C3=A9todo=20em=20metadados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/MetaDados.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java index 6c2d768..8e58e84 100644 --- a/filesys/MetaDados.java +++ b/filesys/MetaDados.java @@ -1,7 +1,7 @@ package filesys; /*classe base que tem o nome, permissões básicas e o dono, tanto Diretorio quanto Arquivo a implementam */ -public class MetaDados { +public abstract class MetaDados { protected String nome; protected String permissoesBasicas; @@ -31,5 +31,7 @@ public MetaDados(String nome, String permissoesBasicas, String dono) { setPermissoesBasicas(permissoesBasicas); setDono(dono); } + + public abstract void alterarPermissao(String usuarioAlvo, String permissao); } From 174c991037b3df7ea8ded866af1c2312e4aff318 Mon Sep 17 00:00:00 2001 From: Mateus Date: Tue, 10 Jun 2025 11:12:32 -0300 Subject: [PATCH 15/30] implementando em arquivo --- filesys/Arquivo.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java index 5d62e9c..6697046 100644 --- a/filesys/Arquivo.java +++ b/filesys/Arquivo.java @@ -45,6 +45,8 @@ public Arquivo(String nome, String permissoesBasicas, String dono, List b this.tamanho = tamanho; } - - + @Override + public void alterarPermissao(String usuarioAlvo, String permissao) { + setPermissoesBasicas(permissao); + } } From 20727768242b0cdd1c400dec8563e5b470a42e07 Mon Sep 17 00:00:00 2001 From: Mateus Date: Tue, 10 Jun 2025 11:12:39 -0300 Subject: [PATCH 16/30] implementando em diretorio --- filesys/Diretorio.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index 8b3bb3f..dd357dd 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -6,14 +6,8 @@ public class Diretorio extends MetaDados{ - public Diretorio(String nome, String permissoesBasicas, String dono, - Diretorio pai, Map filhos, - Map permissoesAvançadas) { - + public Diretorio(String nome, String permissoesBasicas, String dono) { super(nome, permissoesBasicas, dono); - setPai(pai); - setFilhos(filhos); - setPermissoesAvancadas(permissoesAvançadas); } protected Diretorio pai; protected Map filhos; @@ -24,7 +18,7 @@ public Diretorio(String nome, String permissoesBasicas, String dono, * verifica se o usuário é dono, caso sim, retorna as respectivas permissoes * se nenhuma das condições for satisfeita, retorna permissoes de valor default: "nnn" (todas null) */ - public String getPermissoesDeUsuario(String usuario){ + public String getPermissoesUsuario(String usuario){ if(usuario.equals("root")) return "rwx"; else if(usuario.equals(dono)) @@ -94,4 +88,8 @@ public void setPermissoesAvancadas(Map permissoesS) { this.permissoesAvancadas = permissoesS; } + @Override + public void alterarPermissao(String usuarioAlvo, String permissao) { + setPermissoesBasicas(permissao); + } } From f657241fa27b10b8f4000ba3b980baa22cfb521a Mon Sep 17 00:00:00 2001 From: Mateus Date: Tue, 10 Jun 2025 11:13:17 -0300 Subject: [PATCH 17/30] =?UTF-8?q?adicionando=20m=C3=A9todos=20de=20valida?= =?UTF-8?q?=C3=A7=C3=A3o=20e=20o=20chmod=20em=20filesystem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/FileSystemImpl.java | 67 +++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 45fa05d..d18df97 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -1,5 +1,9 @@ package filesys; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; @@ -9,19 +13,62 @@ // O construtor, argumentos do construtor podem ser modificados // e atributos & métodos privados podem ser adicionados public final class FileSystemImpl implements IFileSystem { - private static final String ROOT_USER = "root"; // pode ser necessário + private static final String ROOT_USER = "root"; + private List usuarios = new ArrayList<>(); + private Diretorio raiz; + + public FileSystemImpl() { + raiz = new Diretorio("/", "rwx", ROOT_USER); + adicionarUsuario(new Usuario(ROOT_USER, "rwx", "/")); + } + + private void adicionarUsuario(Usuario user){ + for(Usuario usuario : usuarios){ + if(usuario.getNome().trim().equals(user.getNome())) + throw new IllegalArgumentException("Não é possível existir dois usuários com mesmo nome"); + } + usuarios.add(user); + } + + private MetaDados navegar(String caminho) throws CaminhoNaoEncontradoException { + if (caminho.equals("/")) return raiz; + String[] partes = caminho.split("/"); + Diretorio temp = raiz; + for (String parte : java.util.Arrays.stream(partes).filter(p -> p != null && !p.isEmpty()).toArray(String[]::new)) { + if (!temp.getFilhos().containsKey(parte)) { + throw new CaminhoNaoEncontradoException("Não foi possível encontrar o caminho: " + caminho); + } + temp = temp.getFilhos().get(parte); + } + return temp; + } - public FileSystemImpl() {} @Override public void mkdir(String caminho, String nome) throws CaminhoJaExistenteException, PermissaoException { throw new UnsupportedOperationException("Método não implementado 'mkdir'"); } + /* + * Validar a permissão de acordo com as possíveis formas de permissão 'r w x' ou 'n', + * caso seja nulo, vazio ou não tenha nenhuma dessas letras, lança exceção. + * Ao ser validado, chama o método navegar + */ @Override public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'chmod'"); + + validarPermissao(permissao); + + MetaDados objAlvo = navegar(caminho); + String dono = objAlvo.getDono(); + + if (!usuario.equals("root") && !usuario.equals(dono)) { + throw new PermissaoException( + "Usuário '" + usuario + "' não tem permissão para alterar direitos em: " + caminho); + } + + objAlvo.alterarPermissao(usuarioAlvo, permissao); } @Override @@ -67,4 +114,18 @@ public void cp(String caminhoOrigem, String caminhoDestino, String usuario, bool public void addUser(String user) { throw new UnsupportedOperationException("Método não implementado 'addUser'"); } + + private void validarPermissao(String permissao) { + if (permissao == null || permissao.length() != 3) + throw new IllegalArgumentException("Permissão inválida"); + for (char c : permissao.toCharArray()) { + if ("rwxn".indexOf(c) == -1) + throw new IllegalArgumentException("Permissão inválida"); + } + } + + @Override + public void alterarPermissao(String usuarioAlvo, String permissao) { + setPermissoesUsuario(usuarioAlvo, permissao); + } } From 01789c2604cd867c5989f18e6318617696070cb6 Mon Sep 17 00:00:00 2001 From: Mateus Date: Tue, 10 Jun 2025 11:13:40 -0300 Subject: [PATCH 18/30] tirando erro de filesys --- filesys/FileSystemImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index d18df97..9ac627e 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -124,8 +124,4 @@ private void validarPermissao(String permissao) { } } - @Override - public void alterarPermissao(String usuarioAlvo, String permissao) { - setPermissoesUsuario(usuarioAlvo, permissao); - } } From 77c90c467eca0f2915df734da5c8b49ae475b112 Mon Sep 17 00:00:00 2001 From: Mateus Date: Tue, 10 Jun 2025 11:47:05 -0300 Subject: [PATCH 19/30] tirando imports nao usados --- filesys/FileSystemImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 9ac627e..ee7ace7 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -1,7 +1,6 @@ package filesys; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import exception.CaminhoJaExistenteException; From 6de23a69bc7635c4ccecb53620dd4ec3912d35ed Mon Sep 17 00:00:00 2001 From: Rafael Moura Date: Sun, 15 Jun 2025 17:44:21 -0300 Subject: [PATCH 20/30] =?UTF-8?q?implementando=20m=C3=A9todos=20para=20cri?= =?UTF-8?q?a=C3=A7=C3=A3o=20e=20escrita=20de=20arquivos,=20incluindo=20val?= =?UTF-8?q?ida=C3=A7=C3=B5es=20de=20permiss=C3=A3o=20e=20tratamento=20de?= =?UTF-8?q?=20exce=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/FileSystemImpl.java | 174 +++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 4 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index ee7ace7..c5858e0 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -1,8 +1,11 @@ package filesys; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import exception.BlocoVazioException; import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; @@ -15,6 +18,7 @@ public final class FileSystemImpl implements IFileSystem { private static final String ROOT_USER = "root"; private List usuarios = new ArrayList<>(); private Diretorio raiz; + private Map arquivos = new HashMap<>(); public FileSystemImpl() { raiz = new Diretorio("/", "rwx", ROOT_USER); @@ -45,7 +49,40 @@ private MetaDados navegar(String caminho) throws CaminhoNaoEncontradoException { @Override public void mkdir(String caminho, String nome) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mkdir'"); + if (caminho == null || nome == null || caminho.trim().isEmpty() || nome.trim().isEmpty()) { + throw new IllegalArgumentException("Caminho e nome não podem ser nulos ou vazios"); + } + + + String caminhoCompleto = caminho.equals("/") ? "/" + nome : caminho + "/" + nome; + + + String[] partes = caminhoCompleto.split("/"); + String caminhoAtual = ""; + Diretorio diretorioAtual = raiz; + String usuarioAtual = Thread.currentThread().getName(); + + + for (String parte : java.util.Arrays.stream(partes).filter(p -> p != null && !p.isEmpty()).toArray(String[]::new)) { + + caminhoAtual = caminhoAtual.equals("/") ? "/" + parte : caminhoAtual + "/" + parte; + + + if (diretorioAtual.getFilhos().containsKey(parte)) { + diretorioAtual = diretorioAtual.getFilhos().get(parte); + continue; + } + + + if (!diretorioAtual.temPermissao(usuarioAtual, 'w')) { + throw new PermissaoException("Usuário '" + usuarioAtual + "' não tem permissão de escrita em: " + caminhoAtual); + } + + + Diretorio novoDir = new Diretorio(parte, "rwx", usuarioAtual); + diretorioAtual.addFilho(novoDir); + diretorioAtual = novoDir; + } } /* @@ -84,13 +121,127 @@ public void touch(String caminho, String usuario) throws CaminhoJaExistenteExcep @Override public void write(String caminho, String usuario, boolean anexar, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'write'"); + if (buffer == null) { + throw new IllegalArgumentException("Buffer não pode ser nulo"); + } + + // Obter o diretório pai e nome do arquivo + String diretorioPai = obterDiretorioPai(caminho); + String nomeArquivo = obterNomeArquivo(caminho); + + // Verificar se o arquivo já existe no nosso mapa de arquivos + if (arquivos.containsKey(caminho)) { + Arquivo arquivo = arquivos.get(caminho); + + if (!usuario.equals(ROOT_USER) && !arquivo.getDono().equals(usuario)) { + char permissaoRequerida = 'w'; + String permissoes = arquivo.getPermissoesBasicas(); + if (permissoes.indexOf(permissaoRequerida) == -1) { + throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); + } +} + + // Se não estiver anexando, limpa o arquivo + if (!anexar) { + try { + arquivo.limparArquivo(); + } catch (BlocoVazioException e) { + // O arquivo já está vazio, pode prosseguir + } + } + + // Divide o buffer em blocos e escreve + final int TAMANHO_BLOCO = 4096; + + for (int i = 0; i < buffer.length; i += TAMANHO_BLOCO) { + int tamanhoAtual = Math.min(TAMANHO_BLOCO, buffer.length - i); + byte[] dadosBloco = new byte[tamanhoAtual]; + System.arraycopy(buffer, i, dadosBloco, 0, tamanhoAtual); + + Bloco bloco = new Bloco(); + bloco.setBytes(dadosBloco); + arquivo.adicionarBloco(bloco); + } + } else { + // O arquivo não existe, precisa criar + try { + // Verifica se o diretório pai existe + MetaDados diretorio = navegar(diretorioPai); + + if (!(diretorio instanceof Diretorio)) { + throw new IllegalArgumentException("O caminho pai não é um diretório: " + diretorioPai); + } + + Diretorio dir = (Diretorio) diretorio; + + // Verifica permissão para criar no diretório + if (!dir.getDono().equals(usuario) && !usuario.equals(ROOT_USER)) { + if (!dir.temPermissao(usuario, 'w')) { + throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); + } + } + + // Cria o arquivo e adiciona ao nosso mapa + List blocos = new ArrayList<>(); + Arquivo novoArquivo = new Arquivo(nomeArquivo, "rw-", usuario, blocos, 0); + + // Adiciona ao mapa de arquivos por caminho completo + arquivos.put(caminho, novoArquivo); + + // Divide o buffer em blocos e escreve + final int TAMANHO_BLOCO = 4096; + + for (int i = 0; i < buffer.length; i += TAMANHO_BLOCO) { + int tamanhoAtual = Math.min(TAMANHO_BLOCO, buffer.length - i); + byte[] dadosBloco = new byte[tamanhoAtual]; + System.arraycopy(buffer, i, dadosBloco, 0, tamanhoAtual); + + Bloco bloco = new Bloco(); + bloco.setBytes(dadosBloco); + novoArquivo.adicionarBloco(bloco); + } + + } catch (CaminhoNaoEncontradoException ex) { + throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + diretorioPai); + } + } } - @Override + @Override public void read(String caminho, String usuario, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'read'"); + if (buffer == null) { + throw new IllegalArgumentException("Buffer não pode ser nulo"); + } + + if (!arquivos.containsKey(caminho)) { + throw new CaminhoNaoEncontradoException("Arquivo não encontrado: " + caminho); + } + + Arquivo arquivo = arquivos.get(caminho); + + if (!usuario.equals(ROOT_USER) && !arquivo.getDono().equals(usuario)) { + char permissaoRequerida = 'w'; + String permissoes = arquivo.getPermissoesBasicas(); + if (permissoes.indexOf(permissaoRequerida) == -1) { + throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); + } +} + + // Lê todos os blocos do arquivo + List blocos = arquivo.getAllBlocos(); + int offset = 0; + + for (Bloco bloco : blocos) { + byte[] dados = bloco.getDados(); + int tamanhoACopiar = Math.min(dados.length, buffer.length - offset); + + if (tamanhoACopiar <= 0) break; // Buffer cheio + + // Copia os dados do bloco para o buffer + System.arraycopy(dados, 0, buffer, offset, tamanhoACopiar); + offset += tamanhoACopiar; + } } @Override @@ -123,4 +274,19 @@ private void validarPermissao(String permissao) { } } + private String obterDiretorioPai(String caminho) { + if (caminho.equals("/")) return "/"; + + int ultimaBarra = caminho.lastIndexOf('/'); + if (ultimaBarra == 0) return "/"; + return caminho.substring(0, ultimaBarra); + } + + private String obterNomeArquivo(String caminho) { + if (caminho.equals("/")) return ""; + + int ultimaBarra = caminho.lastIndexOf('/'); + return caminho.substring(ultimaBarra + 1); + } + } From 9a2b722a497bc0cdc23970ff556af6006e261991 Mon Sep 17 00:00:00 2001 From: Rafael Moura Date: Mon, 16 Jun 2025 15:57:04 -0300 Subject: [PATCH 21/30] =?UTF-8?q?refatorando=20m=C3=A9todos=20mkdir=20e=20?= =?UTF-8?q?cp=20para=20melhorar=20valida=C3=A7=C3=B5es=20e=20tratamento=20?= =?UTF-8?q?de=20exce=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/FileSystemImpl.java | 161 ++++++++++++++++++++++++------------ 1 file changed, 109 insertions(+), 52 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index c5858e0..cedc042 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -46,39 +46,31 @@ private MetaDados navegar(String caminho) throws CaminhoNaoEncontradoException { return temp; } - @Override public void mkdir(String caminho, String nome) throws CaminhoJaExistenteException, PermissaoException { if (caminho == null || nome == null || caminho.trim().isEmpty() || nome.trim().isEmpty()) { throw new IllegalArgumentException("Caminho e nome não podem ser nulos ou vazios"); } - String caminhoCompleto = caminho.equals("/") ? "/" + nome : caminho + "/" + nome; - - + String[] partes = caminhoCompleto.split("/"); String caminhoAtual = ""; Diretorio diretorioAtual = raiz; - String usuarioAtual = Thread.currentThread().getName(); + String usuarioAtual = Thread.currentThread().getName(); - for (String parte : java.util.Arrays.stream(partes).filter(p -> p != null && !p.isEmpty()).toArray(String[]::new)) { - caminhoAtual = caminhoAtual.equals("/") ? "/" + parte : caminhoAtual + "/" + parte; - if (diretorioAtual.getFilhos().containsKey(parte)) { diretorioAtual = diretorioAtual.getFilhos().get(parte); continue; } - if (!diretorioAtual.temPermissao(usuarioAtual, 'w')) { throw new PermissaoException("Usuário '" + usuarioAtual + "' não tem permissão de escrita em: " + caminhoAtual); } - Diretorio novoDir = new Diretorio(parte, "rwx", usuarioAtual); diretorioAtual.addFilho(novoDir); diretorioAtual = novoDir; @@ -125,14 +117,12 @@ public void write(String caminho, String usuario, boolean anexar, byte[] buffer) throw new IllegalArgumentException("Buffer não pode ser nulo"); } - // Obter o diretório pai e nome do arquivo String diretorioPai = obterDiretorioPai(caminho); String nomeArquivo = obterNomeArquivo(caminho); - - // Verificar se o arquivo já existe no nosso mapa de arquivos + if (arquivos.containsKey(caminho)) { Arquivo arquivo = arquivos.get(caminho); - + if (!usuario.equals(ROOT_USER) && !arquivo.getDono().equals(usuario)) { char permissaoRequerida = 'w'; String permissoes = arquivo.getPermissoesBasicas(); @@ -140,67 +130,58 @@ public void write(String caminho, String usuario, boolean anexar, byte[] buffer) throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); } } - - // Se não estiver anexando, limpa o arquivo + if (!anexar) { try { arquivo.limparArquivo(); } catch (BlocoVazioException e) { - // O arquivo já está vazio, pode prosseguir } } - - // Divide o buffer em blocos e escreve + final int TAMANHO_BLOCO = 4096; - + for (int i = 0; i < buffer.length; i += TAMANHO_BLOCO) { int tamanhoAtual = Math.min(TAMANHO_BLOCO, buffer.length - i); byte[] dadosBloco = new byte[tamanhoAtual]; System.arraycopy(buffer, i, dadosBloco, 0, tamanhoAtual); - + Bloco bloco = new Bloco(); bloco.setBytes(dadosBloco); arquivo.adicionarBloco(bloco); } } else { - // O arquivo não existe, precisa criar try { - // Verifica se o diretório pai existe MetaDados diretorio = navegar(diretorioPai); - + if (!(diretorio instanceof Diretorio)) { throw new IllegalArgumentException("O caminho pai não é um diretório: " + diretorioPai); } - + Diretorio dir = (Diretorio) diretorio; - - // Verifica permissão para criar no diretório + if (!dir.getDono().equals(usuario) && !usuario.equals(ROOT_USER)) { if (!dir.temPermissao(usuario, 'w')) { throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); } } - - // Cria o arquivo e adiciona ao nosso mapa + List blocos = new ArrayList<>(); Arquivo novoArquivo = new Arquivo(nomeArquivo, "rw-", usuario, blocos, 0); - - // Adiciona ao mapa de arquivos por caminho completo + arquivos.put(caminho, novoArquivo); - - // Divide o buffer em blocos e escreve + final int TAMANHO_BLOCO = 4096; - + for (int i = 0; i < buffer.length; i += TAMANHO_BLOCO) { int tamanhoAtual = Math.min(TAMANHO_BLOCO, buffer.length - i); byte[] dadosBloco = new byte[tamanhoAtual]; System.arraycopy(buffer, i, dadosBloco, 0, tamanhoAtual); - + Bloco bloco = new Bloco(); bloco.setBytes(dadosBloco); novoArquivo.adicionarBloco(bloco); } - + } catch (CaminhoNaoEncontradoException ex) { throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + diretorioPai); } @@ -213,13 +194,13 @@ public void read(String caminho, String usuario, byte[] buffer) if (buffer == null) { throw new IllegalArgumentException("Buffer não pode ser nulo"); } - + if (!arquivos.containsKey(caminho)) { throw new CaminhoNaoEncontradoException("Arquivo não encontrado: " + caminho); } - + Arquivo arquivo = arquivos.get(caminho); - + if (!usuario.equals(ROOT_USER) && !arquivo.getDono().equals(usuario)) { char permissaoRequerida = 'w'; String permissoes = arquivo.getPermissoesBasicas(); @@ -227,18 +208,16 @@ public void read(String caminho, String usuario, byte[] buffer) throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); } } - - // Lê todos os blocos do arquivo + List blocos = arquivo.getAllBlocos(); int offset = 0; - + for (Bloco bloco : blocos) { byte[] dados = bloco.getDados(); int tamanhoACopiar = Math.min(dados.length, buffer.length - offset); - - if (tamanhoACopiar <= 0) break; // Buffer cheio - - // Copia os dados do bloco para o buffer + + if (tamanhoACopiar <= 0) break; + System.arraycopy(dados, 0, buffer, offset, tamanhoACopiar); offset += tamanhoACopiar; } @@ -255,11 +234,89 @@ public void ls(String caminho, String usuario, boolean recursivo) throws Caminho throw new UnsupportedOperationException("Método não implementado 'ls'"); } - @Override - public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) - throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'cp'"); + @Override +public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { + if (arquivos.containsKey(caminhoOrigem)) { + Arquivo arquivoOrigem = arquivos.get(caminhoOrigem); + + if (!usuario.equals(ROOT_USER) && !arquivoOrigem.getDono().equals(usuario)) { + String permissoesOrigem = arquivoOrigem.getPermissoesBasicas(); + if (permissoesOrigem.indexOf('r') == -1) { + throw new PermissaoException("Usuário '" + usuario + + "' não tem permissão de leitura no arquivo de origem: " + caminhoOrigem); + } + } + + int tamanhoTotal = 0; + for (Bloco bloco : arquivoOrigem.getAllBlocos()) { + tamanhoTotal += bloco.getDados().length; + } + + byte[] buffer = new byte[tamanhoTotal]; + read(caminhoOrigem, usuario, buffer); + + write(caminhoDestino, usuario, false, buffer); + + return; + } + + try { + MetaDados metadados = navegar(caminhoOrigem); + + if (metadados instanceof Diretorio) { + if (!recursivo) { + throw new IllegalArgumentException( + "Para copiar um diretório, é necessário usar o modo recursivo"); + } + + Diretorio dirOrigem = (Diretorio) metadados; + + if (!usuario.equals(ROOT_USER) && !dirOrigem.getDono().equals(usuario)) { + if (!dirOrigem.temPermissao(usuario, 'r')) { + throw new PermissaoException("Usuário '" + usuario + + "' não tem permissão de leitura no diretório: " + caminhoOrigem); + } + } + + try { + mkdir(obterDiretorioPai(caminhoDestino), obterNomeArquivo(caminhoDestino)); + } catch (CaminhoJaExistenteException e) { + } + + for (String nomeArquivo : arquivos.keySet()) { + if (nomeArquivo.startsWith(caminhoOrigem + "/")) { + String relativePath = nomeArquivo.substring(caminhoOrigem.length()); + String novoDestino = caminhoDestino + relativePath; + + Arquivo arquivoOrigem = arquivos.get(nomeArquivo); + int tamanhoTotal = 0; + for (Bloco bloco : arquivoOrigem.getAllBlocos()) { + tamanhoTotal += bloco.getDados().length; + } + + byte[] buffer = new byte[tamanhoTotal]; + read(nomeArquivo, usuario, buffer); + + write(novoDestino, usuario, false, buffer); + } + } + + for (String filhoNome : dirOrigem.getFilhos().keySet()) { + String filhoCaminhoOrigem = caminhoOrigem.equals("/") ? + "/" + filhoNome : caminhoOrigem + "/" + filhoNome; + String filhoCaminhoDestino = caminhoDestino.equals("/") ? + "/" + filhoNome : caminhoDestino + "/" + filhoNome; + + cp(filhoCaminhoOrigem, filhoCaminhoDestino, usuario, true); + } + } else { + throw new IllegalArgumentException("O caminho de origem não é um arquivo nem um diretório: " + caminhoOrigem); + } + } catch (CaminhoNaoEncontradoException e) { + throw e; } +} public void addUser(String user) { throw new UnsupportedOperationException("Método não implementado 'addUser'"); @@ -276,7 +333,7 @@ private void validarPermissao(String permissao) { private String obterDiretorioPai(String caminho) { if (caminho.equals("/")) return "/"; - + int ultimaBarra = caminho.lastIndexOf('/'); if (ultimaBarra == 0) return "/"; return caminho.substring(0, ultimaBarra); @@ -284,7 +341,7 @@ private String obterDiretorioPai(String caminho) { private String obterNomeArquivo(String caminho) { if (caminho.equals("/")) return ""; - + int ultimaBarra = caminho.lastIndexOf('/'); return caminho.substring(ultimaBarra + 1); } From 6bc08755cdf003d0c5003892a59e9631a590cc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BAlio=20Henrique?= Date: Tue, 17 Jun 2025 23:43:27 -0300 Subject: [PATCH 22/30] base --- Main.java | 111 ++++-- filesys/Arquivo.java | 56 +-- filesys/Bloco.java | 14 +- filesys/Deslocamento.java | 42 --- filesys/Diretorio.java | 108 ++---- filesys/FileSys.java | 16 + filesys/FileSystem.java | 18 +- filesys/FileSystemImpl.java | 707 ++++++++++++++++++++++++------------ filesys/IFileSystem.java | 10 +- filesys/MetaDados.java | 59 +-- filesys/Offset.java | 21 ++ filesys/Usuario.java | 88 +++-- 12 files changed, 760 insertions(+), 490 deletions(-) delete mode 100644 filesys/Deslocamento.java create mode 100644 filesys/FileSys.java create mode 100644 filesys/Offset.java diff --git a/Main.java b/Main.java index 3b8673a..851c381 100644 --- a/Main.java +++ b/Main.java @@ -1,5 +1,9 @@ import filesys.IFileSystem; +import filesys.Offset; +import filesys.Usuario; +import java.util.HashMap; +import java.util.Map; import java.util.Scanner; import java.io.FileNotFoundException; @@ -13,8 +17,8 @@ // 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 + // 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 = "/"; @@ -29,28 +33,33 @@ 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. + // 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/**, + // 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 + // username dir permission // Exemplo: - // maria /** rw- - // luzia /** rwx + // 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, + // 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. + Map usuariosMap = new HashMap<>(); try { Scanner userScanner = new Scanner(new java.io.File("users/users")); while (userScanner.hasNextLine()) { @@ -61,13 +70,18 @@ public static void main(String[] args) { 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 + /* + * 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. --- FEITO + */ + System.out.println(userListed + " " + dir + " " + dirPermission); // Somente imprime o usuário, + // diretório e permissão + Usuario usuario = usuariosMap.getOrDefault(userListed, new Usuario(userListed)); + usuario.adicionarPermissao(dir, dirPermission); + usuariosMap.put(userListed, usuario); } else { System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); @@ -80,19 +94,21 @@ public static void main(String[] args) { 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?*/); + fileSystem = new FileSystem(usuariosMap); - // // DESCOMENTE O BLOCO ABAIXO PARA CRIAR O DIRETÓRIO RAIZ ANTES DE RODAR O MENU + // // 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()); - // } + try { + fileSystem.mkdir(ROOT_DIR, ROOT_USER); + fileSystem.mkdir("/home", ROOT_USER); + } catch (CaminhoJaExistenteException | PermissaoException | CaminhoNaoEncontradoException e) { + System.out.println(e.getMessage()); + } // Menu interativo. menu(); @@ -152,7 +168,7 @@ public static void menu() { return; default: System.out.println("Comando inválido!"); - } + } } catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException e) { System.out.println("Erro: " + e.getMessage()); } @@ -171,14 +187,14 @@ public static void chmod() throws CaminhoNaoEncontradoException, PermissaoExcept String usuarioAlvo = scanner.nextLine(); 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 { + public static void mkdir() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { System.out.println("Insira o caminho do diretório a ser criado:"); String caminho = scanner.nextLine(); - + fileSystem.mkdir(caminho, user); } @@ -187,14 +203,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 { + public static void touch() throws CaminhoJaExistenteException, CaminhoNaoEncontradoException, PermissaoException { System.out.println("Insira o caminho do arquivo a ser criado:"); String caminho = scanner.nextLine(); - + fileSystem.touch(caminho, user); } @@ -206,24 +222,39 @@ 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]; + + Offset offset = new Offset(0); + int offsetAnterior; + int bytesLidos; + + System.out.println("Conteúdo lido:"); + 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 { + public static void mv() throws CaminhoNaoEncontradoException, CaminhoJaExistenteException, PermissaoException { System.out.println("Insira o caminho do arquivo a ser movido:"); String caminhoAntigo = scanner.nextLine(); System.out.println("Insira o novo caminho do arquivo:"); String caminhoNovo = scanner.nextLine(); - + fileSystem.mv(caminhoAntigo, caminhoNovo, user); } @@ -232,7 +263,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 +274,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); } } diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java index 6697046..3acb55a 100644 --- a/filesys/Arquivo.java +++ b/filesys/Arquivo.java @@ -1,52 +1,28 @@ package filesys; -import exception.BlocoVazioException; -import java.util.List; -public class Arquivo extends MetaDados { +import java.util.ArrayList; +import java.util.List; - List blocos; - private long tamanho; +public class Arquivo { + private MetaDados metaDados; + private List blocos; + public static int TAMANHO_BLOCO = 64; - //adiciona bloco na lista e incrementa o tamanho atual - public void adicionarBloco(Bloco bloco){ - blocos.add(bloco); - tamanho++; - } - //remove blocos da lista por posição passada e decrementa o tamanho atual - public Bloco removerBlocoPos(int pos){ - if(blocos.isEmpty()){ - throw new BlocoVazioException("Não é possível remover pois o bloco já está vazio"); - } - tamanho--; - return blocos.remove(pos); - } - //limpa o arquivo deletando tudo que está lá e iguala tamanho atual a 0 - public void limparArquivo(){ - if(blocos.isEmpty()) - throw new BlocoVazioException("Não é possível limpar pois o bloco já está vazio"); - blocos.clear(); - tamanho = 0; + public Arquivo(String nome, String dono) { + this.metaDados = new MetaDados(nome, dono); + this.blocos = new ArrayList<>(); } - public List getAllBlocos(){ - return blocos; + public MetaDados getMetaDados() { + return metaDados; } - public void setBlocos(List blocos){ - this.blocos = blocos; - } - - public long getTamanhoAtual(){ - return tamanho; - } - public Arquivo(String nome, String permissoesBasicas, String dono, List blocos, long tamanho) { - super(nome, permissoesBasicas, dono); - this.blocos = blocos; - this.tamanho = tamanho; + public List getBlocos() { + return blocos; } - @Override - public void alterarPermissao(String usuarioAlvo, String permissao) { - setPermissoesBasicas(permissao); + public void adicionarBloco(Bloco bloco) { + blocos.add(bloco); + metaDados.setTamanho(metaDados.getTamanho() + bloco.getDados().length); } } diff --git a/filesys/Bloco.java b/filesys/Bloco.java index 28e9eac..be11773 100644 --- a/filesys/Bloco.java +++ b/filesys/Bloco.java @@ -1,17 +1,17 @@ package filesys; public class Bloco { - byte[] bytes; + private byte[] dados; - public Bloco(){ - bytes = new byte[4096]; + public Bloco(int tamanho) { + this.dados = new byte[tamanho]; } - public byte[] getDados(){ - return bytes; + public byte[] getDados() { + return dados; } - public void setBytes(byte[] bytes){ - this.bytes = bytes; + public void setDados(byte[] dados) { + this.dados = dados; } } diff --git a/filesys/Deslocamento.java b/filesys/Deslocamento.java deleted file mode 100644 index 5c148ba..0000000 --- a/filesys/Deslocamento.java +++ /dev/null @@ -1,42 +0,0 @@ -package filesys; - -public class Deslocamento { - private int valorMax; - private int valor; - - - public Deslocamento(int valor) { - setValor(valor); - setValorMax(Integer.MAX_VALUE); - } - - - public int getValorMax() { - return valorMax; - } - - - public void setValorMax(int valorMax) { - if(valorMax < 0) - throw new IllegalArgumentException("Valor não pode ser menor que 0"); - else if(this.valor > valorMax) - throw new IllegalArgumentException("Valor padrão não pode ser maior que o limite"); - - this.valorMax = valorMax; - } - - - public int getValor() { - return valor; - } - - - public void setValor(int valor) { - if(valorMax < 0) - throw new IllegalArgumentException("Valor não pode ser menor que 0"); - else if(this.valor > valorMax) - throw new IllegalArgumentException("Valor padrão não pode ser maior que o limite: " + valorMax); - } - - -} diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index dd357dd..77ac1b6 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -1,95 +1,57 @@ package filesys; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; -import exception.PermissaoException; +public class Diretorio { + private MetaDados metaDados; + private List arquivos; + private List subdiretorios; -public class Diretorio extends MetaDados{ - - public Diretorio(String nome, String permissoesBasicas, String dono) { - super(nome, permissoesBasicas, dono); + public Diretorio(String nome, String dono) { + this.metaDados = new MetaDados(nome, dono); + this.arquivos = new ArrayList<>(); + this.subdiretorios = new ArrayList<>(); } - protected Diretorio pai; - protected Map filhos; - protected Map permissoesAvancadas; - /* - * verifica se o usuário é root, caso sim, retorna todas as permissões em formato de string (rwx) - * verifica se o usuário é dono, caso sim, retorna as respectivas permissoes - * se nenhuma das condições for satisfeita, retorna permissoes de valor default: "nnn" (todas null) - */ - public String getPermissoesUsuario(String usuario){ - if(usuario.equals("root")) - return "rwx"; - else if(usuario.equals(dono)) - return permissoesBasicas; - return permissoesAvancadas.getOrDefault(usuario, "nnn"); + public MetaDados getMetaDados() { + return metaDados; } - - /* - * seta permissões para o usuário utilizando 3 tipos de caracteres: "w", "r" e "x" - * caso as permissões forem diferentes de 3 lança exceção - * para sempre ter 3 letras, caso não houver o usuário não tiver uma permissão, é utilizado o n (null) - */ - public void setPermissoesUsuario(String usuario, String permissoes){ - if(permissoes == null || permissoes.length() != 3) - throw new PermissaoException("Ocorreu um erro quanto as exceções"); - permissoesAvancadas.put(usuario, permissoes); + public List getArquivos() { + return arquivos; } - /* - * verifica se o usuário é root ou dono, caso sim retorna true porque os dois tem as permissões - * se não, ele continua e verifica as permissões do usuário no diretório - * o código continua se nenhuma das condições tiver sido satisfeita e procura as permissões herdadas do pai - * retorna falso se o usuário não tem a permissão - */ - public boolean temPermissao(String usuario, char permissaoRequerida){ - - if(usuario.equals("root") || usuario.equals(dono)) - return true; - String permissoesDoUsuario = permissoesAvancadas.get(usuario); - - if(permissoesDoUsuario != null && permissoesDoUsuario.indexOf(permissaoRequerida) != -1) - return true; - - Diretorio pai = getPai(); - if(pai != null){ - return pai.temPermissao(usuario, permissaoRequerida); - } - return false; + public List getSubdiretorios() { + return subdiretorios; } - public void addFilho(Diretorio filho){ - filho.setPai(this); - filhos.put(filho.getNome(), filho); + public void adicionarArquivo(Arquivo a) { + arquivos.add(a); } - public Diretorio removerFilho(String nome){ - return filhos.remove(nome); + public void adicionarSubdiretorio(Diretorio d) { + subdiretorios.add(d); } - public Diretorio getPai() { - return pai; - } - public void setPai(Diretorio pai) { - this.pai = pai; - } - public Map getFilhos() { - return filhos; + public Diretorio buscarSubdiretorio(String nome) { + return subdiretorios.stream() + .filter(d -> d.getMetaDados().getNome().equals(nome)) + .findFirst().orElse(null); } - public void setFilhos(Map filhos) { - this.filhos = filhos; - } - public Map getPermissoesAvancadas() { - return permissoesAvancadas; + + public Arquivo buscarArquivo(String nome) { + return arquivos.stream() + .filter(a -> a.getMetaDados().getNome().equals(nome)) + .findFirst().orElse(null); } - public void setPermissoesAvancadas(Map permissoesS) { - this.permissoesAvancadas = permissoesS; + + public void removerArquivo(String nome) { + arquivos.removeIf(a -> a.getMetaDados().getNome().equals(nome)); } - @Override - public void alterarPermissao(String usuarioAlvo, String permissao) { - setPermissoesBasicas(permissao); + public void removerSubdiretorio(String nome) { + subdiretorios.removeIf(d -> d.getMetaDados().getNome().equals(nome)); } + } diff --git a/filesys/FileSys.java b/filesys/FileSys.java new file mode 100644 index 0000000..532b8fb --- /dev/null +++ b/filesys/FileSys.java @@ -0,0 +1,16 @@ +package filesys; + +public class FileSys { + private Diretorio raiz; + + public FileSys(String dono) { + this.raiz = new Diretorio("/", dono); + } + + 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..e8a8398 100644 --- a/filesys/FileSystem.java +++ b/filesys/FileSystem.java @@ -1,5 +1,7 @@ package filesys; +import java.util.Map; + 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(Map usuarios) { + this.fileSystemImpl = new FileSystemImpl(usuarios); } @Override @@ -20,7 +22,8 @@ public void chmod(String caminho, String usuario, String usuarioAlvo, String per } @Override - public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + public void mkdir(String caminho, String usuario) + throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { fileSystemImpl.mkdir(caminho, usuario); } @@ -31,7 +34,8 @@ public void rm(String caminho, String usuario, boolean recursivo) } @Override - public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + public void touch(String caminho, String usuario) + throws CaminhoJaExistenteException, CaminhoNaoEncontradoException, PermissaoException { fileSystemImpl.touch(caminho, usuario); } @@ -42,14 +46,14 @@ 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 public void mv(String caminhoAntigo, String caminhoNovo, String usuario) - throws CaminhoNaoEncontradoException, PermissaoException { + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { fileSystemImpl.mv(caminhoAntigo, caminhoNovo, usuario); } diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index cedc042..cb9cf0d 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -1,11 +1,8 @@ package filesys; import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import exception.BlocoVazioException; import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; @@ -15,335 +12,587 @@ // O construtor, argumentos do construtor podem ser modificados // e atributos & métodos privados podem ser adicionados public final class FileSystemImpl implements IFileSystem { - private static final String ROOT_USER = "root"; - private List usuarios = new ArrayList<>(); - private Diretorio raiz; - private Map arquivos = new HashMap<>(); - - public FileSystemImpl() { - raiz = new Diretorio("/", "rwx", ROOT_USER); - adicionarUsuario(new Usuario(ROOT_USER, "rwx", "/")); - } + private static final String ROOT_USER = "root"; // pode ser necessário + private final FileSys fileSys; + private final Map usuarios; - private void adicionarUsuario(Usuario user){ - for(Usuario usuario : usuarios){ - if(usuario.getNome().trim().equals(user.getNome())) - throw new IllegalArgumentException("Não é possível existir dois usuários com mesmo nome"); - } - usuarios.add(user); + public FileSystemImpl(Map usuarios) { + this.fileSys = new FileSys(ROOT_USER); + this.usuarios = usuarios; } - private MetaDados navegar(String caminho) throws CaminhoNaoEncontradoException { - if (caminho.equals("/")) return raiz; + @Override + public void mkdir(String caminho, String usuario) + throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + verificaUsuarioValido(usuario); + + if (caminho.equals("/")) + return; + String[] partes = caminho.split("/"); - Diretorio temp = raiz; - for (String parte : java.util.Arrays.stream(partes).filter(p -> p != null && !p.isEmpty()).toArray(String[]::new)) { - if (!temp.getFilhos().containsKey(parte)) { - throw new CaminhoNaoEncontradoException("Não foi possível encontrar o caminho: " + caminho); + if (!partes[0].isEmpty()) { + throw new IllegalArgumentException("Caminho inválido: deve começar com '/'"); + } + + Diretorio atual = fileSys.getRaiz(); + StringBuilder pathBuilder = new StringBuilder(); + for (int i = 1; i < partes.length; i++) { + if (partes[i].isEmpty()) + continue; + pathBuilder.append("/").append(partes[i]); + String nomeDir = partes[i]; + + Diretorio proximo = atual.buscarSubdiretorio(nomeDir); + boolean ultimo = (i == partes.length - 1); + + if (proximo == null) { + String paiPath = pathBuilder.substring(0, pathBuilder.lastIndexOf("/")); + if (paiPath.isEmpty()) + paiPath = "/"; + verificarPermissaoExecucaoNoCaminho(paiPath, usuario); + verificarPermissao(paiPath, usuario, 'w'); + Diretorio novo = new Diretorio(nomeDir, usuario); + atual.adicionarSubdiretorio(novo); + atual = novo; + } else { + if (ultimo) { + throw new CaminhoJaExistenteException( + "O diretório '" + nomeDir + "' já existe em '" + caminho + "'"); + } + atual = proximo; } - temp = temp.getFilhos().get(parte); } - return temp; } @Override - public void mkdir(String caminho, String nome) throws CaminhoJaExistenteException, PermissaoException { - if (caminho == null || nome == null || caminho.trim().isEmpty() || nome.trim().isEmpty()) { - throw new IllegalArgumentException("Caminho e nome não podem ser nulos ou vazios"); + public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) + throws CaminhoNaoEncontradoException, PermissaoException { + verificaUsuarioValido(usuario); + verificaUsuarioValido(usuarioAlvo); + + // Só root ou quem tem permissão rw pode alterar permissoes + if (!usuario.equals(ROOT_USER) + && !(temPermissao(caminho, usuario, 'r') && temPermissao(caminho, usuario, 'w'))) { + throw new PermissaoException( + "Usuário '" + usuario + "' não tem permissão para alterar permissões em '" + caminho + "'"); + } + + if (caminho.equals("/")) { + fileSys.getRaiz().getMetaDados().setPermissao(usuarioAlvo, permissao); + return; + } + + Diretorio pai = navegarParaDiretorioPai(caminho); + String nome = extrairNomeFinal(caminho); + + Arquivo arq = pai.buscarArquivo(nome); + if (arq != null) { + arq.getMetaDados().setPermissao(usuarioAlvo, permissao); + return; + } + Diretorio dir = pai.buscarSubdiretorio(nome); + if (dir != null) { + dir.getMetaDados().setPermissao(usuarioAlvo, permissao); + return; + } + throw new CaminhoNaoEncontradoException("Caminho '" + caminho + "' não encontrado."); } - String caminhoCompleto = caminho.equals("/") ? "/" + nome : caminho + "/" + nome; + @Override + public void rm(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { + verificaUsuarioValido(usuario); - String[] partes = caminhoCompleto.split("/"); - String caminhoAtual = ""; - Diretorio diretorioAtual = raiz; - String usuarioAtual = Thread.currentThread().getName(); + if (caminho.equals("/")) { + throw new PermissaoException("Não é permitido remover o diretório raiz."); + } - for (String parte : java.util.Arrays.stream(partes).filter(p -> p != null && !p.isEmpty()).toArray(String[]::new)) { - caminhoAtual = caminhoAtual.equals("/") ? "/" + parte : caminhoAtual + "/" + parte; + verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminho), usuario); + verificarPermissao(caminho, usuario, 'r'); + verificarPermissao(obterCaminhoPai(caminho), usuario, 'w'); - if (diretorioAtual.getFilhos().containsKey(parte)) { - diretorioAtual = diretorioAtual.getFilhos().get(parte); - continue; + Diretorio pai = navegarParaDiretorioPai(caminho); + String nome = extrairNomeFinal(caminho); + + Arquivo arq = pai.buscarArquivo(nome); + if (arq != null) { + pai.removerArquivo(nome); + return; } - if (!diretorioAtual.temPermissao(usuarioAtual, 'w')) { - throw new PermissaoException("Usuário '" + usuarioAtual + "' não tem permissão de escrita em: " + caminhoAtual); + Diretorio dir = pai.buscarSubdiretorio(nome); + if (dir == null) { + throw new CaminhoNaoEncontradoException("Caminho '" + caminho + "' não encontrado."); } - Diretorio novoDir = new Diretorio(parte, "rwx", usuarioAtual); - diretorioAtual.addFilho(novoDir); - diretorioAtual = novoDir; - } + if (!recursivo && (!dir.getArquivos().isEmpty() || !dir.getSubdiretorios().isEmpty())) { + throw new PermissaoException("Diretório não vazio. Use o modo recursivo."); + } + + if (recursivo) { + removerDiretorioRecursivamente(dir); + } + + pai.removerSubdiretorio(nome); } - /* - * Validar a permissão de acordo com as possíveis formas de permissão 'r w x' ou 'n', - * caso seja nulo, vazio ou não tenha nenhuma dessas letras, lança exceção. - * Ao ser validado, chama o método navegar - */ @Override - public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) - throws CaminhoNaoEncontradoException, PermissaoException { + public void touch(String caminho, String usuario) + throws CaminhoJaExistenteException, CaminhoNaoEncontradoException, PermissaoException { + verificaUsuarioValido(usuario); - validarPermissao(permissao); + if (caminho.equals("/") || caminho.endsWith("/")) { + throw new CaminhoNaoEncontradoException("Caminho inválido para arquivo: " + caminho); + } - MetaDados objAlvo = navegar(caminho); - String dono = objAlvo.getDono(); + verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminho), usuario); - if (!usuario.equals("root") && !usuario.equals(dono)) { - throw new PermissaoException( - "Usuário '" + usuario + "' não tem permissão para alterar direitos em: " + caminho); + Diretorio pai = navegarParaDiretorioPai(caminho); + String nomeArquivo = extrairNomeFinal(caminho); + + if (pai.buscarArquivo(nomeArquivo) != null) { + throw new CaminhoJaExistenteException("O arquivo '" + nomeArquivo + "' já existe."); } - objAlvo.alterarPermissao(usuarioAlvo, permissao); + verificarPermissao(obterCaminhoPai(caminho), usuario, 'w'); + + Arquivo novoArquivo = new Arquivo(nomeArquivo, usuario); + pai.adicionarArquivo(novoArquivo); } @Override - public void rm(String caminho, String usuario, boolean recursivo) + public void write(String caminho, String usuario, boolean anexar, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'rm'"); - } + verificaUsuarioValido(usuario); + verificarPermissao(caminho, usuario, 'w'); + verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminho), usuario); - @Override - public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'touch'"); + Diretorio pai = navegarParaDiretorioPai(caminho); + String nomeArquivo = extrairNomeFinal(caminho); + Arquivo arquivo = pai.buscarArquivo(nomeArquivo); + + if (arquivo == null) { + throw new CaminhoNaoEncontradoException("Arquivo '" + nomeArquivo + "' não encontrado."); + } + + if (!anexar) { + arquivo.getBlocos().clear(); + arquivo.getMetaDados().setTamanho(0); + } + + escreverBufferEmBlocos(arquivo, buffer); } @Override - public void write(String caminho, String usuario, boolean anexar, byte[] buffer) + public void read(String caminho, String usuario, byte[] buffer, Offset offset) throws CaminhoNaoEncontradoException, PermissaoException { - if (buffer == null) { - throw new IllegalArgumentException("Buffer não pode ser nulo"); - } + verificaUsuarioValido(usuario); + verificarPermissao(caminho, usuario, 'r'); + verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminho), usuario); - String diretorioPai = obterDiretorioPai(caminho); - String nomeArquivo = obterNomeArquivo(caminho); + Diretorio pai = navegarParaDiretorioPai(caminho); + String nomeArquivo = extrairNomeFinal(caminho); + Arquivo arquivo = pai.buscarArquivo(nomeArquivo); - if (arquivos.containsKey(caminho)) { - Arquivo arquivo = arquivos.get(caminho); + if (arquivo == null) { + throw new CaminhoNaoEncontradoException("Arquivo '" + nomeArquivo + "' não encontrado."); + } - if (!usuario.equals(ROOT_USER) && !arquivo.getDono().equals(usuario)) { - char permissaoRequerida = 'w'; - String permissoes = arquivo.getPermissoesBasicas(); - if (permissoes.indexOf(permissaoRequerida) == -1) { - throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); + lerArquivoComOffset(arquivo, buffer, offset); } -} - if (!anexar) { - try { - arquivo.limparArquivo(); - } catch (BlocoVazioException e) { - } - } + @Override + public void mv(String caminhoAntigo, String caminhoNovo, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + verificaUsuarioValido(usuario); - final int TAMANHO_BLOCO = 4096; + if (caminhoAntigo.equals("/") || caminhoNovo.equals("/")) { + throw new CaminhoNaoEncontradoException("Não é possível mover o diretório raiz."); + } - for (int i = 0; i < buffer.length; i += TAMANHO_BLOCO) { - int tamanhoAtual = Math.min(TAMANHO_BLOCO, buffer.length - i); - byte[] dadosBloco = new byte[tamanhoAtual]; - System.arraycopy(buffer, i, dadosBloco, 0, tamanhoAtual); + verificarPermissaoExecucaoNoCaminho(caminhoAntigo, usuario); + verificarPermissaoExecucaoNoCaminho(caminhoNovo, usuario); - Bloco bloco = new Bloco(); - bloco.setBytes(dadosBloco); - arquivo.adicionarBloco(bloco); - } - } else { - try { - MetaDados diretorio = navegar(diretorioPai); + verificarPermissao(caminhoAntigo, usuario, 'r'); + verificarPermissao(obterCaminhoPai(caminhoNovo), usuario, 'w'); + verificarPermissao(obterCaminhoPai(caminhoAntigo), usuario, 'w'); - if (!(diretorio instanceof Diretorio)) { - throw new IllegalArgumentException("O caminho pai não é um diretório: " + diretorioPai); - } + // Extrai nomes e diretórios pai + Diretorio dirOrigemPai = navegarParaDiretorioPai(caminhoAntigo); + Diretorio dirDestinoPai = navegarParaDiretorioPai(caminhoNovo); + String nomeOrigem = extrairNomeFinal(caminhoAntigo); + String nomeDestino = extrairNomeFinal(caminhoNovo); + + // Verifica se já existe algo no destino + if (dirDestinoPai.buscarArquivo(nomeDestino) != null || dirDestinoPai.buscarSubdiretorio(nomeDestino) != null) { + throw new CaminhoJaExistenteException("O destino '" + caminhoNovo + "' já existe."); + } - Diretorio dir = (Diretorio) diretorio; + // Verifica se é arquivo + Arquivo arquivo = dirOrigemPai.buscarArquivo(nomeOrigem); + if (arquivo != null) { + dirOrigemPai.removerArquivo(nomeOrigem); + arquivo.getMetaDados().setNome(nomeDestino); + dirDestinoPai.adicionarArquivo(arquivo); + return; + } - if (!dir.getDono().equals(usuario) && !usuario.equals(ROOT_USER)) { - if (!dir.temPermissao(usuario, 'w')) { - throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); - } - } + // Verifica se é diretório + Diretorio subdir = dirOrigemPai.buscarSubdiretorio(nomeOrigem); + if (subdir != null) { + dirOrigemPai.removerSubdiretorio(nomeOrigem); + subdir.getMetaDados().setNome(nomeDestino); + dirDestinoPai.adicionarSubdiretorio(subdir); + return; + } - List blocos = new ArrayList<>(); - Arquivo novoArquivo = new Arquivo(nomeArquivo, "rw-", usuario, blocos, 0); + // Se não encontrou nada + throw new CaminhoNaoEncontradoException("Caminho de origem '" + caminhoAntigo + "' não encontrado."); + } - arquivos.put(caminho, novoArquivo); + @Override + public void ls(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { + verificaUsuarioValido(usuario); - final int TAMANHO_BLOCO = 4096; + // Verifica se o caminho é válido + if (caminho == null || caminho.isEmpty() || !caminho.startsWith("/")) { + throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); + } - for (int i = 0; i < buffer.length; i += TAMANHO_BLOCO) { - int tamanhoAtual = Math.min(TAMANHO_BLOCO, buffer.length - i); - byte[] dadosBloco = new byte[tamanhoAtual]; - System.arraycopy(buffer, i, dadosBloco, 0, tamanhoAtual); + verificarPermissaoExecucaoNoCaminho(caminho, usuario); - Bloco bloco = new Bloco(); - bloco.setBytes(dadosBloco); - novoArquivo.adicionarBloco(bloco); - } + Diretorio dir = caminho.equals("/") ? fileSys.getRaiz() : navegarParaDiretorioCompleto(caminho); - } catch (CaminhoNaoEncontradoException ex) { - throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + diretorioPai); + verificarPermissao(caminho, usuario, 'r'); + lsDiretorio(dir, caminho.equals("/") ? "/" : dir.getMetaDados().getNome(), recursivo, ""); + } + + private void lsDiretorio(Diretorio dir, String nome, boolean recursivo, String prefixo) { + System.out.println(prefixo + nome + ":"); + + for (Arquivo arq : dir.getArquivos()) { + System.out.println(prefixo + " " + arq.getMetaDados().getNome()); + } + + for (Diretorio sub : dir.getSubdiretorios()) { + System.out.println(prefixo + " " + sub.getMetaDados().getNome() + "/"); + } + // Se recursivo, entra nos subdiretorios + if (recursivo) { + for (Diretorio sub : dir.getSubdiretorios()) { + lsDiretorio(sub, sub.getMetaDados().getNome(), true, prefixo + " "); } } } - @Override - public void read(String caminho, String usuario, byte[] buffer) + @Override + public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - if (buffer == null) { - throw new IllegalArgumentException("Buffer não pode ser nulo"); - } - if (!arquivos.containsKey(caminho)) { - throw new CaminhoNaoEncontradoException("Arquivo não encontrado: " + caminho); - } + verificaUsuarioValido(usuario); - Arquivo arquivo = arquivos.get(caminho); + verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminhoOrigem), usuario); + verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminhoDestino), usuario); - if (!usuario.equals(ROOT_USER) && !arquivo.getDono().equals(usuario)) { - char permissaoRequerida = 'w'; - String permissoes = arquivo.getPermissoesBasicas(); - if (permissoes.indexOf(permissaoRequerida) == -1) { - throw new PermissaoException("Usuário: " + usuario + " não tem permissão de escrita em: " + caminho); - } -} + // Localiza origem e destino + Diretorio dirOrigemPai = navegarParaDiretorioPai(caminhoOrigem); + Diretorio dirDestino = navegarParaDiretorioPai(caminhoDestino); - List blocos = arquivo.getAllBlocos(); - int offset = 0; + String nomeOrigem = extrairNomeFinal(caminhoOrigem); + String nomeDestino = extrairNomeFinal(caminhoDestino); - for (Bloco bloco : blocos) { - byte[] dados = bloco.getDados(); - int tamanhoACopiar = Math.min(dados.length, buffer.length - offset); + // Verifica se é arquivo + Arquivo arquivoOrigem = dirOrigemPai.buscarArquivo(nomeOrigem); + if (arquivoOrigem != null) { + if (!arquivoOrigem.getMetaDados().temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura para o arquivo."); + } + + Arquivo copia = new Arquivo(nomeDestino, usuario); + for (Bloco bloco : arquivoOrigem.getBlocos()) { + Bloco novoBloco = new Bloco(bloco.getDados().length); + novoBloco.setDados(bloco.getDados().clone()); + copia.adicionarBloco(novoBloco); + } + + dirDestino.adicionarArquivo(copia); + return; + } - if (tamanhoACopiar <= 0) break; + // Verifica se é diretório + Diretorio diretorioOrigem = dirOrigemPai.buscarSubdiretorio(nomeOrigem); + if (diretorioOrigem != null) { + if (!diretorioOrigem.getMetaDados().temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura para o diretório."); + } + if (!recursivo) { + throw new PermissaoException("Cópia de diretório requer flag recursiva."); + } - System.arraycopy(dados, 0, buffer, offset, tamanhoACopiar); - offset += tamanhoACopiar; + Diretorio copia = copiarDiretorioRecursivo(diretorioOrigem, usuario); + copia.getMetaDados().setNome(nomeDestino); + dirDestino.adicionarSubdiretorio(copia); + return; } + + throw new CaminhoNaoEncontradoException("Origem não encontrada."); } - @Override - public void mv(String caminhoAntigo, String caminhoNovo, String usuario) - throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mv'"); + public void addUser(String nome, String permissao) { + if (!usuarios.containsKey(nome)) { + Usuario novo = new Usuario(nome, permissao); + usuarios.put(nome, novo); + } } - @Override - public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'ls'"); + public void removeUser(String nome) { + usuarios.remove(nome); } - @Override -public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) - throws CaminhoNaoEncontradoException, PermissaoException { - if (arquivos.containsKey(caminhoOrigem)) { - Arquivo arquivoOrigem = arquivos.get(caminhoOrigem); - - if (!usuario.equals(ROOT_USER) && !arquivoOrigem.getDono().equals(usuario)) { - String permissoesOrigem = arquivoOrigem.getPermissoesBasicas(); - if (permissoesOrigem.indexOf('r') == -1) { - throw new PermissaoException("Usuário '" + usuario + - "' não tem permissão de leitura no arquivo de origem: " + caminhoOrigem); - } + // Navega para o diretorio pai do caminho passado como parametro + // Ex: "/home/user/docs" retorna "/home/user" + private Diretorio navegarParaDiretorioPai(String caminho) throws CaminhoNaoEncontradoException { + if (caminho.equals("/")) { + return fileSys.getRaiz(); + } + String[] partes = caminho.split("/"); + if (!partes[0].isEmpty()) { + throw new IllegalArgumentException("Caminho inválido: deve começar com '/'"); } - int tamanhoTotal = 0; - for (Bloco bloco : arquivoOrigem.getAllBlocos()) { - tamanhoTotal += bloco.getDados().length; + Diretorio atual = fileSys.getRaiz(); + for (int i = 1; i < partes.length - 1; i++) { + if (partes[i].isEmpty()) + continue; + Diretorio encontrado = atual.buscarSubdiretorio(partes[i]); + if (encontrado == null) { + throw new CaminhoNaoEncontradoException("Diretório '" + partes[i] + "' não encontrado."); + } + atual = encontrado; } + return atual; + } - byte[] buffer = new byte[tamanhoTotal]; - read(caminhoOrigem, usuario, buffer); + // Navega para o diretorio completo do caminho passado como parametro + // Ex: "/home/user/docs" retorna o diretorio "docs" + private Diretorio navegarParaDiretorioCompleto(String caminho) throws CaminhoNaoEncontradoException { + String[] partes = caminho.split("/"); + if (!partes[0].isEmpty()) { + throw new IllegalArgumentException("Caminho inválido: deve começar com '/'"); + } - write(caminhoDestino, usuario, false, buffer); + Diretorio atual = fileSys.getRaiz(); + for (int i = 1; i < partes.length; i++) { + if (partes[i].isEmpty()) + continue; + Diretorio encontrado = atual.buscarSubdiretorio(partes[i]); + if (encontrado == null) { + throw new CaminhoNaoEncontradoException("Diretório '" + partes[i] + "' não encontrado."); + } + atual = encontrado; + } + return atual; + } - return; + // Extrai o nome final do caminho, que pode ser um arquivo ou diretório + // Ex: "/home/user/docs" retorna "docs" + private String extrairNomeFinal(String caminho) { + if (caminho == null || caminho.isEmpty()) { + throw new IllegalArgumentException("Caminho inválido: não pode ser nulo ou vazio."); + } + if (caminho.equals("/")) { + return "/"; + } + String[] partes = caminho.split("/"); + return partes[partes.length - 1]; } - try { - MetaDados metadados = navegar(caminhoOrigem); + // Obtém o caminho pai do caminho fornecido + // Ex: "/home/user/docs" retorna "/home/user" + private String obterCaminhoPai(String caminho) { + int ultimoSlash = caminho.lastIndexOf('/'); + if (ultimoSlash == 0) + return "/"; + return caminho.substring(0, ultimoSlash); + } - if (metadados instanceof Diretorio) { - if (!recursivo) { - throw new IllegalArgumentException( - "Para copiar um diretório, é necessário usar o modo recursivo"); - } + private void verificaUsuarioValido(String usuario) throws PermissaoException { + if (!usuarios.containsKey(usuario)) { + throw new PermissaoException("Usuário '" + usuario + "' não encontrado."); + } + } - Diretorio dirOrigem = (Diretorio) metadados; + private void verificarPermissaoExecucaoNoCaminho(String caminho, String usuario) + throws PermissaoException, CaminhoNaoEncontradoException { + if (usuario.equals(ROOT_USER)) + return; - if (!usuario.equals(ROOT_USER) && !dirOrigem.getDono().equals(usuario)) { - if (!dirOrigem.temPermissao(usuario, 'r')) { - throw new PermissaoException("Usuário '" + usuario + - "' não tem permissão de leitura no diretório: " + caminhoOrigem); - } + // Trata casos especiais de caminho vazio ou "/" + if (caminho == null || caminho.isEmpty() || caminho.equals("/")) { + if (!temPermissao("/", usuario, 'x')) { + throw new PermissaoException( + "Usuário '" + usuario + "' não tem permissão 'x' em '/'"); } + return; + } - try { - mkdir(obterDiretorioPai(caminhoDestino), obterNomeArquivo(caminhoDestino)); - } catch (CaminhoJaExistenteException e) { + String[] partes = caminho.split("/"); + StringBuilder pathBuilder = new StringBuilder(); + for (int i = 1; i < partes.length; i++) { + if (partes[i].isEmpty()) + continue; + pathBuilder.append("/").append(partes[i]); + if (!temPermissao(pathBuilder.toString(), usuario, 'x')) { + throw new PermissaoException( + "Usuário '" + usuario + "' não tem permissão 'x' em '" + pathBuilder + "'"); } + } + } - for (String nomeArquivo : arquivos.keySet()) { - if (nomeArquivo.startsWith(caminhoOrigem + "/")) { - String relativePath = nomeArquivo.substring(caminhoOrigem.length()); - String novoDestino = caminhoDestino + relativePath; + private void verificarPermissao(String caminho, String usuario, char tipo) throws PermissaoException { + if (!temPermissao(caminho, usuario, tipo)) { + throw new PermissaoException( + "Usuário '" + usuario + "' não tem permissão '" + tipo + "' em '" + caminho + "'"); + } + } - Arquivo arquivoOrigem = arquivos.get(nomeArquivo); - int tamanhoTotal = 0; - for (Bloco bloco : arquivoOrigem.getAllBlocos()) { - tamanhoTotal += bloco.getDados().length; - } + private boolean temPermissao(String caminho, String usuario, char tipo) { + if (usuario.equals(ROOT_USER)) + return true; // root sempre tem permissão - byte[] buffer = new byte[tamanhoTotal]; - read(nomeArquivo, usuario, buffer); + try { + Diretorio pai = navegarParaDiretorioPai(caminho); + String nome = extrairNomeFinal(caminho); - write(novoDestino, usuario, false, buffer); + Arquivo arq = pai.buscarArquivo(nome); + if (arq != null) { + if (arq.getMetaDados().temPermissao(usuario, tipo)) { + return true; } } - - for (String filhoNome : dirOrigem.getFilhos().keySet()) { - String filhoCaminhoOrigem = caminhoOrigem.equals("/") ? - "/" + filhoNome : caminhoOrigem + "/" + filhoNome; - String filhoCaminhoDestino = caminhoDestino.equals("/") ? - "/" + filhoNome : caminhoDestino + "/" + filhoNome; - - cp(filhoCaminhoOrigem, filhoCaminhoDestino, usuario, true); + Diretorio dir = pai.buscarSubdiretorio(nome); + if (dir != null) { + if (dir.getMetaDados().temPermissao(usuario, tipo)) { + return true; + } } - } else { - throw new IllegalArgumentException("O caminho de origem não é um arquivo nem um diretório: " + caminhoOrigem); + // Se for o diretório raiz + if (caminho.equals("/")) { + if (fileSys.getRaiz().getMetaDados().temPermissao(usuario, tipo)) { + return true; + } + } + } catch (Exception e) { + // ignora, vai checar permissao global do usuario } - } catch (CaminhoNaoEncontradoException e) { - throw e; + + // Checa permissao global do usuario (definida no arquivo users) + Usuario userObj = usuarios.get(usuario); + if (userObj != null) { + String perm = userObj.getPermissaoParaCaminho(caminho); + return perm.indexOf(tipo) != -1; + } + return false; } -} - public void addUser(String user) { - throw new UnsupportedOperationException("Método não implementado 'addUser'"); + private void removerDiretorioRecursivamente(Diretorio dir) { + // Remove arquivos do diretório + for (Arquivo arquivo : new ArrayList<>(dir.getArquivos())) { + dir.removerArquivo(arquivo.getMetaDados().getNome()); + } + + // Remove subdiretórios recursivamente + for (Diretorio subdir : new ArrayList<>(dir.getSubdiretorios())) { + removerDiretorioRecursivamente(subdir); + dir.removerSubdiretorio(subdir.getMetaDados().getNome()); + } } - private void validarPermissao(String permissao) { - if (permissao == null || permissao.length() != 3) - throw new IllegalArgumentException("Permissão inválida"); - for (char c : permissao.toCharArray()) { - if ("rwxn".indexOf(c) == -1) - throw new IllegalArgumentException("Permissão inválida"); + /* + * Escreve o conteúdo do buffer em blocos do arquivo + * Se o buffer for maior que o tamanho do bloco, + * ele será dividido em blocos de tamanho fixo. + */ + private void escreverBufferEmBlocos(Arquivo arquivo, byte[] buffer) { + int blocoTamanho = Arquivo.TAMANHO_BLOCO; + int offset = 0; + while (offset < buffer.length) { + int len = Math.min(blocoTamanho, buffer.length - offset); + byte[] dados = new byte[len]; + System.arraycopy(buffer, offset, dados, 0, len); + Bloco bloco = new Bloco(len); + bloco.setDados(dados); + arquivo.adicionarBloco(bloco); + offset += len; } } - private String obterDiretorioPai(String caminho) { - if (caminho.equals("/")) return "/"; + /* + * Lê o conteúdo do arquivo a partir de um offset mutável. + * O método deve atualizar o offset conforme lê bytes. + * Se o offset for maior que o tamanho do arquivo, retorna 0. + */ + private int lerArquivoComOffset(Arquivo arquivo, byte[] buffer, Offset offset) { + int arquivoTamanho = arquivo.getMetaDados().getTamanho(); + int posicao = offset.getValue(); + + // Zera o buffer antes de preencher + for (int i = 0; i < buffer.length; i++) + buffer[i] = 0; - int ultimaBarra = caminho.lastIndexOf('/'); - if (ultimaBarra == 0) return "/"; - return caminho.substring(0, ultimaBarra); - } + if (posicao >= arquivoTamanho) + return 0; + + int bytesParaLer = Math.min(buffer.length, arquivoTamanho - posicao); + + int blocoTamanho = Arquivo.TAMANHO_BLOCO; + int blocoInicial = posicao / blocoTamanho; + int posNoBloco = posicao % blocoTamanho; - private String obterNomeArquivo(String caminho) { - if (caminho.equals("/")) return ""; + int bufferPos = 0; + int bytesRestantes = bytesParaLer; - int ultimaBarra = caminho.lastIndexOf('/'); - return caminho.substring(ultimaBarra + 1); + for (int i = blocoInicial; i < arquivo.getBlocos().size() && bytesRestantes > 0; i++) { + Bloco bloco = arquivo.getBlocos().get(i); + byte[] dados = bloco.getDados(); + int start = (i == blocoInicial) ? posNoBloco : 0; + int len = Math.min(dados.length - start, bytesRestantes); + System.arraycopy(dados, start, buffer, bufferPos, len); + bufferPos += len; + bytesRestantes -= len; + } + + offset.add(bytesParaLer); // atualiza o offset para a proxima leitura + return bytesParaLer; } + private Diretorio copiarDiretorioRecursivo(Diretorio original, String usuario) throws PermissaoException { + if (!original.getMetaDados().temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão para copiar diretório " + original.getMetaDados().getNome()); + } + + Diretorio copia = new Diretorio(original.getMetaDados().getNome(), usuario); + + for (Arquivo a : original.getArquivos()) { + if (!a.getMetaDados().temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão para copiar arquivo " + a.getMetaDados().getNome()); + } + + Arquivo novo = new Arquivo(a.getMetaDados().getNome(), usuario); + for (Bloco bloco : a.getBlocos()) { + Bloco b = new Bloco(bloco.getDados().length); + b.setDados(bloco.getDados().clone()); + novo.adicionarBloco(b); + } + copia.adicionarArquivo(novo); + } + + for (Diretorio sub : original.getSubdiretorios()) { + Diretorio subCopia = copiarDiretorioRecursivo(sub, usuario); + copia.adicionarSubdiretorio(subCopia); + } + + return copia; + } } diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index a0f0ce0..e1cfdeb 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -14,14 +14,14 @@ public interface IFileSystem { // Cria um novo diretório. Se o diretório já existir, será lançada uma exceção. // Se o usuario não tiver permissão para criar o diretório, será lançada uma exceção. - void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException; + void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException; // Remove um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. // Caso recursivo seja true, o diretório será removido recursivamente. void rm(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException; // Cria um novo arquivo. Atenção: Se o arquivo já existir, será lançada uma exceção. - void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException; + void touch(String caminho, String usuario) throws CaminhoJaExistenteException, CaminhoNaoEncontradoException, PermissaoException; // Escreve dados em um arquivo. Se o diretório não existir, será lançada uma exceção. // Caso o diretório exista, o arquivo será criado ou sobrescrito. @@ -31,12 +31,14 @@ 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; + // Lê dados de um arquivo a partir de um offset mutável. + // O método deve atualizar o offset conforme lê bytes. + 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. // mv é naturalmente recursivo. - void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException; + void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException,CaminhoJaExistenteException; // 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. diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java index 8e58e84..e9fe147 100644 --- a/filesys/MetaDados.java +++ b/filesys/MetaDados.java @@ -1,37 +1,50 @@ package filesys; -/*classe base que tem o nome, permissões básicas e o dono, tanto Diretorio quanto Arquivo a implementam */ -public abstract class MetaDados { - - protected String nome; - protected String permissoesBasicas; - protected String dono; +import java.util.HashMap; - public String getNome() { - return nome; - } - public void setNome(String nome) { +public class MetaDados { + private String nome; + private int tamanho; + private String dono; + private HashMap permissoes; + + public MetaDados(String nome, String dono) { this.nome = nome; + this.dono = dono; + this.tamanho = 0; + this.permissoes = new HashMap<>(); + this.permissoes.put(dono, "rwx"); } - public String getPermissoesBasicas() { - return permissoesBasicas; + + public String getNome() { + return nome; } - public void setPermissoesBasicas(String permissoesBasicas) { - this.permissoesBasicas = permissoesBasicas; + + public int getTamanho() { + return tamanho; } + public String getDono() { return dono; } - public void setDono(String dono) { - this.dono = dono; + + public void setTamanho(int tamanho) { + this.tamanho = tamanho; + } + + public void setPermissao(String usuario, String perm) { + permissoes.put(usuario, perm); + } + + public String getPermissao(String usuario) { + return permissoes.getOrDefault(usuario, "---"); + } + + public boolean temPermissao(String usuario, char tipo) { + return getPermissao(usuario).indexOf(tipo) != -1; } - public MetaDados(String nome, String permissoesBasicas, String dono) { - setNome(nome); - setPermissoesBasicas(permissoesBasicas); - setDono(dono); + public void setNome(String nomeDestino) { + this.nome = nomeDestino; } - - public abstract void alterarPermissao(String usuarioAlvo, String permissao); - } diff --git a/filesys/Offset.java b/filesys/Offset.java new file mode 100644 index 0000000..cd315f4 --- /dev/null +++ b/filesys/Offset.java @@ -0,0 +1,21 @@ +package filesys; + +public class Offset { + private int value; + + public Offset(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + public void add(int delta) { + value += delta; + } +} \ No newline at end of file diff --git a/filesys/Usuario.java b/filesys/Usuario.java index 648b5a2..e19af02 100644 --- a/filesys/Usuario.java +++ b/filesys/Usuario.java @@ -1,40 +1,78 @@ package filesys; +import java.util.HashMap; +import java.util.Map; + public class Usuario { - private String nome; - private String diretorio; - private String permissao; + private final String nome; + private final Map permissoesPorCaminho; // ex: "/**" -> "rwx" - public String getNome() { - return nome; + public Usuario(String nome, String permissao) { + this.nome = nome; + this.permissoesPorCaminho = new HashMap<>(); + this.permissoesPorCaminho.put("**", permissao); } - public void setNome(String nome) { + + public Usuario(String nome) { this.nome = nome; + this.permissoesPorCaminho = new HashMap<>(); } - public String getDiretorio() { - return diretorio; + + public void adicionarPermissao(String caminho, String permissao) { + permissoesPorCaminho.put(caminho, permissao); } - public void setDiretorio(String diretorio) { - this.diretorio = diretorio; + + public String getPermissaoParaCaminho(String caminho) { + String melhorPermissao = "---"; + int melhorTamanho = -1; + for (Map.Entry entry : permissoesPorCaminho.entrySet()) { + String padrao = entry.getKey(); + if (caminhoMatches(padrao, caminho) && padrao.length() > melhorTamanho) { + melhorPermissao = entry.getValue(); + melhorTamanho = padrao.length(); + } + } + return melhorPermissao; } - public String getPermissao() { - return permissao; + + private boolean caminhoMatches(String padrao, String caminho) { + // Aceita qualquer subcaminho + if (padrao.endsWith("/**")) { + String base = padrao.substring(0, padrao.length() - 3); + // Só casa se for um subcaminho, não o próprio diretório base + return !caminho.equals(base) && caminho.startsWith(base + "/"); + } + // Aceita apenas filhos diretos + if (padrao.endsWith("/*")) { + String base = padrao.substring(0, padrao.length() - 2); + if (!caminho.startsWith(base + "/")) + return false; + String resto = caminho.substring(base.length() + 1); + return !resto.isEmpty() && !resto.contains("/"); + } + return padrao.equals(caminho); } - public void setPermissao(String permissao) { - this.permissao = permissao; + + public String getNome() { + return nome; } - - public Usuario(String nome, String diretorio, String permissao) { - setDiretorio(diretorio); - setNome(nome); - setPermissao(permissao); + + public boolean podeLer() { + String permissao = permissoesPorCaminho.get("**"); + return permissao != null && permissao.contains("r"); } - @Override - public String toString(){ - return("Nome: "+ nome +"\n" - + "permissão: " + permissao + "\n" - + "diretorio: " + diretorio + "\n"); + public boolean podeEscrever() { + String permissao = permissoesPorCaminho.get("**"); + return permissao != null && permissao.contains("w"); + } + + public boolean podeExecutar() { + String permissao = permissoesPorCaminho.get("**"); + return permissao != null && permissao.contains("x"); + } + + public String getPermissao() { + return permissoesPorCaminho.get("**"); } - } From e4a5fb1ae2702b77bb52ee98681f21f73675fa1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BAlio=20Henrique?= Date: Wed, 18 Jun 2025 00:25:51 -0300 Subject: [PATCH 23/30] =?UTF-8?q?corre=C3=A7=C3=A3o=20arquivo/diretorio/me?= =?UTF-8?q?tadados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/Arquivo.java | 7 +++++-- filesys/Diretorio.java | 36 +++++++++++++++++++++--------------- filesys/FileSystemImpl.java | 24 ++++++++++++------------ filesys/MetaDados.java | 2 +- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java index 3acb55a..120ea18 100644 --- a/filesys/Arquivo.java +++ b/filesys/Arquivo.java @@ -22,7 +22,10 @@ public List getBlocos() { } public void adicionarBloco(Bloco bloco) { - blocos.add(bloco); - metaDados.setTamanho(metaDados.getTamanho() + bloco.getDados().length); + if (bloco != null && bloco.getDados() != null) { + this.blocos.add(bloco); + int tamanhoBloco = bloco.getDados().length; + this.metaDados.setTamanho(this.metaDados.getTamanho() + tamanhoBloco); + } } } diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java index 77ac1b6..524c91b 100644 --- a/filesys/Diretorio.java +++ b/filesys/Diretorio.java @@ -26,32 +26,38 @@ public List getSubdiretorios() { return subdiretorios; } - public void adicionarArquivo(Arquivo a) { - arquivos.add(a); + public void adicionarArquivo(Arquivo arquivo) { + if (arquivo != null) { + this.arquivos.add(arquivo); + } } - public void adicionarSubdiretorio(Diretorio d) { - subdiretorios.add(d); + public void adicionarSubdiretorio(Diretorio diretorio) { + if (diretorio != null) { + this.subdiretorios.add(diretorio); + } } public Diretorio buscarSubdiretorio(String nome) { - return subdiretorios.stream() - .filter(d -> d.getMetaDados().getNome().equals(nome)) - .findFirst().orElse(null); + return this.subdiretorios.stream() + .filter(d -> nome.equals(d.getMetaDados().getNome())) + .findFirst() + .orElse(null); } public Arquivo buscarArquivo(String nome) { - return arquivos.stream() - .filter(a -> a.getMetaDados().getNome().equals(nome)) - .findFirst().orElse(null); + return this.arquivos.stream() + .filter(a -> nome.equals(a.getMetaDados().getNome())) + .findFirst() + .orElse(null); } - + public void removerArquivo(String nome) { - arquivos.removeIf(a -> a.getMetaDados().getNome().equals(nome)); + this.arquivos.removeIf(a -> nome.equals(a.getMetaDados().getNome())); } - + public void removerSubdiretorio(String nome) { - subdiretorios.removeIf(d -> d.getMetaDados().getNome().equals(nome)); + this.subdiretorios.removeIf(d -> nome.equals(d.getMetaDados().getNome())); } - + } diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index cb9cf0d..7af1656 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -72,7 +72,7 @@ public void chmod(String caminho, String usuario, String usuarioAlvo, String per // Só root ou quem tem permissão rw pode alterar permissoes if (!usuario.equals(ROOT_USER) - && !(temPermissao(caminho, usuario, 'r') && temPermissao(caminho, usuario, 'w'))) { + && !(checkPermissao(caminho, usuario, 'r') && checkPermissao(caminho, usuario, 'w'))) { throw new PermissaoException( "Usuário '" + usuario + "' não tem permissão para alterar permissões em '" + caminho + "'"); } @@ -305,7 +305,7 @@ public void cp(String caminhoOrigem, String caminhoDestino, String usuario, bool // Verifica se é arquivo Arquivo arquivoOrigem = dirOrigemPai.buscarArquivo(nomeOrigem); if (arquivoOrigem != null) { - if (!arquivoOrigem.getMetaDados().temPermissao(usuario, 'r')) { + if (!arquivoOrigem.getMetaDados().checkPermissao(usuario, 'r')) { throw new PermissaoException("Sem permissão de leitura para o arquivo."); } @@ -323,7 +323,7 @@ public void cp(String caminhoOrigem, String caminhoDestino, String usuario, bool // Verifica se é diretório Diretorio diretorioOrigem = dirOrigemPai.buscarSubdiretorio(nomeOrigem); if (diretorioOrigem != null) { - if (!diretorioOrigem.getMetaDados().temPermissao(usuario, 'r')) { + if (!diretorioOrigem.getMetaDados().checkPermissao(usuario, 'r')) { throw new PermissaoException("Sem permissão de leitura para o diretório."); } if (!recursivo) { @@ -430,7 +430,7 @@ private void verificarPermissaoExecucaoNoCaminho(String caminho, String usuario) // Trata casos especiais de caminho vazio ou "/" if (caminho == null || caminho.isEmpty() || caminho.equals("/")) { - if (!temPermissao("/", usuario, 'x')) { + if (!checkPermissao("/", usuario, 'x')) { throw new PermissaoException( "Usuário '" + usuario + "' não tem permissão 'x' em '/'"); } @@ -443,7 +443,7 @@ private void verificarPermissaoExecucaoNoCaminho(String caminho, String usuario) if (partes[i].isEmpty()) continue; pathBuilder.append("/").append(partes[i]); - if (!temPermissao(pathBuilder.toString(), usuario, 'x')) { + if (!checkPermissao(pathBuilder.toString(), usuario, 'x')) { throw new PermissaoException( "Usuário '" + usuario + "' não tem permissão 'x' em '" + pathBuilder + "'"); } @@ -451,13 +451,13 @@ private void verificarPermissaoExecucaoNoCaminho(String caminho, String usuario) } private void verificarPermissao(String caminho, String usuario, char tipo) throws PermissaoException { - if (!temPermissao(caminho, usuario, tipo)) { + if (!checkPermissao(caminho, usuario, tipo)) { throw new PermissaoException( "Usuário '" + usuario + "' não tem permissão '" + tipo + "' em '" + caminho + "'"); } } - private boolean temPermissao(String caminho, String usuario, char tipo) { + private boolean checkPermissao(String caminho, String usuario, char tipo) { if (usuario.equals(ROOT_USER)) return true; // root sempre tem permissão @@ -467,19 +467,19 @@ private boolean temPermissao(String caminho, String usuario, char tipo) { Arquivo arq = pai.buscarArquivo(nome); if (arq != null) { - if (arq.getMetaDados().temPermissao(usuario, tipo)) { + if (arq.getMetaDados().checkPermissao(usuario, tipo)) { return true; } } Diretorio dir = pai.buscarSubdiretorio(nome); if (dir != null) { - if (dir.getMetaDados().temPermissao(usuario, tipo)) { + if (dir.getMetaDados().checkPermissao(usuario, tipo)) { return true; } } // Se for o diretório raiz if (caminho.equals("/")) { - if (fileSys.getRaiz().getMetaDados().temPermissao(usuario, tipo)) { + if (fileSys.getRaiz().getMetaDados().checkPermissao(usuario, tipo)) { return true; } } @@ -568,14 +568,14 @@ private int lerArquivoComOffset(Arquivo arquivo, byte[] buffer, Offset offset) { } private Diretorio copiarDiretorioRecursivo(Diretorio original, String usuario) throws PermissaoException { - if (!original.getMetaDados().temPermissao(usuario, 'r')) { + if (!original.getMetaDados().checkPermissao(usuario, 'r')) { throw new PermissaoException("Sem permissão para copiar diretório " + original.getMetaDados().getNome()); } Diretorio copia = new Diretorio(original.getMetaDados().getNome(), usuario); for (Arquivo a : original.getArquivos()) { - if (!a.getMetaDados().temPermissao(usuario, 'r')) { + if (!a.getMetaDados().checkPermissao(usuario, 'r')) { throw new PermissaoException("Sem permissão para copiar arquivo " + a.getMetaDados().getNome()); } diff --git a/filesys/MetaDados.java b/filesys/MetaDados.java index e9fe147..21ae1de 100644 --- a/filesys/MetaDados.java +++ b/filesys/MetaDados.java @@ -40,7 +40,7 @@ public String getPermissao(String usuario) { return permissoes.getOrDefault(usuario, "---"); } - public boolean temPermissao(String usuario, char tipo) { + public boolean checkPermissao(String usuario, char tipo) { return getPermissao(usuario).indexOf(tipo) != -1; } From 08f2664f296e2a540067f262c4a8bf30bf1cb595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BAlio=20Henrique?= Date: Wed, 18 Jun 2025 00:32:42 -0300 Subject: [PATCH 24/30] organizacao de arquivos --- Main.java | 2 +- filesys/FileSys.java | 2 ++ filesys/FileSystem.java | 1 + filesys/FileSystemImpl.java | 4 ++++ filesys/IFileSystem.java | 1 + filesys/{ => core}/Arquivo.java | 2 +- filesys/{ => core}/Bloco.java | 2 +- filesys/{ => core}/Diretorio.java | 2 +- filesys/{ => core}/MetaDados.java | 2 +- filesys/{ => core}/Offset.java | 2 +- 10 files changed, 14 insertions(+), 6 deletions(-) rename filesys/{ => core}/Arquivo.java (97%) rename filesys/{ => core}/Bloco.java (92%) rename filesys/{ => core}/Diretorio.java (98%) rename filesys/{ => core}/MetaDados.java (98%) rename filesys/{ => core}/Offset.java (93%) diff --git a/Main.java b/Main.java index 851c381..7f623c8 100644 --- a/Main.java +++ b/Main.java @@ -1,6 +1,6 @@ import filesys.IFileSystem; -import filesys.Offset; import filesys.Usuario; +import filesys.core.Offset; import java.util.HashMap; import java.util.Map; diff --git a/filesys/FileSys.java b/filesys/FileSys.java index 532b8fb..ace1063 100644 --- a/filesys/FileSys.java +++ b/filesys/FileSys.java @@ -1,5 +1,7 @@ package filesys; +import filesys.core.Diretorio; + public class FileSys { private Diretorio raiz; diff --git a/filesys/FileSystem.java b/filesys/FileSystem.java index e8a8398..9eaf637 100644 --- a/filesys/FileSystem.java +++ b/filesys/FileSystem.java @@ -5,6 +5,7 @@ import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; +import filesys.core.Offset; // Essa classe deve servir apenas como proxy para o FileSystemImpl final public class FileSystem implements IFileSystem { diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 7af1656..18d9ee4 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -6,6 +6,10 @@ import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; +import filesys.core.Arquivo; +import filesys.core.Bloco; +import filesys.core.Diretorio; +import filesys.core.Offset; // Implemente nesta classe o seu código do FileSystem. // A classe pode ser alterada. diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index e1cfdeb..48da697 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -3,6 +3,7 @@ import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; +import filesys.core.Offset; // Apenas modifique essa interface caso seja EXTREMAMENTE necessário. // Documente & Justifique TODAS asalterações feitas. diff --git a/filesys/Arquivo.java b/filesys/core/Arquivo.java similarity index 97% rename from filesys/Arquivo.java rename to filesys/core/Arquivo.java index 120ea18..41e0f7a 100644 --- a/filesys/Arquivo.java +++ b/filesys/core/Arquivo.java @@ -1,4 +1,4 @@ -package filesys; +package filesys.core; import java.util.ArrayList; import java.util.List; diff --git a/filesys/Bloco.java b/filesys/core/Bloco.java similarity index 92% rename from filesys/Bloco.java rename to filesys/core/Bloco.java index be11773..a8cd0de 100644 --- a/filesys/Bloco.java +++ b/filesys/core/Bloco.java @@ -1,4 +1,4 @@ -package filesys; +package filesys.core; public class Bloco { private byte[] dados; diff --git a/filesys/Diretorio.java b/filesys/core/Diretorio.java similarity index 98% rename from filesys/Diretorio.java rename to filesys/core/Diretorio.java index 524c91b..22ec155 100644 --- a/filesys/Diretorio.java +++ b/filesys/core/Diretorio.java @@ -1,4 +1,4 @@ -package filesys; +package filesys.core; import java.util.ArrayList; import java.util.List; diff --git a/filesys/MetaDados.java b/filesys/core/MetaDados.java similarity index 98% rename from filesys/MetaDados.java rename to filesys/core/MetaDados.java index 21ae1de..98a4cbe 100644 --- a/filesys/MetaDados.java +++ b/filesys/core/MetaDados.java @@ -1,4 +1,4 @@ -package filesys; +package filesys.core; import java.util.HashMap; diff --git a/filesys/Offset.java b/filesys/core/Offset.java similarity index 93% rename from filesys/Offset.java rename to filesys/core/Offset.java index cd315f4..817f2e6 100644 --- a/filesys/Offset.java +++ b/filesys/core/Offset.java @@ -1,4 +1,4 @@ -package filesys; +package filesys.core; public class Offset { private int value; From 099920991044dc9f3003c43763b02021acce7348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BAlio=20Henrique?= Date: Wed, 18 Jun 2025 19:15:18 -0300 Subject: [PATCH 25/30] revisao geral de codigo e modularizacao(tava com mta linha kkkk) --- filesys/FileSystemImpl.java | 458 +++++++++++++++++++----------------- filesys/core/MetaDados.java | 2 +- 2 files changed, 237 insertions(+), 223 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 18d9ee4..55de266 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -25,60 +25,50 @@ public FileSystemImpl(Map usuarios) { this.usuarios = usuarios; } - @Override + @Override // Necessário permissão wx public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { - verificaUsuarioValido(usuario); + checarUsuarioV(usuario); + verificarFormatoCaminho(caminho); if (caminho.equals("/")) return; String[] partes = caminho.split("/"); - if (!partes[0].isEmpty()) { - throw new IllegalArgumentException("Caminho inválido: deve começar com '/'"); - } - Diretorio atual = fileSys.getRaiz(); - StringBuilder pathBuilder = new StringBuilder(); + StringBuilder caminhoAtual = new StringBuilder(); + for (int i = 1; i < partes.length; i++) { if (partes[i].isEmpty()) continue; - pathBuilder.append("/").append(partes[i]); - String nomeDir = partes[i]; - Diretorio proximo = atual.buscarSubdiretorio(nomeDir); + caminhoAtual.append("/").append(partes[i]); + Diretorio proximo = atual.buscarSubdiretorio(partes[i]); boolean ultimo = (i == partes.length - 1); if (proximo == null) { - String paiPath = pathBuilder.substring(0, pathBuilder.lastIndexOf("/")); - if (paiPath.isEmpty()) - paiPath = "/"; - verificarPermissaoExecucaoNoCaminho(paiPath, usuario); - verificarPermissao(paiPath, usuario, 'w'); - Diretorio novo = new Diretorio(nomeDir, usuario); - atual.adicionarSubdiretorio(novo); - atual = novo; + String paiPath = obterCaminhoPai(caminhoAtual.toString()); + checarPermissaoExecucaoRecursiva(paiPath, usuario); + validarPermissao(paiPath, usuario, 'w'); + + atual = criarSubdiretorio(atual, partes[i], usuario); } else { - if (ultimo) { - throw new CaminhoJaExistenteException( - "O diretório '" + nomeDir + "' já existe em '" + caminho + "'"); - } + if (ultimo) + throw new CaminhoJaExistenteException("O diretório '" + partes[i] + "' já existe."); atual = proximo; } } } - @Override + @Override // Necessario permissão rw public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException { - verificaUsuarioValido(usuario); - verificaUsuarioValido(usuarioAlvo); + checarUsuarioV(usuario); + checarUsuarioV(usuarioAlvo); - // Só root ou quem tem permissão rw pode alterar permissoes - if (!usuario.equals(ROOT_USER) - && !(checkPermissao(caminho, usuario, 'r') && checkPermissao(caminho, usuario, 'w'))) { - throw new PermissaoException( - "Usuário '" + usuario + "' não tem permissão para alterar permissões em '" + caminho + "'"); + boolean isRoot = usuario.equals(ROOT_USER); + if (!isRoot && !(checarPermissao(caminho, usuario, 'r') && checarPermissao(caminho, usuario, 'w'))) { + throw new PermissaoException("Sem permissão para alterar permissões em " + caminho); } if (caminho.equals("/")) { @@ -87,33 +77,32 @@ public void chmod(String caminho, String usuario, String usuarioAlvo, String per } Diretorio pai = navegarParaDiretorioPai(caminho); - String nome = extrairNomeFinal(caminho); + String nome = caminho.substring(caminho.lastIndexOf('/') + 1); Arquivo arq = pai.buscarArquivo(nome); if (arq != null) { arq.getMetaDados().setPermissao(usuarioAlvo, permissao); return; } + Diretorio dir = pai.buscarSubdiretorio(nome); if (dir != null) { dir.getMetaDados().setPermissao(usuarioAlvo, permissao); return; } - throw new CaminhoNaoEncontradoException("Caminho '" + caminho + "' não encontrado."); + + throw new CaminhoNaoEncontradoException("Caminho " + caminho + " não encontrado."); } - @Override + @Override // Necessario permissão rw public void rm(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - verificaUsuarioValido(usuario); - - if (caminho.equals("/")) { + checarUsuarioV(usuario); + if (caminho.equals("/")) throw new PermissaoException("Não é permitido remover o diretório raiz."); - } - verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminho), usuario); - verificarPermissao(caminho, usuario, 'r'); - verificarPermissao(obterCaminhoPai(caminho), usuario, 'w'); + String paiPath = obterCaminhoPai(caminho); + validarPermissoesParaRemocao(caminho, paiPath, usuario); Diretorio pai = navegarParaDiretorioPai(caminho); String nome = extrairNomeFinal(caminho); @@ -125,224 +114,159 @@ public void rm(String caminho, String usuario, boolean recursivo) } Diretorio dir = pai.buscarSubdiretorio(nome); - if (dir == null) { + if (dir == null) throw new CaminhoNaoEncontradoException("Caminho '" + caminho + "' não encontrado."); - } - if (!recursivo && (!dir.getArquivos().isEmpty() || !dir.getSubdiretorios().isEmpty())) { + if (!recursivo && (!dir.getArquivos().isEmpty() || !dir.getSubdiretorios().isEmpty())) throw new PermissaoException("Diretório não vazio. Use o modo recursivo."); - } - if (recursivo) { + if (recursivo) removerDiretorioRecursivamente(dir); - } pai.removerSubdiretorio(nome); } - @Override - public void touch(String caminho, String usuario) - throws CaminhoJaExistenteException, CaminhoNaoEncontradoException, PermissaoException { - verificaUsuarioValido(usuario); + @Override // Necessario permissão wx +public void touch(String caminho, String usuario) + throws CaminhoJaExistenteException, CaminhoNaoEncontradoException, PermissaoException { + checarUsuarioV(usuario); - if (caminho.equals("/") || caminho.endsWith("/")) { - throw new CaminhoNaoEncontradoException("Caminho inválido para arquivo: " + caminho); - } + if (caminho.equals("/") || caminho.endsWith("/")) + throw new CaminhoNaoEncontradoException("Caminho inválido para arquivo: " + caminho); - verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminho), usuario); + String paiPath = obterCaminhoPai(caminho); + checarPermissaoExecucaoRecursiva(paiPath, usuario); - Diretorio pai = navegarParaDiretorioPai(caminho); - String nomeArquivo = extrairNomeFinal(caminho); - - if (pai.buscarArquivo(nomeArquivo) != null) { - throw new CaminhoJaExistenteException("O arquivo '" + nomeArquivo + "' já existe."); - } + Diretorio pai = navegarParaDiretorioPai(caminho); + String nomeArquivo = extrairNomeFinal(caminho); - verificarPermissao(obterCaminhoPai(caminho), usuario, 'w'); + if (pai.buscarArquivo(nomeArquivo) != null) + throw new CaminhoJaExistenteException("O arquivo '" + nomeArquivo + "' já existe."); - Arquivo novoArquivo = new Arquivo(nomeArquivo, usuario); - pai.adicionarArquivo(novoArquivo); - } - - @Override - public void write(String caminho, String usuario, boolean anexar, byte[] buffer) - throws CaminhoNaoEncontradoException, PermissaoException { - verificaUsuarioValido(usuario); - verificarPermissao(caminho, usuario, 'w'); - verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminho), usuario); + validarPermissao(paiPath, usuario, 'w'); - Diretorio pai = navegarParaDiretorioPai(caminho); - String nomeArquivo = extrairNomeFinal(caminho); - Arquivo arquivo = pai.buscarArquivo(nomeArquivo); + Arquivo novoArquivo = new Arquivo(nomeArquivo, usuario); + pai.adicionarArquivo(novoArquivo); +} - if (arquivo == null) { - throw new CaminhoNaoEncontradoException("Arquivo '" + nomeArquivo + "' não encontrado."); - } + @Override // Necessario permissão rw +public void write(String caminho, String usuario, boolean anexar, byte[] buffer) + throws CaminhoNaoEncontradoException, PermissaoException { + checarUsuarioV(usuario); + validarPermissao(caminho, usuario, 'w'); + checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminho), usuario); - if (!anexar) { - arquivo.getBlocos().clear(); - arquivo.getMetaDados().setTamanho(0); - } + Arquivo arquivo = obterArquivo(caminho); - escreverBufferEmBlocos(arquivo, buffer); + if (!anexar) { + limparArquivo(arquivo); } - @Override - public void read(String caminho, String usuario, byte[] buffer, Offset offset) - throws CaminhoNaoEncontradoException, PermissaoException { - verificaUsuarioValido(usuario); - verificarPermissao(caminho, usuario, 'r'); - verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminho), usuario); + escreverBufferEmBlocos(arquivo, buffer); +} - Diretorio pai = navegarParaDiretorioPai(caminho); - String nomeArquivo = extrairNomeFinal(caminho); - Arquivo arquivo = pai.buscarArquivo(nomeArquivo); + @Override // Necessario permissão r +public void read(String caminho, String usuario, byte[] buffer, Offset offset) + throws CaminhoNaoEncontradoException, PermissaoException { + checarUsuarioV(usuario); + validarPermissao(caminho, usuario, 'r'); + checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminho), usuario); - if (arquivo == null) { - throw new CaminhoNaoEncontradoException("Arquivo '" + nomeArquivo + "' não encontrado."); - } + Arquivo arquivo = obterArquivo(caminho); - lerArquivoComOffset(arquivo, buffer, offset); - } + lerArquivoComOffset(arquivo, buffer, offset); +} - @Override - public void mv(String caminhoAntigo, String caminhoNovo, String usuario) - throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { - verificaUsuarioValido(usuario); + @Override // Necessario permissão rw +public void mv(String caminhoAntigo, String caminhoNovo, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + checarUsuarioV(usuario); - if (caminhoAntigo.equals("/") || caminhoNovo.equals("/")) { - throw new CaminhoNaoEncontradoException("Não é possível mover o diretório raiz."); - } + if (caminhoAntigo.equals("/") || caminhoNovo.equals("/")) { + throw new CaminhoNaoEncontradoException("Não é possível mover o diretório raiz."); + } - verificarPermissaoExecucaoNoCaminho(caminhoAntigo, usuario); - verificarPermissaoExecucaoNoCaminho(caminhoNovo, usuario); + checarPermissaoExecucaoRecursiva(caminhoAntigo, usuario); + checarPermissaoExecucaoRecursiva(caminhoNovo, usuario); - verificarPermissao(caminhoAntigo, usuario, 'r'); - verificarPermissao(obterCaminhoPai(caminhoNovo), usuario, 'w'); - verificarPermissao(obterCaminhoPai(caminhoAntigo), usuario, 'w'); + validarPermissao(caminhoAntigo, usuario, 'r'); + validarPermissao(obterCaminhoPai(caminhoNovo), usuario, 'w'); + validarPermissao(obterCaminhoPai(caminhoAntigo), usuario, 'w'); - // Extrai nomes e diretórios pai - Diretorio dirOrigemPai = navegarParaDiretorioPai(caminhoAntigo); - Diretorio dirDestinoPai = navegarParaDiretorioPai(caminhoNovo); - String nomeOrigem = extrairNomeFinal(caminhoAntigo); - String nomeDestino = extrairNomeFinal(caminhoNovo); + Diretorio diretorioOrigemPai = navegarParaDiretorioPai(caminhoAntigo); + Diretorio diretorioDestinoPai = navegarParaDiretorioPai(caminhoNovo); - // Verifica se já existe algo no destino - if (dirDestinoPai.buscarArquivo(nomeDestino) != null || dirDestinoPai.buscarSubdiretorio(nomeDestino) != null) { - throw new CaminhoJaExistenteException("O destino '" + caminhoNovo + "' já existe."); - } + String nomeOrigem = extrairNomeFinal(caminhoAntigo); + String nomeDestino = extrairNomeFinal(caminhoNovo); - // Verifica se é arquivo - Arquivo arquivo = dirOrigemPai.buscarArquivo(nomeOrigem); - if (arquivo != null) { - dirOrigemPai.removerArquivo(nomeOrigem); - arquivo.getMetaDados().setNome(nomeDestino); - dirDestinoPai.adicionarArquivo(arquivo); - return; - } + checarExistenciaNoDestino(diretorioDestinoPai, nomeDestino, caminhoNovo); - // Verifica se é diretório - Diretorio subdir = dirOrigemPai.buscarSubdiretorio(nomeOrigem); - if (subdir != null) { - dirOrigemPai.removerSubdiretorio(nomeOrigem); - subdir.getMetaDados().setNome(nomeDestino); - dirDestinoPai.adicionarSubdiretorio(subdir); - return; - } + if (moverArquivoSeExistir(diretorioOrigemPai, diretorioDestinoPai, nomeOrigem, nomeDestino)) return; + if (moverDiretorioSeExistir(diretorioOrigemPai, diretorioDestinoPai, nomeOrigem, nomeDestino)) return; - // Se não encontrou nada - throw new CaminhoNaoEncontradoException("Caminho de origem '" + caminhoAntigo + "' não encontrado."); - } + throw new CaminhoNaoEncontradoException("Caminho de origem '" + caminhoAntigo + "' não encontrado."); +} - @Override - public void ls(String caminho, String usuario, boolean recursivo) - throws CaminhoNaoEncontradoException, PermissaoException { - verificaUsuarioValido(usuario); + @Override // Necessario permissão r + public void ls(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { + checarUsuarioV(usuario); + verificarCaminhoComPermissao(caminho); - // Verifica se o caminho é válido - if (caminho == null || caminho.isEmpty() || !caminho.startsWith("/")) { - throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); - } + checarPermissaoExecucaoRecursiva(caminho, usuario); + validarPermissao(caminho, usuario, 'r'); - verificarPermissaoExecucaoNoCaminho(caminho, usuario); - - Diretorio dir = caminho.equals("/") ? fileSys.getRaiz() : navegarParaDiretorioCompleto(caminho); + Diretorio dir = caminho.equals("/") ? fileSys.getRaiz() : navegarParaDiretorioCompleto(caminho); + imprimirConteudoDiretorio(dir, caminho, recursivo, ""); +} - verificarPermissao(caminho, usuario, 'r'); - lsDiretorio(dir, caminho.equals("/") ? "/" : dir.getMetaDados().getNome(), recursivo, ""); +private void verificarCaminhoComPermissao(String caminho) throws CaminhoNaoEncontradoException { + if (caminho == null || caminho.isEmpty() || !caminho.startsWith("/")) { + throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); } +} - private void lsDiretorio(Diretorio dir, String nome, boolean recursivo, String prefixo) { - System.out.println(prefixo + nome + ":"); - - for (Arquivo arq : dir.getArquivos()) { - System.out.println(prefixo + " " + arq.getMetaDados().getNome()); - } - - for (Diretorio sub : dir.getSubdiretorios()) { - System.out.println(prefixo + " " + sub.getMetaDados().getNome() + "/"); - } - // Se recursivo, entra nos subdiretorios - if (recursivo) { - for (Diretorio sub : dir.getSubdiretorios()) { - lsDiretorio(sub, sub.getMetaDados().getNome(), true, prefixo + " "); - } +private void imprimirConteudoDiretorio(Diretorio dir, String caminho, boolean recursivo, String indent) { + System.out.println(indent + dir.getMetaDados().getNome() + "/"); + for (Arquivo arquivo : dir.getArquivos()) { + System.out.println(indent + " " + arquivo.getMetaDados().getNome()); + } + if (recursivo) { + for (Diretorio subdir : dir.getSubdiretorios()) { + imprimirConteudoDiretorio(subdir, caminho + "/" + subdir.getMetaDados().getNome(), true, indent + " "); } } +} - @Override - public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) - throws CaminhoNaoEncontradoException, PermissaoException { - - verificaUsuarioValido(usuario); - - verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminhoOrigem), usuario); - verificarPermissaoExecucaoNoCaminho(obterCaminhoPai(caminhoDestino), usuario); - - // Localiza origem e destino - Diretorio dirOrigemPai = navegarParaDiretorioPai(caminhoOrigem); - Diretorio dirDestino = navegarParaDiretorioPai(caminhoDestino); + @Override // Necessario permissão rw +public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { - String nomeOrigem = extrairNomeFinal(caminhoOrigem); - String nomeDestino = extrairNomeFinal(caminhoDestino); + checarUsuarioV(usuario); + checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminhoOrigem), usuario); + checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminhoDestino), usuario); - // Verifica se é arquivo - Arquivo arquivoOrigem = dirOrigemPai.buscarArquivo(nomeOrigem); - if (arquivoOrigem != null) { - if (!arquivoOrigem.getMetaDados().checkPermissao(usuario, 'r')) { - throw new PermissaoException("Sem permissão de leitura para o arquivo."); - } + Diretorio origemPai = navegarParaDiretorioPai(caminhoOrigem); + Diretorio destinoPai = navegarParaDiretorioPai(caminhoDestino); - Arquivo copia = new Arquivo(nomeDestino, usuario); - for (Bloco bloco : arquivoOrigem.getBlocos()) { - Bloco novoBloco = new Bloco(bloco.getDados().length); - novoBloco.setDados(bloco.getDados().clone()); - copia.adicionarBloco(novoBloco); - } + String nomeOrigem = extrairNomeFinal(caminhoOrigem); + String nomeDestino = extrairNomeFinal(caminhoDestino); - dirDestino.adicionarArquivo(copia); - return; - } - - // Verifica se é diretório - Diretorio diretorioOrigem = dirOrigemPai.buscarSubdiretorio(nomeOrigem); - if (diretorioOrigem != null) { - if (!diretorioOrigem.getMetaDados().checkPermissao(usuario, 'r')) { - throw new PermissaoException("Sem permissão de leitura para o diretório."); - } - if (!recursivo) { - throw new PermissaoException("Cópia de diretório requer flag recursiva."); - } - - Diretorio copia = copiarDiretorioRecursivo(diretorioOrigem, usuario); - copia.getMetaDados().setNome(nomeDestino); - dirDestino.adicionarSubdiretorio(copia); - return; - } + Arquivo arquivoOrigem = origemPai.buscarArquivo(nomeOrigem); + if (arquivoOrigem != null) { + copiarArquivo(arquivoOrigem, destinoPai, nomeDestino, usuario); + return; + } - throw new CaminhoNaoEncontradoException("Origem não encontrada."); + Diretorio diretorioOrigem = origemPai.buscarSubdiretorio(nomeOrigem); + if (diretorioOrigem != null) { + copiarDiretorio(diretorioOrigem, destinoPai, nomeDestino, usuario, recursivo); + return; } + throw new CaminhoNaoEncontradoException("Origem não encontrada."); +} + public void addUser(String nome, String permissao) { if (!usuarios.containsKey(nome)) { Usuario novo = new Usuario(nome, permissao); @@ -421,20 +345,20 @@ private String obterCaminhoPai(String caminho) { return caminho.substring(0, ultimoSlash); } - private void verificaUsuarioValido(String usuario) throws PermissaoException { + private void checarUsuarioV(String usuario) throws PermissaoException { if (!usuarios.containsKey(usuario)) { throw new PermissaoException("Usuário '" + usuario + "' não encontrado."); } } - private void verificarPermissaoExecucaoNoCaminho(String caminho, String usuario) + private void checarPermissaoExecucaoRecursiva(String caminho, String usuario) throws PermissaoException, CaminhoNaoEncontradoException { if (usuario.equals(ROOT_USER)) return; // Trata casos especiais de caminho vazio ou "/" if (caminho == null || caminho.isEmpty() || caminho.equals("/")) { - if (!checkPermissao("/", usuario, 'x')) { + if (!checarPermissao("/", usuario, 'x')) { throw new PermissaoException( "Usuário '" + usuario + "' não tem permissão 'x' em '/'"); } @@ -447,21 +371,21 @@ private void verificarPermissaoExecucaoNoCaminho(String caminho, String usuario) if (partes[i].isEmpty()) continue; pathBuilder.append("/").append(partes[i]); - if (!checkPermissao(pathBuilder.toString(), usuario, 'x')) { + if (!checarPermissao(pathBuilder.toString(), usuario, 'x')) { throw new PermissaoException( "Usuário '" + usuario + "' não tem permissão 'x' em '" + pathBuilder + "'"); } } } - private void verificarPermissao(String caminho, String usuario, char tipo) throws PermissaoException { - if (!checkPermissao(caminho, usuario, tipo)) { + private void validarPermissao(String caminho, String usuario, char tipo) throws PermissaoException { + if (!checarPermissao(caminho, usuario, tipo)) { throw new PermissaoException( "Usuário '" + usuario + "' não tem permissão '" + tipo + "' em '" + caminho + "'"); } } - private boolean checkPermissao(String caminho, String usuario, char tipo) { + private boolean checarPermissao(String caminho, String usuario, char tipo) { if (usuario.equals(ROOT_USER)) return true; // root sempre tem permissão @@ -471,19 +395,19 @@ private boolean checkPermissao(String caminho, String usuario, char tipo) { Arquivo arq = pai.buscarArquivo(nome); if (arq != null) { - if (arq.getMetaDados().checkPermissao(usuario, tipo)) { + if (arq.getMetaDados().checarPermissao(usuario, tipo)) { return true; } } Diretorio dir = pai.buscarSubdiretorio(nome); if (dir != null) { - if (dir.getMetaDados().checkPermissao(usuario, tipo)) { + if (dir.getMetaDados().checarPermissao(usuario, tipo)) { return true; } } // Se for o diretório raiz if (caminho.equals("/")) { - if (fileSys.getRaiz().getMetaDados().checkPermissao(usuario, tipo)) { + if (fileSys.getRaiz().getMetaDados().checarPermissao(usuario, tipo)) { return true; } } @@ -572,14 +496,14 @@ private int lerArquivoComOffset(Arquivo arquivo, byte[] buffer, Offset offset) { } private Diretorio copiarDiretorioRecursivo(Diretorio original, String usuario) throws PermissaoException { - if (!original.getMetaDados().checkPermissao(usuario, 'r')) { + if (!original.getMetaDados().checarPermissao(usuario, 'r')) { throw new PermissaoException("Sem permissão para copiar diretório " + original.getMetaDados().getNome()); } Diretorio copia = new Diretorio(original.getMetaDados().getNome(), usuario); for (Arquivo a : original.getArquivos()) { - if (!a.getMetaDados().checkPermissao(usuario, 'r')) { + if (!a.getMetaDados().checarPermissao(usuario, 'r')) { throw new PermissaoException("Sem permissão para copiar arquivo " + a.getMetaDados().getNome()); } @@ -599,4 +523,94 @@ private Diretorio copiarDiretorioRecursivo(Diretorio original, String usuario) t return copia; } + + private void verificarFormatoCaminho(String caminho) { + if (!caminho.startsWith("/")) { + throw new IllegalArgumentException("Caminho inválido: deve começar com '/'"); + } + + } + + private Diretorio criarSubdiretorio(Diretorio pai, String nome, String usuario) { + Diretorio novo = new Diretorio(nome, usuario); + pai.adicionarSubdiretorio(novo); + return novo; + } + + private void validarPermissoesParaRemocao(String caminho, String paiPath, String usuario) + throws PermissaoException, CaminhoNaoEncontradoException { + checarPermissaoExecucaoRecursiva(paiPath, usuario); + validarPermissao(caminho, usuario, 'r'); + validarPermissao(paiPath, usuario, 'w'); + } + + private Arquivo obterArquivo(String caminho) throws CaminhoNaoEncontradoException { + Diretorio pai = navegarParaDiretorioPai(caminho); + String nomeArquivo = extrairNomeFinal(caminho); + Arquivo arquivo = pai.buscarArquivo(nomeArquivo); + + if (arquivo == null) { + throw new CaminhoNaoEncontradoException("Arquivo '" + nomeArquivo + "' não encontrado."); + } + return arquivo; +} + +private void limparArquivo(Arquivo arquivo) { + arquivo.getBlocos().clear(); + arquivo.getMetaDados().setTamanho(0); +} + +private void checarExistenciaNoDestino(Diretorio destinoPai, String nomeDestino, String caminhoDestino) + throws CaminhoJaExistenteException { + if (destinoPai.buscarArquivo(nomeDestino) != null || destinoPai.buscarSubdiretorio(nomeDestino) != null) { + throw new CaminhoJaExistenteException("O destino '" + caminhoDestino + "' já existe."); + } +} + +private boolean moverArquivoSeExistir(Diretorio origemPai, Diretorio destinoPai, String nomeOrigem, String nomeDestino) { + Arquivo arquivo = origemPai.buscarArquivo(nomeOrigem); + if (arquivo != null) { + origemPai.removerArquivo(nomeOrigem); + arquivo.getMetaDados().setNome(nomeDestino); + destinoPai.adicionarArquivo(arquivo); + return true; + } + return false; +} + +private boolean moverDiretorioSeExistir(Diretorio origemPai, Diretorio destinoPai, String nomeOrigem, String nomeDestino) { + Diretorio subdir = origemPai.buscarSubdiretorio(nomeOrigem); + if (subdir != null) { + origemPai.removerSubdiretorio(nomeOrigem); + subdir.getMetaDados().setNome(nomeDestino); + destinoPai.adicionarSubdiretorio(subdir); + return true; + } + return false; +} + +private void copiarArquivo(Arquivo origem, Diretorio destinoPai, String nomeDestino, String usuario) throws PermissaoException { + if (!origem.getMetaDados().checarPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura para o arquivo."); + } + Arquivo copia = new Arquivo(nomeDestino, usuario); + for (Bloco bloco : origem.getBlocos()) { + Bloco novoBloco = new Bloco(bloco.getDados().length); + novoBloco.setDados(bloco.getDados().clone()); + copia.adicionarBloco(novoBloco); + } + destinoPai.adicionarArquivo(copia); +} + +private void copiarDiretorio(Diretorio origem, Diretorio destinoPai, String nomeDestino, String usuario, boolean recursivo) throws PermissaoException { + if (!origem.getMetaDados().checarPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura para o diretório."); + } + if (!recursivo) { + throw new PermissaoException("Cópia de diretório requer flag recursiva."); + } + Diretorio copia = copiarDiretorioRecursivo(origem, usuario); + copia.getMetaDados().setNome(nomeDestino); + destinoPai.adicionarSubdiretorio(copia); +} } diff --git a/filesys/core/MetaDados.java b/filesys/core/MetaDados.java index 98a4cbe..be11fd8 100644 --- a/filesys/core/MetaDados.java +++ b/filesys/core/MetaDados.java @@ -40,7 +40,7 @@ public String getPermissao(String usuario) { return permissoes.getOrDefault(usuario, "---"); } - public boolean checkPermissao(String usuario, char tipo) { + public boolean checarPermissao(String usuario, char tipo) { return getPermissao(usuario).indexOf(tipo) != -1; } From 93ac26858151746a53058db407b0d3a727cb508b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BAlio=20Henrique?= Date: Wed, 18 Jun 2025 19:31:53 -0300 Subject: [PATCH 26/30] formatacao --- filesys/FileSystemImpl.java | 310 ++++++++++++++++++------------------ 1 file changed, 158 insertions(+), 152 deletions(-) diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 55de266..6321981 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -127,145 +127,147 @@ public void rm(String caminho, String usuario, boolean recursivo) } @Override // Necessario permissão wx -public void touch(String caminho, String usuario) - throws CaminhoJaExistenteException, CaminhoNaoEncontradoException, PermissaoException { - checarUsuarioV(usuario); + public void touch(String caminho, String usuario) + throws CaminhoJaExistenteException, CaminhoNaoEncontradoException, PermissaoException { + checarUsuarioV(usuario); - if (caminho.equals("/") || caminho.endsWith("/")) - throw new CaminhoNaoEncontradoException("Caminho inválido para arquivo: " + caminho); + if (caminho.equals("/") || caminho.endsWith("/")) + throw new CaminhoNaoEncontradoException("Caminho inválido para arquivo: " + caminho); - String paiPath = obterCaminhoPai(caminho); - checarPermissaoExecucaoRecursiva(paiPath, usuario); + String paiPath = obterCaminhoPai(caminho); + checarPermissaoExecucaoRecursiva(paiPath, usuario); - Diretorio pai = navegarParaDiretorioPai(caminho); - String nomeArquivo = extrairNomeFinal(caminho); + Diretorio pai = navegarParaDiretorioPai(caminho); + String nomeArquivo = extrairNomeFinal(caminho); - if (pai.buscarArquivo(nomeArquivo) != null) - throw new CaminhoJaExistenteException("O arquivo '" + nomeArquivo + "' já existe."); + if (pai.buscarArquivo(nomeArquivo) != null) + throw new CaminhoJaExistenteException("O arquivo '" + nomeArquivo + "' já existe."); - validarPermissao(paiPath, usuario, 'w'); + validarPermissao(paiPath, usuario, 'w'); - Arquivo novoArquivo = new Arquivo(nomeArquivo, usuario); - pai.adicionarArquivo(novoArquivo); -} + Arquivo novoArquivo = new Arquivo(nomeArquivo, usuario); + pai.adicionarArquivo(novoArquivo); + } @Override // Necessario permissão rw -public void write(String caminho, String usuario, boolean anexar, byte[] buffer) - throws CaminhoNaoEncontradoException, PermissaoException { - checarUsuarioV(usuario); - validarPermissao(caminho, usuario, 'w'); - checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminho), usuario); + public void write(String caminho, String usuario, boolean anexar, byte[] buffer) + throws CaminhoNaoEncontradoException, PermissaoException { + checarUsuarioV(usuario); + validarPermissao(caminho, usuario, 'w'); + checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminho), usuario); - Arquivo arquivo = obterArquivo(caminho); + Arquivo arquivo = obterArquivo(caminho); - if (!anexar) { - limparArquivo(arquivo); - } + if (!anexar) { + limparArquivo(arquivo); + } - escreverBufferEmBlocos(arquivo, buffer); -} + escreverBufferEmBlocos(arquivo, buffer); + } @Override // Necessario permissão r -public void read(String caminho, String usuario, byte[] buffer, Offset offset) - throws CaminhoNaoEncontradoException, PermissaoException { - checarUsuarioV(usuario); - validarPermissao(caminho, usuario, 'r'); - checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminho), usuario); + public void read(String caminho, String usuario, byte[] buffer, Offset offset) + throws CaminhoNaoEncontradoException, PermissaoException { + checarUsuarioV(usuario); + validarPermissao(caminho, usuario, 'r'); + checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminho), usuario); - Arquivo arquivo = obterArquivo(caminho); + Arquivo arquivo = obterArquivo(caminho); - lerArquivoComOffset(arquivo, buffer, offset); -} + lerArquivoComOffset(arquivo, buffer, offset); + } @Override // Necessario permissão rw -public void mv(String caminhoAntigo, String caminhoNovo, String usuario) - throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { - checarUsuarioV(usuario); + public void mv(String caminhoAntigo, String caminhoNovo, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + checarUsuarioV(usuario); - if (caminhoAntigo.equals("/") || caminhoNovo.equals("/")) { - throw new CaminhoNaoEncontradoException("Não é possível mover o diretório raiz."); - } + if (caminhoAntigo.equals("/") || caminhoNovo.equals("/")) { + throw new CaminhoNaoEncontradoException("Não é possível mover o diretório raiz."); + } - checarPermissaoExecucaoRecursiva(caminhoAntigo, usuario); - checarPermissaoExecucaoRecursiva(caminhoNovo, usuario); + checarPermissaoExecucaoRecursiva(caminhoAntigo, usuario); + checarPermissaoExecucaoRecursiva(caminhoNovo, usuario); - validarPermissao(caminhoAntigo, usuario, 'r'); - validarPermissao(obterCaminhoPai(caminhoNovo), usuario, 'w'); - validarPermissao(obterCaminhoPai(caminhoAntigo), usuario, 'w'); + validarPermissao(caminhoAntigo, usuario, 'r'); + validarPermissao(obterCaminhoPai(caminhoNovo), usuario, 'w'); + validarPermissao(obterCaminhoPai(caminhoAntigo), usuario, 'w'); - Diretorio diretorioOrigemPai = navegarParaDiretorioPai(caminhoAntigo); - Diretorio diretorioDestinoPai = navegarParaDiretorioPai(caminhoNovo); + Diretorio diretorioOrigemPai = navegarParaDiretorioPai(caminhoAntigo); + Diretorio diretorioDestinoPai = navegarParaDiretorioPai(caminhoNovo); - String nomeOrigem = extrairNomeFinal(caminhoAntigo); - String nomeDestino = extrairNomeFinal(caminhoNovo); + String nomeOrigem = extrairNomeFinal(caminhoAntigo); + String nomeDestino = extrairNomeFinal(caminhoNovo); - checarExistenciaNoDestino(diretorioDestinoPai, nomeDestino, caminhoNovo); + checarExistenciaNoDestino(diretorioDestinoPai, nomeDestino, caminhoNovo); - if (moverArquivoSeExistir(diretorioOrigemPai, diretorioDestinoPai, nomeOrigem, nomeDestino)) return; - if (moverDiretorioSeExistir(diretorioOrigemPai, diretorioDestinoPai, nomeOrigem, nomeDestino)) return; + if (moverArquivoSeExistir(diretorioOrigemPai, diretorioDestinoPai, nomeOrigem, nomeDestino)) + return; + if (moverDiretorioSeExistir(diretorioOrigemPai, diretorioDestinoPai, nomeOrigem, nomeDestino)) + return; - throw new CaminhoNaoEncontradoException("Caminho de origem '" + caminhoAntigo + "' não encontrado."); -} + throw new CaminhoNaoEncontradoException("Caminho de origem '" + caminhoAntigo + "' não encontrado."); + } @Override // Necessario permissão r - public void ls(String caminho, String usuario, boolean recursivo) - throws CaminhoNaoEncontradoException, PermissaoException { - checarUsuarioV(usuario); - verificarCaminhoComPermissao(caminho); - - checarPermissaoExecucaoRecursiva(caminho, usuario); - validarPermissao(caminho, usuario, 'r'); + public void ls(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { + checarUsuarioV(usuario); + verificarCaminhoComPermissao(caminho); - Diretorio dir = caminho.equals("/") ? fileSys.getRaiz() : navegarParaDiretorioCompleto(caminho); - imprimirConteudoDiretorio(dir, caminho, recursivo, ""); -} + checarPermissaoExecucaoRecursiva(caminho, usuario); + validarPermissao(caminho, usuario, 'r'); -private void verificarCaminhoComPermissao(String caminho) throws CaminhoNaoEncontradoException { - if (caminho == null || caminho.isEmpty() || !caminho.startsWith("/")) { - throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); + Diretorio dir = caminho.equals("/") ? fileSys.getRaiz() : navegarParaDiretorioCompleto(caminho); + imprimirConteudoDiretorio(dir, caminho, recursivo, ""); } -} -private void imprimirConteudoDiretorio(Diretorio dir, String caminho, boolean recursivo, String indent) { - System.out.println(indent + dir.getMetaDados().getNome() + "/"); - for (Arquivo arquivo : dir.getArquivos()) { - System.out.println(indent + " " + arquivo.getMetaDados().getNome()); + private void verificarCaminhoComPermissao(String caminho) throws CaminhoNaoEncontradoException { + if (caminho == null || caminho.isEmpty() || !caminho.startsWith("/")) { + throw new CaminhoNaoEncontradoException("Caminho inválido: " + caminho); + } } - if (recursivo) { - for (Diretorio subdir : dir.getSubdiretorios()) { - imprimirConteudoDiretorio(subdir, caminho + "/" + subdir.getMetaDados().getNome(), true, indent + " "); + + private void imprimirConteudoDiretorio(Diretorio dir, String caminho, boolean recursivo, String indent) { + System.out.println(indent + dir.getMetaDados().getNome() + "/"); + for (Arquivo arquivo : dir.getArquivos()) { + System.out.println(indent + " " + arquivo.getMetaDados().getNome()); + } + if (recursivo) { + for (Diretorio subdir : dir.getSubdiretorios()) { + imprimirConteudoDiretorio(subdir, caminho + "/" + subdir.getMetaDados().getNome(), true, indent + " "); + } } } -} @Override // Necessario permissão rw -public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) - throws CaminhoNaoEncontradoException, PermissaoException { + public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { - checarUsuarioV(usuario); - checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminhoOrigem), usuario); - checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminhoDestino), usuario); + checarUsuarioV(usuario); + checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminhoOrigem), usuario); + checarPermissaoExecucaoRecursiva(obterCaminhoPai(caminhoDestino), usuario); - Diretorio origemPai = navegarParaDiretorioPai(caminhoOrigem); - Diretorio destinoPai = navegarParaDiretorioPai(caminhoDestino); + Diretorio origemPai = navegarParaDiretorioPai(caminhoOrigem); + Diretorio destinoPai = navegarParaDiretorioPai(caminhoDestino); - String nomeOrigem = extrairNomeFinal(caminhoOrigem); - String nomeDestino = extrairNomeFinal(caminhoDestino); + String nomeOrigem = extrairNomeFinal(caminhoOrigem); + String nomeDestino = extrairNomeFinal(caminhoDestino); - Arquivo arquivoOrigem = origemPai.buscarArquivo(nomeOrigem); - if (arquivoOrigem != null) { - copiarArquivo(arquivoOrigem, destinoPai, nomeDestino, usuario); - return; - } + Arquivo arquivoOrigem = origemPai.buscarArquivo(nomeOrigem); + if (arquivoOrigem != null) { + copiarArquivo(arquivoOrigem, destinoPai, nomeDestino, usuario); + return; + } - Diretorio diretorioOrigem = origemPai.buscarSubdiretorio(nomeOrigem); - if (diretorioOrigem != null) { - copiarDiretorio(diretorioOrigem, destinoPai, nomeDestino, usuario, recursivo); - return; - } + Diretorio diretorioOrigem = origemPai.buscarSubdiretorio(nomeOrigem); + if (diretorioOrigem != null) { + copiarDiretorio(diretorioOrigem, destinoPai, nomeDestino, usuario, recursivo); + return; + } - throw new CaminhoNaoEncontradoException("Origem não encontrada."); -} + throw new CaminhoNaoEncontradoException("Origem não encontrada."); + } public void addUser(String nome, String permissao) { if (!usuarios.containsKey(nome)) { @@ -545,72 +547,76 @@ private void validarPermissoesParaRemocao(String caminho, String paiPath, String } private Arquivo obterArquivo(String caminho) throws CaminhoNaoEncontradoException { - Diretorio pai = navegarParaDiretorioPai(caminho); - String nomeArquivo = extrairNomeFinal(caminho); - Arquivo arquivo = pai.buscarArquivo(nomeArquivo); + Diretorio pai = navegarParaDiretorioPai(caminho); + String nomeArquivo = extrairNomeFinal(caminho); + Arquivo arquivo = pai.buscarArquivo(nomeArquivo); - if (arquivo == null) { - throw new CaminhoNaoEncontradoException("Arquivo '" + nomeArquivo + "' não encontrado."); + if (arquivo == null) { + throw new CaminhoNaoEncontradoException("Arquivo '" + nomeArquivo + "' não encontrado."); + } + return arquivo; } - return arquivo; -} - -private void limparArquivo(Arquivo arquivo) { - arquivo.getBlocos().clear(); - arquivo.getMetaDados().setTamanho(0); -} -private void checarExistenciaNoDestino(Diretorio destinoPai, String nomeDestino, String caminhoDestino) - throws CaminhoJaExistenteException { - if (destinoPai.buscarArquivo(nomeDestino) != null || destinoPai.buscarSubdiretorio(nomeDestino) != null) { - throw new CaminhoJaExistenteException("O destino '" + caminhoDestino + "' já existe."); + private void limparArquivo(Arquivo arquivo) { + arquivo.getBlocos().clear(); + arquivo.getMetaDados().setTamanho(0); } -} -private boolean moverArquivoSeExistir(Diretorio origemPai, Diretorio destinoPai, String nomeOrigem, String nomeDestino) { - Arquivo arquivo = origemPai.buscarArquivo(nomeOrigem); - if (arquivo != null) { - origemPai.removerArquivo(nomeOrigem); - arquivo.getMetaDados().setNome(nomeDestino); - destinoPai.adicionarArquivo(arquivo); - return true; + private void checarExistenciaNoDestino(Diretorio destinoPai, String nomeDestino, String caminhoDestino) + throws CaminhoJaExistenteException { + if (destinoPai.buscarArquivo(nomeDestino) != null || destinoPai.buscarSubdiretorio(nomeDestino) != null) { + throw new CaminhoJaExistenteException("O destino '" + caminhoDestino + "' já existe."); + } } - return false; -} -private boolean moverDiretorioSeExistir(Diretorio origemPai, Diretorio destinoPai, String nomeOrigem, String nomeDestino) { - Diretorio subdir = origemPai.buscarSubdiretorio(nomeOrigem); - if (subdir != null) { - origemPai.removerSubdiretorio(nomeOrigem); - subdir.getMetaDados().setNome(nomeDestino); - destinoPai.adicionarSubdiretorio(subdir); - return true; + private boolean moverArquivoSeExistir(Diretorio origemPai, Diretorio destinoPai, String nomeOrigem, + String nomeDestino) { + Arquivo arquivo = origemPai.buscarArquivo(nomeOrigem); + if (arquivo != null) { + origemPai.removerArquivo(nomeOrigem); + arquivo.getMetaDados().setNome(nomeDestino); + destinoPai.adicionarArquivo(arquivo); + return true; + } + return false; } - return false; -} -private void copiarArquivo(Arquivo origem, Diretorio destinoPai, String nomeDestino, String usuario) throws PermissaoException { - if (!origem.getMetaDados().checarPermissao(usuario, 'r')) { - throw new PermissaoException("Sem permissão de leitura para o arquivo."); - } - Arquivo copia = new Arquivo(nomeDestino, usuario); - for (Bloco bloco : origem.getBlocos()) { - Bloco novoBloco = new Bloco(bloco.getDados().length); - novoBloco.setDados(bloco.getDados().clone()); - copia.adicionarBloco(novoBloco); + private boolean moverDiretorioSeExistir(Diretorio origemPai, Diretorio destinoPai, String nomeOrigem, + String nomeDestino) { + Diretorio subdir = origemPai.buscarSubdiretorio(nomeOrigem); + if (subdir != null) { + origemPai.removerSubdiretorio(nomeOrigem); + subdir.getMetaDados().setNome(nomeDestino); + destinoPai.adicionarSubdiretorio(subdir); + return true; + } + return false; } - destinoPai.adicionarArquivo(copia); -} -private void copiarDiretorio(Diretorio origem, Diretorio destinoPai, String nomeDestino, String usuario, boolean recursivo) throws PermissaoException { - if (!origem.getMetaDados().checarPermissao(usuario, 'r')) { - throw new PermissaoException("Sem permissão de leitura para o diretório."); + private void copiarArquivo(Arquivo origem, Diretorio destinoPai, String nomeDestino, String usuario) + throws PermissaoException { + if (!origem.getMetaDados().checarPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura para o arquivo."); + } + Arquivo copia = new Arquivo(nomeDestino, usuario); + for (Bloco bloco : origem.getBlocos()) { + Bloco novoBloco = new Bloco(bloco.getDados().length); + novoBloco.setDados(bloco.getDados().clone()); + copia.adicionarBloco(novoBloco); + } + destinoPai.adicionarArquivo(copia); } - if (!recursivo) { - throw new PermissaoException("Cópia de diretório requer flag recursiva."); + + private void copiarDiretorio(Diretorio origem, Diretorio destinoPai, String nomeDestino, String usuario, + boolean recursivo) throws PermissaoException { + if (!origem.getMetaDados().checarPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura para o diretório."); + } + if (!recursivo) { + throw new PermissaoException("Cópia de diretório requer flag recursiva."); + } + Diretorio copia = copiarDiretorioRecursivo(origem, usuario); + copia.getMetaDados().setNome(nomeDestino); + destinoPai.adicionarSubdiretorio(copia); } - Diretorio copia = copiarDiretorioRecursivo(origem, usuario); - copia.getMetaDados().setNome(nomeDestino); - destinoPai.adicionarSubdiretorio(copia); -} } From 2ad37d8e7159983d24974e0aca565464ab70ae4d Mon Sep 17 00:00:00 2001 From: Mateus Date: Thu, 19 Jun 2025 14:35:51 -0300 Subject: [PATCH 27/30] =?UTF-8?q?altera=C3=A7=C3=A3o=20na=20classe=20usuar?= =?UTF-8?q?io?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- filesys/Usuario.java | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/filesys/Usuario.java b/filesys/Usuario.java index e19af02..461fccc 100644 --- a/filesys/Usuario.java +++ b/filesys/Usuario.java @@ -10,7 +10,7 @@ public class Usuario { public Usuario(String nome, String permissao) { this.nome = nome; this.permissoesPorCaminho = new HashMap<>(); - this.permissoesPorCaminho.put("**", permissao); + this.permissoesPorCaminho.put("/**", permissao); } public Usuario(String nome) { @@ -23,18 +23,28 @@ public void adicionarPermissao(String caminho, String permissao) { } public String getPermissaoParaCaminho(String caminho) { - String melhorPermissao = "---"; - int melhorTamanho = -1; + String melhorPermissao = permissoesPorCaminho.getOrDefault("/**", "---"); + int melhorScore = -1; for (Map.Entry entry : permissoesPorCaminho.entrySet()) { String padrao = entry.getKey(); - if (caminhoMatches(padrao, caminho) && padrao.length() > melhorTamanho) { - melhorPermissao = entry.getValue(); - melhorTamanho = padrao.length(); + if (caminhoMatches(padrao, caminho)) { + int score = getPadraoScore(padrao, caminho); + if (score > melhorScore) { + melhorScore = score; + melhorPermissao = entry.getValue(); + } } } return melhorPermissao; } + private int getPadraoScore(String padrao, String caminho) { + if (padrao.equals(caminho)) return 100; // exato + if (padrao.endsWith("/*")) return 10; // filho direto + if (padrao.endsWith("/**")) return 1; // recursivo + return 0; // outros + } + private boolean caminhoMatches(String padrao, String caminho) { // Aceita qualquer subcaminho if (padrao.endsWith("/**")) { @@ -58,21 +68,21 @@ public String getNome() { } public boolean podeLer() { - String permissao = permissoesPorCaminho.get("**"); + String permissao = permissoesPorCaminho.get("/**"); return permissao != null && permissao.contains("r"); } public boolean podeEscrever() { - String permissao = permissoesPorCaminho.get("**"); + String permissao = permissoesPorCaminho.get("/**"); return permissao != null && permissao.contains("w"); } public boolean podeExecutar() { - String permissao = permissoesPorCaminho.get("**"); + String permissao = permissoesPorCaminho.get("/**"); return permissao != null && permissao.contains("x"); } public String getPermissao() { - return permissoesPorCaminho.get("**"); + return permissoesPorCaminho.get("/**"); } } From 0a50736a829124ca5c6ffea42319ea0b19e0f788 Mon Sep 17 00:00:00 2001 From: Mateus Date: Thu, 19 Jun 2025 14:36:02 -0300 Subject: [PATCH 28/30] deletando permissiontest --- tests/PermissionTest.java | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 tests/PermissionTest.java diff --git a/tests/PermissionTest.java b/tests/PermissionTest.java deleted file mode 100644 index 6026e31..0000000 --- a/tests/PermissionTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package tests; - -import static org.junit.Assert.assertTrue; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.BeforeAll; - -import filesys.FileSystemImpl; -import filesys.IFileSystem; - -// Essa classe testa cenários de permissão -public class PermissionTest { - private static IFileSystem fileSystem; - - @BeforeAll - public static void setUp() { - fileSystem = new FileSystemImpl(/*args...*/); - } - - @Test - public void testPermission() { - // Teste de permissão - assertTrue(true); - } -} From 45b2f2071f5bdd882553c95fde70642f257f0cdc Mon Sep 17 00:00:00 2001 From: Mateus Date: Thu, 19 Jun 2025 14:36:23 -0300 Subject: [PATCH 29/30] =?UTF-8?q?implementando=20filesystemtest=20com=20fu?= =?UTF-8?q?ncionalidades=20do=20filesys=20para=20testar=20m=C3=A9todos=20i?= =?UTF-8?q?mplementados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/FileSystemTest.java | 128 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 tests/FileSystemTest.java diff --git a/tests/FileSystemTest.java b/tests/FileSystemTest.java new file mode 100644 index 0000000..af28b97 --- /dev/null +++ b/tests/FileSystemTest.java @@ -0,0 +1,128 @@ +package tests; + +import filesys.FileSystem; +import filesys.Usuario; +import filesys.core.Offset; +import exception.CaminhoNaoEncontradoException; +import exception.PermissaoException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class FileSystemTest { + + private FileSystem fs; + private Map usuarios; + + @BeforeEach + public void setup() { + usuarios = new HashMap<>(); + usuarios.put("root", new Usuario("root", "rwx")); + usuarios.put("maria", new Usuario("maria", "rw-")); + usuarios.put("joao", new Usuario("joao", "rw-")); + fs = new FileSystem(usuarios); + } + + @Test + public void testMkdirAndLs() throws Exception { + fs.mkdir("/home", "root"); + fs.mkdir("/home/maria", "root"); + // Não lança exceção ao listar diretórios criados + assertDoesNotThrow(() -> fs.ls("/home", "root", false)); + assertDoesNotThrow(() -> fs.ls("/home/maria", "root", false)); + } + + @Test + public void testTouchAndWriteAndRead() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/arquivo.txt", "root"); + byte[] conteudo = "Hello World".getBytes(); + fs.write("/docs/arquivo.txt", "root", false, conteudo); + + byte[] buffer = new byte[20]; + Offset offset = new Offset(0); + fs.read("/docs/arquivo.txt", "root", buffer, offset); + String lido = new String(buffer).trim(); + assertTrue(lido.startsWith("Hello World")); + } + + @Test + public void testPermissaoException() throws Exception { + fs.mkdir("/privado", "root"); + // Maria não tem permissão de execução em /privado + assertThrows(PermissaoException.class, () -> fs.mkdir("/privado/novo", "maria")); + } + + @Test + public void testChmod() throws Exception { + fs.mkdir("/area", "root"); + // root dá permissão para maria + fs.chmod("/area", "root", "maria", "rwx"); + // Agora maria pode criar subdiretório + assertDoesNotThrow(() -> fs.mkdir("/area/sub", "maria")); + } + + @Test + public void testRm() throws Exception { + fs.mkdir("/tmp", "root"); + fs.touch("/tmp/file.txt", "root"); + fs.rm("/tmp/file.txt", "root", false); + // Arquivo removido, não pode ler + assertThrows(CaminhoNaoEncontradoException.class, () -> { + byte[] buf = new byte[10]; + fs.read("/tmp/file.txt", "root", buf, new Offset(0)); + }); + } + + @Test + public void testMv() throws Exception { + fs.mkdir("/dir1", "root"); + fs.touch("/dir1/file.txt", "root"); + fs.mkdir("/dir2", "root"); + fs.mv("/dir1/file.txt", "/dir2/file2.txt", "root"); + // Agora só existe em /dir2 + assertThrows(CaminhoNaoEncontradoException.class, () -> { + byte[] buf = new byte[10]; + fs.read("/dir1/file.txt", "root", buf, new Offset(0)); + }); + assertDoesNotThrow(() -> { + byte[] buf = new byte[10]; + fs.read("/dir2/file2.txt", "root", buf, new Offset(0)); + }); + } + + @Test + public void testCp() throws Exception { + fs.mkdir("/origem", "root"); + fs.touch("/origem/arq.txt", "root"); + fs.write("/origem/arq.txt", "root", false, "abc".getBytes()); + fs.mkdir("/destino", "root"); + fs.cp("/origem/arq.txt", "/destino/arq_copia.txt", "root", false); + byte[] buf = new byte[10]; + fs.read("/destino/arq_copia.txt", "root", buf, new Offset(0)); + assertEquals("abc", new String(buf).trim()); + } + + @Test + public void testLsRecursivo() throws Exception { + fs.mkdir("/a", "root"); + fs.mkdir("/a/b", "root"); + fs.touch("/a/b/c.txt", "root"); + // Não lança exceção ao listar recursivamente + assertDoesNotThrow(() -> fs.ls("/a", "root", true)); + } + + @Test + public void testNaoPermiteRemoverRaiz() { + assertThrows(PermissaoException.class, () -> fs.rm("/", "root", true)); + } + + @Test + public void testNaoPermiteMoverRaiz() { + assertThrows(Exception.class, () -> fs.mv("/", "/nova", "root")); + } +} \ No newline at end of file From 6819648f7aac6736c51eca0e56216574c789a4cd Mon Sep 17 00:00:00 2001 From: Mateus Date: Thu, 19 Jun 2025 14:36:41 -0300 Subject: [PATCH 30/30] =?UTF-8?q?implementando=20usuariotest=20para=20test?= =?UTF-8?q?ar=20as=20permiss=C3=B5es=20e=20funcionalidade=20de=20permiss?= =?UTF-8?q?=C3=B5es=20do=20usuarios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/UsuarioTest.java | 107 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 tests/UsuarioTest.java diff --git a/tests/UsuarioTest.java b/tests/UsuarioTest.java new file mode 100644 index 0000000..6643711 --- /dev/null +++ b/tests/UsuarioTest.java @@ -0,0 +1,107 @@ +package tests; + +import filesys.Usuario; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class UsuarioTest { + + @Test + public void testRootUser() { + Usuario user = new Usuario("root", "rwx"); + assertEquals("root", user.getNome()); + assertEquals("rwx", user.getPermissao()); + assertTrue(user.podeLer()); + assertTrue(user.podeEscrever()); + assertTrue(user.podeExecutar()); + + user.adicionarPermissao("/home", "rw-"); + assertEquals("rw-", user.getPermissaoParaCaminho("/home")); + assertEquals("rwx", user.getPermissaoParaCaminho("/")); + } + + @Test + public void testMariaUser() { + Usuario user = new Usuario("maria", "rw-"); + assertEquals("maria", user.getNome()); + assertEquals("rw-", user.getPermissao()); + assertTrue(user.podeLer()); + assertTrue(user.podeEscrever()); + assertFalse(user.podeExecutar()); + + user.adicionarPermissao("/docs", "r--"); + assertEquals("r--", user.getPermissaoParaCaminho("/docs")); + assertEquals("rw-", user.getPermissaoParaCaminho("/")); + } + + @Test + public void testJoaoUser() { + Usuario user = new Usuario("joao", "rw-"); + assertEquals("joao", user.getNome()); + assertEquals("rw-", user.getPermissao()); + assertTrue(user.podeLer()); + assertTrue(user.podeEscrever()); + assertFalse(user.podeExecutar()); + } + + @Test + public void testPedroUser() { + Usuario user = new Usuario("pedro", "rw-"); + assertEquals("pedro", user.getNome()); + assertEquals("rw-", user.getPermissao()); + assertTrue(user.podeLer()); + assertTrue(user.podeEscrever()); + assertFalse(user.podeExecutar()); + } + + @Test + public void testTiagoUser() { + Usuario user = new Usuario("tiago", "rw-"); + assertEquals("tiago", user.getNome()); + assertEquals("rw-", user.getPermissao()); + assertTrue(user.podeLer()); + assertTrue(user.podeEscrever()); + assertFalse(user.podeExecutar()); + } + + @Test + public void testLuziaUser() { + Usuario user = new Usuario("luzia", "rwx"); + assertEquals("luzia", user.getNome()); + assertEquals("rwx", user.getPermissao()); + assertTrue(user.podeLer()); + assertTrue(user.podeEscrever()); + assertTrue(user.podeExecutar()); + } + + @Test + public void testCarlaUser() { + Usuario user = new Usuario("carla", "r--"); + assertEquals("carla", user.getNome()); + assertEquals("r--", user.getPermissao()); + assertTrue(user.podeLer()); + assertFalse(user.podeEscrever()); + assertFalse(user.podeExecutar()); + + user.adicionarPermissao("/tmp", "rw-"); + assertEquals("rw-", user.getPermissaoParaCaminho("/tmp")); + assertEquals("r--", user.getPermissaoParaCaminho("/")); + } + + @Test + public void testPermissaoParaCaminhoPatterns() { + Usuario user = new Usuario("test", "---"); + user.adicionarPermissao("/home/**", "rwx"); + user.adicionarPermissao("/home/*", "rw-"); + user.adicionarPermissao("/home/user", "r--"); + + // /home/user/docs should match /home/** (subpath) + assertEquals("rwx", user.getPermissaoParaCaminho("/home/user/docs")); + // /home/user should match /home/user (exact) + assertEquals("r--", user.getPermissaoParaCaminho("/home/user")); + // /home/abc should match /home/* (direct child) + assertEquals("rw-", user.getPermissaoParaCaminho("/home/abc")); + // / should fallback to default + assertEquals("---", user.getPermissaoParaCaminho("/")); + } +} \ No newline at end of file