Gracias a @ mark4o publicando sobre linkat(2)
, vea su respuesta para más detalles.
Quería probarlo para ver qué sucedió realmente al intentar vincular un archivo anónimo al sistema de archivos en el que está almacenado. (a menudo /tmp
, por ejemplo, para datos de video que Firefox está reproduciendo).
A partir de Linux 3.16, parece que todavía no hay forma de recuperar un archivo eliminado que aún se mantiene abierto. Ni AT_SYMLINK_FOLLOW
ni AT_EMPTY_PATH
para linkat(2)
hacen el truco para los archivos eliminados que solían tener un nombre, incluso como raíz.
La única alternativa es tail -c +1 -f /proc/19044/fd/1 > data.recov
, que hace una copia por separado, y tiene que eliminarla manualmente cuando termina.
Aquí está la envoltura de perl que preparé para la prueba. Use strace -eopen,linkat linkat.pl - </proc/.../fd/123 newname
para verificar que su sistema aún no pueda recuperar los archivos abiertos. (Lo mismo aplica incluso con sudo
). Obviamente, debería leer el código que encuentra en Internet antes de ejecutarlo, o usar una cuenta de espacio aislado.
#!/usr/bin/perl -w
# 2015 Peter Cordes <[email protected]>
# public domain. If it breaks, you get to keep both pieces. Share and enjoy
# Linux-only linkat(2) wrapper (opens "." to get a directory FD for relative paths)
if ($#ARGV != 1) {
print "wrong number of args. Usage:\n";
print "linkat old new \t# will use AT_SYMLINK_FOLLOW\n";
print "linkat - <old new\t# to use the AT_EMPTY_PATH flag (requires root, and still doesn't re-link arbitrary files)\n";
exit(1);
}
# use POSIX qw(linkat AT_EMPTY_PATH AT_SYMLINK_FOLLOW); #nope, not even POSIX linkat is there
require 'syscall.ph';
use Errno;
# /usr/include/linux/fcntl.h
# #define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */
# #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
# #define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */
unless (defined &AT_SYMLINK_NOFOLLOW) { sub AT_SYMLINK_NOFOLLOW() { 0x0100 } }
unless (defined &AT_SYMLINK_FOLLOW ) { sub AT_SYMLINK_FOLLOW () { 0x0400 } }
unless (defined &AT_EMPTY_PATH ) { sub AT_EMPTY_PATH () { 0x1000 } }
sub my_linkat ($$$$$) {
# tmp copies: perl doesn't know that the string args won't be modified.
my ($oldp, $newp, $flags) = ($_[1], $_[3], $_[4]);
return !syscall(&SYS_linkat, fileno($_[0]), $oldp, fileno($_[2]), $newp, $flags);
}
sub linkat_dotpaths ($$$) {
open(DOTFD, ".") or die "open . $!";
my $ret = my_linkat(DOTFD, $_[0], DOTFD, $_[1], $_[2]);
close DOTFD;
return $ret;
}
sub link_stdin ($) {
my ($newp,) = @_;
open(DOTFD, ".") or die "open . $!";
my $ret = my_linkat(0, "", DOTFD, $newp, &AT_EMPTY_PATH);
close DOTFD;
return $ret;
}
sub linkat_follow_dotpaths ($$) {
return linkat_dotpaths($_[0], $_[1], &AT_SYMLINK_FOLLOW);
}
## main
my $oldp = $ARGV[0];
my $newp = $ARGV[1];
# link($oldp, $newp) or die "$!";
# my_linkat(fileno(DIRFD), $oldp, fileno(DIRFD), $newp, AT_SYMLINK_FOLLOW) or die "$!";
if ($oldp eq '-') {
print "linking stdin to '$newp'. You will get ENOENT without root (or CAP_DAC_READ_SEARCH). Even then doesn't work when links=0\n";
$ret = link_stdin($newp);
} else {
$ret = linkat_follow_dotpaths($oldp, $newp);
}
# either way, you still can't re-link deleted files (tested Linux 3.16 and 4.2).
# print STDERR
die "error: linkat: $!.\n" . ($!{ENOENT} ? "ENOENT is the error you get when trying to re-link a deleted file\n" : '') unless $ret;
# if you want to see exactly what happened, run
# strace -eopen,linkat linkat.pl
Ta. Él propone una solución que también debería funcionar, fíjate. Aunque para la pulcritud compulsiva completa probablemente también necesites una forma de llamar a creat() en un directorio para que cree el archivo y el inodo pero no la entrada del directorio, de modo que nunca se vincule en primer lugar. – ijw
La actualización está llena de victoria. No puedo +2 pero lo haría si pudiera. – ijw
Confusamente, 'linkat()' da 'ENOENT' en intentos de volver a adjuntar un archivo normal abierto pero no vinculado. (con 'AT_SYMLINK_FOLLOW' o 'AT_EMPTY_PATH') –