Введение: как работает NFS
Обзор NeFS
Слабости NFS и NEFS
Управление потоком данных на транспортном уровне
Управление потоком запросов и ответов
Настройка клиента и сервера NFS
NFS-монтирование
Перевыполнение NFS-обменов
Самозванные клиенты и серверы
Защита в NEFS
Проблемы протокола
Потенциальные проблемы NEFS
Заключение

Сетеая файловая система NFS (Network File System - NFS), разработанная Sun Microsystems, заняла место стандарта de facto для распределенных файловых систем в большинстве вариантов ОС Unix. Кроме того, она стала популярной и в других операционных системах, таких как DOS и VMS. Несмотря на очевидные достоинства, в реализации NFS есть ряд проблем. Sun анонсировала новый протокол NeFS, пытаясь решить некоторые из них.

Введение: как работает NFS

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

NFS-процедуры реализуют NFS-протокол, состоящий из набора файловых операций - открыть, закрыть, прочитать, записать и т.д. - практически однозначно соответствующих набору вызовов базового ввода/вывода ОС Unix. Рассмотрим подробнее попытку записи в удаленный файл. Ядро ОС Unix определяет, что файл является удаленным, и вызывает NFS-процедуру записи для передачи данных серверу.

NFS-процедура записи делает вызов удаленной процедуры, посылая серверу пакет с NFS-запросом. Запрос содержит всю информацию, необходимую серверу для обработки запроса - идентификаторы делающего запрос пользователя и группы; описатель файла, в который нужно писать; объем записываемых данных; смещение в файле и, конечно, сами данные.

При приеме NFS-запроса сервер проверяет переданные аргументы. Если они приемлемы, операция выполняется, и клиенту сообщается об успешном завершении или неудаче записи.

Краеугольный камень NFS-протокола - сервер, не сохраняющий состояния. Файловые серверы попросту обрабатывают каждый получаемый ими запрос, изолированно от предыдущих запросов клиентов. Несохранение состояния значительно упрощает операции сервера, делая ненужным сохранение следа деятельности отдельных клиентов. Серверы, не сохраняющие состояние, не нуждаются и в специальных методах восстановления при аварии.

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

Обзор NeFS

Слабые места существующего NFS-протокола известны. Некоторые из них представляют собой частные проблемы, например, ограничения в NFS-структурах данных, влияющие на максимальный размер доступных файлов и т.п. Серьезный недостаток: NFS не поддерживает блокировок файлов; более того, NFS-протокол слишком сильно ориентирован на Unix-стиль доступа к файлам и плохо подходит на роль обобщенного протокола распределенной файловой системы. Это усложняет адаптацию NFS на другие ОС. Новый протокол, называемый NeFS - сетевая расширяемая файловая система (Network Extensible File System) - разработан, чтобы решить перечисленные проблемы.

NeFS обеспечивает общую базу для поддержки различных типов файловых систем во многом подобно тому, как система NeWS обеспечивает в равной степени мощную и гибкую среду для поддержки различных стилей пользовательских интерфейсов и оконных систем. NeFS поддерживает как RFS,так и NFS для Unix-систем, а также другие типы сетевых файловых систем для различных ОС.

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

Подобно тому, как NeWS использует язык PostScript для описания изображаемой модели, NEFS использует его вариант для определения модели операций файловой системы; на языке PostScript формулируются запросы клиента и ответы сервера. Использование языка означает, что NEFS не ограничена строго определенным множеством операций файловой системы. Сервер разделяется на два компонента: сетевой интерфейс и интерпретатор. По существу, сетевой интерфейс обеспечивает транспортный сервис, передавая запросы клиента интерпретатору и возвращая клиенту ответы от сервера. Сетевой интерфейс включает драйвер устройства, поддержку транспортного протокола и, предположительно, процедуры для обработки представления данных и аутентификации. Задачи сетевого интерфейса: передача данных в сеть/из сети и подготовка корректной Среды выполнения для интерпретатора.

NEFS-интерпретатору передается в качестве запроса клиента небольшая программа на языке PostScript. Интерпретатор ее выполняет, обращаясь к сетевому интерфейсу за дополнительными данными от клиента, чтобы полностью обработать запросы. Ответы возвращаются сетевому интерфейсу и позже большим массивом направляются клиенту. Возможность пакетирования NEFS-диалога позволяет повысить его эффективность.

