Como alguém altera atomicamente um link simbólico para um diretório no busybox?

18

Estou tentando (o mais próximo possível) alterar atomicamente um link simbólico. Eu tentei:

ln -sf other_dir existing_symlink

Isso acabou de colocar o novo link simbólico no diretório que o existente_symlink apontou.

ln -sf other_dir new_symlink
mv -f new_symlink existing_symlink

Isso fez a mesma coisa: moveu o link simbólico para o diretório.

cp -s other_dir existing_symlink

Recusa porque é um diretório.

Eu li que isso mv -Tfoi feito para isso, mas o busybox não tem a -Tbandeira.

Shawn J. Goff
fonte

Respostas:

1

Não vejo como você pode obter operação atômica. A página de manual para symlink(2)indica EEXISTse o destino já existe. Se o kernel não suportar operação atômica, suas limitações na área do usuário são irrelevantes.

Também não vejo como mv -Tajuda, mesmo que você a tenha. Experimente em uma caixa Linux normal, uma com GNU mv:

$ mkdir a b
$ ln -s a z
$ mv -T b z
mv: cannot overwrite non-directory `z' with directory `b'

Acho que você precisará fazer isso em duas etapas: remova o link simbólico antigo e recrie-o.

Warren Young
fonte
11
De fato, infelizmente não há como você modificar um simbólico atomicamente. O melhor que você pode fazer é remover o link antigo e criar um novo. O GNU coreutils tem uma opção para fazer isso com um único comando ( ln -snf), mas ainda existem duas chamadas de sistema ocultas.
Gilles 'SO- stop be evil'
43

Na verdade, isso pode ser feito atomicamente rename(2), criando primeiro o novo link simbólico com um nome temporário e depois substituindo de maneira limpa o link simbólico antigo de uma só vez. Como a página de manual afirma:

Se newpath se referir a um link simbólico, o link será substituído.

No shell, você faria isso da mv -Tseguinte maneira:

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

Você pode straceexecutar esse último comando para garantir que ele esteja realmente sendo usado por rename(2)baixo do capô:

$ 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

Observe que, acima, ambos mv -Te stracesão específicos do Linux.

No FreeBSD, use mv -halternadamente.

Arto Bendiken
fonte
11
Muito agradável! Pode ser útil dizer que, no final, você tem um link de z para b!
Vincenzo Pii
Para uma solução independente do SO, use uma linguagem de script capaz de usar o renamesyscall diretamente em vez de mv -hou mv -T. Por exemplo, com Perl:perl -e 'rename "z.new", "z" or die $!'
Slaven Rezic 04/04/19
8

Escolhendo de onde Arto parou aqui, isso é totalmente possível, mesmo sem mv -T, você só precisa criar um novo link simbólico com o mesmo nome que o diretório de destino e mvno diretório pai do seu destino:

mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./

Exemplo de código obtido por meio de ( http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/ )

mssaxm
fonte
3

Você já tentou ln -snf?

A opção -nsubstitui o destino em vez de escrevê-lo quando o destino é um link simbólico para um diretório.

Felicidades

sokai
fonte
3
ln -snfnão é atômico: ele desvincula o destino e cria o link simbólico desejado.
Gilles 'SO- stop be evil'
2
Dado que o OP estava interessado em "o mais próximo possível" de alterar atomicamente um link simbólico, essa é uma resposta perfeitamente razoável. Se houver um melhor que possa se aproximar (ou ser) atômico, esse poderá ser aceito. Eu não acho que há uma necessidade de voto negativo.
Wilco