Можете ли вы изменить то, на что указывает символическая ссылка после ее создания?

122

Предоставляет ли какая-либо операционная система механизм (системный вызов, а не программа командной строки) для изменения имени пути, на которое ссылается символическая ссылка (символическая ссылка), кроме отмены связи старой и создания новой?

Стандарт POSIX этого не делает. В Solaris 10 этого нет. MacOS X 10.5 (Leopard) этого не делает. (Я вполне уверен, что ни AIX, ни HP-UX тоже. Судя по этому списку системных вызовов Linux, в Linux такого системного вызова также нет.)

Есть что-нибудь, что делает?

(Я ожидаю, что ответ будет «Нет».)


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

Если вы знаете, что какая-то (Unix-подобная) операционная система, еще не указанная в списке, не имеет системного вызова для перезаписи значения символической ссылки (строки, возвращаемой readlink()) без удаления старой символической ссылки и создания новой, добавьте ее - или их - в ответ.

Джонатан Леффлер
источник
Что плохого в простом повторном подключении? Почему бы просто не выполнить lnкоманду (или эквивалент API), перезаписав старую ссылку? Какая у вас проблема?
S.Lott
9
Забавно - я спрашиваю, есть ли системный вызов для выполнения работы по программированию, и вопрос помечается как «принадлежит другому сайту».
Джонатан Леффлер,
3
Забавно. Было абсолютно непонятно, что вы ищете системный вызов, и вы просто отредактировали вопрос, чтобы добавить эту деталь. Так как же вы можете ожидать, что люди вычтут что-то еще до того, как вы это напишете?
Паскаль Тивент,
2
@ S.Lott: Я пишу статью о безопасности и символических ссылках. В какой-то момент я утверждаю, что «фактический владелец, группа, права доступа к самой символической ссылке несущественны», и аргументирую это тем, что владелец символической ссылки может только удалить ее, но не изменить значение. Я дважды проверяю, что нет другого способа, кроме удаления символической ссылки, для достижения эффекта «переписывания значения символической ссылки». Я игнорирую прямой доступ к необработанному диску и взламываю FS таким образом - для этого требуются привилегии root, и меня беспокоят пользователи без полномочий root, а не то, что может делать root.
Джонатан Леффлер,
4
@Pascal: Мне очень жаль - я не понимал, что было непонятно, что я говорю о системных вызовах, пока люди не ушли в сторону от того, что я намеревался (что явно отличалось от того, что я сказал). Мне жаль, что я ввел в заблуждение; это было непреднамеренно.
Джонатан Леффлер,

Ответы:

106

AFAIK, нет, вы не можете. Вы должны удалить его и создать заново. Фактически, вы можете перезаписать символическую ссылку и, таким образом, обновить путь, на который она ссылается:

$ ln -s .bashrc test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 7 2009-09-23 17:12 test -> .bashrc
$ ln -s .profile test
ln: creating symbolic link `test': File exists
$ ln -s -f .profile test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 8 2009-09-23 17:12 test -> .profile

РЕДАКТИРОВАТЬ : как OP указал в комментарии, использование этой --forceопции заставит lnвыполнить системный вызов unlink()раньше symlink(). Ниже приведены результаты straceмоей работы с Linux, подтверждающие это:

$ strace -o /tmp/output.txt ln -s -f .bash_aliases test
$ grep -C3 ^unlink /tmp/output.txt 
lstat64("test", {st_mode=S_IFLNK|0777, st_size=7, ...}) = 0
stat64(".bash_aliases", {st_mode=S_IFREG|0644, st_size=2043, ...}) = 0
symlink(".bash_aliases", "test")        = -1 EEXIST (File exists)
unlink("test")                          = 0
symlink(".bash_aliases", "test")        = 0
close(0)                                = 0
close(1)                                = 0

Поэтому я предполагаю, что окончательный ответ - «нет».

РЕДАКТИРОВАТЬ : следующее скопировано из ответа Арто Бендикена на unix.stackexchange.com, около 2016 года.

Это может действительно быть сделано атомарно с rename(2), сначала создать новую символическую ссылку под временным именем , а затем аккуратно перезаписывать старый симлинк на одном дыхании. Как указано на странице руководства :

Если newpath ссылается на символическую ссылку, ссылка будет перезаписана.

В оболочке это можно сделать mv -Tследующим образом:

$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z

Вы можете использовать straceпоследнюю команду, чтобы убедиться, что она действительно используется rename(2)под капотом:

$ strace mv -T z.new z
lstat64("z.new", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
lstat64("z", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
rename("z.new", "z")                    = 0

Обратите внимание, что в приведенном выше примере оба mv -Tи straceотносятся к Linux.

В FreeBSD используйте mv -hпоочередно.

Примечание редактора: именно так Капистрано делал это уже много лет, начиная с ~ 2.15. См. Этот запрос на перенос .

Паскаль Тивент
источник
2
Разве опция '-f' не заставляет 'ln' выполнять системные вызовы unlink (), затем symlink ()?
Джонатан Леффлер,
1
Оно делает. Но вопрос мог бы быть воспринят как «как мне сделать это за один шаг», а затем он сводился к определению «шага» - командной строки? SYSCALL?
Михаил Крелин - хакер,
1
Я только что заметил, что вы добавили формулировку системного вызова в свой вопрос ;-)
Майкл Крелин - хакер
2
@Pascal: это так. В Solaris вывод из truss ln -spx и truss ln -s -fpx показывает вызов unlink () перед вызовом symlink () во втором случае (и неудачный вызов symlink () в первом). Я ожидал, что это применимо к большинству, если не ко всем вариантам Unix.
Джонатан Леффлер,
12
+1 к комментарию @Taai - если вы имеете дело с символической ссылкой, которая разыменовывает (указывает на) каталог, вам нужно указать «-n», чтобы убедиться, что этот трюк работает.
Уолли
161

Да, ты можешь!

$ ln -sfn source_file_or_directory_name softlink_name
Taai
источник
9
Спасибо за ответ. В -fозначает опция «удалить существующий пункт назначения» , прежде чем создавать новый. Итак, эта команда достигает результата, но unlink(2)после выполнения команды symlink(2). Это не то, о чем исходный вопрос. Также обратите внимание, что принятый ответ и следующий по количеству голосов ответ используются ln -sfдля выполнения работы, но, как straceпоказывают выходные данные, это так unlink()и symlink()потому, что нет системного вызова для изменения символической ссылки.
Джонатан Леффлер
17
Кажется, что -n требуется, когда исходная ссылка ведет в каталог, а не в файл.
Steampowered
11
Переключатель "n" важен. Я боролся с проблемой, что символическая ссылка не была обновлена, но команда создала другую ссылку внутри существующей, потому что опция «no derefencing» не была установлена.
Джонатан Грубер
14

Нет необходимости явно отключать старую символическую ссылку. Ты можешь сделать это:

ln -s newtarget temp
mv temp mylink

(или используйте эквивалентные вызовы символической ссылки и переименования). Это лучше, чем явное отключение, потому что переименование является атомарным, поэтому вы можете быть уверены, что ссылка всегда будет указывать либо на старую, либо на новую цель. Однако это не приведет к повторному использованию исходного inode.

В некоторых файловых системах цель символической ссылки сохраняется в самом inode (вместо списка блоков), если он достаточно короткий; это определяется во время его создания.

Что касается утверждения о том, что фактический владелец и группа несущественны, символическая ссылка (7) в Linux говорит, что есть случай, когда это важно:

Владелец и группу существующей символической ссылки можно изменить с помощью lchown (2). Единственный раз, когда право собственности на символическую ссылку имеет значение, это когда ссылка удаляется или переименовывается в каталоге, в котором установлен бит закрепления (см. Stat (2)).

Отметки времени последнего доступа и последнего изменения символьной ссылки можно изменить с помощью utimensat (2) или lutimes (3).

В Linux разрешения символьной ссылки не используются ни в каких операциях; разрешения всегда равны 0777 (чтение, запись и выполнение для всех категорий пользователей) и не могут быть изменены.

mark4o
источник
@ mark4o: The Good - Ссылка на lchown () и владение каталогом sticky-it полезны - спасибо. Я знал о lchown (), и это не имело значения для моей диссертации. Я также знал о каталогах с липкими битами; Я думаю, что право собственности является почти стандартным следствием правил - разница и, предположительно, причина, по которой он вызывается, заключается в том, что обычно вы можете удалить файл, если можете писать в него, и номинально любой может писать в символическую ссылку, потому что из 777 разрешений, но в этом случае правила немного другие.
Джонатан Леффлер,
1
@ mark4o: The Not So Good - показанная последовательность команд не делает то, что вы думаете (по крайней мере, в сценарии :) set -x -e; mkdir junk; ( cd junk; mkdir olddir newdir; ln -s olddir mylink; ls -ilR; ln -s newdir temp; ls -ilR; mv temp mylink; ls -ilR; ); rm -fr junk. Если вы создадите файлы oldfile и newfile вместо каталогов и запустите эквивалентный сценарий (в основном меняя olddir на oldfile, newdir на newfile), то вы получите ожидаемый эффект. Всего одна дополнительная сложность! Спасибо за ответ.
Джонатан Леффлер,
2

Просто предупреждение к правильным ответам выше:

Использование метода -f / --force создает риск потери файла, если вы перепутаете источник и цель:

mbucher@server2:~/test$ ls -la
total 11448
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:27 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
-rw-r--r--  1 mbucher www-data 7582480 May 25 15:27 otherdata.tar.gz
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ln -s -f thesymlink otherdata.tar.gz 
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ls -la
total 4028
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:28 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
lrwxrwxrwx  1 mbucher www-data      10 May 25 15:28 otherdata.tar.gz -> thesymlink
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz

Конечно, это задумано, но обычно случаются ошибки. Итак, удаление и восстановление символической ссылки - это немного больше работы, но также немного экономия:

mbucher@server2:~/test$ rm thesymlink && ln -s thesymlink otherdata.tar.gz 
ln: creating symbolic link `otherdata.tar.gz': File exists

