[Dovecot] Small problem with src/lib/mountpoint.c [now with patch attached!]

Mike Brudenell pmb1 at york.ac.uk
Wed Jul 18 14:52:59 EEST 2007


Ah!  After a bit of test programming I understand what's going on now.

If I'm right then there are a few incorrect assumptions in Dovecot's  
logic.  (Sorry! :-)


On 17 Jul 2007, at 18:12, Timo Sirainen wrote:

>> auto_direct     /mailstore/messages/p   autofs   
>> direct,ignore,dev=4740014       1184648400
>> crypt2.york.ac.uk:/vol/vol9/p   /mailstore/messages/p   nfs      
>> proto=tcp,xattr,dev=4700386     1184668792
>>
>> Although there are two entries they have different device  
>> numbers.  The mountpoint_get() function attempts to guard against  
>> this by checking the device number obtained matches that of the  
>> path being located.
>
> What do you mean different device numbers? Both have the same mount  
> path, so how can they be different?

Looking again at the above two example lines from our /etc/mnttab I  
see that the first entry includes "ignore" as an option.  As I  
recollected that Dovecot's code in mountpoint_get() checks for  
filesystems marked "ignore" and should, umm, ignore them(!) when  
searching for the correct entry in the mount tab ... yet it wasn't  
working.

So I've just written a minimal program to display all the entries of / 
etc/mnttab and their fields using getmntent() and now think Dovecot's  
checks are incorrect, at least on Solaris...


PROBLEM 1 :: Skipping swap and ignored filestores is incorrect
--------------------------------------------------------------
The mountpoint_get() function in lib/mountpoint.c uses getmntent() to  
fetch each entry from /etc/mnttab in turn.  It then checks the  
ent.mnt_fstype field to see if it is MNTTYPE_SWAP ("swap") or  
MNTTYPE_IGNORE ("ignore") and, if it is, the entry is skipped.

However I this appears to be incorrect for Solaris 10 (and probably  
earlier versions too).  For example:
   * swap does NOT return "swap" as the value of ent.mnt_fstype
   * ignored filestores do NOT return "ignore" as the value of  
ent.mnt_fstype

Instead you get:

   * swap returns "swap" has the value of ent.mnt_special and "tmpfs"  
for
     ent.mnt_fstype

   * ignored filesystems return "ignore" as one of the options within  
the
     ent.mnt_mntopts field.  (NB: this might be one of several comma- 
separated
     options and could appear anywhere in the list.)

So I now believe that:

1.  My fix of also skipping filestores whose ent.mnt_fstype is  
"autofs" is not
     fully correct -- the problem should really be dealt with by  
properly
     checking for the "ignore" option instead;

2.  The "ignore" option should be being checked within  
ent.mnt_mntopts instead
     of ent.mnt_fstype; this would deal with the problem of the  
template autofs
     entry being picked up and used as it has the "ignore" option.

3.  Swap should be detected by checking either ent.mnt_special being  
"swap" or
     ent.mnt_fstype being "tmpfs" -- I'm not sure which is correct,  
or if both
     tests are needed.


PROBLEM 2 :: Matching an entry in /etc/mnttab using getmntent() is  
incorrect
------------------------------------------------------------------------ 
----
As for the oddity with device numbers I mentioned previously, I now  
understand this and think it shows up another incorrect assumption...

When using the automounter you get a permanent entry in /etc/mnttab  
that I will refer to as a 'template' entry.  In its options field it  
includes a device number of the form "dev=XXXXX" where XXXXX is a  
hexadecimal number (at least, it is hex on Solaris 10!).

When the automounter REALLY mounts the filestore as it is needed it  
ADDS a second entry for the mounted filesystem.  This has its own  
(different) "dev=XXXXX" number.  Don't ask me why this is: presumably  
there are Good Reasons.

When locating the entry for a path in /etc/mnttab Dovecot tries to do  
this by comparing the device number for the entry with the device  
number of the path being located.  It does this by using stat() on  
the mount point given in the entry.

Whilst this works for manually-listed mounts it does not work when  
the auto-mounter is involved.  This is how Dovecot is being fooled...

1.  We start off with just the automounter's 'template' entry in /etc/ 
mnttab:

     auto_direct  /mailstore/messages/p  autofs   
direct,ignore,dev=4740014  1184648400

2.  Dovecot starts by performing a stat on the path to be located.  This
     causes the automounter to secretly mount the real filestore --  
adding the
     second entry to /etc/mnttab with its different device number:

     crypt2.york.ac.uk:/vol/vol9/p  /mailstore/messages/p  nfs   
proto=tcp,xattr,dev=4700386  1184668792

     The number stat() returns is THIS device number -- 4700386 --  
and NOT that
     from the 'template' (4740014).  This gets stored away in the  
"st" structure
     ready for later comparisons.

3.  Dovecot now uses getmntent() to fetch each entry from /etc/mnttab  
in turn.
     It calls stat() on the mount point in the entry to gets its  
device number
     into the "st2" structure.

     In the case of using the automounter the first entry reached is the
     'template' entry, with mount point /mailstore/messages/p.  Using  
stat()
     on this returns its device number ... and the number you get  
back is that
     of the real mounted filesystem.

     Dovecot then compares the deivce numbers in the "st" and "st2"  
structures,
     finds they match and so assumes it has found the matching entry in
     /etc/mnttab

     Unfortunately it hasn't: it has only got the template, which  
doesn't have
     the remote host and fspath of the server from which the  
filestore is
     mounted.  This entry even has a different device number  
(4740014) which
     would have shown up the problem ... but unfortunately Dovecot isn't
     checking the device number from the entry itself.

I believe the correct solution is to change step (3) so that it does  
NOT stat() the mount point of each entry: you can't rely on getting  
the right device number for that entry when the automounter is used.

Instead the proper solution is to parse the mount options in  
ent.mnt_mntopts and extract the device number from the "dev=" option,  
then compare THAT with the device number of the path being located  
(from step (1)).

Of course this is fraught with problems because the mount options is  
a comma-separated text string and the "dev=" option could appear  
anywhere within it.  Also, the value of "dev=" is in hexadecimal (at  
least it is on Solaris 10), making comparison with the decimal  
integer from the start() in step (1) non-trivial.

Given this complexity it may be sufficient to instead properly check  
and skip filestores marked "ignore".  The automounter's 'template'  
entries are so marked; thus skipping them would allow Dovecot to pass  
over them in the list and locate the proper entry for the real,  
mounted, filestore instead.

Unfortunately this check still involves parsing the ent.mnt_mntopts  
string, as the "ignore" option could appear as any one of:

     ignore
     ignore,
     ,ignore
     ,ignore,

So some clever coding is still needed or a regular expression such as
     ^(.*,)?ignore(,.*)?$
Alternatively there's the dreaded strtok() function -- dreaded  
because it actually changes the source string by over-writing  
separator characters with '\0' as it tokenises it.  Lovely!  :-(


Cheers,
Mike B-)

-- 
The Computing Service, University of York, Heslington, York Yo10 5DD, UK
Tel:+44-1904-433811  FAX:+44-1904-433740

* Unsolicited commercial e-mail is NOT welcome at this e-mail address. *




More information about the dovecot mailing list