LP2PFS: Um Cliente VFS para o Sistema LP2P

por

Daniel Stefani Marcon UNIVERSIDADE DO VALE DO RIO DOS SINOS

DANIEL STEFANI MARCON

LP2PFS: Um Cliente VFS Linux para o Sistema LP2P

Monografia apresentada como requisito parcial para a obtenção do grau de Bacharel em Ciência da Computação

Prof. Dr. Rafael Bohrer Ávila Orientador

São Leopoldo, Dezembro de 2010 “Making the simple complicated is commonplace; making the complicated simple, awesomely simple, that’s creativity. — Charles Mingus DEDICATÓRIA

Dedico este trabalho a minha dinda-mãe Darcila, que sempre me apoiou e me ajudou em todos os momentos da minha vida. AGRADECIMENTOS

Agradeço, em primeiro lugar, a Deus, por ter me proporcionado finalizar este trabalho. Agradeço a minha dinda-mãe Darcila e ao meu dindo Fausto pela ajuda em todos os momentos durante esse trabalho, que me apoiaram durante toda a minha vida para mim chegar até aqui. Agradeço ao meu orientador Rafael, por toda a orientação durante o trabalho, pelos conselhos sobre a melhor forma de implementar o sistema de arquivos, sobre o que escrever e como apresentar o projeto nessa monografia e pelas revisões desse texto. Agradeço a todos os meus amigos que me ajudaram durante esse trabalho, direta ou indiretamente. SUMÁRIO

LISTA DE ABREVIATURAS E SIGLAS ...... 7

LISTA DE FIGURAS ...... 9

LISTA DE LISTAGENS ...... 11

LISTA DE TABELAS ...... 12

RESUMO ...... 13

ABSTRACT ...... 14

1 INTRODUÇÃO ...... 15

2 REFERENCIAL TEÓRICO ...... 19 2.1 Sistemas Centralizados ...... 19 2.1.1 Network - NFS ...... 19 2.1.2 ...... 22 2.2 Sistemas Distribuídos ...... 24 2.2.1 Coda ...... 24 2.2.2 Serverless Network File System - xFS ...... 26 2.3 Sistemas Peer-to-Peer ...... 28 2.3.1 Gnutella ...... 29 2.3.2 Freenet ...... 31 2.3.3 Ivy ...... 33 2.3.4 BitTorrent ...... 35 2.4 Síntese ...... 37

3 LOCAL PEER-TO-PEER PROTOCOL ...... 40 3.1 Arquitetura ...... 41 3.2 Mensagens de Comunicação Interna ...... 44 3.3 Módulos Externos ...... 46

4 LP2P FILE SYSTEM ...... 48 4.1 Implementação ...... 48 4.1.1 ...... 49 4.1.1.1 Mapeamento dos Metadados dos Arquivos ...... 55 4.1.2 Page Cache ...... 55 4.1.2.1 Mapeamento dos Dados dos Arquivos ...... 57 4.2 Otimizações ...... 58 4.3 Comunicação com o LP2P ...... 60 4.4 Notificação de Eventos ...... 61 4.5 Arquivo no Sistema ...... 62 4.6 Opções de Montagem ...... 63

5 VALIDAÇÃO E RESULTADOS EXPERIMENTAIS ...... 64 5.1 Versões do Kernel ...... 64 5.2 Disponibilização dos Arquivos ...... 65 5.3 Testes no Sistema LP2P ...... 66

6 CONCLUSÃO ...... 73

REFERÊNCIAS ...... 75 LISTA DE ABREVIATURAS E SIGLAS

API Application Programming Interface CA-NFS Congestion Aware Network File System CIFS Common Internet File System CHK Content-hash Key DNS Domain Name System DNS-SD Domain Name System Based Service Discovery FTP File Transfer Protocol GCC GNU Compiler Collection GNU GNU’s Not Unix! HTTP Hypertext Transfer Protocol KSK Keyword-signed Key LAN Local Area Network LFS Log-structured File Systems LRU Least Recently Used mDNS Multicast Domain Name System NFS Network File System P2P Peer-to-Peer pNFS Parallel Network File System POSIX Portable Interface RAID Redundant Arrays of Inexpensive Disks RPC Remote Procedure Call RTT Round-Trip Time SHA Secure Hash Algorithm SMB SSK Signed-subspace Key VFS Virtual File System TTL Time-to-live xFS Serverless Network File System LISTA DE FIGURAS

Figura 1.1: Estrutura do sistema LP2P ...... 17

Figura 2.1: Estado da página em memória com escrita assíncrona ...... 21 Figura 2.2: Estrutura de um cliente Coda ...... 25 Figura 2.3: Leitura de um bloco no sistema xFS (fonte: Anderson et al. (1996)) 27 Figura 2.4: Estrutura da rede Gnutella ...... 29 Figura 2.5: Rede de sobreposição Gnutella com os nodos mal distribuídos .... 30 Figura 2.6: Procura de um arquivo na rede Freenet ...... 33 Figura 2.7: Estrutura do sistema Ivy ...... 34

Figura 3.1: Funcionamento do LP2P ...... 41 Figura 3.2: Arquitetura do sistema LP2P ...... 42 Figura 3.3: Comunicação interna do sistema LP2P ...... 44 Figura 3.4: Estrutura da mensagem LP2P ...... 44 Figura 3.5: Formato da mensagem llist ...... 45 Figura 3.6: Formato da mensagem lsendl ...... 45 Figura 3.7: Formato da mensagem lgetu ...... 46 Figura 3.8: Formato da mensagem lsendu ...... 46 Figura 3.9: Formato da mensagem lget ...... 46 Figura 3.10: Formato da mensagem lsendf ...... 47

Figura 4.1: A camada de abstração do VFS ...... 49 Figura 4.2: Relações entre os objetos do VFS (fonte: Mauerer (2008)) ..... 54 Figura 4.3: Relação entre as estruturas do VFS e do Page Cache (fonte: Mauerer (2008)) ...... 56 Figura 4.4: Exemplo de uma árvore radix (fonte: Mauerer (2008)) ...... 58 Figura 4.5: Estrutura com alinhamento de bytes (fonte: Mauerer (2008)) .... 60 Figura 4.6: Montagem do sistema LP2PFS ...... 60 Figura 4.7: Leitura do conteúdo de um arquivo ...... 61

Figura 5.1: Compartilhamento de pacotes Debian ...... 65 Figura 5.2: Vazão de dados usando TCP ...... 66 Figura 5.3: Latência de comunicação usando TCP ...... 67 Figura 5.4: Vazão individual de cada peer para arquivos de 1 MB ...... 68 Figura 5.5: Vazão individual de cada peer para arquivos de 8 MB ...... 69 Figura 5.6: Vazão individual de cada peer para arquivos de 16 MB ...... 70 Figura 5.7: Vazão agregada do sistema ...... 71 LISTA DE LISTAGENS

Listagem 4.1: Estrutura super_block ...... 49 Listagem 4.2: Estrutura inode ...... 50 Listagem 4.3: Estrutura dentry ...... 51 Listagem 4.4: Estrutura file ...... 53 Listagem 4.5: Estrutura file_system_type ...... 54 Listagem 4.6: Estrutura address_space ...... 56 Listagem 4.7: Funções de leitura de páginas ...... 57 Listagem 4.8: Função __builtin_expect ...... 59 Listagem 4.9: Macros likely e unlikely ...... 59 LISTA DE TABELAS

Tabela 2.1: Síntese das características dos sistemas estudados ...... 39

Tabela 5.1: Teste do sistema com arquivos de 1 MB ...... 68 Tabela 5.2: Teste do sistema com arquivos de 8 MB ...... 69 Tabela 5.3: Teste do sistema com arquivos de 16 MB ...... 70 Tabela 5.4: Vazão agregada do sistema ...... 71 RESUMO

Sistemas de distribuição de arquivos em rede tem como principal objetivo per- mitir o compartilhamento de dados entre os seus usuários, provendo uma ou mais das seguintes características: transparência de localização, replicação de arquivos, tolerância a falhas, eficiência, segurança e consistência. Nesse contexto, no âm- bito do Programa Interdisciplinar de Pós-Graduação em Computação Aplicada da Unisinos (PIPCA), surgiu a proposta de desenvolver um sistema peer-to-peer para redes locais, o Local Peer-to-Peer Protocol (LP2P), possibilitando a rápida troca de arquivos entre os componentes da rede. Esse sistema, através de seus módulos externos, disponibiliza funções de descoberta de serviços e mecanismos de segurança e otimização para a transferência de arquivos entre os peers da rede. No entanto, é necessário o desenvolvimento de uma interface robusta e de fácil utilização para o sistema. Desse modo, nesse projeto, foi implementado um módulo para o kernel do Linux, que atua como um sistema de arquivos, denominado LP2PFS. Esse módulo permite disponibilizar os arquivos presentes no sistema LP2P, atuando como a sua interface com o usuário. O LP2PFS foi desenvolvido com base na camada de abs- tração do kernel para sistemas de arquivos, o Virtual File System (VFS), utilizando o subsistema de Page Cache para o armazenamento dos dados na memória. O sis- tema de arquivos apresenta as funcionalidades de notificação de eventos ocorridos no sistema às aplicações, através da interface fsnotify, além de possuir um arquivo virtual no sistema Procfs, com dados da sua montagem e dos arquivos presentes no sistema. O módulo LP2PFS obteve êxito em todos os testes executados. A sua implementação é compatível com diversas versões do kernel do Linux e a disponi- bilização dos arquivos presentes no sistema LP2P foi alcançada, sendo comprovada pela verificação da integridade dos dados desses arquivos através do algoritmo MD5. Por fim, foram realizados testes de desempenho do sistema de arquivos em conjunto com o sistema LP2P, através dos quais foi comprovado o desempenho e a eficiência do módulo implementado.

Palavras-chave: Sistema de arquivos. peer-to-peer. LP2P. LP2PFS: A VFS Linux Client for the LP2P System

ABSTRACT

Network file distribution systems aim to allow data sharing among users, pro- viding some of the following features: location transparency, file replication, fault tolerance, efficiency, security and consistency. In this sense, at PIPCA, it was pro- posed the development of a peer-to-peer system for local networks, the Local Peer- to-Peer Protocol (LP2P), allowing fast file exchange among the participants of the network. This system, through its external modules, also provide other functions, like service discovery, security and otimization for data exchange among the hosts of the network. However, this system needs a powerful and easy to use interface. Therefore, in this project, it was developed a module, a file system called LP2PFS, to make the files in the LP2P system available to all participants, operating as the user interface of the system. The LP2PFS was structured based on the abstraction layer of the kernel for file systems, the Virtual File System (VFS), using the Page Cache subsystem to store data in the memory. LP2PFS also provide the functionality to notify events in the file system to the applications, through the fsnotify interface, and it was developed an entry in the Procfs system with infor- mation about its mount data and metadata of all files in the LP2P system. All experiments executed with the LP2PFS were succesful. Its implementation is com- patible with many versions of the Linux kernel and all the files in the system were succesfully shared, since data integrity was verified with MD5 algorithm. Lastly, the file system was tested together with the LP2P daemon, through which it was confirmed the performance and efficiency of the module.

Keywords: file systems, peer-to-peer, LP2P. 15

1 INTRODUÇÃO

O desenvolvimento contínuo de hardware de maior desempenho possibilita a expansão da Internet, tornando possível taxas cada vez maiores de download e de upload, e o aumento da capacidade de processamento dos computadores, possibili- tando a criação e a troca de arquivos com tamanhos gigantescos, inimagináveis há algum tempo atrás (COULOURIS; DOLLIMORE; KINDBERG, 2007). O modelo cliente/servidor apresenta problemas em algumas situações desse novo cenário, já que os servidores podem ficar sobrecarregados ao enviar arquivos grandes para diversos clientes ao mesmo tempo, ou seja, o seu nível de escalabilidade é relativamente baixo. Assim, houve a necessidade de buscar um novo modelo para a troca de dados e o compartilhamento de recursos. Contrastando com o modelo cliente/servidor, sistemas distribuídos apresentam maior escalabilidade e melhor utilização dos recursos disponíveis. Nesse cenário, sistemas de arquivos distribuídos são definidos como uma coleção de computado- res fracamente acoplados, interligados por uma rede de comunicação, permitindo que processos em diferentes máquinas compartilhem dados por longos períodos de tempo. Eles permitem que arquivos sejam armazenados e acessados em qualquer computador pertencente à rede (TANENBAUM, 2007; TANENBAUM; STEEN, 2006). Esse tipo de sistema é composto por três módulos principais: um serviço, que provê um ou vários tipos de funções aos clientes, um servidor, que coordena as ações de todo o sistema, e um cliente, que provê a interface do sistema com o usuário. Sendo assim, em um sistema de arquivos distribuído, os três módulos (serviço, servidor e cliente) estão dispersos pela rede (COULOURIS; DOLLIMORE; KINDBERG, 2007). O sistema deve aparentar aos seus usuários ser um sistema de arquivos local e centralizado. A dispersão de seus servidores deve permanecer invisível para o usuário, fazendo com que não seja perceptível a diferença entre arquivos locais e remotos. Entretanto, na prática, essas características nem sempre são alcançadas. Também é preciso considerar o desempenho do sistema, principalmente a quantidade de tempo necessária para satisfazer as requisições dos usuários, o que inclui o tempo de enviar a requisição ao servidor, o tempo do servidor responder, além do overhead do protocolo de comunicação (SILBERSCHATZ; GALVIN; GAGNE, 2008). Já o modelo peer-to-peer (P2P), cujas redes não possuem uma infraestrutura 16 centralizada e dedicada, mas dependem da participação voluntária de peers1 (nodos da rede), apresenta um meio eficiente e de baixo custo para o compartilhamento de recursos e de arquivos presentes nas máquinas participantes da rede (SAROIU; GUMMADI; GRIBBLE, 2002). Assim, a pesquisa nessa área cresceu exponencial- mente nos últimos anos. Dentre as principais iniciativas, destacam-se o BitTorrent (COHEN, 2003), o Gnutella (PORTMANN et al., 2001) e o Freenet (CLARKE et al., 2001). Nesse contexto, no âmbito do Programa Interdisciplinar de Pós-Graduação em Computação Aplicada da Unisinos (PIPCA), surgiu a proposta de desenvolver um sistema P2P para redes locais (LANs), o Local P2P (LP2P), possibilitando a rá- pida troca de arquivos entre os nodos da rede. Desse modo, a proposta do sistema LP2P é permitir o compartilhamento de arquivos em redes locais, com o auxílio de tecnologias para anúncio e descoberta de serviços, como Multicast DNS (mDNS) (CHESHIRE; KROCHMAL, 2010b) e DNS Based Service Discovery (DNS-SD) (CHESHIRE; KROCHMAL, 2010a; CHESHIRE; STEINBERG, 2006), e a apli- cação de mecanismos de segurança e a otimização da transferência de arquivos entre os peers da rede (ROCHA; MARCON; ÁVILA, 2010). Entretanto, torna-se necessário desenvolver uma interface de acesso aos arquivos presentes no sistema LP2P. Essa interface deve exigir o menor esforço possível do usuário, adaptando-se ao modelo com o qual o usuário já está familiarizado em seu cotidiano. Uma interface amplamente utilizada pelos usuários é a interface de sistema de arquivos, que, do ponto de vista do usuário, é utilizada da mesma maneira em diversos sistemas operacionais. Essa interface oferecida pelo sistema operacional requer um esforço mínimo do usuário, que já está adaptado a ela, conhecendo as suas diversas funcionalidades. Dessa maneira, o usuário necessita de pouco ou nenhum aprendizado, já que ele a utiliza diariamente para todas as suas atividades no computador, desde a mais simples, até atividades complexas, que necessitem de conhecimentos avançados. Além disso, a interface de sistema de arquivos viabiliza a leitura de arquivos sem o usuário ter conhecimento da localização na rede desses arquivos. Isso possibilita ao sistema de arquivos distribuído disponibilizar arquivos remotos da mesma forma que um sistema de arquivos local disponibiliza arquivos locais, do ponto de vista do usuário. Portanto, a utilização do sistema de arquivos como interface do sistema LP2P deve oferecer ao usuário os seguintes benefícios:

• Transparência de localização de arquivos, cuja localização física na rede só é conhecida pelo sistema LP2P;

• Eficiência, realizando as operações requisitadas pelo usuário em um intervalo de tempo próximo ao de um sistema de arquivos local;

• Minimização da complexidade do sistema do ponto de vista do usuário, que só necessita interagir com o sistema de arquivos.

1Esse termo não será traduzido nem destacado em itálico, por ser utilizado com muita frequên- cia ao longo do trabalho e pelo autor considerar que sua tradução prejudica o entendimento do texto. Outros termos, utilizados frequentemente, também não serão destacados em itálico: kernel, hardware, software, Internet, host, peer-to-peer, byte, kilobyte, megabyte e gigabyte. 17

Dessa forma, a proposta desse trabalho de conclusão é desenvolver um módulo para o kernel do Linux (SALZMAN, 2009), com o objetivo de realizar a ligação entre os dados dos arquivos distribuídos pela rede, cujas informações estão presentes no daemon LP2P, e o usuário. Assim, o módulo atuará como um sistema de arquivos, denominado LP2PFS, podendo-se realizar operações sobre tais arquivos, abstraindo- se a sua localização e toda a comunicação existente entre o sistema de arquivos e o daemon LP2P, dando ao usuário a impressão de estar manipulando arquivos locais. A estrutura completa do sistema peer-to-peer, desde a camada de rede até o sistema de arquivos LP2PFS pode ser visualizada na Figura 1.1.

Figura 1.1: Estrutura do sistema LP2P

O objetivo principal do projeto é a implementação de um sistema de arquivos para a interação com o daemon LP2P, proporcionando o compartilhamento de arqui- vos entre os nodos de uma LAN, através da interface padrão do kernel do Linux para sistemas de arquivos, o Virtual File System (VFS). Assim, os objetivos específicos são:

• Permitir o acesso aos compartilhamentos LP2P via sistema de arquivos Linux;

• Estudar a interface de programação do kernel do Linux, com foco no VFS e no Page Cache;

• Analisar como são desenvolvidos os sistemas de arquivos com características distribuídas, principalmente o Network File System (NFS), o Serverless Netwok File System (xFS) e o Ivy;

• Estudar as técnicas de otimização de código proporcionadas pela interface de programação do kernel do Linux.

Esta monografia está organizada da seguinte forma: o Capítulo 2 apresenta al- guns trabalhos relacionados, suas vantagens e suas desvantagens, e o Capítulo 3 apresenta o sistema LP2P, o seu protocolo de comunicação e os seus diversos mó- dulos. No Capítulo 4, é descrito o sistema de arquivos LP2PFS, sua estrutura, o método de armazenamento de dados em memória e as suas funcionalidades. Por fim, no Capítulo 5, são apresentados os resultados dos testes realizados com o sistema 18

LP2PFS em conjunto com o sistema LP2P e, no Capítulo 6, são feitas as conside- rações finais sobre esse projeto, bem como os trabalhos futuros a serem realizados. 19

