Архитектура файловой системы Puppy Linux

С высоты птичьего полёта

Эта диаграмма — картина файловой системы в целом:

На данной диаграмме каждый слой следует рассматривать как отдельную полноценную файловую систему с иерархией директорий от самого »/» (корня). Эти слои расположены друг над другом, и это достигается с применением файловой системы UnionFS. Довольно просто представить, как эта конструкция работает. Например, пусть на «розовом» уровне расположен файл /usr/lib/libgdkxft. Он также будет виден и на «синем» уровне. Однако если синий уровень уже сам содержит файл с точно таким же названием, то он виден уже не будет, так как на него «наслоился» тот же самый файл с более верхнего («розового») уровня.

Вот описание каждого из уровней:

ramdisk
Это файловая система типа tmpfs, существующая в оперативной памяти, в которой создаются и изменяются файлы.
pup_save.3fs
Этот файл — постоянное хранилище, где все ваши данные, настройки, email, установленные пакеты и т.д. сохранены на постоянной основе. ».3fs» означает, что файл содержит файловую систему типа ext3.
pup_xxx.sfs
Этот файл и есть Puppy. Встроенные приложения, оконный менеджер, скрипты — всё это. Здесь ».sfs» означает, что данный файл содержит сжатую файловую систему типа squashfs, а «xxx» — номер версии Puppy. Например, для Puppy 3.01 «ххх» будет «301».
*_xxx.sfs
Эти файлы содержат дополнительные файловые системы типе squashfs. Здесь «*» может быть чем угодно. Например, devx_xxx.sfs — полная среда программирования С/С++.

Вот, что, однако, тут следует отметить. Всё это хозяйство находится «под ковром». Когда мы работаем с Puppy, то всё, что мы видим (с нашего верхнего уровня) — это единая файловая система. Таким образом, в нашем примере, мы видим файл /usr/lib/libgdkxft.so, и для нас не будет иметь значения, на каком уровне он находится на самом деле.

Заманчивая альтернатива уже существующей файловой системе squashfs — использовать какой-нибудь существующий дистрибутив линукса (underdog.lnx) как нижний уровень для неё:

Что приведенная выше диаграмма скрывает — это то, что самый нижний уровень в данном случае — раздел диска, а не сам файл underdog.lnx. Сам файл underdog.lnx — это просто текстовый файл, содержащий название этого раздела, например, «hda1».

При загрузке, Puppy прочитает файл underdog.lnx и смонтирует упомянутый в нём раздел как самый нижний уровень. Если окажется, что этот раздел содержит установленный на него дистрибутив линукса, то он целиком будет «просвечивать» через верхние уровни unionfs в Puppy.

При этом, мы будем видеть, как мы запускаем JWM и/или что-то ещё, будем видеть наш рабочий стол, и одновременно целый дистрибутив (из нижнего уровня) будет также доступен нам для работы: все приложения, среды программирования, менеджеры пакетов, и т.д.

Из первых двух диаграмм вы увидели, что уровни unionfs могут быть очень разными. В связи с этим (и еще с кое-чем другим), у Puppy появилась «переменная состояния» — PUPMODE, которая показывает в каком состоянии (конфигурации слоёв) Puppy в настоящий момент запущен. Эта переменная определена в файле /etc/rc.d/PUPSTATE как целое число. Например, «12». Каждое значение PUPMODE требует отдельного описания.

Первичная загрузка с live-CD - PUPMODE 5

Puppy запускается в этой конфигурации, когда вы впервые загружаете его с live-CD или USB-накопителя. Мы поговорим о примере с live-CD, поскольку наиболее вероятно, что это ваш самый первый опыт общения с Puppy.

В первый раз, когда вы вставляете live-CD и загружаетесь с него, у вас еще не определено никакого постоянного места хранения данных, и unionfs включает только два слоя: верхний — ваши «файлы для работы» (файловая система типа tmpfs в оперативной памяти) и нижний — pup_xxx.sfs (сжатая файловая система типа squashfs на live-CD), содержащий все файлы Puppy. Эти два слоя оказываются наложенными друг на друга, начиная с корневой директории »/», однако, также могут быть рассмотрены независимо — через их точки монтирования. Файловая система tmpfs смонтирована на чтение и запись в точку /initrd/pup_rw, а файловая система из pup_xxx.sfs смонтирована только на чтение в точку /initrd/pup_ro2.

Таким образов, вы имеете возможность использовать Puppy, при этом не трогая ваш жесткий диск. Вы можете запускать приложения, конфигурировать, скачивать файлы из интернета, устанавливать пакеты, но всё это будет производиться только в tmpfs в оперативной памяти. Это и есть, так называемый, ramdisk. Объём рамдиска зависит от того, как много в вашем компьютере оперативной памяти (а еще, от того, есть ли у вас на жестких дисках своп-разделы линукса).

Интересное начинается, когда вы собираетесь закончить работу и выключить компьютер. Тогда запускается скрипт выключения /etc/rc.d/rc.shutdown, выводящий на экран окно, в котором спрашивает, хотите ли вы сохранить данную сессию. То есть, какие бы директории и файлы ни были созданы в рамдиске, все они в тот момент могут быть сохранены.

