birthtime initialization
Bruce Evans
brde at optusnet.com.au
Wed Jul 23 15:42:54 UTC 2008
On Wed, 23 Jul 2008, Jaakko Heinonen wrote:
> On 2008-07-22, Bruce Evans wrote:
>>> + VATTR_NULL(vap);
>>
>> I want to initialize va_birthtime to { -1, 0 } here only. Don't
>> initialize the whole vattr here. VOP_GETTATR() is supposed to initalize
>> everything, but doesn't for va_birthtime. If there any other fields
>> that VOP_GETTATR() doesn't initialize, then these should be searched
>> for and fixed instead of setting them to the garbage value given by
>> vattr_null.
>
> At least xfs gets it wrong for several fields.
>
> /*
> * Fields with no direct equivalent in XFS
> * leave initialized by VATTR_NULL
> */
> #if 0
> vap->va_filerev = 0;
> vap->va_birthtime = va.va_ctime;
> vap->va_vaflags = 0;
> vap->va_flags = 0;
> vap->va_spare = 0;
> #endif
That's amazingly bad.
First, the fields shouldn't be initialized using VATTR_NULL() in
VOP_GETATTR(). Doing so breaks the preinitialization that we want to
add (maybe also layering). This bug seems to be present in only the
following file systems:
fdescfs, mqfs, pseudofs, tmpfs, xfs
The uninitialized fields should give stack garbage.
Second, VNOVAL is an extremly bogus default value. For va_flags, it
gives all flags set, so ls -lo output would be weird (and wrong since
the flags aren't actually there).
Third, va_vaflags and va_spare aren't part of the VOP_GETATTR() API.
va_vaflags is an input parameter for VOP_SETATTR(). va_spare is just
spare (unused). VATTR_NULL() initializes va_vaflags to 0, not VNOVAL
(as is required for the usual case in VOP_SETTATR()), and it knows
better than to initialize unused fields (it also doesn't initialize
unnamed padding -- stack garbage in this is OK since vattr is never
copied directly to userland).
After deleting the bogus initializations, we're left with va_filerev,
va_birthtime and va_flags. Most file systems don't support these, so
they could usefully all be handled by defaulting them as in the proposed
changes for va_birthtime.
>>> Index: sys/ufs/ufs/ufs_vnops.c
>>> ...
>>> Index: sys/fs/msdosfs/msdosfs_vnops.c
>>> ...
>>> Index: sys/nfsclient/nfs_subs.c
>>
>> There are a probably more file systems that have missing or slightly
>> incorrect (all zero) settings of va_birthtime.
>
> Many file systems misses settings of va_birthtime. That's the reason why
> I initialized it in vn_stat(). I have seen four types of
> initializations:
>
> 1) Support and set birthtime. (UFS2, tmpfs, msdosfs (not all
> variants of msdosfs support birthtime), nfs4?)
>
> 2) Set birthtime to zero. (UFS1, nfs (nfs zeroes the vattr structure))
Zeroing it is almost as bad as VATTR_NULL()ing it.
> 3) Initialize vattr with VATTR_NULL() but not birthtime explicitly. Thus
> tv_sec and tv_nsec are set to -1 (VNOVAL). (devfs, xfs, portalfs,
> pseudofs)
>
> 4) Not initialize birthtime at all. Those would be fixed by initializing the
> birthtime in vn_stat(). (cd9660, hpfs, ntfs, smbfs, udf, ext2fs,
> reiserfs)
> I couldn't test but I suspect that also coda belongs to this group.
>
> So I see two ways to fix:
>
> - initialize birthtime in vn_stat() and add/fix explicit setting for group 2
> and 3 file systems or
> - add explicit initialization to all file systems missing it
> (groups 3 and 4) and fix group 2 to initialize birthtime to correct value
(3) and (4) are only different due to bugs. I want to initialize
va_birthtime and maybe a couple of other fields in vn_stat(), and depend
on this and not initialize to the same or a worse value in case (3). This
requires removing VATTR_NULL() or zeroing in some cases and checking that
everything is still initialized. All old fields should be handled by
explicit initialization as in ffs1, and all new fields should have defaults.
> I have updated the patch per your comments and checked more file
> systems. I have verified that with this patch these file systems return
> correct birthtime values (real birthtime or {-1, 0} if not supported):
>
> UFS2, UFS1, cd9660, nfs, ext2fs, smbfs, reiserfs, xfs, ntfs, devfs,
> procfs, linprocfs, tmpfs, msdosfs, portalfs, udf.
I don't want the case (3). Otherwise good.
>
> For pseudofs I set birthtime to current time.
I don't like this. birthtime should be <= all other file times. If
a file system doesn't support birthtime, then it could also set birthtime
= mtime, but that isn't useful either. Better set it to -1 as in ffs1
(exept ffs1 set it to 0).
>
> %%%
> Index: sys/kern/vfs_vnops.c
> ===================================================================
> --- sys/kern/vfs_vnops.c (revision 180729)
> +++ sys/kern/vfs_vnops.c (working copy)
> @@ -703,6 +703,13 @@ vn_stat(vp, sb, active_cred, file_cred,
> #endif
>
> vap = &vattr;
> +
> + /*
> + * Not all file systems initialize birthtime.
> + */
Change to something like:
/*
* Initialize defaults for new and/or unusual fields, so that file
* systems which don't support these fields don't need to know
* about them.
*/
> + vap->va_birthtime.tv_sec = -1;
> + vap->va_birthtime.tv_nsec = 0;
> +
> error = VOP_GETATTR(vp, vap, active_cred, td);
> if (error)
> return (error);
> Index: sys/ufs/ufs/ufs_vnops.c
> ===================================================================
> --- sys/ufs/ufs/ufs_vnops.c (revision 180729)
> +++ sys/ufs/ufs/ufs_vnops.c (working copy)
> @@ -410,7 +410,7 @@ ufs_getattr(ap)
> vap->va_mtime.tv_nsec = ip->i_din1->di_mtimensec;
> vap->va_ctime.tv_sec = ip->i_din1->di_ctime;
> vap->va_ctime.tv_nsec = ip->i_din1->di_ctimensec;
> - vap->va_birthtime.tv_sec = 0;
> + vap->va_birthtime.tv_sec = -1;
> vap->va_birthtime.tv_nsec = 0;
> vap->va_bytes = dbtob((u_quad_t)ip->i_din1->di_blocks);
> } else {
Can just delete birthtime references here. Unless I've missed a bzero.
> Index: sys/nfsclient/nfs_subs.c
> ===================================================================
> --- sys/nfsclient/nfs_subs.c (revision 180729)
> +++ sys/nfsclient/nfs_subs.c (working copy)
> @@ -628,6 +628,8 @@ nfs_loadattrcache(struct vnode **vpp, st
> vap->va_rdev = rdev;
> mtime_save = vap->va_mtime;
> vap->va_mtime = mtime;
> + vap->va_birthtime.tv_sec = -1;
> + vap->va_birthtime.tv_nsec = 0;
> vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
> if (v3) {
> vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
Need to remove the zeroing and check other fields before defaulting
birthtime here.
> Index: sys/fs/pseudofs/pseudofs_vnops.c
> ===================================================================
> --- sys/fs/pseudofs/pseudofs_vnops.c (revision 180729)
> +++ sys/fs/pseudofs/pseudofs_vnops.c (working copy)
> @@ -200,7 +200,7 @@ pfs_getattr(struct vop_getattr_args *va)
> vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0];
> vap->va_nlink = 1;
> nanotime(&vap->va_ctime);
> - vap->va_atime = vap->va_mtime = vap->va_ctime;
> + vap->va_atime = vap->va_mtime = vap->va_birthtime = vap->va_ctime;
>
> switch (pn->pn_type) {
> case pfstype_procdir:
I don't understand why it doesn't have _any_ persistent storage for times.
> Index: sys/fs/portalfs/portal_vnops.c
> ===================================================================
> --- sys/fs/portalfs/portal_vnops.c (revision 180729)
> +++ sys/fs/portalfs/portal_vnops.c (working copy)
> @@ -462,6 +462,8 @@ portal_getattr(ap)
> nanotime(&vap->va_atime);
> vap->va_mtime = vap->va_atime;
> vap->va_ctime = vap->va_mtime;
> + vap->va_birthtime.tv_sec = -1;
> + vap->va_birthtime.tv_nsec = 0;
> vap->va_gen = 0;
> vap->va_flags = 0;
> vap->va_rdev = 0;
This uses both bzero() and vattr_null(). Oops, I only grepped for use
of VATTR_NULL() when I looked for bogus initializations above.
VATTR_NULL() is the public interface and vattr_null() is an implementation
detail. Add the following file systems to the list of file systems with
bogusly initialized vattr's in VOP_GETATTR():
devfs, portalfs
These both misuse bzero() and vattr_null(). There are no other misuses
of vattr_null().
> Index: sys/fs/devfs/devfs_vnops.c
> ===================================================================
> --- sys/fs/devfs/devfs_vnops.c (revision 180729)
> +++ sys/fs/devfs/devfs_vnops.c (working copy)
> @@ -543,6 +543,8 @@ devfs_getattr(struct vop_getattr_args *a
>
> vap->va_rdev = cdev2priv(dev)->cdp_inode;
> }
> + vap->va_birthtime.tv_sec = -1;
> + vap->va_birthtime.tv_nsec = 0;
> vap->va_gen = 0;
> vap->va_flags = 0;
> vap->va_nlink = de->de_links;
See above.
> Index: sys/gnu/fs/xfs/FreeBSD/xfs_vnops.c
> ===================================================================
> --- sys/gnu/fs/xfs/FreeBSD/xfs_vnops.c (revision 180729)
> +++ sys/gnu/fs/xfs/FreeBSD/xfs_vnops.c (working copy)
> @@ -263,6 +263,8 @@ _xfs_getattr(
> vap->va_atime = va.va_atime;
> vap->va_mtime = va.va_mtime;
> vap->va_ctime = va.va_ctime;
> + vap->va_birthtime.tv_sec = -1;
> + vap->va_birthtime.tv_nsec = 0;
> vap->va_gen = va.va_gen;
> vap->va_rdev = va.va_rdev;
> vap->va_bytes = (va.va_nblocks << BBSHIFT);
See above (need to do somethign about the VATTR_NULL()).
> %%%
Grepping for va_.*flags in only sys/fs/ shows the following problems
in VOP_SETATTR():
- coda: sets va_vaflags in a macro but never uses va_vaflags (needed
for layering?)
- ntfs: sets va_flags to ip->i_flag -- nonsense -- i_flag is an internal
flag that has nothing to do with va_flags
- nwfs: sets va_vaflags in nwfs_attr_cacheenter(), but I think nothing uses
this setting.
- smbfs: sets va_vaflags in smbfs_attrcachelookup() ...
- tmpfs: sets va_vaflags and also va_spare.
and the following non-problems:
- all except msdosfs set va_flags to 0, so defaulting va_flags to 0 and
deleting most settings of it would work well.
Bruce
More information about the freebsd-fs
mailing list