Обычный C: открытие каталога с помощью fopen()

У меня есть программа, которая открывает файл и проверяет его длину.

FILE* fd = fopen(argv[1], "rb");
fseek(fd, 0, SEEK_END);
size_t flen = ftell(fd);
if (flen == ((size_t)-1)) {
    printf("%s is a directory.\n", argv[1]);
    fclose(fd);
    exit(1);
}

Теперь, по крайней мере, под Linux, fopen() возвращает действительный дескриптор файла при открытии каталога. Это приводит к возвращению операции поиска -1 (или, как size_t без знака, 0xFFFFFFFFFFFFFFFF= 264-1 в 64-битной системе).

К сожалению, условие в приведенном выше коде (flen == ((size_t)-1)) не поймает тот случай, и не делает flen == 0xFFFFFFFF (РЕДАКТИРОВАТЬ: должно быть 0xFFFFFFFFFFFFFFFF). printf()-Команды с %x ога %d в качестве строки формата показано, что обе стороны сравнения должны иметь одинаковое значение.

Почему оператор сравнения ведет себя так странно, даже когда обе стороны имеют одинаковый тип (size_t)? Я использую GCC 4.8.1 в качестве компилятора.

2 ответа

Решение

Каталоги не существуют в стандарте C99 (или в C2011). Итак, по определению, fopen -ing каталог является специфическим для реализации или неопределенным поведением.

fopen (3) может потерпеть неудачу (давая NULL результат). fseek (3) также может потерпеть неудачу (возвращая -1). И тогда вам лучше проверить errno (3) или использовать perror (3)

ftell задокументировано, чтобы вернуть long, а также -1L на провал. На 64-битном Linux это 0xffffffffffffffff,

Ваш код должен быть вместо

FILE* fd = fopen(argv[1], "rb");
if (!fd) 
  { perror(argv[1]); exit(EXIT_FAILURE); };
if (fseek(fd, 0, SEEK_END)<0) 
  { perror("fseek"); exit(EXIT_FAILURE); };
long flen = ftell(fd);
if (flen == -1L)
  { perror("ftell"); exit(EXIT_FAILURE); };

Кстати, в Linux/Debian/Sid/AMD64 с ядром libc-2.17 и 3.10.6 эти коды работают нормально, когда argv[1] является /tmp; surprizingly, flen является LONG_MAX т.е. 0x7fffffffffffffff

Кстати, в Linux каталоги - это особый случай файлов. Используйте stat(2) для пути к файлу (и fstat в файловом дескрипторе, возможно, полученном с помощью fileno (3) из некоторых FILE*) узнать больше метаданных о каком-либо файле, включая его "тип" (через его режим). Вы хотите, чтобы opendir (3), readdir (3) и closedir (3) работали с содержимым каталога. Смотрите также inode (7).

С http://pubs.opengroup.org/onlinepubs/7908799/xsh/fopen.html:

The fopen() function will fail if: 
[EISDIR] The named file is a directory and mode requires write access.

По крайней мере, на Linux, если вы пытаетесь fopen("dirname", "wb") Вы получаете ошибку EISDIR. Я также попытался с каталогом с d-------- правами доступа, и я все еще получаю EISDIR (а не EACCES.)

Другие вопросы по тегам