2 REFERENCIAL TEÓRICO

Os sistemas de distribuição de arquivos em rede podem ser divididos em três grandes grupos: i) sistemas centralizados, os quais são desenvolvidos, em sua grande maioria, no modelo cliente/servidor, possuindo uma única máquina que gerencia o sistema; ii) sistemas distribuídos, que são uma coleção de computadores independen- tes que compartilham recursos e que aparentam ser um sistema único para o usuário (TANENBAUM; STEEN, 2006); iii) sistemas peer-to-peer, que são sistemas cuja rede é formada dinamicamente e com controle distribuído entre os participantes (CEBALLOS; GORRICHO, 2006). Este capítulo apresenta alguns projetos relacionados ao desenvolvimento desse trabalho, dando ênfase ao módulo cliente desses projetos, o qual interage diretamente com o usuário do sistema, analisando criticamente suas vantagens e desvantagens.

2.1 Sistemas Centralizados

Os sistemas centralizados são, em sua grande maioria, desenvolvidos no modelo cliente/servidor, o que limita a sua escalabilidade e a sua eficiência, já que uma máquina necessita atender as requisições de todos os clientes. Entretanto, o geren- ciamento de consistência dos dados é facilitado, uma vez que há uma única máquina responsável pelos dados, que, nesse caso, é o servidor (COULOURIS; DOLLIMORE; KINDBERG, 2007). Dentre esses sistemas, destacam-se o Network File System (NFS) (PAWLOWSKI et al., 1994, 2000) e o Samba (VERNOOIJ, 2009).

2.1.1 Network File System - NFS

O protocolo NFS é uma coleção de procedimentos que permitem aos clientes acessarem arquivos armazenados remotamente em um servidor, sendo independente de arquitetura, sistema operacional e protocolo de rede. Esse protocolo possui 4 versões:

• A primeira versão existiu somente internamente na Sun Microsystems;

• A segunda versão foi lançada em 1984 (SUN MICROSYSTEMS, 1989); 20

• As versões 3 (CALLAGHAN; PAWLOWSKI; STAUBACH, 1995) e 4 (SHE- PLER et al., 2003) resolvem alguns problemas encontrados nas versões ante- riores, principalmente na questão de desempenho e eficiência (PAWLOWSKI et al., 1994, 2000).

O protocolo NFS é stateless até a versão 3, já que cada requisição contém todas as informações necessárias para ser processada. Entretanto, o servidor deve manter o estado dos dados que estão armazenados no disco rígido, além do mapeamento dos descritores para os dados dos arquivos no sistema de arquivos. Na versão 4, o NFS tornou-se stateful, devido às necessidades de trava de arquivo e delegação de serviços (PAWLOWSKI et al., 2000). Em sua segunda versão, de acordo com Pawlowski et al. (1994), o protocolo apresentava alguns problemas desde o seu projeto até a sua implementação, dentre os quais os mais sérios eram a limitação de 4 gigabytes do tamanho de arquivos e a escrita de dados síncrona, de forma que o servidor só gerava uma resposta ao cliente após os dados terem sido escritos no disco rígido, ocasionando aos clientes uma considerável perda de desempenho. A versão 3 do protocolo (NFSv3), por outro lado, introduziu melhorias que afeta- ram positivamente não somente o desempenho do sistema, mas também a eficiência e o tempo de resposta dos clientes. Dentre essas melhorias, destacam-se a escrita assíncrona de dados, o armazenamento de tamanhos de arquivos em estruturas de 64 bytes, a operação de leitura de diretórios otimizada, a recuperação de falhas e a consistência fraca de memória cache. A escrita assíncrona eliminou o gargalo da escrita existente na versão anterior. Ao receber uma requisição de escrita de um cliente, o servidor responde imediata- mente, possibilitando ao cliente continuar o seu processamento. Após algum tempo, o cliente verifica se os dados foram escritos em um meio não-volátil. O servidor, então, responde a essa requisição somente após os dados terem sido armazenados. Entretanto, essa função é opcional na especificação do protocolo e, consequente- mente, as implementações podem escolher não suportá-la. A Figura 2.1 mostra o diagrama de estados para a realização da escrita assín- crona de uma página de memória alterada no cliente. Quando a página for marcada como suja (dirty), ou seja, tiver sido alterada em memória, o cliente envia uma mensagem de escrita assíncrona ao servidor. Enquanto ele não receber do servidor uma mensagem de operação realizada (COMMIT), a página permanece armazenada em cache na sua memória, pois, em caso de erro no servidor, ele reenviará a página, dessa vez em uma operação de escrita síncrona (PAWLOWSKI et al., 1994). Para a recuperação de falhas, o NFS adota a filosofia de clientes “espertos”, ou seja, o cliente mantém em cache uma cópia dos dados até o servidor responder que esses dados foram armazenados em meio físico. Desse modo, se houver uma falha no servidor, o cliente irá reenviar os dados. Outra operação introduzida foi a leitura de diretórios otimizada, que retorna os descritores de arquivos juntamente com os atributos de cada arquivo presente no diretório. Essa operação ocasionou uma grande redução do overhead de comunicação entre clientes e servidores, já que, até então, uma operação de leitura de diretório 21

Figura 2.1: Estado da página em memória com escrita assíncrona era seguida por diversas operações de requisição de atributos de arquivos, de acordo com o número de arquivos presentes no diretório lido (PAWLOWSKI et al., 1994). Entretanto, essa versão do protocolo possui alguns problemas, principalmente relacionados à segurança, à perda de desempenho e à baixa extensibilidade do pro- tocolo, além de problemas com travas de arquivos. Dessa maneira, foi desenvolvida a versão 4 da especificação do protocolo NFS. Na versão 4 (NFSv4), uma grande diferença estrutural em relação às 3 versões anteriores está na eliminação de protocolos auxiliares, já que as versões anteriores utilizavam o protocolo mount para obter o descritor de arquivo inicial e o protocolo Network Lock Manager para travas de arquivos, e a introdução de operações para comunicação entre cliente e servidores, substituindo os procedimentos RPC. Essas operações, também denominadas de procedimento composto (COMPOUND), correspondem a várias ações a serem realizadas no sistema de arquivos em uma única comunicação, o que não era possível nas versões anteriores do protocolo, já que cada procedimento RPC correspondia a uma ação específica a ser executada no sistema de arquivos. Esse tipo de operação não é atômica, ou seja, em um conjunto de operações requisitadas sobre o sistema de arquivos, o servidor as processa em ordem, até o final ou até aquela que retornar erro, não processando as operações seguintes do conjunto requisitado. Assim, o servidor retorna a resposta ao cliente de todas as operações ou até a operação que retornou erro, inclusive o erro (PAWLOWSKI et al., 2000). Outra diferença estrutural marcante é a introdução de estado na operações de abertura e fechamento de arquivo, tornando o protocolo stateful. A introdução de estado no protocolo proporciona a delegação de operações em um arquivo para um cliente específico, o que aumenta consideravelmente o desempenho do cliente através da realização de um mecanismo forte de cache dos dados, já que o cliente detém a trava do arquivo. Krishnan et al. (2007) conduziram um estudo para avaliar os mecanismos de detecção e recuperação de falhas na implementação do NFS no Linux. Os resultados dos testes mostraram-se preocupantes em certos casos, já que o NFS nem sempre consegue detectar a corrupção de metadados, agindo de maneiras inesperadas. Em certas operações de erro, o cliente retorna ao usuário informações de sucesso da operação, em outras, o kernel do sistema do cliente é afetado, tornando todo o 22 sistema instável. Já Traeger, Thangavelu e Zadok (2007) propuseram um modelo de privacidade round-trip para o NFSv4, já que essa versão só define a segurança dos dados sendo transmitidos entre clientes e servidores. A segurança do protocolo NFS apresenta deficiências, devido ao fato de servidores com acesso à Internet possuírem dados des- protegidos. Dessa forma, o servidor só deveria armazenar dados cifrados, garantindo a segurança de tais dados caso ele seja invadido. Nesse novo modelo de segurança, o tempo de processamento para atender requi- sições dos clientes é menor no servidor, já que ele não precisa cifrar e decifrar dados. Assim, o gargalo da rede (o servidor) é aliviado e, ao mesmo tempo, a privacidade dos dados é aumentada. Além disso, de acordo com os testes realizados, a escalabilidade não é afetada pelas medidas extras de segurança (TRAEGER; THANGAVELU; ZADOK, 2007). Além disso, em momentos de congestionamento do sistema, não é definida ne- nhuma ação a ser tomada pelo NFS. Por isso, não há um estabelecimento de priori- dades para realizar as operações pendentes. Dessa forma, no estudo conduzido por Batsakis et al. (2009), é proposto um framework para coordenar e gerenciar o uso dos recursos do sistema de arquivos entre todos os clientes nas operações realizadas pelo NFS, denominado Congestion Aware NFS (CA-NFS). Ao atingir um estado crítico, o CA-NFS realiza um escalonamento de priorida- des, sendo preferencial operações de leitura e escrita síncronas em relação às assín- cronas, além de ser dada preferência para escritas de blocos de dados que bloqueiem leituras. Esse framework foi implementado na versão 2.6.18 do kernel do Linux. Baseados nessa medição de recursos, os clientes CA-NFS tem a opção de atrasar ou acelerar uma escrita assíncrona. A aceleração de uma escrita é feita se a utilização de rede estiver baixa ou se o cliente estiver com problemas de memória cache. Além disso, as operações de leitura assíncrona no cliente, baseadas na quantidade de dados a serem lidos antecipadamente (), são otimizadas com base na utilização dos recursos de rede e de disco. Outro projeto desenvolvido com base no protocolo NFSv4 é o Parallel NFS (pNFS), com o objetivo de eliminar o gargalo dos servidores em clusters, enquanto é mantida a facilidade de gerenciamento e interoperabilidade do NFS. Ele foi proje- tado como uma extensão à especificação do protocolo NFS, permitindo a separação entre dados e metadados, possibilitando aos clientes o acesso direto e em paralelo a servidores (CHAI et al., 2007; HILDEBRAND; HONEYMAN, 2007).

2.1.2 Samba

O Samba (VERNOOIJ, 2009) é um projeto de código aberto, que possibilita o compartilhamento de arquivos e o acesso a serviços de impressão a clientes que rodem sistemas operacionais diferentes do Microsoft Windows e que utilizem os protocolos Server Message Block (SMB) e Common Internet File System (CIFS) (STORAGE NETWORKING INDUSTRY ASSOCIATION, 2002). Além disso, a aplicação pode desempenhar o papel de servidor, disponibilizando arquivos a clientes Windows. Por isso, o principal objetivo desse projeto é prover interoperabilidade entre clientes e 23 servidores operando em diferentes sistemas operacionais (BLAIR, 1998). No Linux, para acessar arquivos disponibilizados através do protocolo SMB, foram implementados dois sistemas de arquivos clientes em espaço de kernel, o smbfs e o cifs. O smbfs é um cliente SMB limitado, já que ele só implementa parcialmente as semânticas POSIX e as suas configurações são especificadas através do arquivo smb.conf. Além disso, ele só possui a opção de leitura de 1 página de memória por vez, limitando o seu desempenho. Entretanto, o smbfs suporta a autenticação através do protocolo Kerberos, que não é suportada pelo cifs. Por outro lado, o cliente cifs suporta completamente as semânticas POSIX e a sua configuração é feita dinamicamente através do sistema Procfs, em arquivos presentes no diretório fs/cifs/, ou pela passagem de parâmetros no carregamento do módulo. Ele suporta hardlinks, leitura e escrita de várias páginas simultâneas de memória e trava de arquivos (LINUX KERNEL ORGANIZATION, 2010). Foi pro- jetado de acordo com a referência técnica SNIA CIFS (STORAGE NETWORKING INDUSTRY ASSOCIATION, 2002) e possui suporte ao Windows 2000, Windows XP, Samba e a servidores cifs (FRENCH, 2007). Algumas das opções mais importantes do sistema de arquivos, que afetam dire- tamente o seu desempenho, são as seguintes:

• Tamanho do buffer de escrita de dados de arquivos (wsize): define a quanti- dade média de bytes que o sistema de arquivos envia para o servidor a cada requisição de escrita. Por padrão, o sistema define 14 páginas de memória; • Tamanho do buffer de leitura de dados de arquivos (rsize): define a quantidade média de bytes que o sistema de arquivos requisita ao servidor. Por padrão, o sistema define 4 páginas de memória. Alterar esse valor influenciará muito pouco o desempenho do sistema, já que o buffer de requisição possui um ta- manho aproximado de 4 páginas de memória também. Entretanto, é possível alterar o tamanho do buffer no momento do carregamento do módulo; • Número máximo de requisições a um servidor: passado por parâmetro no momento do carregamento do módulo. Por padrão, são 50 requisições.

Além dos objetos padrões do Virtual File System, o cifs define diversos objetos de uso específico, no arquivo fs/cifs/cifsglob.h, como um objeto de informações sobre o servidor TCP, um objeto que identifica o usuário que realizou uma montagem no sistema, um objeto para cada compartilhamento montado e um objeto auxiliar para cada representação de arquivo (definida pela estrutura inode) do sistema. No momento do carregamento do módulo, é criada uma thread de gerenciamento de cache, principalmente para a escrita dos dados. Uma segunda thread, denominada “cifsd”, é criada no momento da montagem do sistema, encarregada de gerenciar as conexões de rede do compartilhamento, e uma terceira thread é criada para a notificação de eventos em diretórios do sistema de arquivos. O sistema também aloca 3 buffers para o gerenciamento de requisições (cifs_ small_rq), cifs_request e cifs_mpx_ids). O tamanho de cada buffer pode ser 24 configurado no momento de carregamento do módulo. Entretanto, se muitas apli- cações acessarem o sistema de arquivos simultaneamente, serão alocados e desalo- cados dinamicamente mais buffers. Para aumentar o desempenho, o cifs também utiliza o subsistema do kernel de Page Cache para armazenar os dados dos arquivos (FRENCH, 2007).

2.2 Sistemas Distribuídos

Os sistemas distribuídos, ao contrário dos sistemas centralizados, não possuem um servidor para coordenar as ações dos clientes, fator que aumenta a sua escalabi- lidade e a tolerância a falhas, porém necessita de um projeto mais complexo, devido a necessidade de coerência de dados entre os nodos da rede. Nessa categoria, encontram-se sistemas como o Coda (SATYANARAYANAN, 2002) e o Serverless Network File System (xFS) (ANDERSON et al., 1996).

2.2.1 Coda

O Coda, desenvolvido na Carnegie Mellon University, com base no AFS-2 (SATYA- NARAYANAN, 1990), foi projetado para operar em ambientes de clientes Unix não confiáveis e servidores Unix confiáveis. Cada cliente enxerga o Coda como um sis- tema de arquivos, com transparência de localização, no qual o processo denominado Vênus obtém e mantém em cache os volumes mapeados. Segundo Satyanarayanan (2002), esse sistema utiliza dois mecanismos para a garantir alta disponibilidade:

• Replicação de servidores: para permitir que os volumes possuam cópia com permissão de escrita em vários servidores, mantendo um baixo custo devido às operações de cache realizadas nos clientes e ao uso de protocolos de acesso paralelo; • Operação desconectada: em períodos de falha da rede, quando um volume de dados ficar indisponível, o módulo Vênus do cliente processa as requisições do sistema de arquivos baseando-se somente no conteúdo da sua memória cache.

Além disso, o sistema apresenta outras características importantes:

• Realização de operações de cache no cliente; • Modelo de segurança bem definido para autenticação, controle de acesso e cifragem dos dados; • Adaptação a banda de rede disponível; • Escalabilidade, adotando-se diversos mecanismos, dentre os quais os mais im- portantes são coerência de cache e armazenamento em cache de arquivos in- teiros, para que, em eventuais falhas da rede, um problema na busca de dados 25

ocorrer somente em uma operação de abertura de arquivo, nunca em uma ope- ração de leitura ou de escrita (CARNEGIE MELLON UNIVERSITY, 2000);

• Semânticas de compartilhamento bem definidas.

A estrutura dos clientes Coda é ilustrada na Figura 2.2. Em cada máquina cli- ente do sistema, há um processo denominado Vênus, implementado em espaço de usuário, devido à sua complexidade e a fim de garantir portabilidade. As chamadas de sistema chegam até a interface vnode no kernel, que as repassa para um filtro MiniCache em espaço de kernel e, finalmente, chegam ao processo Vênus, em espaço de usuário. Esse filtro MiniCache é utilizado para filtrar as comunicações entre o kernel e o Vênus, com o objetivo de minimizar o overhead do sistema (SATYANA- RAYANAN, 2002).

Figura 2.2: Estrutura de um cliente Coda

Ao receber uma chamada de sistema, primeiramente o processo Vênus procura pela resposta em seu cache. Caso não a encontre, ele envia uma mensagem aos servidores que possuem o arquivo associado a chamada de sistema e, caso nenhuma resposta seja recebida, ele começará a executar em modo desconectado (CARNEGIE MELLON UNIVERSITY, 2000). Ao receber uma requisição de abertura de arquivo pela primeira vez, o Vênus requisita dos servidores os dados do arquivo completo, através de procedimentos RPC. Assim, as próximas requisições a esse arquivo serão completamente locais, inclusive as de escrita, não havendo comunicação em rede. O mesmo procedimento é realizado para diretórios, já que diretórios também são arquivos. Quando o arquivo é fechado, se ele tiver sido modificado, o processo Vênus enviará o arquivo aos servidores. Desse modo, o processo Vênus só envia um arquivo aos servidores que o possuem caso ele tenha sido alterado. Qualquer alteração efetuada é propagada aos servidores, desde a criação de arquivos em um diretório até a remoção de um link simbólico. Esse mecanismo agressivo de cache nos clientes permite ao sistema de arquivos operar em modo desconectado em ocasiões de falhas da rede, muitas vezes sem o usuário perceber. Além disso, estudos confirmaram que os usuários abrem arquivos 26 em modo somente leitura na maioria das vezes, o que beneficia a estratégia utilizada no desenvolvimento do Coda (CARNEGIE MELLON UNIVERSITY, 2000). Esse sistema está em uso constante na Carnegie Mellon University, como sis- tema de arquivos de uso geral e para aplicações específicas apropriadas para utilizar a característica de operação desconectada. O sistema também pode ser utilizado para aplicações de espelho de sites de FTP (File Transfer Protocol) e replicação de servidores web.

2.2.2 Serverless Network File System - xFS

O Serverless Network File System (xFS) foi desenvolvido com o propósito de distribuir as responsabilidades do servidor através de um grande número de má- quinas, que cooperam entre si, tornando-o um sistema totalmente descentralizado, com armazenamento e controle de processamento distribuídos. Assim, qualquer máquina na rede pode armazenar, manter cache ou controlar um bloco de dados. Esse esquema de controle e processamento distribuído permite ao sistema migrar responsabilidades em situações de falhas de componentes, ocasionando alta dispo- nibilidade. Segundo Anderson et al. (1996), a arquitetura totalmente distribuída do xFS deve-se a três fatores:

