четверг, 2 декабря 2010 г.

AUFS-2.1: установка и работа в связке с SquashFS

Этот пост является логическим продолжением поста от 20 апреля Создание LiveCD Slackware based: использование Squashfs. Тогда я рассказал, как создать LiveCD c системным разделом полностью на SquashFS, минуя cpio/gzip сжатие. Но вопрос работы на полностью Read-Only системном разделе остался тогда не раскрытым. Логично предположить, что устройством записи будет оперативная память. У нас есть две файловые системы, работающие в памяти: ramfs и tmpfs. Вторая, она же shmfs, является развитием первой и отличается тем, что может работать не только с физической памятью, но и с виртуальной aka swap, и размер ее разделов можно менять "налету". Принципиальной разницы, что использовать - нет, это, как говорится, дело хозяйское. Итак, у нас есть две файловые системы: одна только на чтение, другая - на запись и чтение; осталось их как-то объединить. Здесь нам и пригодится каскадная файловая система AUFS, которая создает стек файловых систем.

Снизу мы положим SquashFS, а сверху tmpfs. При изменении файла из первого слоя он автоматически перейдет на второй, т.е. мы получим SquashFS доступную на запись. Думаю, результат стоит того, чтобы связаться с таким неблагодарным делом, как наложение патчей на ядро. Нам потребуются рутовые права. Сайт проекта AUFS
http://aufs.sourceforge.net
На сайте приведена инструкция по получению, установке и использованию AUFS.
Для получения исходников последовательно вводим три команды:
# git clone http://git.c3sl.ufpr.br/pub/scm/aufs/aufs2-standalone.git aufs2-standalone.git
# cd aufs2-standalone.git
# git checkout origin/aufs2.1-31

В последней команде вместо суффикса 31 необходимо поставить версию ядра на которую Вы хотите получить патчи. Тогда не будет ошибок приведенных ниже. Т.о. чтобы получить патчи AUFS-2.1 для ядра 2.6.38 нужно выполнить команды:
$ git clone http://git.c3sl.ufpr.br/pub/scm/aufs/aufs2-standalone.git aufs2-standalone.git
$ cd aufs2-standalone.git
$ git checkout origin/aufs2.1-38
(прим. от 29.05.11)

После чего выполняем ls -l и смотрим чего получили:
bash-4.1# ls -l
итого 272
-rw-r--r-- 1 root root 17990 2010-12-01 13:09 COPYING
-rw-r--r-- 1 root root 210297 2010-12-01 13:10 ChangeLog
drwxr-xr-x 3 root root 72 2010-12-01 13:10 Documentation
-rw-r--r-- 1 root root 1181 2010-12-01 13:09 Makefile
-rw-r--r-- 1 root root 14067 2010-12-01 13:10 README
-rw-r--r-- 1 root root 3090 2010-12-01 13:10 aufs2-base.patch
-rw-r--r-- 1 root root 980 2010-12-01 13:10 aufs2-kbuild.patch
-rw-r--r-- 1 root root 9495 2010-12-01 13:10 aufs2-standalone.patch
-rw-r--r-- 1 root root 3033 2010-12-01 13:10 config.mk
drwxr-xr-x 2 root root 368 2010-12-01 13:10 design
drwxr-xr-x 3 root root 72 2010-12-01 13:10 fs
drwxr-xr-x 3 root root 72 2010-12-01 13:09 include

Итак, мы имеем три патча и директории: fs, include, Documentation.
Делаем копию исходников ядра:
# cp -a /usr/src/linux-2.6.33.4 /usr/src/linux-2.6.33.4.my

Копируем исходники aufs в исходники ядра:
# cp -v *patch /usr/src/linux-2.6.33.4.my
# cp -a ./Documentation /usr/src/linux-2.6.33.4.my
# cp -a ./fs /usr/src/linux-2.6.33.4.my
# cp -a ./include/linux/aufs_type.h /usr/src/linux-2.6.33.4.my/include/linux/
# echo "unifdef-y += aufs_type.h" >> /usr/src/linux-2.6.33.4.my/include/linux/Kbuild

Дело за малым. Переходим в каталог исходников ядра:
# cd /usr/src/linux-2.6.33.4.my

Правим ревизию ядра:
# sed -i 's/^EXTRAVERSION = .4/EXTRAVERSION = .4_my/' ./Makefile

Очищаем от результатов предыдущей компиляции:
# make mrproper

