На картинке выше вы можете наблюдать, как ls считает, что linkylink/.. это не то же самое, что текущий каталог. При этом cd, кажется, с ним не согласен.
Начну рассказ со всем знакомых веб-адресов, которые похожи на системные пути.
Две точки в путях URI (в вебе)
Интерпретация точек описана в секции 5.2.4 RFC 3986.
Работает это так: каждый сегмент из двух точек уничтожает предыдущий сегмент:
/a/b/c/../../g <=> /a/g
при этом, если уничтожать нечего, две точки игнорируются:
example.com/../../../etc/passwd <=> example.com/etc/passwd
Правила были придуманы, чтобы относительные пути (../img/pic.png) можно было преобразовывать в абсолютные префиксом из uri-контекста:
- /a/css/index.css ссылается на ../img/pic.png
- в /a/css/index.css уничтожается все после последнего слеша => /a/css/
- ../img/pic.png прибавляется к /a/css/ => /a/css/../img/pic.png
- точки интерпретируются => /a/img/pic.png
Эти операции обычно делаются браузером, когда он преобразовывает относительные uri в абсолютные. Также, согласно стандарту, операция уничтожения точек должна выполняться при приведении uri к каноничному виду — нормализации — в том числе абсолютных uri.
Веб-сервера в дикой природе не сталкиваются с запросами содержащими точки и каждый обрабатывает их по-своему.
В целом, исходя из правила нормализации следует, что в uri вида 'http://example.com/a/b/../c' 'b' не обязана существовать.
Две точки в шелле
Схожим образом себя ведет шелловая команда cd: две точки всегда ведут на один сегмент путя выше, как бы отменяя предыдущий переход в подкаталог. Но, в отличие от uri, шелл проверяет существование промежуточных каталогов.
Если вы считаете первое естественным, то эта публикация для вас. На самом деле шелл эмулирует такое поведение cd: во всех остальных местах *nix ".." работает по-другому.
Даже встроенная команда source (или ее синоним ".") имеет отличное поведение от cd
Разница проявляется на символьных ссылках на каталоги: для cd переход по такой ссылке обратим через "..", тогда как остальная система будет воспринимать ".." как физического родителя каталога, на который ссылка указывает.
По-другому первое поведение называется logical (-L у pwd и cd), в противоположность physical (-P).
Две точки в файловой системе
В *nix ".." — это реальный подкаталог, единственный физический родитель, независимый от символьных ссылок. Если посмотреть внутрь файловой системы ext2, то описание подкаталогов ".." и "." ничем не будет отличаться от других, кроме того, что они перечисляются вначале.
В некоторых случаях (например, в FreeBSD) этот факт даже используется для вычисления пути к рабочему каталогу (который там отдельно не хранится), через последовательные переходы по "..".
Переход по некоторому пути — это то же самое, что последовательный переход в подкаталоги.
Нетрудно заметить, что если мы попали в некоторый каталог по символьной ссылке, то у нас не останется информации, чтобы вернуться: ".." будет указывать в физического родителя. Но как тогда cd работает с логическими путями, включающими в себя симлинки? Для этого шелл запоминает путь, по которому он пришел в каталог. Логический путь, построенный шеллом, доступен через переменную окружения $PWD или через pwd [-L].
Две точки требуют внимания, особенно в скриптах.
PS: Символьных ссылок на каталоги очень много в sysfs:
bug@earth /sys/class/net/lo % ls addr_assign_type flags phys_port_id address gro_flush_timeout phys_switch_id addr_len ifalias power ... bug@earth /sys/class/net/lo % ls .. lo bug@earth /sys/class/net/lo % pwd -P; pwd -L /sys/devices/virtual/net/lo /sys/class/net/lo bug@earth /sys/class/net/lo % cd ..; ls eth0 lo
Комментарии (18)
schuykov
29.01.2016 13:45+5Хмм…
1. «Вообще, в *nix нигде не хранится путь до рабочего каталога процесса, а вместо этого для каждого процесса система запоминает inode (идентификатор файла) текущего и корневого каталогов. Когда где-нибудь требуется путь, он вычисляется последовательными переходами вверх по файловой иерархии, от текущего каталога, до корня, запоминая каждый шаг.»
Linux конечно не unix, однако в /proc/idПроцесса путь хранится в виде симлинка exec
Кроме того у Вас не указано, что же такое linkylink?TheRipper
29.01.2016 14:07Если вы про /proc/PID/exe, то он хранится как inode каталога + inode файла, в этом можно убедиться вот так:
bug@earth ~ % echo $$ 1459 bug@earth ~ % ls -l /proc/1459/exe lrwxrwxrwx 1 bug bug 0 Jan 29 01:37 /proc/1459/exe -> /bin/zsh5 bug@earth ~ % sudo mv /bin/zsh5 foo bug@earth ~ % ls -l /proc/1459/exe lrwxrwxrwx 1 bug bug 0 Jan 29 01:37 /proc/1459/exe -> /home/bug/foo
Путь к exe вычисляется каждый раз.
А linkylink это как раз символьная ссылка на каталог, на которых видна разница в поведении cd и других утилит.TheRipper
29.01.2016 15:23+1Хм, я был очень неправ. Оно действительно хранится как путь и каким-то образом обновляется.
И даже больше, getcwd тоже читает готовый путь.
Поправлю в статье.TheRipper
29.01.2016 15:47Возможно что вычисление пути через переход к корню я видел в какой-то реализации libc.
EnterSandman
29.01.2016 14:12+2И тут мне вспомнился анекдот про два путя
domix32
29.01.2016 15:21+3Травите для незнающих
urticazoku
29.01.2016 16:17+2Возможно: «Если вас съели — не отчаивайтесь: у вас всегда есть два выхода.»
kvaps
29.01.2016 19:46если сделать:
rm -rf /*
— то команда cd будет единственной командой, которая останется работать, казалось бы почему?)TheRipper
29.01.2016 20:04+2Попробовал — нет, pwd, например, тоже работает. Но вот браузер уже действительно почему-то не запускается.
kvaps
29.01.2016 20:26+2Ну вообще да, не единственная, вот все которые останутся:
Скрытый текст! builtin cp esac function let pwd source ulimit ./ caller declare eval getopts ll read suspend umask : case dirs exec hash local readarray test unalias [ cd disown exit help logout readonly then unset [[ command do export history ls return time until ]] compgen done false if mapfile rm times wait alias complete echo fc in mv select trap while bg compopt elif fg jobs popd set true { bind continue else fi kill printf shift type } break coproc enable for l. pushd shopt typeset
TheRipper
29.01.2016 20:32+1Они встроены в шелл (причем pwd бывает как встроенная, так и внешняя).
А вот реализовать cd как внешнюю утилиту невозможно (как культурно можно поменять рабочий каталог у другого процесса?).
amarao
02.02.2016 18:08Ещё останутся самые важные для попытки что-то починить (не после rm -rf /*, конечно) — это '|', '>', '>>', '<'.
dpivovarov
Точки в MySQL :)