Символическая ссылка

**Символическая ссылка**  (она же  **симлинк** ,  **софтлинк** , в простонародье —  **ярлык на стероидах** ) — это такая особая форма магии файловой системы в православных ОС, позволяющая одному файлу притворяться другим, находиться в нескольких местах одновременно и порождать тем самым когнитивный диссонанс у неофитов и трудноуловимые баги у быдлокодеров. В отличие от своего убогого кастрированного кузена из Windows, известного как  *ярлык* , симлинк является полноценным обманом на уровне системы. Для 95% программ и пользователей он неотличим от оригинала, что открывает как бездну возможностей для хитроумной настройки системы, так и ещё более глубокую бездну для эпичнейших фейлов.

Симлинк — это не сам файл. Это даже не указатель на файл в привычном C-подобном смысле. Это, мой юный падаван, всего лишь крохотный текстовый файлик, в котором записан путь до другого файла. Но операционная система, наткнувшись на эту записку, делает вид, что записки нет, а вместо неё лежит тот самый, оригинальный файл. Эдакий телепорт или червоточина в пространстве вашего харда. Вы открываете /home/vasya/porn/skachat_besplatno_bez_registracii_i_sms/, а на самом деле ваше тело, разум и поток данных переносятся в /mnt/secret_raid_array/do_not_touch/fsb_archive/all_your_base_are_belong_to_us/compilation_228.mkv. Удобно? Ещё бы. Опасно? А то!

## Веревка и мыло

Чтобы постичь всю глубину замысла отцов-основателей, необходимо мысленно перенестись в те дремучие времена, когда компьютеры были большими, а программисты — бородатыми и суровыми. В эпоху, когда дисковое пространство измерялось не терабайтами, а благоговейным трепетом, а понятие  *копировать*  было ругательным. Представь, анон, что у тебя есть сверхценная библиотека, допустим, `libc.so.6`. Она нужна примерно всем программам, которые ты запускаешь. И что же, прикажешь каждому рукожопому  *Hello World'* у тащить с собой копию этой многомегабайтной дуры? Да винчестер треснет и пойдет по миру с протянутой магнитной головкой!

Тут-то на сцену и выходят они, ссылки. Изначально были придуманы так называемые  **жесткие ссылки**  (хардлинки), которые есть суть одно и то же файловое тело, но с разными именами, возможно, в разных каталогах (но, что важно, в пределах одной файловой системы — одного раздела диска, sic!). Это как если бы у человека было два паспорта с разными именами, но отпечатки пальцев и ИНН — одни и те же. Удаляешь одно имя — человек продолжает жить под другим. И только когда исчезнет последнее имя, тело предадут земле, то есть освободят занимаемые им блоки на диске. Мощно, кондово, по-серьезному. Но с кучей ограничений. Нельзя создать хардлинк на каталог, чтобы случайно не зациклить файловую систему в ленту Мёбиуса и не отправить ядро в паническое путешествие по бесконечному дереву каталогов. Нельзя сослаться на файл на другом диске, потому что магия хардлинков работает на уровне inode'ов, а у каждого раздела они свои.

Мудрецы из Bell Labs почесали свои бороды и поняли, что нужен более гибкий, хоть и менее надежный инструмент. Нужна была возможность просто оставить записку:  *Эй, ядро, если кто будет искать файл X, скажи ему, что он на самом деле лежит вот по этому адресу Y. И не твое собачье дело, есть ли там что-то на самом деле, существует ли вообще тот диск и не ведет ли этот путь прямиком в /dev/null* . Так родилась символическая ссылка — квинтэссенция пофигизма и перекладывания ответственности, столь свойственных гениальным разработчикам.

> Симлинк — это как записка на холодильнике  *«котлеты в кастрюле»* . Ты видишь записку, но ожидаешь найти котлеты. Хардлинк — это когда у одной и той же кастрюли есть две ручки в разных комнатах. А ярлык Windows — это рисунок кастрюли с котлетами, нарисованный твоим пятилетним сыном в Paint. Выглядит похоже, но наесться этим нельзя, и по клику на него в лучшем случае запустится программа для просмотра картинок.

С тех пор симлинк стал одним из краеугольных камней UNIX-way. Нужно обновить программу, не трогая основной исполняемый файл? Пожалуйста: /usr/bin/programm -> /opt/programm/versions/1.0/programm. Вышла новая версия? Просто меняем симлинк на /opt/programm/versions/2.0/programm, и все довольны. Нужно расшарить свои конфиги между несколькими машинами через Dropbox? Элементарно, Ватсон: переносим ~/.bashrc в папку Dropbox, а на его место ставим симлинк. Вариантов использования — легион, и каждый ограничивается лишь фантазией и прямизной рук админа.

## Каббалистика для избранных

