Puoi cambiare ciò a cui punta un collegamento simbolico dopo che è stato creato?


122

Qualche sistema operativo fornisce un meccanismo (chiamata di sistema - non programma da riga di comando) per modificare il percorso a cui fa riferimento un collegamento simbolico (collegamento simbolico), a parte scollegare quello vecchio e crearne uno nuovo?

Lo standard POSIX no. Solaris 10 no. MacOS X 10.5 (Leopard) no. (Sono abbastanza certo che né AIX né HP-UX lo abbiano . A giudicare da questo elenco di chiamate di sistema Linux, Linux non ha nemmeno una chiamata di sistema del genere.)

C'è qualcosa che fa?

(Mi aspetto che la risposta sia "No".)


Dato che provare un negativo è difficile, riorganizziamo la domanda.

Se sai che alcuni sistemi operativi (Unix-like) non già elencati non hanno una chiamata di sistema per riscrivere il valore di un collegamento simbolico (la stringa restituita da readlink()) senza rimuovere il vecchio collegamento simbolico e crearne uno nuovo, aggiungilo - o loro - in una risposta.


Cosa c'è di sbagliato nel ricollegare semplicemente? Perché non emettere semplicemente il lncomando (o l'API equiavalente) sovrascrivendo il vecchio collegamento? Che problema hai?
S.Lott,

9
Divertente: sto chiedendo se è presente una chiamata di sistema per eseguire un lavoro di programmazione e la domanda viene contrassegnata come "appartiene a un altro sito".
Jonathan Leffler,

3
Divertente: non era assolutamente chiaro che stavi cercando una chiamata di sistema e hai appena modificato la domanda per aggiungere questo dettaglio. Quindi come puoi aspettarti che le persone deducano qualcosa prima ancora che tu lo scriva?
Pascal Thivent,

2
@ S.Lott: Sto scrivendo un articolo sulla sicurezza e sui collegamenti simbolici. Ad un certo punto faccio l'affermazione "il proprietario effettivo, il gruppo, i permessi sul collegamento simbolico stesso sono irrilevanti" e il ragionamento è che il proprietario del collegamento simbolico può solo rimuoverlo e non modificare il valore. Sto ricontrollando che non ci sia altro che rimuovere il collegamento simbolico per ottenere l'effetto di "riscrivere il valore del collegamento simbolico". Sto ignorando l'accesso diretto al disco grezzo e hackerando l'FS in questo modo: richiede i privilegi di root e le mie preoccupazioni riguardano gli utenti non root, non quello che può fare root.
Jonathan Leffler,

4
@Pascal: Mi dispiace - non mi rendevo conto che non fosse chiaro che stavo parlando di chiamate di sistema fino a quando le persone non sono andate in tangente da quello che intendevo (che era evidentemente diverso da quello che ho detto). Mi dispiace di aver tratto in inganno; non è stato intenzionale.
Jonathan Leffler,

Risposte:


106

AFAIK, no, non puoi. Devi rimuoverlo e ricrearlo. In realtà, puoi sovrascrivere un collegamento simbolico e quindi aggiornare il percorso a cui fa riferimento:

$ 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

EDIT : come l'OP ha sottolineato in un commento, l'utilizzo --forcedell'opzione farà lneseguire una chiamata di sistema a unlink()prima symlink(). Di seguito, l'output di stracesulla mia macchina Linux che lo dimostra:

$ 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

Quindi immagino che la risposta finale sia "no".

EDIT : Quanto segue è copiato dalla risposta di Arto Bendiken su unix.stackexchange.com, circa 2016.

Questo può essere fatto in modo atomico rename(2), creando prima il nuovo collegamento simbolico con un nome temporaneo e quindi sovrascrivendo in modo pulito il vecchio collegamento in una volta sola. Come afferma la pagina man :

Se newpath fa riferimento a un collegamento simbolico, il collegamento verrà sovrascritto.

Nella shell, dovresti farlo mv -Tcome segue:

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

Puoi stracequell'ultimo comando per assicurarti che stia effettivamente usando rename(2)sotto il cofano:

$ 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

Nota che in quanto sopra, sia mv -Testrace sono specifici di Linux.

Su FreeBSD, usa mv -h alternativamente.

Nota del redattore: questo è il modo in cui Capistrano lo ha fatto da anni, da ~ 2.15. Vedi questa richiesta pull .


