Символическая ссылка
Символическая ссылка (она же симлинк, софтлинк, в простонародье — ярлык на стероидах) — это такая особая форма магии файловой системы в православных ОС, позволяющая одному файлу притворяться другим, находиться в нескольких местах одновременно и порождать тем самым когнитивный диссонанс у неофитов и трудноуловимые баги у быдлокодеров. В отличие от своего убогого кастрированного кузена из 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` со слэшем на конце и проверяет права доступа не только к самой ссылке, но и к ее цели.