Для создания этой файловой аномалии используется заклинание `ln` с ключом `-s`. Казалось бы, что может быть проще? `ln -s /куда/ведем /где/создаем`. Однако именно на этом моменте 95% неофитов, впервые открывших для себя консоль, совершают ритуальное самоубийство своего мозга. В их головах, отравленных логикой графических интерфейсов, прочно засело правило  *сначала источник, потом назначение* , как при банальном копировании `cp source destination`. И они, полные праведного гнева, набирают `ln -s link_name target`, получая в результате неработающую ссылку внутри самой себя или ещё какую-нибудь дичь, и бегут на форум с воплями  *ААААААА ЛИНУПС ГОВНО НИЧЕВО НЕ РАБОТАЕТ!!!* .

На самом деле, синтаксис `ln` абсолютно логичен. Первый аргумент, `target` — это тот самый текст, который будет записан внутрь файла-ссылки. Это просто строка. Второй аргумент, `link_name` — это имя файла, который мы создаем. Всё. Никакой магии. Но есть нюанс. Путь, который записывается в ссылку, может быть  *абсолютным*  (начинающимся с `/`) или  *относительным*  (не начинающимся). И вот тут начинается настоящий цирк с конями.

Допустим, некий Вася, пытаясь сделать по-модному и вынести свои бесценные скрипты из домашней папки в /usr/local/bin, пишет:
`cd /home/vasya/scripts`
`ln -s super_script.sh /usr/local/bin/super_script`
Он создал ссылку с  *относительным*  путем. Пока он находится в каталоге `/home/vasya/scripts`, все работает. Но стоит системе попытаться найти `super_script` из любого другого места, она посмотрит на ссылку `/usr/local/bin/super_script`, увидит внутри нее текст  *super_script.sh*  и попытается найти этот файл...  *относительно текущего местоположения ссылки* , то есть в `/usr/local/bin/`. А его там нет! Итог —  *Broken pipe* ,  *File not found* , слезы, сопли и ненависть к ГНУ/Линупсу. Правильный путь — всегда использовать абсолютные пути, если вы не уверены в своих действиях на 200%. Либо понимать, как работает относительная адресация. Но понимать — это для слабаков, правда?

> Проблема не в том, что `ln` сложный. Проблема в том, что современный юзер в принципе не способен удержать в голове концепцию  *текущей рабочей директории* . Для него существует только Рабочий стол и папка  *Мои документы* . Всё, что за пределами, — terra incognita, населенная драконами и демонами командной строки.

Ситуацию усугубляет и то, что многие утилиты ведут себя с симлинками по-разному. Например, `cd` по умолчанию переходит по ссылке так, что вы оказываетесь в целевой директории, меняя свой PWD. Но если использовать `cd -P`, то он останется  *снаружи* , считая, что вы все еще находитесь в папке со ссылкой. Команда `ls -l` честно показывает, куда ведет ссылка, а просто `ls` — нет. И вот на этих тонкостях и строятся хитроумные ловушки для невнимательных.

## Грабли имени дедушки Кернигана

Количество способов выстрелить себе в ногу с её помощью стремится к бесконечности.

Самое любимое развлечение — это, конечно же,  **рекурсивная ссылка** . Создается элементарно: `ln -s . self`. Всё. Внутри текущей директории появляется ссылка `self`, которая указывает... на текущую директорию. Что будет, если запустить утилиту, рекурсивно обходящую файлы, например `find .` или, не дай бог, `rm -rf .`? Правильно, она войдет в `self`, окажется в той же папке, снова увидит `self`, снова в неё войдет, и так до тех пор, пока не переполнится стек или не придет администратор с огнетушителем. Старые, не очень умные файловые менеджеры вроде `mc` при попытке посчитать размер такой папки могли подвесить систему намертво, бешено вращая счетчиком гигабайт и нагревая процессор до температур на поверхности Солнца.

Второй по популярности вид спорта —  **удаление не того** . Вот есть у вас папка `/var/log` с важными системными журналами. А место на корневом разделе заканчивается. Что делает грамотный админ? Он переносит `/var/log` на другой, большой диск, скажем в `/mnt/data/logs`, а на старом месте оставляет симлинк: `/var/log -> /mnt/data/logs`. Все работает, логи пишутся, место есть. А потом приходит стажер Петя, которому сказали  *почистить старые логи* . Он, недолго думая, пишет `rm -rf /var/log/`. Обратите внимание на слэш в конце! Для многих команд, включая `rm`, слэш на конце пути симлинка-на-директорию означает  *работай с тем, на что он указывает, а не с самим симлинком* . Вжух! — и вместо маленького файла-ссылки на пару десятков байт в небытие улетает вся многогигабайтная папка с логами на другом диске. Петя получает нагоняй, админ — седые волосы, а сервер — новую, девственно чистую жизнь.