• O controle de processamento é distribuído dinamicamente em uma granulosi- dade por arquivo; • O armazenamento de dados é distribuído em diversos servidores, implementando- se Redundant Arrays of Inexpensive Disks (RAID) em software, utilizando-se um sistema baseado em log; • É feito um esquema de cache cooperativo, tornando parte da memória do cliente um segmento de uma cache global do sistema.

O software RAID, em um sistema com N discos de armazenamento de dados, particiona cada arquivo em N-1 blocos de dados, sendo o enésimo bloco de paridade. Assim, cada bloco é armazenado em um disco diferente, aumentando-se o desem- penho através do acesso paralelo aos discos, além do bloco de paridade prover um bom mecanismo de tolerância a falhas, já que é possível recuperar o conteúdo de um disco com problemas através dos outros blocos e do bloco de paridade. Contudo, há o overhead gerado pelo RAID, que pode aumentar para a escrita de porções muito pequenas de dados. Esse empecilho é contornado pelo sistema de armazenamento baseado em log, o LFS (Log-structured File System), que adiciona em um buffer escritas pequenas, enviando ao disco os dados em grupos contínuos e grandes, denominados segmentos de log. O controle de metadados e de consistência de cache é feito pelos gerenciadores de metadados. Esses gerenciadores utilizam quatro mapeamentos para localizar os dados de blocos de arquivos quando necessário:

• Manager map: para localizar o responsável do sistema por um arquivo; 27

• Imap: permite ao gerenciador encontrar o local do log do disco onde estão armazenados os seus arquivos;

• File directories: utilizado para determinar o índice do arquivo, a partir do seu nome;

• Stripe group map: contém o mapeamento dos identificadores de segmento presentes nos logs para as máquinas que possuem os dados armazenados.

Para a leitura de um bloco de dados, são necessários diversos passos a serem seguidos. Primeiramente, é necessário a abertura do diretório ao qual o arquivo pertence, a fim de determinar o índice do arquivo. Após, o cliente procura os dados requisitados em seu cache e, caso eles não estejam na sua memória, é gerada uma requisição pela rede. Nessa etapa, o sistema utiliza o manager map para localizar a máquina gerenciadora do arquivo, a partir de seu índice no diretório, para, após, enviar a requisição à máquina gerenciadora. A máquina gerenciadora procura por clientes que possuam os dados em cache e, se encontrá-los, ela repassa a requisição para uma das máquinas encontradas, que enviará os dados para o cliente que originou a requisição. Caso nenhuma máquina possua os dados em cache, o gerenciador procura na tabela imap para encontrar o índice do nodo do bloco. O gerenciador pode ter esse dado em memória ou lê-lo do disco, usando o stripe group map para determinar qual o servidor de armazenamento acessar. O próximo passo é o gerenciador enviar uma requisição de índice de bloco para o servidor de armazenamento, que responderá ao gerenciador. O gerenciador, então, utiliza o índice do nodo para identificar o endereço do log do bloco de dados, enviando uma requisição para o servidor com o endereço do log e o stripe group map do bloco de dados. O servidor, ao receber essa requisição, responde diretamente ao cliente que requisitou os dados. A leitura de um bloco de dados é representada na Figura 2.3.

Figura 2.3: Leitura de um bloco no sistema xFS (fonte: Anderson et al. (1996))

Do mesmo modo que a leitura é realizada em uma granulosidade de bloco, tam- bém é a consistência da memória cache de cada cliente. Antes de modificar um bloco de dados, o cliente precisa adquirir a permissão de alteração daquele bloco, 28 enviando uma mensagem ao gerenciador de blocos. O gerenciador de blocos, então, invalida todas as cópias daquele bloco em outros clientes, atualiza a informação na sua memória cache e envia a permissão ao cliente para escrever naquele bloco. Uma vez com permissão de escrita, o cliente tem a possibilidade de escrever no bloco quantas vezes forem necessárias. Essa permissão será revogada quando outro cliente solicitar uma operação de leitura ou escrita sobre o bloco, o que forçará o cliente com a permissão sobre o bloco a enviar o bloco de dados para o servidor de armazenamento e, também, a enviá-lo ao cliente que solicitou o bloco. O protótipo do sistema foi implementado em quatro partes distintas: um módulo para o kernel do sistema Solaris, possibilitando o acesso a interface vnode do sistema, e três daemons em espaço de usuário, que desempenham as funções de processamento e comunicação com outros nodos do sistema caso o cliente não possua os dados localmente (ANDERSON et al., 1996). Os testes de desempenho, escalabilidade e disponibilidade realizados no xFS apresentaram resultados satisfatórios, com índices de desempenho superiores aos alcançados pelo NFS. O principal destaque nos testes foi a grande escalabilidade apresentada pelo sistema, já que o desempenho não foi afetado com o acréscimo de clientes, sendo que os testes possuíram um máximo de 32 clientes. A largura de banda do sistema atingiu um pico de 13.8 MB/s para leitura e também para a escrita de dados. Entretanto, o protótipo do sistema está longe de um estado ideal, já que a comunicação em rede foi realizada com RPC, o que acrescenta um overhead significativo, além do sistema rodar com três módulos em espaço de usuário e um módulo em espaço de kernel (ANDERSON et al., 1996). O sistema, entretanto, é apropriado somente para máquinas confiáveis, conecta- das a uma rede de alta velocidade. Essa questão de segurança é a maior desvantagem do projeto, que tem a possibilidade de suportar clientes não confiáveis, mas com o uso de outros protocolos de comunicação, o que ocasionará um aumento do overhead do sistema.

2.3 Sistemas Peer-to-Peer

Os sistemas peer-to-peer (P2P) são sistemas altamente escaláveis e robustos, cujos nodos da rede, denominados peers, realizam download e upload de dados, ou seja, cada nodo disponibiliza e, ao mesmo tempo, consome recursos da rede (LEGOUT et al., 2007). As redes P2P são formadas através da criação de uma camada lógica de so- breposição na camada de rede subjacente, por exemplo, a Internet. As principais propriedades dessas redes são:

• Nenhum peer possui uma visualização global da rede;

• Os dados e serviços oferecidos são acessíveis a partir de qualquer nodo;

• Não há um ponto central de coordenação; 29

• Não há um banco de dados central.

Esse tipo de rede pode ser classificada em rede estruturada e rede não estru- turada, variando de acordo com a forma de organização dos dados utilizados para satisfazer requisições de procura. Em redes estruturadas, as informações são arma- zenadas com um método pré-definido, através do auxílio de tabelas hash, enquanto redes não estruturadas são organizadas de forma hierárquica, como, por exemplo, com peers e super-peers, armazenando informações de localização de dados somente em alguns nodos da rede, nesse caso os super-peers (CEBALLOS; GORRICHO, 2006). Os sistemas mais representativos para esse projeto são o Gnutella (PORTMANN et al., 2001), o Freenet (CLARKE et al., 2001), o Ivy (MUTHITACHAROEN et al., 2002) e o BitTorrent (LEGOUT et al., 2007).

2.3.1 Gnutella

O Gnutella é um protocolo aberto, implementado por diversas empresas, como a Limewire e a Bearshare. Ele foi um dos primeiros sistemas descentralizados, desen- volvido com o objetivo de compartilhamento de arquivos (DUFOUR; TRAJKOVIĆ, 2006). A comunicação entre os peers consiste de mensagens com um time-to-live (TTL) definido, já que é adotado o esquema de inundação de mensagens. A estrutura da rede é organizada em duas camadas, com ultra-peers e nodos folhas, como é mostrado na Figura 2.4. Cada ultra-peer conecta-se com diversos outros ultra-peers e controla um grupo de nodos folhas, e cada nodo folha, os quais são a maioria na rede, está conectado a um ou mais ultra-peers. Além disso, são os ultra-peers que contém as informações de localização dos nodos folhas, bem como os seus arquivos sendo compartilhados, e administram as requisições de procura por arquivos na rede (STUTZBACH; REJAIE; SEN, 2008; MOTTA; NIENABER; JENKINS, 2008).

Figura 2.4: Estrutura da rede Gnutella

O cliente Gnutella decide, no momento da sua inicialização, qual dos dois papéis na rede ele desempenhará. Primeiramente, ele contata um servidor para obter o endereço de alguns peers. Essa é a primeira e única vez que o servidor é contatado. O cliente, então, envia mensagens de conexão aos peers que o servidor passou o endereço. Após estabelecer conexões, os clientes enviam periodicamente mensagens 30 gnutella ping aos seus vizinhos, que também as enviarão a todos os seus vizinhos e, assim, sucessivamente, até que o TTL da mensagem chegue ao valor zero. Essa mensagem é enviada para procurar por nodos que aceitem novas conexões, os quais responderão com uma mensagem gnutella pong (DUFOUR; TRAJKOVIĆ, 2006). Caso um nodo folha não encontre um ultra-peer disponível na rede, ele verifica a sua conexão e, se ele possuir uma alta largura de banda e puder receber conexões, ele reconfigura-se como um ultra-peer. Desse modo, é possível manter uma taxa aceitável entre ultra-peers e nodos folha. Entretanto, não há uma regra para a conexão entre os nodos, o que pode levar a diversas conexões distantes, aumentando a latência de comunicação entre os vizi- nhos. Também é possível a formação de redes mal estruturadas, o que ocasiona a subutilização dos recursos disponíveis. Um exemplo de rede cujos recursos são mal aproveitados é mostrado na Figura 2.5 (DUFOUR; TRAJKOVIĆ, 2006).

Figura 2.5: Rede de sobreposição Gnutella com os nodos mal distribuídos

Para localizar arquivos na rede, na primeira versão do protocolo era realizada uma inundação de mensagens entre os nodos da rede (KUROSE; ROSS, 2009; BAR- BOSA et al., 2004). Já na segunda versão, a fim de diminuir o tráfego de rede, o cliente envia mensagens de query para os ultra-peers que ele conhece. O ultra-peer, ao receber uma mensagem de query, procura em seu banco de dados se os seus nodos folhas estão compartilhando o arquivo sendo procurado. Caso ele encontre o arquivo em nodos folhas, ele envia os resultados em uma mensagem query hit ao cliente que iniciou a busca. Caso contrário, ele repassa a busca para outros ultra-peers. Ao ser localizado um ou mais resultados, são enviadas ao cliente mensagens query hit (MOTTA; NIENABER; JENKINS, 2008). Para fazer o download do arquivo, o cliente se conecta diretamente ao nodo que possui o arquivo, utilizando o protocolo HTTP para a transferência dos dados do arquivo. Com o objetivo de analisar a rede Gnutella, Stutzbach, Rejaie e Sen (2008) apre- sentam uma aplicação, denominada Cruiser, para mapear essa rede de sobreposição. Foi constatado que a rede se recupera rapidamente aos peers que saem dela, mesmo que sejam ultra-peers. Os ultra-peers com mais tempo de vida na rede se conectam formando um núcleo denso, provendo conectividade estável e eficiente para os outros nodos da rede. Quanto mais um nodo permanece na rede, mais vizinhos que estão 31 há bastante tempo na rede ele consegue. Por isso, é sugerido que as requisições de procura nessa rede sejam direcionadas para esse núcleo de ultra-peers. Por outro lado, Dufour e Trajković (2006) questionam o desempenho da rede, devido a latência na comunicação entre os nodos da rede. Por isso, é proposta a utilização de um algoritmo de seleção de vizinhos que considera a informação de localização física do nodo, levando em consideração o tempo de ida e volta de uma mensagem (round-trip time - RTT). Com esse novo método de escolha de vizinhos, foram realizadas simulações levando-se em consideração o número médio de nodos que recebem as mensagens de query e o número médio de mensagens query hits recebidas de volta. Os resultados mostraram que o desempenho da rede melhora com essa metodologia de esco- lha de vizinhos, diminuindo o tempo de propagação das mensagens trocadas entre os nodos. Outro estudo foi realizado por Motta, Nienaber e Jenkins (2008) sobre as ame- aças de segurança e problemas de desempenho na rede Gnutella. Um dos principais objetivos dessa pesquisa foi obter informações sobre nodos que retornam respos- tas falsas, espalhando conteúdo mal-intencionado na rede. Esse aspecto acarreta a transferência de arquivos maliciosos, possibilitando ataques de negação de servi- ços. Ao analisar-se os endereços IP dos nodos responsáveis pelas respostas falsas, foi observado que são os mesmos com o passar do tempo e que eles aparecem em determinados segmentos da rede. Além disso, eles entram na rede como super-peers e apresentam certos padrões que sugerem que estejam sendo executados clientes modificados. Para aumentar a segurança da rede, foram propostas duas opções. A primeira opção é a criação de um arquivo com endereços IPs de nodos maliciosos. Esse arquivo seria atualizado dinamicamente e de maneira distribuída, possibilitando a contribuição de todos os peers da rede, mas controlado por uma autoridade central, sendo baixado pelo cliente através do protocolo HTTP antes de entrar na rede Gnutella. A segunda opção seria a aplicação cliente verificar os arquivos sendo compartilhados, procurando por certos padrões de dados suspeitos. Apesar de haver a possibilidade de modificar o código fonte da aplicação, a maioria dos usuários não possui o conhecimento necessário para isso. Chawathe et al. (2003) destacam o problema da escalabilidade dessas redes. Para contornar esse problema, foi proposto um novo sistema P2P baseado no protocolo Gnutella, denominado Gia. As principais modificações são: acréscimo de controle de fluxo, principalmente com relação a mensagens de busca de arquivos, tratamento da heterogeneidade dos nodos e adaptação dinâmica da topologia de rede, na qual nodos mais poderosos possuem mais vizinhos. Os testes mostraram que a escalabilidade do sistema aumenta em até 5 vezes.

2.3.2 Freenet

Freenet é um sistema distribuído de armazenamento de informações, com o ob- jetivo prover segurança, disponibilidade de dados e independência de localização. O sistema permite ao usuário adicionar e requisitar arquivos anonimamente. Segundo 32

Clarke et al. (2001), os principais objetivos do sistema são:

• Proteção da identidade dos usuários;

• Negação ao acesso de informações a usuários não autorizados;

• Armazenamento dinâmico e roteamento eficiente de informação;

• Descentralização de todas as funções da rede.

O sistema executa em nível de aplicação e, apesar da necessidade de medidas de segurança para a transferência de dados, é independente de protocolo de transporte. O protocolo de comunicação é orientado a mensagens, sendo que cada mensagem inclui um identificador, a fim dos nodos conhecerem o estado das operações em execução. Também não há garantias que um arquivo seja armazenado permanen- temente, já que isso é dependente da capacidade de armazenamento do sistema, a qual varia dinamicamente de acordo com os nodos que estão na rede. Em cada nodo do sistema, pode ou não haver um espaço para armazenamento de arquivos, sendo que os peers possuem permissão de escrita e leitura nos espaços disponibilizados pelos nodos. Cada nodo também possui uma tabela dinâmica de roteamento, que contém endereços de outros peers da rede, bem como os arquivos que eles armazenam, cada um representado por uma chave que o identifica. Desse modo, o sistema permite aos usuários compartilharem o espaço no disco rígido que não está sendo utilizado, podendo-se utilizar o sistema como uma extensão do disco rígido para o armazenamento de dados. Como mencionado anteriormente, os arquivos presentes na rede são identificados por chaves, obtidas através do algoritmo SHA-1. Ao todo, há 3 tipos diferentes de chaves:

• Keyword-signed key (KSK): é gerada a partir do texto de descrição do arquivo. A partir desse texto, são geradas as chaves pública, que representará o arquivo, e privada, utilizada para garantir uma verificação mínima de integridade do arquivo;

• Signed-subspace key (SSK): permite ao usuário criar espaços de nomes (names- paces) pessoais, evitando que dois usuários possuam a mesma chave de arquivo para arquivos diferentes com a mesma descrição. Assim, o usuário deve criar um espaço de nomes, gerar um par de chaves (pública e privada), inserir o arquivo, passar o algoritmo de hash na chave pública do espaço de nomes e na chave pública do arquivo, fazer uma operação de XOR entre os dois resultados de hash e passar o algoritmo de hash novamente no resultado do XOR; • Content-hash key (CHK): formada a partir do hash do conteúdo do arquivo.

Para localizar um arquivo na rede, o cliente deve, primeiramente, calcular a chave do arquivo. Após, ele envia uma mensagem de requisição do arquivo para a aplicação local. A aplicação local verifica se possui o arquivo e retorna se encontrá- lo. Caso contrário, ela procura em sua tabela de rotamento pela chave mais próxima 33 da chave procurada e envia uma mensagem de requisição com um determinado TTL para o nodo correspondente. Cada nodo que recebe a mensagem verifica se possui o arquivo e, se não possui-lo, repassa a mensagem para o nodo que possuir a chave de arquivo mais próxima da procurada. O TTL da mensagem é decrementado a cada nodo, evitando a propagação indefinida da mensagem caso o arquivo não seja encontrado (BARBOSA et al., 2004; CLARKE et al., 2001). Esse processo continua até o arquivo ser encontrado ou o campo TTL da men- sagem atingir o valor nulo. Nos dois casos é enviada uma resposta pelo caminho inverso. No caso do arquivo ter sido encontrado, a resposta irá conter o arquivo. Assim, cada nodo em que a resposta passar armazenará uma cópia do arquivo local- mente e criará uma nova entrada na sua tabela de roteamento associando o arquivo a si mesmo. Um exemplo de busca por um arquivo é mostrado na Figura 2.6. Nela, é mos- trado que um nodo, quando não possuir vizinhos para enviar a requisição (nodo c), retorna uma mensagem de erro ao nodo do qual ele recebeu a mensagem (nodo b). Nesse caso, o nodo b envia para outro vizinho a requisição pelo arquivo. Outra situação é a ocorrência de loop na busca (identificada pelas comunicações 4, 5 e 6), detectada pelo nodo b, que envia uma mensagem de falha ao nodo e. Desse modo, o nodo e envia a mensagem para outro vizinho (nodo d), o qual possui o arquivo sendo procurado (CLARKE et al., 2001).

Figura 2.6: Procura de um arquivo na rede Freenet

Com a forma de armazenamento de arquivos adotada pelo sistema, na qual, em mensagens de resposta geradas pela busca por um arquivo, todos os nodos que re- passam a resposta armazenam o arquivo contido na mensagem, um nodo malicioso possui muita dificuldade de modificar um arquivo presente no sistema ou mesmo de removê-lo. Isso ocorre devido a possibilidade do arquivo estar armazenado em diver- sos nodos e, por causa da característica de anonimato dos nodos, o peer malicioso não conhece todos os nodos que possuem o arquivo.

2.3.3 Ivy

O Ivy é um sistema de arquivos peer-to-peer para leitura e escrita de dados. Ele não possui nenhum componente centralizado e provê integridade mesmo na ocorrência de usuários não confiáveis (MUTHITACHAROEN et al., 2002). 34

