aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStuart Swales <stuart.swales.croftnuisk@gmail.com>2011-03-22 19:35:05 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-22 20:44:17 -0400
commit7a9730af9c596749425a98eba136152e5be4602a (patch)
tree1a7f03d7541768a9d2b4b25fab2ee07eec37c268
parent2f09719af705db56032ae480a2d9c32c2a3fcbd3 (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>
-rw-r--r--fs/adfs/inode.c40
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 */
179static void 180static void
180adfs_adfs2unix_time(struct timespec *tv, struct inode *inode) 181adfs_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/*