[PATCH] Better handle NULL utimes() in the NFS client

Bruce Evans brde at optusnet.com.au
Wed Jan 16 03:49:20 UTC 2013


On Tue, 15 Jan 2013, John Baldwin wrote:

> On Monday, January 14, 2013 11:51:23 pm Bruce Evans wrote:
>>
>> I can't see anything that does the different permissions check for
>> the VA_UTIMES_NULL case, and testing shows that this case is just broken,
>> at least for an old version of the old nfs client -- the same permissions
>> are required for all cases, but write permission is supposed to be
>> enough for the VA_UTIMES_NULL case (since write permission is sufficient
>> for setting the mtime to the current time (plus epsilon) using write(2)
>> and truncate(2).  Setting the atime to the current time should require
>> no more and no less than read permission, since it can be done using
>> read(2), but utimes(NULL) requires write permission for that too).
>
> Correct.  All the other uses of VA_UTIMES_NULL in the tree are to
> provide the permissions check you describe and there is a large
> comment about it in ufs_setattr().  Other filesystems have comments
> that reference ufs_setattr().  I think these checks should be done
> in nfs_setattr() rather than in the routine to build an NFS attribute
> object however.

Perhaps it can be done in vfs.  There are some technical problems with
this, but perhaps they are small.  One is that file systems might not
even have any timestamps.  (I forgot to mention a related problem with
the error handling.  The permissions checks for utimes() are usually
too strict for file systems that only have fake ownerships, like
msdosfs.  OTOH, msdosfs also doesn't have atimes for most variants of
the file system.  Since utimes() is supposed to set both the mtime and
the atime (especially in the non-NULL case), it strictly cannot work
on msdosfs.  But msdosfs is not strict about this.  It silently ignores
the atimes when it can't set them.)

> Fixing NFS to properly use vfs_timestamp() seems to be a larger
> project.

I think it is smaller.  For the new nfs code, it is not as simple as
changing the NFSGETTIME() macro, since nfs wants extra precision in
most cases (for things like comparing cache times), and very rarely
wants the semantics of vfs_timestamp().

I somehow missed seeing seeing even more confusion in this area:
- the new nfs code also has a macro NFSGETNANOTIME() which reduces to
   getnanotime().
- monotonic times should be used if possible, but the new nfs code only
   uses them for NFSD_MONOSEC (which is used a lot) and in 2 places in
   nfs_commonkrpc.c where a hard-coded getmicrouptime() is used.
- NFSGETNANOTIME() is used 4 times.  But there are 4 hard-coded uses of
   getnanotime().  The latter are mostly in places where vfs_timestamp()
   is correct, for things like n_atim for fifos.  nfs almost never needs
   to set file timestamps, since most file timestamps are set by leaf
   (non-nfs) file systems on the server.  The only exceptions that I know
   about are the ones already noted (utimes(NULL) on the server, something
   in create() (?), and n_atim for fifos on the client (?).  n_atim for
   special files on the client were an exception when special files were
   supported.
In the old nfs code:
- there are no NFSGET*TIME() macros, and there don't seem to be any
   get*time() calls instead either.  The get*time() calls used are
   almost exactly the same ones as in the nfs nfs code, with the only
   exception that I noticed being the ones in the server for utimes(NULL).
- monotonic times are only used in the same 2 places in nfs_commonkrpc.c.
   The non-monotonic time_second is used a lot (hard-coded), I think in
   much the same places where the new nfs server code time_uptime via
   NFSD_MONOSEC.

I don't like obfuscating standard time calls using macros.  Others that
I don't like:
- both the old and the new nfs client use NFS_TIMESPEC_COMPARE() instead
   of the standard and better timespeccmp().  NFS_TIMESSPEC_COMPARE()
   is more verbose and only compares for equality.  Its implementation
   is home made and not based on timespeccmp().
- the new nfs client also has a macro NFS_CMPTIME().  This gives the
   same result as NFS_TIMESPEC_COMPARE() (or timespeccmp(..., =)..  Iti
   works accidentally for both timespecs and timevals, due to the POSIX
   bug that the struct members for timespecs abuse the prefix for timevals
   in their spelling.  Code derived from the old nfs client has not been
   translated to use the new macro (the "new" macro is probably actually
   older and may even be older or more likely just from a different code
   base than FreeBSD's timespeccmp()).
- the new nfs client also has a macro NFS_SETTIME().  This doesn't actually
   set the time, but converts from the global timeval `time' to a timespec
   in the same way as the standard macro TIMEVAL_TO_TIMESPEC() would if it
   is passed a pointer to the global timeval.  Accessing the global `time'
   like this would give races.  Fortunately, the global `time' doesn't
   exist in FreeBSD, so of course this macro is never used.

I like NFSD_MONOSEC, however.  Global variables give a much more fragile
and harder to translate API than function calls and function-like macros.

Bruce


More information about the freebsd-fs mailing list