2
L'opzione '-f' non forza 'ln' a fare le chiamate di sistema unlink () e poi symlink ()?
Jonathan Leffler,

1
Lo fa. Ma la domanda potrebbe essere stata percepita come "come faccio a farlo in un passaggio" e poi si riduce alla definizione di "passaggio" - riga di comando? syscall?
Michael Krelin - hacker

1
Ho appena notato che hai aggiunto la formulazione syscall alla tua domanda ;-)
Michael Krelin - hacker

2
@Pascal: lo fa. Su Solaris, l'output di 'truss ln -spx' e 'truss ln -s -fpx' mostra una chiamata unlink () prima della chiamata symlink () nel secondo caso (e una chiamata symlink () fallita nel primo). Mi aspetto che si applichi alla maggior parte se non a tutte le varianti di Unix.
Jonathan Leffler

12
+1 al commento di @Taai - se si ha a che fare con un collegamento simbolico che dereferenzia (punta a) una directory, è necessario specificare "-n" per essere sicuri che questo trucco funzioni.
wally

161

Si, puoi!

$ ln -sfn source_file_or_directory_name softlink_name

9
Grazie per aver risposto. L' -fopzione significa "rimuovi la destinazione esistente" prima di creare quella nuova. Quindi, questo comando ottiene il risultato, ma facendo unlink(2)seguito da symlink(2). Non è questo l'argomento della domanda originale. Nota anche che la risposta accettata e la successiva risposta più votata usano entrambe ln -sfper fare il lavoro, ma come stracemostra l' output, lo fa unlink()e symlink()perché non c'è una chiamata di sistema per modificare un collegamento simbolico.
Jonathan Leffler

17
L'-n sembra essere richiesto quando il collegamento originale va a una directory invece che a un file.
Steampowered

11
L'opzione "n" è importante. Ho lottato con il problema che il collegamento simbolico non è stato aggiornato, ma il comando ha creato un altro collegamento all'interno di quello esistente perché l'opzione "no derefencing" non era impostata.
Jonathan Gruber

14

Non è necessario scollegare esplicitamente il vecchio collegamento simbolico. Puoi farlo:

ln -s newtarget temp
mv temp mylink

(o usa il collegamento simbolico equivalente e rinomina le chiamate). È meglio che scollegare esplicitamente perché la ridenominazione è atomica, quindi puoi essere certo che il collegamento punterà sempre al vecchio o al nuovo target. Tuttavia questo non riutilizzerà l'inode originale.

Su alcuni filesystem, la destinazione del collegamento simbolico è memorizzata nell'inode stesso (al posto dell'elenco dei blocchi) se è abbastanza breve; questo è determinato al momento della creazione.

Per quanto riguarda l'affermazione che il proprietario e il gruppo effettivi sono irrilevanti, symlink (7) su Linux dice che c'è un caso in cui è significativo:

Il proprietario e il gruppo di un collegamento simbolico esistente possono essere modificati utilizzando lchown (2). L'unico caso in cui la proprietà di un collegamento simbolico è importante è quando il collegamento viene rimosso o rinominato in una directory in cui è impostato lo sticky bit (vedere stat (2)).

I timestamp dell'ultimo accesso e dell'ultima modifica di un collegamento simbolico possono essere modificati utilizzando utimensat (2) o lutimes (3).

Su Linux, i permessi di un collegamento simbolico non vengono utilizzati in nessuna operazione; i permessi sono sempre 0777 (lettura, scrittura ed esecuzione per tutte le categorie utente) e non possono essere modificati.


@ mark4o: The Good - Il riferimento a lchown () e alla proprietà in una directory sticky-it è utile - grazie. Ero a conoscenza di lchown () e non era materiale per la mia tesi. Ero anche a conoscenza delle directory sticky-bit; Penso che la proprietà sia quasi una conseguenza standard delle regole - la differenza, e presumibilmente il motivo per cui viene richiamata, è che normalmente puoi rimuovere un file se puoi scriverci e, nominalmente, chiunque può scrivere su un collegamento simbolico perché delle autorizzazioni 777, ma in questo caso le regole sono leggermente diverse.
Jonathan Leffler

1
@ mark4o: The Not So Good - la sequenza di comandi mostrata non fa quello che pensi che faccia (almeno, nello scenario :) 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. Se crei file oldfile e newfile invece di directory ed esegui lo script equivalente (principalmente cambiando olddir in oldfile, newdir in newfile), otterrai l'effetto che ti aspettavi. Solo una complessità in più! Grazie per la risposta.
Jonathan Leffler,

2

Solo un avvertimento per le risposte corrette sopra:

L'uso del metodo -f / --force comporta il rischio di perdere il file se si confondono origine e destinazione:

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

Ovviamente questo è inteso, ma di solito si verificano degli errori. Quindi, eliminare e ricostruire il collegamento simbolico è un po 'più laborioso ma anche un po' più economico:

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

che almeno conserva il mio file.


Usare un'opzione -fo --forceè sempre un po 'pericoloso; presume che l'utente sappia di cosa si tratta. È doppiamente letale se lo usi abitualmente ( rm -fr …ogni volta è pericoloso).
Jonathan Leffler

1

Scollegarlo e creare quello nuovo non farebbe comunque la stessa cosa alla fine?


2
Perché scollegarsi in primo luogo? Perché non sovrascriverlo semplicemente?
S.Lott,

5
Il risultato netto è approssimativamente lo stesso, ma il proprietario e il gruppo e l'ora dell'ultima modifica (e probabilmente il numero di inode) sarebbero tutti diversi, in generale.
Jonathan Leffler,

2
@ S.Lott: quale chiamata di sistema esegue la "sovrascrittura"? In POSIX, non esiste tale chiamata. In Solaris e MacOS X, non esiste tale chiamata. C'è una chiamata per farlo su ... Linux, AIX, HP-UX, qualsiasi altra cosa che assomigli a Unix? Windows non ha realmente collegamenti simbolici allo stesso modo, AFAICT, quindi non è fondamentale per la mia analisi, ma sarebbero utili le informazioni sull'API di Windows equivalente.
Jonathan Leffler,

@ Jonathan Leffler: Ummm .... il ln --forcecomando sovrascriverà assolutamente un collegamento esistente.
S.Lott

Ragazzi, penso che stiate parlando per scopi incrociati. S.Lott sta discutendo di un eseguibile ln (1), mentre matt b. sta discutendo il fatto che symlink (2)non non supporta la sovrascrittura. Avete entrambi ragione e suggerirei che esaminare l'implementazione di ln (1)darebbe il modo più idomatico di eseguire la procedura di scollegamento-ricollegamento.
dmckee --- gattino ex moderatore,

0

Nel caso in cui aiuti: c'è un modo per modificare un collegamento simbolico con midnight commander (mc). Il comando di menu è (in francese sulla mia interfaccia mc):

Fichier / Éditer le lien symbolique

che può essere tradotto in:

File / Edit symbolic link

La scorciatoia è Cx Cs

Forse utilizza internamente l'estensione ln --force comando, non lo so.

Ora, sto cercando di trovare un modo per modificare un sacco di collegamenti simbolici contemporaneamente (è così che sono arrivato qui).


1
Hai verificato cosa fa MC dietro le quinte? Ci sono buone probabilità che in effetti faccia un unlink()seguito da a symlink(), semplicemente perché è quello che forniscono i sistemi Unix.
Jonathan Leffler

No, non ho controllato. E sì, è abbastanza probabile che faccia come dici tu, infatti Il fatto che modifico una casella di testo dà la sensazione che sto effettivamente modificando l'obiettivo del collegamento simbolico, ma probabilmente sono stato ingannato ...
Pierre

0

Tecnicamente, non esiste un comando integrato per modificare un collegamento simbolico esistente. Può essere facilmente ottenuto con pochi brevi comandi.

Ecco una piccola funzione bash / zsh che ho scritto per aggiornare un collegamento simbolico esistente:

# -----------------------------------------
# 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
Grazie per lo sforzo. Questo non soddisfa il mio requisito di essere una chiamata di sistema in linguaggio C. Ci sono molti modi per modificare un collegamento purché si disponga dell'autorizzazione di scrittura sulla directory che contiene il collegamento (cosa che ciò richiede). Non ci sono modi che io sappia, anche adesso, per cambiare il valore di un collegamento simbolico senza farlo unlink()e symlink()con il nuovo valore, che è ciò che accade dietro le quinte con il tuo codice. Personalmente, farei in modo che la funzione insista esattamente su due (o forse un multiplo di due) argomenti e salvi se l'invocazione non fosse corretta. Questa è una prospettiva particolare, però.
Jonathan Leffler
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.