Тут появляется выбор. Если вы загрузились с live-CD, скорее всего, вы захотите сохраниться на жесткий диск, однако вы также можете выбрать и гибкий диск, USB-носитель, или даже тот же самый CD/DVD, с которого вы загрузились (при условии, что он записан в режиме мультисессии). Это очень просто: окно диалога предложит список разделов вам на выбор. При этом, окно диалога, также на выбор, предложит сохранить рамдиск вашей с сессией либо в виде файла, либо в виде раздела. В результате, например, будет создан файл pup_save.3fs, содержащий файловую систему ext3, размером 512МБ (как часто выбирают), или же, как альтернативу, вы выберите какой-либо существующий раздел, в который произведется сохранение. Часто выбор между файлом или разделом определяется тем, содержит ли раздел файловую систему одного из линукс-совместимых типов (напр. ext2, ext3, reiserfs). Если доступны только файловые системы типа FAT, то подойдёт только вариант с созданием файла pup_save.3fs.

Пока, допустим, мы сохранились в этом файле.

Вторичная загрузка с live-CD - PUPMODE 12

Итак, вы снова загружаете live-CD, зная, что сохранили свою предыдущую рабочую сессию в файле pup_save.3fs раздела жёсткого диска с файловой системой линукса, FAT, или NTFS. При этом Puppy загрузится в режиме PUPMODE 12.

PUPMODE 12

Что произошло? Puppy обнаружил файл pup_save.3fs на жестком диске и решил смонтировать файловую систему из этого файла прямо в самый верхний уровень. Таким образом, тут нет никакого промежуточного рамдиска с его временной файловой системой tmpfs. Теперь в файл pup_save.3fs запись и чтение производятся напрямую. Этот сценарий очень подходит для загрузки на компьютерах с малым размером оперативной памяти. Рамдиск начальной стадии загрузки, то есть, файл initrd.gz присутствует в оперативной памяти, однако его размер составляет всего 1.9МБ.

Вторичная загрузка с USB - PUPMODE 13

Если вы установили Puppy на USB-флеш-накопитель, то, используя Универсальный Инсталлятор или вручную, вы создали загрузочный носитель с файлами vmlinuz (ядро линукса), initrd.gz (рамдиск начальной загрузки), pup_xxx.sfs (файловая система типа squashfs со всеми файлами ОС Puppy) и syslinux.cfg (конфигурационный файл программы syslinux). Ситуация напоминает таковую с live-CD — при первой загрузке Puppy будет в состоянии PUPMODE 5, поскольку никаких постоянных хранилищ информации пока им не создано. При первом выключении, как описывалось выше в разделе про PUPMODE 5, вы создадите постоянное хранилище — либо файл pup_save.3fs, либо целый раздел.

При второй загрузке, Puppy обнаружит это постоянное хранилище, но в этом случае поймет, что сам-то он расположен на носителе, для которого следует серьёзно ограничивать частоту записи. Поэтому Puppy запустится в режиме PUPMODE 13.

PUPMODE 13

На приведенной диаграмме самый верхний слой занимает файловая система tmpfs рамдиска, в которую попадут все вновь созданные или модифицированные директории. Это — рабочая зона, и она имеет потенциальные огранчения в соответствии с количеством доступной оперативной памяти. Но, конечно, если у вас на жестком диске есть готовый своп-раздел линукса, то Puppy его задействует для повышения эффективного размера рамдиска.

В случае наличия постоянного хранилища информации на флеш-накопителе, смонтированном во втором уровне («оранжевом»), Puppy будет сохранять в него всё с самого верхнего уровня с периодичностью в 30 минут. С точки зрения unionfs, второй уровень монтрован только для чтения, и только в самый верхний уровень разрешена запись, однако Puppy способен периодически брать верхний уровень целиком и «сливать» его на нижний уровень. Тут надо отметить одну техническую особенность. «Сливать» — это не совсем точное выражение. В идеале, этого хотелось бы — сохранять всё на уровень постоянного хранения, и при этом каждый раз получать полностью свободный рамдиск. Тем не менее, когда я (Барри — прим. перев.) попытался это сделать, то тем самым разрушил unionfs. Так что, компромисс заключается в том, что содержимое самого верхнего уровня копируется (а не перемещается-«сливается» — прим. перев.) вниз на предыдущий уровень. И такой режим unionfs официально поддерживает. Иными словами, это означает, что рамдиск никогда по-настоящему не «сливается», и если вы загрузили много всякой всячины или инсталлировали достаточно большой пакет, то он может забиться под завязку. Чтобы обойти эту проблему, в Puppy имеется программа, запущенная в фоновом режиме (демон), которая предупредит вас, если свободная оперативная память будет на исходе. И если так случилось, то ваше спасение просто: перезагрузитесь.

Уточнение по поводу второго (сверху) уровня. Это — постоянное хранилище, но, как обсуждалось выше в секции PUPMODE 5, при первом выключении вам предлагают создать такое хранилище либо в файле (pup_save.3fs), либо в разделе носителя информации. В последнем случае, рабочая сессия может быть сохранена в целом разделе, но только если это раздел с файловой системой линукса. Тогда то, что монтируется как второй уровень будет разделом, а не файлом. В случае загрузки с USB-флеш накопителя, использовать целый раздел носителя для хранения персональных данных было бы очень неплохо. Файл pup_save.3fs имеет ограниченный размер, обычно 512МБ или еще меньше, если на разделе накопителя, куда он сохраняется, не так много свободного места. Кроме того, были сообщения на Puppy-форуме, что этот файл может быть увеличен только до размера 750МБ–1ГБ. Ну, полагаю, что последнее — не самая страшная проблема для большинства флеш-накопителей.