O sistema consiste em um conjunto de logs, um por participante. Cada partici- pante possui permissão de escrita somente em seu log e permissão de leitura nos logs de todos os participantes da rede. Cada log consiste em uma lista encadeada de re- gistros e contém todas as modificações feitas pelo cliente em dados e em metadados dos arquivos. Os logs são armazenados por tempo indeterminado em uma tabela hash distri- buída, que consiste no mapeamento de chaves em valores. Cada cliente da rede possui uma tabela local, mas as entradas dessa tabela são replicadas em outros cli- entes, evitando a perda de dados caso um cliente falhe. A integridade dos dados da tabela é verificada de duas maneiras possíveis: a chave na tabela deve ser o hash do seu valor com o algoritmo SHA-1 ou a chave deve ser uma chave pública e o seu valor ser assinado com a chave privada correspondente. Assim, é possível verificar a autenticidade de todos os dados retirados da tabela hash, prevenindo a inserção de dados em um arquivo feita por um nodo malicioso. Além disso, os peers da rede comunicam-se somente no momento da montagem do sistema e, após, através do compartilhamento de informações da tabela hash. Dessa maneira, é possível que diversos clientes escrevam no mesmo arquivo con- correntemente. Essas operações serão armazenadas nos logs de cada cliente, para, após, o sistema agregar os logs dos clientes, ordená-los e aplicar as alterações no arquivo. A implementação desse sistema foi realizada em C++, utilizando-se o cliente NFSv3 como interface do sistema de arquivos com o usuário. Ele está dividido em vários módulos, que cooperam entre si. Cada cliente possui um servidor Ivy, que opera como cliente da tabela hash distribuída (DHash), e um agente, que armazena as chaves privadas do sistema. O módulo DHash se comunica com os módulos DHash de outros clientes, trocando informações sobre as modificações feitas nos arquivos do sistema. A Figura 2.7 mostra a estrutura do sistema de arquivos Ivy em um cliente na rede (MUTHITACHAROEN et al., 2002).

Figura 2.7: Estrutura do sistema Ivy

O sistema armazena os proprietários e as permissões de cada arquivo, mas não utiliza essas informações para evitar ou permitir o acesso aos arquivos. Se um ar- quivo não deve ser visto por alguns participantes da rede, a única maneira disponível 35

é criptografar o seu conteúdo. Além disso, o sistema foi intencionalmente modelado para que não seja possível inserir automaticamente um participante na rede an- tes que todos os participantes concordem com a sua entrada, a fim de aumentar a segurança da rede. Outro mecanismo de segurança adotado é a recuperação do sistema após um participante malicioso realizar alguma ação prejudicial. Assim, mesmo que um par- ticipante normal passe a prejudicar o sistema, é possível excluir esse participante do sistema, ignorando as suas ações no log, ou aceitar as operações desse usuário até um certo instante, antes dele tornar-se um nodo malicioso. O desempenho do Ivy é inferior ao do NFS em aproximadamente 33% (MUTHI- TACHAROEN et al., 2002). Os fatores mais influentes nesse resultado são a latência na comunicação em rede e a geração de assinaturas digitais dos dados armazenados na tabela hash distribuída.

2.3.4 BitTorrent

O BitTorrent é um sistema peer-to-peer robusto e altamente escalável (LEGOUT et al., 2007; LEVIN et al., 2008). Esse sistema, implementado e disponibilizado em 2001, é o responsável por uma taxa de 18% a 35% de todo o tráfego de dados da Internet. O sistema permite que múltiplos clientes, denominados peers, possam realizar o download de um arquivo concorrentemente, através do compartilhamento dos blocos de dados do arquivo (SALMON; TRAN; ABHARI, 2008; LUAN; TSANG, 2006). Segundo Salmon, Tran e Abhari (2008), o protocolo BitTorrent pode ser dividido em 3 componentes principais:

• Seeder inicial: é o peer que contém o arquivo completo sendo distribuído;

• Tracker: é um servidor centralizado que mantém uma lista de todos os peers presentes na rede, além de coletar estatísticas de utilização da rede;

• Arquivo torrent: é um arquivo de metadados com informações sobre os da- dos sendo distribuídos na rede, possuindo a extensão .torrent. Esse arquivo é criado pelo seeder inicial, que divide o arquivo a ser compartilhado em blocos de dados, normalmente com tamanho entre 65 kilobytes e 1 megabyte (SAL- MON; TRAN; ABHARI, 2008). Além disso, esse seeder calcula o checksum para cada bloco criado, utilizando um algoritmo de hash. Dessa maneira, o arquivo torrent contém o nome do arquivo original sendo compartilhado, o tamanho e o hash de cada bloco de dados do arquivo e o endereço do tracker.

Para entrar nessa rede, primeiramente o cliente deve contatar um tracker, que é o único componente centralizado do sistema. Ao ser contatado, o tracker adiciona o novo cliente a sua lista de peers e envia um subconjunto randômico de peers para o novo cliente poder entrar na rede (ARTHUR; PANIGRAHY, 2006). O cliente, por sua vez, ao receber a lista, fará tentativas de estabelecimento de conexão com um ou mais nodos, e, caso ele consiga poucos vizinhos, há a possibilidade de solicitar outros endereços de peers ao tracker. Inicialmente, o cliente é chamado de leecher, 36 pois ele dá preferência ao recebimento de dados. Ao completar o arquivo, o cliente torna-se um seeder no sistema, pois ele somente enviará dados para outros peers da rede, não consumindo recursos (PARVEZ et al., 2008). Após estar na rede, o cliente começa a requisitar blocos de dados. A seleção de blocos de dados é feita com extrema cautela, já que um julgamento errado pode levar a uma situação em que o cliente terá todos os blocos que estejam sendo oferecidos, não conseguindo os restantes, ou, por outro lado, não tendo nenhum bloco para enviar aos seus vizinhos. No início, quando o cliente não possui nenhum bloco completo para realizar o upload, ele seleciona blocos aleatórios para pegar, até que ele complete o primeiro bloco de dados. Após ter um bloco completo, é utilizado o algoritmo rarest-first para a seleção de blocos. Esse algoritmo seleciona o bloco que o menor número de peers vizinhos possui, ou seja, o bloco mais raro naquela área da rede de sobreposição. Assim, o objetivo do algoritmo é realizar o download de blocos de outros peers, somente pegando dos seeders blocos que nenhum vizinho possui (COHEN, 2003; PARVEZ et al., 2008; LUAN; TSANG, 2006; SHERMAN; NIEH; STEIN, 2009). O cliente conhece os blocos que seus vizinhos possuem através das mensagens trocadas no estabelecimento da conexão, que possuem um campo de bits, no qual são informados quais blocos completos o cliente possui. Além disso, cada peer envia mensagens para seus vizinhos quando ele completa o recebimento de um novo bloco (NGIWLAY; INTANAGONWIWAT; TENG-AMNUAY, 2008). Para decidir com qual peer trocar dados, o cliente utiliza o algoritmo de cho- king. O algoritmo dá preferência aos peers com altas taxas de upload, além de ter o objetivo de eliminar clientes que não compartilham dados (NGIWLAY; INTANA- GONWIWAT; TENG-AMNUAY, 2008). A grande vantagem desse sistema é que a rede possui múltiplas cópias do arquivo sendo compartilhado, devido a sua divisão em blocos. O número de cópias aumenta de dois modos: quando um cliente finaliza o download do arquivo ou quando um conjunto de clientes podem combinar os seus blocos para formar uma nova cópia do arquivo (SALMON; TRAN; ABHARI, 2008). Entretanto, de acordo com (GUO et al., 2005), o BitTorrent possui 3 desvantagens principais:

• A disponibilidade dos dados do sistema decai rapidamente, devido ao decrés- cimo da chegada de peers com o passar do tempo e a falta de seeders. Isso ocasiona uma dificuldade crescente aos peers em localizar certos blocos do arquivo;

• O desempenho dos clientes no sistema é instável, variando de acordo com os peers presentes na rede;

• A taxa de compartilhamento de dados dos clientes (taxa de upload dividia pela taxa de download) decresce muito rapidamente.

Além disso, ao finalizar um download, não há incentivos para o cliente per- manecer conectado, exceto pelo fato do compartilhamento de dados. Também há o problema da cifragem de dados, que é necessária devido às restrições impostas 37 pelos servidores de acesso à Internet, ocasionando um overhead extra ao sistema (SALMON; TRAN; ABHARI, 2008). Legout et al. (2007) realizaram um estudo experimental sobre as propriedades do protocolo, o uso da largura de banda dos peers, principalmente de upload, e a metodologia de incentivo ao compartilhamento de arquivos, variando-se alguns parâmetros entre os testes, como a taxa de upload de leechers e seeders, a fim de avaliar o algoritmo de choking. Em testes de incentivo de compartilhamento, com um seeder inicial com grande capacidade de upload, foi demonstrado que o algoritmo de choking empregado pelo BitTorrent desempenha bem a sua função: os clientes que mais contribuem com dados para a rede são os primeiros a completar o download do arquivo. Além disso, leechers lentos acabam recebendo maior quantidade de dados provenientes de seeders, que enviam dados aos peers sem levar em conta a velocidade de upload de cada um (LEGOUT et al., 2007). Apesar da descentralização, o sistema ainda possui um componente centralizado, o tracker. Fry e Reiter (2006) apresentam um modelo para remover completamente esse componente, substituindo-o por um conjunto de protocolos distribuídos base- ados em caminhadas aleatórias pela rede, porém sem localizar todos os nodos do sistema. Nesse modelo, a entrada de um peer na rede é feita a partir de pontos de entrada (entry points), que desempenham uma função semelhante a de um tracker, podendo ou não pertencer a rede. Dessa forma, em um primeiro momento, cada ponto de entrada na rede só conhece alguns peers. Para fornecer ao novo cliente da rede um conjunto aleatório de peers, há duas opções: o ponto de entrada utiliza o caminhamento aleatório pela rede, a partir de seus vizinhos, ou o novo nodo pode realizar esse caminhamento pela rede, reduzindo o overhead do ponto de entrada. Além disso, caso um nodo esteja precisando de vizinhos, ele pode adquiri-los a partir de qualquer nodo da rede, já que todos podem desempenhar a função de ponto de entrada. Os testes realizados com esse novo modelo mostraram que o sistema é expandido de modo similar ao modelo com tracker, ou, em certas ocasiões, essa expansão é até superior. Entretanto, a expansão do sistema pode ser melhorada, renovando-se o conjunto de vizinhos dos pontos de entrada. Essa renovação pode ser feita após um determinado período de tempo, aleatoriamente removendo-se vizinhos ou realizando- se o caminhamento aleatório nos vizinhos de algum vizinho. Há também a possibilidade de integrar-se esse modelo com o modelo atual do BitTorrent, com tracker junto com pontos de entrada. Dessa maneira, novos clientes poderiam entrar na rede mesmo se o tracker estivesse com problemas.

2.4 Síntese

Com base nos sistemas apresentados, percebe-se diversas características distri- buídas entre os diferentes projetos que levam ao seu êxito, desempenhando uma fun- ção vital na obtenção de resultados satisfatórios. Entretanto, esses sistemas também 38 apresentam desvantagens, alguns no seu projeto e outros na sua implementação. No caso do NFS, houve uma grande evolução da versão 3 para a versão 4, principalmente no que diz respeito ao overhead de comunicação entre os clientes e o servidor. Além disso, o NFS roda em espaço de kernel, o que diminui o overhead de processamento do sistema. Embora o NFS seja um sistema projetado nos mínimos detalhes, ele foi baseado no modelo cliente/servidor, o que limita consideravelmente a sua escalabilidade. Ele também não possui um controle de falhas eficiente, já que, em casos de corrupção de dados, a resposta do NFS ao usuário pode não refletir o que realmente ocorreu na operação, muitas vezes retornando sucesso em uma operação que falhou ou até mesmo corrompendo partes do kernel do Linux. Também há a questão do uso de RPC para a comunicação em rede, ocasionando um overhead muito grande. Já o Coda apresenta os mecanismos de operação desconectada, ocultando possí- veis falhas do servidor, e de cache persistente no cliente, aumentando o desempenho do sistema e possibilitando ao usuário continuar a utilizá-lo em eventuais falhas da rede. Embora a operação desconectada seja uma característica que traz benefícios, há também problemas, já que ela exige uma grande quantidade de memória cache no cliente, além de acarretar problemas de consistência de dados caso a rede falhe e um arquivo seja alterado em mais de um cliente durante essa falha. O xFS apresenta diversas características importantes para um sistema completa- mente distribuído, destacando-se o armazenamento dos dados dos arquivos, no qual esses dados são divididos entre os nodos da rede em uma granulosidade de bloco. Contudo, a granulosidade de distribuição em bloco de dados é pequena. Portanto, essa completa distribuição do sistema acarreta um overhead de comunicação muito grande, devido a grande quantidade de informações que precisam ser trocadas entre os nodos do sistema. O BitTorrent apresenta como diferenciais o controle da taxa de compartilha- mento de dados dos peers da rede, limitando o download dos clientes que não com- partilham seus dados na mesma proporção em que requisitam novos dados, e a escalabilidade, possibilitando a formação de uma rede de sobreposição entre milha- res de peers. Por outro lado, os algoritmos utilizados pelo BitTorrent nem sempre alcançam o objetivo desejado. Há ocasiões em que a taxa de compartilhamento de dados dos peers é muito baixa e o maior dos problemas é quando o seeder inicial do sistema possuir uma baixa largura de banda para o compartilhamento de um determinado arquivo. A Tabela 2.1 resume os sistemas estudados com alguns dos principais atributos de sistemas de distribuição de arquivos. Para cada atributo apresentado na tabela, foram definidos três valores possíveis em ordem crescente: baixo, médio e alto. O valor do atributo para cada sistema na tabela indica o quanto esse atributo influencia a execução desse sistema. Ao analisar essas características, percebe-se a importância de se projetar e imple- mentar com cuidado um sistema peer-to-peer, dando especial atenção aos seguintes detalhes:

• Comunicação em rede, evitando a troca exagerada de mensagens entre os peers 39

Tabela 2.1: Síntese das características dos sistemas estudados Overhead de Utilização de Sistema Escalabilidade comunicação Disponibilidade Robustez memória em rede NFS Baixa Média Alto Alta Alta smbfs / cifs Baixa Média Médio Alta Média Coda Alta Alta Alto Alta Média xFS Alta Média Alto Alta Alta Gnutella Média Baixa Médio Média Média Freenet Alta Alta Alto Média Média Ivy Média Média Alto Média Média BitTorrent Alta Média Médio Alta Alta

da rede;

• Armazenamento de dados de arquivos na memória do cliente, já que essa memória é utilizada como cache do sistema

• Overhead de processamento do sistema.

Dessa forma, é necessário desenvolver uma interface otimizada para o sistema, capaz de atender às requisições do usuário em um intervalo de tempo, idealmente, igual ao de um sistema de arquivos local. Entretanto, também é necessário controlar a utilização de memória ocasionada pelo armazenamento dos dados dos arquivos remotos. Com base nisso, é preciso que haja um balanceamento entre o tempo de resposta das requisições do usuário e a utilização de memória do sistema. 40

3 LOCAL PEER-TO-PEER PROTOCOL

O Local Peer-to-Peer Protocol (LP2P) (ROCHA; MARCON; ÁVILA, 2010) é uma plataforma de comunicação P2P para uso em ambientes de redes locais (LANs), sendo totalmente descentralizado, escalável, autogerenciável e auto-organizável. Ele reúne características da arquitetura peer-to-peer e das redes locais para prover o compartilhamento de arquivos de forma eficiente e distribuída. Além disso, o LP2P emprega mecanismos de confiança e recompensa e possui um módulo de descoberta de serviços, minimizando o overhead da comunicação em rede e aumentando a trans- parência das ações efetuadas perante o usuário. Dessa forma, é necessário que cada usuário execute o daemon LP2P localmente. À medida que o LP2P de vários clientes são inicializados na rede, é realizada uma verificação quanto ao conjunto de compartilhamentos especificados por cada usuário, sendo que esses compartilhamentos são definidos através de dados de inicialização. O conceito de compartilhamento é similar ao conceito de “pasta compartilhada”, presente em diversos sistemas operacionais. Há também a possibilidade de criação de compartilhamentos sigilosos, cuja transferência de conteúdo é protegida por meio de criptografia, com o acesso inicial sendo autorizado mediante a apresentação de credenciais válidas (senhas ou chaves criptográficas). Após a sua inicialização, o daemon presente em cada máquina busca verificar o conteúdo disponível nos demais integrantes dos compartilhamentos que ele possui. Assim, é formada uma base de conhecimento composta por compartilhamentos, hosts e dados compartilhados, que, por sua vez, estão disponíveis para o uso de aplicações externas presentes em cada peer e que se comunicam com a plataforma LP2P localmente. Essas aplicações, que utilizam as informações fornecidas pelo LP2P, formam a interface do sistema aos usuários, contendo a relação de arquivos disponíveis como uma pasta única, porém fisicamente distribuída. Um exemplo do funcionamento do LP2P pode ser visualizado na Figura 3.1. Nesse exemplo, o peer01 e o peer04 ingressam no compartilhamento mapas e o peer02 e o peer03 conectam-se ao compartilhamento pesquisa (ROCHA; MAR- CON; ÁVILA, 2010). O LP2P realiza toda a interface entre peers e aplicações, empregando uma abor- dagem descentralizada e escalável. De acordo com o modelo peer-to-peer descentra- lizado, a arquitetura conta com o envolvimento de todos os participantes da rede para garantir a manutenção dos índices responsáveis pela localização e distribuição dos arquivos disponíveis em cada compartilhamento. 41

peer01 peer03 pesquisa recife.gif redes.pdf lp2p.tex lp2p.tex mapas peer02 peer04 mapas.gz recife.gif redes.pdf mapas.gz

Figura 3.1: Funcionamento do LP2P

Vale ressaltar que esse protocolo foi planejado e desenvolvido com o objetivo de gerar um baixo overhead de comunicação, mas com alta eficiência. Com essa meta, foi especificado um conjunto de mensagens de controle, que empregam em sua maioria mensagens do tipo multicast, e de transferência de dados, que são do tipo unicast. A diferenciação na forma de envio desses pacotes melhora o aproveitamento da largura de banda e auxilia nas questões de escalabilidade do modelo. Esse capítulo está estruturado da seguinte maneira: a arquitetura do sistema é explicada na Seção 3.1, o formato das mensagens de comunicação interna do sistema, as quais são utilizadas para a comunicação com o sistema de arquivos desenvolvido nesse projeto, é mostrado na Seção 3.2 e, por fim, na Seção 3.3, são descritos os módulos externos ao sistema.

3.1 Arquitetura

A arquitetura do LP2P foi definida modularmente, a fim de garantir o funcio- namento do sistema com aplicações externas, como interfaces HTTP e sistemas de montagem de arquivos, bem como com os sistemas para descoberta de serviços e me- canismos de recompensa e confiança. Ela é mostrada na Figura 3.2, apresentando as seguintes características:

• Pilha de protocolos: para garantir a modularização da arquitetura, facili- tando o seu desenvolvimento e a escalabilidade do sistema;

• Correlação: para definir a relação de funcionamento entre os diferentes mó- dulos do sistema;

• Sistema de mensagens: para estabelecer um padrão de mensagens no sis- tema, tanto internamente no host, quanto externamente na comunicação en- tre os participantes da rede. Esse conjunto de mensagens é responsável pela divulgação de informações de controle e de transferência de dados, tanto re- motamente entre os peers quanto de cada peer às aplicações locais;

• Método de transporte: define o protocolo de transporte a ser utilizado e o tamanho dos blocos de dados. 42

Aplicações Trust Sec-SD MGT-BD MGT-MSG LP2PFS LP2P LP2P

LP2P-CORE Hardware

Figura 3.2: Arquitetura do sistema LP2P

O módulo LP2P-CORE disponibiliza uma interface de comunicação entre todos os componentes do sistema, oferecendo uma camada de abstração entre as plata- formas de comunicação, de descoberta de serviços, de sistema de recompensa, de interação com a base de conhecimento e de aplicações externas. Seu comportamento dependerá do tipo de ação a ser executada, bem como dos aspectos de segurança empregados aos compartilhamentos da rede. Assim, as seguintes situações são pos- síveis:

• Inicialização: é efetuada na entrada de um peer na rede. Primeiramente, é realizada a verificação dos compartilhamentos disponíveis na rede, validando- os com os parâmetros locais. Após, são enviadas solicitações multicast para a obtenção dos recursos disponíveis e de suas respectivas informações. Caso exista um compartilhamento de acesso restrito, é necessário que o módulo externo Sec-SD envie ao LP2P-CORE uma chave de grupo, possibilitando ao peer requisitar chaves de sessão no futuro;

• Atualização: para garantir a consistência dos dados presentes no sistema, é realizado o monitoramento periódico de alterações nos arquivos presentes nos compartilhamentos do sistema. Ao detectar modificações em arquivos, o mó- dulo realiza duas ações. A primeira ação consiste na atualização da base local de conhecimento, com a ajuda do módulo LP2P-BD. Após, a segunda ação consiste em propagar essa modificação aos demais participantes da rede, com a ajuda do módulo LP2P-MSG. A mensagem de notificação é transmitida via multicast, com a possibilidade de ser criptografada caso o compartilhamento em que ocorreu a alteração seja restrito;

• Estabelecimento de Chaves de Sessão: ao identificar a necessidade de trocar dados de forma segura, o LP2P-CORE verifica a existência de uma chave de sessão para o peer. Caso o peer não possua essa chave, ocorre uma negociação entre os peers envolvidos, com o auxílio da chave de grupo obtida na inicialização. Ao adquirir essa chave, o peer irá armazená-la na sua base de conhecimento, possibilitando o estabelecimento de conexões seguras no futuro;

• Transferência de dados: o LP2P-CORE consulta a base de conhecimento do peer, com o auxílio do módulo LP2P-BD, a fim de obter as fontes do recurso, bem como a sua reputação. De acordo com as informações obtidas, o módulo 43

define para qual peer será feita a solicitação de dados e envia uma mensagem de solicitação de dados ao peer escolhido;

• Comunicação Interna: envolve o recebimento de mensagens locais proveni- entes de sistemas de arquivos ou de outras aplicações que realizem a interface do sistema com o usuário.

O LP2P-CORE se comunica com o módulo LP2P-BD, que é o gerenciador da base de conhecimentos do peer. Essa comunicação é feita com o objetivo de popular a base de conhecimentos local, a qual utiliza um banco de dados local para regis- trar informações do sistema, como reputação, segurança, vizinhos, recursos locais e recursos disponíveis na rede. O módulo LP2P-MSG é o responsável pelo envio e recebimento de mensagens no sistema. Dessa maneira, a comunicação entre os peers remotos e entre o sistema com as aplicações é feita através da troca de mensagens. Essas mensagens são divididas em quatro grupos, denominados grupos de primitivas, os quais são:

• Manipulação de arquivos: engloba toda ação referente a manipulação de arquivos no sistema. Esse grupo é dividido em quatro mensagens principais:

– List: enviada por um peer da rede aos outros para solicitar a listagem de arquivos presentes em um determinado compartilhamento; – Send list (sendl): enviada em resposta ao recebimento de uma men- sagem list, contendo os arquivos que o peer possui no compartilhamento especificado na mensagem list; – Get: mensagem de solicitação de envio de um conjunto de dados de um arquivo; – Send file (sendf): mensagem gerada pelo peer em resposta ao recebi- mento da mensagem get, contendo os dados solicitados;

• Notificações do sistema: são mensagens informativas sobre modificações que ocorrem no sistema. Podem ser de:

– Adição de arquivos: informa que um ou mais arquivos foram adicio- nados a um determinado compartilhamento; – Remoção de arquivos: atua na informação de remoção de um ou mais arquivos de um determinado compartilhamento;

• Erros do sistema: para mensagens que informam a ocorrência de erros du- rante uma determinada operação do sistema;

• Comunicação interna: utilizada para a comunicação interna do LP2P com as aplicações que realizem a interface com o usuário do sistema. Um exem- plo da comunicação do daemon LP2P com o sistema de arquivos LP2PFS é apresentado na Figura 3.3. 44

Figura 3.3: Comunicação interna do sistema LP2P

3.2 Mensagens de Comunicação Interna

A troca de dados entre o daemon e os módulos locais do LP2P é feita através do protocolo da camada de transporte TCP. O formato geral da mensagem, que pode ser visualizado na Figura 3.4, possui os seguintes campos:

• Protocolo, que possui o valor padrão “LP2P”;

• Versão, que indica a versão do LP2P em uso;

• Primitiva, que representa o grupo de primitivas do protocolo;

• Operação, que define a operação a ser executada de acordo com a primitiva;

• Um campo de tamanho variável, que possui os dados sendo transferidos.

Figura 3.4: Estrutura da mensagem LP2P

As primitivas objetivam agrupar de forma hierárquica o conjunto de operações definidas pelo protocolo. Dessa maneira, o protocolo oferece um conjunto de 65536 operações possíveis no sistema, já que os campos primitiva e operação possuem 1 byte cada um. Resumidamente, os 7 primeiros bytes são fixos, enquanto o tama- nho do último campo, o payload da mensagem, dependerá do tipo de operação do protocolo. A primitiva de comunicação local, definida pelo grupo 4, é utilizada para a co- municação interna no peer, sendo, portanto, utilizada pela comunicação do daemon com o sistema de arquivos LP2PFS. Nesse contexto, há 6 tipos de mensagens troca- das, os quais são local list (llist), local send list (lsenl), local get update (lgetu), local send update (lsendu), local get (lget) e local send file (lsendf). 45

Local list (llist)

Esta mensagem é enviada ao LP2P para requisitar a listagem de conteúdo de um determinado compartilhamento. Basicamente, essa mensagem é enviada no momento da montagem do sistema de arquivos. O campo de primitiva possui valor 4 e o campo de operação possui valor 1. O seu formato pode ser visualizado na Figura 3.5.

Figura 3.5: Formato da mensagem llist

Local send list (lsendl)

É uma mensagem enviada pelo LP2P em resposta à mensagem list, contendo as informações dos arquivos presentes no compartilhamento solicitado. O campo de primitiva possui valor 4 e o campo de operação possui valor 2. O formato da mensagem lsendl pode ser visualizado na Figura 3.6.

Figura 3.6: Formato da mensagem lsendl

Local get update (lgetu)

A mensagem lgetu é enviada pelo sistema de arquivos ao ser solicitada a listagem dos arquivos presentes no compartilhamento montado, a fim de obter-se as informa- ções necessárias sobre eventuais alterações de arquivos desde a última comunicação com o daemon. O campo de primitiva possui valor 4 e o campo de operação possui valor 3. O seu formato é mostrado na Figura 3.7.

Local send update (lsendu)

Essa mensagem é gerada pelo LP2P em resposta ao recebimento de uma mensa- gem lgetu. Nela, estarão as informações de todos os arquivos adicionados, excluídos 46

Figura 3.7: Formato da mensagem lgetu e alterados no compartilhamento desde a última comunicação. O campo de primi- tiva possui valor 4 e o campo de operação possui valor 4. O seu formato é mostrado na Figura 3.8.

Figura 3.8: Formato da mensagem lsendu

Local get (lget)

É enviada pelo sistema de arquivos para solicitar um subconjunto dos dados de um determinado arquivo. O campo de primitiva possui valor 4 e o campo de operação possui valor 5. O seu formato é mostrado na Figura 3.9.

Figura 3.9: Formato da mensagem lget

Local send file (lsendf)

É uma mensagem gerada em resposta ao recebimento da mensagem lget, con- tendo os dados solicitados. O campo de primitiva possui valor 4 e o campo de operação possui valor 6. O seu formato pode ser visualizado na Figura 3.10.

3.3 Módulos Externos

O LP2P possui diversos módulos sendo desenvolvidos externamente ao núcleo do sistema, dentre os quais destacam-se o Secure Service Discovery (Sec-SD), o TrustLP2P e o LP2PFS. 47

Figura 3.10: Formato da mensagem lsendf

O módulo Sec-SD provê ao sistema recursos de descoberta de serviços em redes locais. O módulo garante a segurança dos dados nos compartilhamentos de arquivos do sistema, já que há a possibilidade de existência de arquivos confidenciais, os quais podem ser acessados somente por um grupo específico de usuários do sistema, além de ser necessário que um peer possa verificar a autenticidade de outro peer que lhe envia um arquivo. Dessa forma, o módulo possui recursos de descoberta e anúncio de serviços, possibilitando a automatização na descoberta de peers no sistema. Outro módulo externo é o TrustLP2P, que é um sistema de reputação para o LP2P, cujo objetivo é recompensar os hosts da rede de acordo com o seu com- portamento, além de combater possíveis tipos de ataques maliciosos. Outro dado utilizado é o valor de confiança, que é utilizado para determinar o quanto um peer confia mesmo que o outro é quem está dizendo ser. O terceiro módulo externo é o módulo do sistema de arquivos LP2PFS, que foi desenvolvido nesse projeto, o qual disponibiliza ao usuário os arquivos presentes nos compartilhamentos através da interface de sistema de arquivos do Linux. O sistema LP2PFS é apresentado no capítulo 4. 48

4 LP2P FILE SYSTEM

O sistema de arquivos LP2P (LP2PFS) foi desenvolvido como um módulo para o kernel do Linux (SALZMAN, 2009), sendo um sistema de arquivos como qualquer outro do ponto de vista do usuário. O objetivo do sistema de arquivos é proporcionar ao usuário o acesso aos arquivos sendo compartilhados através do LP2P. Portanto, na primeira versão, o sistema de arquivos é um sistema somente leitura (read-only). Para o sistema ser montado, basta ser digitado no terminal o seguinte comando:

# mount -t lp2pfs

onde:

• -t lp2pfs: significa o tipo de sistema de arquivos;

: informa o nome do compartilhamento do sistema LP2P a ser montado;

: local em que o sistema de arquivos será montado.

A implementação do sistema de arquivos é descrita na Seção 4.1, sendo apresen- tada a sua estrutura na Seção 4.1.1 e o mecanismo de armazenamento de dados em memória na Seção 4.1.2. Após, são apresentadas as otimizações feitas no código do sistema na Seção 4.2, de acordo com as técnicas oferecidas pela interface de progra- mação do kernel do Linux, e, na Seção 4.3, é descrita a comunicação com o sistema LP2P. Por fim, são apresentadas algumas das funcionalidades do sistema, como no- tificação de eventos para as aplicações na Seção 4.4, o arquivo virtual do sistema LP2PFS no sistema de arquivos Procfs na Seção 4.5 e as opções de montagem do sistema na Seção 4.6.

4.1 Implementação

A estrutura do sistema de arquivos deve estar de acordo com a camada do Virtual File System (VFS) do kernel do Linux e, consequentemente, com o Page Cache para o armazenamento dos dados dos arquivos em memória. 49

4.1.1 Virtual File System

O kernel do Linux disponibiliza aos desenvolvedores uma camada de abstração para a implementação de sistemas de arquivos, denominada Virtual File System (VFS). Assim, o principal objetivo do VFS é prover um método uniforme para a manipulação de arquivos, diretórios e outros objetos de diferentes sistemas de ar- quivos, como sistemas de arquivos locais (ext3 e ext4), sistemas de arquivos virtuais (Procfs e ) e sistemas de arquivos em rede (NFS) (LINUX KERNEL ORGA- NIZATION, 2010; MAUERER, 2008; BOVET; CESATI, 2005). Na camada de abstração, são definidas as interfaces e estruturas de dados co- muns a todos os sistemas de arquivos. Dessa maneira, o detalhes específicos de implementação de cada sistema são invisíveis ao kernel, que só necessita conhecer os métodos genéricos de acesso ao sistema, que são definidos pelo VFS. Na Figura 4.1 é mostrada a importância do VFS, possibilitando o suporte a vários sistemas de arquivos.

Figura 4.1: A camada de abstração do VFS

Apesar do código do VFS ter sido desenvolvido na linguagem C, foi utilizado o conceito de orientação a objetos, simulando-o com o uso de estruturas. Nele, há quatro objetos principais, que formam a base de todos os sistemas de arquivos do Linux (LOVE, 2005b):

• super_block: é utilizado par armazenar informações de um determinado sis- tema, representando um sistema de arquivos montado. Essa estrutura está definida em e uma versão simplificada é mostrada na Lista- gem 4.1.

Listagem 4.1: Estrutura super_block struct super_block { struct list_head s_list; dev_t s_dev; unsigned long s_blocksize; unsigned char s_blocksize_bits; unsigned char s_dirt; unsigned long long s_maxbytes; struct file_system_type *s_type; const struct super_operations *s_op; /* ... */ 50

unsigned long s_flags; unsigned long s_magic; struct dentry *s_root; struct rw_semaphore s_umount; struct mutex s_lock; int s_count; /* ... */ struct list_head s_inodes; struct list_head s_dirty; /* ... */ struct list_head s_instances; /* ... */ void *s_fs_info; /* ... */ };

Nesse objeto, estão definidas diversas propriedades do sistema de arquivos, dentre as quais destacam-se o tamanho máximo de um arquivo, tamanho de bloco de dados, um ponteiro para o diretório root do sistema montado e uma lista de todos os inodes do sistema. Essa estrutura também apresenta um ponteiro (s_op) para a estrutura super_ operations, a qual é composta por ponteiros de funções que devem ser de- finidas pelo sistema de arquivos para a manipulação do objeto super-bloco. Dentre essas funções, destacam-se os métodos utilizados para alocar e desalo- car um inode, preparar o sistema para ser desmontado e gerar estatísticas do sistema montado.

• inode: representa um arquivo e seus metadados. A estrutura está definida em e uma versão simplificada pode ser visualizada na Listagem 4.2.

Listagem 4.2: Estrutura inode struct inode { struct hlist_node i_hash; struct list_head i_list; struct list_head i_sb_list; struct list_head i_dentry; unsigned long i_ino; atomic_t i_count; unsigned int i_nlink; uid_t i_uid; gid_t i_gid; dev_t i_rdev; u64 i_version; loff_t i_size; /* ... */ struct timespec i_atime; struct timespec i_mtime; struct timespec i_ctime; /* ... */ umode_t i_mode; spinlock_t i_lock; struct mutex i_mutex; struct rw_semaphore i_alloc_sem; const struct inode_operations *i_op; const struct file_operations *i_fop; 51

struct super_block *i_sb; struct file_lock *i_flock; struct address_space *i_mapping; /* ... */ unsigned int i_flags; /* ... */ void *i_private; };

A maior parte dos elementos dessa estrutura é dedicada ao gerenciamento de informações do arquivo, como data de modificação, data de criação, tamanho em bytes, tamanho em blocos, o identificador do usuário e do grupo do usuário que criou o arquivo e um número que identifica o objeto unicamente no sistema de arquivos. Entretanto, o nome do arquivo não está presente nesse objeto. Além disso, o inode é colocado em diversas listas, dentre as quais destacam-se uma tabela hash para acesso rápido, uma lista por super-bloco de todos os inodes e, por fim, em uma lista que varia de acordo com o estado atual do objeto, podendo haver 3 estados principais:

– O objeto existe em memória, mas não está associado a nenhum arquivo válido; – O objeto está em memória e sendo acessado por um ou mais processos; – O objeto está em uso, com seus dados tendo sido modificados.

A estrutura também contém um ponteiro para uma estrutura que representa as operações possíveis sobre um objeto inode, chamada inode_operations. Dentre as principais operações presentes na estrutura, destacam-se os métodos para criação e remoção de diretórios, criação de arquivos especiais, criação de links simbólicos e manipulação de atributos de um arquivo.

• dentry: representa uma entrada de diretório, um único componente em um ca- minho para um arquivo. Esse objeto é definido no arquivo e pode ser visualizado na Listagem 4.3.

