Чи можете ви змінити, на що вказує символьне посилання після його створення?


122

Чи надає будь-яка операційна система механізм (системний виклик - не програма командного рядка) для зміни імені шляху, на який посилається символічне посилання (symlink), - крім від'єднання старого та створення нового?

Стандарт POSIX цього не робить. Solaris 10 - ні. MacOS X 10.5 (Leopard) ні. (Я терпимо впевнений, що не робить ні AIX, ні HP-UX. Судячи з цього списку системних викликів Linux, у Linux також немає такого системного виклику.)

Чи є щось, що робить?

(Я очікую, що відповідь "Ні".)


Оскільки довести негатив важко, давайте реорганізуємо питання.

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


Що поганого в тому, щоб просто перекинутися? Чому б просто не видати lnкоманду (або еквівалент API), перезаписавши старе посилання? Яка у вас проблема?
S.Lott

9
Смішно - я запитую, чи є системний виклик для виконання програми програмування, і питання позначається "належить на іншому сайті".
Джонатан Леффлер

3
Смішно - абсолютно не зрозуміло, що ви шукаєте системний дзвінок, і ви просто відредагували питання, щоб додати цю деталь. Тож як ти можеш розраховувати, що люди щось вирахують, перш ніж ти це навіть напишеш?
Паскаль Thivent

2
@ S.Lott: Я пишу статтю про безпеку та символічні посилання. В один момент я роблю твердження "фактичний власник, група, дозволи на самій симпосилання неістотні", і міркуємо, що власник симпосилання може лише видалити його, а не змінити значення. Я двічі перевіряю, що немає іншого способу, як видалити символьне посилання для досягнення ефекту "переписання значення символьної посилання". Я ігнорую прямий доступ до неочищеного диска і таким чином зломлюю FS - для цього потрібні права доступу до 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

РЕДАКТУВАННЯ : Як в коментарі вказано в ОП, за допомогою цього --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

Тому я здогадуюсь, що остаточна відповідь - «ні».

EDIT : Далі скопійовано з відповіді Арто Бендікена на 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", щоб переконатися, що цей трюк працює.
wally

161

Так, ти можеш!

$ ln -sfn source_file_or_directory_name softlink_name

9
Дякую за відгук. В -fозначає опція «видалити існуючий пункт призначення» , перш ніж створювати новий. Отже, ця команда досягає результату, але виконуючи unlink(2)наступні дії symlink(2). Це не те, про що було в первісному питанні. Також зауважте, що прийнята відповідь та наступна найбільш проголосована відповідь обидва використовують ln -sfдля виконання завдання, але як straceпоказує результат, він це робить unlink()і symlink()тому, що немає системного виклику, щоб змінити симпосилання.
Джонатан Леффлер

17
Здається, -n потрібен, коли вихідне посилання переходить до каталогу замість файлу.
підключений парою

11
Перемикач 'n' важливий. Я боровся з проблемою, що симпосилання не оновлювалося, але команда створила ще одне посилання всередині існуючого, оскільки параметр "без розмежування" не був встановлений.
Джонатан Грубер

14

Не потрібно явно відключати старе символьне посилання. Ви можете зробити це:

ln -s newtarget temp
mv temp mylink

(або використовувати еквівалентні символьні посилання та перейменувати виклики). Це краще, ніж явне від’єднання, оскільки перейменування є атомним, тому ви можете бути впевнені, що посилання завжди вказуватиме на стару або нову ціль. Однак це не повторно використовувати вихідний inode.

У деяких файлових системах ціль символьної посилання зберігається у самій inode (замість списку блоків), якщо вона є достатньо короткою; це визначається в момент його створення.

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

Власника та групу існуючого символічного посилання можна змінити за допомогою lchown (2). Єдине значення, коли право власності на символічне посилання має значення, - це коли видалення або перейменування посилання в каталог, у якому встановлений клейкий біт (див. Stat (2)).

Останні часові позначки доступу та останньої модифікації символічного посилання можуть бути змінені за допомогою utimensat (2) або lutimes (3).

У Linux права доступу символічного посилання не використовуються в жодних операціях; дозволи завжди 0777 (читати, записувати та виконувати для всіх категорій користувачів), і їх неможливо змінити.


@ mark4o: The Good - Посилання на lchown () та право власності в довідковому каталозі корисно - дякую. Я знав про lchown (), і це не було суттєвим для моєї тези. Я також знав про клейкі каталоги; Я думаю, що право власності - це майже стандартний наслідок правил - відмінність, і, мабуть, чому це викликано, полягає в тому, що зазвичай ви можете видалити файл, якщо зможете записати на нього, і номінально кожен може написати на симпосилання, оскільки з 777 дозволів, але в цьому випадку правила дещо відрізняються.
Джонатан Леффлер

1
@ mark4o: Не так добре - показана послідовність команд не робить те, що ви думаєте, що це робить (принаймні, у сценарії:) 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
Чистий результат приблизно однаковий - але власник та група та останні останні модифіковані часи (і, мабуть, номер індексу) взагалі були б різними.
Джонатан Леффлер

2
@ S.Lott: який системний виклик виконує "перезапис"? У POSIX такого дзвінка немає. У Solaris та MacOS X такого дзвінка немає. Чи є заклик зробити це на Linux, AIX, HP-UX, що-небудь ще схоже на Unix? Windows насправді не має подібних посилань аналогічно AFAICT, тому це не є критично важливим для мого аналізу - але інформація про еквівалентний API Windows була б корисною.
Джонатан Леффлер

@Jonathan Leffler: Ummm .... ln --forceкоманда абсолютно замінить існуюче посилання.
С.Лотт

Хлопці, я думаю, ви розмовляєте між собою. С.Лотт обговорює виконуваний файл ln (1), а matt b. обговорюють той факт , що symlink (2)зовсім НЕ підтримує перезапис. Ви обидва вірні, і я б запропонував, щоб перегляд виконання ln (1)було б самим ідентичним способом виконання процедури відключення і повторної посилання.
dmckee --- кошеня колишнього модератора,

0

Про всяк випадок це допомагає: є спосіб редагувати симпосилання з командиром опівночі (mc). Команда меню є (французькою моєю mc-інтерфейсом):

Fichier / Éditer le lien symbolique

що може бути перекладено на:

File / Edit symbolic link

Ярлик - це Cx Cs

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

Зараз я намагаюся знайти спосіб редагувати одразу цілу кількість посилань (ось як я приїхав сюди).


1
Ви перевірили, що MC робить за кадром? Я маю шанси на те, що насправді це unlink()супроводжує 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"
}

2
Дякую за старання. Це не відповідає моїй вимозі бути викликом системної мови С. Існує чималий спосіб змінити посилання, якщо у вас є дозвіл на запис у каталог, що містить посилання (для чого це потрібно). Там час з методи , які я знаю, навіть зараз, щоб змінити значення символьного посилання , не роблячи unlink()і symlink()з новим значенням, яке є те , що відбувається за лаштунками з вашим кодом. Особисто я мав би функцію наполягати на точно двох (а може бути, кратних на два) аргументах і під заставу, якщо виклик був невірним. Але це особлива перспектива.
Джонатан Леффлер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.