Если вы выбрали сохранение рабочей сессии прямо на раздел USB-носителя, то это должен быть линукс-раздел, то есть раздел с файловой системой ext2, ext3, или reiserfs. В то же время, сохранение файла pup_save.3fs может производиться на любой тип раздела накопителя — их обычно выпускают с файловой системой FAT16, что вполне подойдёт.

Полная инсталляция - PUPMODE 2

Многие пользователи Puppy знают, что существует 2 способа установки Puppy на жесткий диск: то, что мы называем «опицией 1» и «опцией 2».

Опция 1 — выбор с наименьшим проникновением в систему (наименее инвазивный), при котором просто производится копирование файлов vmlinuz, initrd.gz и pup_xxx.sfs в выбранный раздел носителя. Чтобы загружать Puppy, установленный таким способом, Универсальный Установщик предлагает создать загрузочный гибкий диск. Однако, в настоящий момент, загрузочный гибкий диск может загрузить только тот Puppy, который установлен на раздел с файловой системой FAT. Для загрузки Puppy также возможно сконфигурировать программы Grub или Lilo. Преимущество данной опции в том, что при таком раскладе установка Puppy никак не влияет на какие-либо другие разделы носителя, и, если на них уже установлены Windows или другие дистрибутивы Linux, то они останутся совершенно нетронутыми. При Опции 1 загрузка Puppy проходит в режиме PUPMODE12.

Опция 2 означает, что Puppy устанавливается на целый раздел, который должен быть линукс-разделом (ext2, ext3, reiserfs). Это рекомендуется разработчикам или вообще всем, кто собирается компилировать программы, так как такой способ экономит свободное место на диске.

PUPMODE 2

Это — самая простая конфигурация из всех. Нет никакого рамдиска, а сам раздел смонтирован прямо на самый верхний уровень. На самом деле, если не надо загружать никаких ».sfs», то Puppy не будет использовать unionfs, поскольку нет никаких слоев.

PDEV1 , обозначенная на диаграмме — просто переменная в файле /etc/rc.d/PUPSTATE, содержащая название раздела, например hda1, который монтируется прямо в »/« (корень). Но как узнать, какое значение примет PDEV1 во время загрузки? Утверждать, что это значение записано в файле /etc/rc.d/PUPSTATE , который находится на загружаемом разделе — это все равно, что говорить, что яйцо было раньше курицы (яйцо действительно было раньше курицы, но Барри об этом, видимо, не знал — прим перев). Этот файл, в действительности существует только для скриптов, которым нужно знать, откуда Puppy загрузился. В данном случае, Puppy загружается с гибкого диска, с загрузочного USB-флеш накопителя или с помощью Grub или Lilo. Универсальный Установщик дает возможность задействовать на выбор: гибкий диск, USB или Grub.

Мультисессия на CD/DVD - PUPMODE 77

Данный режим используется для мультисессионных CD и DVD. В этом случае, наше постоянное хранилище состоит из папок на CD/DVD. Каждый раз при вылючении сессия сохраняется в физически новых папках, так что последние постепенно накапливаются на CD/DVD. При загрузке, эти папки считываются системой в обратном порядке и загружаются в рамдиск.

PUPMODE 77

Считваются они во второй уровень («оранжевый»). Во время рабочей сессии, вновь созданные, а также изменённые директории и файлы будут записаны в самый верхний («зеленый») уровень. При выключении, этот уровень записывается на CD/DVD как новая папка.

Логика в нумерации режимов PUPMODE

Недавно я обсуждал режимы PUPMODE с пользователем Phil на форуме Puppy. Он загрузил Puppy2, используя GRUB , при этом имея initrd.gz, vmlinuz и pup_xxx.sfs (т.е все необходимые файлы Puppy) на одном из разделов жесткого диска. После этого он сохранил рабочую сессию прямо в загрузочный раздел.

Будем считать, что раздел называется /dev/hda2. Тогда ситуация выглядит так:

partition hda2
все сохраненные сессии тут, включая файл /etc/puppyversion.
/initrd.gz
/pup_xxx.sfs
/vmlinuz

Я упомянул файл /etc/puppyversion, поскольку он сохраняется во время каждой сессии, и Puppy ищет его при загрузке, чтобы определить, имеются ли в данном разделе диска файлы, сохраненные Puppy. Обратите внимание, что файлы initrd.gz, pup_xxx.sfs и vmlinuz могут находиться в папках, например в /boot, при условии, конечно, что для этого вы также сконфигурируете GRUB соответствующим образом. В случае Phil, Puppy запустится в режиме PUPMODE 7, но чем это определяется? Какая логика стоит за этим?

PUPMODE — это двоичное число, где каждый бит (под номером … 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) является флагом:

БИТ ДЕСЯТ.ВЕЛИЧИНА ЗНАЧЕНИЕ
0 1 используется для самого верхнего уровня tmpfs (/initrd/pup_rw) системы unionfs.
1 2 загрзочный раздел (PDEV1) содержит сохранённые сессии Puppy.
2 4 в загрузочном разделе (PDEV1) существует файл pup_xxx.sfs
3 8 существует файл pup_save.3fs (в котором сохранены сессии).
4 16
5 32
6 64 флаг мультисессий.

Например, мульсессионный CD/DVD запускается с PUPMODE=77, что есть 1+4+8+64. Вполне логично, как видите.