Клиенты могут формировать абстрактные запросы. Например, в предварительной спецификации NEFS даны такие примеры запросов: скопировать файл; распечатать каталог; рекурсивно обойти файловую систему. В NEFS эти действия вызвали бы огромный сетевой трафик (в частности, NFS-операции чтения и записи для каждого копируемого блока). NEFS-клиент может послать серверу небольшую программу локального копирования. Сервер выполнит программу, не увеличивая сетевого трафика, и просто возвратит результат, когда копирование завершится.

Столь высокий уровень абстракции вызывает интересный вопрос: "Где следует расположить интерпретатор?". Он может быть слишком большим, чтоб располагаться в ядре. Если же его сделать пользовательским процессом, от чрезмерных переключений контекста может пострадать эффективность; кроме того, большое число пользовательских процессов, обслуживающих одновременные NFS-запросы, может истощить память.

Еще один интересный вопрос: "Насколько пользовательскому процессу разрешено контролировать NEFS?". Если копирующий процесс знает, что он действует над файлами, находящимися на одном сервере, может ли он послать PostScript-код для выполнения операции непосредственно на этом сервере? Это не очевидно. Естественнее это сделать для более низкого уровня файловых операций - например, чтения и записи блоков.

Слабости NFS и NEFS

Проблемы NFS возникают как следствие слабых мест или компромиссов существующего NFS-протокола. Пока имеется только предварительная спецификация NEFS, поэтому ее недостатки можно ликвидировать до того, как реализован протокол.

Прозрачность

Идеал любой распределенной файловой системы - прозрачность. Не должно быть никаких различий в доступе к локальным удаленным файлам, за исключением, быть может, времени доступа. В целом NFS достигает прозрачности, хотя есть и заметные исключения - файловые операции, подразумевающие наличие в сервере специфичной для клиента информации о состоянии. Наиболее яркий пример - блокировка файла. Если клиент заблокировал удаленный файл, сервер должен знать, что файл заблокирован (и кем), чтобы предотвратить доступ других процессов к нему.

Менее очевидный пример - доступ к удаленным устройствам. Часть из них по своей сути рассчитаны на монопольное использование (например, накопители на магнитной ленте), Другим необходима хранимая информация о состоянии (например, аппаратные установки терминальной линии). Кроме сервера, эту информацию поддерживать некому. В то же время и сама информация, и методы доступа в значительной мере специфичны для конкретной ОС; трудно представить себе независимый от ОС протокол, способный учесть особенности доступа к устройствам в таких разных системах, как VMS и Unix. Действительно, банку с червяками лучше не открывать. Умышленно избегая этих проблем, можно значительно упростить действия сервера и уменьшить последствия аварий.

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

Есть и другие ситуации, ставящие под сомнение прозрачность NFS. В Unix-системах NFS как правило устанавливаются в защищенном режиме, запрещающем привилегированный доступ к удаленным файлам и каталогам. NFS-клиент также можно сконфигурировать так, чтобы он не учитывал биты переустановки удаленных выполнимых файлов. NFS, кроме того, крайне бесцеремонно обходятся с режимами доступа удаленных файлов, допуская удаленный доступ на чтение к файлам, имеющие разрешение только на выполнение. Требование подкачки удаленного выполнимого файла вызывает NFS-запросы чтения, удовлетворяемые, только если сервер считает, что клиент имеет право на чтение этого файла. Таким образом, в NFS право только на выполнение иногда подразумевает право еще и на чтение. Хуже того, пользователю иногда разрешаются делать все что угодно с файлами, владельцем которых он является, безотносительно к правам доступа. Можно использовать NFS для записи через сеть в свой собственный файл, доступный только на чтение!

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

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

Проблемы функционирования

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

Управление потоком данных на транспортном уровне

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

NFS решает это проблемы очень просто: клиенты повторяют передачу запросов, пока не получат ответ от сервера. Если датаграммы действительно теряются, это приводит к тупику.

