diff --git a/Main.java b/Main.java deleted file mode 100644 index 3b8673a..0000000 --- a/Main.java +++ /dev/null @@ -1,249 +0,0 @@ -import filesys.IFileSystem; - -import java.util.Scanner; -import java.io.FileNotFoundException; - -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 -public class Main { - - // Constantes úteis para a versão interativa. - // Para esse tipo de execução, o tamanho max do buffer de - // leitura pode ser menor. - private static final String ROOT_USER = "root"; - private static final String ROOT_DIR = "/"; - private static final int READ_BUFFER_SIZE = 256; - - // Sistema de arquivos - private static IFileSystem fileSystem; - - // Scanner para leitura de entrada do usuário - private static Scanner scanner = new Scanner(System.in); - - // 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. - 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. - try { - Scanner userScanner = new Scanner(new java.io.File("users/users")); - while (userScanner.hasNextLine()) { - String line = userScanner.nextLine().trim(); - if (!line.isEmpty()) { - String[] parts = line.split(" "); - if (parts.length == 3) { - String userListed = parts[0]; - String dir = parts[1]; - String dirPermission = parts[2]; - - /* A FAZER: - * Processar a permissão de todos os usuários existentes por diretório. - * Por enquanto esse código somente imprime as permissões contidas no arquivo users. - */ - System.out.println(userListed + " " + dir + " " + dirPermission); // Somente imprime o usuário, diretório e permissão - - - } else { - System.out.println("Formato ruim no arquivo de usuários. Linha: " + line); - } - } - } - userScanner.close(); - } catch (FileNotFoundException e) { // Retorna se o arquivo de usuários não for encontrado - System.out.println("Arquivo de usuários não encontrado"); - - return; - } - - // Finalmente cria o Sistema de Arquivos - // Lista de usuários é imutável durante a execução do programa - // Obs: Como passar a lista de usuários para o FileSystem? - fileSystem = new FileSystem(/*usuários?*/); - - // // DESCOMENTE O BLOCO ABAIXO PARA CRIAR O DIRETÓRIO RAIZ ANTES DE RODAR O MENU - // // Cria o diretório raiz do sistema. Root sempre tem permissão total "rwx" - // try { - // fileSystem.mkdir(ROOT_DIR, ROOT_USER); - // } catch (CaminhoJaExistenteException | PermissaoException e) { - // System.out.println(e.getMessage()); - // } - - // Menu interativo. - menu(); - } - - // Menu interativo para fins de teste. - // Os testes junit não são feitos com esse menu, - // mas diretamente na interface IFileSystem - public static void menu() { - while (true) { - System.out.println("\nComandos disponíveis:"); - System.out.println("1. chmod - Alterar permissões"); - System.out.println("2. mkdir - Criar diretório"); - System.out.println("3. rm - Remover arquivo/diretório"); - System.out.println("4. touch - Criar arquivo"); - System.out.println("5. write - Escrever em arquivo"); - System.out.println("6. read - Ler arquivo"); - System.out.println("7. mv - Mover/renomear arquivo"); - System.out.println("8. ls - Listar diretório"); - System.out.println("9. cp - Copiar arquivo"); - System.out.println("0. exit - Sair"); - System.out.print("\nDigite o comando desejado: "); - - String opcao = scanner.nextLine(); - try { - switch (opcao) { - case "1": - chmod(); - break; - case "2": - mkdir(); - break; - case "3": - rm(); - break; - case "4": - touch(); - break; - case "5": - write(); - break; - case "6": - read(); - break; - case "7": - mv(); - break; - case "8": - ls(); - break; - case "9": - cp(); - break; - case "0": - System.out.println("Encerrando..."); - - return; - default: - System.out.println("Comando inválido!"); - } - } catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException e) { - System.out.println("Erro: " + e.getMessage()); - } - - System.out.println("Pressione Enter para continuar..."); - scanner.nextLine(); - System.out.print("\033[H\033[2J"); - System.out.flush(); - } - } - - public static void chmod() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho do arquivo ou diretório:"); - 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\"):"); - 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); - } - - public static void rm() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho do diretório a ser removido:"); - 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); - } - - public static void write() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho do arquivo a ser escrito:"); - String caminho = scanner.nextLine(); - System.out.println("Anexar? (true/false):"); - boolean anexar = Boolean.parseBoolean(scanner.nextLine()); - System.out.println("Insira o conteúdo a ser escrito:"); - 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. - } - - public static void mv() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho do arquivo a ser movido:"); - String caminhoAntigo = scanner.nextLine(); - System.out.println("Insira o novo caminho do arquivo:"); - String caminhoNovo = scanner.nextLine(); - - fileSystem.mv(caminhoAntigo, caminhoNovo, user); - } - - public static void ls() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho do diretório a ser listado:"); - String caminho = scanner.nextLine(); - System.out.println("Listar recursivamente? (true/false):"); - boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); - - fileSystem.ls(caminho, user, recursivo); - } - - public static void cp() throws CaminhoNaoEncontradoException, PermissaoException { - System.out.println("Insira o caminho da origem do arquivo a ser copiado:"); - String caminhoOrigem = scanner.nextLine(); - System.out.println("Insira o caminho do destino do arquivo a ser copiado:"); - 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/README.md b/README.md new file mode 100644 index 0000000..24124e9 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +TRABALHO PRATICO SISTEMAS OPERACIONAIS; + +GRUPO: ARTHUR ANGONESI MENDES, BERNARDO ALVAREZ, BRUNO FERRAZ + +ENTREGA FINAL: 20/06 diff --git a/TESTES TP PRATICO.docx b/TESTES TP PRATICO.docx new file mode 100644 index 0000000..9219d5d Binary files /dev/null and b/TESTES TP PRATICO.docx differ diff --git a/filesys/Block.java b/filesys/Block.java new file mode 100644 index 0000000..492cc86 --- /dev/null +++ b/filesys/Block.java @@ -0,0 +1,45 @@ +package filesys; + +public class Block { + private byte[] data; + private int currentSize; + private final int capacity; + + public Block(int capacity) { + this.capacity = capacity; + this.data = new byte[capacity]; + this.currentSize = 0; + } + + public int getCapacity() { + return capacity; + } + + public int getCurrentSize() { + return currentSize; + } + + public void setCurrentSize(int size) { + this.currentSize = size; + } + + public byte[] getData() { + return data; + } + + // Escreve até 'length' bytes de 'src' começando em 'srcOffset' para o bloco a partir de 'blockOffset' + public int write(byte[] src, int srcOffset, int length, int blockOffset) { + if (blockOffset >= capacity) return 0; + int bytesToWrite = Math.min(length, capacity - blockOffset); + System.arraycopy(src, srcOffset, data, blockOffset, bytesToWrite); + return bytesToWrite; + } + + // Lê até 'length' bytes do bloco a partir de 'blockOffset' para 'dest' começando em 'destOffset' + public int read(byte[] dest, int destOffset, int length, int blockOffset) { + if (blockOffset >= currentSize) return 0; + int bytesToRead = Math.min(length, currentSize - blockOffset); + System.arraycopy(data, blockOffset, dest, destOffset, bytesToRead); + return bytesToRead; + } +} \ No newline at end of file diff --git a/filesys/Directory.java b/filesys/Directory.java new file mode 100644 index 0000000..e76d077 --- /dev/null +++ b/filesys/Directory.java @@ -0,0 +1,67 @@ +package filesys; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class Directory { + private MetaData metaData; + private Map subdirectories; + private Map files; + + public Directory(String name, String owner) { + this.metaData = new MetaData(name, owner, true); + this.subdirectories = new LinkedHashMap<>(); + this.files = new LinkedHashMap<>(); + } + + public MetaData getMetaData() { return metaData; } + + public Map getSubdirectories() { return subdirectories; } + public Map getFiles() { return files; } + + public boolean containsSubdirectory(String name) { + return subdirectories.containsKey(name); + } + + public boolean containsFile(String name) { + return files.containsKey(name); + } + + public Directory getSubdirectory(String name) { + return subdirectories.get(name); + } + + public File getFile(String name) { + return files.get(name); + } + + public void addSubdirectory(Directory dir) { + subdirectories.put(dir.getMetaData().getName(), dir); + } + + public void addFile(File file) { + files.put(file.getMetaData().getName(), file); + } + + public void removeSubdirectory(String name) { + subdirectories.remove(name); + } + + public void removeFile(String name) { + files.remove(name); + } + + public boolean isEmpty() { + return subdirectories.isEmpty() && files.isEmpty(); + } + + // Cópia profunda da estrutura (sem arquivos e subdirs) + public Directory deepCopyStructure(String newName, String newOwner) { + Directory newDir = new Directory(newName, newOwner); + newDir.getMetaData().setPermission(newOwner, this.metaData.getPermissions().get(this.metaData.getOwner())); + if (this.metaData.getPermissions().containsKey("other")) { + newDir.getMetaData().setPermission("other", this.metaData.getPermissions().get("other")); + } + return newDir; + } +} \ No newline at end of file diff --git a/filesys/File.java b/filesys/File.java new file mode 100644 index 0000000..0852606 --- /dev/null +++ b/filesys/File.java @@ -0,0 +1,136 @@ +package filesys; + +import java.util.ArrayList; +import java.util.List; +import java.util.Arrays; + +public class File { + private MetaData metaData; + private List blocks; + private long size; + private static final int DEFAULT_BLOCK_SIZE = 4096; + + public File(String name, String owner) { + this.metaData = new MetaData(name, owner, false); + this.blocks = new ArrayList<>(); + this.size = 0; + } + + public MetaData getMetaData() { + return metaData; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + this.metaData.updateModificationTime(); + } + + // Escreve dados no arquivo a partir de um offset ou anexa ao final + public long write(byte[] data, long offset, boolean append) { + if (data == null || data.length == 0) { + return 0; + } + + long actualOffset = append ? this.size : offset; + long bytesWritten = 0; + int dataPointer = 0; + + while (actualOffset / DEFAULT_BLOCK_SIZE >= blocks.size()) { + blocks.add(new Block(DEFAULT_BLOCK_SIZE)); + } + + while (dataPointer < data.length) { + long blockIndex = actualOffset / DEFAULT_BLOCK_SIZE; + int offsetInBlock = (int) (actualOffset % DEFAULT_BLOCK_SIZE); + + if (blockIndex >= blocks.size()) { + blocks.add(new Block(DEFAULT_BLOCK_SIZE)); + } + + Block currentBlock = blocks.get((int) blockIndex); + + int bytesToProcessInCurrentBlock = Math.min(data.length - dataPointer, currentBlock.getCapacity() - offsetInBlock); + + int writtenToBlock = currentBlock.write(data, dataPointer, bytesToProcessInCurrentBlock, offsetInBlock); + + bytesWritten += writtenToBlock; + dataPointer += writtenToBlock; + actualOffset += writtenToBlock; + + currentBlock.setCurrentSize(Math.max(currentBlock.getCurrentSize(), offsetInBlock + writtenToBlock)); + } + + this.size = Math.max(this.size, actualOffset); + this.metaData.updateModificationTime(); + return bytesWritten; + } + + // Lê dados do arquivo a partir de um offset para um buffer de destino + public long read(byte[] destination, long offset, int length) { + if (destination == null || destination.length == 0 || length <= 0) { + return 0; + } + if (offset >= this.size) { + return 0; + } + + long bytesRead = 0; + int destinationPointer = 0; + + while (bytesRead < length && offset < this.size) { + long blockIndex = offset / DEFAULT_BLOCK_SIZE; + int offsetInBlock = (int) (offset % DEFAULT_BLOCK_SIZE); + + if (blockIndex >= blocks.size()) { + break; + } + + Block currentBlock = blocks.get((int) blockIndex); + + int bytesToProcessInCurrentBlock = Math.min(length - destinationPointer, currentBlock.getCurrentSize() - offsetInBlock); + + if (bytesToProcessInCurrentBlock <= 0) { + offset = (blockIndex + 1) * DEFAULT_BLOCK_SIZE; + continue; + } + + int readFromBlock = currentBlock.read(destination, destinationPointer, bytesToProcessInCurrentBlock, offsetInBlock); + + bytesRead += readFromBlock; + destinationPointer += readFromBlock; + offset += readFromBlock; + } + + return bytesRead; + } + + public void clearContent() { + this.blocks.clear(); + this.size = 0; + this.metaData.updateModificationTime(); + } + + // Cria uma cópia profunda deste arquivo + public File deepCopy(String newName, String newOwner) { + File newFile = new File(newName, newOwner); + + newFile.getMetaData().setPermission(newOwner, this.metaData.getPermissions().get(this.metaData.getOwner())); + if(this.metaData.getPermissions().containsKey("other")) { + newFile.getMetaData().setPermission("other", this.metaData.getPermissions().get("other")); + } + + for (Block originalBlock : this.blocks) { + Block newBlock = new Block(originalBlock.getCapacity()); + System.arraycopy(originalBlock.getData(), 0, newBlock.getData(), 0, originalBlock.getCurrentSize()); + newBlock.setCurrentSize(originalBlock.getCurrentSize()); + newFile.blocks.add(newBlock); + } + newFile.size = this.size; + newFile.metaData.updateModificationTime(); + return newFile; + } +} \ No newline at end of file diff --git a/filesys/FileSystem.java b/filesys/FileSystem.java index 8d7a83b..11231e2 100644 --- a/filesys/FileSystem.java +++ b/filesys/FileSystem.java @@ -5,9 +5,12 @@ import exception.PermissaoException; // Essa classe deve servir apenas como proxy para o FileSystemImpl +// Ela não deve conter a lógica de negócio do sistema de arquivos, apenas delegar. final public class FileSystem implements IFileSystem { - private final IFileSystem fileSystemImpl; + + public final IFileSystem fileSystemImpl; + public FileSystem() { fileSystemImpl = new FileSystemImpl(); @@ -31,7 +34,8 @@ public void rm(String caminho, String usuario, boolean recursivo) } @Override - public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + // CORREÇÃO: Adicionado CaminhoNaoEncontradoException à assinatura. + public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { fileSystemImpl.touch(caminho, usuario); } @@ -61,7 +65,7 @@ public void ls(String caminho, String usuario, boolean recursivo) @Override public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) - throws CaminhoNaoEncontradoException, PermissaoException { + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { fileSystemImpl.cp(caminhoOrigem, caminhoDestino, usuario, recursivo); } -} \ No newline at end of file +} diff --git a/filesys/FileSystemImpl.java b/filesys/FileSystemImpl.java index 45fa05d..07e0b79 100644 --- a/filesys/FileSystemImpl.java +++ b/filesys/FileSystemImpl.java @@ -4,67 +4,538 @@ import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; -// Implemente nesta classe o seu código do FileSystem. -// A classe pode ser alterada. -// O construtor, argumentos do construtor podem ser modificados -// e atributos & métodos privados podem ser adicionados +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + public final class FileSystemImpl implements IFileSystem { - private static final String ROOT_USER = "root"; // pode ser necessário + private Directory root; + private String currentUser; + private static final String ROOT_USER = "root"; - public FileSystemImpl() {} + public FileSystemImpl() { + this.root = new Directory("/", ROOT_USER); + this.currentUser = ROOT_USER; + System.out.println("Sistema de arquivos inicializado. Usuário atual: " + currentUser); + } - @Override - public void mkdir(String caminho, String nome) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mkdir'"); + private List getPathComponents(String path) { + return Arrays.stream(path.split("/")) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + } + + private String[] resolveParentAndName(String path) { + if ("/".equals(path)) { + return new String[]{"", ""}; + } + List components = getPathComponents(path); + if (components.isEmpty()) { + return new String[]{"", ""}; + } + String name = components.get(components.size() - 1); + List parentComponents = components.subList(0, components.size() - 1); + String parentPath = "/" + String.join("/", parentComponents); + if (parentPath.isEmpty()) { + parentPath = "/"; + } + return new String[]{parentPath, name}; + } + + public Object getNodeAtPath(String path) { + if ("/".equals(path) || path.isEmpty()) { + return root; + } + List components = getPathComponents(path); + Directory currentDir = root; + for (int i = 0; i < components.size(); i++) { + String component = components.get(i); + if (i == components.size() - 1) { + if (currentDir.containsSubdirectory(component)) { + return currentDir.getSubdirectory(component); + } else if (currentDir.containsFile(component)) { + return currentDir.getFile(component); + } else { + return null; + } + } else { + if (currentDir.containsSubdirectory(component)) { + currentDir = currentDir.getSubdirectory(component); + } else { + return null; + } + } + } + return null; + } + + private Directory getParentDirectory(String path, String currentUser) throws CaminhoNaoEncontradoException, PermissaoException { + if ("/".equals(path) || path.isEmpty()) { + return null; + } + String[] parentAndName = resolveParentAndName(path); + String parentPath = parentAndName[0]; + Object node = getNodeAtPath(parentPath); + if (node instanceof Directory) { + Directory parentDir = (Directory) node; + if (!parentDir.getMetaData().canExecute(currentUser)) { + throw new PermissaoException("Permissão negada: não pode navegar em '" + parentPath + "' para o usuário '" + currentUser + "'."); + } + return parentDir; + } + throw new CaminhoNaoEncontradoException("Caminho pai '" + parentPath + "' não existe ou não é um diretório."); } @Override public void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'chmod'"); + if (caminho == null || caminho.isEmpty()) { + throw new IllegalArgumentException("Caminho inválido para chmod."); + } + Object node = getNodeAtPath(caminho); + if (node == null) { + throw new CaminhoNaoEncontradoException("chmod: '" + caminho + "': Nenhum arquivo ou diretório encontrado."); + } + MetaData metaDataToChange; + if (node instanceof File) { + metaDataToChange = ((File) node).getMetaData(); + } else if (node instanceof Directory) { + metaDataToChange = ((Directory) node).getMetaData(); + } else { + throw new CaminhoNaoEncontradoException("chmod: '" + caminho + "': Tipo de nó desconhecido."); + } + if (ROOT_USER.equals(usuario)) { + } else if (!metaDataToChange.getOwner().equals(usuario)) { + throw new PermissaoException("Permissão negada: Somente o dono ou '" + ROOT_USER + "' pode alterar permissões de '" + caminho + "'."); + } else if (!metaDataToChange.canWrite(usuario)) { + throw new PermissaoException("Permissão negada: O usuário '" + usuario + "' é o dono, mas não tem permissão de escrita no item para alterar suas permissões de '" + caminho + "'."); + } + if (permissao == null || !permissao.matches("[r-][w-][x-]")) { + throw new IllegalArgumentException("Formato de permissão inválido. Use 'rwx', 'rw-', 'r-x', '---', etc."); + } + metaDataToChange.setPermission(usuarioAlvo, permissao); + System.out.println("Permissões de '" + caminho + "' para o usuário '" + usuarioAlvo + "' alteradas para '" + permissao + "'."); + } + + @Override + public void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { + if (caminho == null || caminho.isEmpty() || "/".equals(caminho)) { + throw new CaminhoJaExistenteException("Caminho inválido ou raiz já existente para mkdir."); + } + List components = getPathComponents(caminho); + Directory currentDir = root; + for (int i = 0; i < components.size(); i++) { + String component = components.get(i); + if (!currentDir.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode criar diretório em '" + currentDir.getMetaData().getName() + "' para o usuário '" + usuario + "'."); + } + if (currentDir.containsFile(component)) { + throw new CaminhoJaExistenteException("Não é possível criar diretório '" + component + "': já existe um arquivo com este nome."); + } + if (currentDir.containsSubdirectory(component)) { + currentDir = currentDir.getSubdirectory(component); + } else { + Directory newDir = new Directory(component, usuario); + newDir.getMetaData().setPermission(usuario, "rwx"); + newDir.getMetaData().setPermission("other", "r-x"); + currentDir.addSubdirectory(newDir); + currentDir = newDir; + System.out.println("Diretório '" + component + "' criado em '" + (i == 0 ? "/" : "/" + String.join("/", components.subList(0, i))) + "'."); + } + } + System.out.println("Comando mkdir para '" + caminho + "' executado com sucesso."); } @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.isEmpty() || "/".equals(caminho)) { + throw new PermissaoException("Não é possível remover a raiz ou caminho inválido para rm."); + } + String[] parentAndName = resolveParentAndName(caminho); + String nodeName = parentAndName[1]; + Directory parentDir = getParentDirectory(caminho, usuario); + if (parentDir == null) { + throw new CaminhoNaoEncontradoException("Caminho pai de '" + caminho + "' não existe."); + } + if (!parentDir.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode remover em '" + parentAndName[0] + "' para o usuário '" + usuario + "'."); + } + Object nodeToRemove = getNodeAtPath(caminho); + if (nodeToRemove == null) { + throw new CaminhoNaoEncontradoException("rm: '" + caminho + "': Nenhum arquivo ou diretório encontrado para remoção."); + } + if (nodeToRemove instanceof File) { + parentDir.removeFile(nodeName); + System.out.println("Arquivo '" + caminho + "' removido com sucesso."); + } else if (nodeToRemove instanceof Directory) { + Directory dirToRemove = (Directory) nodeToRemove; + if (!dirToRemove.isEmpty() && !recursivo) { + throw new PermissaoException("Diretório '" + caminho + "' não está vazio. Use rm -r para remover recursivamente."); + } + parentDir.removeSubdirectory(nodeName); + System.out.println("Diretório '" + caminho + "' e seu conteúdo (se houver) removidos com sucesso."); + } else { + throw new CaminhoNaoEncontradoException("rm: '" + caminho + "': Tipo de nó desconhecido para remoção."); + } } @Override - public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'touch'"); + public void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + if (caminho == null || caminho.isEmpty() || "/".equals(caminho)) { + throw new IllegalArgumentException("Caminho inválido para touch."); + } + String[] parentAndName = resolveParentAndName(caminho); + String parentPath = parentAndName[0]; + String fileName = parentAndName[1]; + Directory parentDir = getParentDirectory(caminho, usuario); + if (parentDir == null) { + parentDir = root; + } + if (!parentDir.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode criar arquivo em '" + parentPath + "' para o usuário '" + usuario + "'."); + } + if (parentDir.containsFile(fileName)) { + File existingFile = parentDir.getFile(fileName); + existingFile.getMetaData().updateModificationTime(); + System.out.println("Arquivo '" + fileName + "' já existe, tempo de modificação atualizado."); + return; + } else if (parentDir.containsSubdirectory(fileName)) { + throw new CaminhoJaExistenteException("Não é possível criar arquivo '" + fileName + "': já existe um diretório com este nome."); + } + File newFile = new File(fileName, usuario); + parentDir.addFile(newFile); + System.out.println("Arquivo '" + fileName + "' criado em '" + parentPath + "'."); } @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 (caminho == null || caminho.isEmpty()) { + throw new IllegalArgumentException("Caminho inválido para write."); + } + Object node = getNodeAtPath(caminho); + if (!(node instanceof File)) { + throw new CaminhoNaoEncontradoException("'" + caminho + "' não é um arquivo ou não existe para escrita."); + } + File file = (File) node; + if (!file.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode escrever em '" + caminho + "' para o usuário '" + usuario + "'."); + } + if (!anexar) { + file.clearContent(); + } + long offset = file.getSize(); + long bytesWritten = file.write(buffer, offset, anexar); + System.out.println(bytesWritten + " bytes escritos em '" + caminho + "'."); } @Override public void read(String caminho, String usuario, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'read'"); + if (caminho == null || caminho.isEmpty()) { + throw new IllegalArgumentException("Caminho inválido para read."); + } + Object node = getNodeAtPath(caminho); + if (!(node instanceof File)) { + throw new CaminhoNaoEncontradoException("'" + caminho + "' não é um arquivo ou não existe para leitura."); + } + File file = (File) node; + if (!file.getMetaData().canRead(usuario)) { + throw new PermissaoException("Permissão negada: não pode ler '" + caminho + "' para o usuário '" + usuario + "'."); + } + int actualLengthToRead = (int) Math.min(buffer.length, file.getSize()); + if (actualLengthToRead <= 0) { + System.out.println("0 bytes lidos de '" + caminho + "'. (Buffer ou arquivo vazio)"); + return; + } + long bytesRead = file.read(buffer, 0, actualLengthToRead); + System.out.println(bytesRead + " bytes lidos de '" + caminho + "'."); } @Override public void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'mv'"); + if (caminhoAntigo == null || caminhoAntigo.isEmpty() || caminhoNovo == null || caminhoNovo.isEmpty()) { + throw new IllegalArgumentException("Caminhos de origem ou destino inválidos para mv."); + } + if (caminhoAntigo.equals(caminhoNovo)) { + throw new IllegalArgumentException("Caminhos de origem e destino são os mesmos."); + } + if ("/".equals(caminhoAntigo)) { + throw new PermissaoException("Não é possível mover o diretório raiz."); + } + Object sourceNode = getNodeAtPath(caminhoAntigo); + if (sourceNode == null) { + throw new CaminhoNaoEncontradoException("mv: '" + caminhoAntigo + "': Nenhum arquivo ou diretório encontrado."); + } + String[] sourceParentAndName = resolveParentAndName(caminhoAntigo); + String sourceNodeName = sourceParentAndName[1]; + Directory sourceParentDir = getParentDirectory(caminhoAntigo, usuario); + if (sourceParentDir == null) { + throw new CaminhoNaoEncontradoException("Caminho pai de origem não existe para '" + caminhoAntigo + "'."); + } + if (!sourceParentDir.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode remover item em '" + sourceParentAndName[0] + "' para o usuário '" + usuario + "'."); + } + if (!sourceParentDir.getMetaData().canExecute(usuario)) { + throw new PermissaoException("Permissão negada: não pode navegar no diretório de origem '" + sourceParentAndName[0] + "' para o usuário '" + usuario + "'."); + } + String[] destParentAndName = resolveParentAndName(caminhoNovo); + String destName = destParentAndName[1]; + Directory destParentDir = null; + Object existingDest = getNodeAtPath(caminhoNovo); + if (caminhoNovo.equals("/")) { + destParentDir = root; + destName = sourceNodeName; + } else { + try { + destParentDir = getParentDirectory(caminhoNovo, usuario); + } catch (CaminhoNaoEncontradoException e) { + if (existingDest == null) { + throw new CaminhoNaoEncontradoException("Caminho de destino pai não existe para '" + caminhoNovo + "'."); + } + } + } + if (destParentDir == null) { + throw new CaminhoNaoEncontradoException("Diretório de destino inválido ou permissão negada: '" + caminhoNovo + "'"); + } + if (!destParentDir.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode escrever no diretório de destino '" + destParentAndName[0] + "' para o usuário '" + usuario + "'."); + } + try { + if (sourceNode instanceof File) { + File sourceFile = (File) sourceNode; + if (existingDest instanceof Directory) { + Directory targetDir = (Directory) existingDest; + if (targetDir.containsFile(sourceFile.getMetaData().getName()) || targetDir.containsSubdirectory(sourceFile.getMetaData().getName())) { + throw new CaminhoJaExistenteException("Já existe um item com o nome '" + sourceFile.getMetaData().getName() + "' no diretório de destino '" + caminhoNovo + "'."); + } + sourceParentDir.removeFile(sourceNodeName); + targetDir.addFile(sourceFile); + sourceFile.getMetaData().updateModificationTime(); + System.out.println("Arquivo '" + caminhoAntigo + "' movido para '" + caminhoNovo + "/" + sourceFile.getMetaData().getName() + "'."); + } else if (existingDest instanceof File) { + File destFile = (File) existingDest; + if (!destFile.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode sobrescrever o arquivo de destino '" + caminhoNovo + "' para o usuário '" + usuario + "'."); + } + sourceParentDir.removeFile(sourceNodeName); + destParentDir.removeFile(destFile.getMetaData().getName()); + sourceFile.getMetaData().setName(destName); + destParentDir.addFile(sourceFile); + sourceFile.getMetaData().updateModificationTime(); + System.out.println("Arquivo '" + caminhoAntigo + "' movido e renomeado para '" + caminhoNovo + "'."); + } else { + sourceParentDir.removeFile(sourceNodeName); + sourceFile.getMetaData().setName(destName); + destParentDir.addFile(sourceFile); + sourceFile.getMetaData().updateModificationTime(); + System.out.println("Arquivo '" + caminhoAntigo + "' movido e/ou renomeado para '" + caminhoNovo + "'."); + } + } else if (sourceNode instanceof Directory) { + Directory sourceDir = (Directory) sourceNode; + if (caminhoNovo.startsWith(caminhoAntigo + "/")) { + throw new IllegalArgumentException("Não é possível mover um diretório para um de seus subdiretórios."); + } + if (existingDest instanceof Directory) { + Directory targetDir = (Directory) existingDest; + if (targetDir.containsSubdirectory(sourceDir.getMetaData().getName()) || targetDir.containsFile(sourceDir.getMetaData().getName())) { + throw new CaminhoJaExistenteException("Já existe um item com o nome '" + sourceDir.getMetaData().getName() + "' no diretório de destino '" + caminhoNovo + "'."); + } + sourceParentDir.removeSubdirectory(sourceNodeName); + targetDir.addSubdirectory(sourceDir); + sourceDir.getMetaData().updateModificationTime(); + System.out.println("Diretório '" + caminhoAntigo + "' movido para '" + caminhoNovo + "/" + sourceDir.getMetaData().getName() + "'."); + } else if (existingDest instanceof File) { + throw new CaminhoJaExistenteException("Não é possível mover diretório para um arquivo existente: '" + caminhoNovo + "'."); + } else { + sourceParentDir.removeSubdirectory(sourceNodeName); + sourceDir.getMetaData().setName(destName); + destParentDir.addSubdirectory(sourceDir); + sourceDir.getMetaData().updateModificationTime(); + System.out.println("Diretório '" + caminhoAntigo + "' movido e/ou renomeado para '" + caminhoNovo + "'."); + } + } + } catch (CaminhoJaExistenteException e) { + throw new PermissaoException(e.getMessage()); + } } - + @Override public void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'ls'"); + Object node = getNodeAtPath(caminho); + if (node == null) { + throw new CaminhoNaoEncontradoException("ls: '" + caminho + "': Nenhum arquivo ou diretório encontrado."); + } + if (node instanceof File) { + File file = (File) node; + if (!file.getMetaData().canRead(usuario)) { + throw new PermissaoException("Permissão negada: não pode ler metadados de '" + caminho + "' para o usuário '" + usuario + "'."); + } + System.out.println(file.getMetaData().getName() + " (arquivo, " + file.getSize() + " bytes) - " + + file.getMetaData().getPermissions().getOrDefault(file.getMetaData().getOwner(), "---") + " " + file.getMetaData().getOwner()); + return; + } + Directory dirToList = (Directory) node; + if (!dirToList.getMetaData().canRead(usuario)) { + throw new PermissaoException("Permissão negada: não pode ler diretório '" + caminho + "' para o usuário '" + usuario + "'."); + } + if (!dirToList.getMetaData().canExecute(usuario)) { + throw new PermissaoException("Permissão negada: não pode navegar (executar) no diretório '" + caminho + "' para o usuário '" + usuario + "'."); + } + StringBuilder result = new StringBuilder(); + String currentDisplayPath = caminho.equals("/") ? "/" : caminho; + lsRecursiveHelper(dirToList, recursivo, usuario, result, 0, currentDisplayPath); + System.out.print(result.toString()); + } + + private void lsRecursiveHelper(Directory currentDir, boolean recursive, String currentUser, StringBuilder result, int depth, String currentPath) { + String indent = " ".repeat(depth); + result.append(indent).append("Diretório: ").append(currentPath) + .append(" (").append(currentDir.getMetaData().getPermissions().getOrDefault(currentDir.getMetaData().getOwner(), "---")) + .append(" ").append(currentDir.getMetaData().getOwner()).append(")") + .append("\n"); + currentDir.getSubdirectories().values().stream() + .sorted((d1, d2) -> d1.getMetaData().getName().compareTo(d2.getMetaData().getName())) + .forEach(subDir -> { + String subDirPath = currentPath.equals("/") ? "/" + subDir.getMetaData().getName() : currentPath + "/" + subDir.getMetaData().getName(); + result.append(indent).append(" ├── ").append(subDir.getMetaData().getName()) + .append(" (DIR) - ") + .append(subDir.getMetaData().getPermissions().getOrDefault(subDir.getMetaData().getOwner(), "---")) + .append(" ").append(subDir.getMetaData().getOwner()) + .append("\n"); + if (recursive) { + try { + if (subDir.getMetaData().canRead(currentUser) && subDir.getMetaData().canExecute(currentUser)) { + lsRecursiveHelper(subDir, true, currentUser, result, depth + 1, subDirPath); + } else { + result.append(indent).append(" (Permissão negada para listar ou navegar em '").append(subDirPath).append("')\n"); + } + } catch (Exception e) { + result.append(indent).append(" (Erro ao listar '").append(subDirPath).append("': ").append(e.getMessage()).append(")\n"); + } + } + }); + currentDir.getFiles().values().stream() + .sorted((f1, f2) -> f1.getMetaData().getName().compareTo(f2.getMetaData().getName())) + .forEach(file -> { + result.append(indent).append(" └── ").append(file.getMetaData().getName()) + .append(" (FILE, ").append(file.getSize()).append(" bytes) - ") + .append(file.getMetaData().getPermissions().getOrDefault(file.getMetaData().getOwner(), "---")) + .append(" ").append(file.getMetaData().getOwner()) + .append("\n"); + }); } @Override public void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) - throws CaminhoNaoEncontradoException, PermissaoException { - throw new UnsupportedOperationException("Método não implementado 'cp'"); + throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + if (caminhoOrigem == null || caminhoOrigem.isEmpty() || caminhoDestino == null || caminhoDestino.isEmpty()) { + throw new IllegalArgumentException("Caminhos de origem ou destino inválidos para cp."); + } + Object sourceNode = getNodeAtPath(caminhoOrigem); + if (sourceNode == null) { + throw new CaminhoNaoEncontradoException("cp: '" + caminhoOrigem + "': Nenhum arquivo ou diretório encontrado."); + } + String[] destParentAndName = resolveParentAndName(caminhoDestino); + String destParentPath = destParentAndName[0]; + String destName = destParentAndName[1]; + Directory destParentDir = null; + if (!caminhoDestino.equals("/")) { + destParentDir = getParentDirectory(caminhoDestino, usuario); + } else { + destParentDir = root; + } + if (destParentDir == null) { + throw new CaminhoNaoEncontradoException("cp: Destino '" + caminhoDestino + "': Caminho pai não existe."); + } + if (!destParentDir.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode escrever no diretório de destino '" + destParentPath + "' para o usuário '" + usuario + "'."); + } + Object existingDest = getNodeAtPath(caminhoDestino); + if (sourceNode instanceof File) { + File sourceFile = (File) sourceNode; + if (!sourceFile.getMetaData().canRead(usuario)) { + throw new PermissaoException("Permissão negada: não pode ler arquivo de origem '" + caminhoOrigem + "' para o usuário '" + usuario + "'."); + } + if (existingDest instanceof Directory) { + Directory targetDir = (Directory) existingDest; + if (targetDir.containsFile(sourceFile.getMetaData().getName()) || targetDir.containsSubdirectory(sourceFile.getMetaData().getName())) { + throw new CaminhoJaExistenteException("Já existe um item com o nome '" + sourceFile.getMetaData().getName() + "' no diretório de destino '" + caminhoDestino + "'."); + } + File newFile = sourceFile.deepCopy(sourceFile.getMetaData().getName(), usuario); + targetDir.addFile(newFile); + System.out.println("Arquivo '" + sourceFile.getMetaData().getName() + "' copiado para '" + caminhoDestino + "/'."); + } else if (existingDest instanceof File) { + File destFile = (File) existingDest; + if (!destFile.getMetaData().canWrite(usuario)) { + throw new PermissaoException("Permissão negada: não pode sobrescrever o arquivo de destino '" + caminhoDestino + "' para o usuário '" + usuario + "'."); + } + destFile.clearContent(); + byte[] content = new byte[(int) sourceFile.getSize()]; + sourceFile.read(content, 0, (int) sourceFile.getSize()); + destFile.write(content, 0, false); + System.out.println("Arquivo '" + sourceFile.getMetaData().getName() + "' copiado para '" + caminhoDestino + "' (sobrescrito)."); + } else { + File newFile = sourceFile.deepCopy(destName, usuario); + destParentDir.addFile(newFile); + System.out.println("Arquivo '" + sourceFile.getMetaData().getName() + "' copiado para '" + caminhoDestino + "'."); + } + } else if (sourceNode instanceof Directory) { + Directory sourceDir = (Directory) sourceNode; + if (!sourceDir.getMetaData().canRead(usuario) || !sourceDir.getMetaData().canExecute(usuario)) { + throw new PermissaoException("Permissão negada: não pode ler/navegar no diretório de origem '" + caminhoOrigem + "' para o usuário '" + usuario + "'."); + } + if (caminhoDestino.startsWith(caminhoOrigem + "/")) { + throw new IllegalArgumentException("Não é possível copiar um diretório para um de seus subdiretórios."); + } + Directory targetParentForNewDir = destParentDir; + String actualDestName = destName; + if (existingDest instanceof File) { + throw new CaminhoJaExistenteException("Não é possível copiar diretório para um arquivo existente: '" + caminhoDestino + "'."); + } else if (existingDest instanceof Directory) { + targetParentForNewDir = (Directory) existingDest; + actualDestName = sourceDir.getMetaData().getName(); + if (targetParentForNewDir.containsSubdirectory(actualDestName) || targetParentForNewDir.containsFile(actualDestName)) { + throw new CaminhoJaExistenteException("Já existe um item com o nome '" + actualDestName + "' no diretório de destino '" + caminhoDestino + "'."); + } + } else { + if (destParentDir.containsSubdirectory(actualDestName) || destParentDir.containsFile(actualDestName)) { + throw new CaminhoJaExistenteException("Já existe um item com o nome '" + actualDestName + "' no destino."); + } + } + Directory newDir = sourceDir.deepCopyStructure(actualDestName, usuario); + targetParentForNewDir.addSubdirectory(newDir); + cpRecursiveHelper(sourceDir, newDir, usuario); + System.out.println("Diretório '" + caminhoOrigem + "' e seu conteúdo copiados para '" + caminhoDestino + "'."); + } + } + + private void cpRecursiveHelper(Directory source, Directory destination, String currentUser) + throws PermissaoException, CaminhoNaoEncontradoException, CaminhoJaExistenteException { + for (File file : source.getFiles().values()) { + File newFile = file.deepCopy(file.getMetaData().getName(), currentUser); + destination.addFile(newFile); + } + for (Directory subDir : source.getSubdirectories().values()) { + Directory newSubDir = subDir.deepCopyStructure(subDir.getMetaData().getName(), currentUser); + destination.addSubdirectory(newSubDir); + cpRecursiveHelper(subDir, newSubDir, currentUser); + } + } + + public void changeUser(String newUser) { + if (newUser == null || newUser.trim().isEmpty()) { + System.out.println("Erro: Nome de usuário inválido."); + return; + } + this.currentUser = newUser; + System.out.println("Usuário atual alterado para: " + this.currentUser); } - public void addUser(String user) { - throw new UnsupportedOperationException("Método não implementado 'addUser'"); + public String getCurrentUser() { + return currentUser; } -} +} \ No newline at end of file diff --git a/filesys/IFileSystem.java b/filesys/IFileSystem.java index a0f0ce0..81bee8a 100644 --- a/filesys/IFileSystem.java +++ b/filesys/IFileSystem.java @@ -4,44 +4,15 @@ import exception.CaminhoNaoEncontradoException; import exception.PermissaoException; -// Apenas modifique essa interface caso seja EXTREMAMENTE necessário. -// Documente & Justifique TODAS asalterações feitas. +// Interface principal do sistema de arquivos virtual public interface IFileSystem { - // Altera as permissões de um arquivo ou diretório. - // Configura a permissao do caminho para o usuarioAlvo. - // Apenas o usuario root ou que tenha permissão de rw do caminho podem alterar as permissões. void chmod(String caminho, String usuario, String usuarioAlvo, String permissao) throws CaminhoNaoEncontradoException, PermissaoException; - - // Cria um novo diretório. Se o diretório já existir, será lançada uma exceção. - // Se o usuario não tiver permissão para criar o diretório, será lançada uma exceção. void mkdir(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException; - - // Remove um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. - // Caso recursivo seja true, o diretório será removido recursivamente. void rm(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException; - - // Cria um novo arquivo. Atenção: Se o arquivo já existir, será lançada uma exceção. - void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException; - - // Escreve dados em um arquivo. Se o diretório não existir, será lançada uma exceção. - // Caso o diretório exista, o arquivo será criado ou sobrescrito. - // Caso anexar (append) seja true, os dados serão adicionados ao final do arquivo. - // Escrita sequencial. + void touch(String caminho, String usuario) throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException; void write(String caminho, String usuario, boolean anexar, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException; - - // Lê dados de um arquivo. Se o arquivo não existir, será lançada uma exceção. - // Leitura sequencial - todo o conteudo do arquivo sera lido e armazenado no buffer. void read(String caminho, String usuario, byte[] buffer) throws CaminhoNaoEncontradoException, PermissaoException; - - // Move ou renomeia um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. - // Se o diretório já existir, será sobrescrito. - // mv é naturalmente recursivo. void mv(String caminhoAntigo, String caminhoNovo, String usuario) throws CaminhoNaoEncontradoException, PermissaoException; - - // Lista o conteúdo de um diretório. Se o diretório não existir, será lançada uma exceção. - // Caso recursivo seja true, todo o conteúdo do diretório será listado recursivamente. void ls(String caminho, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException; - - // Copia um arquivo ou diretório. Se o diretório não existir, será lançada uma exceção. - void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException; -} \ No newline at end of file + void cp(String caminhoOrigem, String caminhoDestino, String usuario, boolean recursivo) throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException; +} \ No newline at end of file diff --git a/filesys/Main.java b/filesys/Main.java new file mode 100644 index 0000000..844289c --- /dev/null +++ b/filesys/Main.java @@ -0,0 +1,194 @@ +package filesys; + +import java.util.Scanner; +import java.nio.charset.StandardCharsets; + +import exception.PermissaoException; +import exception.CaminhoJaExistenteException; +import exception.CaminhoNaoEncontradoException; + +public class Main { + + private static final String ROOT_USER = "root"; + private static final String ROOT_DIR = "/"; + private static final int READ_BUFFER_SIZE = 4096; + + private static FileSystem fileSystemProxy; + private static FileSystemImpl fileSystemImpl; + private static Scanner scanner = new Scanner(System.in); + private static String user; + + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("Uso: java Main "); + System.out.println("Exemplo: java Main joao"); + return; + } + user = args[0]; + + fileSystemProxy = new FileSystem(); + fileSystemImpl = (FileSystemImpl) fileSystemProxy.fileSystemImpl; + fileSystemImpl.changeUser(user); + + System.out.println("Bem-vindo ao Sistema de Arquivos Virtual!"); + System.out.println("Usuário logado: " + fileSystemImpl.getCurrentUser()); + System.out.println("Digite 'help' para ver os comandos disponíveis."); + + menu(); + } + + public static void menu() { + while (true) { + System.out.println("\nComandos disponíveis:"); + System.out.println("1. chmod - Alterar permissões"); + System.out.println("2. mkdir - Criar diretório"); + System.out.println("3. rm - Remover arquivo/diretório"); + System.out.println("4. touch - Criar arquivo"); + System.out.println("5. write - Escrever em arquivo"); + System.out.println("6. read - Ler arquivo"); + System.out.println("7. mv - Mover/renomear arquivo"); + System.out.println("8. ls - Listar diretório"); + System.out.println("9. cp - Copiar arquivo"); + System.out.println("0. exit - Sair"); + System.out.print("\nDigite o comando desejado: "); + + String opcao = scanner.nextLine(); + try { + switch (opcao) { + case "1": + chmod(); + break; + case "2": + mkdir(); + break; + case "3": + rm(); + break; + case "4": + touch(); + break; + case "5": + write(); + break; + case "6": + read(); + break; + case "7": + mv(); + break; + case "8": + ls(); + break; + case "9": + cp(); + break; + case "0": + System.out.println("Encerrando..."); + return; + default: + System.out.println("Comando inválido!"); + } + } catch (CaminhoNaoEncontradoException | CaminhoJaExistenteException | PermissaoException | IllegalArgumentException e) { + System.err.println("Erro: " + e.getMessage()); + } + + System.out.println("Pressione Enter para continuar..."); + scanner.nextLine(); + } + } + + public static void chmod() throws CaminhoNaoEncontradoException, PermissaoException { + System.out.println("Insira o caminho do arquivo ou diretório:"); + String caminho = scanner.nextLine(); + System.out.println("Insira o usuário para o qual deseja alterar as permissões (e.g., 'root', 'user1', 'other'):"); + String usuarioAlvo = scanner.nextLine(); + System.out.println("Insira a permissão (formato: 3 caracteres 'rwx', 'rw-', 'r-x', '---'):"); + String permissoes = scanner.nextLine(); + + fileSystemProxy.chmod(caminho, user, usuarioAlvo, permissoes); + } + + public static void mkdir() throws CaminhoJaExistenteException, PermissaoException { + System.out.println("Insira o caminho do diretório a ser criado (ex: /home/user/newdir):"); + String caminho = scanner.nextLine(); + + fileSystemProxy.mkdir(caminho, user); + } + + public static void rm() throws CaminhoNaoEncontradoException, PermissaoException { + System.out.println("Insira o caminho do item a ser removido (ex: /home/user/file.txt ou /home/user/mydir):"); + String caminho = scanner.nextLine(); + System.out.println("Remover recursivamente para diretórios? (true/false):"); + boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); + + fileSystemProxy.rm(caminho, user, recursivo); + } + + public static void touch() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + System.out.println("Insira o caminho do arquivo a ser criado ou atualizado (ex: /home/user/newfile.txt):"); + String caminho = scanner.nextLine(); + + fileSystemProxy.touch(caminho, user); + } + + public static void write() throws CaminhoNaoEncontradoException, PermissaoException { + System.out.println("Insira o caminho do arquivo a ser escrito (ex: /home/user/data.txt):"); + String caminho = scanner.nextLine(); + System.out.println("Anexar ao final do arquivo? (true/false):"); + boolean anexar = Boolean.parseBoolean(scanner.nextLine()); + System.out.println("Insira o conteúdo a ser escrito:"); + String content = scanner.nextLine(); + byte[] buffer = content.getBytes(StandardCharsets.UTF_8); + + fileSystemProxy.write(caminho, user, anexar, buffer); + } + + public static void read() throws CaminhoNaoEncontradoException, PermissaoException { + System.out.println("Insira o caminho do arquivo a ser lido (ex: /home/user/data.txt):"); + String caminho = scanner.nextLine(); + byte[] buffer = new byte[READ_BUFFER_SIZE]; + + try { + fileSystemProxy.read(caminho, user, buffer); + Object node = fileSystemImpl.getNodeAtPath(caminho); + if (node instanceof filesys.File) { + filesys.File file = (filesys.File) node; + int actualLength = (int) Math.min(file.getSize(), READ_BUFFER_SIZE); + String readContent = new String(buffer, 0, actualLength, StandardCharsets.UTF_8); + System.out.println("Conteúdo lido: \"" + readContent + "\""); + } else { + System.out.println("Não foi possível exibir o conteúdo. O caminho não é um arquivo ou está vazio."); + } + } catch (CaminhoNaoEncontradoException | PermissaoException e) { + System.out.println("Erro ao ler/obter o arquivo para exibição do conteúdo: " + e.getMessage()); + } + } + + public static void mv() throws CaminhoNaoEncontradoException, PermissaoException { + System.out.println("Insira o caminho antigo do arquivo/diretório (origem):"); + String caminhoAntigo = scanner.nextLine(); + System.out.println("Insira o novo caminho/nome para o arquivo/diretório (destino):"); + String caminhoNovo = scanner.nextLine(); + + fileSystemProxy.mv(caminhoAntigo, caminhoNovo, user); + } + + public static void ls() throws CaminhoNaoEncontradoException, PermissaoException { + System.out.println("Insira o caminho do diretório a ser listado (ex: /home ou /):"); + String caminho = scanner.nextLine(); + System.out.println("Listar recursivamente? (true/false):"); + boolean recursivo = Boolean.parseBoolean(scanner.nextLine()); + + fileSystemProxy.ls(caminho, user, recursivo); + } + + public static void cp() throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + System.out.println("Insira o caminho da origem do arquivo/diretório a ser copiado:"); + String caminhoOrigem = scanner.nextLine(); + System.out.println("Insira o caminho do destino do arquivo/diretório a ser copiado:"); + String caminhoDestino = scanner.nextLine(); + boolean recursivo = true; + + fileSystemProxy.cp(caminhoOrigem, caminhoDestino, user, recursivo); + } +} \ No newline at end of file diff --git a/filesys/MetaData.java b/filesys/MetaData.java new file mode 100644 index 0000000..2e0191b --- /dev/null +++ b/filesys/MetaData.java @@ -0,0 +1,62 @@ +package filesys; + +import java.util.HashMap; +import java.util.Map; + +public class MetaData { + private String name; + private String owner; + private boolean isDirectory; + private long lastModified; + private Map permissions; // Ex: "user" -> "rwx", "other" -> "r-x" + + public MetaData(String name, String owner, boolean isDirectory) { + this.name = name; + this.owner = owner; + this.isDirectory = isDirectory; + this.lastModified = System.currentTimeMillis(); + this.permissions = new HashMap<>(); + // Permissões padrão: dono rwx, outros r-x para diretórios, r-- para arquivos + if (isDirectory) { + permissions.put(owner, "rwx"); + permissions.put("other", "r-x"); + } else { + permissions.put(owner, "rw-"); + permissions.put("other", "r--"); + } + } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getOwner() { return owner; } + public boolean isDirectory() { return isDirectory; } + public long getLastModified() { return lastModified; } + public Map getPermissions() { return permissions; } + + public void setPermission(String user, String perm) { + permissions.put(user, perm); + } + + public boolean canRead(String user) { + return checkPermission(user, 0, 'r'); + } + + public boolean canWrite(String user) { + return checkPermission(user, 1, 'w'); + } + + public boolean canExecute(String user) { + return checkPermission(user, 2, 'x'); + } + + private boolean checkPermission(String user, int pos, char permChar) { + String perm = permissions.get(user); + if (perm == null) perm = permissions.get("other"); + if (perm == null || perm.length() < 3) return false; + return perm.charAt(pos) == permChar; + } + + public void updateModificationTime() { + this.lastModified = System.currentTimeMillis(); + } +} \ No newline at end of file diff --git a/tests/PermissionTest.java b/tests/PermissionTest.java index 6026e31..e994836 100644 --- a/tests/PermissionTest.java +++ b/tests/PermissionTest.java @@ -1,26 +1,137 @@ 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.FileSystem; import filesys.FileSystemImpl; import filesys.IFileSystem; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import exception.CaminhoJaExistenteException; +import exception.CaminhoNaoEncontradoException; +import exception.PermissaoException; + +import java.nio.charset.StandardCharsets; -// Essa classe testa cenários de permissão public class PermissionTest { - private static IFileSystem fileSystem; + private IFileSystem fileSystem; + private FileSystemImpl fileSystemImplRef; + + @BeforeEach + public void setUp() { + FileSystem proxy = new FileSystem(); + fileSystem = proxy; + fileSystemImplRef = (FileSystemImpl) proxy.fileSystemImpl; + } + + @Test + public void testRootHasFullPermission() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + assertDoesNotThrow(() -> fileSystem.mkdir("/testdir_root", "root")); + assertDoesNotThrow(() -> fileSystem.touch("/testfile_root.txt", "root")); + assertDoesNotThrow(() -> fileSystem.mkdir("/home_user1", "root")); + assertDoesNotThrow(() -> fileSystem.chmod("/home_user1", "root", "user1", "rwx")); + String user1 = "user1"; + assertDoesNotThrow(() -> fileSystem.touch("/home_user1/my_own_file.txt", user1)); + assertDoesNotThrow(() -> fileSystem.chmod("/home_user1/my_own_file.txt", "root", user1, "r--")); + assertDoesNotThrow(() -> fileSystem.chmod("/home_user1/my_own_file.txt", "root", "other", "rwx")); + String content = "Hello World"; + byte[] buffer = content.getBytes(StandardCharsets.UTF_8); + assertDoesNotThrow(() -> fileSystem.write("/testfile_root.txt", "root", false, buffer)); + byte[] readBuffer = new byte[buffer.length]; + assertDoesNotThrow(() -> fileSystem.read("/testfile_root.txt", "root", readBuffer)); + assertEquals(content, new String(readBuffer, StandardCharsets.UTF_8)); + } + + @Test + public void testUserCannotWriteWithoutPermission() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + assertDoesNotThrow(() -> fileSystem.mkdir("/public_dir", "root")); + assertDoesNotThrow(() -> fileSystem.chmod("/public_dir", "root", "other", "r-x")); + String normalUser = "joao"; + assertThrows(PermissaoException.class, () -> fileSystem.touch("/public_dir/joaofile.txt", normalUser)); + assertDoesNotThrow(() -> fileSystem.mkdir("/home_joao", "root")); + assertDoesNotThrow(() -> fileSystem.chmod("/home_joao", "root", "joao", "rwx")); + assertThrows(PermissaoException.class, () -> fileSystem.mkdir("/home_joao/joao_subdir", normalUser)); + assertThrows(PermissaoException.class, () -> fileSystem.chmod("/home_joao", normalUser, "other", "r--")); + String outroUser = "maria"; + assertThrows(PermissaoException.class, () -> fileSystem.touch("/home_joao/maria_file.txt", outroUser)); + } + + @Test + public void testChmodPermissions() throws CaminhoNaoEncontradoException, PermissaoException, CaminhoJaExistenteException { + assertDoesNotThrow(() -> fileSystem.touch("/file_perms.txt", "root")); + String userA = "userA"; + assertDoesNotThrow(() -> fileSystem.chmod("/file_perms.txt", "root", userA, "rw-")); + byte[] data = "Hello from userA".getBytes(StandardCharsets.UTF_8); + assertDoesNotThrow(() -> fileSystem.write("/file_perms.txt", userA, false, data)); + assertDoesNotThrow(() -> fileSystem.chmod("/file_perms.txt", "root", userA, "r--")); + assertThrows(PermissaoException.class, () -> fileSystem.write("/file_perms.txt", userA, false, "New data".getBytes(StandardCharsets.UTF_8))); + assertDoesNotThrow(() -> fileSystem.mkdir("/home_userB", "root")); + assertDoesNotThrow(() -> fileSystem.chmod("/home_userB", "root", "userB", "rwx")); + String userB = "userB"; + assertDoesNotThrow(() -> fileSystem.touch("/home_userB/another_file.txt", userB)); + String userC = "userC"; + assertThrows(PermissaoException.class, () -> fileSystem.chmod("/home_userB/another_file.txt", userC, "userB", "rwx")); + } + + @Test + public void testRmPermissions() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + assertDoesNotThrow(() -> fileSystem.mkdir("/parent_dir", "root")); + assertDoesNotThrow(() -> fileSystem.touch("/parent_dir/file_to_remove.txt", "root")); + String userB = "userB"; + assertThrows(PermissaoException.class, () -> fileSystem.rm("/parent_dir/file_to_remove.txt", userB, false)); + assertDoesNotThrow(() -> fileSystem.chmod("/parent_dir", "root", "other", "r-x")); + assertDoesNotThrow(() -> fileSystem.mkdir("/home_userB_rm", "root")); + assertDoesNotThrow(() -> fileSystem.chmod("/home_userB_rm", "root", userB, "rwx")); + assertDoesNotThrow(() -> fileSystem.touch("/home_userB_rm/my_file.txt", userB)); + assertDoesNotThrow(() -> fileSystem.rm("/home_userB_rm/my_file.txt", userB, false)); + assertDoesNotThrow(() -> fileSystem.mkdir("/non_empty_dir", "root")); + assertDoesNotThrow(() -> fileSystem.touch("/non_empty_dir/a.txt", "root")); + assertThrows(PermissaoException.class, () -> fileSystem.rm("/non_empty_dir", "root", false)); + assertDoesNotThrow(() -> fileSystem.rm("/non_empty_dir", "root", true)); + } + + @Test + public void testMvPermissions() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + assertDoesNotThrow(() -> fileSystem.mkdir("/dir1", "root")); + assertDoesNotThrow(() -> fileSystem.mkdir("/dir2", "root")); + assertDoesNotThrow(() -> fileSystem.touch("/dir1/file1.txt", "root")); + assertDoesNotThrow(() -> fileSystem.chmod("/dir1", "root", "userC", "rwx")); + String userC = "userC"; + assertThrows(PermissaoException.class, () -> fileSystem.mv("/dir1/file1.txt", "/dir2/file1.txt", userC)); + assertDoesNotThrow(() -> fileSystem.touch("/dir1/file_userC.txt", userC)); + assertDoesNotThrow(() -> fileSystem.chmod("/dir2", "root", userC, "rwx")); + assertDoesNotThrow(() -> fileSystem.mv("/dir1/file_userC.txt", "/dir2/moved_file.txt", userC)); + assertThrows(PermissaoException.class, () -> fileSystem.mv("/", "/new_root", "root")); + } - @BeforeAll - public static void setUp() { - fileSystem = new FileSystemImpl(/*args...*/); + @Test + public void testCpPermissions() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + assertDoesNotThrow(() -> fileSystem.mkdir("/source_dir", "root")); + assertDoesNotThrow(() -> fileSystem.mkdir("/dest_dir", "root")); + assertDoesNotThrow(() -> fileSystem.touch("/source_dir/file_orig.txt", "root")); + String userD = "userD"; + assertThrows(PermissaoException.class, () -> fileSystem.cp("/source_dir/file_orig.txt", "/dest_dir/copy.txt", userD, false)); + assertDoesNotThrow(() -> fileSystem.chmod("/source_dir/file_orig.txt", "root", "other", "r--")); + assertDoesNotThrow(() -> fileSystem.chmod("/source_dir", "root", "other", "r-x")); + assertDoesNotThrow(() -> fileSystem.chmod("/dest_dir", "root", "other", "r-x")); + assertThrows(PermissaoException.class, () -> fileSystem.cp("/source_dir/file_orig.txt", "/dest_dir/copy.txt", userD, false)); + assertDoesNotThrow(() -> fileSystem.chmod("/dest_dir", "root", userD, "rwx")); + assertDoesNotThrow(() -> fileSystem.cp("/source_dir/file_orig.txt", "/dest_dir/copy_by_userD.txt", userD, false)); } @Test - public void testPermission() { - // Teste de permissão - assertTrue(true); + public void testReadWritePermissions() throws CaminhoJaExistenteException, PermissaoException, CaminhoNaoEncontradoException { + assertDoesNotThrow(() -> fileSystem.touch("/private_file.txt", "root")); + String content = "Secret data"; + byte[] originalContentBytes = content.getBytes(StandardCharsets.UTF_8); + assertDoesNotThrow(() -> fileSystem.write("/private_file.txt", "root", false, originalContentBytes)); + String hacker = "hacker"; + byte[] readBuffer = new byte[originalContentBytes.length]; + assertDoesNotThrow(() -> fileSystem.chmod("/private_file.txt", "root", "other", "---")); + assertThrows(PermissaoException.class, () -> fileSystem.read("/private_file.txt", hacker, readBuffer)); + assertThrows(PermissaoException.class, () -> fileSystem.write("/private_file.txt", hacker, false, "Malicious code".getBytes(StandardCharsets.UTF_8))); + assertDoesNotThrow(() -> fileSystem.chmod("/private_file.txt", "root", "other", "r--")); + assertDoesNotThrow(() -> fileSystem.read("/private_file.txt", hacker, readBuffer)); + assertEquals(content, new String(readBuffer, StandardCharsets.UTF_8)); } -} +} \ No newline at end of file