В случае Phil, Puppy запустится в режиме PUPMODE=6 (2+4), и, поскольку сохранённые сессии находятся на быстром носителе с неограниченным возможным числом операций записи, то тогда становится не нужно хранить в оперативной памяти самый верхний уровень — tmpfs. Обратите внимание, что этот tmpfs используется как промежуточное депо для сохранения рабочих файлов и директорий, и его существование оправдано только тогда, когда мы органичены по числу записей на носитель (напр. Флеш-память), или, когда текущая сессия должна быть сохранена на очень медленный носитель и/или только в строго определённое время (запись на дорожку мультисессионного CD/DVD), или же, когда мы заведомо хотим использовать всю систему, загруженной прямо в память (как бывает при первой загрузке Puppy).

Полная инсталляция Puppy («опция 2») запускается в режиме PUPMODE 2.

Решение о том, какой режим загрузки использовать, принимается внутри первого заргузочного скрипта /initrd/sbin/init.

Хотя, в случае полной установки на жесткий диск, файл initrd.gz не используется совсем, и первым запущенным скриптом является /etc/rc.d/rc.sysinit. Puppy полагает PUPMODE=2.

Начальный рамдиск

Первое событие, которое происходит при загрузке системы — это загрузка ядра линукса vmlinuz в оперативную память. За ним идет начальный рамдиск, который находиться в файле initrd.gz. Обратите внимание, что исключение составляет PUPMODE 2, т.е. вариант полной инсталляции на жесткий диск, в случае которой отдельного файла initrd.gz не существует (не совсем понимаю, что Барри имеет в виду, т.к., из моего личного опыта, при полной инсталляции, такой файл существует, правда, не обязательно в корневой директории раздела, а в там, откуда загружается GRUB. — прим. перев.). Когда начальный рамдиск загружен, на выполнение запускается скрипт /sbin/init. Ниже этот скрипт приведен в упрощенном виде для иллюстрации.

#!/bin/ash<BR>#(c) Copyright 2006 Barry Kauler, www.puppylinux.com
PATH="/bin:/sbin"
KERNVER="`uname -r`"
 
#Параметры загрузки PMEDIA= usbflash|usbhd|usbcd|ideflash|idehd|idecd|idezip|satahd|scsihd|scsicd
#уровни unionfs:              RW            RO1             RO2             PUPMODE
#только PUPSFS:               tmpfs                         pup_xxx.sfs      5
#PDEV1 с puppy:               tmpfs         PDEV1*                           3
#PDEV1 с puppy, без tmpfs:    PDEV1                                          2
#PDEV1, PUPSFS:               tmpfs         PDEV1*          pup_xxx.sfs      7
#уровни unionfs:              RW            RO1             RO2              PUPMODE
#найденный PUPSFS, PUPSAVE:   tmpfs         pup_save.3fs*   pup_xxx.sfs      13
#PUPSAVE(мульти), PUPSFS:     tmpfs         folders(tmpfs2) pup_xxx.sfs      13+64 = 77
#PUPSAVE, PUPSFS, no tmpfs:   pup_save.3fs                  pup_xxx.sfs      12
 
fsfunc() #аргумент этой функции --- тип файловой системы
{
 FSTYPE="$1"
 [ "$FSTYPE" = "Ext2" ] && FSTYPE="ext2"
 [ "$FSTYPE" = "ReiserFS" ] && FSTYPE="reiserfs"
 [ "$FSTYPE" = "Ext3" ] && FSTYPE="ext3"
 [ "$FSTYPE" = "FAT16" ] && FSTYPE="vfat"
 [ "$FSTYPE" = "FAT32" ] && FSTYPE="vfat"
 [ "$FSTYPE" = "NTFS" ] && FSTYPE="ntfs"
 [ "$FSTYPE" = "ISO9660" ] &&  FSTYPE="iso9660"
}
 
guesspmediafunc() {
 код для определения PMEDIA, если этот параметр не задан при загрузке ядра
}
 
ispupfunc() #fstype partition
{
 код для поиска постоянного хранилища(раздела)
}
 
searchsavefunc() {
 код для поиска постоянного хранилища в виде фаила pup_save.3fs
}
 
findpupfunc() #аргументы --- список дисков (не разделов!) для поиска
{
 код для поиска загрузочного раздела и загрузоных файлов puppy
}
 
loadfolders() { #арг.: папка-источник, целевая-папка
 код для загрузки папок с сохраненными сессиями на мультисессионных CD/DVD
}
 
mount -t proc none /proc
PUPPYVERSION="`cat /PUPPYVERSION`"
 
####ЗАГРУЗКА МОДУЛЕЙ###########################################
 
код для загрузки модулей ядра
 
###########################################################
 