Ситуацию могло бы исправить использование надежного транспортного протокола (такого как TCP), включающего управление потоком данных. Это разрушило бы концепцию несохранения состояния, так как сервер хранил бы данные о каждом соединении с клиентом; кроме того, взаимодействие стало бы двухточечным. Другой путь: выбор клиентом и сервером максимальной пропускной способности их сетевых интерфейсов и соглашение не пытаться передавать данные с большими скоростями.

NEFS вообще не упоминает об этой проблеме: предполагается, что сложности надежной передачи данных отработаны нижним уровнем сетевого интерфейса. Если же новый протокол будет выполняться над UDP, проблема останется.

Управление потоком запросов и ответов

Существует также проблема управления потоком данных более высокого уровня, а именно скорость, с которой посылаются и обрабатываются NFS-запросы (и, возможно, NEFS-запросы). Лучшая иллюстрация этого - рекурсивный обход удаленной файловой системы командами do или find. Обходы порождают лавину NFS-запросов, способную затопить даже самый быстрый сервер. В проекте версии 3 NFS-протокола серверы могут возвращать код ошибки, когда они обнаруживают повторную передачу уже выполняющегося запроса. Однако протокол не определяет, что должны в этом случае делать клиенты (если они вообще должны что-либо делать). Заметим, что обеспечивается только механизм замедления клиентов для серверов; эквивалентного средства для клиента пока нет. По общему признанию, перегруженность клиента - менее серьезная проблема, чем перегруженность сервера, но и ее не следует игнорировать.

Одно из возможных решений - оценка клиентами и серверами NFS своих скоростей обработки NFS-транзакций и обмен этими данными. Затем клиенты и серверы могут скорректировать скорость NFS-диалога.

NEFS-протокол специфицирует для NFS примитивы, позволяющие определить, превысили ли запросы лимиты выделенных сервером ресурсов - процессорное время и память. Однако не ясно, как клиенту реагировать на возвращенные значения ошибки. Должен он возвращать ошибку процессу клиента или пытаться повторять запрос в надежде, что сможет в конце концов достичь цели? Как серверу защититься от переполнения дублированными запросами от чрезмерно активных клиентов?

Настройка клиента и сервера NFS

Наконец, существует черная магия настройки NFS. Толковому администратору NFS следует поэкспериментировать со всеми видами параметров, чтобы добиться максимальной производительности. В простейшем случае - это варьирование числом процессоров biod и nfsd. Более предприимчивые администраторы будут исследовать эффекты изменения размеров блоков чтения/записи, значений повторных передач и таймаутов, других, еще более причудливых опций NFS-монтирования. Большей частью, эти свойства должным образом не документированы; мастерство же приходит в процессе проб и ошибок.

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

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

Вопросы защиты

Возможно, защита - та область, на которой сосредотачивается большинство критиков NFS. Здесь действительно есть много причин для подлинного беспокойства. К сожалению, в NEFS немного дополнительных средств защиты; вся ответственность переложена на сетевой интерфейс и NEFS-интерпретатор.

NFS-монтирование

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

Перевыполнение NFS-обменов

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

Эта проблема решена в защищенном варианте NFS, поддерживаемом Sun ОС Version 4. NFS-данные шифруются; к ним добавлены временные метрики для защиты диалога от дешифровки и последующего повторного выполнения. При шифровании используется опознавание клиентов и серверов по открытому ключу и DES-аппаратура. Однако эта схема по-прежнему ненадежна - фактические данные, которые читаются и пишутся, не зашифровываются. Более того, опознавательные ключи передаются при помощи службы Yellow Pages, известной своей уязвимостью.

Самозванные клиенты и серверы

По умолчанию NFS использует для NFS-диалога непривилегированный номер UDP-порта. Так как любой пользователь может связаться с этим портом, то можно и направить посторонние сообщения в NFS-трафик. Воспроизвести NFS-протокол не просто, но и не слишком сложно. Использование привилегированных портов - это выход из положения, но только для тех Unix-систем, где привилегию суперпользователя получить непросто. При этом невелика защита от атак со стороны компьютера, подобного персональному, в котором нет понятия суперпользователя, не говоря уже о номере привилегированного UDP-порта.

Защита в NEFS

