Pull to refresh

Как бы вы решили такую проблему совместимости? Ответ

Reading time 3 min
Views 690
Original author: Raymond Chen
Напоминаю, что это все-таки перевод Реймонда Чена, а не ответ на комментарии в предыдущем топике на хабре. Хотя предложения в комментариях здесь и там довольно похожи.

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


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

По поводу определения ошибочного драйвера, протокол CIFS (он же SMB) не дает клиенту достаточно информации, для того, чтобы определить, какая версия стоит на сервере. Есть только поле «family», которое сообщает об общей категории сервера (OS/2, Samba, Windows NT, …). Все, что клиент может сказать, это «Ну, сервер работает на какой-то версии Samba». Он не может сказать, ошибочная это версия, либо исправленная. Единственный способ это определить, начать работать с ошибочным сервером, и смотреть, чем это закончиться.

Сразу перечитать список в случае ошибки мы не можем: клиенту уже был возвращен частичный список в тот момент, когда ошибка была обнаружена. Приложение вызывает IShellFolder::EnumObjects и шелл выполняет быстрый запрос. Каждый раз приложение выполняет IEnumIDList::Next, и получает следующий результат. После возврата около 100 элементов, опс, оказывается, что сервер один из тех плохих, который ломает быстрый запрос. И что теперь? Невозможно вернуться назад во времени и забрать у приложения все те элементы, что ему уже были отданы.

Еще одним предложением было возвращать новый код ошибки типа ERROR_PLEASE_RESTART, который бы значил «Хм, у сервера проблемы. Пожалуйста, попробуйте снова, только медленней». Это практически то же, что «Ничего не делать», потому что сервер уже вернул конкретную ошибку, а именно STATUS_INVALID_LEVEL. Эта ошибка на самом деле значит не «Пожалуйста, повторите снова», а «Извини, но я не могу сделать это». Она может совершенно легально возникнуть тогда, когда, например, вы пытаетесь получить от сервера ответ в «быстром» режиме, а он его не поддерживает. Но эффект с точки зрения программы тот же. «Если FindNextFile возвратил ошибку xyz, это значит, что у сервера проблемы, и вы должны повторить запрос заново.» Назовите это «ERROR_PLEASE_RESTART» или «STATUS_INVALID_LEVEL» или «PURPLE_LILACS». Не важно, что вы выберете, результат один и тот же: существующий код должен быть изменен для того, чтобы знать о новой ошибке и правильно на нее реагировать. Программы, которые не будут изменены, начнут себя вести странно.

Борьба с этим багом, на самом деле, продолжалась несколько месяцев. За это время было обнаружено еще множество устройств, которые неправильно работали в «быстром» режиме. Некоторые из них были основаны на непропатченной версии Samba, некоторые использовали собственные реализации протокола SMB, и решение, найденное для Samba, для них не работало. Плохо было еще то, что большинство устройств были совсем бюджетными, и не позволяли как-либо проапдейтить прошивку.

Были также сообщения о подобных проблемах даже в некоторых полностью пропатченных распространённых дистрибутивах Linux.

Далее, некоторые из этих устройств неправильно обрабатывали «быстрые» запросы совершенно по-разному. Для примера, один из них, с которым я имел дело, не возвращал никаких кодов ошибки. Он просто возвращал мусорные данные, например, пропускал в каждом файле первые пять символов и возвращал остальные. Как можно определить подобную ошибку? Если сервер говорит «Ок, у меня есть файл e.txt», что ему ответит Windows: «О, я так не думаю. Я ставлю на то, что один из тех нехороших серверов, который пропускает первые пять символов, а на самом деле ты имеешь в виду readme.txt». А что если он действительно имеет файл e.txt?

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

Такое разнообразие ошибочного поведения при использовании «быстрых» запросов сделало сценарии автоматического определения ошибочных серверов нереалистичными. Особенно в случае, когда сервер возвращает правильно сформированные, но неправильные данные. И даже, если определение было бы правильным, то, когда сервер просто падает, это все равно не спасет.

Таким образом, решение было принято такое: перестать использовать «быстрые» запросы для чего-либо иного, кроме локальных дисков. Драйверы наиболее популярных файловых систем (NTFS, FAT, CDFS, UDF) находятся полностью под контролем Microsoft, и протестированы на совместимость с быстрым режимом.

Это все печально, но такова цена обеспечения совместимости.

У Реймонда несколько постов, связанных с разбором высказанных предложений, и я их все не стану переводить. Но, кому интересно, то:

Adding flags to APIs to work around driver bugs doesn't scale

Be very careful if you decide to change the rules after the game has ended

Adding a new flag to enable behavior that previously was on by default

Tags:
Hubs:
+21
Comments 19
Comments Comments 19

Articles