среда, 29 октября 2014 г.

Пакетная запись на dvd/cd-rw в linux

Существует два способа дозаписи на dvd-r и cd-r - это мультисессия и пакетная
запись. В первом случае можно использовать утилиту growisofs:
$growisofs -Z /dev/dvd -JRV "Диск 1" file1 file2 ...
здесь -J - использовать расширение jolie для отображение юникода и длинных имён
файлов, -R - RockRidge для сохранения атрибутов posix, -V - метка тома,
последние параметры - список файлов и папок.
Диск запишется, в дальнейшем для дозаписи нужно следующее:
$growisofs -M /dev/dvd -JRV "Диск 1" file3 "file 4" ...
т.е. меняется один параметр - -Z на -M, в первом случае диск инициализируется и
записывается первая сессия, во втором - записываются последующие сессии.
Подробнее можно узнать в man growisofs.
Данная утилита может иметь другие названия в разных дистрибутивах, в Debian -
growisofs.
Перейдём ко второму случаю, он заключается в том, чтобы отформатировать диск в
файловую систему udf, а потом использовать его как флешку, т.е. записывать файлы
сразу напрямую по отдельности, также можно удалять файлы. Для этого подходит
любая болванка, даже не перезаписываемая, типа dvd-r, но в этом случае при
удалении файлов место на диске освобождаться не будет. Для работы требуется
пакет udftools, который есть в репозитории Debian, он достаточно популярен,
поэтому должен быть и в других дистрибутивов. И так, необходимы следующие
действия:

1. Подготавливаем диск.

Для dvd-rw и bd-re:
$dvd+rw-format -force /dev/dvd
Для cd-rw:
$cdrwtools -q -d /dev/cdrw
Для dvd-r и cd-r пропускаем этот шаг.

2. Форматируем в файловую систему udf

$mkudffs -r 0x0150 --media-type=dvdrw /dev/dvd
Для cd-rw media-type будет cdrw, для dvd-r - dvd и т.д.
-r означает версию udf, если не требуется ничего особого, то лучше поставить
версию поменьше (1.5 хватит для любых целей) для гарантии, что диск будет
читаться на всех ОС. Можно без этой опции, тогда версия будет 2.01.
В файле README из документации пакета udftool написано, что нужно использовать
утилиту pktsetup и использовать вместо /dev/dvd (или /dev/sr0) что-то вроде
/dev/pktcdvd/0, но у меня из-за этого были жуткие тормоза, диск записывался
полностью аж за 10 часов. Этот способ можно использовать, если не получилось
напрямую через /dev/dvd:
$pktsetup 0 /dev/dvd
$mkudffs /dev/pktcdvd/0
В дальнейшем работаем уже с этим устройством.

3. Монтируем файловую систему и работем с ней

$mount -t udf -o rw,noatime /dev/dvd /media/dvd0
noatime нужно, чтобы при чтении не обновлялось время доступа к файлам. Это
сохранит ресурс rw диска и ускорит работу за счёт уменьшения количества
операций.

4. Завершаем работу

После завершения работы сбрасываем кэш файловой системы, отмонтируем её и
вытаскиваем диск:
$sync
$umount /media/dvd0
$eject
Хотя всё выполнится автоматически, если нажать кнопку на dvd-приводе, правда
придётся подождать, т.к. операции могут занять достаточно длительное время.

Дополнительно

Если в вашей системе не разрешены вышеприведённые команды на выполнение
обычному пользователю, то следует их запускать от имени суперпользователя (кроме
команды sync).
Данный способ записи плохо подходит, если требуется забить диск до отказа
мелкими файлами, т.е. производительность очень сильно страдает от этого, и
вместо 15 минут записи образа на dvd-rw, операция может занять несколько часов.
Даже при записи одного большого файла скорость записи dvd-rw 4x снижается до 3x.
Поэтому данный способ подходит, когда требуется регулярно дописывать на диск
небольшой объём данных, например при инкрементальном резервном копировании.
А вообще, если не требуется, чтобы диск читался на windows и других системах,
можно использовать любой тип файловой системы, например ext2 (желательно, чтоб
ФС не была журналируемой). Разумеется, в этом случае подойдёт только
перезаписываемый диск. Просто вместо mkudffs используем
$mkfs.ext2 /dev/dvd
Правда ждать придётся ооочень долго… Поэтому лучше сделать так:
Создаём образ (я обычно использую разряжённый файл):
$dd if=/dev/null bs=4700MB seek=1 of=dvd.img
Форматируем его:
$mkfs.ext2 dvd.img
Записываем:
$growisofs -Z /dev/dvd=dvd.img
Монтируем диск:
$mount /dev/dvd /media/dvd0
Ну и работаем, по производительности ничего не могу сказать, т.к. делал это
только в качестве эксперимента.
Также у меня не было возможности проверить работу на dvd-ram и bluray, но
отличия, скорее всего в способе очистки и опции media-type при форматировании.