Listagem 4.3: Estrutura dentry struct dentry { atomic_t d_count; unsigned int d_flags; spinlock_t d_lock; struct inode *d_inode; struct hlist_node d_hash; struct dentry *d_parent; struct qstr d_name; struct list_head d_lru; union { struct list_head d_child; struct rcu_head d_rcu; } d_u; struct list_head d_subdirs; struct list_head d_alias; unsigned long d_time; struct dentry_operations *d_op; 52

struct super_block *d_sb; void *d_fsdata; #ifdef CONFIG_PROFILING struct dcookie_struct *d_cookie; #endif int d_mounted; unsigned char d_iname[DNAME_INLINE_LEN_MIN]; };

O conceito de dentry não é perceptível do ponto de vista do usuário, já que esse tipo de objeto é criado e destruído dinamicamente pelo VFS. Um exemplo de uso desse tipo de objeto é a resolução de um caminho para um arquivo, como no caso do caminho /bin/ls. Nesse caso, são criadas dentries para /, bin e ls. Esses objetos são utilizados para a procura de arquivos no sistema, já que eles garantem a validade do caminho. Vale destacar que é nesse objeto que são armazenados os nomes dos arquivos. As instâncias desses objetos formam uma rede de mapeamento da estrutura do sistema de arquivos, já que uma entrada de diretório contém ponteiros para as instâncias das entradas dos seus arquivos e também das entradas do diretório pai. Contudo, essa topologia do sistema de arquivos não é mape- ada totalmente, já que só permanecem na memória as entradas dos arquivos mais utilizados, principalmente devido ao tamanho da memória RAM e ao desempenho do sistema. Esse objeto possui 3 estados possíveis:

– Usado: o objeto aponta para um inode válido, apontado pelo campo d_inode e o seu contador (d_count) é positivo, indicando que o arquivo está em uso; – Não usado: o objeto aponta para um inode válido, mas ele não está em uso pelo VFS no momento, ou seja, o valor do campo d_count é zero. O objeto é mantido em memória por algum tempo, caso o arquivo venha a ser utilizado, a fim de aumentar o desempenho do sistema. – Negativo: significa que o objeto não está associado a nenhum inode, ou seja, o campo d_inode possui valor NULL.

Cada requisição ao sistema de arquivos leva o VFS a criar um objeto dentry, a fim de armazenar os resultados da requisição. Após o objeto ser criado e inicializado, o kernel o coloca no directory entry cache, para possibilitar o rápido acesso aos objetos em memória. Esse cache consiste em três partes:

– Uma tabela hash; – Lista de dentries que referenciam o mesmo objeto inode, acessadas através do campo i_dentry desse objeto inode. Esse campo é imple- mentado como uma lista, pois um arquivo pode possuir vários links e, consequentemente, várias dentries; – Uma lista LRU de dentries não usadas e negativas. 53

A estrutura também possui um ponteiro para um objeto que representa as ope- rações possíveis sobre uma entrada de diretório, a estrutura dentry_operations. As principais operações são: determinação da validade de uma entrada de di- retório, criação de um valor hash do objeto, comparação do objeto com outro objeto e a remoção do objeto.

• file: representa um arquivo aberto, associado a um processo. O objeto é criado dinamicamente em resposta a chamada de sistema open e destruído na chamada close. A estrutura file está definida no arquivo e é mostrada na Listagem 4.4.

Listagem 4.4: Estrutura file struct file { union { struct list_head fu_list; struct rcu_head fu_rcuhead; } f_u; struct path f_path; #define f_dentry f_path.dentry #define f_vfsmnt f_path.mnt const struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; struct fown_struct f_owner; unsigned int f_uid, f_gid; struct file_ra_state f_ra; u64 f_version; #ifdef CONFIG_SECURITY void *f_security; #endif void *private_data; #ifdef CONFIG_EPOLL struct list_head f_ep_links; spinlock_t f_ep_lock; struct address_space *f_mapping; #ifdef CONFIG_DEBUG_WRITECOUNT unsigned long f_mnt_write_state; #endif };

O objeto representa a visão do processo sobre o arquivo, podendo haver di- versos objetos para um mesmo arquivo em um mesmo momento, se vários processos estiverem com o arquivo aberto. Ele contém informações do modo de abertura do arquivo, qual usuário invocou a chamada open e ponteiros para outras estruturas, como para a estrutura dentry do arquivo e para a estrutura file_operations, que contém ponteiros para funções de manipulação do ar- quivo. As principais funções são de leitura e escrita síncrona e assíncrona do arquivo, de mapeamento de memória e de sincronização dos dados do arquivo.

O VFS possui também outros objetos auxiliares, como a estrutura vfsmount, definida em , que contém informações sobre a montagem do sis- 54 tema de arquivos, como o ponto de montagem e as opções passadas no momento da montagem. Por fim, há a estrutura file_system_type, que é o ponto de entrada para o sistema de arquivos no momento da montagem. Essa estrutura possui o nome do sistema de arquivos e ponteiros paras as funções que alocam (get_sb) e desalocam (kill_sb) o objeto super-bloco. Ela está definida em e uma versão simplificada é mostrada na Listagem 4.5. Listagem 4.5: Estrutura file_system_type struct file_system_type { const char *name; int fs_flags; int (*get_sb) (struct file_system_type *, int, const char *, void *, struct vfsmount *); void (*kill_sb) (struct super_block *); struct module *owner; struct file_system_type *next; struct list_head fs_supers; /* ... */ };

As relações entre alguns dos objetos do VFS são apresentadas na Figura 4.2.

Figura 4.2: Relações entre os objetos do VFS (fonte: Mauerer (2008))

Outras estruturas, presentes na estrutura de cada processo (task_struct), estão relacionadas ao sistema de arquivos: a estrutura files_struct, definida no arquivo 55

, contém todas as informações dos arquivos abertos pelo processo, e a estrutura fs_struct, definida em , contém informações do sistema de arquivos relacionadas ao processo.

4.1.1.1 Mapeamento dos Metadados dos Arquivos

Para obter as informações dos arquivos presentes no compartilhamento montado, o LP2PFS envia as mensagens de requisição de lista de arquivos (llist) e requisição de atualização de arquivos no compartilhamento (lgetu). O daemon LP2P responde com as mensagens de lista de arquivos no compartilhamento (lsendl) e atualização da lista de arquivos no compartilhamento (lsendu), respectivamente. Essas mensagens de resposta recebidas pelo sistema de arquivos possuem as seguintes informações sobre cada arquivo:

• Identificador (id) no sistema LP2P;

• Nome do arquivo;

• Tamanho do arquivo;

• Data de modificação do arquivo.

Com essas informações, o sistema de arquivos irá criar, para cada arquivo, um objeto inode e um objeto dentry. O objeto inode será alocado com um identificador único de inode dentro do sis- tema, armazenando o tamanho do arquivo e a sua data de modificação. Além disso, no campo i_private do objeto, o qual é utilizado para uso específico do sistema de arquivos, é colocado o identificador do arquivo no sistema LP2P. É importante ressaltar que o identificador do inode e o identificador do arquivo no sistema LP2P são campos diferentes, não possuindo nenhuma relação entre si. Além disso, o objeto é colocado na lista de inodes do sistema, na tabela hash para acesso rápido e na lista que indica que o objeto está em memória. O objeto dentry, por sua vez, é criado e associado ao inode do arquivo, ar- mazenando o nome desse arquivo. Esse objeto é colocado em uma lista hash para acesso rápido e em uma lista de dentries que referenciam o mesmo inode.

4.1.2 Page Cache

O kernel utiliza parte da memória RAM para assegurar que os dados mais aces- sados e mais importantes estejam disponíveis quando houver uma requisição por eles, a fim de aumentar a eficiência do sistema, minimizando operações de entrada e saída de dados (LOVE, 2005b). O page cache manipula páginas da memória, dividindo a memória RAM e a me- mória virtual em pequenos segmentos, denominados páginas. Assim, ele é utilizado para operações que estejam baseadas em unidades de páginas de memória. Além disso, a camada do VFS utiliza uma página de memória como unidade básica de 56

Figura 4.3: Relação entre as estruturas do VFS e do Page Cache (fonte: Mauerer (2008)) leitura e escrita em memória. É possível visualizar a relação entre o Virtual File System e o Page Cache na Figura 4.3. A base desse sistema é a estrutura address_space, que está associada a um inode e mantém todas as informações dos dados que estão na memória referentes àquele arquivo. Assim, essa estrutura estabelece a ligação entre os dados armazena- dos na memória e o dispositivo utilizado para se obter tais dados. Essa estrutura é definida no arquivo e uma parte dela é mostrada na Listagem 4.6.

Listagem 4.6: Estrutura address_space struct address_space { struct inode *host; struct radix_tree_root page_tree; spinlock_t tree_lock; unsigned int i_mmap_writable; struct prio_tree_root i_mmap; struct list_head i_mmap_nonlinear; spinlock_t i_mmap_lock; unsigned int truncate_count unsigned long nrpages; pgoff_t writeback_index; const struct address_space_operations *a_ops; unsigned long flags; struct backing_dev_info *backing_dev_info; spinlock_t private_lock; struct list_head private_list; struct address_space *assoc_mapping; } __attribute__((aligned(sizeof(long))));

Essa estrutura armazena diversas informações, dentre as quais as mais importan- tes são um ponteiro para o inode do arquivo correspondente, o número de páginas de dados armazenados na memória, a árvore em que estão armazenado os dados em memória e uma estrutura que representa o meio físico em que os dados estão armazenados, denominada estrutura backing_dev_info. 57

Além disso, a estrutura apresenta um ponteiro para uma estrutura que define funções para a manipulação de páginas, dentre as quais as mais relevantes são as funções readpage e readpages, cujo protótipo é apresentado na Listagem 4.7. Essas funções são invocadas para a leitura de uma página e de várias páginas por vez, respectivamente. Atualmente, o kernel chama, em situações normais, somente a função readpages, aumentando o desempenho da busca por dados. Entretanto, em situações de exceção, como, por exemplo, ao ser retornado um erro pela função readpages, o kernel faz uma segunda tentativa de leitura dos dados do arquivo através da função readpage. Listagem 4.7: Funções de leitura de páginas int (*readpage)(struct file *, struct page *); int (*readpages)(struct file *filp, struct address_space *mapping, struct list_head *pages, unsigned nr_pages);

Vale ressaltar que na estrutura backing_dev_info há um campo denominado ra_pages, no qual é definido o número médio de páginas lidas antecipadamente (readahead), com o objetivo de aumentar a eficiência do sistema. Para o gerenciamento das páginas de dados em memória, é utilizada uma árvore radix. Essa árvore não é balanceada, ou seja, cada galho da árvore pode possuir qualquer profundidade. As folhas da árvore são definidas como ponteiros para void, podendo ser instâncias de qualquer estrutura. A raiz da árvore é definida pela estrutura radix_tree_root, que contém a altura máxima da árvore e um ponteiro para o primeiro nodo da árvore. Os nodos são representados pela estrutura radix_tree_node, a qual possui um ponteiro para um array (denominado slots) de filhos do nodo, cujo tamanho é definido pelo resultado da operação 2RADIX_TREE_MAP_SHIFT. Cada nodo possui também um vetor de tags, sendo possível um número RADIX_TREE_MAX_TAGS de tags diferentes para cada filho do nodo. As folhas da árvore, nesse caso representadas pela estrutura page, contém os da- dos dos arquivos. O formato da estrutura da página é independente de arquitetura, somente a quantidade de dados armazenadas em cada página varia de arquitetura para arquitetura, sendo definida pela constante PAGE_CACHE_SIZE. Além disso, a estrutura apresenta um campo de flags, que representam os atributos da página. Um exemplo de árvore radix é apresentado na Figura 4.4.

4.1.2.1 Mapeamento dos Dados dos Arquivos

Para obter os dados do arquivo requisitado pelo VFS, o LP2PFS envia a men- sagens de requisição de dados de um determinado arquivo (lget). O daemon LP2P responde com uma mensagem contendo o intervalo de dados requisitado do arquivo (lsendf). A mensagem de resposta recebida pelo sistema de arquivos possui os se- guintes dados:

• Identificador (id) do arquivo no sistema LP2P; • Posição inicial dos dados no arquivo; 58

Figura 4.4: Exemplo de uma árvore radix (fonte: Mauerer (2008))

• Tamanho do conjunto de dados; • Os dados do arquivo.

O VFS irá requisitar esses dados através da chamada das funções readpage e readpages. Essas funções são chamadas para a leitura de uma página e de várias pá- ginas de dados, respectivamente. O número de páginas lidas pela função readpages é definido pelo VFS, levando-se em consideração o valor de páginas de readahead definido no campo ra_pages da estrutura backing_dev_info. Essas funções irão se comunicar com o daemon LP2P, recebendo a mensagem com os dados. Assim, os dados recebidos são armazenados em páginas da memória, PAGE_CACHE_ SIZE bytes por página. Além disso, as páginas que possuem dados são adicionadas na árvore radix da estrutura address_space do arquivo. Os dados são sempre lidos em quantidades múltiplas do valor definido na constante PAGE_CACHE_SIZE, exceto quando o final do arquivo for atingido, quando a parte da página que não for pre- enchida com dados será completada com bytes de valor zero (0).

4.2 Otimizações

O kernel do Linux possui alguns mecanismos para otimizar certas partes do có- digo, além certos detalhes que o programador deve ter atenção para não desperdiçar memória. O compilador GCC define um conjunto de funções que permitem manipular a execução de aplicativos de um modo que não seria possível sem recorrer a código Assembly. Cada arquitetura possui uma série dessas funções, denominadas funções builtin, entretanto algumas delas são comuns a todas as arquiteturas, sendo duas delas utilizadas pelo kernel. A função mais importante é a __builtin_expect, cujo protótipo pode ser vi- sualizado na Listagem 4.8. Ela influencia as predições do processador, ajudando o compilador a otimizá-las. O primeiro argumento passado é a condição a ser avaliada 59 e o segundo argumento é o resultado mais frequente da condição: 1 caso ela seja frequentemente verdadeira ou 0 caso contrário. Listagem 4.8: Função __builtin_expect __builtin_expect(long expression, long c);

Dessa maneira, são definidas duas macros no arquivo do código fonte do kernel: likely, para condições que serão verdadeiras na maioria das vezes, e unlikely, para condições que serão falsas na maioria das vezes. Essas macros podem ser visualizadas na Listagem 4.9. Listagem 4.9: Macros likely e unlikely # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0)

Uma curiosidade é o fato da dupla negação na definição dessas macros, que ocorre devido a dois fatores: em primeiro lugar, isso permite que macros sejam utilizadas com ponteiros que são implicitamente convertidos em valores-verdade e, por fim, isso permite que valores-verdade maiores que zero sejam convertidos para o valor um, que é o esperado pela função __builtin_expect (MAUERER, 2008). Outro recurso oferecido é a possibilidade da declaração de funções inline. Ao adicionar a palavra-chave inline na declaração da função, o compilador copia o código dessa função para a posição na qual a função é chamada, exatamente como na utilização de uma macro. A grande vantagem de utilização desse método é que a verificação de tipos de dados é feita em tempo de compilação, como em chamadas normais de funções. O uso dessa técnica é importante, pois, apesar das chamadas de funções em C acrescentarem pouco overhead, em seções críticas e trechos de código frequente- mente chamados, é importante reduzir o tempo de processamento o máximo possível. Entretanto, essa técnica apresenta desvantagens, já que ao tornar inline funções frequentemente utilizadas, o código binário gerado terá um tamanho muito maior, o que pode gerar problemas em certos sistemas (LOVE, 2005b). Também é importante verificar questões de alinhamento de bytes, já que qual- quer dado elementar deve ser armazenado na memória em endereços divisíveis pelo tamanho (largura) do seu tipo de dado. Na maioria das operações, isso não causa problemas, pois ao alocar-se memória, o kernel assegura que os bytes estejam ali- nhados. Entretanto, isso pode levar a um aumento de utilização da memória, sem o conhecimento do programador (LOVE, 2005b; MAUERER, 2008). Por exemplo, ao ser definida uma estrutura, pode ser necessário que o compilador acrescente espaços na memória entre os campos da estrutura, denominados padding, devido à necessidade de alinhamento dos dados. Um caso possível é mostrado na Figura 4.5, cuja estrutura possui os seguintes campos: um ponteiro para string (“ptr1”), um caractere (“c”) e outro ponteiro para string (“ptr2”), na qual o kernel acrescentará um espaço livre entre o segundo campo e o terceiro, a fim de manter o ponteiro “ptr2” alinhado. Apesar disso, o kernel também disponibiliza métodos para o acesso de dados não alinhados em memória através das funções get_unaligned, utilizada para ler 60

Figura 4.5: Estrutura com alinhamento de bytes (fonte: Mauerer (2008)) um ponteiro não alinhado, e put_unaligned, que escreve um valor em um ponteiro não alinhado.

4.3 Comunicação com o LP2P

A comunicação com o LP2P é feita apenas localmente, não sendo possível ao sistema de arquivos comunicar-se com os daemons LP2P de outros peers da rede, devido a diversos fatores, dentre os quais destacam-se a confidencialidade e a inte- gridade dos dados. A comunicação local é feita através do protocolo da camada de transporte TCP. Essa comunicação ocorre nas seguintes situações:

• No momento da montagem do sistema de arquivos, a fim de buscar a lista de arquivos do compartilhamento solicitado, ocasionando o envio de uma mensa- gem llist e o consequente recebimento de uma mensagem lsendl. Essa situação pode ser visualizada na Figura 4.6, que exibe também as chamadas de funções do VFS até o sistema de arquivos LP2PFS;

Figura 4.6: Montagem do sistema LP2PFS

• Quando o usuário solicitar a visualização dos arquivos presentes no comparti- lhamento montado, acarretando o envio de uma mensagem lgetu para requisi- tar as informações atualizadas dos arquivos no compartilhamento e o recebi- 61

mento de uma mensagem lsendu, com as informações dos arquivos que foram alterados desde a última comunicação com o LP2P; • Ao ser aberto um arquivo, ocasionando o envio de uma mensagem de requisição dos dados do arquivo (lget) e o recebimento de uma mensagem com os dados do arquivo requisitado (lsendf). Essa situação pode ser visualizada na Figura 4.7;

Figura 4.7: Leitura do conteúdo de um arquivo

• Em determinados momentos, quando o kernel chamar a função de leitura as- síncrona dos dados do arquivo, que é invisível ao usuário. Essa chamada ocorre devido a função de readahead de dados presente no kernel, aumentando a efi- ciência do sistema do ponto de vista do usuário; • O sistema possui uma thread com o objetivo de atualizar os arquivos presentes no compartilhamento. Essa thread requisita atualizações ao daemon LP2P em intervalos regulares de tempo. O intervalo de tempo entre as atualizações é definido na constante UPDATE_INTERVAL.

4.4 Notificação de Eventos

O kernel do Linux necessita prover às aplicações um método rápido e eficiente de sinalização para a ocorrência de modificações em arquivos de um determinado sistema. Para esse propósito, há duas interfaces disponíveis: dnotify e . A interface dnotify (directory notify) foi introduzida na versão 2.4 do kernel para reportar às aplicações modificações em diretórios. Já a interface inotify, que significa inode notify e foi introduzida na versão 2.6.13 do kernel, atua como um subsistema, possuindo as mesmas funções da interface dnotify, exceto pelo fato dessa interface avisar às aplicações as mudanças ocorridas em qualquer arquivo do sistema, não somente em diretórios. Desse modo, esse subsistema permite às aplicações requisitarem o monitora- mento de um conjunto de arquivos para uma lista de eventos. Ao ocorrer um evento, 62 a aplicação é notificada. Segundo Love (2005a), as vantagens desse subsistema em relação a interface dnotify são:

• Com a interface dnotify só é possível monitorar diretórios. Por outro lado, inotify é baseado em inodes, possibilitando o monitoramento de eventos para qualquer tipo de arquivo; • dnotify necessita manter um descritor de arquivo aberto para o diretório sendo monitorado. Isso implica em problemas na desmontagem do sistema de arqui- vos ao qual o diretório pertence e em problemas relacionados a quantidade de descritores de arquivos abertos quando for necessário monitorar diversos dire- tórios. Já a interface inotify não necessita de um descritor de arquivo aberto para o seu monitoramento; • dnotify envia sinais às aplicações. Com inotify, a comunicação com a aplicação é feita através de um único descritor de arquivo.

Apesar dessas vantagens, essas duas interfaces apresentam um problema em comum: são interfaces complexas de serem implementadas pelos sistemas de arquivos e pouco otimizadas. Por isso, foi desenvolvida a interface fsnotify, que implementa as duas interfaces de notificação de eventos de forma otimizada, aumentando o desempenho dos sistemas de arquivos (LINUX KERNEL ORGANIZATION, 2010). Além disso, a interface fsnotify foi desenvolvida com um objetivo secundário: servir como camada intermediária para o novo subsistema de notificação do kernel que está sendo desenvolvido, chamado fanotify, cujo objetivo é integrar todos os sistemas de notificação em um só e servir como scanner de código malicioso em sistemas Linux (PARIS, 2009; CORBET, 2009). Dessa forma, o sistema LP2PFS implementa a interface fsnotify para a noti- ficação de eventos para as aplicações, possibilitando às aplicações a utilização de qualquer uma das duas interfaces de notificação de eventos. Assim, as aplicações podem requisitar o conjunto de arquivos a serem monitorados, bem como a lista de eventos. Isso também já proporciona ao sistema LP2PFS ser totalmente compatível com o novo sistema fanotify.

4.5 Arquivo no Sistema Procfs

O sistema de arquivos virtual Procfs, localizado em /proc, fornece um meca- nismo para os módulos do kernel enviarem informações para aplicações em espaço de usuário e para os próprios usuários (SALZMAN, 2009). Há duas APIs disponíveis no kernel para a implementação de um arquivo nesse sistema:

• A API padrão, que pode ser facilmente implementada. Entretanto, ela é útil somente para lidar com pequenas quantidades de dados, que podem ser de, no máximo, PAGE_CACHE_SIZE bytes. Ela possibilita a criação de arquivos e diretórios, sendo necessário especificar somente duas funções, uma para a leitura e outra para a escrita de dados no arquivo; 63

• A API seq_file, desenvolvida para facilitar a leitura dos arquivos presentes nesse sistema. Assim, é possível ler grandes quantidades de bytes, não havendo nenhuma restrição sobre a quantidade de bytes a serem lidos. Também é pos- sível realizar operações de posicionamento do ponteiro dentro de um arquivo (seek). Entretanto, com essa API, as aplicações não podem realizar a operação de escrita no arquivo virtual (LINUX KERNEL ORGANIZATION, 2010).

Portanto, o módulo LP2PFS implementa a interface disponibilizada pela API seq_file para o acesso de informações do sistema de arquivos. O arquivo virtual está localizado em fs/lp2pfs/. Nesse arquivo, são disponibilizados os seguintes dados: local da montagem do sistema, nome do com- partilhamento e as informações de todos os arquivos presentes no compartilhamento montado, as quais são: número de identificação do inode no sistema de arquivos, ID do arquivo no sistema LP2P e nome e tamanho em bytes do arquivo.

4.6 Opções de Montagem

O sistema LP2PFS apresenta as seguintes opções de montagem:

• uid=: representa o usuário que será o proprietário dos arquivos presentes no compartilhamento. Por padrão, se essa opção não for especificada, será utilizado o usuário do processo que chamou o comando de montagem do sistema de arquivos;

• gid=: representa o grupo de usuários que será o proprietário dos arquivos presentes no compartilhamento. Caso essa opção não seja especifi- cada, o sistema utilizará o grupo principal do usuário que chamou o comando de montagem do sistema;

• port=: especifica a porta que será utilizada para realizar a comu- nicação com o LP2P. Caso essa opção não seja especificada, o sistema utilizará a porta padrão definida na constante LOCAL_PORT. 64

5 VALIDAÇÃO E RESULTADOS EXPERIMENTAIS

A fim de validar o LP2PFS, foram realizados diversos testes, desde testes de compatibilidade com várias versões do kernel do Linux, até testes de desempenho do sistema de arquivos em conjunto com o sistema LP2P. A distribuição1 Linux escolhida para a realização dos testes foi a distribuição Debian estável. Essa escolha deve-se, principalmente, a dois fatores: a estabilidade do sistema e ao kernel utilizado. A versão estável dessa distribuição é robusta, possuindo um bom sistema de monitoramento e correção de bugs, ao contrário de outras distribuições, que apre- sentam diversos erros, principalmente para os desenvolvedores que necessitam de um sistema estável. O segundo fator está relacionado ao código fonte do kernel utilizado por essa distribuição. Enquanto a maioria das distribuições aplica diversos patches2 ao código do kernel, visando customizá-lo para os seus interesses, a dis- tribuição Debian aplica somente patches de segurança, possibilitando a esse kernel ser similar a versão original disponibilizada nos repositórios do kernel do Linux, em http://www.kernel.org. Esse capítulo está organizado da seguinte maneira: na Seção 5.1 é descrito a compatibilidade do módulo do sistema implementado com as diferentes versões do kernel do Linux, com o objetivo de não restringir o módulo a uma única versão do kernel. Já na Seção 5.2 são mostrados os testes de disponibilidade dos arquivos presentes no sistema, que é o principal objetivo do projeto. Por fim, na Seção 5.3, são apresentados os resultados de testes do sistema de arquivos em conjunto com o sistema LP2P.

5.1 Versões do Kernel

A interface de programação do kernel do Linux sofre diversas alterações a cada nova versão do kernel disponibilizada. As principais alterações no VFS são em

1Uma distribuição é uma versão do Linux feita por uma companhia, organização ou por um indivíduo. Cada distribuição possui o seu conjunto de ferramentas e aplicações, entretanto todas utilizam como base o kernel do Linux (LINUX ONLINE INC., 2010). 2Um patch é um arquivo texto que contém as diferenças entre o código original e o novo código, além de informações adicionais sobre nomes de arquivos e números de linhas. Para aplicar um patch ao código fonte do kernel, o programa patch é utilizado (LINUX KERNEL ORGANIZATION, 2010). 65 seus objetos que representam as operações possíveis de serem realizadas sobre outro objeto, com a adição e remoção de algumas funções desses objetos. Além disso, há várias alterações nos protótipos de diversas funções essenciais para o programador em espaço de kernel. Devido a esses fatores, foi necessário comprovar o funcionamento do sistema de arquivos desenvolvido em diversas versões do kernel do Linux. Desse modo, o módulo LP2PFS foi compilado e executado com êxito no sistema Debian nas seguintes versões do kernel: 2.6.25, 2.6.26, 2.6.27, 2.6.28, 2.6.29, 2.6.30, 2.6.31, 2.6.32, 2.6.33, 2.6.34 e 2.6.35.

5.2 Disponibilização dos Arquivos

A disponibilização dos arquivos presentes nos compartilhamentos do sistema LP2P é o principal objetivo do trabalho de conclusão. Esse objetivo foi alcançado com êxito, como é possível verificar na Figura 5.1, na qual é mostrado a sequen- cia de comandos desde o carregamento do módulo LP2PFS, a sua montagem e a visualização dos arquivos no compartilhamento montado através do comando ls.

Figura 5.1: Compartilhamento de pacotes Debian

Além disso, como é possível visualizar na Figura 5.1, o usuário pode acessar os arquivos sem possuir o conhecimento da sua localidade física, o que garante ao sistema o objetivo de transparência de localização dos arquivos, possibilitando ao usuário manipulá-los como se eles estivessem armazenados localmente. Entretanto, nesse cenário de distribuição de arquivos entre diversas máquinas de uma rede local (LAN), torna-se necessário verificar a integridade dos dados recebidos da rede. Por esse motivo, no código fonte do sistema, na função de recebimento de dados, é feita uma verificação sobre a quantidade de dados esperada e a quantidade de dados recebida. Vale ressaltar que não é necessário verificar a integridades de cada byte recebido, visto que a conexão com o daemon LP2P é feita através do protocolo da camada de transporte TCP, que garante essa integridade de dados. 66

Apesar disso, foram realizados testes de integridade dos dados dos arquivos presentes no compartilhamento com o algoritmo MD53. Desse modo, a integridade dos dados dos arquivos presentes no compartilhamento foi comprovada.

5.3 Testes no Sistema LP2P

Foram realizados diversos testes de desempenho do sistema de arquivos LP2PFS em conjunto com o sistema LP2P. O protótipo do sistema LP2P utilizado para os testes foi implementado na linguagem Java. Desse modo, o sistema de arquivos LP2PFS atuou como um front-end entre usuário e a pilha de protocolos LP2P. Os testes foram executados no Laboratório de Uso Geral do PIPCA, com uma rede local formada por 16 estações Linux (distribuição Debian Lenny com kernel 2.6.26-2-686), com 4 GB de memória RAM, 250 GB de HD e processadores Intel Core 2 Duo de 1.8 GHZ. A estrutura de rede conta com tecnologia FastEthernet e switches 3COM 10/100/1000, viabilizando a conexão dos peers a uma velocidade de 100 Mbps. A versão do kernel do Linux utilizada para os testes foi a 2.6.26-2-686 devido ao fato dessa versão ser a versão oficial do sistema estável da distribuição Debian, possibilitando maior confiabilidade ao sistema e aos testes realizados. Primeiramente, foi implementada uma aplicação do tipo PingPong entre dois computadores da rede, a fim de avaliar o desempenho puro do TCP nessa rede. Essa aplicação foi implementada na linguagem Java, já que o processo daemon do LP2P está sendo desenvolvido nessa linguagem de programação. A vazão de dados de acordo com o tamanho das mensagens é mostrada na Figura 5.2, na qual é possível verificar que a vazão de cada peer atinge um valor próximo do ideal com mensagens cujo tamanho se aproxima de 1 megabyte, e na Figura 5.3 é mostrada a latência de comunicação com o TCP.

12

11

10

9

8

7

6 Vazão (MB/s)

5

4

3

2 1k 2k 4k 8k 16k 32k 64k 128k 256k 512k 1M Tam. msg (bytes, log scale)

Figura 5.2: Vazão de dados usando TCP

3Message-Digest algorithm 5 (MD5) (RIVEST, 1992) é um algoritmo de hash que converte um conjunto arbitrário de bytes em uma saída de 128 bits. É utilizado para a verificação de integridade de dados. 67

1800

1600

1400

1200

1000

800 Latência (us) 600

400

200

0 0 5000 10000 15000 20000 25000 30000 Tam. msg (bytes)

Figura 5.3: Latência de comunicação usando TCP

Após o teste do PingPong, foram executados os testes com os sistemas LP2PFS e LP2P com 3 tamanhos diferentes de arquivos: 1 megabyte, 8 megabytes e 16 me- gabytes. Como o objetivo principal desses testes foi medir o desempenho e a eficiên- cia somente do sistema de arquivos, foram utilizadas diferentes configurações para o sistema de arquivos. Durante o desenvolvimento do módulo LP2PFS, foi consta- tado que o desempenho de um sistema de arquivos, nesse caso especificamente de um sistema de arquivos que requisita dados através de uma rede de alta velocidade, é dependente do número de páginas de dados configurado para o kernel requisitar a leitura antecipada desses dados, que é chamado de readahead. Como mencionado no Capítulo 4, o readahead do sistema é definido no campo ra_pages da estrutura backing_dev_info. Esse valor é utilizado pelo kernel do Li- nux para definir a quantidade de páginas requisitadas para leitura através da função readpages da estrutura address_space_operations. Para calcular a quantidade de páginas a serem requisitadas, o kernel utiliza o número de páginas de readahead definido pelo sistema de arquivos, além de considerar o número total de páginas livres que o sistema operacional possui e o tamanho do arquivo. Devido a necessidade de comunicação em rede, o valor do readahead torna- se essencial para garantir um bom tempo de resposta às requisições do usuário. Entretanto, esse valor não pode ser muito elevado, pois, quanto maior for o valor, maior será a utilização de memória do sistema. Os testes foram feitos da seguinte maneira: cada peer presente na rede lê todos os arquivos de um mesmo tamanho presentes no compartilhamento, entretanto em ordens diferentes. Por exemplo, o peer1 da rede lê os arquivos do peer2 até o peer16 e, por fim, lê o seu próprio arquivo, já o peer2 lê o arquivo do peer3 até o peer16, lê do peer1 e, por último, lê o seu próprio arquivo, e assim sucessivamente. Dessa forma, em qualquer instante de tempo do teste, cada peer está lendo um arquivo e enviando outro arquivo para outro peer. Além disso, para cada tamanho de arquivo, o sistema de arquivos foi executado com os seguintes valores de readahead (em número de páginas de memória): 0, 25, 50, 75, 100, 125, 150, 175 e 200. Todos os resultados dos testes mostrados são médias de, no mínimo, 30 repetições do experimento. 68

Os tempos de leitura de arquivos de 1 megabyte e a vazão individual obtida por cada peer da rede são mostrados na Tabela 5.1 e na Figura 5.4, que mostra o gráfico da vazão de cada peer para os diferentes valores de readahead utilizados. Nesse caso, mesmo em arquivos de tamanho reduzido, é possível observar o quão importante é o papel desempenhado pela leitura dos dados de um conjunto grande de páginas por vez, representada pelo readahead do sistema. Nesse teste, a vazão do peer não sofreu grandes alterações a partir do valor de 100 páginas de readahead, principalmente devido ao tamanho do arquivo.

Tabela 5.1: Teste do sistema com arquivos de 1 MB

Readahead (em Arquivo de 1 MB páginas) Tempo Vazão do Peer 0 14.838s 1.078 MB/s 25 2.936s 5.450 MB/s 50 2.686s 5.956 MB/s 75 2.590s 6.177 MB/s 100 2.586s 6.187 MB/s 125 2.622s 6.103 MB/s 150 2.623s 6.099 MB/s 175 2.574s 6.217 MB/s 200 2.543s 6.291 MB/s

Arquivos de 1 MB 10.00

9.00

8.00

7.00

6.00

5.00

4.00

Vazão do peer (em MB/s) 3.00

2.00

1.00

0.00 0 25 50 75 100 125 150 175 200 Readahead (em número de páginas)

Figura 5.4: Vazão individual de cada peer para arquivos de 1 MB

Com arquivos de 8 megabytes, os resultados são mostrados na Tabela 5.2 e na Figura 5.5. É possível, novamente, perceber a diferença de desempenho dos peers (mostrada pela vazão individual de cada peer) quando não há readahead definido e quando há um valor definido, mesmo que baixo. É importante ressaltar que nos casos em que não há readahead definido (valor 0), o kernel requisitará ao sistema 69 de arquivos sempre a leitura de uma página de dados por vez, aumentando enorme- mente o overhead de comunicação pela rede do sistema LP2P. Novamente, o sistema apresentou os melhores resultados com o readahead com valores de 175 e 200 páginas de memória.

Tabela 5.2: Teste do sistema com arquivos de 8 MB

Readahead (em Arquivo de 8 MB páginas) Tempo Vazão do Peer 0 116.523s 1.098 MB/s 25 19.923s 6.425 MB/s 50 17.691 7.235 MB/s 75 17.266s 7.413 MB/s 100 16.594s 7.714 MB/s 125 16.183s 7.910 MB/s 150 16.108s 7.947 MB/s 175 15.979s 8.010 MB/s 200 15.178s 8.041 MB/s

10.00

9.00

8.00

7.00

6.00

5.00

4.00

Vazão do peer (em MB/s) 3.00

2.00

1.00

0.00 0 25 50 75 100 125 150 175 200 Readahead (em número de páginas)

Figura 5.5: Vazão individual de cada peer para arquivos de 8 MB

Os resultados dos testes com arquivos de 16 megabytes são mostrados na Ta- bela 5.3 e na Figura 5.6. Nesses testes, foi atingido o maior valor de vazão individual de um peer: 8.320 MB/s com 200 páginas de readahead. Com os resultados dos 3 testes, é possível comprovar que o o readahead afeta diretamente o desempenho do sistema de arquivos, já que com valores maiores, a vazão do peer aumenta. En- tretanto, a partir de 150 páginas de readahead, a vazão do peer não sofre grandes variações. Além disso, a vazão também é influenciada pelo tamanho do arquivo sendo requisitado, já que em arquivos de 1 megabyte, a vazão máxima foi de 6.291 MB/s, chegando a 8.041 MB/s com arquivos de 8 megabytes e atingindo o máximo de 8.320 MB/s com arquivos de 16 megabytes. 70

Tabela 5.3: Teste do sistema com arquivos de 16 MB

Readahead (em Arquivo de 16 MB páginas) Tempo Taxa (em MB/s) 0 233.858s 1.095 MB/s 25 39.378s 6.501 MB/s 50 34.867s 7.342 MB/s 75 33.796s 7.575 MB/s 100 32.591s 7.855 MB/s 125 31.580s 8.106 MB/s 150 31.707s 8.074 MB/s 175 31.128s 8.224 MB/s 200 30.769s 8.320 MB/s

10.00

9.00

8.00

7.00

6.00

5.00

4.00

Vazão do peer (em MB/s) 3.00

2.00

1.00

0.00 0 25 50 75 100 125 150 175 200 Readahead (em número de páginas)

Figura 5.6: Vazão individual de cada peer para arquivos de 16 MB

Não foram mostrados testes para arquivos maiores que 16 megabytes, devido ao fato de, com arquivos de 16 megabytes, já ser possível saturar a rede com dados, visto que com arquivos desse tamanho e com um alto valor de readahead do sistema de arquivos (150 páginas ou mais), o kernel já requisita um grande número de dados para leitura antecipada. Com isso, o sistema de arquivos requisitará, na maioria das vezes, quantidades de dados que se aproximam do valor de 1 megabyte, com o qual o TCP atinge um valor de vazão de dados próximo do ideal, como pode ser comprovado pela Figura 5.2. Desse modo, com arquivos maiores, o desempenho individual de cada peer permaneceu entre 8 MB/s e 8.5 MB/s, para valores de readahead maiores que 150 páginas de memória. Na Tabela 5.4 e na Figura 5.7 são mostrados os valores da vazão agregada4

4Vazão agregada é o somatório da vazão de dados de todos os peers do sistema em um deter- minado momento. 71 do sistema para os 3 testes realizados, para cada um dos valores de readahead. É possível verificar facilmente que a medida que o valor de readahead aumentou, a vazão agregada também aumentou, além de aumentar também a medida que aumenta o tamanho do arquivo utilizado nos testes.

Tabela 5.4: Vazão agregada do sistema

Readahead (em Vazão agregada (VAG) do sistema páginas) Arquivo de 1 MB Arquivo de 8 MB Arquivo de 16 MB 0 18.332 MB/s 18.674 MB/s 18.610 MB/s 25 92.655 MB/s 109.222 MB/s 110.519 MB/s 50 101.252 MB/s 123.001 MB/s 124.818 MB/s 75 105.006 MB/s 126.025 MB/s 128.774 MB/s 100 105.186 MB/s 131.132 MB/s 133.532 MB/s 125 103.752 MB/s 134.464 MB/s 137.810 MB/s 150 103.691 MB/s 135.092 MB/s 137.258 MB/s 175 105.687 MB/s 136.179 MB/s 139.810 MB/s 200 106.950 MB/s 136.703 MB/s 141.442 MB/s

170 Arquivos de 1 MB 160 Arquivos de 8 MB Arquivos de 16 MB 150 140 130 120 110 100 90 80 70 60 50 40 Vazão agregada do sistema (em MB/s) 30 20 10 0 0 25 50 75 100 125 150 175 200 Readahead (em número de páginas)

Figura 5.7: Vazão agregada do sistema

Entretanto, nos testes do sistema com 16 peers, não foi possível atingir uma vazão individual de cada peer acima de 10 MB/s, o que seria o esperado para um sistema que utiliza conexão de rede de 100 Mbps. Dessa forma, foi realizado outro teste no sistema de arquivos LP2PFS. Com o objetivo de comprovar o real desempenho do LP2PFS e que o resultado da vazão do sistema deve-se ao fato do daemon LP2P estar em fase inicial de desenvolvimento, foi feito um teste que consiste em ler um arquivo localizado localmente, não utilizando a conexão do LP2P entre hosts remotos. 72

O arquivo utilizado para o teste possui o tamanho de 200 megabytes. Foi utili- zado um arquivo de tamanho maior para esse teste devido ao fato da leitura local ser muito rápida, não havendo como obter valores precisos com os arquivos utilizados nos testes anteriores. Dessa maneira, a leitura local do arquivo a partir do sistema de arquivos LP2PFS segue os seguintes passos:

• Para cada requisição de dados que o LP2PFS envia ao LP2P

1. O LP2P requisita o intervalo de dados do arquivo ao sistema ext3 (o arquivo lido está armazenado em um sistema de arquivos ext3); 2. O sistema ext3 lê o intervalo de dados requisitado do disco rígido; 3. O LP2P envia os dados para o LP2PFS.

Para esse experimento, foram realizadas 20 execuções e a média de tempo para o LP2PFS atender a requisição de cópia do arquivo foi de 3.614 segundos. Após, foi realizado o mesmo teste, entretanto sem a presença dos sistemas LP2P e LP2PFS, a fim de verificar o overhead imposto pelo sistema. Lendo o arquivo diretamente do sistema ext3, a média de tempo foi de 3.522 segundos. Dessa forma, o overhead imposto pelo sistema LP2P e LP2PFS é de apenas 2.61%, o que comprova o desempenho e a eficiência do sistema de arquivos LP2PFS, comprovando que o gargalo atual do sistema está no processo daemon LP2P. Embora o sistema de arquivos LP2PFS esteja 100% completo, a implementação do sistema LP2P ainda está em fase inicial de desenvolvimento. Para os testes, foi utilizado um protótipo do LP2P, que foi implementado, em sua primeira versão, apenas com o objetivo de permitir o compartilhamento de arquivos, sem considerar o desempenho das operações realizadas. Além disso, considerando a relação do número de páginas de memória de rea- dahead definido no sistema e a utilização de memória, foi definida uma função no sistema de arquivos para calcular dinamicamente o valor de readahead no momento da montagem do sistema, denominada set_readahead. Essa função define um valor baseando-se na quantidade total de memória do sistema e no número páginas livres que o sistema possui no momento da montagem. Como resultado, a função calcula o número ideal de páginas de readahead, que pode ser um valor entre 0 e 150 pági- nas. O valor superior ficou definido em 150 páginas, já que mesmo que o sistema possua uma grande quantidade de memória livre, deve-se levar em consideração que o sistema de arquivos só terá disponível uma parte dessa memória. 73

6 CONCLUSÃO

Este trabalho apresentou o desenvolvimento de um sistema de arquivos em es- paço de kernel para atuar como a interface do sistema LP2P com o usuário, possibi- litando a interação do usuário com o sistema LP2P através de uma interface simples e amplamente utilizada. O sistema de arquivos foi implementado em modo somente-leitura (read-only), devido ao fato do protocolo LP2P, atualmente, suportar somente operações de lei- turas de dados de arquivos. Ao montar o sistema, o usuário tem a possibilidade de informar o uid e o gid do usuário do sistema que será o proprietário dos arquivos no sistema de arquivos, além de poder especificar a porta TCP em que a conexão com o processo daemon do LP2P local será realizada. O sistema de arquivos LP2PFS foi desenvolvido com base na estrutura da ca- mada de abstração do kernel do Linux para sistemas de arquivos, o Virtual File System (VFS). Essa camada de abstração possibilita ao sistema ser montado den- tro de qualquer outro sistema de arquivos Linux, possibilitando ao usuário o acesso uniforme a arquivos em diferentes sistemas de arquivos. O gerenciamento dos dados dos arquivos em memória foi implementado com a utilização do subsistema de Page Cache, já que a interface do VFS utiliza esse subsistema para a manipulação de dados em memória. A unidade básica de dados de um arquivo é denominada página de memória, cujo tamanho varia entre as diferentes arquiteturas, mas, no kernel, é definido pela constante PAGE_CACHE_SIZE. Além da funcionalidade básica de um sistema de arquivos, ou seja, possibilitar aos usuários o acesso aos arquivos presentes no sistema, foi implementado também a notificação de eventos do sistema para as aplicações. Desse modo, as aplicações em espaço de usuário podem utilizar as interfaces fsnotify ou fanotify para requisitar o recebimento de eventos ocorridos no sistema de arquivos. Cada aplicação pode escolher os tipos de eventos que interessam, descartando os demais. Outra funcionalidade importante, nesse caso voltada para os desenvolvedores, foi a criação de um arquivo virtual no sistema de arquivos Procfs. Esse arquivo está localizado em fs/lp2pfs/, possibilitando o acesso a informações do sistema, como ponto de montagem, informações internas do sis- tema, como número dos inodes dos arquivos, e acesso aos metadados dos arquivos presentes no sistema. Desse modo, o sistema de arquivos LP2PFS alcançou o seu objetivo, que é 74 disponibilizar os arquivos presentes nos compartilhamentos do LP2P aos usuários, provendo integridade de dados. Além disso, o sistema é compatível com diversas versões do kernel, não restringindo o usuário ao uso de uma versão específica. Nos testes de desempenho, foi comprovado que o desempenho do sistema de arquivos é dependente do número de páginas de dados requisitadas pelo kernel do Linux para leitura. Esse número é influenciado de maneira direta pelo valor defi- nido, dentro do sistema de arquivos, na variável ra_pages, presente na estrutura backing_dev_info. Os valores alcançados de vazão de dados dos peers não foram os ideais, mas esse resultado foi fortemente influenciado pelo estágio de desenvolvimento do sistema LP2P, que possui implementado apenas um protótipo com poucas funcionalidades e que não está devidamente otimizado. Além disso, foi comprovado que o overhead para a leitura de arquivos locais imposto pelo sistema completo (LP2P e LP2PFS) é de apenas 2.61%, indicando que o gargalo do sistema, atualmente, é a comunicação em rede entre os processos daemon do LP2P. O desenvolvimento desse trabalho de conclusão possibilitou a publicação do artigo “Comunicação Peer-to-Peer aplicado a Redes Locais”, publicado no Fórum de Pós-Graduação da Escola Regional de Redes de Computadores de 2010, sendo eleito o melhor artigo na sua categoria. Também foi enviado o artigo “LP2P: A Peer- to-Peer System for Local Area Networks” para a Conferência Internacional sobre Novas Tecnologias, Mobilidade e Segurança (Fourth IFIP International Conference on New Technologies, Mobility and Security). Como trabalhos futuros, é possível citar a adição da funcionalidade de escrita de dados em arquivos, quando o protocolo LP2P suportar essa operação. Também é possível portar o sistema de arquivos para outras versões do kernel do Linux que ainda não são suportadas. 75

REFERÊNCIAS

ANDERSON, T. E. et al. Serverless network file systems. ACM Trans. Comput. Syst., ACM, New York, NY, USA, v. 14, n. 1, p. 41–79, 1996. ISSN 0734-2071.

ARTHUR, D.; PANIGRAHY, R. Analyzing bittorrent and related peer-to-peer networks. In: SODA ’06: Proceedings of the seventeenth annual ACM-SIAM symposium on Discrete algorithm. New York, NY, USA: ACM, 2006. p. 961–969. ISBN 0-89871-605-5.

BARBOSA, M. W. et al. Using locality of reference to improve performance of peer-to-peer applications. In: WOSP ’04: Proceedings of the 4th international workshop on Software and performance. New York, NY, USA: ACM, 2004. p. 216–227. ISBN 1-58113-673-0.

BATSAKIS, A. et al. Ca-nfs: A congestion-aware network file system. Trans. Storage, ACM, New York, NY, USA, v. 5, n. 4, p. 1–24, 2009. ISSN 1553-3077.

BLAIR, J. Introducing samba. Linux J., Specialized Systems Consultants, Inc., Seattle, WA, USA, p. 12, 1998. ISSN 1075-3583.

BOVET, D.; CESATI, M. Understanding the Linux Kernel, Third Edition. 3. ed. [S.l.]: O’Reilly Media, Inc., 2005. Paperback. ISBN 0596005652.

CALLAGHAN, B.; PAWLOWSKI, B.; STAUBACH, P. RFC 1813 - NFS Version 3 Protocol Specification. 1995. Disponível em . Acesso em abr. 2010.

CARNEGIE MELLON UNIVERSITY. Coda File System. 2000. Disponível em . Acesso em maio 2010.

CEBALLOS, M.-R.; GORRICHO, J.-L. P2p file sharing analysis for a better performance. In: ICSE ’06: Proceedings of the 28th international conference on Software engineering. New York, NY, USA: ACM, 2006. p. 941–944. ISBN 1-59593-375-1.

CHAI, L. et al. pNFS/PVFS2 over infiniband: early experiences. In: PDSW ’07: Proceedings of the 2nd international workshop on Petascale data storage. New York, NY, USA: ACM, 2007. p. 5–11. ISBN 978-1-59593-899-2.

CHAWATHE, Y. et al. Making gnutella-like p2p systems scalable. In: SIGCOMM ’03: Proceedings of the 2003 conference on Applications, technologies, architectures, 76 and protocols for computer communications. New York, NY, USA: ACM, 2003. p. 407–418. ISBN 1-58113-735-4.

CHESHIRE, S.; KROCHMAL, M. DNS Based Service Discovery. 2010. Disponível em . Internet draft. Acesso em abr. 2010.

CHESHIRE, S.; KROCHMAL, M. Multicast DNS. 2010. Disponível em . Internet draft. Acesso em abr. 2010.

CHESHIRE, S.; STEINBERG, D. H. Zero Configuration Networking. [S.l.]: O’Reilly Media, 2006.

CLARKE, I. et al. Freenet: A Distributed Anonymous Information Storage and Retrieval System. Lecture Notes in Computer Science, v. 2009, p. 46–66, 2001.

COHEN, B. Incentives Build Robustness in BitTorrent. [S.l.], 2003. Disponível em: .

CORBET, J. The fanotify API. 2009. Disponível em . Documentação explicativa sobre o sistema fanotify. Acesso em ago. 2010.

COULOURIS, G.; DOLLIMORE, J.; KINDBERG, T. Distributed Systems: Concepts and Design. [S.l.]: Bookman, 2007.

DUFOUR, A.; TRAJKOVIĆ, L. Improving gnutella network performance using synthetic coordinates. In: QShine ’06: Proceedings of the 3rd international conference on Quality of service in heterogeneous wired/wireless networks. New York, NY, USA: ACM, 2006. p. 31. ISBN 1-59593-537-1.

FRENCH, S. Linux CIFS Client Guide. 2007. Disponível em . Acesso em out. 2010.

FRY, C. P.; REITER, M. K. Really truly trackerless bittorrent. [S.l.], 2006.

GUO, L. et al. Measurements, analysis, and modeling of bittorrent-like systems. In: IMC ’05: Proceedings of the 5th ACM SIGCOMM conference on Internet Measurement. Berkeley, CA, USA: USENIX Association, 2005. p. 4–4.

HILDEBRAND, D.; HONEYMAN, P. Direct-pnfs: scalable, transparent, and versatile access to parallel file systems. In: HPDC ’07: Proceedings of the 16th international symposium on High performance distributed computing. New York, NY, USA: ACM, 2007. p. 199–208. ISBN 978-1-59593-673-8.

KRISHNAN, S. et al. The effects of metadata corruption on NFS. In: StorageSS ’07: Proceedings of the 2007 ACM workshop on Storage security and survivability. New York, NY, USA: ACM, 2007. p. 37–41. ISBN 978-1-59593-891-6.

KUROSE, J. F.; ROSS, K. W. Computer Networking: A Top-Down Approach. 5th. ed. USA: Addison-Wesley Publishing Company, 2009. ISBN 0136079679, 9780136079675. 77

LEGOUT, A. et al. Clustering and sharing incentives in bittorrent systems. In: SIGMETRICS ’07: Proceedings of the 2007 ACM SIGMETRICS international conference on Measurement and modeling of computer systems. New York, NY, USA: ACM, 2007. p. 301–312. ISBN 978-1-59593-639-4.

LEVIN, D. et al. Bittorrent is an auction: analyzing and improving bittorrent’s incentives. In: SIGCOMM ’08: Proceedings of the ACM SIGCOMM 2008 conference on Data communication. New York, NY, USA: ACM, 2008. p. 243–254. ISBN 978-1-60558-175-0.

LINUX KERNEL ORGANIZATION. The Linux Kernel Archives. 2010. Disponível em . Acesso em abr. 2010.

LINUX ONLINE INC. The Linux Home Page. 2010. Disponível em . Acesso em set. 2010.

LOVE, R. Intro to inotify. Linux Journal, 2005.

LOVE, R. Linux Kernel Development (2nd Edition). [S.l.]: Novell Press, 2005. ISBN 0672327201.

LUAN, H.; TSANG, D. H. K. A simulation study of block management in bittorrent. In: InfoScale ’06: Proceedings of the 1st international conference on Scalable information systems. New York, NY, USA: ACM, 2006. p. 58. ISBN 1-59593-428-6.

MAUERER, W. Professional Linux Kernel Architecture. Birmingham, UK, UK: Wrox Press Ltd., 2008. ISBN 0470343435, 9780470343432.

MOTTA, R.; NIENABER, W.; JENKINS, J. Gnutella: integrating performance and security in fully decentralized p2p models. In: ACM-SE 46: Proceedings of the 46th Annual Southeast Regional Conference on XX. New York, NY, USA: ACM, 2008. p. 272–277. ISBN 978-1-60558-105-7.

MUTHITACHAROEN, A. et al. Ivy: a read/write peer-to-peer file system. SIGOPS Oper. Syst. Rev., ACM, New York, NY, USA, v. 36, n. SI, p. 31–44, 2002. ISSN 0163-5980.

NGIWLAY, W.; INTANAGONWIWAT, C.; TENG-AMNUAY, Y. Bittorrent peer identification based on behaviors of a choke algorithm. In: AINTEC ’08: Proceedings of the 4th Asian Conference on Internet Engineering. New York, NY, USA: ACM, 2008. p. 65–74. ISBN 978-1-60558-127-9.

PARIS, E. fanotify: the fscking all notification system. 2009. Disponível em . Documentação escrita pelo desenvolvedor do sistema. Acesso em ago. 2010.

PARVEZ, N. et al. Analysis of bittorrent-like protocols for on-demand stored media streaming. In: SIGMETRICS ’08: Proceedings of the 2008 ACM SIGMETRICS international conference on Measurement and modeling of computer systems. New York, NY, USA: ACM, 2008. p. 301–312. ISBN 978-1-60558-005-0. 78

PAWLOWSKI, B. et al. Nfs version 3 - design and implementation. In: In Proceedings of the Summer USENIX Conference. [S.l.: s.n.], 1994. p. 137–152.

PAWLOWSKI, B. et al. The nfs version 4 protocol. In: In Proceedings of the 2nd International System Administration and Networking Conference (SANE 2000. [S.l.: s.n.], 2000.

PORTMANN, M. et al. The cost of peer discovery and searching in the gnutella peer-to-peer file sharing protocol. In: ICON ’01: Proceedings of the 9th IEEE International Conference on Networks. Washington, DC, USA: IEEE Computer Society, 2001. p. 263. ISBN 0-7695-1186-4.

RIVEST, R. RFC 1321 - The MD5 Message-Digest Algorithm. 1992. Disponível em . Acesso em set. 2010.

ROCHA, E. S.; MARCON, D. S.; ÁVILA, R. B. Comunicação peer-to-peer aplicado a redes locais. In: Escola Regional de Redes de Computadores - Fórum de Pós-Graduação. [S.l.: s.n.], 2010.

SALMON, R.; TRAN, J.; ABHARI, A. Simulating a file sharing system based on bittorrent. In: SpringSim ’08: Proceedings of the 2008 Spring simulation multiconference. San Diego, CA, USA: Society for Computer Simulation International, 2008. p. 1–5. ISBN 1-56555-319-5.

SALZMAN, P. J. The Linux Kernel Module Programming Guide. Paramount, CA: CreateSpace, 2009. ISBN 1441418865, 9781441418869.

SAROIU, S.; GUMMADI, P. K.; GRIBBLE, S. D. A measurement study of peer-to-peer file sharing systems. In: . [S.l.: s.n.], 2002.

SATYANARAYANAN, M. Scalable, secure, and highly available distributed file access. Computer, IEEE Computer Society Press, Los Alamitos, CA, USA, v. 23, n. 5, p. 9–18, 20–21, 1990. ISSN 0018-9162.

SATYANARAYANAN, M. The evolution of coda. ACM Trans. Comput. Syst., ACM, New York, NY, USA, v. 20, n. 2, p. 85–124, 2002. ISSN 0734-2071.

SHEPLER, S. et al. RFC 3530 - Network File System (NFS) version 4 Protocol. 2003. Disponível em . Acesso em abr. 2010.

SHERMAN, A.; NIEH, J.; STEIN, C. Fairtorrent: bringing fairness to peer-to-peer systems. In: CoNEXT ’09: Proceedings of the 5th international conference on Emerging networking experiments and technologies. New York, NY, USA: ACM, 2009. p. 133–144. ISBN 978-1-60558-636-6.

SILBERSCHATZ, A.; GALVIN, P. B.; GAGNE, G. Operating System Concepts. [S.l.]: Wiley Publishing, 2008. ISBN 0470128720.

STORAGE NETWORKING INDUSTRY ASSOCIATION. CIFS Technical Reference. 2002. Disponível em . Acesso em jul. 2010. 79

STUTZBACH, D.; REJAIE, R.; SEN, S. Characterizing unstructured overlay topologies in modern p2p file-sharing systems. IEEE/ACM Trans. Netw., IEEE Press, Piscataway, NJ, USA, v. 16, n. 2, p. 267–280, 2008. ISSN 1063-6692.

SUN MICROSYSTEMS. RFC 1094 - NFS: Network File System Protocol Specification. 1989. Disponível em . Acesso em abr. 2010.

TANENBAUM, A. S. Modern Operating Systems. Upper Saddle River, NJ, USA: Prentice Hall Press, 2007. ISBN 9780136006633.

TANENBAUM, A. S.; STEEN, M. v. Distributed Systems: Principles and Paradigms. 2nd. ed. Upper Saddle River, NJ, USA: Prentice-Hall, Inc., 2006. ISBN 0132392275.

TRAEGER, A.; THANGAVELU, K.; ZADOK, E. Round-trip privacy with nfsv4. In: StorageSS ’07: Proceedings of the 2007 ACM workshop on Storage security and survivability. New York, NY, USA: ACM, 2007. p. 1–6. ISBN 978-1-59593-891-6.

VERNOOIJ, J. R. Samba Developers Guide. 2009. Disponível em . Acesso em jul. 2010.