PDEV1=""
case $PMEDIA in
 idecd)
  CDDRIVES="`test-eide | grep '\- cdrom \-' | cut -f 1 -d " " | cut -f 3 -d "/" | tr "\n" " "`"
  findpupfunc $CDDRIVES
  #возвращает, например: PDEV1=hdc FSTYPE=iso9660 PUPSFS=pup_001.sfs
  [ ! "$PUPSAVE" ] && searchsavefunc #search for PUPSAVE.
  ;;
 idehd)
  HDDRIVES="`test-eide | grep '\- disk \-' | cut -f 1 -d " " | cut -f 3 -d "/" | tr "\n" " "`"
  findpupfunc $HDDRIVES
  #возвращает, например: PDEV1=hda3 FSTYPE=ext3 PUPSFS=pup_001.sfs
  ;;
 usbflash)
  #тут может быть и "superfloppy" без mbr и разделов.
  USBDRIVES="`cat /proc/partitions | grep "sd[a-z]$" | tr -s " " | cut -f 5 -d " " | tr "\n" " "`"
  findpupfunc $USBDRIVES
  #возвращает, например: PDEV1=sda1 FSTYPE=ext2 PUPSFS=pup_001.sfs DEV1PUP=""
  ;;
 ideflash)
  #Похоже, обычный диск IDE. Однако, для него может потребоваться параметр загрузки ядра "ide=nodma" 
  HDDRIVES="`test-eide | grep '\- disk \-' | cut -f 1 -d " " | cut -f 3 -d "/" | tr "\n" " "`"
  findpupfunc $HDDRIVES
  #возвращает, например: PDEV1=hda1 FSTYPE=ext3 PUPSFS=pup_001.sfs
  ;;
 idezip) код
  ;;
 satahd) код
  ;;
 satacd) код
  ;;
 usbhd) код
  ;;
 *)
  #загрузим носитель usb...
  usbstoragefunc #loads usb-storage.o 
  ALLDRIVES="`probedisk | grep '^/dev/' | cut -f 1 -d '|' | cut -f 3 -d '/' | tr "\n" " "`"
  findpupfunc $ALLDRIVES
  #возвращает, например: PDEV1=hdc FSTYPE=iso9660 PUPSFS=pup_001.sfs
  guesspmediafunc
  [ ! "$PUPSAVE" ] && searchsavefunc #search for PUPSAVE.
  ;;
esac
#выходим с PDEV1=полное-имя-устройства
 
#всего оперативной памяти, кроме зарезервированной под видеопамять
PCRAMSIZE=`free | head -n 2 | tail -n 1 | tr -s " " | cut -f 3 -d " "`
SIZEFILLK=`expr $PCRAMSIZE \/ 2` #половина памяти.
#для машин с 128M оперативной памяти надо извернуться, чтобы загрузить pup_xxx.sfs в память.
if [ $SIZEFILLK -gt 50000 ];then
 [ $SIZEFILLK -lt 70000 ] && SIZEFILLK=71680 #70M.
fi
PHYSICALFILLK="$SIZEFILLK"
 
#узнаем, есть ли доступный нам своп-раздел
SWAPINFO="`fdisk -l | grep "Linux swap" | head -n 1`"
if [ ! "$SWAPINFO" = "" ];then
 #теперь мы можем сделать действительно большой рамдиск
 SWAPPART="`echo "$SWAPINFO" | cut -f 1 -d " "`"
 SWAPSIZE=`fdisk -s $SWAPPART`
 SWAPSIZ4=`expr $SWAPSIZE \/ 4`
 SWAPSIZ2=`expr $SWAPSIZE \/ 2`
 SWAPSIZE=`expr $SWAPSIZ2 + $SWAPSIZ4` #3/4 of original
 SIZEFILLK=`expr $SIZEFILLK + $SWAPSIZE`
 echo "Загружаю своп-раздел $SWAPPART..."
 swapon $SWAPPART
fi
 
#монтируем главную файловую систему Puppy
PUPMODE=1                                             #используем tmpfs.
[ "$DEV1PUP" = "yes" ] && PUPMODE=`expr $PUPMODE + 2` #PDEV1 с puppy installed.
[ "$PUPSFS" ] && PUPMODE=`expr $PUPMODE + 4`           #pup_xxx.sfs exists (on PDEV1).
[ "$PUPSAVE" ] && PUPMODE=`expr $PUPMODE + 8`          #pup_save.3fs exists.
if [ $PUPMODE -eq 13 ];then
 #multisession cd, run in PUPMODE 13+64=77
 [ ! "`echo -n "$PUPSAVE" | grep '/20[0-9][0-9]'`" = "" ] && PUPMODE=77
fi
 
UMNT1='/pup_rw=rw'
DEV1MNT="/pup_ro1"
case $PUPMODE in
 3) #PDEV1 с puppy, и испольуем tmpfs (возможно).
  echo -n "Mounting /dev/${PDEV1}..."
  if [ ! "`echo -n "$PMEDIA" | grep --extended-regexp "idehd|satahd|scsihd"`" = "" ];then
  #не использовать unionfs, а просто монтироваться на /pup_new вместо /pup_rw
   mount -o rw -t $FSTYPE /dev/$PDEV1 /pup_new
   PUPMODE=2
  else
   mount -o ro -t $FSTYPE /dev/$PDEV1 /pup_ro1
   mount tmpfs /pup_rw -t tmpfs -o size=${SIZEFILLK}k
   UMNT1='/pup_rw=rw:/pup_ro1=ro'
  fi
  ;;
 5) #есть tmpfs, нет уровня для постоянного хранения, есть pup_xxx.sfs
  # ***ПРИ ПЕРВОМ ЗАПУСКЕ***
  echo -n "Монтирую ${PDEV1}..."
  mount -o rw,noatime -t $FSTYPE /dev/$PDEV1 /mnt/dev_ro1
  echo -n "Монтирую tmpfs..."
  mount tmpfs /pup_rw -t tmpfs -o size=${SIZEFILLK}k
  UMNT1='/pup_rw=rw:/pup_ro2=ro' 
 #если хватит места в рамдиске, тогда скопируем эту ф.с.
  if [ $SIZEFILLK -gt `du /mnt/dev_ro1/$PUPSFS | cut -f 1` ];then
   echo -n "Копирую $PUPSFS в рамдиск..."
   cp -f /mnt/dev_ro1/$PUPSFS /pup_rw/
   sync
   losetup /dev/loop0 /pup_rw/$PUPSFS
   echo "Размонтирую ${PDEV1}..."
   umount /dev/$PDEV1
  else
   echo -n "Монтирую $PUPSFS прямо с /dev/$PDEV1..."
   losetup /dev/loop0 /mnt/dev_ro1/$PUPSFS
  fi
  mount -r -t squashfs -o noatime /dev/loop0 /pup_ro2
  ;;
 7) #есть tmpfs, есть PDEV1 - постоянное хранилище, есть pup_xxx.sfs.
  echo -n "Монтирую /dev/${PDEV1}..."
  mount -o rw -t $FSTYPE /dev/$PDEV1 /pup_ro1
  echo -n "Монтирую tmpfs..."
  mount tmpfs /pup_rw -t tmpfs -o size=${SIZEFILLK}k
  UMNT1='/pup_rw=rw:/pup_ro1=ro:/pup_ro2=ro'
  #если хватает места в рамдиске, скопируем туда эту ф.с.
  if [ $SIZEFILLK -gt `du /pup_ro1/$PUPSFS | cut -f 1` ];then
   echo "Копирую $PUPSFS в рамдиск..."
   cp -f /pup_ro1/$PUPSFS /pup_rw/
   sync
   losetup /dev/loop0 /pup_rw/$PUPSFS
  else
   echo "Монтирую $PUPSFS прямо с /dev/$PDEV1..."
   losetup /dev/loop0 /pup_ro1/$PUPSFS
  fi
  echo -n "Монтирую ${PUPSFS}..."
  mount -r -t squashfs -o noatime /dev/loop0 /pup_ro2
  ;;
 13) #есть tmpfs, есть pup_save.3fs - постоянное хранилище, есть pup_xxx.sfs.
  SAVEFS="`echo -n "$PUPSAVE" | cut -f 1 -d ','`"  #ф.с. и раздел, где расположен pup_save.3fs
  SAVEPART="`echo -n "$PUPSAVE" | cut -f 2 -d ','`"
  SAVEFILE="`echo -n "$PUPSAVE" | cut -f 3 -d ','`"
  echo -n "Монтирую ${PDEV1}..."
  mount -o rw,noatime -t $FSTYPE /dev/$PDEV1 /mnt/dev_ro1
  TMPFSMNT="/pup_rw" #tmpfs --- на самый верх
  EFSMNT="/pup_ro1"  #pup_save.3fs на следующий уровень
  UMNT1='/pup_rw=rw:/pup_ro1=ro:/pup_ro2=ro'
  #возможно, скатимся на PUPMODE=12...
  if [ ! "`echo -n "$PMEDIA" | grep --extended-regexp "idecd|usbcd|scsicd"`" = "" ];then
   #Грузимся с cd. Если есть pup_save.3fs на быстром носителе, загружаем его напрямую
   [ ! "`echo -n "$SAVEPART" | grep '^hd'`" = "" ] && SAVEDIRECT="yes"
  fi
  if [ ! "`echo -n "$PMEDIA" | grep --extended-regexp "idehd|satahd|scsihd"`" = "" ];then
   #полагаем, что pup_save.3fs присутствует на этом быстром носителе
   SAVEDIRECT="yes"
  fi
  if [ "$SAVEDIRECT" = "yes" ];then
   #pup_save.sfs находится на быстром носителе, что позволяет неограниченно записывать,  значит tmpfs больше не нужна
   PUPMODE=12 #rc.shutdown не надо будет ничего сохранять
   #still need a tmpfs, to load pup_xxx.sfs...
   TMPFSMNT="/mnt/tmpfs" #tmpfs создана где-то еще
   EFSMNT="/pup_rw"      #pup_save.3fs на самом верхнем уровне
   UMNT1='/pup_rw=rw:/pup_ro2=ro'
  fi
  echo -n "Монтирую tmpfs..."
  mount tmpfs $TMPFSMNT -t tmpfs -o size=${SIZEFILLK}k
  #если достаточно места на рамдиске, скопируем эту ф.с
  if [ $SIZEFILLK -gt `du /mnt/dev_ro1/$PUPSFS | cut -f 1` ];then
   echo -n "Копирую $PUPSFS на рамсдиск..."
   cp -f /mnt/dev_ro1/$PUPSFS $TMPFSMNT/
   sync
   losetup /dev/loop0 $TMPFSMNT/$PUPSFS
   umount /dev/$PDEV1
  else
   echo -n "Монтирую $PUPSFS прямо с /dev/$PDEV1..."
   losetup /dev/loop0 /mnt/dev_ro1/$PUPSFS
  fi
  mount -r -t squashfs -o noatime /dev/loop0 /pup_ro2
  #теперь сделаем PUPSAVE...
  SMNTPT="`mount | grep "/dev/$SAVEPART" | tr -s " " | cut -f 3 -d " "`"
  if [ "$SMNTPT" = "" ];then
   SMNTPT="/mnt/dev_save"
   mount -t $SAVEFS -o noatime,rw /dev/$SAVEPART /mnt/dev_save
  fi
  #собираемся смонтировать pup_save.3fs, но перед тем проверим, надо ли изменить его размер
  if [ -f $SMNTPT/pupsaveresize.txt ];then
   KILOBIG=`cat $SMNTPT/pupsaveresize.txt`
   rm -f $SMNTPT/pupsaveresize.txt
   echo "Увеличиваем $SAVEFILE на $KILOBIG Кбайт, п-ста, подождите..."
   dd if=/dev/zero bs=1k count=$KILOBIG | tee -a $SMNTPT$SAVEFILE > /dev/null
   sync
   e2fsck -y -f $SMNTPT$SAVEFILE
   resize2fs -pf $SMNTPT$SAVEFILE #если нет размера, мы заполним файл целиком
   sync
   sleep 6 #так мы сможем увидеть результат
  fi
  losetup /dev/loop1 $SMNTPT$SAVEFILE
  echo -n "Монтируем ${SAVEFILE}..."
  FILEFS="ext3"
  [ ! "`echo -n "$SAVEFILE" | grep "2fs"`" = "" ] && FILEFS="ext2"
  mount -t $FILEFS -o noatime,rw /dev/loop1 $EFSMNT
  ;;
 77) #multisession cd/dvd
  SAVEFS="`echo -n "$PUPSAVE" | cut -f 1 -d ','`"   #ф.с. И раздел, где расположен файл pup_save.3fs
  SAVEPART="`echo -n "$PUPSAVE" | cut -f 2 -d ','`" # "
  SAVEFILE="`echo -n "$PUPSAVE" | cut -f 3 -d ','`"
  echo -n "Монтирую ${PDEV1}..."
  mount -o rw,noatime -t $FSTYPE /dev/$PDEV1 /mnt/dev_ro1;check_status $?
  TMPFSMNT="/pup_rw" #tmpfs --- на самый верхний уровень
  EFSMNT="/pup_ro1"  #папки с cd - на следующий уровень
  UMNT1='/pup_rw=rw:/pup_ro1=ro:/pup_ro2=ro'
  #выполним PUPSAVE...
  SMNTPT="`mount | grep "/dev/$SAVEPART" | tr -s " " | cut -f 3 -d " "`"
  if [ "$SMNTPT" = "" ];then
   SMNTPT="/mnt/dev_save"
   mount -t $SAVEFS -o noatime,rw /dev/$SAVEPART /mnt/dev_save
  fi
  #личные данные сохранены в датированных папках
  #создадим tmpfs на /pup_ro1 и загрузим в нее папки только в физической памяти
  ALMOSTFILLK=`expr $PHYSICALFILLK \/ 4`
  ALMOSTFILLK=`expr $PHYSICALFILLK - $ALMOSTFILLK`
  mount tmpfs /pup_ro1 -t tmpfs -o size=${ALMOSTFILLK}k;check_status $?
  loadfolders $SMNTPT /pup_ro1
  ALMOSTFILLK=`free | grep 'Mem:' | tr -s " " | cut -f 5 -d " "`
  ALMOSTFILLK=`expr $ALMOSTFILLK \/ 2`
  SIZEFILLK=`expr $ALMOSTFILLK + $SWAPSIZE`
  echo -n "Mounting tmpfs..."
  mount tmpfs $TMPFSMNT -t tmpfs -o size=${SIZEFILLK}k;check_status $?
  #if enough room in ramdisk, copy it...
  if [ $SIZEFILLK -gt `du /mnt/dev_ro1/$PUPSFS | cut -f 1` ];then
   echo -n "Copying $PUPSFS to ramdisk..."
   cp -f /mnt/dev_ro1/$PUPSFS $TMPFSMNT/
   sync
   losetup /dev/loop0 $TMPFSMNT/$PUPSFS
   umount /dev/$PDEV1
  else
   echo -n "Монтируем $PUPSFS прямо с /dev/$PDEV1..."
   losetup /dev/loop0 /mnt/dev_ro1/$PUPSFS
  fi
  mount -r -t squashfs -o noatime /dev/loop0 /pup_ro2;check_status $?
  sync
  umount /dev/$SAVEPART 2>/dev/null
  ;;
 *) #неполная комбинация
  #некуда сохранять и/или puppy не найден.
  echo "ОШИБКА, не могу найти Puppy на носителе $PMEDIA."
  echo "PUPMODE=$PUPMODE PDEV1=$PDEV1"
  sleep 10
  exit
  ;;