Ставим конфиг:
cp -v /boot/config-generic-2.6.33.4 ./.config

Накладываем патчи aufs:
# patch -p1 -i aufs2-kbuild.patch
patching file fs/Kconfig
Hunk #1 succeeded at 44 with fuzz 2 (offset -143 lines).
patching file fs/Makefile
patching file include/linux/Kbuild

# patch -p1 -i aufs2-base.patch
patching file fs/namei.c
Hunk #1 succeeded at 1207 (offset -12 lines).
patching file fs/splice.c
Hunk #1 succeeded at 1053 (offset -4 lines).
Hunk #2 succeeded at 1085 (offset 1 line).
patching file include/linux/namei.h
Hunk #1 succeeded at 73 with fuzz 2 (offset -2 lines).
patching file include/linux/splice.h

# patch -p1 -i aufs2-standalone.patch
patching file fs/file_table.c
Hunk #1 succeeded at 34 (offset 2 lines).
Hunk #2 succeeded at 346 (offset -29 lines).
patching file fs/inode.c
Hunk #1 succeeded at 85 with fuzz 1.
patching file fs/namei.c
Hunk #1 succeeded at 349 (offset 12 lines).
Hunk #2 succeeded at 1205 (offset -24 lines).
Hunk #3 succeeded at 1264 (offset 12 lines).
patching file fs/namespace.c
patching file fs/notify/group.c
patching file fs/notify/inode_mark.c
patching file fs/open.c
Hunk #1 succeeded at 226 with fuzz 2 (offset 5 lines).
patching file fs/splice.c
Hunk #1 succeeded at 1077 (offset -3 lines).
Hunk #2 succeeded at 1107 (offset 1 line).
patching file security/commoncap.c
Hunk #1 succeeded at 946 (offset -68 lines).
patching file security/device_cgroup.c
Hunk #1 succeeded at 514 (offset 1 line).
patching file security/integrity/ima/ima_main.c
Hunk #1 succeeded at 273 (offset -51 lines).
patching file security/security.c
Hunk #1 succeeded at 404 (offset 18 lines).
Hunk #3 succeeded at 420 (offset 18 lines).
Hunk #5 succeeded at 438 (offset 18 lines).
Hunk #7 succeeded at 478 with fuzz 2 (offset 38 lines).
Hunk #9 succeeded at 565 (offset 38 lines).
misordered hunks! output would be garbled
Hunk #11 FAILED at 657.
1 out of 11 hunks FAILED -- saving rejects to file security/security.c.rej

Упс... В последний раз что-то пошло не так. Но тем не менее ядро собирается, значит, продолжаем.

Корректируем конфиг:
# make oldconfig

Осталось поправить конфигурацию сборки ядра:
# make menuconfig

Файловую систему SquashFS нужно будет собрать монолитно, AUFS можно модулем, но есть смысл собрать ее также монолитно.

После сборки ядра и модулей, используя слакбилды, получаем два пакета. В моем случае это были:
kernel-generic-2.6.33.4_32.2-i486-1.txz
kernel-modules-2.6.33.4_32.2-i486-1.txz

Осталось все это заставить заработать на реальной системе. Качаем recovery-версию:
# cd /tmp
# wget http://sourceforge.net/projects/slavanka/files/SlavankaOS/slavanka-recovery-1.iso

Монтируем и копируем содержимое:
# mount slavanka-recovery-1.iso /mnt/cdrom -o loop
# rm -r /tmp/livecd
# cp -a /mnt/cdrom /tmp/livecd
# umount /mnt/cdrom

Переходим в целевой каталог и распаковываем образ initrd
cd /tmp/livecd
sh ./op

удаляем 32-битное ядро и модули
# rm ./linux32
# rm -r ./1/lib/modules

Ставим наше ядрышко:
# installpkg -root ./1 /tmp/kernel-generic-2.6.33.4_32.2-i486-1.txz
# installpkg -root ./1 /tmp/kernel-modules-2.6.33.4_32.2-i486-1.txz

Теперь самое главное: как наш перпетум мобиле завести. У меня не получилось установить aufs сразу на корень системного раздела, не используя промежуточное монтирование из busybox. Зато не вызвало проблем установить aufs на корневые каталоги /etc, /var, /root и т.д. Делаем так:
# mkdir -p ./1/opt/{etc,var,tmp,root}

