diff --git a/Main.java b/Main.java index 3b8673a..4f51918 100644 --- a/Main.java +++ b/Main.java @@ -1,20 +1,25 @@ import filesys.IFileSystem; +import filesys.Offset; +import filesys.Usuario; +import filesys.FileSystem; import java.util.Scanner; import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; import exception.PermissaoException; import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; -import filesys.FileSystem; - -// MENU INTERATIVO PARA O SISTEMA DE ARQUIVOS -// SINTA-SE LIVRE PARA ALTERAR A CLASSE MAIN +/* + MENU INTERATIVO PARA O SISTEMA DE ARQUIVOS + SINTA-SE LIVRE PARA ALTERAR A CLASSE MAIN +*/ public class Main { - // Constantes úteis para a versão interativa. - // Para esse tipo de execução, o tamanho max do buffer de + // 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,30 +34,21 @@ 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/**, - // deve-se checar se o usuário tem permissão de escrita (r) neste caminho. if (args.length < 2) { System.out.println("Usuário não fornecido"); return; } user = args[1]; - + // Carrega a lista de usuários do sistema a partir de arquivo - // Formato do arquivo users: - // username dir permission - // Exemplo: - // maria /** rw- - // luzia /** rwx - // Essa permissão vale para o diretório raiz e sub diretórios. - // A partir do momento que um usuário cria outro diretório ou arquivo, - // a permissão desse usuário é de leitura, escrita e execução nesse novo diretório/arquivo, - // e sempre será rwx para o usuário root. + List usuarios = new ArrayList<>(); try { - Scanner userScanner = new Scanner(new java.io.File("users/users")); + Scanner userScanner = new Scanner(new java.io.File("users/userMkdir")); while (userScanner.hasNextLine()) { String line = userScanner.nextLine().trim(); if (!line.isEmpty()) { @@ -61,46 +57,44 @@ 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 - - + usuarios.add(new Usuario(userListed, dirPermission, dir)); } else { System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); } } } userScanner.close(); - } catch (FileNotFoundException e) { // Retorna se o arquivo de usuários não for encontrado + } catch (FileNotFoundException e) { System.out.println("Arquivo de usuários não encontrado"); - return; } - - // Finalmente cria o Sistema de Arquivos - // Lista de usuários é imutável durante a execução do programa - // Obs: Como passar a lista de usuários para o FileSystem? - fileSystem = new FileSystem(/*usuários?*/); - - // // DESCOMENTE O BLOCO ABAIXO PARA CRIAR O DIRETÓRIO RAIZ ANTES DE RODAR O MENU - // // Cria o diretório raiz do sistema. Root sempre tem permissão total "rwx" - // try { - // fileSystem.mkdir(ROOT_DIR, ROOT_USER); - // } catch (CaminhoJaExistenteException | PermissaoException e) { - // System.out.println(e.getMessage()); - // } + + // Inicializa o sistema de arquivos com a lista de usuários + fileSystem = new FileSystem(usuarios); + + // Cria os diretórios e aplica as permissões conforme o arquivo + for (Usuario usuario : usuarios) { + try { + fileSystem.mkdir(usuario.getDiretorio(), usuario.getNome()); + // Aplica a permissão do arquivo para o usuário no diretório criado + fileSystem.chmod(usuario.getDiretorio(), "root", usuario.getNome(), usuario.getPermissao()); + } catch (CaminhoJaExistenteException | PermissaoException | CaminhoNaoEncontradoException e) { + // Se já existe, não tem permissão ou não encontra, apenas ignore + } + } + + // Cria o diretório raiz do sistema (caso não exista) + try { + fileSystem.mkdir(ROOT_DIR, ROOT_USER); + } catch (CaminhoJaExistenteException | PermissaoException e) { + // Pode ignorar se já existe + } // Menu interativo. menu(); } - // Menu interativo para fins de teste. - // Os testes junit não são feitos com esse menu, - // mas diretamente na interface IFileSystem + // … o resto de Main fica inalterado, igual ao que você já tinha … public static void menu() { while (true) { System.out.println("\nComandos disponíveis:"); @@ -148,11 +142,10 @@ public static void menu() { break; case "0": System.out.println("Encerrando..."); - return; default: System.out.println("Comando inválido!"); - } + } } catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException e) { System.out.println("Erro: " + e.getMessage()); } @@ -169,16 +162,16 @@ public static void chmod() throws CaminhoNaoEncontradoException, PermissaoExcept String caminho = scanner.nextLine(); System.out.println("Insira o usuário para o qual deseja alterar as permissões:"); String usuarioAlvo = scanner.nextLine(); - System.out.println("Insira a permissão (formato: 3 caracteres\"rwx\"):"); + System.out.println("Insira a permissão (formato: 3 caracteres\"rwx\"): "); String permissoes = scanner.nextLine(); - + fileSystem.chmod(caminho, user, usuarioAlvo, permissoes); } public static void mkdir() throws CaminhoJaExistenteException, PermissaoException { System.out.println("Insira o caminho do diretório a ser criado:"); String caminho = scanner.nextLine(); - + fileSystem.mkdir(caminho, user); } @@ -187,14 +180,14 @@ public static void rm() throws CaminhoNaoEncontradoException, PermissaoException String caminho = scanner.nextLine(); System.out.println("Remover recursivamente? (true/false):"); boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); - + fileSystem.rm(caminho, user, recursivo); } public static void touch() throws CaminhoJaExistenteException, PermissaoException { System.out.println("Insira o caminho do arquivo a ser criado:"); String caminho = scanner.nextLine(); - + fileSystem.touch(caminho, user); } @@ -206,16 +199,29 @@ 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; + + 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 { @@ -223,7 +229,7 @@ public static void mv() throws CaminhoNaoEncontradoException, PermissaoException String caminhoAntigo = scanner.nextLine(); System.out.println("Insira o novo caminho do arquivo:"); String caminhoNovo = scanner.nextLine(); - + fileSystem.mv(caminhoAntigo, caminhoNovo, user); } @@ -232,7 +238,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 +249,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/exception/OperacaoInvalidaException.java b/exception/OperacaoInvalidaException.java new file mode 100644 index 0000000..9f446af --- /dev/null +++ b/exception/OperacaoInvalidaException.java @@ -0,0 +1,7 @@ +package exception; + +public class OperacaoInvalidaException extends Exception { + public OperacaoInvalidaException(String message) { + super(message); + } +} diff --git a/filesys/Arquivo.java b/filesys/Arquivo.java new file mode 100644 index 0000000..337f19c --- /dev/null +++ b/filesys/Arquivo.java @@ -0,0 +1,73 @@ +package filesys; + +import java.util.ArrayList; +import java.util.List; + +public class Arquivo extends ElementoFS { + private List blocos; + private long tamanho; + + public Arquivo(String nome, String permissoes, String dono) { + super(nome, permissoes, dono); + // this.arquivo = new Bloco[0]; + this.blocos = new ArrayList<>(); + this.tamanho = 0; + } + + /* + * public Bloco[] getArquivo() { + * return arquivo; + * } + * + * public void setArquivo(Bloco[] arquivo) { + * this.arquivo = arquivo; + * } + */ + + public void adicionarBloco(byte[] dados) { + blocos.add(dados); + tamanho += dados.length; + } + + public List getBlocos() { + return blocos; + } + + public long getTamanho() { + return tamanho; + } + + public void removerBloco(int index) { + if (index < 0 || index >= blocos.size()) { + throw new IndexOutOfBoundsException("Índice fora dos limites da lista."); + } + byte[] removed = blocos.remove(index); + tamanho -= removed.length; + } + + public void limparBlocos() { + blocos.clear(); + tamanho = 0; + } + + @Override + public boolean isArquivo() { + return true; + } + + @Override + public String toString() { + return "Arquivo: " + nomeDiretorio + " | Dono: " + donoDiretorio + " | Permissões: " + permissoesPadrao + " | Tam: " + + tamanho + " bytes"; + } + + public void adicionarFilho(ElementoFS filho) { + throw new UnsupportedOperationException("Não é possível adicionar filhos a um arquivo."); + } + + + public void removerFilho(String nomeFilho) { + throw new UnsupportedOperationException("Não é possível remover filhos de um arquivo."); + } + +} \ No newline at end of file diff --git a/filesys/Bloco.java b/filesys/Bloco.java new file mode 100644 index 0000000..87d8cf9 --- /dev/null +++ b/filesys/Bloco.java @@ -0,0 +1,19 @@ +package filesys; + +public class Bloco { + private static final int TAMANHO_BLOCO = 4096; // 4KB por bloco + private byte[] dados; + + + public Bloco(int tamanho) { + this.dados = new byte[TAMANHO_BLOCO]; + } + + public byte[] getDados() { + return dados; + } + + public void setDados(byte[] dados) { + this.dados = dados; + } +} \ No newline at end of file diff --git a/filesys/Diretorio.java b/filesys/Diretorio.java new file mode 100644 index 0000000..90ae5a4 --- /dev/null +++ b/filesys/Diretorio.java @@ -0,0 +1,77 @@ +package filesys; + +import java.util.HashMap; +import java.util.Map; + +public class Diretorio extends ElementoFS { + private Diretorio diretorioPai; + private Map filhos; + private Map acessosPorUsuario; // permissões específicas + + public Diretorio(String nome, String permissoes, String dono) { + super(nome, permissoes, dono); + if (permissoes == null || permissoes.length() != 3) { + throw new IllegalArgumentException("Permissões devem conter 3 caracteres (rwx)"); + } + this.filhos = new HashMap<>(); + this.acessosPorUsuario = new HashMap<>(); + } + + public void setPermissaoUsuario(String nomeUsuario, String permissoes) { + if (permissoes == null || permissoes.length() != 3) { + throw new IllegalArgumentException("Permissões devem conter 3 caracteres (rwx)"); + } + acessosPorUsuario.put(nomeUsuario, permissoes); + } + + public boolean temPermissao(String usuario, char tipoPermissao) { + if ("root".equals(usuario)) return true; + if (usuario.equals(donoDiretorio)) return permissoesPadrao.indexOf(tipoPermissao) >= 0; + String permissoesUsuario = acessosPorUsuario.get(usuario); + if (permissoesUsuario != null && permissoesUsuario.indexOf(tipoPermissao) >= 0) return true; + if (diretorioPai != null) return diretorioPai.temPermissao(usuario, tipoPermissao); + return false; + } + + public String obterPermissoesDoUsuario(String usuario) { + if ("root".equals(usuario)) return "rwx"; + if (usuario.equals(donoDiretorio)) return permissoesPadrao; + return acessosPorUsuario.getOrDefault(usuario, "---"); + } + + public void adicionarFilho(ElementoFS filho) { + if (filhos.containsKey(filho.getNomeDiretorio())) { + throw new IllegalArgumentException("Já existe um filho com este nome: " + filho.getNomeDiretorio()); + } + if (filho instanceof Diretorio) { + ((Diretorio) filho).setDiretorioPai(this); + } + filhos.put(filho.getNomeDiretorio(), filho); + } + + public void removerFilho(String nomeFilho) { + filhos.remove(nomeFilho); + } + + public Map getFilhos() { + return filhos; + } + + public Diretorio getDiretorioPai() { + return diretorioPai; + } + + public void setDiretorioPai(Diretorio diretorioPai) { + this.diretorioPai = diretorioPai; + } + + @Override + public boolean isArquivo() { + return false; + } + + @Override + public String toString() { + return "Dir: " + nomeDiretorio + " | Owner: " + donoDiretorio + " | Perms: " + permissoesPadrao; + } +} diff --git a/filesys/ElementoFS.java b/filesys/ElementoFS.java new file mode 100644 index 0000000..f2e53f2 --- /dev/null +++ b/filesys/ElementoFS.java @@ -0,0 +1,39 @@ +package filesys; + +public abstract class ElementoFS { + protected String nomeDiretorio; + protected String permissoesPadrao; + protected String donoDiretorio; + + public ElementoFS(String nomeDiretorio, String permissoesPadrao, String donoDiretorio) { + this.nomeDiretorio = nomeDiretorio; + this.permissoesPadrao = permissoesPadrao; + this.donoDiretorio = donoDiretorio; + } + + public String getNomeDiretorio() { + return nomeDiretorio; + } + + public void setNomeDiretorio(String nomeDiretorio) { + this.nomeDiretorio = nomeDiretorio; + } + + public String getPermissoesPadrao() { + return permissoesPadrao; + } + + public void setPermissoesPadrao(String permissoesPadrao) { + this.permissoesPadrao = permissoesPadrao; + } + + public String getDonoDiretorio() { + return donoDiretorio; + } + + public void setDonoDiretorio(String donoDiretorio) { + this.donoDiretorio = donoDiretorio; + } + + public abstract boolean isArquivo(); +} diff --git a/filesys/FileSystem.java b/filesys/FileSystem.java index 8d7a83b..bc92c7d 100644 --- a/filesys/FileSystem.java +++ b/filesys/FileSystem.java @@ -1,5 +1,7 @@ package filesys; +import java.util.List; + import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; @@ -9,8 +11,8 @@ final public class FileSystem implements IFileSystem { private final IFileSystem fileSystemImpl; - public FileSystem() { - fileSystemImpl = new FileSystemImpl(); + public FileSystem(List usuarios) { + fileSystemImpl = new FileSystemImpl(usuarios); } @Override @@ -42,9 +44,9 @@ public void write(String caminho, String usuario, boolean anexar, byte[] buffer) } @Override - public void read(String caminho, String usuario, byte[] buffer) + public void read(String caminho, String usuario, byte[] buffer, Offset offset) throws CaminhoNaoEncontradoException, PermissaoException { - fileSystemImpl.read(caminho, usuario, buffer); + fileSystemImpl.read(caminho, usuario, buffer, offset); } @Override diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 45fa05d..1a90b19 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -1,5 +1,9 @@ package filesys; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import exception.CaminhoJaExistenteException; import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; @@ -10,61 +14,563 @@ // 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 Diretorio raiz; + private Map usuarios = new HashMap<>(); + + public FileSystemImpl(List u) { + this.raiz = new Diretorio("/", "rwx", ROOT_USER); // adicionando um diretorio raiz, usuário ROOT tendo todas as + // permissões + usuarios.put(ROOT_USER, new Usuario(ROOT_USER, "rwx", "/")); // criando um usuário com seu diretorio e colocando + // dentro de usuarios + for (Usuario usuario : u) { + if (!usuario.getNome().equalsIgnoreCase("root")) + usuarios.put(usuario.getNome(), usuario); + } + } + + private ElementoFS navegar(String caminho) throws CaminhoNaoEncontradoException { + // Se o caminho for apenas "/", retorna o diretório raiz + if (caminho.equals("/")) + return raiz; + + // Divide o caminho pelos separadores "/" + // Ex: "/home/user/docs" -> ["", "home", "user", "docs"] + String[] partes = caminho.split("/"); - public FileSystemImpl() {} + // Começa a navegação a partir do diretório raiz + Diretorio atual = raiz; + // Itera pelas partes do caminho (ignorando a primeira que é vazia) + for (int i = 1; i < partes.length; i++) { + // Tenta obter o filho (arquivo ou diretório) com o nome da parte atual + ElementoFS filho = atual.getFilhos().get(partes[i]); + + // Se não existir esse filho, lança exceção indicando que o caminho não foi + // encontrado + if (filho == null) + throw new CaminhoNaoEncontradoException("Caminho não encontrado: " + caminho); + + // Se esta é a última parte do caminho, retorna o elemento encontrado (pode ser + // arquivo ou diretório) + if (i == partes.length - 1) + return filho; + + // Se ainda há partes para percorrer e o elemento atual não for um arquivo (ou + // seja, é um diretório), continua a navegação + if (!filho.isArquivo()) { + atual = (Diretorio) filho; + } else { + // Se encontrou um arquivo no meio do caminho, lança exceção (pois não pode + // navegar dentro de um arquivo) + throw new CaminhoNaoEncontradoException("Caminho não encontrado (esperado diretório): " + caminho); + } + } + + // Retorna o último diretório acessado (caso o caminho terminasse em diretório) + return atual; + } + + /** + * Cria diretórios no caminho especificado, incluindo diretórios intermediários + * caso necessário, + * semelhante ao comando `mkdir -p` do Linux. + * + * @param caminho Caminho absoluto do diretório a ser criado. + * @param usuario Usuário que está realizando a operação. + * @throws CaminhoJaExistenteException Se houver um arquivo no meio do caminho + * com o mesmo nome. + * @throws PermissaoException Se o usuário não tiver permissão de + * escrita em algum diretório do caminho. + */ @Override - public void mkdir(String caminho, String nome) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mkdir'"); + public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + // ignora casos inválidos + if (caminho == null || caminho.isEmpty() || caminho.equals("/")) + return; + + // divide o caminho nas partes + // começa da raiz o sistema de arquivos + String[] partes = caminho.split("/"); + Diretorio atual = raiz; + + for (int i = 1; i < partes.length; i++) { + String nomeDir = partes[i]; + if (nomeDir.isEmpty()) + continue; // Ignora barras duplas // + + // busca se o filho ja possui diretorios com esse nome + ElementoFS filho = atual.getFilhos().get(nomeDir); + + if (filho == null) { + // Verifica permissão de escrita antes de criar + if (!atual.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão para criar em: " + getCaminhoCompleto(atual, nomeDir)); + } + + // criando o diretorio e adicionando + Diretorio novoDir = new Diretorio(nomeDir, "rwx", usuario); + atual.adicionarFilho(novoDir); + atual = novoDir; + } else if (filho.isArquivo()) { + throw new CaminhoJaExistenteException( + "Já existe arquivo com esse nome: " + getCaminhoCompleto(atual, nomeDir)); + } else { + atual = (Diretorio) filho; + } + } + } + + /** + * Monta o caminho completo de um diretório, adicionando opcionalmente o nome de + * um novo elemento. + * + * @param atual Diretório de referência. + * @param nomeNovo Nome do novo diretório ou arquivo a ser incluído no final do + * caminho. + * @return String com o caminho completo. + */ + private String getCaminhoCompleto(Diretorio atual, String nomeNovo) { + StringBuilder sb = new StringBuilder(); + + while (atual != null && atual.getNomeDiretorio() != null && !atual.getNomeDiretorio().equals("/")) { + sb.insert(0, "/" + atual.getNomeDiretorio()); + atual = atual.getDiretorioPai(); + } + sb.insert(0, "/"); + + if (nomeNovo != null && !nomeNovo.isEmpty()) { + if (sb.length() > 1) + sb.append("/"); + sb.append(nomeNovo); + } + + return sb.toString().replaceAll("//+", "/"); } @Override public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'chmod'"); + // 1) Validar string de permissão: deve ter exatamente 3 caracteres, cada um + // 'r','w','x' ou '-' + if (permissao == null || permissao.length() != 3) { + throw new IllegalArgumentException("Permissão inválida (deve ter 3 chars): " + permissao); + } + for (char c : permissao.toCharArray()) { + if (c != 'r' && c != 'w' && c != 'x' && c != '-') { + throw new IllegalArgumentException("Permissão contém caractere inválido: " + c); + } + } + + // 2) Localizar o objeto (Arquivo ou Diretório) em 'caminho' + Object objAlvo = navegar(caminho); + + // 3) Verificar se 'usuario' tem permissão para executar chmod: + // - Se for ROOT_USER, sempre permitido. + // - Caso contrário, somente se for dono do objeto + String dono; + if (objAlvo instanceof Arquivo) { + dono = ((Arquivo) objAlvo).getDonoDiretorio(); + } else if (objAlvo instanceof Diretorio) { + dono = ((Diretorio) objAlvo).getDonoDiretorio(); + } else { + throw new CaminhoNaoEncontradoException("Caminho encontrado não é arquivo nem diretório: " + caminho); + } + if (!usuario.equals("root") && !usuario.equals(dono)) { + throw new PermissaoException( + "Usuário '" + usuario + "' não tem permissão para alterar direitos em: " + caminho); + } + + // 4) Alterar (ou inserir) a permissão de 'usuarioAlvo' para 'permissao' + if (objAlvo instanceof Arquivo) { + ((Arquivo) objAlvo).setPermissoesPadrao(permissao); + } else if (objAlvo instanceof Diretorio) { + ((Diretorio) objAlvo).setPermissaoUsuario(usuarioAlvo, permissao); + } } @Override public void rm(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'rm'"); + if (caminho == null || caminho.equals("/") || caminho.isEmpty()) { + throw new CaminhoNaoEncontradoException("Não é permitido remover a raiz ou caminho vazio."); + } + + // Descobre diretório pai e nome do elemento a remover + int idx = caminho.lastIndexOf("/"); + String paiPath = (idx == 0) ? "/" : caminho.substring(0, idx); + String nome = caminho.substring(idx + 1); + + ElementoFS paiElem = navegar(paiPath); + if (!(paiElem instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + paiPath); + } + Diretorio dirPai = (Diretorio) paiElem; + + // Verifica permissão de escrita no diretório pai + if (!dirPai.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita em: " + paiPath); + } + + ElementoFS alvo = dirPai.getFilhos().get(nome); + if (alvo == null) { + throw new CaminhoNaoEncontradoException("Arquivo ou diretório não encontrado: " + caminho); + } + + if (!alvo.isArquivo()) { + Diretorio dirAlvo = (Diretorio) alvo; + if (!recursivo && !dirAlvo.getFilhos().isEmpty()) { + throw new PermissaoException("Diretório não está vazio. Use o modo recursivo para remover: " + caminho); + } + if (recursivo) { + removerDiretorioRecursivo(dirAlvo, usuario); + } else if (dirAlvo.getFilhos().isEmpty()) { + dirPai.removerFilho(nome); + } + } else { + dirPai.removerFilho(nome); + } + } + + // Método auxiliar para remoção recursiva de diretórios + private void removerDiretorioRecursivo(Diretorio dir, String usuario) throws PermissaoException { + for (ElementoFS filho : dir.getFilhos().values().toArray(new ElementoFS[0])) { + if (!filho.isArquivo()) { + removerDiretorioRecursivo((Diretorio) filho, usuario); + } + dir.removerFilho(filho.getNomeDiretorio()); + } + // Após remover todos os filhos, remove o próprio diretório do pai + Diretorio pai = dir.getDiretorioPai(); + if (pai != null) { + pai.removerFilho(dir.getNomeDiretorio()); + } } + /** + * Método touch: cria um arquivo vazio em 'caminho'. + * Exemplo: touch("/usr/local/meuarquivo.txt", "alice") + * -> pai = "/usr/local", nome = "meuarquivo.txt" + */ @Override - public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'touch'"); + public void touch(String caminho, String usuario) + throws CaminhoJaExistenteException, PermissaoException { + // 1) Separar o caminho em 'pai' e 'nomeDoArquivo' + String path = caminho.trim(); + if (!path.startsWith("/")) { + throw new CaminhoJaExistenteException("Caminho inválido (deve começar com '/'): " + caminho); + } + if (path.equals("/")) { + throw new CaminhoJaExistenteException("Não é possível criar arquivo na raiz sem nome: " + caminho); + } + + int indexSlash = path.lastIndexOf("/"); + String paiPath = (indexSlash == 0) ? "/" : path.substring(0, indexSlash); + String nomeArquivo = path.substring(indexSlash + 1); + if (nomeArquivo.isEmpty()) { + throw new CaminhoJaExistenteException("Nome de arquivo vazio em: " + caminho); + } + + // 2) Localizar o diretório pai + Diretorio dirPai; + try { + Object o = navegar(paiPath); + if (!(o instanceof Diretorio)) { + throw new CaminhoJaExistenteException("Caminho pai não é um diretório: " + paiPath); + } + dirPai = (Diretorio) o; + } catch (CaminhoNaoEncontradoException e) { + throw new CaminhoJaExistenteException("Caminho não encontrado: " + paiPath); + } + + // 3) Verificar permissão de escrita no dirPai para o usuário informado + if (!dirPai.temPermissao(usuario, 'w')) { + throw new PermissaoException( + "Usuário '" + usuario + "' não tem permissão de escrita em: " + paiPath); + } + + // 4) Verificar se já existe um arquivo ou diretório com esse nome em dirPai + if (dirPai.getFilhos().containsKey(nomeArquivo)) { + throw new CaminhoJaExistenteException( + "Já existe arquivo ou diretório chamado '" + nomeArquivo + "' em: " + paiPath); + } + + // 5) Cria o arquivo vazio (sem blocos) + // Permissões padrão: "rw-" para o dono + Arquivo novoArq = new Arquivo(nomeArquivo, "rw-", usuario); + // Nenhum bloco é adicionado (arquivo vazio) + + // 6) Adiciona ao diretório pai + dirPai.adicionarFilho(novoArq); } @Override public void write(String caminho, String usuario, boolean anexar, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'write'"); + // 1) Localiza o arquivo + Object obj = navegar(caminho); + if (!(obj instanceof Arquivo)) { + throw new CaminhoNaoEncontradoException("Arquivo não encontrado: " + caminho); + } + Arquivo arquivo = (Arquivo) obj; + + // 2) Verifica permissão de escrita + if (!usuario.equals(ROOT_USER) && !arquivo.donoDiretorio.equals(usuario) + && !arquivo.permissoesPadrao.contains("w")) { + throw new PermissaoException("Sem permissão de escrita em: " + caminho); + } + + // 3) Se não for append, sobrescreve o arquivo (limpa blocos) + if (!anexar) { + arquivo.limparBlocos(); + } + + // 4) Escreve o buffer em blocos de 4096 bytes (tamanho do bloco) + // buffer -> conteudo completo a ser salvo + int TAMANHO_BLOCO = 4096; // define o tamanho de cada bloco + // evita escrever tudo de uma vez no buffer + // se diminuir o tamanho do bloco, o desperdício cai, porém a memória para + // manter metadados cresce + int bufferOffset = 0; // se o bufferOffset não tem controler de pro onde continuar a leitura do buffer + while (bufferOffset < buffer.length) { + int bytesParaEscrever = Math.min(TAMANHO_BLOCO, buffer.length - bufferOffset); + byte[] bloco = new byte[bytesParaEscrever]; + System.arraycopy(buffer, bufferOffset, bloco, 0, bytesParaEscrever); + arquivo.adicionarBloco(bloco); + bufferOffset += bytesParaEscrever; + } } @Override - public void read(String caminho, String usuario, byte[] buffer) + public void read(String caminho, String usuario, byte[] buffer, Offset offset) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'read'"); + // 1) Localiza o arquivo + Object obj = navegar(caminho); + if (!(obj instanceof Arquivo)) { + throw new CaminhoNaoEncontradoException("Arquivo não encontrado: " + caminho); + } + Arquivo arquivo = (Arquivo) obj; + + if (!usuario.equals(ROOT_USER) && !arquivo.permissoesPadrao.contains("r")) { + throw new PermissaoException("Sem permissão de leitura em: " + caminho); + } + + int readOffset = (offset != null) ? offset.getValue() : 0; // onde dentro do arquivo vc deve começar a ler + int bufferPos = 0; // indica em que posição do buffer de destino você ja escreveu dados lidos + int filePos = 0; // marca qual posição de leitura do arquivo vc ja consumiu + + for (byte[] bloco : arquivo.getBlocos()) { + // 1) Se todo o bloco ainda está antes do offset, pule-o por inteiro: + if (filePos + bloco.length <= readOffset) { + filePos += bloco.length; // avança a posição no arquivo + continue; // vai para o próximo bloco + } + + // 2) Determina onde, dentro do bloco atual, começar a copiar: + // - Se readOffset > filePos, significa que já consumimos parte desse bloco + int blocoOffset = Math.max(0, readOffset - filePos); + + // 3) Quantos bytes desse bloco cabem no buffer restante? + int bytesParaLer = Math.min( + bloco.length - blocoOffset, // do bloco a partir de blocoOffset + buffer.length - bufferPos // até encher o buffer destino + ); + + // Se não há mais nada a ler (buffer cheio ou bloco já totalmente antes do + // offset), encerra: + if (bytesParaLer <= 0) + break; + + // 4) Copia bytesParaLer do bloco para o buffer: + // de bloco[blocoOffset … blocoOffset+bytesParaLer-1] + // para buffer[bufferPos … bufferPos+bytesParaLer-1] + System.arraycopy(bloco, blocoOffset, buffer, bufferPos, bytesParaLer); + + // 5) Atualiza índices: + bufferPos += bytesParaLer; // avançou no buffer destino + filePos += bloco.length; // bloco todo foi “lido” em termos de filePos + readOffset += bytesParaLer; // avança a posição real de leitura + + // 6) Se o buffer ficou cheio, pare de ler: + if (bufferPos >= buffer.length) + break; + } + + if (offset != null) + offset.setValue(readOffset); } @Override public void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mv'"); + // 1) Localiza o elemento a ser movido + ElementoFS elemento = navegar(caminhoAntigo); + + // 2) Verifica permissão de escrita no diretório pai do antigo e do novo caminho + String antigoPaiPath = caminhoAntigo.substring(0, caminhoAntigo.lastIndexOf("/")); + if (antigoPaiPath.isEmpty()) + antigoPaiPath = "/"; + Diretorio dirPaiAntigo = (Diretorio) navegar(antigoPaiPath); + if (!dirPaiAntigo.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita no diretório de origem: " + antigoPaiPath); + } + + int idxNovo = caminhoNovo.lastIndexOf("/"); + String novoPaiPath = (idxNovo == 0) ? "/" : caminhoNovo.substring(0, idxNovo); + String novoNome = caminhoNovo.substring(idxNovo + 1); + Diretorio dirPaiNovo = (Diretorio) navegar(novoPaiPath); + if (!dirPaiNovo.temPermissao(usuario, 'w')) { + throw new PermissaoException("Sem permissão de escrita no diretório de destino: " + novoPaiPath); + } + + // 3) Não sobrescreve arquivos/diretórios existentes + if (dirPaiNovo.getFilhos().containsKey(novoNome)) { + throw new CaminhoNaoEncontradoException( + "Já existe arquivo ou diretório com esse nome no destino: " + caminhoNovo); + } + + // 4) Remove do diretório antigo + dirPaiAntigo.removerFilho(elemento.getNomeDiretorio()); + + // 5) Atualiza nome se for renomeação + elemento.setNomeDiretorio(novoNome); + + // 6) Adiciona ao novo diretório + dirPaiNovo.adicionarFilho(elemento); + + // 7) Se for diretório, atualiza referência de pai + if (!elemento.isArquivo() && elemento instanceof Diretorio) { + ((Diretorio) elemento).setDiretorioPai(dirPaiNovo); + } } @Override - public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'ls'"); + public void ls(String caminho, String usuario, boolean recursivo) + throws CaminhoNaoEncontradoException, PermissaoException { + // 1) Busca o objeto pelo caminho fornecido + Object obj = navegar(caminho); + + // **Novo**: se for arquivo e não for recursivo, imprime o próprio nome e + // retorna + if (obj instanceof Arquivo && !recursivo) { + System.out.println(((Arquivo) obj).getNomeDiretorio()); + return; + } + + // 2) Se não for um diretório, lança exceção + if (!(obj instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Não é um diretório: " + caminho); + } + Diretorio dir = (Diretorio) obj; + + // 3) Verifica permissão de leitura + if (!dir.temPermissao(usuario, 'r')) { + throw new PermissaoException("Sem permissão de leitura em: " + caminho); + } + + // 4) Lista o conteúdo do diretório + listarConteudo(dir, caminho, recursivo, ""); + } + + // Método auxiliar para listar conteúdo + private void listarConteudo(Diretorio dir, String caminho, boolean recursivo, String prefixo) { + for (ElementoFS filho : dir.getFilhos().values()) { + System.out.println(prefixo + filho.getNomeDiretorio()); + if (recursivo && !filho.isArquivo()) { + listarConteudo((Diretorio) filho, + caminho + "/" + filho.getNomeDiretorio(), + true, + prefixo + " "); + } + } } @Override public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'cp'"); + ElementoFS origem = navegar(caminhoOrigem); + + // Verifica permissão de leitura na origem + if (origem.isArquivo()) { + if (!usuario.equals(ROOT_USER) && !origem.donoDiretorio.equals(usuario) + && !origem.permissoesPadrao.contains("r")) { + throw new PermissaoException("Sem permissão de leitura em: " + caminhoOrigem); + } + try { + copiarArquivo((Arquivo) origem, caminhoDestino, usuario); + } catch (CaminhoJaExistenteException e) { + throw new PermissaoException("Já existe arquivo ou diretório em: " + caminhoDestino); + } + } else { + Diretorio dirOrigem = (Diretorio) origem; + if (!usuario.equals(ROOT_USER) && !dirOrigem.donoDiretorio.equals(usuario) + && !dirOrigem.permissoesPadrao.contains("r")) { + throw new PermissaoException("Sem permissão de leitura em: " + caminhoOrigem); + } + if (!recursivo) { + throw new PermissaoException("Cópia de diretório requer opção recursiva."); + } + try { + copiarDiretorioRecursivo(dirOrigem, caminhoDestino, usuario); + } catch (CaminhoJaExistenteException e) { + throw new PermissaoException("Já existe arquivo ou diretório em: " + caminhoDestino); + } + } } - public void addUser(String user) { - throw new UnsupportedOperationException("Método não implementado 'addUser'"); + private void copiarArquivo(Arquivo origem, String caminhoDestino, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + // Verifica se já existe algo no destino + try { + navegar(caminhoDestino); + throw new CaminhoJaExistenteException("Já existe arquivo ou diretório em: " + caminhoDestino); + } catch (CaminhoNaoEncontradoException e) { + // ok, pode criar + } + + // Descobre diretório pai e nome do novo arquivo + int idx = caminhoDestino.lastIndexOf("/"); + String paiPath = (idx == 0) ? "/" : caminhoDestino.substring(0, idx); + String nomeArquivo = caminhoDestino.substring(idx + 1); + + ElementoFS pai = navegar(paiPath); + if (!(pai instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + paiPath); + } + Diretorio dirPai = (Diretorio) pai; + + // Cria novo arquivo com mesmo conteúdo e permissões do usuário + Arquivo novoArq = new Arquivo(nomeArquivo, origem.permissoesPadrao, usuario); + for (byte[] bloco : origem.getBlocos()) { + novoArq.adicionarBloco(bloco.clone()); + } + dirPai.adicionarFilho(novoArq); } + + private void copiarDiretorioRecursivo(Diretorio origem, String caminhoDestino, String usuario) + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + // Descobre diretório pai e nome do novo diretório + int idx = caminhoDestino.lastIndexOf("/"); + String paiPath = (idx == 0) ? "/" : caminhoDestino.substring(0, idx); + String nomeDir = caminhoDestino.substring(idx + 1); + + ElementoFS pai = navegar(paiPath); + if (!(pai instanceof Diretorio)) { + throw new CaminhoNaoEncontradoException("Diretório pai não encontrado: " + paiPath); + } + Diretorio dirPai = (Diretorio) pai; + + // Cria novo diretório + Diretorio novoDir = new Diretorio(nomeDir, origem.permissoesPadrao, usuario); + dirPai.adicionarFilho(novoDir); + + // Copia arquivos + for (ElementoFS filho : origem.getFilhos().values()) { + if (filho.isArquivo()) { + copiarArquivo((Arquivo) filho, caminhoDestino + "/" + filho.nomeDiretorio, usuario); + } else { + copiarDiretorioRecursivo((Diretorio) filho, caminhoDestino + "/" + filho.nomeDiretorio, usuario); + } + } + } + } diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index a0f0ce0..8a448ff 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -31,15 +31,17 @@ public interface IFileSystem { // Lê dados de um arquivo. Se o arquivo não existir, será lançada uma exceção. // Leitura sequencial - todo o conteudo do arquivo sera lido e armazenado no buffer. - void read(String caminho, String usuario, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException; + void read(String caminho, String usuario, byte[] buffer, Offset offset) throws CaminhoNaoEncontradoException, PermissaoException; // Move ou renomeia um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. // Se o diretório já existir, será sobrescrito. // mv é naturalmente recursivo. + // arlindo void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException; // Lista o conteúdo de um diretório. Se o diretório não existir, será lançada uma exceção. // Caso recursivo seja true, todo o conteúdo do diretório será listado recursivamente. + //joaquim void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException; // Copia um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. diff --git a/filesys/Offset.java b/filesys/Offset.java new file mode 100644 index 0000000..56430a8 --- /dev/null +++ b/filesys/Offset.java @@ -0,0 +1,53 @@ +package filesys; + +public class Offset { + private int value; + private int max; + + public Offset() { + this.setValue(0); + this.setMax(Integer.MAX_VALUE); + } + + public Offset(int initialValue) { + this.setValue(initialValue); + this.setMax(Integer.MAX_VALUE); + } + + public Offset(int initialValue, int max) { + this.setValue(initialValue); + this.setMax(max); + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + if (value < 0) { + throw new IllegalArgumentException("Offset não pode ser negativo."); + } + if (value > max) { + throw new IllegalArgumentException("Offset excede o valor máximo permitido (" + max + ")."); + } + this.value = value; + } + + public void add(int delta) { + setValue(this.value + delta); + } + + public void reset() { + this.value = 0; + } + + public void setMax(int max) { + if (max < 0) throw new IllegalArgumentException("Max deve ser positivo."); + this.max = max; + if (value > max) value = max; // ajusta valor atual se necessário + } + + public int getMax() { + return max; + } +} diff --git a/filesys/Permissao.java b/filesys/Permissao.java new file mode 100644 index 0000000..3cdc360 --- /dev/null +++ b/filesys/Permissao.java @@ -0,0 +1,31 @@ +package filesys; + +import java.util.HashMap; +import java.util.Map; + +public class Permissao { + //quem pode fazer o quê é definido aqui + private Map permissoesPorUsuario; + + public Permissao() { + this.permissoesPorUsuario = new HashMap<>(); + } + + public void definirPermissao(String usuario, String permissao) { + permissoesPorUsuario.put(usuario, permissao); // ex: "rw", "r", "-" + } + + public boolean podeLer(String usuario) { + String p = permissoesPorUsuario.getOrDefault(usuario, ""); + return p.contains("r"); + } + + public boolean podeEscrever(String usuario) { + String p = permissoesPorUsuario.getOrDefault(usuario, ""); + return p.contains("w"); + } + + public String getPermissao(String usuario) { + return permissoesPorUsuario.getOrDefault(usuario, "-"); + } +} diff --git a/filesys/Usuario.java b/filesys/Usuario.java new file mode 100644 index 0000000..14bffac --- /dev/null +++ b/filesys/Usuario.java @@ -0,0 +1,51 @@ +package filesys; + +public class Usuario { + private String nome; + private String diretorio; + private String permissao; + + public Usuario(String nome, String permissao, String diretorio) { + this.setNome(nome); + this.setPermissao(permissao); + this.setDiretorio(diretorio); + } + + public String getNome() { + return nome; + } + + public String getPermissao() { + return permissao; + } + + public void setPermissao(String permissao) { + this.permissao = permissao; + } + + public void setNome(String nome) { + this.nome = nome; + } + + public void setDiretorio(String diretorio) { + this.diretorio = diretorio; + } + + + + @Override + public String toString() { + return "Usuario{" + + "nome='" + nome + '\'' + + ", permissao='" + permissao + '\'' + + ", diretorio='" + diretorio + '\'' + + '}'; + } + + public String getDiretorio() { + return diretorio; + } + + + +} \ No newline at end of file diff --git a/tests/FileSystemImplTest.java b/tests/FileSystemImplTest.java new file mode 100644 index 0000000..93117c8 --- /dev/null +++ b/tests/FileSystemImplTest.java @@ -0,0 +1,214 @@ +package tests; + +import exception.CaminhoJaExistenteException; +import exception.CaminhoNaoEncontradoException; +import exception.PermissaoException; +import filesys.FileSystemImpl; +import filesys.Offset; +import filesys.Usuario; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.*; + +class FileSystemImplTest { + + private FileSystemImpl fs; + private Usuario root; + + @BeforeEach + void setUp() { + root = new Usuario("root", "rwx", "/"); + fs = new FileSystemImpl(Arrays.asList(root)); + } + + /** + * Testa a criação de diretórios e arquivos. + * Verifica se mkdir e touch funcionam e se mkdir é idempotente. + */ + @Test + void testMkdirAndTouch() throws Exception { + fs.mkdir("/home/root/docs", "root"); + fs.touch("/home/root/docs/file.txt", "root"); + assertDoesNotThrow(() -> fs.mkdir("/home/root/docs", "root")); + } + + /** + * Testa se o touch lança exceção ao tentar criar arquivo sem permissão de escrita. + */ + @Test + void testTouchNoWritePermission() throws Exception { + fs.mkdir("/docs", "root"); + fs.chmod("/docs", "root", "root", "---"); + // Tenta criar arquivo como maria (não-root) + assertThrows(PermissaoException.class, () -> fs.touch("/docs/file.txt", "maria")); + } + + /** + * Testa escrita e leitura de arquivos. + * Verifica se o conteúdo lido é igual ao escrito. + */ + @Test + void testWriteAndReadFile() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + byte[] data = "hello world".getBytes(); + fs.write("/docs/file.txt", "root", false, data); + + byte[] buffer = new byte[20]; + Offset offset = new Offset(); + fs.read("/docs/file.txt", "root", buffer, offset); + String read = new String(buffer, 0, "hello world".length()); + assertEquals("hello world", read); + } + + /** + * Testa se a escrita falha quando não há permissão de escrita. + */ + @Test + void testWriteNoPermission() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.chmod("/docs/file.txt", "root", "root", "r--"); // remove permissão de escrita + + // Tenta escrever como maria (não-root, não-dono) + assertThrows(PermissaoException.class, () -> { + fs.write("/docs/file.txt", "maria", false, "fail".getBytes()); + }); + } + + /** + * Testa se a leitura falha quando não há permissão de leitura. + */ + @Test + void testReadNoPermission() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.write("/docs/file.txt", "root", false, "abc".getBytes()); + + // Remove todas as permissões do arquivo + fs.chmod("/docs/file.txt", "root", "root", "---"); + + // Tenta ler como um usuário não-root (deve lançar PermissaoException) + assertThrows(PermissaoException.class, () -> { + fs.read("/docs/file.txt", "maria", new byte[10], new Offset()); + }); +} + + /** + * Testa remoção de arquivos e diretórios. + * Verifica se a remoção de um diretório inexistente lança exceção. + */ + @Test + void testRmFileAndDirectory() throws Exception { + fs.mkdir("/tmp", "root"); + fs.touch("/tmp/file.txt", "root"); + fs.rm("/tmp/file.txt", "root", false); + fs.rm("/tmp", "root", false); + assertThrows(CaminhoNaoEncontradoException.class, () -> fs.rm("/tmp", "root", false)); + } + + /** + * Testa se a remoção de diretório não vazio sem recursão lança exceção. + */ + @Test + void testRmNonEmptyDirWithoutRecursive() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file.txt", "root"); + assertThrows(PermissaoException.class, () -> fs.rm("/dir", "root", false)); + } + + /** + * Testa remoção recursiva de diretório não vazio. + */ + @Test + void testRmNonEmptyDirWithRecursive() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file.txt", "root"); + fs.rm("/dir", "root", true); + assertThrows(CaminhoNaoEncontradoException.class, () -> fs.rm("/dir", "root", false)); + } + + /** + * Testa chmod e permissões de escrita. + */ + @Test + void testChmodAndPermissions() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.chmod("/docs/file.txt", "root", "root", "rw-"); + fs.write("/docs/file.txt", "root", false, "ok".getBytes()); + } + + /** + * Testa se ls lista o conteúdo do diretório sem lançar exceção. + */ + @Test + void testLsListsContents() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file1.txt", "root"); + fs.touch("/dir/file2.txt", "root"); + assertDoesNotThrow(() -> fs.ls("/dir", "root", false)); + } + + /** + * Testa o comando mv para mover e renomear arquivos. + */ + @Test + void testMvFile() throws Exception { + // Cria diretório e arquivo + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + // Move arquivo para novo nome + fs.mv("/docs/file.txt", "/docs/file2.txt", "root"); + // Verifica se o arquivo antigo não existe mais e o novo existe + assertThrows(CaminhoNaoEncontradoException.class, () -> fs.ls("/docs/file.txt", "root", false)); + assertDoesNotThrow(() -> fs.ls("/docs/file2.txt", "root", false)); + } + + /** + * Testa o comando cp para copiar arquivos. + */ + @Test + void testCpFile() throws Exception { + fs.mkdir("/docs", "root"); + fs.touch("/docs/file.txt", "root"); + fs.write("/docs/file.txt", "root", false, "abc".getBytes()); + fs.cp("/docs/file.txt", "/docs/file2.txt", "root", false); + + byte[] buffer = new byte[10]; + Offset offset = new Offset(); + fs.read("/docs/file2.txt", "root", buffer, offset); + String read = new String(buffer, 0, 3); + assertEquals("abc", read); + } + + /** + * Testa o comando cp para copiar diretórios recursivamente. + */ + @Test + void testCpDirectoryRecursive() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file.txt", "root"); + fs.write("/dir/file.txt", "root", false, "xyz".getBytes()); + fs.cp("/dir", "/dir2", "root", true); + + byte[] buffer = new byte[10]; + Offset offset = new Offset(); + fs.read("/dir2/file.txt", "root", buffer, offset); + String read = new String(buffer, 0, 3); + assertEquals("xyz", read); + } + + /** + * Testa se a cópia de diretório sem recursão lança exceção. + */ + @Test + void testCpDirectoryWithoutRecursiveThrows() throws Exception { + fs.mkdir("/dir", "root"); + fs.touch("/dir/file.txt", "root"); + assertThrows(PermissaoException.class, () -> fs.cp("/dir", "/dir2", "root", false)); + } +} \ No newline at end of file diff --git a/tests/PermissionTest.java b/tests/PermissionTest.java index 6026e31..81561bb 100644 --- a/tests/PermissionTest.java +++ b/tests/PermissionTest.java @@ -2,12 +2,14 @@ import static org.junit.Assert.assertTrue; +import java.util.Collections; + import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeAll; import filesys.FileSystemImpl; import filesys.IFileSystem; +import filesys.Usuario; // Essa classe testa cenários de permissão public class PermissionTest { @@ -15,7 +17,7 @@ public class PermissionTest { @BeforeAll public static void setUp() { - fileSystem = new FileSystemImpl(/*args...*/); + fileSystem = new FileSystemImpl(Collections.singletonList(new Usuario("root", "rwx", "/"))); } @Test diff --git a/users/userMkdir b/users/userMkdir new file mode 100644 index 0000000..8de4353 --- /dev/null +++ b/users/userMkdir @@ -0,0 +1,13 @@ +joao / rwx +joao /users rwx +joao /users/joao rwx +joao /users/joao/documentos rwx +joao /users/joao/documentos/viagem rwx +joao /users/joao/documentos/viagem/2024 rwx +joao /users/joao/documentos/viagem/2024/dezembro rwx +joao /users/joao/documentos/viagem/2024/dezembro/fotossíntese rwx +joao /users/joao/documentos/viagem/2024/dezembro/fotos rwx +joao /users/joao/documentos/viagem/2024/janeiro rwx +joao /users/joao/documentos/viagem/2023 rwx +joao /users/joao/documentos/viagem/2023/janeiro rwx +joao /users/joao/documentos/viagem/2023/janeiro/fotos rwx \ No newline at end of file