Файловая система, дешево и быстро

    Разработчикам часто приходится иметь дело с файлами, представляющими из себя древовидную структуру: XML, JSON, YAML, всякого рода языки разметки вроде Markdown или Org-mode. Облегчая в общем и целом нашу жизнь, такие файлы имеют склонность к бесконтрольному росту, в какой-то момент из решения превращаясь в проблему.


    Стандартное решение этой проблемы — разбиение на меньшие файлы. Это, конечно, работает, но не всегда удобно.


    Но существует и альтернатива, о которой — ниже.


    org-mode и его разметка.


    Пожалуй, стоит сначала изложить мою проблему. Я использую Емакс и — как многие пользователи Емакса — для написания почти всех моих документов, заметок, рабочего дневника и списков задач использую язык разметки org-mode. Выглядит документ в этой разметке примерно следующим образом:


    ... простой пример файла из репозитория ...
    > cat tests/simple.org
    document section
    * headline 1
    headline section 1
    ** inner headline 1
    some inner section 1
    some inner section 1-2
    ** inner headline 2
    inner section 2
    ** inner headline 3
    *** inner inner headline 1
    * headline 2
    section text 2

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


    И тогда мне в голову пришло, что было бы здорово ходить по моему файлу как по директориям, при помощи, скажем, стандартных в Юниксах cd headline1 или cd .., ls -l и cat section.


    Иными словами, мне захотелось уметь представлять дерево заголовков и текстовых секций в виде обыкновенного дерева директорий и файлов. В терминах тех же Юниксов это желание звучит следующим образом: смонтировать некую специализированную файловую систему.


    Конечно, писать полноценную файловую систему для Линукса — дело долгое, неблагодарное и уж точно не стоит оно того в такого рода редких случаях.


    FUSE


    Впрочем, в наши дни уже никто так и не делает, то есть с тех пор как лет десять назад в Линукс был включен модуль FUSE, позволяющий делать файловые системы в виде обыкновенного пользовательского процесса, на который из ядра маршрутизируются все связанные со смонтированной файловой системой /системные вызовы.


    С помощью FUSE было написано множество самых разных файловых систем, от игрушечных ФС, монтирующих, например, статьи с Википедии, до вполне серьезных частей современных Линуксов вроде того же Gnome. Таким образом, FUSE стал обязательным элементом популярных дистрибутивов.


    Еще приятней работу с FUSE делает тот факт, что в наши дни доступны совсем уж тривиальные в использовании обертки на высокоуровневых языках вроде Python, Ruby, Java и многих других, т.е. собственную файловую систему можно сделать буквально за два-три часа.


    fusepy


    Конкретно на Питоне оберток вокруг libfuse (клиентской части FUSE) даже несколько, но больше всего мне понравился проект fusepy: код проекта очень простой и понятный, кроме примеров на Гитхабе и исходного кода мне так ничего и не понадобилось.


    Файловая система на базе fusepy сводится к переопределению методов класса fuse.Operations, каждый из которых соответствует какому-либо системному вызову.


    Для непереопределенных системных вызовов есть либо разумное поведение по умолчанию, либо стандартная ошибка.


    Orgfuse


    Собственно, конкретный формат файла, который хочется представить в виде дерева директорий и файлов, не так важен. В случае с разметкой org-mode мне не понравился ни один из доступных парсеров для Питона, и я просто написал собственный. Парсер проходит по указанному файлу, создавая дерево, отражающее структуру документа.


    Дерево разбора (parse tree) файла разметки дальше преобразуется в другое дерево, отражающее файлы и директории, которые будет видеть пользователь файловой системы.


    Чтобы работать с последним деревом было достаточно реализовать четыре системных вызовов (open, read, readdir, getattr), каждый из которых занимал буквально несколько строк кода на Питоне:


    class FuseOperations(Operations):
    
        def __init__(self, tree):
            self.tree = tree
            self.fd = 0
    
        def open(self, path, flags):
            self.fd += 1
            return self.fd
    
        def read(self, path, size, offset, fh):
            node = self.tree.find_path(path)
            if node is None:
                raise FuseOSError(EIO)
            return node.content[offset:offset + size]
    
        def readdir(self, path, fh):
            node = self.tree.find_path(path)
            if node is None:
                raise FuseOSError(EROFS)
            return ['.', '..'] + [child for child in node.children]
    
        def getattr(self, path, fh=None):
            node = self.tree.find_path(path)
            if node is None:
                raise FuseOSError(ENOENT)
            return node.get_attrs()

    Итоговый скрипт работает примерно следующим образом:


    ... монтируем файл как файловую систему ...
    > mkdir mount
    > python orgfuse.py tests/simple.org mount/
    ... открываем другой терминал и наслаждаемся ...
    > tree mount
    mount/
    ├── headline 1
    │   ├── inner headline 1
    │   │   └── section
    │   ├── inner headline 2
    │   │   └── section
    │   ├── inner headline 3
    │   │   └── inner inner headline 1
    │   └── section
    ├── headline 2
    │   └── section
    └── section
    
    6 directories, 5 files

    Все это чудо занимает порядка двух сотен строк или 3-4 часа моей ленивой вечерней работы, с моей маленькой задачей справляется замечательно.


    Инструкции по установке и код, как водится, можно найти Github.


    Если кому интересно преращение прототипа во что-то удобоваримое, с возможностью редактирования файлов и поддержкой большего количества форматов — буду рад пообщаться.

    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 50
    • НЛО прилетело и опубликовало эту надпись здесь
      • –8
        Читал по диагонали, не понял, какую проблему решали то? Что плохого в древовидном json например?
        • –1
          Спасибо за минуса, товарищи. Я перечитал статью. Всё равно не понял, ибо человек сначала придумал себе проблемы, потом их героически решил.
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              Я не понял в чем проблема хранить файлово то, к чему хочется обращаться как к файлам.
              • 0
                Может быть, я не достаточно аккуратно изложил проблему.

                Есть единый сложный файл, который в принципе кормится одной системе как единый файл, и с этим трудно что-то сделать. С другой стороны, хочется с некоторыми ветками дерева этого файла работать прямо из консоли, простыми скриптами.

                Вот и все дела.
                • 0
                  Этот файл вами и создан, как я понимаю. В чём проблема создать N файлов так как вам хочется иерархически?
                  • 0
                    Вы не читаете то, что я уже написал: файл нужен как единое целое в другом месте.

                    Более того, этот скрипт и делает это самое «создать N файлов иерархически» совершенно автоматически.
                    • 0
                      синхронизировать между машинами или каким-либо скриптом обрабатывать один файл все же легче
                      Но синхронизация как раз таки файловая же везде нынче. Файлы — проще. Синхронизация отдельных файлов — быстрее.

                      А по скриптам — так вообще не понял, наоборот же, проще каждому скрипту рассказать в какой файл он должен писать, чем объяснить структуру единого файла.

                      Привязка к одному файлу у вас идеалогическая, имхо, а не техническая.
                      • 0
                        А по скриптам — так вообще не понял, наоборот же, проще каждому скрипту рассказать в какой файл он должен писать, чем объяснить структуру единого файла.


                        Именно про это я и говорю. Писать легче в отдельные файлы. Поэтому большой конфиг превращается в файлы.

                        Но этот же конфиг мне нужен как один штука, потому что только один штука на входе понимает та система, которая его переваривает. Вот и все дела :-)
                        • 0
                          Ааа, просто есть ещё какая то система. Вот про это в шапке что-то не уловил и не понял в итоге зачем. Всё, тогда понятно, спасибо.
        • +1
          \<offtop\>
          У меня другая проблема – не могу придумать правильную структуру каталогов (классификацию файлов), т.е. есть много файлов, которые можно отнести сразу в несколько папок, городить ссылки из одной папки в другую как-то мягко говоря не правильно.
          Такое ощущение, что нужно писать базу данных с ссылками на локальные файлы и задавать им классификацию (категории, метки, темы и т.п.), далее к этому уже интерфейс какой-то… Каталогизатор какой-то получается.
          \</offtop\>
          • +2
            Вам тегировать файлы нужно. Посмотрите в сторону чего нить вроде https://www.tagsistant.net/
            • 0
              Есть-же такая штука — жёсткая ссылка.
              Вот только как реализовать.:( С питоном я никак от слова совсем…
            • +2
              Такая же проблема.
              Уже пару лет думаю над тем, чтобы сесть и попробовать написать свой файловый менеджер:
              — иерархические теги, возможность задать файлу неограниченное количество тегов
              — поиск по тегам с помощью диаграмм Венна (для наглядности)
              — отслеживанием изменений фс в заданных папках (добавление и удаление файлов, переименование старых, перемещение в отслеживаемые папки — папок с файлами)
              — всплывающее окно с предложением назначить теги новым файлам в отслеживаемых папках
              — отслеживание полных дубликатов
              — и, видимо, хранить всю эту инфу в бд какой-нибудь.

              Просто когда количество схем оригами начинает переваливать за десяток гигабайт начинаются проблемы с поиском какой-нибудь там схемы хамелеона Итагаки Ючи, которая была в каком-то из 150 номеров Tanteidan Magazine. Причем каждый сборник — это пдфка целая, в которой десяток разных схем, так что в имя нельзя все занести…
              • 0
                Вы не один, лично знаю ещё 3-ех человек которые задавались подобным вопросом. Предлагаемые варианты решения:
                1. жесткие ссылки (пробовал, имхо не очень удобно, запутывался в количестве ссылок на файл)
                2. дерево катологов (пробовал, нереально создать такое для всех своих файлов)
                3. файловый менеджер с БД с информацией о файлах (такой создает Konachan700, но только для картинок afaik)
                4. метаинформация
                — в файле по типу rarjpeg
                — в расширенных атрибутах
                — в специализированной фс

                Вообще было бы неплохо собраться в кучку и сочинить что-нибудь, цели очень похожие.
                cc: Am0ralist
                • 0
                  Немного перефразирую, хранить метаинформацию хочется в любом случае, можно делать это в
                  — файле
                  — пути (папки = теги + жесткие ссылки)
                  — БД файлового менеджера
                  — расширенных атрибутах фс
                  • 0
                    1) Если хранить в доп.файле для каждого из тегируемых файлов, то копировать надо все их, что проблематично, когда ты хочешь поделиться одним файлом из папки.
                    А для каждого поиска вначале найти все доп.файлы, а потом уже анализировать из содержимое.
                    Мне кажется, больно много накладных расходов, если только не кешировать это все еще куда и не копировать опять же спец.инструментом, а тогда опять же получается БД + файловый менеджер.

                    2) в пути и жестких ссылках хранить большое количество тегов (особенно иерархических, особенно, когда тег может быть включен в разные иерархические ветки) + легко это взять и скопировать друзьям?

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

                    4) Было бы идеально, но желательно тогда, чтоб все популярные ФС это поддерживали, для легкого копирования между компьютерами…

                    Возможно где-то ошибаюсь? Просто в свое время потратил много времени на анализ хотелок и возможностей разных программ, а в итоге найденная программа, в которой работала хотя бы часть желалок — работала только под win32 из-за того, что служба не была скомпилирована под x64, а кросплатформерных приложений вообще не нашел.
                    Особенно порадовала прога с а-ля облаком тегов, в котором облако не масштабировалось и не прокручивалось, в итоге после 30 тегов программу просто выкинул.
                    • 0
                      1) Не в доп. файле, а в том же самом.
                      4) Мне кажется это оно: https://en.wikipedia.org/wiki/Extended_file_attributes
                      • 0
                        1) Если в том же самом, то есть изменяем файл — то не все форматы поддерживают метаданные. Как поведут себя файлы произвольного формата — мне сложно сказать. Плюс проблема дубликатов возникает, когда у тебя хранится уже измененный файл, а ты добавляешь исходный с другим названием, например.

                        4) В windows для этого используют по сути альтернативные файловые потоки особо извращенным способом. Проблема в том, что эти расширенные атрибуты молча не копируются на тот же Fat32 (привет флешкам и SD-картам), а вот альтернативные потоки хотя бы ошибочку выдадут. Плюс доступ к ним через Жо… веселое API.
                        При этом сами по себе эти альтернативные потоки позволяют к 1 кб файлику прицепить хоть 100 гб, которые напрямую не увидишь — удобно, что бы прятать (порно) любой файлик от любопытных взглядов. Ну, то есть я знаю одну программу, которая точно позволяла легко смотреть файловые потоки, да.

                        С учетом, что я это планировал делать для удобного обмена файлами между людьми, которые слабо понимают в IT (самому перелопатить все да потом поддерживать — нереально)…
                        • 0
                          С учетом, что я это планировал делать для удобного обмена файлами между людьми, которые слабо понимают в IT (самому перелопатить все да потом поддерживать — нереально)…


                          Рискну «влезть» — из серии «хорошо забылое старое».

                          Были на заре такие файловые менеджеры — Volkov Commander и DOS Navigator. Кто у кого тогда «спёр» идею, сейчас уже не угадаешь, но умели они, как и FIDO-шный софт править файлы-компаньоны (как минимум — files.bbs и descript.ion) — в частности при копировании с места на место.

                          По сути — та же БД со спец. инструментом, но вполне доступным, встроенным в файловый менеджер (ну, да, какое-то расширение для GUI-евых «проводнико» напрашивается) и годным для людей, далеких от IT.

                          Некоторые мои знакомые архивариусы (из совсем не молодых ещё по тем временам) с огромным удовольствием пользовались такой возможностью и этого им вполне хватало (в купе с ручной правкой/написанием содержимого files.bbs — ну не было тогда готовых отдельных редакторов)
                          • 0
                            А почему «рискну»?
                            Тут любые варианты интересны в плане того, что даже посмотреть кто как делал.
                            Да и вдруг всплывет что-нибудь уже существующее а-ля tag2find, который под 64 не работает к сожалению, но хотя бы часть возможностей требуемых поддерживал.

                            Ибо пилить свой велосипед не сильно владея программированием и так весело, а если еще и изначально не верный вариант выбрать, то вообще обидно будет.
                            • 0
                              А почему «рискну»?
                              Тут любые варианты интересны

                              Это, скорее, хабра-призказка ;)
                              Для себя искал и порывался написать «тегировалку» фото коллекции — после ухода с винды, где этого добра валом, но как-то «отпустило» или стало лень

                              А на предмет вариантов — из той же серии: берём графические редакторы или даже более-менее старые фотоаппараты с поддержкой RAW — там к каждому файлу, который «неизменен», прикладывается мелкий (.thm, .xmp и т.п.) со всякими разностями — от режима «проявки» RAW, до истории изменений и тегов.

                              Из готового — тот же shotwell под линукс — умеет коллекции тегов с сортировкой/выборкой. По отдельной команде записывает теги в файлы, если поддержка, как у jpeg. Далее такой файл переносится на другой комп, а там теги вычитываются и кладутся в локальную базу. Вроде бы оно даже умело интегрироваться с «родными» проводниками для Gnome/KDE a'la дополнительные атрибуты файлов.
                • 0
                  есть много файлов, которые можно отнести сразу в несколько папок

                  Значит пора отказываться от концепции «папок» в пользу концепции «меток».


                  городить ссылки из одной папки в другую как-то мягко говоря не правильно

                  Правильно. Все каталоги у нас теперь не «папки», а «метки», и все ссылки на один и тот же документ должны быть равноправны, то есть есть это должны быть жесткие ссылки, а не символьные.


                  Единственная проблема — ext[234], в отличие от, например, NTFS, не хранит обратных ссылок с инодов на файлы, а поэтому, если стороннего индекса нет, то для удаления документа, если такое вдруг понадобится, придется делать полный перебор:


                  $ find ~/ -xdev -samefile useless-file.org -delete

                  Для резервирования такой ФС, rsync(1)’у надо будет додать ключ -H.


                  В остальном — никаких особенностей.

                  • 0
                    а копировать такое друзьям как?
                    Ну вот хочу часть своей базы схем поделиться с кем-то, вместе со всеми «тегами» конкретных файлов, кроме личных оценок (оценок сложности схемы и того, нравится ли мне модель, например).
                    Как это будет происходить в таком случае?
                    • 0
                      хочу часть[ю] своей базы схем поделиться с кем-то

                      Если часть — это такие-то метки со всем, что под ними лежит, то просто — тем же rsync -aH.


                      вместе со всеми «тегами» конкретных файлов

                      А вот есть так, то есть если часть — это отдельные документы, размазанные по всему дереву меток, то опять же — только полным перебором. Увы, не хранит ext обратные ссылки.


                      Мне это ни разу не было не нужно, так что костыля я не написал, но понятно, что он элементарный.

                      • +1
                        Мне это ни разу не было не нужно, так что костыля я не написал, но понятно, что он элементарный.

                        А вообще, что уж там, давайте напишем:


                        #!/bin/bash
                        
                        # config
                        TREE_ROOT="$HOME/origami"
                        
                        SCRIPTNAME='amoralist-cp'
                        USAGE=$"Usage: $SCRIPTNAME <source>... <dest>"
                        
                        (($# >= 2)) || { printf >&2 '%s\n' "$USAGE"; exit 0; }
                        
                        dest="${!#}"
                        
                        for ((i = 1; i <= $# - 1; i++)); do
                            if [[ -d ${!i} ]]; then
                                printf >&2 '%s\n' $"${!i} is directory; ignored"
                            else
                                find_argv+=('-samefile' "${!i}" '-or')
                            fi
                        done
                        unset find_argv[-1]
                        
                        find "$TREE_ROOT" -xdev "${find_argv[@]}" -printf '%P\n' \
                            | rsync --archive --hard-links --files-from - "$TREE_ROOT" "$dest"
                    • 0
                      А есть файловые системы которые хранят обрытные ссылки?
                      • 0
                        Ну, одну я уже упомянул — NTFS. :-)

                        Есть ли *стандартные* файловые системы, что хранят обратные ссылки с инодов на файлы, вы хотите спросить? А вот не знаю — интересовался этим весьма поверхностно.
                        • 0
                          меняем *стандартные* на *можно адекватно использовать с Linux или FreeBSD* и получится то что я хотел спросить :^)
                  • 0
                    отличное решение!
                    до этого я только предполагал такую реализацию через плагин VFS для Midnight Commander. Там мне показалось не так удобно как с fusepy))
                    • +1
                      Парсинг все равно происходит и все запросы парсера файловой системы пройдут через ядро. Гораздо быстрее работать с единым файлом напрямую. Но как задача для знакомства с fusepy — годится.
                      • 0
                        Я вроде не написал, что данное решение мне срочно надо использовать для обработки миллионов запросов в секунду…

                        Для скорости я бы писал не на Питоне, и обрабатывал системные вызовы бы не в один поток, и т.д. и т.п. В конце концов можно написать модуль файловой системы для ядра.

                        И если вам действительно надо условный миллион раз за условную же наносекунду читать конфиг, то, быть может, вы что-то делалете не то..?
                        • –2
                          Конечно, я часто делаю что-то не так. А кто не ошибается? Но работа приучила меня проектировать так, чтобы задача решалась с наименьшим выделением тепла. Это нравится экологам и в итоге не приносит неприятных сюрпризов с ростом обрабатываемой информации.
                          Вы же понимаете, что на один шаг парсера файловой системы будет 4(!) переключения из юзерспейса в ядро и обратно?
                          Здесь гораздо интереснее будет инвертировать задачу: раскидать много малых файлов по реальной файловой системе и предоставить один виртуальный для КЭШированного доступа.
                          В итоге и применять можно оба подхода и системные вызовы экономятся.
                          • +1
                            Ну да, отчасти вы, конечно, правы. С другой стороны, у меня было несколько часов на все про все, и тут уж не до спасения планеты. :-)

                            Ваша идея тоже звучит интересно, признаться, но это ваша идея и ваш интерес, не могу ж я их взять и украсть :-)
                      • 0
                        А что за ад из переносов строки в тексте статьи?
                        • 0
                          Хабр не вполне корректно обрабатывает переносы. Спасибо, исправил.
                        • 0
                          Редактирование определенно необходимо. Я бы даже очень был рад полному интерфейсу
                          • +1
                            Похожую тему рассказывали про ОС Plan9. Её постоянным пользователем является (как оказалось) некий ВУЗ Барселоны, занимающийся переводами текстов. Им очень нравится, что в основе ОС многоязыковость и при этом очень легко создавать свои специализированные файловые системы. Вот они дожились до того, чтобы переводить тексты при помощи таких файловых систем — документ делится на разделы, разделы переводят разные люди — для ускорения. И вот здесь фишки Plan9 им оказываются очень впору. Если интересно, можно раскопать, как у них это происходило — у автора статьи очень похожий — и правильный! — взгляд на схожую проблему. Круто решать такие задачи :-)
                            • 0
                              Да, мне очень нравится этот подход, когда все — файлы, и поэтому все инструменты являются универсальными.

                              собственно говоря, FUSE, Sysfs и procfs в Линуксах появились по мотивам разработок Plan9
                            • 0
                              А чем ZIM не подошел(не имею ввиду портирования файлов в него)?
                              Глянув на его код можно было и немного переиначить, а потом «натравить» на свои.
                              • 0
                                ZIM, который формат файла? А при чем здесь вообще конкретный формат файла?

                                Статья ведь про то, что файлы с иерархией можно легко монтировать как файловую систему, и тогда для работы с содержимым можно пользоваться любыми привычными инструментами.
                                • 0
                                  ZIM — программа, домашнее wiki. На питоне.
                                  файлы с иерархией можно легко монтировать как файловую систему

                                  Я про это и сказал, что вместо того, чтобы разрабатывать с нуля, можно было посмотреть, как программа делает из одного файла древовидную структуру. А потом, на том же питоне(zim на питоне), написать код под персональные нужды.
                                  Ссылка: https://ru.wikipedia.org/wiki/Zim
                                  • 0
                                    У меня нет проблемы сделать из файла древовидную структуру :-) Такие вещи называются парсерами, и с этим уже давно ни у кого нет проблем.

                                    У меня проблема — представление одной древовидной структуры в виде другой, файловой.
                                    • 0
                                      Эммм… А я разве о другом? Там, в проге есть разделение(типа офисовского «совместного использования файла»(зависит от версии)). Как прога предоставляет ее по сети? Разве не файловой системой?
                                      Тут, мне кажется, спор излишен, ибо я говорил о «посмотреть код программы», а вы так и становились на файловой системе.
                                      • 0
                                        Т.е. вы предлагаете адаптировать программу, визуально отображающую файлы с вики-подобной разметкой в виде дерева..?

                                        Я, вероятно, не совсем вас сразу понял.
                                        • 0
                                          Не совсем. Просто посмотреть код и взять нужное для себя.
                                          • 0
                                            А что вообще мне там может быть нужно, в этом коде? Т.е., зачем мне разбираться в софтине на тысячи строк кода, когда все решение занимает меньше двух сотен строк?

                                            Опять же, какое отношение софт для просмотра файлов с вики-подобным синтаксисом имеет к скриптам, работающим с файловой системой?

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.