Файловые системы в Linux
XFS — начало разработки 1993 год, фирма Silicon Graphics, в мае 2000 года предстала в GNU GPL, для пользователей большинства Linux систем стала доступна в 2001-2002 гг. Отличительная черта системы — прекрасная поддержка больших файлов и файловых томов, 8 эксбибайт — 1 байт (8*2 60 -1 байт) для 64-х битных систем. Ко всему прочему обладает другими немаловажными особенностями — непрерывные области дискового пространства, задержка выделения пространства и онлайн дефрагментация. Является одной из старейших журналируемых файловых систем для *nix, и содержит в себе наиболее отлаженный, в этом контексте, исходный код.
ReiserFS (Reiser3) — одна из первых журналируемых файловых систем под Linux, разработана Namesys. Имеет некоторые врождённые головные боли, но в целом неплохая система, ведущая отсчёт дней своих с 2001 года. Оговорюсь, что смысл журналируемых систем заключается в дисковых транзакциях, которые последовательно пишутся в специальную зону диска (журнал, он же лог), перед тем как данные попадают в конечные точки файловой системы. Максимальный объём тома для этой системы равен 16 тебибайт (16*2 40 байт).
JFS (Journaled File System) — файловая система, детище IBM, явившееся миру в далёком 1990 году для ОС AIX (Advanced Interactive eXecutive). В виде первого стабильного релиза, для пользователей Linux, система стала доступна в 2001 году. Из плюсов системы — неплохая масштабируемость. Из минусов — не особо активная поддержка на протяжении всего жизненного цикла. Максимальный рамер тома 32 пэбибайта (32*2 50 байт).
ext (extended filesystem) — появилась в апреле 1992 года, это была первая файловая система, изготовленная специально под нужды Linux ОС. Разработана Remy Card с целью преодолеть ограничения файловой системы Minix.
ext2 (second extended file system) — была разработана Remy Card в 1993 году. Не журналируемая файловая система, это был основной её недостаток, который исправит ext3.
ext3 (third extended filesystem) — по сути расширение исконной для Linux ext2, способное к журналированию. Разработана Стивеном Твиди (Stephen Tweedie) в 1999 году, включена в основное ядро Linux в ноябре 2001 года. На фоне других своих сослуживцев обладает более скромным размером пространства, до 4 тебибайт (4*2 40 байт) для 32-х разрядных систем. На данный момент является наиболее стабильной и поддерживаемой файловой системой в среде Linux.
Reiser4 — первая попытка создать файловую систему нового поколения для Linux. Впервые представленная в 2004 году, система включает в себя такие передовые технологии как транзакции, задержка выделения пространства, а так же встроенная возможность кодирования и сжатия данных. Ханс Рейзер (Hans Reiser), главный разработчик системы, рекламировал использовать своё детище непосредственно как БД с улучшенными метаданными. После того, как Ханс Рейзер был осуждён за убийство в 2008 году, дальнейшая судьба системы стала сомнительной.
ext4 — попытка создать 64-х битную ext3 способную поддерживать больший размер файловой системы (1 эксбибайт). Позже добавились возможности — непрерывные области дискового пространства, задержка выделения пространства, онлайн дефрагментация и прочие. Обеспечивается прямая совместимость с системой ext3 и ограниченная обратная совместимость при недоступной способности к непрерывным областям дискового пространства.
UPD: Btrfs (B-tree FS или Butter FS) — проект изначально начатый компанией Oracle, впоследствии поддержанный большинством Linux систем. Многие считаеют систему эдаким ответом на ZFS. Ключевыми особенностями данной файловой системы являются технологии: copy-on-write, позволяющая сделать снимки областей диска (снапшоты), которые могут пригодится для последующего восстановления; контроль за целостностью данных и метаданных (с повышенной гарантией целостности); сжатие данных; оптимизированный режим для накопителей SSD (задаётся при монтировании) и прочие. Немаловажным фактором является возможность перехода с ext3 на Btrfs. С августа 2008 года данная система выпускается под GNU GPL.
Tux2 — известная, но так и не анонсированная публично файловая система. Создатель Дэниэл Филипс (Daniel Phillips), система базируется на алгоритме «Фазового Дерева», который как и журналирование защищает файловую систему от сбоев. Организована как надстройка на ext2.
Tux3 — наступая на пятки Btrfs, представлена новая файловая система. Система создана на основе FUSE (Filesystem in Userspace), специального модуля для создания файловых систем на *nix платформах. Данный проект ставит перед собой цель избавиться от привычного журналирования, взамен предлагая версионное восстановление (состояние в определённый промежуток времени). Преимуществом используемой в данном случае версионной системы, является способ описания изменений, где для каждого файла создаётся изменённая копия, а не переписывается текущая версия. Такой подход позволяет более гибко управлять версиями.
UPD: Xiafs — задумка и разработка данной файловой системы принадлежат Frank Xia, основана на файловой системе MINIX. В настоящее время считается устаревшей и практически не используется. Наряду с ext2 разрабатывалась, как замена системе ext. В декабре 1993 года система была добавлена в стандартное ядро Linux. И хотя система обладала большей стабильностью и занимала меньше дискового пространства под контрольные структуры — она оказалась слабее ext2, ведущую роль сыграли ограничения максимальных размеров файла и раздела, а так же способность к дальнейшему расширению.
UPD: ZFS (Zettabyte File System) — изначально созданная в Sun Microsystems файловая система, для небезызвестной операционной системы Solaris в 2005 году. Отличительные особенности — отсутствие фрагментации данных как таковой, возможности по управлению снапшотами (snapshots), пулами хранения (storage pools), варьируемый размер блоков, 64-х разрядный механизм контрольных сумм, а так же способность адресовать 128 бит информации! В Linux системах может использоваться посредствам FUSE.
Источник
Building linux file system
WSLFS — Windows Subsystem for Linux File System (For Linux)
The purpose of this repository is to create a file system for linux (Not the windows linux subsystem) that can read the windows subsystem file system. This would allow someone to log into there linux partition on a dual boot system and access there windows windows wsl filesystem.
NOTE: This probably needs more testing. This seems to work with regular files/folders and with symlinks but has not been tested with any other file types and likely will not work with any other file types.
If you have different user mapping from those in your Windows Subsystem for Linux then I reccomend you use bindfs to map your username and possibly group names to your current system to avoid messing up your windows wsl permissions.
Use ntfs-3g to mount the windows partition (Make sure it is the latest version)
To umount use the fusermount command.
To run and build wslfs the following dependencies are required.
For a debian based distribution you can install libfuse-dev using apt-get
ntfs-3g may have to be installed from source. Follow instructions at http://www.tuxera.com/community/open-source-ntfs-3g/
I am using version ntfs-3g version ntfs-3g 2016.2.22 (We will consider that the minimum version)
Build from source
To build from source, do the following, make sure that you satisfy the dependencies. Check-out the repository and run:
If you want to install PTFS into a certain directory simply set the CMAKE_INSTALL_PREFIX to the appropriate directory.
will install into /tmp/foo.
WSLFS is distributed under the terms of the MIT licence. See the LICENSE file for the full license text
About
Windows Subsystem for Linux File system for Linux
Источник
Пишем файловую систему в ядре Linux
Для кого эта статья
Данная статья составлена по материалам практики по курсу операционных систем в Академическом университете . Материал готовился для студентов, и ничего сложного здесь не будет, достаточно базового знания командной строки, языка C, Makefile и общих теоретических знаний о файловых системах.
Весь материал разбит на несколько частей, в данной статье будет описана вводная часть. Я коротко расскажу о том, что понадобится для разработки в ядре Linux, затем мы напишем простейший загружаемый модуль ядра, и наконец напишем каркас будущей файловой системы — модуль, который зарегистрирует довольно бесполезную (пока) файловую систему в ядре. Люди уже знакомые (пусть и поверхностно) с разработкой в ядре Linux не найдут здесь ничего интересного.
Введение
Файловая система является одной из центральных подсистем ОС. Развитие файловых систем шло вместе с развитием ОС. На настоящий момент мы имеем целый зоопарк самых разнообразных файловых систем от старой «классической» UFS , до новых интересных NILFS (хотя идея совсем не новая, посмотрите на LFS ) и BTRFS . Так что, можно сказать, теория и практика создания файловых систем вполне проработаны. Мы не будем пытаться свергнуть монстров вроде ext3/4 и BTRFS, наша файловая система будет носить исключительно образовательный характер, на ее примере мы будем знакомиться с ядром Linux.
Настройка окружения
Перед тем как лезть в ядро, подготовим все необходимое для сборки нашего модуля файловой системы. У каждого свои предпочтения в дистрибутивах Linux, но мне привычнее использовать Ubutnu, поэтому настройку окружения я буду показывать на ее примере, к счастью это совсем не трудно. Для начала нам понадобятся компилятор и средства для сборки:
sudo apt-get install gcc build-essential
Дальше нам понадобятся исходники ядра, или не понадобятся. Мы пойдем простым путем — не будем пересобирать ядро из исходников, просто установим себе заголовки ядра, этого будет достаточно, чтобы написать загружаемый модуль. Установить заголовки можно так:
sudo apt-get install linux-headers-`uname -r`
Тут я должен сделать небольшое лирическое отступление. Ковыряться в ядре на рабочей машине не самая удачная идея, поэтому настоятельно советую проделывать это все в виртуальной машине. Мы не будем делать ничего опасного, так что сохраненные данные в безопасности, но если
что-то пойдет не так, вероятно придется перезагружать систему, а это довольно быстро надоедает. Кроме того отлаживать модули ядра удобнее в виртуальной машине (такой как QEMU), хотя этот вопрос не будет рассмотрена в этой статье.
Проверяем окружение
Чтобы проверить окружение напишем и запустим модуль ядра, который не будет делать ничего полезного (Hello, World!). Посмотрим на код модуля, я его назвал super.c (не подумайте ничего, super это от superblock):
В самом начале идут два заголовка, считайте, что они обязательная часть любого загружаемого модуля ядра, ничего интересного в них нет. Далее идут две функции aufs_init и aufs_fini — они будут вызваны после загрузки и перед выгрузкой модуля соответственно.
Некоторых из вас может смутить метка __init. __init это подсказка ядру, что функция используется только во время инициализации модуля, а значит, после инициализации модуля эту функцию можно выгрузить из памяти. Аналогичный маркер есть и для данных, впрочем, ядро вполне может игнорировать эти подсказки. Обращение к __init функциям и данным из основного кода модуля это потенциальная ошибка, поэтому во время сборки модуля проверяется, что таких обращений нет. Если же такое обращение найдено, система сборки ядра выдаст предупреждение. Аналогично проверка делается для __exit функций и данных. Если вам интересны детали того, что из себя представляют __init и __exit, то можете обратиться к исходникам .
Обратите внимание, что aufs_init возвращает int. Таким образом ядро узнает, что во время инициализации модуля что-то пошло не так — если модуль вернул не нулевое значение, значит во время инициализация произошла ошибка.
Чтобы указать какие именно функции нужно вызвать при загрузке и выгрузке модуля используются два макроса module_init и module_exit. Они раскрываются в некоторое количество компиляторной магии, но мы не будем углубляться в детали, интересующиеся могут обратиться к lxr и походить по ссылкам (надо сказать, для освоения ядра очень удобная штука).
pr_debug — это функция (на самом деле это макрос, но пока нам это не важно) вывода в лог ядра, очень похожа семейство функций printf с некоторыми расширениями, например, для печати IP и MAC адресов. Полный список модификаторов можно найти в документации к ядру. Вместе с pr_debug, есть целое семейство макросов: pr_info, pr_warn, pr_err и другие. Те кто немного знаком с разработкой модулей Linux наверняка знают про функцию printk, в принципе, макросы pr_* раскрываются в вызовы printk, так что вместо них можно использовать напрямую printk.
Далее идут макросы с информацией для потомков — лицензия и автор. Есть и другие макросы позволяющие сохранить самую разнообразную информацию о модуле, например, MODULE_VERSION, MODULE_INFO, MODULE_SUPPORTED_DEVICE и другие. Кстати, забавный факт, если вы используете лицензию отличную от GPL, то некоторые функции доступные GPL модулям вы использовать не сможете.
Теперь соберем и загрузим наш модуль. Для этого напишем Makefile, который будет собирать наш модуль:
По факту, этот Makefile для сборки вызывает Makefile ядра, который должен находиться в каталоге /lib/modules/$(shell uname -r)/build (uname -r — команда, которая возвращает версию запущенного ядра), если у вас заголовки (или исходники) ядра находятся в другом каталоге, то нужно поправить.
obj-m — позволяет указать имя будущего модуля, в нашем случае модуль будет называться aufs.ko (именно ko — от kernel object). А aufs-objs позволяет указать из каких исходных файлов собирать модуль aufs, в нашем случае будет использоваться всего один файл super.c. Кроме того, можно указать различные флаги компилятора, которые будут использоваться (в дополнение к тем, которые использует Makefile ядра) при сборке объектных файлов, в нашем случае я передаю флаг -DDEBUG при сборке super.c. Если не передавать флаг -DDEBUG, то мы не увидим вывод pr_debug в системном логе.
Чтобы собрать модуль нужно выполнить команду make. Если все хорошо, то в каталоге должен будет появится файл aufs.ko — это наш загружаемый модуль. Загрузить модуль довольно просто:
sudo insmod ./aufs.ko
Чтобы убедиться, что модуль загрузился можно посмотреть на вывод команды lsmod:
lsmod | grep aufs
Чтобы посмотреть системный лог нужно вызвать команду dmesg, и там мы должны увидеть сообщения от нашего модуля. Выгрузить модуль тоже не сложно:
sudo rmmod aufs
Возвращаемся к файловой системе
Итак окружение настроено и работает, мы научились собирать простейший модуль, загружать и выгружать его, пора взяться за файловую систему. Проектирование файловой системы должно начинаться «на бумажке», с основательного продумывания используемых структур данных и прочего. Но мы пойдем простым путем и отложим детали хранения файлов и папок (и много чего другого) на диске на следующий раз, а сейчас напишем каркас нашей будущей файловой системы.
Жизнь файловой системы начинается с регистрации. Зарегистрировать файловую систему можно с помощью вызова register_filesystem . Мы будем регистрировать файловую систему в функции инициализации модуля. Чтобы разрегистрировать файловую систему есть функция unregister_filesystem , и вызывать мы ее будем в функции aufs_fini нашего модуля.
Обе функции принимают как параметр указатель на структуру file_system_type — она будет «описывать» файловую систему, считайте, что это класс файловой системы. Полей в этой структуре хватает, но нас интересуют лишь некоторые из них:
В первую очередь нас интересует поле name, оно хранит название файловой системы, именно это название будет использоваться при монтировании, но об этом позже, просто запомните его.
mount и kill_sb — два поля хранящие указатели на функции. Первая функция будет вызвана при монтировании файловой системы, а вторая при размонтировании. Нам достаточно реализовать всего одну, а вместо второй будем использовать kill_block_super, которую любезно предоставляет ядро.
Поле fs_flags — хранит различные флаги, в нашем случае оно хранит флаг FS_REQUIRES_DEV, который говорит, что нашей файловой системе для работы нужен диск (хотя пока это не так). Можно этот флаг не указывать, все будет прекрасно работать и без него.
Наконец, поле owner нужно для организации счетчика ссылок на модуль. Счетчик ссылок нужен, чтобы модуль не выгрузили раньше времени, например, если файловая система была примонтирована, то выгрузка модуля может привести к краху, счетчик ссылок не позволит выгрузить модуль, пока он используется, т. е. пока мы не размонтируем файловую систему.
Теперь рассмотрим функцию aufs_mount. Она должна примонтировать устройство и вернуть структуру описывающую корневой каталог файловой системы. Звучит довольно сложно, но, к счастью, и тут ядро почти все сделает за нас:
По факту, большая часть работы происходит внутри функции moun_bdev, нас интересует лишь ее параметр aufs_fill_sb — это указатель на функцию (опять), которая будет вызвана из mount_bdev чтобы инициализировать суперблок. Но перед тем как мы перейдем к ней остановимся на важной для файловой подсистемы ядра структуре dentry . Эта структура представляет участок пути в имени файла, например, если мы обращаемся к файлу /usr/bin/vim, то у нас будут экземпляры структуры dirent представляющие участки пути / (корневой каталог), bin/ и vim. Ядро поддерживает кеш этих структур, что позволяет быстро искать inode (еще одна центровая структура) по имени (пути) файла. Так вот, функция aufs_mount должна вернуть dentry представляющую корневой каталог нашей файловой системы, а создаст его функция aufs_fill_sb.
Итак, aufs_fill_sb пока что самая важная функция в нашем модуле, и выглядит она так:
В первую очередь мы заполняем структуру super_block . Что это за структура такая? Обычно, файловые системы хранят в специальном месте раздела диска (это место выбирает файловая система) набор параметров файловой системы, таких как размер блока, количество свободных/занятых блоков, версию файловой системы, «указатель» на корневой каталог, магическое число, по которому драйвер файловой системы может проверить, что на диске хранится именно та самая файловая система, а не что-то еще, ну и прочие данные. Эта структура называется суперблоком (см. картинку ниже). Структура super_block в ядре Linux, в целом, предназначен для схожих целей, мы сохраняем в нем магическое число и dentry для корневого каталога (ту самую, которую возвращает mount_bdev).
Кроме того, в поле s_op структуры super_block мы сохраняем указатель на структуру super_operations — это «методы класса» super_block, т. е. еще одна структура, которая хранит кучу указателей на функции.
Тут я сделаю еще одно отступление, ядро Linux написано на C, т. е. без поддержки различных ООП фич со стороны языка, но структурировать программу следуя идеям ООП можно и без поддержки со стороны языка, поэтому структуры содержащие кучу указателей на функции довольно часто встречаются в ядре — это такой способ реализации полиморфизма подтипов (aka виртуальных функций) имеющимися средствами.
Но вернемся к структуре super_block и ее «методам», мы не будем сейчас углубляться в детали структуры super_operations, нас будет интересовать только одно ее поле — put_super. В put_super мы сохраним «деструктор» нашего суперблока:
Пока функция aufs_put_super ни делает ничего полезного, мы используем ее исключительно чтобы напечатать в системный лог еще одну строчку. Функция aufs_put_super будет вызвана внутри kill_block_super (см. выше) перед уничтожением структуры super_block, т. е. при размонтировании файловой системы.
Теперь вернемся к нашей самой важной функции aufs_fill_sb. Перед созданием dentry для корневого каталога мы должны создать inode корневого каталога. Структура inode является, пожалуй, самой важной в файловой подсистеме, каждый объект файловой системы (файл, папка, специальный файл, журнал и пр) идентифицируется inode. Как и с super_block, структура inode отражает то, как файловые системы хранятся на диске. Название inode происходит от index node, т. е. он индексирует файлы и папки на диске. Обычно внутри inode на диске хранится указание на то, где хранятся данные файла на диске (в каких блоках сохранено содержимое файла), различные флаги доступа (доступен для чтения/записи/исполнения), информация о владельце файла, время создания/модификации/доступа и прочие подобные вещи (см. картинку выше).
Пока мы не умеем читать с диска, так что заполняем inode фиктивными данными. В качестве времени создания/модификации/доступа используем текущее время, а назначение владельца и прав доступа делегируем ядру (вызываем функцию inode_init_owner). Ну наконец создаем dentry связанную с корневым inode.
Проверяем каркас
Каркас нашей файловой системы готов, пора его проверить. Сборка и загрузка драйвера файловой системы ничем не отличается от сборки и загрузки обычного модуля. Вместо реального диска для экспериментов мы будем использовать loop устройство. Это такой драйвер «диска», который пишет данные не на физическое устройство, а в файл (образ диска). Создадим образ диска, пока он не хранит никаких данных, поэтому все просто:
Кроме того, нам нужно создать каталог, который будет точкой монтирования (корнем) нашей файловой системы:
Теперь используя этот образ примонтируем нашу файловую систему:
sudo mount -o loop -t aufs ./image ./dir
Если операция завершилась удачно, то в системном логе мы должны увидеть сообщения от нашего модуля. Чтобы размонтировать файловую систему делаем так:
sudo umount ./dir
И опять проверяем системный лог.
Мы поверхностно познакомились с созданием загружаемых модулей ядра и основными структурами файловой подсистемы. Мы также написали настоящую файловую систему, которая умеет только монтироваться и размонтироваться, пока она довольно глупая, даже сделать cd в корень файловой системы не получится, но мы собираемся исправить это в будущем.
Далее мы планируем рассмотреть чтение данных с диска, для начала мы определим, как мы будем хранить данные на дисках, и научимся читать суперблок и иноды с диска.
Список литературы и ссылки
- Код к статье лежит на github
- Не так давно, один индиец уже писал простую файловую систему с нуля в образовательных целях, он проделал очень большую работу
- Я понимаю, что отправлять новичков к исходникам ядра не очень педагогично (хотя исходники читать полезно), но тем не менее советую всем интересующимся посмотреть на исходники очень простой файловой системы ramfs . Кроме того, в отличие от нашей файловой системы ramfs не использует диск, а хранит все в памяти.
Источник