К сожалению, новый протокол не предлагает механизмов защиты от подобных атак. По-видимому, считается, что сетевой интерфейс произведет опознание до передачи NEFS-запросов интерпретатору. Без сомнения, проверка будет основываться на чем-нибудь подобном Kerberos или защищенному RPC-механизму фирмы Sun.

Одно полезное средство NEFS все же обеспечивает: клиент посредством acces может спросить у сервера, разрешено ли ему выполнять конкретную названную операцию. Когда клиент узнает, как сервер будет реагировать на попытку выполнить названную файловую операцию, он предпримет те или иные действия. Это средство может использоваться клиентами, чтобы узнать, какие механизмы файлового доступа поддерживаются сервером.

Проблемы протокола

Надежность, и, следовательно, целостность файлов, доступных с помощью NEFS, зависит от надежности используемого транспортного протокола. Являясь датаграммным продуктом, UDP ненадежен по своей сути. NFS, следовательно, поставлена перед фактом ненадежности UDP, поскольку большинство реализаций основано именно на нем.

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

Рассмотрим следующую ситуацию. Клиент посылает запрос "сделать ссылку на файл". Сервер делает ссылку и сообщает об успехе; ответ теряется. Время запроса клиента истекает, запрос повторяется. Теперь сервер возвратит ошибку, так как запрос уже существует. Код ошибки возвращается процессу клиента, "указывая", что первоначальный запрос потерпел неудачу, в то время как на сомом деле он быд успешно выполнен! Если бы сервер знал, что второй запрос - повторение уже выполненного запроса, он смог бы просто сообщить об успехе, не пытаясь пересоздать ссылку.

Серьезные осложнения возможны, когда между клиентами и серверами более одного маршрута. (две различные сети необязательны. Достаточно, чтобы данные долго задержались в пути, например, на мосту или IP-маршрутизаторе). Предположим, клиент хочет укоротить файл, а затем дописать в него. Первый запрос посылается, и его время истекает - допустим, он идет по очень медленному маршруту или задерживается где-нибудь по дороге. Клиент повторяет запрос, повторная попытка успешно завершается, успешно выполняется и запись. Тем временем первый запрос по-прежнему бродит по сети и в конце концов достигает сервера. Его выполнение может разрушить файл. Такой сценарий гипотетичен, но он может произойти.

Использование надежного транспортного протокола разом решило бы проблему: сообщения просто не терялись бы. Другое возможное решение - это расширение NFS-протокола включением в сообщения последовательных номеров. Номера увеличивались бы при каждом отправлении нового сообщения. Это позволило бы клиентам и серверам определять номер очередного сообщения. Запросы с номерами, меньшими следующего ожидаемого, могут идентифицироваться сервером как дубликаты и игнорироваться. С другой стороны, сервер может повторно передавать соответствующий ответ, не обрабатывая вновь такой запрос.

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

Потенциальные проблемы NEFS

Наиболее очевидная проблема NEFS - отказоустойчивость интерпретатора. Клиенты могут сформировать запросы, которые введут интерпретатор в бесконечный цикл, исчерпают доступную память или совершат еще какие-нибудь антиобщественные поступки.

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

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

Примитивы для проверки ограничений на ресурсы полезны. Однако, если, как и предполагалось, NEFS используется в неоднородной среде, ограничения надо определенным способом нормализовать. Простой байтовый счетчик свободной памяти или число миллисекунд процессорного времени на запрос не подойдут: слишком велика разница между тем, какой ресурс считают большим или малым маленькая рабочая станция и большая ЭВМ. Можно объявить ограничения в терминах допустимого числа NEFS-операций, предполагая, что сервер знает, сколько памяти и процессорного времени необходимо для среднего запроса.

Заключение

NEFS - средство для разделения ресурсов. Когда система будет реализована, она сделает взаимодействие неоднородных систем более открытым и естесственным, свободным от большинства ограничений NFS. NEFS предлагает гибкую и легко расширяемую среду, способную приспособиться к большинству типов файловых систем и методов файлового доступа. Такая гибкость должна облегчить решение части непредвиденных проблем, которые неизбежно возникнут при взаимодействии посредством NEFS более разнообразных систем.

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

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

Наконец, остаются нерешенными проблемы защиты. Появляются надежные схемы аутентификации, подобные Kerberos; можно надеяться, что NEFS адаптирует по крайней мере одну из них.