суббота, 11 октября 2014 г.

Инкрементальный бэкап в линукс

Зачастую требуется резервное копирование большого количество данных, в которых
изменяется лишь незначительная их доля. Для этой цели подходит инкрементальный
бэкап, т.е. делается один полный бэкап, а потом записываются лишь изменения в
резервируемых данных.
Я не буду здесь рассматривать все существующие способы инкрементального
резервного копирования, приведу лишь 3, которыми я пользовался.

rdiff-backup

Самое просто решение, просто указываем две папки - откуда копировать и куда
копировать. Данная утилита определяет изменения в файловой системе и записывает
лишь их. В дальнейшем можно восстановить не только последнюю копию, но и все
копии, которые были сделаны до этого. Также при помощи rdiff-backup-fs можно
смонтировать каталог с резервной копией через fuse и получить виртаульную
файловую систему с папками, соответствующими времени копирования, в которых
будут находится файлы, скопированные в это время.
На данный момент утилита имеется в репозиториях большинства популярных
дистрибутивов, поэтому установка не должна составить трудности.

Примеры

Простое резервное копирование папки /home:
$ rdiff-backup /home/ /backup/
Резервное копирование определённых папок:
$ rdiff-backup --globbing-file-list lits.txt /home/user/ /backup/
Пример содержания файла list.txt:
+ /home/user/.asoundrc
+ /home/user/.bashrc
- /home/user/bin/netbeans-7.3.1
- /home/user/bin/eclipse
- /home/user/bin/eclipse-cpp
+ /home/user/bin
+ /home/user/Books
+ /home/user/.config
+ /home/user/databases
+ /home/user/.gconf
+ /home/user/.gconfd
+ /home/user/.local/share/gtg
+ /home/user/.local/share/gnote
+ /home/user/.local/share/Psi+
+ /home/user/.profile
+ /home/user/Projects
+ /home/user/.psi
+ /home/user/.purple
+ /home/user/list.txt
+ /home/user/scripts
+ /home/user/.Skype
+ /home/user/.ssh
+ /home/user/.vim
+ /home/user/.vimrc
+ /home/user/vimwiki
+ /home/user/Документы
+ /home/user/Рабочий стол
- **
+ означает добавить файл или директорию, - - убрать из списка резервного
копирования. В данном примере добавляем определённые каталоги, при этом из папки
bin исключаем некоторые папки, которые резервировать не нужно.
Вместо файла со списком можно использовать ключи --include, --include-regexp
и прочие, подробнее в man rdiff-backup.

Особенности

Можно выделить плюсы и минусы данного метода.
Плюсы:
  • можно получить доступ к любой версии резервируемых файлов;
  • можно удалять старые бэкапы.
Минусы:
  • резервная копия хранится в виде множества файлов, которые с каждым бэкапом
    обновляются до текущего состояния (из-за этого нет возможности дописывать
    бэкап на dvd-r, например).

Incremental tar

Архиватор tar, который содержится в любом дистрибутиве, умеет делать
инкрементальные архивы, если указать опцию -g с указанием файла снапшота.

Примеры использования

Создаём полный бэкап домашней папки:
$tar -cvjf /backup/home.tar.bz2 -g /backup/home.snapshot -C /home/user/ .
Делаем следующий бэкап (например, через неделю):
$tar -cvjf /backup/home.1.tar.bz2 -g /backup/home.snapshot -C /home/user/ .
Первая команда создаёт стандартный архив tar и снапшот состояния файловой
системы на момент бэкапа. Вторая команда читает этот снапшот и архивирует только
изменённые файлы.
Желательно, создавать свой файл снапшота для каждого архива, т.к. он каждый раз
обновляется. Это позволит делать инкрементальный бэкап не только от предыдущего,
но и от первого архива, т.е. создать дифференциальный бэкап.

Недостатки

