5

Я намерен передать полные и инкрементные резервные копии моих подобъемов btrfs в службу архивации на магнитную ленту. Служба предоставляет конечные точки FTP и SSH. Если бы мне было разрешено выполнять произвольные команды на конечной точке SSH, я бы сделал следующее для инкрементного резервного копирования:

btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME | compress | encrypt |
    ssh -p $PORT $USER@$ENDPOINT "cat > $SUBVOLUME.$YYYYMMDD.btrfs.bz2.gpg"

Мне, однако, не разрешено делать это:

$ ssh -p $PORT $USER@$ENDPOINT
Last login: Mon Jan  3 01:23:45 2067 from 123.456.789.123

This account is restricted by rssh.
Allowed commands: scp sftp rsync 

If you believe this is in error, please contact your system administrator.

Connection to some.remote.endpoint closed.

Так что вместо этого я решил использовать протокол SCP для передачи. Однако мой двоичный файл scp отказывается передавать именованный канал:

$ scp -P $PORT <(btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME | compress | encrypt) \
    $USER@$ENDPOINT:$SUBVOLUME.$YYYYMMDD.btrfs.bz2.gpg
/dev/fd/63: not a regular file

Ирония в том, что, очевидно, scp когда-то делал правильные вещи. Полагаю, не все думали, что передача именованных каналов может быть вменяемой / полезной. РЕДАКТИРОВАТЬ (2017-06-03): Это было неправильное наблюдение. Как отмечает Кенстер, протокол SCP не позволяет отправлять файлы неизвестного размера.

ОБНОВЛЕНИЕ (2017-06-04): Я также попытался передать данные, используя протокол SFTP. По-видимому, протокол FTP позволяет отправлять файлы неизвестного размера, о чем свидетельствует поддержка передачи данных в двоичные файлы ftp (ссылка) и ncftpput (ссылка, описание раздела, последний абзац). Я не нашел такой поддержки в SFTP-клиентах, которые я пробовал (sftp , lftp), что может указывать на то, что SFTP (в отличие от FTP) не поддерживает отправку файлов неизвестного размера (в отличие от того, что я думал, SFTP не просто FTP-туннельный) через SSH; это другой протокол).

ОБНОВЛЕНИЕ (2017-06-05): Согласно интернет-проекту протокола SFTP версии 3 (ссылка), каждый пакет уровня приложения должен указывать длину полезной нагрузки перед полезной нагрузкой (ссылка, раздел 3). Однако SFTP поддерживает поиск в записанном файле (ссылка, раздел 6.4) с явной поддержкой записи за пределы текущего конца файла. Поэтому, если это возможно, можно использовать небольшой буфер на стороне клиента и отправлять файл неизвестного размера небольшими порциями известного размера:

#!/bin/bash
# <Exchange SSH_FXP_INIT requests.>
# <Send an SSH_FXP_OPEN request.>
CHUNK_SIZE=32768
OFFSET=0
IFS=''; while read -r -N $CHUNK_SIZE CHUNK; do
  ACTUAL_SIZE=`cat <<<"$CHUNK" | head -c -1 | wc -c`
  # <Send an SSH_FXP_WRITE request with payload $CHUNK of size
  # $ACTUAL_SIZE at offset $OFFSET.>
  OFFSET=$(($OFFSET+$ACTUAL_SIZE))
done < <(command)
# <Send an SSH_FXP_CLOSE request.>

Однако выполнение связи вручную через оболочку было бы довольно болезненным. Я ищу клиента SFTP, который предоставляет такую функциональность.

Рекомендации

3 ответа3

6

SCP не подходит для ваших целей. Протокол SCP не поддерживает отправку потока байтов неизвестного размера в удаленную систему для сохранения в виде файла. Сообщение протокола SCP для отправки файла требует, чтобы размер файла был отправлен первым, а затем байты, составляющие файл. С потоком байтов, считанных из канала, вы, как правило, не знаете, сколько байтов собирается создать канал, поэтому нет способа отправить сообщение протокола SCP, включая правильный размер.

(Единственное онлайн-описание протокола SCP, которое я смог найти, здесь и здесь. Сосредоточьтесь на описании сообщения "С".)

Протокол SFTP может быть использован для такого рода вещей. Насколько я знаю, нормальная sftp утилита командной строки не поддерживает чтение трубы и хранить его в качестве удаленного файла. Но есть библиотеки SSH/SFTP для большинства современных языков программирования (perl, python, ruby, C #, Java, C и т.д.). Если вы знаете, как использовать один из этих языков, написать программу, которая будет делать то, что вам нужно, не составит труда.

Если вы застряли в сценариях оболочки, возможно, достаточно подделать протокол SCP для передачи файла. Вот пример:

#!/bin/bash
cmd='cat /etc/group'

size=$($cmd | wc -c)    
{
        echo C0644 $size some-file
        $cmd
        echo -n -e '\000'
} | ssh user@host scp -v -p -t /some/directory

Это создаст some-file /some/directory на удаленной системе с разрешениями 644. Содержимое файла будет тем, что $cmd записывает в свой стандартный вывод. Обратите внимание, что вы запускаете команду дважды, с любым потреблением ресурсов и побочными эффектами, которые это подразумевает. И команда должна выводить одинаковое количество байтов каждый раз.

3

lftp 4.6.1 и новее должны быть в состоянии сделать это: https://github.com/lavv17/lftp/issues/104

К сожалению, команда, предложенная в связанной проблеме, не работает, но слегка измененная:

lftp -p $port sftp://$user:$pass@$host -e "put /dev/stdin -o $filename"

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

Эта команда читает из /dev/stdin . Если вы должны использовать другой именованный канал, я думаю, что это тоже должно сработать - если нет, вы всегда можете cat named-pipe | lftp ...

Я протестировал lftp с загрузкой> 100GB без каких-либо проблем.


rclone также поддерживает SFTP и должен иметь возможность загружать данные по каналу с помощью команды rcat , которая должна быть выпущена в 1.38 (следующая версия).

2

Если вы можете написать Perl, вы можете попробовать использовать Net::SFTP::Foreign, который может передавать файлы из дескриптора открытого файла через SFTP:

#!/usr/bin/perl
# untested!
use Net::SFTP::Foreign;
my $user = ...;
my $host = ...;
my $remote_path = ...;
my $btrfs_cmd = 'btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME';
my $sftp = Net::SFTP::Foreign->new("$user\@$host", ...);
open my($pipe), "$btrfs_cmd | compress | encrypt |"
    or die $!;
$sftp->put($fh, $remote_path);

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .