Я просматривал исходный код SVN и нашел этот комментарий в исходном коде.
/* The following makes sure that file descriptors 0 (stdin), 1
(stdout) and 2 (stderr) will not be "reused", because if
e.g. file descriptor 2 would be reused when opening a file, a
write to stderr would write to that file and most likely
corrupt it. */
за которым следует этот код:
if ((fstat(0, &st) == -1 && open("/dev/null", O_RDONLY) == -1) ||
(fstat(1, &st) == -1 && open("/dev/null", O_WRONLY) == -1) ||
(fstat(2, &st) == -1 && open("/dev/null", O_WRONLY) == -1))
{
if (error_stream)
fprintf(error_stream, "%s: error: cannot open '/dev/null'\n",
progname);
return EXIT_FAILURE;
}
Я не понимаю, как «повторное использование» дескриптора файла может повредить файл. И как вы можете открыть файл с файловым дескриптором 2 (stderr)? И как открытие файла может повлиять на запись в stderr?
@JonathanLeffler Я не понимаю, как стандартная ошибка может указывать на файл SVN. Разве мы не используем его в качестве дескриптора выходного файла. Если мы, например. иметь файловый дескриптор, открытый для файла, такого как fopen, и возвращать FP, как этот FP указывает на stderr. Есть ли код, который может продемонстрировать это?
@Plimpus Да, я искал там свою локальную копию репозитория svn. Дело в том, что я не понимаю, как указатель файла может указывать на stderr. Например, это проблема, когда мы fprintf выводим на экран, что unix каким-то образом неправильно понимает fprintf (stderr, ...) и записывает его в последний открытый файловый дескриптор или что-то в этом роде?
@ Zer0day в сообщении журнала фиксации упоминается утилита svnadmin. Поэтому я не уверен, подходит ли код для операций на стороне клиента. Возможно, это больше для svnadmin и других утилит администратора.
Если вы запустите svn … 2>&-, SVN запустится без стандартной ошибки (2>&- означает «закрыть файловый дескриптор 2»). Тогда первому открытому файлу будет присвоен файловый дескриптор 2. Но stderr использует файловый дескриптор 2, поэтому любое сообщение об ошибке будет отправлено в открытый файл. Запись завершится ошибкой, если файл будет открыт для чтения, а не для записи, но в противном случае сообщение об ошибке появится там, где вы этого не ожидаете. Это немного отличается от svn … 2>/dev/null, где стандартная ошибка перенаправляется на /dev/null, или svn … 2>/tmp/svn.log, где она переходит к именованному файлу.
@JonathanLeffler Итак, каждый раз, когда мы используем дескриптор файла с open () или close (), этот дескриптор файла также будет использоваться для следующей операции open () или close (), верно? Это функции open() и close() из стандартной библиотеки C или функции linux close() и open()? Также относится ли это к другим функциям, наряду с open и close ?
Официальное правило гласит: «open() — или любая другая аналогичная функция создания дескриптора файла, включая pipe() — будет использовать наименьший номер дескриптора файла, который в данный момент закрыт». Насколько я знаю, единственными исключениями являются dup2() (где вы указываете используемый дескриптор файла) и fcntl(F_DUPFD, …) (где вы указываете наименьший номер дескриптора файла для использования). Но если файловый дескриптор 2 закрыт (но 0 и 1 открыты), то первым доступным файловым дескриптором будет 2, и он будет возвращен open() или другим вызываемым коллегой.
@JonathanLeffler Но мы должны вызывать fcntl для каждого дескриптора файла, который мы создаем после закрытия stderr, если мы хотим снова открыть stderr. Есть ли глобальный способ установить это? Значит, нам не нужно вызывать fcntl для каждого файлового дескриптора?
Если вы знаете, что файловые дескрипторы 0, 1, 2 открыты (поскольку стандарт C предписывает, что они должны быть открыты, или потому что вы заранее проверили), а код в приложении (в данном случае SVN) никогда не закрывает ни один из этих дескрипторов , то нет необходимости постоянно проверять. Кажется, именно это и делает код, который вы цитируете — используйте /dev/null с файловыми дескрипторами 0, 1, 2, если они еще не открыты. И это позволяет избежать неприятностей. POSIX определяет поведение, которое я отметил в §2.14 Распределение файловых дескрипторов.





Боюсь, я не знаю о проблемах, специфичных для SVN, для решения которых предназначен этот код (он был зафиксирован более 18 лет назад). Но я думаю, что приведенное ниже резюме описывает потенциальную проблему на высоком уровне. Поэтому я предполагаю, что рассматриваемая часть кода предотвращает это:
Давняя практика Unix требует, чтобы приложения запускались с стандартные потоки ввода, вывода и ввода/вывода ошибок в файловых дескрипторах 0, 1 и 2 соответственно. Предположение, что эти файловые дескрипторы будет правильно настроен, настолько силен, что большинство разработчиков даже не задумываются проверить их. Так что интересные вещи могут произойти, если приложение запускать с одним или несколькими закрытыми стандартными файловыми дескрипторами.
Рассмотрим, например, запуск программы с файловым дескриптором 2. закрыто. Следующему файлу, который откроет программа, будет присвоен этот дескриптор. Если что-то затем заставляет программу писать (что она думает, что это) стандартный поток ошибок, этот вывод вместо этого будет идти в другой файл, который был открыт, вероятно, повредив этот файл. А злонамеренный пользователь может легко натворить беспорядка таким образом; когда setuid программы вовлечены, потенциальные последствия хуже.
Взято с https://lwn.net/Articles/347815/
PS Проверьте svn вину за эту часть кода . В нем есть лог-сообщение, описывающее цель этих проверок.
Спасибо за повтор. Я как бы понимаю это немного больше, но все еще не мог понять идею. Запуск программы с закрытым файловым дескриптором 2. Что означает файловый дескриптор stderr закрытый? Насколько я знаю, в unix есть только функция close(), которая закрывает любой файловый дескриптор. Так как же программа закрывает stderr? И допустим, мы его закрыли, как следующий файл, который мы открываем, назначается stderr. Я не эксперт, но я не знаю, как программа может открыть файл с помощью stderr. Можете ли вы уточнить эту часть немного больше?
@Zer0day Я предлагаю вам обратиться в список рассылки dev@ Subversion, чтобы узнать точное назначение этого кода и какие проблемы он предназначен для предотвращения.
Если код записывает сообщение об ошибке в стандартную ошибку, но стандартная ошибка указывает на файл SVN, а не на терминал или что-то подобное, то этот файл SVN будет поврежден.