Далее открываем нашим любимым текстовым радактором nano первый инициализационный скрипт rc.S
# nano -w ./1/etc/rc.d/rc.S

...и видим там такие строки:
#!/bin/sh
#
# /etc/rc.d/rc.S: System initialization script.
#
# Mostly written by: Patrick J. Volkerding,
#

PATH=/sbin:/usr/sbin:/bin:/usr/bin

# Try to mount /proc:
/sbin/mount -v proc /proc -n -t proc 2> /dev/null

# Mount sysfs next, if the kernel supports it:
if [ -d /sys ]; then
if grep -wq sysfs /proc/filesystems ; then
if ! grep -wq sysfs /proc/mounts ; then
/sbin/mount -v sysfs /sys -n -t sysfs
fi
fi
fi

Эти команды - первое, что делает операционная система после инициализации ядра и запуска первого процесса init. Видно, что перво-наперво система монтирует виртуальные файловые системы proc и sysfs. Полагаю, это тот самый момент, когда наши RO каталоги следует сделать доступными на запись. Вписываем следующие строки:
/sbin/mount -t tmpfs -o size=1M tmpfs /opt/etc
/sbin/mount -t tmpfs -o size=1M tmpfs /opt/var
/sbin/mount -t tmpfs -o size=1M tmpfs /opt/tmp
/sbin/mount -t tmpfs -o size=1M tmpfs /opt/root
/sbin/mount -t aufs -o dirs=/opt/etc:/etc=ro none /etc
/sbin/mount -t aufs -o dirs=/opt/var:/var=ro none /var
/sbin/mount -t aufs -o dirs=/opt/tmp:/tmp=ro none /tmp
/sbin/mount -t aufs -o dirs=/opt/root:/root=ro none /root

Дело сделано. Сохраняем файл и выходим из редактора. Остальное - дело техники. Удаляем лишние модули ядра:
# cd ./1/lib/modules/2.6.33.4_32.2/kernel/; rm -rv `ls -1 ./ |grep -vE "fs|drivers|net"`;cd -
# cd ./1/lib/modules/2.6.33.4_32.2/kernel/fs/; rm -rv `ls -1 ./ |grep -vE "mbcache|nls|jbd|jbd2|fuse|ext2|ext3|ext4|isofs|reiserfs|fat"`;cd -
# cd ./1/lib/modules/2.6.33.4_32.2/kernel/fs/nls/; rm -rv `ls -1 ./ |grep -vE "utf8|koi8|cp866"`;cd -
# cd ./1/lib/modules/2.6.33.4_32.2/kernel/drivers/; rm -rv `ls -1 ./ |grep -vE "acpi|dma|i2c|rtc|usb|net|hwmon|thermal|watchdog"`;cd -
# cd ./1/lib/modules/2.6.33.4_32.2/kernel/drivers/net; rm -rv `ls -1 ./ |grep -vE "ko|e1000"`;cd -
# cd ./1/lib/modules/2.6.33.4_32.2/kernel/net/; rm -rv `ls -1 ./ |grep -vE "ipv4|netfilter"`;cd -

Копируем новое ядро в директорию загрузчика и удаляем /boot каталог:
# cp -v ./1/boot/vmlinuz-generic-2.6.33.4_32.2 ./linux32
# rm -r ./1/boot

Удаляем теперь уже не нужную опцию монтирования /usr директории в fstab:
# sed -i "1d" ./1/etc/fstab

Сжимаем системный раздел:
# cd ./1
# mksquashfs . ../image
# cd ..
# rm -r ./1

Для полноты картины поправим 64-битный образ:
# sh ./op
# rm -r ./1/lib/modules/2.6.33.4_32
# sh ./cl

Последний шаг. Правим в isolinux.cfg строку в секции linux32:
APPEND vga=normal initrd=rootfs.cgz root=/dev/ram0 rw vt.default_utf8=1 acpi_enforce_resources=lax

на
APPEND vga=normal initrd=image root=/dev/ram0 ro looptype=squashfs ramdisk_size=54000 vt.default_utf8=1 acpi_enforce_resources=lax

Дело в шляпе. Запускаем:
# sh ./mk

...и получаем образ /tmp/mycd.iso на выходе.

В итоге таких нехитрых действий мы уменьшили потребеление оперативки LiveCD в два раза с 256Мб до 128Мб, не теряя на функционале, не жертвуя содержимым, лишь используя более грамотную организацию корневого раздела LiveCD.

