0intro — Введение в файловый протокол 9P в Inferno OS

Данной статьей мы начинаем серию переводов секции 5 страниц man из Inferno OS. Эта секция посвящена целиком описанию протокола 9P, который в Inferno ранее назывался Styx, и содержит описание разных типов сообщений протокола. К D серия переводов почти не имеет отношения, но мы решили опубликовать этот материал, так как он полезен для работы с нашей библиотекой styx2000. Также, как нам кажется, описания из man’ов Inferno поможет взглянуть иными глазами на описанное ранее в статьях по Styx.

Публикуем перевод с нашими незначительными улучшениями, которые не повлияют на качество материала. Саму подачу мы решили оставить такой же, как и в страницах man.

НАЗВАНИЕ

intro — введение в файловый протокол Plan 9 в Inferno OS.

ЗАГОЛОВОЧНЫЕ ФАЙЛЫ

#include <lib9.h>
#include <styx.h>

ОПИСАНИЕ

Inferno использует протокол 9Pиз Plan 9 для отображения ресурсов как иерархий файлов. Существовали две версии 9P, но самая последняя из них — 9P2000. Изначально Inferno использовала свою версию протокола, которую назвали Styxи которая являлась упрощенной версией оригинального протокола 9P. Затем, когда 9P пересмотрели в пользу 9P2000, для протокола по-прежнему использовалось название Styx. Чтобы исключить путаницу и подчеркнуть, что Plan 9 и Inferno используют тот же самый протокол, наименование протокола «Styx» заменяется на «9P». Отсылки к Styx остаются в документации и программах, но в конечном итоге, будут исключены.

Сервер Inferno — это агент, который предоставляет одну или несколько иерархических файловых систем — файловых деревьев — которые могут быть доступны для процессов в Inferno. Сервер отвечает на запросы клиентов: на проход по иерархии, на создание, удаление, чтение и запись файлов. Прототипный сервер — это отдельная машина, хранящая большое количество пользовательских файлов на постоянном носителе. Другим вариантом сервера может быть создание файлов по требованию, возможно, на базе структур внутри ядра, к примеру, так делает устройство ядра prog(3), которое является частью ядра Inferno. Пользовательские программы также могут работать как серверы. Одним из простых способов создания сервера для набора файлов является интерфейс sys-file2chan(2). Более сложные файловые сервисы (приложения, написанные на Limbo) могут использовать styx(2) для непосредственной обработки сообщений протокола или использовать styxservers(2), предоставляющий набор структур более высокого уровня для обслуживания файлов.

Соединение с сервером — это двунаправленный путь связи от клиента к серверу. Здесь может быть один клиент или несколько клиентов, которые разделяют между собой одно и то же соединение. Дерево файлов сервера прикреплено к пространству имен группы процессов путем использования вызовов bind и mount; cм. intro(2) и sys-bind(2). Процессы в группе затем становятся клиентами сервера: системные вызовы, обращающиеся к файлам, переводятся в запросы и ответы, передаваемые при подключении к соответствующей службе.

Файловый протокол файла Plan 9 (9P) используется для передачи сообщений между клиентами и серверами. Клиент передает запросы (T-messages, T-сообщения) на сервер, который впоследствии возвращает ответы (R-Messages, R-сообщения) клиенту. Комбинированные акты передачи (получения) запроса конкретного типа и получения (передачи) ответа на него называют транзакцией такого типа.

Каждое сообщение состоит из последовательности байтов. Двух-, четырех- и восьми байтовые поля содержат беззнаковые целые числа, представленные в порядке little-endian (т.е сначала идет наименьший значимый байт). Единицы данных крупнее или же с переменной длиной представлены двухбайтным полем, указывающим количество байтовв единице данных (n), за которым следует n байтов данных. Строки текста представлены точно также, но сам по себе текст хранится как последовательность Unicode-символов, закодированных в UTF-8 (см. utf(6)). Строки текста в сообщениях 9P не могут заканчивается NUL: при подсчете числа n включаются все байты данных UTF-8, которые не содержат концевого нулевого байта. Также символ NUL является недопустимым во всех строках текста в 9P и исключается из имен файлов, пользователей и т.д.

Каждое 9P сообщение начинается с четырехбайтового поля, указывающего длину в байтах всего сообщения, включая четыре байта и самого поля. Следующий байт — это тип сообщения, константа из модуля styx(2) или из include файла для C <styx.h>(см. styx(10.2)). Следующие два байта представляют собой идентификационный тег, как описано ниже. Оставшиеся параметры имеют разные размеры в байтах. В описаниях сообщений, количество байтовв поле указано в квадратных скобкахпосле имени поля. Параметр нотации [n], где n не является постоянным, представляет собой описание параметра переменной длины: сначала идет поле с описанием размера n[2], за которым следует n байтов данных, образующих параметр. Строка нотации [s](с использованием литерального символа s) является сокращением для s[2], за которым следует s байтов текста UTF-8. (Системы могут ограничивать набор корректных символов для уменьшения количества синтаксических проблем, к примеру, могут быть удалены слэши из компонентов имен, однако, в протоколе нет таких ограничений. Имена в Inferno могут содержать любые символы (это так, любые символы вне шестнадцатеричного диапазона 00-1F и 80-9F), кроме слэша.) Сообщения передаются в байтовой форме для достижения независимости от архитектуры; styx(10.2) описывает процедуры, которые преобразовывают данную форму (а также из нее) в машинозависмые С-структуры.