А ещё есть  **висячие ссылки**  (dangling links). Это когда ссылка есть, а файла, на который она указывала, уже нет. Цель удалили, переместили, переименовали — а записка-то осталась! Система на это реагирует спокойно: ну, нет и нет. Библиотека какая-нибудь отвалится, программа перестанет запускаться — это проблемы юзера, а не системы. Найти такие ссылки можно специальными утилитами, но большинство  *эникейщиков*  о них и не подозревают, пока что-нибудь окончательно не отвалится. Их файловая система со временем превращается в кладбище таких вот надгробий-ссылок, указывающих в пустоту.

Но все это меркнет перед возможностями для  **злонамеренного использования** . Трюк, известный как  *symlink race attack* , заключается в том, что пока привилегированная программа собирается создать файл во временной директории (например, `/tmp/session_data`), злоумышленник успевает подменить его на симлинк, ведущий, скажем, к `/etc/passwd`. Программа, ничего не подозревая, открывает файл и со своими высокими правами записывает в него какую-нибудь чушь, тем самым портя системный файл и открывая путь к дальнейшим эксплойтам. Это атака класса "Time-of-Check-to-Time-of-Use" (TOCTOU). Сейчас, конечно, большинство систем и программистов научились с этим бороться, но время от времени подобные уязвимости всплывают до сих пор, вызывая ностальгическую улыбку у старых хакеров.

## Виндовые костыли

А что же в это время происходило в параллельной вселенной, в стенах кампуса Microsoft? А там, как обычно, изобретали свой собственный велосипед с квадратными колесами и без сиденья. На протяжении десятилетий единственным подобием ссылок в Windows были файлы с расширением `.lnk` — те самые  *ярлыки* . Этот уродец не является частью файловой системы. Это просто файл, который понимает только графическая оболочка Explorer.exe. Для любой другой программы это обычный бинарный мусор. Попробуйте в командной строке сделать `cd` на ярлык, ведущий к папке, или `type` на ярлык текстового файла. Результат немного предсказуем.

Ярлык — это именно что симулякр. Он хранит в себе не только путь к цели, но и иконку, параметры запуска, рабочую директорию и ещё тонну мусора. Он отчаянно пытается отслеживать перемещение целевого файла, что иногда получается, а иногда приводит к феерическим глюкам. Но на системном уровне это просто пшик, пустышка. Невозможно с его помощью обмануть программу, заставить ее писать логи в другое место или подгружать библиотеку с другого диска.

Разумеется,  *небыдло*  и корпоративные пользователи стонали и требовали нормальных ссылок. И Microsoft, скрепя сердце, их родила. Сначала в NTFS появились  **точки соединения**  (junction points), которые были, по сути, симлинками, но только для директорий. Создавать их можно было только через специальные утилиты, а большинство пользователей о них даже не слышало. А потом, с выходом Vista (которую никто не хотел) и Windows 7, явился и полноценный аналог `ln -s` — команда `mklink`.

> C:\>mklink /D "C:\Program Files\Common Files" "C:\Users\All Users\Application Data"
> *Отказано в доступе.*
> Вот примерно так выглядит первая встреча рядового виндузятника с `mklink`. Нужно запустить `cmd.exe` с правами Администратора, правильно указать ключи (`/D` для директорий, `/H` для хардлинков, или `/J` для точек соединения), не перепутать, в отличие от `ln`, местами назначение и источник, и вообще — совершить множество сакральных действий, которые противоречат всей философии  *Plug and Play & Next, Next, Finish* . Неудивительно, что 99.9% пользователей Windows проживают всю свою жизнь, так и не узнав, что их система умеет в эту магию. Для них мир так и остается плоским, а ссылки — просто красивыми иконками на рабочем столе.

## И все-таки, она вертится!

Несмотря на весь сопутствующий геморрой, тонны граблей и потенциальные дыры в безопасности, символические ссылки были, есть и будут одним из самых мощных и элегантных инструментов в арсенале любого человека, который работает с компьютером на уровне выше  *пользователя Ворда* . Это тот самый уровень абстракции, который позволяет строить гибкие, легко изменяемые и конфигурируемые системы. Без них мир современного DevOps, с его контейнерами, системами развертывания и управления конфигурациями, был бы просто немыслим.

Симлинк — это лакмусовая бумажка, проверка на профпригодность. Если человек путает симлинк с копией файла, боится его как огня или, наоборот, бездумно тыкает им во все подряд, не понимая последствий, — перед вами дилетант. Настоящий же гуру файловой магии плетет из симлинков такие конструкции, которые заставляют систему работать как швейцарские часы, оставаясь при этом прозрачными и управляемыми. Он знает, где можно обойтись относительным путем, а где нужен абсолютный. Он помнит про `rm -rf` со слэшем на конце и проверяет права доступа не только к самой ссылке, но и к ее цели.

## См. также

- Linux

- Windows

- Админ

- Грабли