the Maildir standard

The Maildir standard is a blessing to mail reliability. You can read about it here and here.

A maildir is a directory with 3 subdirectories, named tmp, new and cur.

The following is my interpretation of a completely compliant Maildir delivery:

  1. Generate a name for the message, consisting of time.pid.host. time is a normal UNIX timestamp (looks like 982493758). pid is something that doesn't repeat within one second on a single machine. host is the hostname of the machine that is running the delivery process.

    pid is often the process ID of the delivering process, sometimes extended with the value of a counter internal to that process. It may also be the output from some reliable sequence number generator prepended with "#".

  2. Start a 24 hour timer. If the timer expires, abort delivery.
  3. chdir() into the Maildir.
  4. stat() tmp/time.pid.host. If it returns ENOENT, continue with the next step. Otherwise, wait 2 seconds and try again (generating a new compliant filename first), for a limited number of times.
  5. Create the file in tmp/.
  6. Write the message to the file, checking every returnvalue of write() during this process.
  7. fsync() and close() the file, checking the returnvalues of both syscalls.
  8. link() the message to new/time.pid.host and check the returnvalue of the systemcall.
  9. Message delivered.

An implementation may attempt to unlink() the file in tmp/ before exiting, irregardless of whether delivery succeeded.

Maildir readers look in new/ for new messages. Readers may freely read and remove new/unique. They may also rename messages from new/unique to cur/unique:info. [I think this is a mistake. The rename should be replaced by a link() and unlink(). What if, somehow, a message gets delivered, read, moved to cur/, and then another message is delivered into new/ with the *same* 'unique' filename? The rename will overwrite the old copy.]


The following is a list of named 'requirements'. Those with Name mentioned in bold are part of the Maildir standard and are required for a compliant implementation. All the others are features that can be implemented for good karma but are not required.

Some of the requirementes listed only apply to readers or only to MDA's.

Name Requirement
24hourtimer Implementing the 24hour timer.
stattmp stat()'ing the name for tmp
unique generating a unique filename according to the spec.
write-check checking the returnvalue of write() everytime.
fsync doing fsync().
close doing close().
fsync-check checking fsync()'s returnvalue.
close-check checking close()'s returnvalue.
link using link() to move the message into new/.
link-check checking link()'s returnvalue.
success-unlink unlinking from tmp/ after successful delivery.
failure-unlink unlinking from tmp/ after failed delivery.
clean-tmp (readers) cleaning up files older than 36 hours in tmp/
skip-dot (readers) skipping filenames starting with dot when reading a maildir
quit-rename renaming files from new/ to cur/unique:info when a reader exits
quit-rename-link implementing quit-rename through link() and unlink()

I have audited the Maildir-implementations in several pieces of software. My findings:


One level up
peter(at)dataloss.nl