который, по крайней мере, хранит мой файл.

Маркус Бухер
источник
Использование опции -fили --forceвсегда немного опасно; предполагается, что пользователь знает, о чем они. Это вдвойне смертельно, если использовать его постоянно ( rm -fr …каждый раз опасно).
Джонатан Леффлер
1

Разве отключение его и создание нового в любом случае не приведет к тому же результату?

Мэтт Би
источник
2
Зачем вообще отключать связь? Почему бы просто не перезаписать его?
S.Lott
5
Конечный результат примерно такой же, но владелец, группа и время последнего изменения (и, вероятно, номер inode) в целом будут разными.
Джонатан Леффлер,
2
@ S.Lott: какой системный вызов выполняет перезапись? В POSIX такого вызова нет. В Solaris и MacOS X такого вызова нет. Есть ли призыв сделать это на ... Linux, AIX, HP-UX, на чем-нибудь еще, похожем на Unix? В Windows нет таких же символических ссылок, как AFAICT, поэтому для моего анализа это не критично, но информация об эквивалентном Windows API была бы полезна.
Джонатан Леффлер,
@Jonathan Leffler: Эммм .... ln --forceкоманда полностью перезапишет существующую ссылку.
S.Lott
Ребята, я думаю, вы говорите о противоречии. С.Лотт обсуждает исполняемый файл ln (1), а Мэтт Б. обсуждают тот факт , что symlink (2)вовсе не поддерживает перезапись. Вы оба правы, и я бы посоветовал взглянуть на реализацию, ln (1)чтобы получить наиболее идеальный способ выполнения процедуры отсоединения-повторного связывания.
dmckee --- котенок экс-модератора
0

На всякий случай поможет: есть способ отредактировать символическую ссылку с помощью midnight commander (mc). Команда меню (на французском языке в моем интерфейсе MC):

Fichier / Éditer le lien symbolique

который можно перевести на:

File / Edit symbolic link

Ярлык - Cx Cs

Может, он внутренне использует ln --forceкоманду, я не знаю.

Теперь я пытаюсь найти способ редактировать сразу множество символических ссылок (вот как я сюда попал).

пьер
источник
1
Вы проверили, что MC делает за кулисами? Я полагаю, что на самом деле он делает, за unlink()которым следует a symlink(), просто потому, что это то, что предоставляют Unix-подобные системы.
Джонатан Леффлер
Нет, не проверял. И да, вполне вероятно, что это действительно так, как вы говорите. Тот факт, что я редактирую текстовое поле, дает ощущение, что я на самом деле редактирую цель символической ссылки, но меня, вероятно, обманули ...
Пьер,
0

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

Вот небольшая функция bash / zsh, которую я написал для обновления существующей символической ссылки:

# -----------------------------------------
# Edit an existing symbolic link
#
# @1 = Name of symbolic link to edit
# @2 = Full destination path to update existing symlink with 
# -----------------------------------------
function edit-symlink () {
    if [ -z "$1" ]; then
        echo "Name of symbolic link you would like to edit:"
        read LINK
    else
        LINK="$1"
    fi
    LINKTMP="$LINK-tmp"
    if [ -z "$2" ]; then
        echo "Full destination path to update existing symlink with:"
        read DEST
    else
        DEST="$2"
    fi
    ln -s $DEST $LINKTMP
    rm $LINK
    mv $LINKTMP $LINK
    printf "Updated $LINK to point to new destination -> $DEST"
}
blizzrdof77
источник
2
Спасибо за ваши усилия. Это не соответствует моему требованию быть системным вызовом на языке C. Существует множество способов изменить ссылку, если у вас есть разрешение на запись в каталог, содержащий ссылку (что для этого требуется). Даже сейчас я не знаю способов изменить значение символической ссылки без выполнения unlink()и symlink()с новым значением, что и происходит за кулисами с вашим кодом. Лично я бы попросил, чтобы функция настаивала на двух (или, возможно, кратном двух) аргументах и ​​брала под залог, если вызов был неправильным. Однако это особая точка зрения.
Джонатан Леффлер,