diff options
author | Stuart Swales <stuart.swales.croftnuisk@gmail.com> | 2011-03-22 19:35:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-22 20:44:17 -0400 |
commit | 7a9730af9c596749425a98eba136152e5be4602a (patch) | |
tree | 1a7f03d7541768a9d2b4b25fab2ee07eec37c268 /fs/adfs | |
parent | 2f09719af705db56032ae480a2d9c32c2a3fcbd3 (diff) |
adfs: improve timestamp precision
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.
Signed-off-by: Stuart Swales<stuart.swales.croftnuisk@gmail.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/adfs')
-rw-r--r-- | fs/adfs/inode.c | 40 |
1 files changed, 19 insertions, 21 deletions
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 09fe40198d1c..16d7ef2dffe1 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c | |||
@@ -174,50 +174,48 @@ adfs_mode2atts(struct super_block *sb, struct inode *inode) | |||
174 | 174 | ||
175 | /* | 175 | /* |
176 | * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time | 176 | * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time |
177 | * referenced to 1 Jan 1900 (til 2248) | 177 | * referenced to 1 Jan 1900 (til 2248) so we need to discard 2208988800 seconds |
178 | * of time to convert from RISC OS epoch to Unix epoch. | ||
178 | */ | 179 | */ |
179 | static void | 180 | static void |
180 | adfs_adfs2unix_time(struct timespec *tv, struct inode *inode) | 181 | adfs_adfs2unix_time(struct timespec *tv, struct inode *inode) |
181 | { | 182 | { |
182 | unsigned int high, low; | 183 | unsigned int high, low; |
184 | /* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since | ||
185 | * 01 Jan 1900 00:00:00 (RISC OS epoch) | ||
186 | */ | ||
187 | static const s64 nsec_unix_epoch_diff_risc_os_epoch = | ||
188 | 2208988800000000000LL; | ||
189 | s64 nsec; | ||
183 | 190 | ||
184 | if (ADFS_I(inode)->stamped == 0) | 191 | if (ADFS_I(inode)->stamped == 0) |
185 | goto cur_time; | 192 | goto cur_time; |
186 | 193 | ||
187 | high = ADFS_I(inode)->loadaddr << 24; | 194 | high = ADFS_I(inode)->loadaddr & 0xFF; /* top 8 bits of timestamp */ |
188 | low = ADFS_I(inode)->execaddr; | 195 | low = ADFS_I(inode)->execaddr; /* bottom 32 bits of timestamp */ |
189 | 196 | ||
190 | high |= low >> 8; | 197 | /* convert 40-bit centi-seconds to 32-bit seconds |
191 | low &= 255; | 198 | * going via nanoseconds to retain precision |
199 | */ | ||
200 | nsec = (((s64) high << 32) | (s64) low) * 10000000; /* cs to ns */ | ||
192 | 201 | ||
193 | /* Files dated pre 01 Jan 1970 00:00:00. */ | 202 | /* Files dated pre 01 Jan 1970 00:00:00. */ |
194 | if (high < 0x336e996a) | 203 | if (nsec < nsec_unix_epoch_diff_risc_os_epoch) |
195 | goto too_early; | 204 | goto too_early; |
196 | 205 | ||
197 | /* Files dated post 18 Jan 2038 03:14:05. */ | 206 | /* convert from RISC OS to Unix epoch */ |
198 | if (high >= 0x656e9969) | 207 | nsec -= nsec_unix_epoch_diff_risc_os_epoch; |
199 | goto too_late; | ||
200 | |||
201 | /* discard 2208988800 (0x336e996a00) seconds of time */ | ||
202 | high -= 0x336e996a; | ||
203 | 208 | ||
204 | /* convert 40-bit centi-seconds to 32-bit seconds */ | 209 | *tv = ns_to_timespec(nsec); |
205 | tv->tv_sec = (((high % 100) << 8) + low) / 100 + (high / 100 << 8); | ||
206 | tv->tv_nsec = 0; | ||
207 | return; | 210 | return; |
208 | 211 | ||
209 | cur_time: | 212 | cur_time: |
210 | *tv = CURRENT_TIME_SEC; | 213 | *tv = CURRENT_TIME; |
211 | return; | 214 | return; |
212 | 215 | ||
213 | too_early: | 216 | too_early: |
214 | tv->tv_sec = tv->tv_nsec = 0; | 217 | tv->tv_sec = tv->tv_nsec = 0; |
215 | return; | 218 | return; |
216 | |||
217 | too_late: | ||
218 | tv->tv_sec = 0x7ffffffd; | ||
219 | tv->tv_nsec = 0; | ||
220 | return; | ||
221 | } | 219 | } |
222 | 220 | ||
223 | /* | 221 | /* |