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

Стандарт оболочки 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, это намного больше, чем это.

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

3 ответа

Решение

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

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

Хорошо, чтобы каталоги в вашем 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 путь
Истинно, если путь разрешается к существующей записи каталога для файла, для которого будет предоставлено разрешение на выполнение файла (или поиск по нему, если это каталог), как определено в разделе "Чтение, запись и создание файла". False, если путь не может быть разрешен, или если путь разрешается к существующей записи каталога для файла, для которого разрешение на выполнение (или поиск) файла не будет предоставлено. [Акцент добавлен.]

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

POSIX exec функция

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

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

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

В этом случае символические ссылки следуют прозрачно, без канонизации окончательного пути. Другими словами, 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,

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

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

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

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

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