СООБЩЕНИЯ

size[4] Tversion tag[2] msize[4] version[s]
size[4] Rversion tag[2] msize[4] version[s]
  
size[4] Tauth tag[2] afid[4] uname[s] aname[s]
size[4] Rauth tag[2] aqid[13]
  
size[4] Rerror tag[2] ename[s]
  
size[4] Tflush tag[2] oldtag[2]
size[4] Rflush tag[2]
  
size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
size[4] Rattach tag[2] qid[13]
  
size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
  
size[4] Topen tag[2] fid[4] mode[1]
size[4] Ropen tag[2] qid[13] iounit[4]
  
size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]
size[4] Rcreate tag[2] qid[13] iounit[4]
  
size[4] Tread tag[2] fid[4] offset[8] count[4]
size[4] Rread tag[2] count[4] data[count]
  
size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
size[4] Rwrite tag[2] count[4]
  
size[4] Tclunk tag[2] fid[4]
size[4] Rclunk tag[2]
  
size[4] Tremove tag[2] fid[4]
size[4] Rremove tag[2]
  
size[4] Tstat tag[2] fid[4]
size[4] Rstat tag[2] stat[n]

size[4] Twstat tag[2] fid[4] stat[n]
size[4] Rwstat tag[2]

Каждое T-сообщение имеет поле тега, выбранное и используемое клиентом для идентификации сообщения. Ответ на сообщение будет иметь тот же тег. Клиенты должны договориться о том, что в одном и том же соединении нет двух исходящих сообщений с тем же тегом. Исключением является тег NOTAG, определяемый как 16rFFFF в styx(2) и styx(10.2): клиент может использовать его при установлении соединения, чтобы переопределить соответствие тегов в сообщениях version.

Тип R-сообщениябудет или на единицу больше, чем тип соответствующего T-сообщения или Rerror, что указывает на то, что запрос не удался. В последнем случае, поле ename содержит строку, описывающую причину сбоя.

Сообщение versionидентифицирует версию протокола и указывает максимальный размер сообщения, к которому подготовлена система. Он также инициализирует соединение и прерывает все выполняющиеся операции ввода/вывода на соединении. Набор сообщений между запросами version называется сеансом.

Большинство T-сообщений содержат fid, 32-битное беззнаковое целое число, которое клиент использует для идентификации «текущего файла» на сервере. В некоторой степени fid похожи на дескрипторы файлов в пользовательском процессе, но они не ограничены файлами, открытыми для ввода/вывода: просматриваемые каталоги, файлы, доступ к вызовам sys-stat(2) и т.д. — все это файлы, манипулирующие операционной системой — все это может быть идентифицировано через fid. Параметр fid выбирается клиентом. Все запросы на подключении имеют одно и то же пространство fid; когда несколько клиентов разделяют подключение, агент, управляющий обменом, должен договориться с клиентами о том, что нет двух клиентов выбирающих один и тот же fid.

Идентификатор fid, поставляемый в сообщении attach, будет взят сервером для обозначения корня обслуживанного дерева файлов. Сообщение attachидентифицирует пользователя на сервере и может указать конкретное дерево файлов, обслуживаемое сервером (для тех серверов, кто поставляет более одного дерева).

Полномочия на attach к предоставляемому сервису доказывается путем предоставления специального fid, называемого afid, в сообщении attach. Данный afidустанавливается путем обмена сообщениями auth и впоследствии управляемый с помощью сообщений read и write в ходе обмена информацией о аутентификации, не определенной явно в 9P. После завершения аутентификации, afid представленный в attachпозволяет получить доступ к сервису.

Сообщение walk заставляет сервер изменить текущий файл, связанный с fid, на файл в каталоге, который является старым текущим файлом, или одним из его подкаталогов. Сообщение walk возвращает новый fid, который относится к полученному файлу. Обычно, клиент удерживает fidкорня и осуществляет продвижение с помощью walk, отталкиваясь от корневого fid.

Клиент может отправлять несколько T-сообщений, не ожидая соответствующих R-сообщений, но все выполняющиеся T-сообщения должны иметь различные теги. Сервер может задержать ответ на запросы и ответить на них позднее; иногда это необходимо, например, когда клиент читает из файла, который сервер создает в зависимости от внешних событий, таких как ввод с клавиатуры.

