поиск PATH включает в себя символические ссылки?


9

Стандарт оболочки POSIX говорит на этом сайте

http://pubs.opengroup.org/onlinepubs/9699919799/

о том, как оболочки используют PATHдля поиска исполняемых файлов:

«Список следует искать от начала до конца, применяя имя файла к каждому префиксу, пока не будет найден исполняемый файл с указанным именем и соответствующими разрешениями на выполнение».

Ну, это не то, как это работает в реальной реализации POSIX:

man which говорит:

"возвращает пути к файлам (или ссылкам), которые были бы выполнены в текущей среде, если бы его аргументы были заданы как команды в строго POSIX-совместимой оболочке. Это делается путем поиска в PATH исполняемых файлов, соответствующих именам аргументы. Он не следует за символическими ссылками. "

Хорошо, давайте посмотрим на эту ситуацию:

$ pwd /home/mark

$ echo $PATH /home/mark/bin:...

$ ls -l bin/foobar
lrwxrwxrwx 1 mark mark 18 Dec 12 22:51 bin/foobar -> /home/mark/foobar1
$ touch foobar1
$ which foobar
$ chmod a+x foobar1
$ which foobar
/home/mark/bin/foobar

Хорошо, вот символическая ссылка PATHс правильным именем, и она сообщается lsкак исполняемая.

which не смотрит на это вообще, а интересуется только тем, на что он указывает.

Это несмотря на тот факт, что оба man whichявно говорят, что они не следуют по символическим ссылкам (а мы действительно видим, что это не так, потому which foobarчто не печатает foobar1), а также что документация оболочки POSIX, приведенная выше, никогда не упоминает следующие символические ссылки в PATHалгоритме.

Так, whichа существующие оболочки ошибочны, или я не понимаю документацию?

УТОЧНИТЬ:

Я знаю и могу объяснить существующее поведение. Мой вопрос не «как это работает?». Что я знаю.

Мой вопрос касается документации: где моя ошибка в следовании документации, которую я цитировал. Или документация неверна?

МОТИВАЦИЯ: Почему меня это волнует?

Ну, я реализатор. Различные исполнители предъявляют разные требования. Для меня требование состоит в том, чтобы слово текущего стандарта POSIX ДОЛЖНО сопровождаться ТОЧНО (или, точнее, лучшим, каким оно может быть, потому что сам стандарт несколько глючит). Как будто это было слово Божие.

Теперь стандартная формулировка довольно ясна - следующие символические ссылки не упоминаются, где во многих других местах упоминается, где это необходимо сделать. Так что в этом случае нет.

Однако я всегда дважды проверяю, как dashи как bashсебя вести, просто чтобы убедиться. Теперь, конечно, здесь есть небольшая проблема, dashдаже несмотря на то, что она называется POSIX, имеет множество мелких ошибок, соответствующих POSIX. bashЯ еще не нашел никаких ошибок с POSIX, но ... bash на самом деле не POSIX, это намного больше, чем это.

Так что у вас есть это. Вот почему я забочусь


Вы не понимаете: что не следует за символическими ссылками на файлы . $PATHможет содержит символические ссылки. Попробуй which sh.
Ипор Сирсер

ОК, но в моем случае $PATHнет никаких символических ссылок.
user322908

Практически во всех ситуациях символические ссылки следуют прозрачно. Случаи, когда их нет , обычно упоминаются в явном виде (например, системные вызовы, например lstat(2)), после них обычно не указывается. Например, в описании open(2)упоминаются только символические ссылки, когда речь идет о поведении O_CREAT | O_EXCL. Не нужно указывать, что целевой файл будет открыт.
Бармар

Ответы:


10

Разрешения самой символической ссылки не имеют значения. Вы даже не могли бы изменить их, если бы попытались.

Что имеет значение, так это права доступа к базовому файлу.

Хорошо, чтобы в вашем PATH каталоги содержали символические ссылки на исполняемые файлы. Фактически, вполне вероятно, что многие исполняемые файлы в вашем PATH являются символическими ссылками. Например, в системах, подобных debian / ubuntu:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 23  2017 /bin/sh -> dash

Документация

От man chmod:

chmod никогда не меняет права доступа к символическим ссылкам; системный вызов chmod не может изменить их разрешения. Это не проблема, поскольку разрешения символических ссылок никогда не используются. Однако для каждой символической ссылки, указанной в командной строке, chmod изменяет права доступа к указанному файлу. Напротив, chmod игнорирует символические ссылки, встречающиеся во время рекурсивных обходов каталогов. [Акцент добавлен.]

пример