-------------- Д О П О Л Н Е Н И Е от 9 марта 2012 -----------------------------

Сейчас, спустя более года может показаться странным, к чему такие сложности, если у Junjiro Okajima лежит готовое ядро с наложенными патчами. Бери и собирай. Дело в том, что тогда в ядре еще не было поддержки сверхсильного сжатия XZ, LZMA для SquashFS и на ядро приходилось последовательно накладывать патчи LZMA for SqushFS, а затем AUFS. На тот момент самым простым для меня оказалось взять готовое ядро с LZMA SquashFS и затем наложить патчти AUFS. Сейчас с появлением сжатия XZ для SquashFS в ядре нет никакой необходимости для таких сложностей. Время самураев ушло :)
С выходом ядра третьей версии Junjiro Okajima выпустил AUFS также третьей версии и перевел git репозиторий третьей версии на sourceforge.net
читаем инструкцию Junjiro:
$ git clone --reference /your/linux/git/tree \
git://aufs.git.sourceforge.net/gitroot/aufs/aufs3-linux.git \
aufs3-linux.git
- if you don't have linux GIT tree, then remove "--reference ..."
$ cd aufs3-linux.git
$ git checkout origin/aufs3.0

и соответственно выполняем
$ cd /tmp
$ git clone git://aufs.git.sourceforge.net/gitroot/aufs/aufs3-linux.git linux-3.2.9.aufs64
$ cd linux-3.2.9.aufs64
$ git checkout origin/aufs3.2

Обратите внимание на последнюю цифру. В инструкции написано 3.0, а нам нужно набрать 3.2 потому что к у нас ядро 3.2 а не 3.0. В итоге у нас оказываются готовые исходники ядра 3.2.0 и дело за малым осталось наложить минорный патч. На сегодня это 3.2.9
$ wget http://www.kernel.org/pub/linux/kernel/v3.0/patch-3.2.9.bz2
$ bzcat patch-3.2.9.bz2 | patch -p1

вот и все сложности, дальше собираем обычным способом.

6 комментариев:

  1. добавил небольшое дополнение в свете нынешних реальностей

    ОтветитьУдалить
  2. Статья информативная и нужная. Спасибо автору за работу.

    ОтветитьУдалить
  3. После добавления патча AUFS в конфигурации ядра появляются параметры
    Maximum number of branches
    Detect direct branch access (bypassing aufs)
    NFS-exportable aufs
    Readdir in userspace
    support for /proc/maps and lsof(1)
    Respect the attributes (mtime/ctime mainly) of special files
    Show whiteouts
    Ramfs (initramfs/rootfs) as an aufs branch
    Fuse fs as an aufs branch
    Hfsplus as an aufs branch
    Debug aufs

    По умолчанию почти все параметры отключены. Нужно ли установить их в yes или лучше всё оставить как есть?

    ОтветитьУдалить
    Ответы
    1. >По умолчанию почти все параметры отключены. Нужно ли установить их в yes или лучше всё оставить как есть?

      зависит от ваших задач)
      скрин с моими опциями:
      http://3.bp.blogspot.com/-YOxFn93MM48/UU0L6Ku7GZI/AAAAAAAAAHc/g76N0qB0Wck/s1600/kernel.png

      Удалить
    2. Присоединяюсь к вопросу. Что будет, если включить все параметры (кроме Debug, с ним ясно)? Если среди них нет таких, могущих повлиять на стабильность системы или сильно замедлить скорость работы, хочу попробовать включить всё, кроме debug.
      Ядро с поддержкой aufs мне нужно для своей сборки, на основе Linux Mint 17.3 Mate.
      Нигде нет внятного объяснения по этим опциям aufs.

      Удалить
    3. ничего страшного не будет. Но мне они, по большей части, кажутся бесполезными. Для чего например мне fuse файловые системы монтировать в качестве слоя aufs? FUSE ФС - это всякие превдо ФС типа ntfs-3g, webdav, sshfs ftpfp и пр. Это же очень тормозные штуки. Зачем? c hfsplus полагаю тоже все не просто. "show whiteouts" - это такие файлики, которые aufs создает каогда удаляется какой-либо файл из RO слоя/branch ФС в этом служебном файле, помечает удаленный файл как несуществующий. Вам обязательно натыкаться на эти файлики? Ну и т.д

      Удалить