Philip Withnall
philip.withnall@collabora.co.uk 2015
파일 시스템에 접근하기
조성호
shcho@gnome.org 2016-2018.

# 파일 시스템 접근

#

        요약

        파일 시스템에 접근해야 할 때 피해야 할 몇가지 방식이 있습니다. 이 내용은 GFile, GInputStream, GOutputStream 표준 API를 알고 있음을 가졍합니다.

          파일 접근시 비동기 입출력을 활용하십시오().
          파일 이름 및 경로를 만들 때마다 적당한 함수를 활용하십시오().
          파일 경로를 활용하기 전 예상 디렉터리에 있는지 검증하십시오().
          파일 접근을 통제하는 필수 접근 통제 프로파일을 사용하십시오().

# 비동기 입출력

대부분의 모든 입출력 과정은 비동기 방식으로 처리해야합니다. 무슨 이야기냐면 GLib 메인 컨텍스트의 동작을 방해하면 안된다는 의미입니다. 각 입출력 함수의 `*_async()` 및 `*_finish()` 변형 함수를 활용하여 언제든 수행 가능합니다.

예를 들어 `g_input_stream_read()` 함수 대신 `g_input_stream_read_async()` 함수를 사용하십시오.

동기 입출력은 메인 루프의 실행을 가로 막는데, 사용자 입력, 네트워크 패킷 수신, 콜백 시간 초과 및 대기 같은 상황의 경우 프로그램의 동작 흐름을 가로막은 함수에서 반환 값이 나오기 전에는 처리할 수 없다는 의미입니다.

동기 입출력은 리눅스에서 로컬 동기 입출력 보다 비동기 처리 스케줄의 처리 시간/용량이 비교적 좋지 않은 일부 상황에서 용납 가능합니다. 예를 들어 로컬 파일 또는 /proc 같은 가상 파일 시스템에서 작은 데이터를 읽어들이는 경우가 있습니다. 이 때, GIO 대신 `g_open()`, `read()`, `g_close()` 저수준 함수를 사용해야합니다.

로컬에 없다고 간주하는 사용자 개인 디렉터리에 있는 파일은, 네트워크 파일 시스템에 있을 수 있습니다.

개별 스레드의 동기 입출력 처리 같은 대안책은 가급적 피하시는게 좋습니다. 자세한 내용은 스레드 처리 지침서를 참고하십시오.

# 파일 경로 만들기

파일 이름 및 경로는 일반 문자열이 아닙니다. GLib에서는 일반 문자열에 항상 UTF-8을 사용한다고 간주하지만, 일부 시스템에서는 UTF-8이 아닌 다른 문자 인코딩을 사용할 수 있습니다. 이 이유로, 파일 이름 및 경로를 만들고 처리할 때 특별한 함수를 사용해야합니다(최신 리눅스 시스템에서는 대부분 보편적으로 파일 이름 인코딩에 UTF-8을 사용하여 실제로는 문제되지 않지만, UTF-16 파일 이름을 사용하는 윈도우 같은 시스템의 호환성을 확보하려면 파일 경로 함수를 활용해야합니다).

예를 들어 파일 경로는 `g_strconcat()` 함수보다 `g_build_filename()` 함수를 사용하여 만들어야합니다.

이렇게 하면 코드가 무슨 일을 하는지 의미를 좀 더 분명하게 하며, (필연적으로 절대적이진 않지만)디렉터리 구분 문자 중복을 방지하여 올바른 경로를 반환합니다.

다른 예를 들어보자면, `g_strrstr()` 함수 및 다른 직접적인 검색 함수를 활용하기보다는, `g_path_get_basename()` 함수와 `g_path_get_dirname()` 함수를 활용하여 경로를 분해해야합니다.

# 경로 검증 및 샌드박싱

웹 페이지 또는 사용자 입력 등의 외부 입력으로 파일 이름 또는 경로를 받는다면, 파일 경로로 둔 경로의 임의 경로 여부를 확인하여 검증해야합니다.예를 들어 ~/ 상수 문자열에 일부 사용자 입력을 받아 파일 이름을 만들고, 여기서 사용자 입력이 ../../etc/passwd라면, 사용자가 프로그램을 어떻게 실행하며, 만든 경로에서 어떤 데이터를 불러오냐에 따라 민감한 계정 정보로 접근하는데 접근할 수 있는 권한을(잠재적으로) 얻을 수 있습니다.

이 기능을 사용하기 전, `g_file_resolve_relative_path()` 함수로 상대 경로를 절대 경로로 변환하고 처리에 적당한 루트 샌드박싱 디렉터리에 있는 경로인지 검증하는 방식으로 작성한 경로를 검증하여 (악의적 접근을) 막을 수 있습니다. 이를테면, 코드로 파일을 다운로드할 때, `g_file_has_parent()` 함수를 사용하면 모든 경로가 ~/Downloads 디렉터리에 있는지 검증할 수 있습니다.

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.