Формат электронной почты Maildir — это распространенный способ хранения сообщений электронной почты в файловой системе , а не в базе данных. Каждому сообщению назначается файл с уникальным именем, а каждая почтовая папка — это каталог файловой системы, содержащий эти файлы. Maildir был разработан Дэниелом Дж. Бернстайном около 1995 года с главной целью — исключить необходимость в программном коде для обработки блокировки и разблокировки файлов с помощью локальной файловой системы. [1] Дизайн Maildir отражает тот факт, что единственными операциями, допустимыми для сообщения электронной почты, являются его создание, удаление или изменение его статуса каким-либо образом.
Каталог Maildir (часто называемый Maildir
) обычно имеет три подкаталога с именами tmp
, new
и cur
. [2]
tmp
временно хранит сообщения электронной почты, которые находятся в процессе доставки. Этот подкаталог может также хранить другие виды временных файлов.new
подкаталоге хранятся сообщения, которые были доставлены, но еще не просмотрены ни одним почтовым приложением.cur
подкаталоге хранятся сообщения, которые уже были просмотрены почтовыми приложениями. [3]Сэм Варшавчик, автор Courier Mail Server и другого программного обеспечения, определил расширение Maildir++ [3] [4] для формата Maildir для поддержки подпапок и почтовых квот. Каталоги Maildir++ содержат подкаталоги с именами, начинающимися с '.' (точка), которые также являются папками Maildir++. Расширение соответствует исходной спецификации Maildir, которая допускает подкаталоги в дополнение к tmp , new и cur .
Агент доставки почты — это программа , которая доставляет сообщение электронной почты в Maildir. Агент доставки почты создает новый файл с уникальным именем в tmp
каталоге. [5] [6] [3] Во время его изобретения гарантировать уникальные имена файлов было сложно. Первоначальный алгоритм qmail [1] для уникальных имен был следующим:
stat()
сообщает, что имя файла существует, то подождите две секундыК 2000 году автор qmail рекомендовал в обновленной спецификации [5] добавлять значение счетчика для каждого процесса к PID, значение которого должно увеличиваться после каждой доставки. Рекомендация по ограничению скорости «подождать две секунды» была отменена.
К 2003 году рекомендации были дополнительно изменены, чтобы вместо PID и счетчика средняя часть имени файла создавалась путем «объединения достаточного количества следующих строк для гарантии уникальности» даже в случае нескольких одновременных доставок в один и тот же почтовый каталог от одного или нескольких процессов: [7]
- # n , где n — это (в шестнадцатеричном формате) вывод
unix_sequencenumber()
системного вызова операционной системы, который возвращает число, увеличивающееся на 1 при каждом вызове, начиная с 0 после перезагрузки.- X n , где n — это (в шестнадцатеричном формате) вывод
unix_bootnumber()
системного вызова операционной системы, который сообщает количество загрузок системы. Вместе с # это гарантирует уникальность; к сожалению, большинство операционных систем не поддерживаютunix_sequencenumber()
иunix_bootnumber()
.- R n , где n — это (в шестнадцатеричном формате) вывод
unix_cryptorandomnumber()
системного вызова операционной системы или эквивалентный источник, например/dev/urandom
. К сожалению, некоторые операционные системы не включают криптографические генераторы случайных чисел.- В n , где n — это (в шестнадцатеричном формате) номер инода UNIX этого файла. К сожалению, номера инодов не всегда доступны через NFS .
- V n , где n — это (в шестнадцатеричном формате) номер устройства UNIX этого файла. К сожалению, номера устройств не всегда доступны через NFS. (Номера устройств также бесполезны в стандартной файловой системе UNIX: maildir должен находиться в пределах одного устройства UNIX для
link()
иrename()
для работы.)- M n , где n — (в десятичной системе) счетчик микросекунд,
gettimeofday()
используемый в левой части уникального имени.- P n , где n — идентификатор процесса (в десятичной системе счисления).
- Q n , где n — (в десятичной дроби) количество доставок, выполненных данным процессом.
Этот алгоритм 2003 года подвергся критике [8] в 2006 году как излишне сложный со стороны Тимо Сирайнена , создателя Dovecot .
По состоянию на ноябрь 2023 года автор qmail Дэниел Бернстайн не внес никаких дальнейших изменений в рекомендации по генерации имен файлов 2003 года. [9] В современных системах POSIX временные файлы можно безопасно создавать с помощью mkstemp
функции библиотеки C.
Процесс доставки сохраняет сообщение в maildir путем создания и записи в , а затем перемещения этого файла в . Перемещение может быть выполнено с помощью , что является атомарным во многих системах. [10] В качестве альтернативы это можно сделать путем жесткой привязки файла к , а затем удаления ссылки на файл из . Любой оставшийся файл в конечном итоге будет удален. Эта последовательность гарантирует, что программа, читающая maildir, не увидит частично записанное сообщение. Может быть несколько программ, читающих maildir одновременно. Они варьируются от почтовых пользовательских агентов (MUA), которые напрямую обращаются к файловой системе сервера, через серверы протокола доступа к сообщениям в Интернете или почтового протокола, действующие от имени удаленных MUA, до таких утилит, как biff и rsync , которые могут знать или не знать о структуре maildir. Читатели никогда не должны заглядывать в .tmp/uniquefilename
new/uniquefilename
rename
new
tmp
tmp
Когда осознанный процесс чтения maildir (либо сервер POP или IMAP , либо почтовый агент пользователя, действующий локально) находит сообщения в new
каталоге, он должен переместить их в cur
. Это всего лишь способ уведомить пользователя «у вас есть X новых сообщений». [11] Это перемещение должно быть выполнено с использованием атомарной файловой системы rename()
, поскольку альтернативный метод связывания-затем-отсоединения является неатомарным и может привести к дублированию сообщений. На этом этапе к именам файлов добавляется информационный суффикс. Он состоит из двоеточия ( для отделения уникальной части имени файла от фактической информации), «2», запятой и различных флагов . «2» указывает версию информации, которая следует за запятой. «2» — единственная официально указанная в настоящее время версия, «1» — экспериментальная версия. Спецификация определяет флаги, которые показывают, было ли сообщение прочитано, удалено и т. д.: начальная (заглавная) буква слов «Passed», «Replied», «Seen», «Trashed», «Draft» и «Flagged». [7] Приложения часто решают дополнить этот весьма ограниченный набор флагов, например, notmuch [12] предлагает синхронизацию флагов в дополнение к произвольным определяемым пользователем флагам, [13] в то время как Dovecot использует строчные буквы для соответствия 26 ключевым словам IMAP, [6] которые могут включать такие ключевые слова, как $ MDNSent или определяемые пользователем флаги.
Хотя Maildir был предназначен для использования без блокировки, на практике некоторое программное обеспечение, использующее Maildirs, также использует блокировки, например Dovecot. [14]
Стандарт Maildir может быть реализован только в системах, которые принимают двоеточия в именах файлов. [15]
Системы, не допускающие двоеточия в именах файлов (включая Microsoft Windows и некоторые конфигурации Novell Storage Services ), могут использовать нестандартный альтернативный разделитель, такой как ";" или "-". Часто бывает просто исправить свободное и открытое программное обеспечение , чтобы использовать другой разделитель. [16]
Поскольку в настоящее время нет соглашения о том, какой символ должен быть этим альтернативным разделителем, могут возникнуть трудности с совместимостью между различными программами, поддерживающими Maildir, в этих системах. Однако не все программное обеспечение, связанное с Maildir, должно знать, какой символ является разделителем, поскольку не все программное обеспечение, связанное с Maildir, должно иметь возможность читать или изменять флаги сообщения («прочитано», «ответил» и т. д.); программное обеспечение, которое просто доставляет в Maildir или архивирует старые сообщения из него, основываясь только на дате, должно работать независимо от того, какой разделитель используется. Если только MUA необходимо читать или изменять флаги сообщений, и используется только один MUA, то нестандартные альтернативные разделители могут использоваться без проблем с совместимостью.
Все эти проблемы довольно бессмысленны. Только первый шаг действительно гарантирует, что письма не будут перезаписаны, остальное просто звучит хорошо. Даже если они могут время от времени обнаруживать проблему, они не дают гарантированной защиты и будут так же легко пропускать дублирующиеся имена файлов и перезаписывать существующие письма.¶ Шаг 2 бессмыслен, потому что между шагами 2 и 3 есть состояние гонки. Комбинация PID/хоста сама по себе уже должна гарантировать, что такой файл никогда не будет найден. Если это так, что-то сломалось, и проверка stat() не поможет, так как другой процесс может делать то же самое в то же время, и вы в конечном итоге пишете в тот же файл в tmp/, что приводит к повреждению почты.¶ На шаге 4 link() не сработает, если идентичный файл уже существует в Maildir, верно? Неправильно. Файл мог быть уже перемещен в каталог cur/, и поскольку к тому времени он может содержать любое количество флагов, вы больше не сможете проверить с помощью простой stat(), существует он или нет.¶ Шаг 2 был указан как полезный, если часы перешли в обратную сторону. Однако это не дает никаких фактических гарантий безопасности, так как идентичное базовое имя файла уже может существовать в cur/. Кроме того, если система была только что перезагружена, файл в tmp/, вероятно, можно было бы даже безопасно перезаписать (предполагая, что он еще не был связан с new/).¶ Так что на самом деле, все, что важно для того, чтобы письма не были перезаписаны в вашем Maildir, это шаг 1: Всегда создавайте имена файлов, которые гарантированно будут уникальными. Забудьте о 2-секундном ожидании и тому подобном, о чем говорится на странице руководства Qmail.
Эта спецификация требует, чтобы действие функции было атомарным.