adfs: improve timestamp precision
Quoted 11 lines · On Wed, 12 Jan 2011 18:03:08 +0000 Stuart Swales…
From: Stuart Swales<[email protected]>
ADFS (FileCore) storage complies with the RISC OS timestamp specification (40-bit centiseconds since 01 Jan 1900 00:00:00). It is desirable that stored timestamp precision be maintained to facilitate a precise copy of data and metadata from a hard disc (or image thereof) into a RISC OS emulator (such as RPCEmu). This patch implements a full-precision conversion from ADFS to Unix timestamp as the existing driver, for ease of calculation with old 32-bit compilers, uses the common trick of shifting the 40-bits representing centiseconds around into 32-bits representing seconds thereby losing precision.
The patch looks sane to me but I was unable to extract a diff which applies. Your email client is doing weird things. Please fix it and resend.
Quoted 17 lines
fs/adfs/inode.c +5
-1@@ -175,50 +175,48 @@ adfs_mode2atts(struct super_block *sb, s/* * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time -* referenced to 1 Jan 1900 (til 2248) +* referenced to 1 Jan 1900 (til 2248) so we need to discard 2208988800 seconds +* of time to convert from RISC OS epoch to Unix epoch. */ static void adfs_adfs2unix_time (struct timespec *tv,struct inode *inode) {unsigned int high, low; +/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since +* 01 Jan 1900 00:00:00 (RISC OS epoch) +*/
fs/adfs/inode.c adfs_mode2atts(struct super_block *sb, s · line 195
Quoted 10 lines
fs/adfs/inode.c +4
-2+static const s64 nsec_unix_epoch_diff_risc_os_epoch = +2208988800000000000LL ; +s64 nsec;if (ADFS_I (inode)->stamped ==0 ) goto cur_time; - high =ADFS_I (inode)->loadaddr<<24 ; - low =ADFS_I (inode)->execaddr; + high =ADFS_I (inode)->loadaddr&0xFF ;/* top 8 bits of timestamp */
We have weird things going on with the code layout.
fs/adfs/inode.c adfs_mode2atts(struct super_block *sb, s · line 201
Quoted 8 lines
fs/adfs/inode.c +5
-2+ low =ADFS_I (inode)->execaddr;/* bottom 32 bits of timestamp */ - high |= low>>8 ; - low&=255 ; +/* convert 40-bit centi-seconds to 32-bit seconds +* going via nanoseconds to retain precision +*/ + nsec = (((s64 ) high<<32 ) | (s64 ) low) *10000000 ;/* cs to ns */
and here.
nsec = (((s64)high << 32) | (s64)low) * 10000000; /* cs to ns */
would be typical.
fs/adfs/inode.c adfs_mode2atts(struct super_block *sb, s · line 203
Quoted 3 lines
fs/adfs/inode.c +1
-1/* Files dated pre 01 Jan 1970 00:00:00. */ -if (high<0x336e996a ) +if (nsec< nsec_unix_epoch_diff_risc_os_epoch)
more. Feel free to fix things up when altering code.
Quoted 31 lines
fs/adfs/inode.c +5
-16goto too_early; -/* Files dated post 18 Jan 2038 03:14:05. */ -if (high>=0x656e9969 ) - goto too_late; - -/* discard 2208988800 (0x336e996a00) seconds of time */ - high -=0x336e996a ; - -/* convert 40-bit centi-seconds to 32-bit seconds */ - tv->tv_sec = (((high %100 )<<8 ) + low) /100 + (high /100 <<8 ); - tv->tv_nsec =0 ; +/* convert from RISC OS to Unix epoch */ + nsec -= nsec_unix_epoch_diff_risc_os_epoch; + + *tv =ns_to_timespec (nsec);return ; cur_time: - *tv =CURRENT_TIME_SEC ; + *tv =CURRENT_TIME ;return ; too_early: tv->tv_sec = tv->tv_nsec =0 ;return ; - - too_late: - tv->tv_sec =0x7ffffffd ; - tv->tv_nsec =0 ; -return ;
[Thanks Andrew - icedove had screwed up the patch somewhat. I'm using alpine to send this (have tested that it went round the loop ok and produces the same patch)]
fs/adfs/inode.c +20 -22
@@ -175,50 +175,48 @@ adfs_mode2atts(struct super_block *sb, s/* * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time -* referenced to 1 Jan 1900 (til 2248) +* referenced to 1 Jan 1900 (til 2248) so we need to discard 2208988800 seconds +* of time to convert from RISC OS epoch to Unix epoch. */ static void adfs_adfs2unix_time (struct timespec *tv,struct inode *inode) {unsigned int high, low; +/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since +* 01 Jan 1900 00:00:00 (RISC OS epoch) +*/ +static const s64 nsec_unix_epoch_diff_risc_os_epoch = +2208988800000000000LL ; +s64 nsec;if (ADFS_I (inode)->stamped ==0 ) goto cur_time; - high =ADFS_I (inode)->loadaddr <<24 ; - low =ADFS_I (inode)->execaddr; + high =ADFS_I (inode)->loadaddr &0xFF ;/* top 8 bits of timestamp */ + low =ADFS_I (inode)->execaddr;/* bottom 32 bits of timestamp */ - high |= low >>8 ; - low &=255 ; +/* convert 40-bit centi-seconds to 32-bit seconds +* going via nanoseconds to retain precision +*/ + nsec = (((s64 ) high <<32 ) | (s64 ) low) *10000000 ;/* cs to ns */ /* Files dated pre 01 Jan 1970 00:00:00. */ -if (high <0x336e996a ) +if (nsec < nsec_unix_epoch_diff_risc_os_epoch) goto too_early; -/* Files dated post 18 Jan 2038 03:14:05. */ -if (high >=0x656e9969 ) - goto too_late; - -/* discard 2208988800 (0x336e996a00) seconds of time */ - high -=0x336e996a ; - -/* convert 40-bit centi-seconds to 32-bit seconds */ - tv->tv_sec = (((high %100 ) <<8 ) + low) /100 + (high /100 <<8 ); - tv->tv_nsec =0 ; +/* convert from RISC OS to Unix epoch */ + nsec -= nsec_unix_epoch_diff_risc_os_epoch; + + *tv =ns_to_timespec (nsec);return ; cur_time: - *tv =CURRENT_TIME_SEC ; + *tv =CURRENT_TIME ;return ; too_early: tv->tv_sec = tv->tv_nsec =0 ;return ; - - too_late: - tv->tv_sec =0x7ffffffd ; - tv->tv_nsec =0 ; -return ; }/*