--- /dev/null
+# Подготовка окружения для исследуемого процесса с испольнованием btrfs
+
+**Update:** По состоянию на 2025 год, коллеги из БазАльта предложили более совершенный способ переключения между снепшотами, основанный на обращении к соответствующим системным вызовам ядра прямо из кода исследуемой программы, в самом ее начале. Это позволяет реализовать фаззинг со снепшотами каталога данных на основе свободного AFL++. Описание подхода находится [Здесь](https://www.altlinux.org/PostgreSQL16_fuzzing_technique) ([WebArchive](http://web.archive.org/web/20241203093118/https://www.altlinux.org/PostgreSQL16_fuzzing_technique))
+
+
+---
+
+Часто при фаззинге сложных проектов может оказаться, что для нормальной работы каждому экземпляру исследуемой программы нужна собственная директория с данными, которую не плохо бы перед каждым новым прогоном исследуемой программы возвращать в исходное состояние. В данном посте в формате "заметок" я расскажу как мне удалось сделать восстановление окружения при фаззинге postgres'а, используя btrfs под фаззером Crusher.
+
+Для фаззига postgres'а система восстановления контекста чрезвычайно важна. Полноценный postgres не может работать без своего личного хранилища, и при этом меняет его практически на каждый чих. Без возвращения хранилища к исходному состоянию при тестировании, ни о какой воспроизводимости и речи быть не может. Поэтому как только заходит речь о фаззинг-тестировании postgres'а в сборе, необходимо сразу думать о системе восстановления контекста.
+
+## btrfs -- файловая система со снепшотами
+
+В качестве framework'а для системы восстановления контекста была выбрана файловая система btrfs. Btrfs позволяет эффективно работать со снепшотами на уровне директорий. А именно, позволяет в рамках файловой системы получить клон указанной директории с минимальными затратами. Клонирование происходит на логическом уровне, данные по факту не копируются, и только лишь изменения внесенные в копию или в оригинал занимают дополнительное место на диске. Все это позволяет экономить время и дисковое пространство.
+
+Фаззинг, при его промышленном применении, имеет практический смысл, будучи запущенным во много потоков на многих процессорах. Для каждого инстанса фаззера, а точнее для исследуемой программы запущенной этим фаззером, предполагается создать свой собственный снепшот с уникальным именем, содержащий данные необходимые для запуска исследуемой программы, и возвращать этот снепшот к исходному состоянию перед каждым новым прогоном исследуемой программы, чтобы следы оставленные в хранилище предыдущими прогонами не оказывали влияния на последующее..
+
+Мной был написан скрипт `snapshooter.pl` (код приведен в конце поста) реализующий действия со снепшотами, необходимые для фаззинга. Скрипту первым параметром передается имя команды и далее параметры которые нужны команде для работы.
+
+Ниже приведены команды и их описания:
+
+###### `init MOUNT_POINT IMAGE_FILE IMAGE_SIZE IMAGE_COUNT`
+
+Принимает 4 аргумента: точка монтирования `MOUNT_POINT`, путь к создаваемому образу файловой системы `IMAGE_FILE`, предполагаемый размер одного снепшота `IMAGE_SIZE` и ожидаемое количество снепшотов `IMAGE_COUNT`.
+
+Скрипт отмонтирует то, что было примантировано к точки монтирования, создаст новый образ файловой системы, примонтирует его к точке монтирования, и создаст в файловой системе директорию `_reference`.
+
+В эту директорию следует скопировать "эталонный" снепшот, на базе которого будут строиться снепшоты для всех экземпляров
+
+###### `clone MOUNT_POINT SNAPSHOT_NAME`
+
+Принимает два аргумента: точку монтирования `IMAGE_COUNT` и имя снэпшота `SNAPSHOT_NAME`.
+
+Команда `clone` клонирует эталонный снепшот в директорию `[SNAPSHOT_NAME].base`. Если есть потребность внести какие-то специфичные для инстанса изменения в снепшот, то это следует сделать в этой директории. В дальнейшем перед каждым запуском исследуемой программы рабочий снепшот будет приводится к "базововму" состоянию.
+
+###### `reset MOUNT_POINT SNAPSHOT_NAME`
+
+Принимает два аргумента: точку монтирования `MOUNT_POINT`и имя снэпшота `SNAPSHOT_NAME`.
+
+Команда `reset` удаляет снепшот `SNAPSHOT_NAME`, и создает новый с таким же именем клонируя базовый снепшот `[SNAPSHOT_NAME].base`. Таким образом происходит восстановление целевого снепшота исходное состояние.
+
+В дальнейшем, для ускорения работы, планируется образ файловой системы со снепшотами перенести с диска в память, но я пока еще не разбирался как это делается.
+
+## Crusher
+
+В Crusher 2.11.0 появились инструменты позволяющие инициировать подготовку, и восстановления окружения для запускаемых процессов. К сожалению для того чтобы задействовать эти инструменты необходимо написание скриптов на языке python (и ни на каком другом), а я этот python не умею, и честно говоря не очень хочу уметь. К счастью коллеги снабдили меня обертками которые позволяют из непонятного питона запускать понятные `.sh` и прочие скрипты. Поэтому конструкция получилась двухсоставная.
+
+Предложенное решение использует два "хука" предоставляемые крашером. Первый хук дергается при создании нового инстанса фаззера (см. `--configurator-script` в документации), и используется для создания нового снепшота с данными хранилища, которыми будет пользоваться исследуемая программа. Второй хук дергается перед каждым новым прогоном исследуемой программы (см. `--environment-plugin` в документации), и используется для того чтобы вернуть снепшот с хранилищем в исходное состояние (ибо есть шанс что при предыдущих запусках он мог быть иземенен)
+
+### Подготовка окружения
+
+Изначальная подготовка окружения осуществляется через скрипт `conf.py`:
+
+```python
+import json
+import os
+import sys
+import traceback
+import subprocess
+
+def transform_options(ops_json):
+ try:
+ jops = json.loads(ops_json)
+ instance_name = jops['configuration']['instance_name']
+ this_dir = os.path.dirname(__file__)
+ script_path = this_dir + '/init_script.sh'
+ pr = subprocess.Popen([script_path, instance_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ pr.wait()
+ assert pr.returncode == 0
+ output = pr.stdout.read()
+ for line in output.split():
+ ls = line.split('=')
+ assert len(ls) == 2
+ var_name = ls[0].strip()
+ var_value = ls[1].strip()
+ os.environ[var_name] = var_value
+ return json.dumps(jops)
+ except Exception as ex:
+ print("EXCEPTION!")
+ traceback.print_exc()
+ return None
+```
+Который в свою очередь вызывает скрипт `init_script.sh`:
+
+```bash
+#!/bin/bash
+
+INSTANCE_NAME=$1
+THIS_DIR=$(dirname $0)
+
+sudo ./snapshooter.pl clone mnt "${INSTANCE_NAME}" 1>&2 || exit 1
+sudo ./snapshooter.pl reset mnt "${INSTANCE_NAME}" 1>&2 || exit 1
+
+echo "PGDATA=${THIS_DIR}/mnt/$INSTANCE_NAME"
+```
+
+Скрипту `init_script.sh` в качестве аргумента передается уникальное имя инстанса фаззера, это имя скрипт использует в качестве имени создаваемого снепшота. Кроме того на `STDOUT` скрипт выводит пары `NAME=VALUE` переменных окружения которые должны быть установлены при запуске. В нашем случае мы через переменную окружения сообщаем постгресу где находится его хранилище. Вывод скрипта анализируется оберткой `env.py` и значения переменных окружения передаются фаззеру для последующего применения.
+
+При запуске фаззера необходимо указать расположение скрипта `conf.py` через параметр `--configurator-script `
+
+### Восстановление окружения
+Идея та же, что и с созданием окружения. Питоновская обертка `env.py`:
+
+```python
+import json
+import os
+
+error_msg = '...'
+
+def get_error():
+ return error_msg
+
+instance_name = None
+
+def init(json_options):
+ jops = json.loads(json_options)
+ global instance_name
+ instance_name = jops['configuration']['instance_name']
+ return True
+
+def finish():
+ return True
+
+def setup():
+ this_dir = os.path.dirname(__file__)
+ script_path = this_dir + '/run_script.sh'
+ cmd = script_path + ' ' + instance_name
+ r = os.system(cmd)
+ return r == 0
+
+def teardown():
+ return True
+```
+И `.sh` скрипт выполняющий содержательную работу:
+```bash
+#!/bin/bash
+
+INSTANCE_NAME=$1
+
+sudo ./snapshooter.pl reset mnt "${INSTANCE_NAME}" 1>&2 || exit 1
+```
+В данном случае мы получаем имя инстанса в качестве аргумента и восстанавливаем снепшот с этим именем в исходное состояние.
+
+При запуске фаззинга расположение файла `env.py` указывается через параметр конфигурации `--environment-plugin`
+
+### Запуск фаззинга
+Перед запуском фаззинга мы должны инициализировать хранилище снепшотов, раздать эталонному снепшоту правильные права (постгрес требует чтобы хранилище не было доступно на чтение кому либо кроме постгреса) и заполнить правильными данными. Заранее подготовленный снимок хранилища у меня лежит в директории `etalon`, я его просто копирую. Ну а дальше все достаточно просто.
+
+```bash
+#!/bin/bash
+
+ME=`whoami`
+sudo ./snapshooter.pl init mnt image.img 40000000 10
+sudo chown $ME: mnt/_reference
+chmod 700 mnt/_reference
+
+cp -r etalon/* mnt/_reference
+
+~/crushers/latest/bin_x86-64/fuzz_manager --bitmap-size 300000 --start 4 --eat-cores 1 --dse-cores 0 -i in -o out -T StdIn -F -I StaticNoForkSrv --configurator-script conf.py --environment-plugin env.py -- /home/nataraj/tests/fuzz_psql/pg/bin/postgres --single postgres
+```
+
+Таким образом удалось успешно запустить простейший параллельный фаззинг SQL-запросов с восстановлением состояния хранилища к исходному перед каждым запуском. На сам по себе прямой фаззинг SQL-запросов я больших надежд не возлагаю, фаззить в лоб сложные синтаксические конструкции -- малопродуктивно, однако система восстановления контекста в дальнейшем будет применена для фаззинг-исследования функциональности потенциально приводящей к модификации хранилища postgresql.
+
+## Приложения
+### snapshooter.pl
+```perl
+#!/usr/bin/perl
+
+use strict;
+
+my $command = shift @ARGV;
+
+die "Укажите команду первым аргументом" unless $command;
+
+if ($command eq "init")
+{
+ my $mount_path = shift @ARGV;
+ my $image_path = shift @ARGV;
+ my $snapshot_size = shift @ARGV;
+ my $snapshots_count = shift @ARGV;
+
+ unless ($image_path && $mount_path && $snapshot_size && $snapshots_count)
+ {
+ die "Команде init нужны 4 параметра: точка монтирования,путь к образу, размер снэпшота и из кол-во";
+ }
+
+ `mountpoint -q $mount_path`;
+ unless( $?)
+ {
+ print "$mount_path -- примонтирован. Пробуем отмонтировать...\n";
+ `umount $mount_path`;
+ }
+
+ my $min_btrfs_size = 114294784;
+ if ($snapshot_size * $snapshots_count < $min_btrfs_size)
+ {
+ print "Увеличиваем размер образа до минимально возможных $min_btrfs_size байт";
+ $snapshot_size = $min_btrfs_size;
+ $snapshots_count = 1;
+ }
+
+ print "Создаем и форматируем образ...\n";
+ `dd if=/dev/zero of=$image_path count=$snapshots_count bs=$snapshot_size`;
+ `mkfs.btrfs $image_path`;
+
+ print "Монтируем образ...\n";
+ `mkdir -p $mount_path`;
+ `mount $image_path $mount_path`;
+
+ print "Создаем reference снэпшот...\n";
+ `btrfs subvolume create $mount_path/_reference`;
+}
+
+if ($command eq "clone")
+{
+ my $mount_path = shift @ARGV;
+ my $target = shift @ARGV;
+
+ unless ($target)
+ {
+ die "Команде $command нужны 2 параметра: точка монтирования, имя снэпшота";
+ }
+
+ my $name = "$mount_path/$target.base";
+
+ if (-e $name)
+ {
+ die "Снэпшот $name уже существует";
+ }
+ print "Создаю основу для снэпшота $target: $name\n";
+
+ `btrfs subvolume snapshot $mount_path/_reference $name`;
+}
+
+if ($command eq "reset")
+{
+
+ my $mount_path = shift @ARGV;
+ my $target = shift @ARGV;
+
+ unless ($target)
+ {
+ die "Команде $command нужны 2 параметра: точка монтирования, имя снэпшота";
+ }
+ my $name = "$mount_path/$target";
+ unless (-e "$name.base")
+ {
+ die "Не существует основы для снэпшота $target: $name.base";
+ }
+
+ print "Обновляю снэпшот $target: \n";
+
+ if (-e $name)
+ {
+ `rm -r $name`;
+ print "Удаляем старый\n";
+ }
+
+ print "Создаем новый\n";
+
+ `btrfs subvolume snapshot $name.base $name`;
+}
+```
+
+++ /dev/null
-# Подготовка окружения для исследуемого процесса с испольнованием btrfs
-
-**Update:** По состоянию на 2025 год, коллеги из БазАльта предложили более совершенный способ переключения между снепшотами, основанный на обращении к соответствующим системным вызовам ядра прямо из кода исследуемой программы, в самом ее начале. Это позволяет реализовать фаззинг со снепшотами каталога данных на основе свободного AFL++. Описание подхода находится [Здесь](https://www.altlinux.org/PostgreSQL16_fuzzing_technique) ([WebArchive](http://web.archive.org/web/20241203093118/https://www.altlinux.org/PostgreSQL16_fuzzing_technique))
-
-
----
-
-Часто при фаззинге сложных проектов может оказаться, что для нормальной работы каждому экземпляру исследуемой программы нужна собственная директория с данными, которую не плохо бы перед каждым новым прогоном исследуемой программы возвращать в исходное состояние. В данном посте в формате "заметок" я расскажу как мне удалось сделать восстановление окружения при фаззинге postgres'а, используя btrfs под фаззером Crusher.
-
-Для фаззига postgres'а система восстановления контекста чрезвычайно важна. Полноценный postgres не может работать без своего личного хранилища, и при этом меняет его практически на каждый чих. Без возвращения хранилища к исходному состоянию при тестировании, ни о какой воспроизводимости и речи быть не может. Поэтому как только заходит речь о фаззинг-тестировании postgres'а в сборе, необходимо сразу думать о системе восстановления контекста.
-
-## btrfs -- файловая система со снепшотами
-
-В качестве framework'а для системы восстановления контекста была выбрана файловая система btrfs. Btrfs позволяет эффективно работать со снепшотами на уровне директорий. А именно, позволяет в рамках файловой системы получить клон указанной директории с минимальными затратами. Клонирование происходит на логическом уровне, данные по факту не копируются, и только лишь изменения внесенные в копию или в оригинал занимают дополнительное место на диске. Все это позволяет экономить время и дисковое пространство.
-
-Фаззинг, при его промышленном применении, имеет практический смысл, будучи запущенным во много потоков на многих процессорах. Для каждого инстанса фаззера, а точнее для исследуемой программы запущенной этим фаззером, предполагается создать свой собственный снепшот с уникальным именем, содержащий данные необходимые для запуска исследуемой программы, и возвращать этот снепшот к исходному состоянию перед каждым новым прогоном исследуемой программы, чтобы следы оставленные в хранилище предыдущими прогонами не оказывали влияния на последующее..
-
-Мной был написан скрипт `snapshooter.pl` (код приведен в конце поста) реализующий действия со снепшотами, необходимые для фаззинга. Скрипту первым параметром передается имя команды и далее параметры которые нужны команде для работы.
-
-Ниже приведены команды и их описания:
-
-###### `init MOUNT_POINT IMAGE_FILE IMAGE_SIZE IMAGE_COUNT`
-
-Принимает 4 аргумента: точка монтирования `MOUNT_POINT`, путь к создаваемому образу файловой системы `IMAGE_FILE`, предполагаемый размер одного снепшота `IMAGE_SIZE` и ожидаемое количество снепшотов `IMAGE_COUNT`.
-
-Скрипт отмонтирует то, что было примантировано к точки монтирования, создаст новый образ файловой системы, примонтирует его к точке монтирования, и создаст в файловой системе директорию `_reference`.
-
-В эту директорию следует скопировать "эталонный" снепшот, на базе которого будут строиться снепшоты для всех экземпляров
-
-###### `clone MOUNT_POINT SNAPSHOT_NAME`
-
-Принимает два аргумента: точку монтирования `IMAGE_COUNT` и имя снэпшота `SNAPSHOT_NAME`.
-
-Команда `clone` клонирует эталонный снепшот в директорию `[SNAPSHOT_NAME].base`. Если есть потребность внести какие-то специфичные для инстанса изменения в снепшот, то это следует сделать в этой директории. В дальнейшем перед каждым запуском исследуемой программы рабочий снепшот будет приводится к "базововму" состоянию.
-
-###### `reset MOUNT_POINT SNAPSHOT_NAME`
-
-Принимает два аргумента: точку монтирования `MOUNT_POINT`и имя снэпшота `SNAPSHOT_NAME`.
-
-Команда `reset` удаляет снепшот `SNAPSHOT_NAME`, и создает новый с таким же именем клонируя базовый снепшот `[SNAPSHOT_NAME].base`. Таким образом происходит восстановление целевого снепшота исходное состояние.
-
-В дальнейшем, для ускорения работы, планируется образ файловой системы со снепшотами перенести с диска в память, но я пока еще не разбирался как это делается.
-
-## Crusher
-
-В Crusher 2.11.0 появились инструменты позволяющие инициировать подготовку, и восстановления окружения для запускаемых процессов. К сожалению для того чтобы задействовать эти инструменты необходимо написание скриптов на языке python (и ни на каком другом), а я этот python не умею, и честно говоря не очень хочу уметь. К счастью коллеги снабдили меня обертками которые позволяют из непонятного питона запускать понятные `.sh` и прочие скрипты. Поэтому конструкция получилась двухсоставная.
-
-Предложенное решение использует два "хука" предоставляемые крашером. Первый хук дергается при создании нового инстанса фаззера (см. `--configurator-script` в документации), и используется для создания нового снепшота с данными хранилища, которыми будет пользоваться исследуемая программа. Второй хук дергается перед каждым новым прогоном исследуемой программы (см. `--environment-plugin` в документации), и используется для того чтобы вернуть снепшот с хранилищем в исходное состояние (ибо есть шанс что при предыдущих запусках он мог быть иземенен)
-
-### Подготовка окружения
-
-Изначальная подготовка окружения осуществляется через скрипт `conf.py`:
-
-```python
-import json
-import os
-import sys
-import traceback
-import subprocess
-
-def transform_options(ops_json):
- try:
- jops = json.loads(ops_json)
- instance_name = jops['configuration']['instance_name']
- this_dir = os.path.dirname(__file__)
- script_path = this_dir + '/init_script.sh'
- pr = subprocess.Popen([script_path, instance_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- pr.wait()
- assert pr.returncode == 0
- output = pr.stdout.read()
- for line in output.split():
- ls = line.split('=')
- assert len(ls) == 2
- var_name = ls[0].strip()
- var_value = ls[1].strip()
- os.environ[var_name] = var_value
- return json.dumps(jops)
- except Exception as ex:
- print("EXCEPTION!")
- traceback.print_exc()
- return None
-```
-Который в свою очередь вызывает скрипт `init_script.sh`:
-
-```bash
-#!/bin/bash
-
-INSTANCE_NAME=$1
-THIS_DIR=$(dirname $0)
-
-sudo ./snapshooter.pl clone mnt "${INSTANCE_NAME}" 1>&2 || exit 1
-sudo ./snapshooter.pl reset mnt "${INSTANCE_NAME}" 1>&2 || exit 1
-
-echo "PGDATA=${THIS_DIR}/mnt/$INSTANCE_NAME"
-```
-
-Скрипту `init_script.sh` в качестве аргумента передается уникальное имя инстанса фаззера, это имя скрипт использует в качестве имени создаваемого снепшота. Кроме того на `STDOUT` скрипт выводит пары `NAME=VALUE` переменных окружения которые должны быть установлены при запуске. В нашем случае мы через переменную окружения сообщаем постгресу где находится его хранилище. Вывод скрипта анализируется оберткой `env.py` и значения переменных окружения передаются фаззеру для последующего применения.
-
-При запуске фаззера необходимо указать расположение скрипта `conf.py` через параметр `--configurator-script `
-
-### Восстановление окружения
-Идея та же, что и с созданием окружения. Питоновская обертка `env.py`:
-
-```python
-import json
-import os
-
-error_msg = '...'
-
-def get_error():
- return error_msg
-
-instance_name = None
-
-def init(json_options):
- jops = json.loads(json_options)
- global instance_name
- instance_name = jops['configuration']['instance_name']
- return True
-
-def finish():
- return True
-
-def setup():
- this_dir = os.path.dirname(__file__)
- script_path = this_dir + '/run_script.sh'
- cmd = script_path + ' ' + instance_name
- r = os.system(cmd)
- return r == 0
-
-def teardown():
- return True
-```
-И `.sh` скрипт выполняющий содержательную работу:
-```bash
-#!/bin/bash
-
-INSTANCE_NAME=$1
-
-sudo ./snapshooter.pl reset mnt "${INSTANCE_NAME}" 1>&2 || exit 1
-```
-В данном случае мы получаем имя инстанса в качестве аргумента и восстанавливаем снепшот с этим именем в исходное состояние.
-
-При запуске фаззинга расположение файла `env.py` указывается через параметр конфигурации `--environment-plugin`
-
-### Запуск фаззинга
-Перед запуском фаззинга мы должны инициализировать хранилище снепшотов, раздать эталонному снепшоту правильные права (постгрес требует чтобы хранилище не было доступно на чтение кому либо кроме постгреса) и заполнить правильными данными. Заранее подготовленный снимок хранилища у меня лежит в директории `etalon`, я его просто копирую. Ну а дальше все достаточно просто.
-
-```bash
-#!/bin/bash
-
-ME=`whoami`
-sudo ./snapshooter.pl init mnt image.img 40000000 10
-sudo chown $ME: mnt/_reference
-chmod 700 mnt/_reference
-
-cp -r etalon/* mnt/_reference
-
-~/crushers/latest/bin_x86-64/fuzz_manager --bitmap-size 300000 --start 4 --eat-cores 1 --dse-cores 0 -i in -o out -T StdIn -F -I StaticNoForkSrv --configurator-script conf.py --environment-plugin env.py -- /home/nataraj/tests/fuzz_psql/pg/bin/postgres --single postgres
-```
-
-Таким образом удалось успешно запустить простейший параллельный фаззинг SQL-запросов с восстановлением состояния хранилища к исходному перед каждым запуском. На сам по себе прямой фаззинг SQL-запросов я больших надежд не возлагаю, фаззить в лоб сложные синтаксические конструкции -- малопродуктивно, однако система восстановления контекста в дальнейшем будет применена для фаззинг-исследования функциональности потенциально приводящей к модификации хранилища postgresql.
-
-## Приложения
-### snapshooter.pl
-```perl
-#!/usr/bin/perl
-
-use strict;
-
-my $command = shift @ARGV;
-
-die "Укажите команду первым аргументом" unless $command;
-
-if ($command eq "init")
-{
- my $mount_path = shift @ARGV;
- my $image_path = shift @ARGV;
- my $snapshot_size = shift @ARGV;
- my $snapshots_count = shift @ARGV;
-
- unless ($image_path && $mount_path && $snapshot_size && $snapshots_count)
- {
- die "Команде init нужны 4 параметра: точка монтирования,путь к образу, размер снэпшота и из кол-во";
- }
-
- `mountpoint -q $mount_path`;
- unless( $?)
- {
- print "$mount_path -- примонтирован. Пробуем отмонтировать...\n";
- `umount $mount_path`;
- }
-
- my $min_btrfs_size = 114294784;
- if ($snapshot_size * $snapshots_count < $min_btrfs_size)
- {
- print "Увеличиваем размер образа до минимально возможных $min_btrfs_size байт";
- $snapshot_size = $min_btrfs_size;
- $snapshots_count = 1;
- }
-
- print "Создаем и форматируем образ...\n";
- `dd if=/dev/zero of=$image_path count=$snapshots_count bs=$snapshot_size`;
- `mkfs.btrfs $image_path`;
-
- print "Монтируем образ...\n";
- `mkdir -p $mount_path`;
- `mount $image_path $mount_path`;
-
- print "Создаем reference снэпшот...\n";
- `btrfs subvolume create $mount_path/_reference`;
-}
-
-if ($command eq "clone")
-{
- my $mount_path = shift @ARGV;
- my $target = shift @ARGV;
-
- unless ($target)
- {
- die "Команде $command нужны 2 параметра: точка монтирования, имя снэпшота";
- }
-
- my $name = "$mount_path/$target.base";
-
- if (-e $name)
- {
- die "Снэпшот $name уже существует";
- }
- print "Создаю основу для снэпшота $target: $name\n";
-
- `btrfs subvolume snapshot $mount_path/_reference $name`;
-}
-
-if ($command eq "reset")
-{
-
- my $mount_path = shift @ARGV;
- my $target = shift @ARGV;
-
- unless ($target)
- {
- die "Команде $command нужны 2 параметра: точка монтирования, имя снэпшота";
- }
- my $name = "$mount_path/$target";
- unless (-e "$name.base")
- {
- die "Не существует основы для снэпшота $target: $name.base";
- }
-
- print "Обновляю снэпшот $target: \n";
-
- if (-e $name)
- {
- `rm -r $name`;
- print "Удаляем старый\n";
- }
-
- print "Создаем новый\n";
-
- `btrfs subvolume snapshot $name.base $name`;
-}
-```
-