esac
 
UMNTRO=""
#Ищем *_$PUPPYVERSION.sfs (ex: dev_200.sfs)...
[ $PUPMODE -eq 7 ] && EXTRASFS="/pup_ro1"
[ $PUPMODE -eq 13 ] && EXTRASFS="`mount | grep "/dev/$SAVEPART" 2>/dev/null | tr -s " " | cut -f 3 -d " "`"
[ $PUPMODE -eq 77 ] && EXTRASFS="`mount | grep "/dev/$SAVEPART" 2>/dev/null | tr -s " " | cut -f 3 -d " "`"
if [ "$EXTRASFS" ];then
 cd $EXTRASFS
 if [ -f underdog.lnx ];then
  #вот тут релизована идея загружать Puppy поверх других дистрибутивов линукса
  UNDERDOGPART="`cat underdog.lnx`"
  UDMNTPT="`mount | grep "/dev/$UNDERDOGPART" | tr -s " " | cut -f 3 -d " "`"
  if [ "$UDMNTPT" = "" ];then
   UNDERDOGFS="`disktype /dev/$UNDERDOGPART | grep 'file system' | head -n 1 | cut -f 1 -d " "`"
   [ "$UNDERDOGFS" = "Ext2" ] && UNDERDOGFS="ext2"
   [ "$UNDERDOGFS" = "Ext3" ] && UNDERDOGFS="ext3"
   [ "$UNDERDOGFS" = "Reiserfs" ] && UNDERDOGFS="reiserfs"
   if [ ! "`echo -n "$UNDERDOGFS" | grep --extended-regexp 'ext2|ext3|reiserfs'`" = "" ];then
    mount -r -t $UNDERDOGFS /dev/$UNDERDOGPART /pup_ro3
    if [ $? -eq 0 ];then
     UMNTRO="${UMNTRO}:/pup_ro3=ro"
     #fixes to prevent library clashes...
     MNTFIX='/pup_rw'
     [ ! "`echo -n "$UMNTRO" | grep 'pup_ro1'`" = "" ] && MNTFIX='/pup_ro1'
     [ ! -f $MNTFIX/lib/.wh.i686 ] && touch $MNTFIX/lib/.wh.i686 #hides /lib/i686
     #puppy needs dir name /usr/lib/qt at bootup (see rc.profile)...
     REALQTDIR="`find /pup_ro3/usr/lib -maxdepth 1 -type d -name qt* | tail -n 1 | sed -e 's/\/pup_ro3\/usr\/lib\///g'`"
     if [ "$REALQTDIR" ];then
      if [ ! -e $MNTFIX/usr/lib/qt ];then
       [ "`find /pup_ro3/usr/lib -maxdepth 1 -xtype d -name qt`" = "" ] && ln -s $REALQTDIR $MNTFIX/usr/lib/qt
      fi
     fi
    fi
   fi
  else
   UMNTRO="${UMNTRO}:${UDMNTPT}=ro"
  fi
 
 else
  CNTLOOP=3
  for ONESFS in `ls -1 *_${PUPPYVERSION}.sfs | grep -v "^pup_" | tr "\n" " "`
  do
   losetup-FULL /dev/loop${CNTLOOP} $EXTRASFS/$ONESFS
   mount -r -t squashfs -o noatime /dev/loop${CNTLOOP} /pup_ro${CNTLOOP}
   [ $? -eq 0 ] && UMNTRO="${UMNTRO}:/pup_ro${CNTLOOP}=ro"
   CNTLOOP=`expr $CNTLOOP + 1`
  done
 fi
 cd /