Ответы (R-сообщения) на auth, walk, openи createпередают поле qid обратно клиенту. Поле qid представляет уникальный идентификатор на сервередля доступа к файлу: два файла на той же иерархии сервера одинаковы, тогда и только тогда, когда их qidодинаковы. (У клиента может быть несколько fid, указывающих на один и тот же файл на сервере, но имеющим только один qid.) Тринадцатибайтовое поле qid содержит однобайтовый тип, указывающий на то, является ли файл каталогом, файлом только для дозаписи и т.д., и два беззнаковых целых числа: сначала четырехбайтная версия, затем — восьмибайтный путь. Путь является целым уникальным среди всех файлов в иерархии. Если файл удален и воссоздан с тем же именем в одном и том же каталоге, старая и новая компонента пути qid должны быть разными. Параметр версия является номером версии для файла; как правило, она увеличивается каждый раз, когда файл изменяется.

Существующий файл может быть открыт (open), или новый файл может быть создан (create) в текущем (каталоге) файле. Ввод/вывод заданного количества байтов по заданному смещению (offset) на открытом файле выполняется с помощью readи write.

Клиент должен выполнить clunk для любого более неиспользуцемого fid. Сообщение remove удаляет файлы.

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

Запрос может быть прерван с помощью сообщения flush. Когда сервер получает Tflush, он не должен отвечать на сообщение с помощью тега oldtag (если только он уже так не ответил) и должен немедленно отправить Rflush. Клиент должен подождать, пока он не получит Rflush (даже если ответ на исходное сообщение придеь через длительный промежуток времени), только после этого oldtag может быть использован повторно.

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

Большинство программ не работают с протоколом напрямую; вместо этого вызовы в процедур библиотеки, которая обращается к файлам, переводятся драйвером mountmnt(3) в сообщения 9P.

ПАПКИ

Папки создаются с помощью сообщения create и установкой DMDIR в аргументе прав доступа (см. stat(5)). Содержимое папки может быть считано посредством read(5). Все папки должны поддерживать переход к папке ..(точка-точка) обозначающей родительскую папку, однако, по соглашению папки могут не содержать явных записей ..или .(точка). Родительской папкой для корневой папки является файловое дерево самого сервера.

ПРАВА ДОСТУПА

Каждый файловый сервер поддерживает набор имен пользователей и групп. Каждый пользователь может быть членом любого количества групп. У каждой группы есть руководитель группы, у которого есть особые привилегии (см. stat(5)). Каждый запрос файла имеет неявный идентификатор пользователя (скопированный из исходного сообщения attach) и неявный набор групп (каждая группа, участником которой является пользователь).

Каждый файл ассоциирован с идентификатором пользователя, группы, а также тремя наборами прав: для владельца файла, для группы владельца и для остальных пользователей. Когда владелец пытается сделать что-либо с файлом, то учитывается набор разрешений владельца, его группы и группы остальных пользователей, и если кто-либо из этих групп предоставил права, то операция разрешена. Для любых других пользователей, которые не являются владельцами, но входят в группу, указанную для файла, принимаются во внимание набор разрешений группы и остальных пользователей. Для остальных применяются разрешения прочих пользователей. Каждый набор разрешений говорит о том, разрешено ли чтение, разрешена ли запись и разрешено ли выполнение. Вхождение в каталог рассматривается как выполнение каталога, а не как его чтение. Разрешения храняться как биты низкого порядка в режиме работы с файлом: чтение/запись/выполнение для владельца представлено как 1в битах 8, 7, и 6 соответственно (0 указывается для отзыва соответствующих разрешений). Разрешения группы находятся в битах 5, 4 и 3, а полномочия прочих определяются в битах 2, 1и 0.

Режим файла содержит некоторые дополнительные аттрибуты, помимо разрешений. Если установлен бит 31 (DMDIR), то файл является каталогом; если устанавливается бит 30 (DMAPPEND) — файл только для дозаписи (указание значения offsetигнорируется в сообщениях write); если установлен бит 29 (DMEXCL) — файл только с эксклюзивным использованием (только один клиент может открыть его за раз); если установлен бит 27 (DMAUTH) — файл является файлом аутентификации, подготовленным сообщениями auth; если установлен бит 26 (DMTMP), то содержимое файла (или каталога) не включено в ночные архивы. (Бит 28 пропущен по историческим причинам.) Эти биты воспроизводятся, из верхнего бита, в байте для типа qid: QTDIR, QAPPEND, QTEXCL, (пропуская один бит) QTAUTH и QTTMP. Аттрибут QTFILE, выставленный в ноль, передает значение типа для простого файла.

СМОТРИТЕ ТАКЖЕ

intro(2), styx(2), styxservers(2), sys-bind(2), sys-stat(2), mnt(3), prog(3), read(5), stat(5), styx(10.2)

Добавить комментарий