В оболочке есть тест, -xчтобы определить, является ли файл исполняемым. Давайте попробуем это:

$ ls -l
total 0
lrwxrwxrwx 1 john1024 john1024 7 Dec 12 23:36 foo -> foobar1
-rw-rw---- 1 john1024 john1024 0 Dec 12 23:36 foobar1
$ [ -x foo ] && echo foo is executable
$ chmod +x foobar1
$ [ -x foo ] && echo foo is executable
foo is executable

Таким образом, как и в случае с whichоболочкой, оболочка не считает исполняемый файл программной ссылки, если базовый файл не является исполняемым.

Как работает

В системе Debian whichэто сценарий оболочки. Соответствующий раздел кода:

 case $PROGRAM in
  */*)
   if [ -f "$PROGRAM" ] && [ -x "$PROGRAM" ]; then
    puts "$PROGRAM"
    RET=0
   fi
   ;;
  *)
   for ELEMENT in $PATH; do
    if [ -z "$ELEMENT" ]; then
     ELEMENT=.
    fi
    if [ -f "$ELEMENT/$PROGRAM" ] && [ -x "$ELEMENT/$PROGRAM" ]; then
     puts "$ELEMENT/$PROGRAM"
     RET=0
     [ "$ALLMATCHES" -eq 1 ] || break
    fi
   done
   ;;
 esac

Как видите, он использует -xтест, чтобы определить, является ли файл исполняемым.

POSIX определяет -xтест следующим образом:

-x pathname
Истинно, если pathname разрешает существующую запись каталога для файла, для которого будет предоставлено разрешение на выполнение файла (или поиск в нем, если это каталог), как определено в «Чтение, запись и создание файла». False, если путь не может быть разрешен, или если путь разрешается к существующей записи каталога для файла, для которого разрешение на выполнение (или поиск) файла не будет предоставлено. [Акцент добавлен.]

Так, POSIX проверяет то , что имя пути рассасывается в. Другими словами, он принимает символические ссылки.

POSIX exec функция

Функция POSIX exec следует символическим ссылкам. Спецификация POSIX идет долго, чтобы указать условия ошибки, о которых она может сообщить, если символические ссылки имеют круглую или слишком глубокую форму, например:

[ELOOP]
Цикл существует в символических ссылках, встречающихся при разрешении аргумента пути или файла.

[ELOOP]
Более чем {SYMLOOP_MAX} символических ссылок было найдено во время разрешения пути или аргумента файла.
[ENAMETOOLONG]
В результате обнаружения символической ссылки в разрешении аргумента пути длина замещенной строки имени пути превысила {PATH_MAX}.


Я ЗНАЮ все, что вы написали в своем ответе. Я знаю, как все "работает". Это не мой вопрос. Мой вопрос о документации. Укажите мне, где я не понимаю документацию. Или скажите, что документация неверна.
user322908

@ user322908 На большинстве систем whichэто скрипт оболочки. Таким образом, это скорее всего просто использование -xтеста, который я показал. Согласно POSIX, -xтестирует, «разрешает» ли файл исполняемый файл. Если вы видите что-то другое, куда вы смотрите?
John1024

Спасибо, и я прошу прощения за такую ​​боль ... Я понимаю, что мой вопрос отличается от 99% вопросов, поэтому трудно понять, что я хочу. Опять же, меня не интересует, как « whichработает», или это так, -xили что-то еще. Мне интересно знать, где я неправильно следую документации, которую я цитировал.
user322908

4
@ user322908 POSIX execфункции следующим образом символьные ссылки. Кажется, это ясно показывает, что файлы с символическими ссылками могут быть исполняемыми файлами под POSIX.
John1024

2
Я также проверил Ubuntu 17.10, man which который говорит: «Он не канонизирует имена путей». Это не значит, что он не следует по ссылкам. Это означает только то, что, как вы заметили, это не «канонизирует» имена.
John1024

3

В этом случае символические ссылки следуют прозрачно, без канонизации окончательного пути. Другими словами, whichне имеет значения, /home/mark/binявляется ли символическая ссылка или нет. Что его волнует, так это то, /home/mark/bin/foobarсуществует файл или нет. Это не нужно вручную сплющивающихся симлинками по пути - ОС может сделать это только штраф по своей собственной.

И действительно, когда whichзапрашивается информация о файле /home/mark/bin/foobar, ОС замечает, /home/mark/binчто является символической ссылкой, следует за ней и успешно находит foobarв целевом каталоге.

Это поведение по умолчанию, если программа не использует open(…, O_NOFOLLOW)или fstatat(…, AT_SYMLINK_NOFOLLOW)для доступа к файлу.

[комментарии объединены в]

В то время как вы говорите , что утилиты оболочки сделать это на индивидуальной основе случая, это не то же самое с ядром системных вызовов: все файлы, связанные вызовы делать следуют символические ссылки по умолчанию, если флаг «NOFOLLOW» не дается. (Даже lstat следует за символическими ссылками во всех компонентах пути, кроме последнего.)

Если в спецификации явно не указано, что делать с символическими ссылками, это означает, что будет использоваться поведение по умолчанию. Таким образом, оболочка, следующая за алгоритмом пути, не разрешает символические ссылки вручную и явно не отказывается от ОС, делая то же самое. (Он просто объединяет каждый компонент $ PATH с именем исполняемого файла.)

Когда на странице справки which (1) говорится, что она не следует символическим ссылкам, это может означать несколько вещей, но версия GNU coreutils утверждает это следующим образом:

При этом два эквивалентных каталога будут считаться разными, если в одном из них содержится путь с символической ссылкой.

Это намного уже по объему - это только означает, что whichон не будет пытаться вручную канонизировать все пути для удаления дубликатов, но это не означает, что инструмент откажется от символьных ссылок, следующих за ОС в целом. Например, если /binэто символическая ссылка /usr/bin, запуск which -a shвернет оба /bin/sh и /usr/bin/sh.


Да, спасибо, я все это знаю. Мой вопрос не в том, как все "работает". Я знаю, как они работают. Это не моя точка зрения. Моя точка зрения касается документации. Где я неправильно следую документации. Или документация неверна.
user322908

2
Вы неправильно понимаете документацию - если в ней не упоминаются следующие символические ссылки, это означает, что она не разрешает символические ссылки вручную , но обычное поведение ОС все еще применяется. Страница whichруководства GNU гласит это по-разному: «Который будет считать разные эквивалентные каталоги разными, если один из них содержит путь с символической ссылкой».
user1686

ОК, лучше, спасибо! Я пытаюсь понять ... Но ... простите за то, что у меня болит шея: "обычное поведение ОС" - это НЕ всегда неявно следовать символическим ссылкам. Есть множество утилит, которые этого не делают. Это на индивидуальной основе.
user322908

1
Все вызовы ядра - chdir, open, chmod, execve ... - будут следовать символическим ссылкам как в пути, так и в хвосте, если вы не укажете AT_SYMLINK_NOFOLLOW или аналогичный. (lstat - единственный, который не разыменовывает символические ссылки в хвосте, но все же делает это для оставшегося пути.) Поэтому поведение по умолчанию состоит в том, чтобы следовать символическим ссылкам. Например, когда оболочка вызывает execve("/home/mark/bin/foobar", …)ее, все символические ссылки будут отслеживаться.
user1686

ОК, я думаю, что я покупаю execveаргумент, на самом деле, в моей реализации, это то execl()же самое. Пожалуйста, если вы включите это в свой ответ, я приму.
user322908

1

Оболочка соответствует своей документации в том смысле, что она следует правилам разрешения пути. whichсоответствует своей документации. Два делают немного разные вещи.

Выходные данные which- это имя файла и путь ссылки, а не путь, на который указывает символическая ссылка. Это прописано в справочной странице.

Когда команда выполняется, ссылка «следует» согласно Разделу 4.13 «Разрешение имени пути» в том же разделе . Соответствующее условие для выполнения файла:

Во всех других случаях система должна ставить префикс оставшегося имени пути, если таковое имеется, с содержимым символической ссылки, за исключением того, что если содержимое символической ссылки является пустой строкой, то любое разрешение пути не должно выполняться с функциями, сообщающими [ENOENT ] ошибки и утилиты, пишущие эквивалентное диагностическое сообщение, или путь к каталогу, содержащему символическую ссылку, должны использоваться вместо содержимого символической ссылки. Если содержимое символьной ссылки состоит исключительно из символов, то все итоговые символы оставшегося имени пути должны быть исключены из полученного комбинированного имени пути, оставляя только начальные символы из содержимого символической ссылки. В тех случаях, когда происходит префикс, если объединенная длина превышает {PATH_MAX}, и реализация считает это ошибкой, Разрешение пути не должно выполняться, если функции сообщают об ошибке [ENAMETOOLONG], а утилиты записывают эквивалентное диагностическое сообщение. В противном случае разрешенный путь должен быть разрешением только что созданного пути. Если результирующее имя пути не начинается с, предшественником первого имени файла пути считается каталог, содержащий символическую ссылку.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.