Существует один недостаток - это то, как tar определяет, какие файлы изменились.
Он проверяет файлы по inode, сверяет атрибуты файла, если они изменились, то
добавляет файл в архив. Вроде бы всё хорошо, но некоторые файловые системы имеют
свойство менять inode файлов для оптимизации или просто не имеют таковых, а ядро
при монтировании назначает каждый раз произвольные значения. Таким образом, при
изменении inode всех файлов, получается полный бэкап. Я с этим столкнулся на
файловой системе btrfs. Также вообще не получится сделать подобное на fat.

Tar + rdiff

Это такой гибридный способ на основе предыдущих, я стал его использовать в
последнее время. Принцип состоит в следующем:
Создание первого бэкапа:
  1. Создаём полный архив tar без сжатия:
    $tar -cvf backup.tar /home
  2. Создаём файл signature с помощью rdiff:
    $rdiff signature backup.tar backup.signature
  3. Архив можно сжать для экономии места.
Создание следующих бэкапов:
  1. Создаём полный архив tar без сжатия.
  2. Создаём файл signature с помощью rdiff для этого архива, например:
    $rdiff signature backup1.tar backup1.signature
  3. Создаём файл rdiff - разницу между предыдущим и текущим архивами:
    $rdiff delta backup.signature backup1.tar backup1.rdiff
  4. Можно удалить backup1.tar
Как видно, для создания инкрементального архива не требуется наличие первой
полной копии. Как восстановить последний архив:
$rdiff patch backup.tar bakup1.rdiff backup1.tar
Если бэкапов несколько, то восстановление осуществляется по цепочке.
Сжатие архива и создание сигнатуры для первого архива, а также создание разницы
и сигнатуры для последующих архивов можно выполнить за один шаг, не создавая
несжатые файлы с полным архивом, для этого можно использовать именованные
каналы. Привожу пример скрипта для архивации (для понятности добавил
комментарии):
#!/bin/bash
prefix=all
dt=`date +%Y-%m-%d`
srcsignature="$1"
#Проверка вида бэкапа
if [ -e "$srcsignature" -a -n "$srcsignature"]
then
    ext="rdiff.xz"
else
    ext="tar.xz"
fi
sourcefile=$prefix.$ext
endfile=$prefix.$dt.$ext
sourcesignature=$prefix.signature
endsignature=$prefix.$dt.signature

#Создаём именованные каналы
mkfifo for_signature for_delta

#Создание сигнатуры
rdiff signature for_signature $sourcesignature &
if [ -e "$srcsignature" -a -n "$srcsignature"]
then
    #Создание дельты
    rdiff delta $srcsignature for_delta | xz -zc > $sourcefile &
else
    #Сжатие полного архива
    xz -zc for_delta > $sourcefile &
fi

#Архивация (исправить для использования)
tar -cvf - -C ~/ \
scripts/ \
Документы/ \
Projects/ \
vimwiki/ \
.local/share/gnote/ |\
tee for_signature for_delta > /dev/null

#На всякий случай ждём окончания действий
sleep 10
sync
#Переименовываем файлы
mv $sourcefile $endfile
mv $sourcesignature $endsignature

#Удаляем каналы
rm for_signature for_delta
В качестве единственного параметра этого скрипта выступает файл сигнатуры
предыдущего бэкапа. Если файл не существует, или запуск производился без
параметров, делается полный бэкап. Сам список задаётся в команде tar, там же
можно указать опции архивации.
Есть один недостаток - архивирование выполняется долго, т.к. по сути создаётся
полный архив, но оно того стоит, т.к. в этом случае, в отличие от
инкрементального копирования средствами tar, получаются файлы меньшего размера.
Дело в том, что rdiff находит разницу между бинарными файлами, а в случае с
архивами - между содержанием архивов. Т.е. если изменится незначительно большой
файл, то при помощи rdiff будут записаны только изменения этого файла, а при
помощи tar будет перезаписан весь файл полностью.

Итог

Всё зависит от способа хранения резервных копий, если записывать инкрементальные
архивы на dvd-r, то подойдут 2 и 3 способы, а первый подойдёт только в случае
копирования на внешний винчестер или на другой компьютер по сети.
P.S.:
Кстати, rdiff-backup, использует rdiff для хранения изменений в файлах, хотя об
этом можно догадаться и из названия.
P.P.S.:
Библиотека rdiff также используется в rsync.