NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/48449: y2038 shortcomings of ffsv1



The following reply was made to PR kern/48449; it has been noted by GNATS.

From: Robert Elz <kre%munnari.OZ.AU@localhost>
To: gnats-bugs%netbsd.org@localhost
Cc: 
Subject: Re: kern/48449: y2038 shortcomings of ffsv1
Date: Thu, 10 Jun 2021 18:25:27 +0700

     Date:        Thu, 10 Jun 2021 05:30:02 +0000 (UTC)
     From:        David Holland <dholland-bugs%netbsd.org@localhost>
     Message-ID:  <20210610053002.E2EEC1A923B%mollari.NetBSD.org@localhost>
 
   |  Or, much more simply, store a 64-bit epoch in the superblock (this
   |  lets you pick it at newfs time) and add the 32-bit times to it. This
   |  requires adding a superblock field but we can probably afford that.
 
 No, that's is almost the worst of all possible solutions (even including
 just doing nothing).   It is simple and easy to implement in the steady
 state, but the transition mechanism is horrid.  If you really meant *only*
 at newfs time, then it is a waste of effort, anyone doing a newfs can set -O2
 and the issue doesn't arise.   The problem is with all the existing FFSv1
 filesystems, not new ones (and doing a dump/restore to handle the problem
 is way too invasive, and risky).
 
 While it is possible to set up a method whereby the offset can be added to
 (or changed in, which is the same thing) an existing filesystem, doing it
 is far more complex than is needed, as maintaining constant timestamps
 means that if the offset alters, so must every timestamp in every inode,
 and as that can't be done instantaneously, there's the possibility that the
 filesystem might get into a state where some inodes have been converted,
 and others not, which means inodes would need a state bit (old or new offset)
 and the superblock would need two new offset fields.   All a giant mess.
 
 Further, no matter what is done, a FFSv1 cannot store a range of timestamps
 that spans more than 140 years (approx), so any solution needs to live with
 that - which really means that we need to make FFSv1 vanish by 2100 or so.
 That is aside from perhaps access to some ancient filesystems in read only
 (or at least no time update) mode.
 
 Creating an environment where we can live with new FFSv1 filesystems created
 hundreds of years from now, because they can have a base offset at or near
 that time (and so would work fine) should not be an objective - assuming that
 FFS filesystems (in any form) will survive that long in common usage is
 something of a stretch, planning to make it possible is absurd - even if we
 do still want them then, the extra overhead of FFSv2 (or successors) will be
 irrelevant (it already is with today's storage sizes, compared with when it
 was created, and it was OK then).
 
 The "paste in the top half of the current time" is one solution that is even
 worse than this suggestion, as it is really just the same thing with a
 dynamically varying offset, and no flexibility at all.
 
 A very simple solution which will give us time to phase out (most, see below
 for the workaround) FFSv1 completely (within another 80/90 years from now) is
 to add a single extra bit to the superblock, which, when set, indicates that
 all times stored in the filesystem are unsigned.   That alters the range of
 times from 1907..2038 to 1970..2100 (give or take) instantly.
 
 To that we add a tool that scans the filesystem (or an option to fsck more
 likely) which determines if it is safe to set the bit (ie: there are no
 times with the top bit set anywhere) which should be true if no-one has
 been cheating (no-one really ever created/modified/accessed a FFS filesystem
 file before 1970 or after 2037) and if so, simply set the bit and that
 filesystem becomes safe to continue using until 2100 (after which, because
 of the range issue above, nothing can really extend it by more than a few
 years).  The kernel just needs one change to deal with this - when converting
 FFSv1 timestamps to time_t, check the bit in the superblock (copied to
 the mount struct) and if set, zero extend rather than sign extend the value.
 Nothing needs to change going the other way (except possibly defeating some
 compiler sign/range warnings, but that's probably already done).
 
 Simple, safe, and works.   Closer to 2100 we can drop support in the
 kernel for mounting FFSv1 filesystem at all (see below).
 
 For filesystems with times before 1970 or after 2037 (ie: anything with the
 top bit currently set) we can offer to fix them (set the value to something
 else of the user's choosing) and until that is fixed, hopefully sometime
 in the next 15 years, simply leave the bit clear, and the filesystem works
 the same (for that period) as it does now.
 
 In 2038 (or the years just before) we change the kernel to refuse to mount
 FFSv1 filesystems with the bit clear (see below) at least in rw mode (ro
 mode mounts can continue as long as the kernel retains FFSv1 support).
 
 There's a slightly more general method we could use if there are users
 with a legitimate reason for files with timestamps that predate 1970, but
 only perhaps for a decade or two or three (ie: files from older, pre-unix,
 systems, but created in the computer age ... seems doubtful to me, but
 perhaps someone has such things).
 
 For that, we just need to recognise that while the 140 year range is all
 that FFSv1 can possibly deal with, nothing says that the 0 value must
 occur in the centre of that range.   We could allow timestamps that range
 from 1950..2080 (approx) for example (and the range can vary per filesystem).
 This requires a (32 bits is enough) field in the superblock, not to store
 the origin, but to store the minimum (or max, makes no real difference) value
 of the range, and then we simply convert between those values and time_t.
 Once again time_t -> FFS time is a no-op conversion (just truncate the
 value to the low 32 bits) but this time FFS->time_t requires a comparison,
 if the (unsigned) FFS value is less than N (the unsigned version of the
 minimum (maybe maximum, they turn out to be almost the same thing) value
 of the supported range, for that filesystem) then treat the value as signed,
 sign extend it to become a time_t.   If it is greater (or equal), then zero
 extend instead. Or some arithmetic just like that (the precise formula can
 come later, if we need this, but it is that simple).   This becomes the same
 as the "one bit" version when the range is set to be 1970..2100 (or whatever)
 ie: all values become zero extended as the other side of the test can never
 be true.   Again, when the current time passes the end of the range, the
 kernel refuses to (rw) mount that filesystem any more.
 
 To deal with old filesystems into the distant future (this is the "below"
 referred to above) we need a user level (FUSE I assume) FFSv1 filesystem
 converter which can be configured via mount options to convert timestamps
 however is appropriate (there could even be a range of such things for
 different types of conversion, especially when allowing writes, and deciding
 how to store timestamps that are out of the range of what the filesystem
 could normally handle ... since performance doesn't matter any more, and
 special cases are definitely allowed, all kinds of possibilities exist
 for what to do).   Fortunately we have some decades before this will
 really be required (and by then, it might not be).
 
 Just please, no "variable time" nonsense in the filesystems, an inode
 with a timestamp that currently converts as Thu Jun 10 11:01:00 UTC 2021
 must always convert as exactly that (over the range, 1970-2038),
 never anything else, or not from the kernel anyway (what a user level hack
 might do is up to it, and if the user is content, that's good enough).
 
 kre
 


Home | Main Index | Thread Index | Old Index