Best buffer size to store pathnames, PATH_MAX

Buffer size to store pathnames

보통 filename (또는 file name)이라고 하면 어떤 파일 이름 그 자체를 나타냅니다 (예: "hello.c" 또는 "src") 그리고 pathname이라고 하면 filename 또는 이 파일의 위치 정보까지 포함된 문자열을 뜻합니다. (예: "/usr/bin", "./a.out", "/home/cinsk/.emacs" 등) 이 때 pathname은 보통 root 디렉토리에서 시작하느냐의 여부에 따라 absolute pathname (절대경로) 또는 relative pathname (상대 경로)로 나타냅니다. Absolute pathname의 경우, "/"로 시작하는 pathname입니다.

파일 또는 디렉토리를 다루는 프로그램을 작성하다보면, filename이나 pathname을 취급할 경우가 많은데, 이 때 주어진 pathname을 저장하기 위해, 흔히 적당히 큰 문자 배열을 준비하고 쓰는 경우가 많습니다. 이런 식으로 작성한 프로그램은 적당히 크다고 생각한 값이 별로 크지 않을 경우, 버그가 발생할 수도 있으며, 나중에 수정하기도 꽤 어렵습니다. 따라서 이 경우에는 POSIX가 정의하고 있는 PATH_MAX⁠란 상수를 쓰는 것이 바람직합니다.

PATH_MAX⁠는 패스 이름이 가질 수 있는 최대 글자 수를 나타냅니다. 패스 이름이란 디렉토리 이름을 포함한 파일 이름을 뜻합니다. (예: "/usr/bin/ls", "/opt/share/info"). 문자열의 끝을 나타내는 '\0'도 포함입니다. 따라서 정확히 파일이 가질 수 있는 글자 수의 총 길이는 PATH_MAX - 1⁠이 됩니다.

따라서 파일 이름을 처리하거나 저장할 경우, PATH_MAX⁠ 크기의 메모리 공간을 준비해 놓고 작업하면 됩니다. 예를 들면 다음과 같습니다:

void foo(const char *pathname)
{
  char buf[PATH_MAX];
  buf[0] = '\0';     
  strncpy(buf, pathname, PATH_MAX - 1);

  ...
}

인생이 이렇게 단순했으면 얼마나 좋겠습니까만… 사실 이게 전부가 아닙니다.

먼저, POSIX 호환 시스템이라 하더라도 PATH_MAX⁠가 정의되어 있지 않은 경우가 있습니다. 왜 정의가 되어 있지 않냐면, 파일 이름의 길이 제한이 파일 시스템마다 달라질 수 있기 때문입니다. 예를 들어 무조건 한 파일 시스템만 사용하는 OS나, 여러 파일 시스템을 지원하더라도 파일 길이 제한의 값이 같은 OS라면 PATH_MAX⁠가 정의되어 있습니다. 하지만, 만약 여러 파일 시스템을 지원하고, 또 각 파일 시스템마다 지원하는 최대 파일 이름 길이가 다르다면, 단순히 한 상수로 최대 파일 이름 길이를 나타낼 수 없습니다.

또한, 드물지만, 파일 이름 길이에 제한이 없다면, PATH_MAX⁠가 정의되어 있지 않습니다.

둘째, PATH_MAX⁠가 정의되어 있다 하더라도, 이 매크로가 배열의 크기로 쓰기에는 너무나 큰 상수일 가능성이 있습니다. 따라서 다음과 같이, 배열의 크기를 지정하는 목적으로 PATH_MAX⁠를 쓰는 것은 별로 바람직한 방법은 아닙니다. (저도 여유가 없으면 자주 쓰는 방식이긴 하지만.. -_-;;)

char buf[PATH_MAX];

세째, PATH_MAX⁠의 정확한 뜻에 관한 것입니다. PATH_MAX⁠는 임의의 한 pathname이 가질 수 있는 최대 값을 나타내는 것이 아닙니다. 다시 말해, PATH_MAX⁠보다 큰 pathname이 존재할 수도 있습니다. 여기에 관한 것은 바로 뒤에 pathconf(3)를 설명할 때 다루겠습니다.

그럼 PATH_MAX⁠가 정의되어 있지 않다면 어떻게 pathname의 최대값을 얻을 수 있느냐? 답은 pathconf(3)나 fpathconf(3)를 쓰는 것입니다.

#include <unistd.h>

long fpathconf(itn filedes, int name);
long pathconf(const char *path, int name);

두 함수 모두, pathname에 관련된 정보를 얻는 목적으로 쓰이며, fpathconf()는 이미 열려있는 file descriptor를, pathconf()는 파일/디렉토리 이름을 첫 인자로 받습니다. PATH_MAX⁠의 값을 얻으려면 두 함수 모두 두번째 인자에 _PC_PATH_MAX⁠를 주면 됩니다.

왜 file descriptor나 파일/디렉토리 이름이 필요한지 궁금해 하는 분도 있을 것입니다. 그 이유는, 앞에서 잠깐 말했듯이, pathname의 최대값은 현재 파일 시스템에 따라 달라질 수 있기 때문에, 기준이 되는 파일/디렉토리 이름이 필요하기 때문입니다.