fi
 
#теперь создадим unionfs...
mkdir -p /pup_rw/etc/rc.d
echo -n "$PUPMODE" > /pup_rw/etc/rc.d/PUPMODE #его прочитает rc.shutdown.
echo -n "$PDEV1" > /pup_rw/etc/rc.d/PDEV1     # "
echo -n "$FSTYPE" > /pup_rw/etc/rc.d/DEV1FS     # "
echo -n "$PUPSFS" > /pup_rw/etc/rc.d/PUPSFS   # "
echo -n "$PUPSAVE" > /pup_rw/etc/rc.d/PUPSAVE # "
echo -n "$PMEDIA" > /pup_rw/etc/rc.d/PMEDIA   # "
echo -n "$PUPPYVERSION" > /pup_rw/etc/puppyversion #для проверки на наличие сохраненных сессий при загрузке (ispupfunc)
mkdir /pup_rw/initrd
sync
echo -n "Создаем unionfs с локальным корнем в директории /pup_new..."
mount -t unionfs -o dirs=${UMNT1}${UMNTRO} none /pup_new
 
#надо запустить dnotify из скрипта загрузки...
#/sbin/launchpupsafe &
 
cd /pup_new
sync
umount /proc
#echo "pivot_root переводит загрузку из началького рамдиска в директорию /pup_new..."
pivot_root . initrd
exec chroot . sh -c "exec /bin/busybox init"  <dev/console >dev/console 2>&1

Похоже, тут много чего происходит! Но, на самом деле, всё это довольно просто. По сути, скрипт ищет загрузочный раздел и файл-хранилище или раздел-хранилище, используя параметр загрузки PMEDIA как ориентир. А когда находит их, то определяет соотвтствующий PUPMODE, а затем создает необхдимые уровни unionfs. В самом конце, скрипт выполняет команду pivot_root, которая подключает директорию /pup_new в качестве нового »/« корня файловой системы. Эта директория /pup_new — то место, где слиты воедино все слои unionfs.

Начальный рамдиск оставлен в памяти, и после работы pivot_root он будет перемещен в директорию /initrd. То есть, всё, что было »/», после pivot_root будет в /initrd. И именно в /initrd вы сможете потом получить доступ к слоям по-отдельности. Хотя последнее представляет интерес скорее для разработчиков, чем для пользователей.

Следующий, исполняющийся после pivot_root скрипт — это /etc/rc.d/rc.sysinit. Обратите внимание, что всё внутри директории /etc/rc.d можно редактировать, поскольку всё внутри »/» сохраняется (имеется в виду, что сценарии загрузки можно изменять, сохраняя при этом сессию обычным способом — прим. перев.). Единственный скрипт, который не удастся отредактировать напрямую — это /initrd/sbin/init — скрипт загрузки из начального рамдиска.

Продолжение следует… ^_^

Печать/экспорт