Philip Withnall
philip.withnall@collabora.co.uk 2015
Acessando o sistema de arquivos
Rafael Fontenelle
rafaelff@gnome.org 2017

# Acesso ao sistema de arquivos

#

        Resumo

        Há alguns antipadrões a serem considerados ao acessar o sistema de arquivos. Esse artigo presume conhecimento das APIs GFile, GInputStream e GOutputStream padrões.

          Use E/S assíncrona para acesso de arquivo. ()
          Sempre use funções adequadas para construir nomes e caminhos de arquivos . ()
          Valide caminhos de arquivos que estão em diretórios esperados antes de usá-los. ()
          Use perfis obrigatórios de controle de acesso para reforçar restrições de acesso de arquivos. ()

# E/S assíncrona

Quase toda E/S deve ser feita assincronamente, ou seja, sem bloqueio do contexto principal do GLib. Isso pode ser alcançado por sempre usar as variantes `*_async()` e `*_finish()` de cada função de E/S.

Por exemplo, `g_input_stream_read_async()` em vez de `g_input_stream_read()`.

E/S síncrona bloqueia o loop principal, o que significa que outros eventos, tal como entrada de usuário, chegada de pacotes de rede, expiração de tempo limite e retorno de chamada ocioso, não são tratados até que a função bloqueadora retorne.

E/S síncrona é aceitável em certas circunstâncias nas quais a sobrecarga de agendar uma operação assíncrona excede o custo de E/S síncrona local no Linux. Por exemplo, fazer uma leitura pequena de um arquivo local, ou de um sistema de arquivos virtual tal como /proc. Para tais leituras, as funções de baixo nível `g_open()`, `read()` e `g_close()` devem ser usadas, em vez do GIO.

Arquivos no diretório pessoal do usuário não contam como locais, pois eles podem estar em um sistema de arquivos de rede.

Note que a alternativa – executar E/S síncrona em uma thread separada – é altamente desencorajada; veja as diretrizes de thread para mais informações.

# Construção de caminho de arquivo

Nomes e caminhos de arquivos não são strings normais: em alguns sistemas, eles podem usar uma codificação de caracteres diferente de UTF-8, enquanto strings normais no GLib têm a garantia de sempre usar UTF-8. Por este motivo, funções especiais devem ser usadas para compilar e tratar nomes e caminhos de arquivos. (Sistemas Linux modernos quase que universalmente usam UTF-8 para codificação de nome de arquivo, então isso não é um problema na prática, mas as funções de caminho de arquivo ainda devem ser usadas para compatibilidade com sistemas como o Windows, que usam nomes de arquivos UTF-16.)

Por exemplo, caminhos de arquivos devem ser construídos usando `g_build_filename()` em vez de `g_strconcat()`.

Fazer isso deixa mais claro qual é a intenção do código e também elimina separadores duplicados de diretórios, de forma que o caminho retornado é canonical (apesar de não necessariamente absoluto).

Como um outro exemplo, caminhos devem ser desmembrados usando `g_path_get_basename()` e `g_path_get_dirname()` em vez de `g_strrstr()` e outras funções manuais de pesquisa.

# Validação de caminho e sandboxing

Se um nome ou caminho de arquivo vem com uma entrada externa, tal como uma página web ou entrada de usuário, ele deve ser validado para garantir que colocá-lo em um caminho de arquivo não produzirá um caminho arbitrário. Por exemplo, se um nome de arquivo é construído a partir de uma string constante ~/ junto com alguma entrada de usuário, se o usuário insere ./../etc/passwd, eles podem (potencialmente) ganhar acesso a informação sensível de contas, dependendo de com qual usuário o programa está sendo executado e o que ele faz com os dados carregados a partir do caminho construído.

Isso pode ser evitado por meio de uma validação dos caminhos construídos antes de usá-los usando `g_file_resolve_relative_path()` para converter quaisquer caminhos relativos para absolutos e, então, validar se aqueles caminhos estão dentro de um diretório raiz de sandbox apropriado para a operação. Por exemplo, se um código baixa um arquivo, ele poderia validar se aqueles caminhos estejam dentro de ~/Downloads, usando `g_file_has_parent()`.

As a second line of defense, all projects which access the file system should consider providing a mandatory access control profile, using a system such as AppArmor or SELinux, which limits the directories and files they can read from and write to.