이 두 함수 모두, 다른 정보를 얻기 위해 사용되기도 합니다. 예를 들어, 단순한 파일 이름의 최대값을 얻으려면 위 함수의 두번째 인자로 _PC_NAME_MAX⁠를 사용합니다. 또 주어진 파일이 pipe (또는 FIFO)인 경우, 파이프 버퍼의 크기를 얻기 위해 _PC_PIPE_BUF⁠를 쓸 수도 있습니다.

한가지 주의할 것은 이 때 얻은 PATH_MAX⁠ 값의 정확한 뜻입니다. 두 번째 인자로 _PC_PATH_MAX⁠를 쓸 경우, 첫 번째 인자로 전달한 파일 이름이나 file descriptor는 반드시 디렉토리에 대한 이름 또는 file descriptor이어야 합니다. 첫 번째 인자가 디렉토리를 가르킬 경우, 이 때 리턴한 값은 주어진 디렉토리를 기준으로한 상대 경로가 가질 수 있는 최대 길이를 뜻합니다. 만약 첫번째 인자가 디렉토리를 가리키지 않는다면, 리턴 값과 주어진 파일과 어떤 관계가 있다는 것을 보장할 수 없습니다. 또한 pathname의 길이에 대한 제한이 없는 경우, 이 두 함수는 -1을 리턴하고 errno를 설정하지 않습니다.

추가적으로, POSIX는 _POSIX_PATH_MAX⁠란 매크로를 256으로 정의하고 있습니다. 그리고 PATH_MAX⁠는 적어도 _POSIX_PATH_MAX⁠와 같거나 큰 값을 가져야한다고 정의합니다. 또 오래된 유닉스 시스템은 전통적으로 MAXPATHLEN이란 매크로를 쓰는 경우가 많습니다. (주의, 필자는 MAXPATHLEN의 정확한 뜻이나 유래에 대해 잘 모릅니다. 아시는 분은 제게 알려주시면 고맙겠습니다.)

또, ISO C 표준은 파일 이름을 저장하기 위한 배열의 크기를 지정할 목적으로 FILENAME_MAX⁠란 매크로를 <stdio.h>⁠에 정의하고 있습니다. 이 매크로는 배열을 선언할 때 쓸 목적으로 만든 것이기 때문에 다음과 같이 쓰는 것이 가능합니다:

char buf[FILENAME_MAX];

하지만, 사용가능한 파일의 최대 길이가 제한이 없는 경우라면, 문자 배열의 크기로 쓸만한 값을 FILENAME_MAX⁠로 정의한다고 나와 있습니다. 따라서 파일의 최대 길이가 제한이 없는 경우라면 pathconf()나 fpathconf()를 써야만 알 수 있습니다. (errno 변경없이 -1을 리턴)

따라서 이식성이 뛰어난 프로그램을 만들고 싶다면 다음과 같은 코드를 헤더 파일에 포함시키는 것도 좋을 것입니다:

#include <unistd.h>
#include <limits.h>

#ifndef _POSIX_PATH_MAX
#define _POSIX_PATH_MAX    256
#endif

#if !defined =PATH_MAX=  && defined _PC_PATH_MAX
# define PATH_MAX    (pathconf("/", _PC_PATH_MAX) < 1 ? 1024 \
		      : pathconf("/", _PC_PATH_MAX))
#endif

#if !defined PATH_MAX && defined MAXPATHLEN
# define PATH_MAX MAXPATHLEN
#endif

#if !defined PATH_MAX && defined FILENAME_MAX
# define PATH_MAX FILENAME_MAX
#endif

#ifndef PATH_MAX
# define PATH_MAX _POSIX_PATH_MAX
#endif

물론 완벽한 것은 아닙니다. 사실 위 코드는 gnulib 패키지의 <pathmax.h>를 조금 손본 것이며, pathname 길이에 제한이 없는 경우는 고려하지 않았습니다.

Summary

지금까지 내용을 요약해 보면,

  1. pathname을 저장하기 위해, PATH_MAX⁠를 쓰는 것은 바람직하나, PATH_MAX⁠보다 큰 pathname이 존재할 수도 있다는 것.
  2. PATH_MAX⁠를 쓸 경우, 동적으로 메모리를 할당하는 방식 (예: malloc() 함수)을 쓰는 것이 바람직하다는 것.
  3. PATH_MAX⁠는 마지막 '\0'도 포함한다는 것. 즉 PATH_MAX⁠ + 1과 같은 형태로 쓸 필요가 없다는 것.
  4. PATH_MAX⁠가 정의되어 있지 않을 경우, pathconf(3) 또는 fpathconf(3)를 써서 PATH_MAX⁠의 값을 얻을 수 있다는 것.
  5. 세번째 목적으로 pathconf(3)나 fpatconf(3)를 쓸 때, 첫번째 인자는 디렉토리를 가리키고 있어야 한다는 것입니다.

마지막으로, FILENAME_MAX⁠를 제외한 모든 매크로, 함수는 SUS 표준 (POSIX)이며, ISO C 표준에는 나와 있지 않다는 것을 말해 둡니다.

댓글

Comments powered by Disqus