aboutsummaryrefslogtreecommitdiffstats
path: root/fs/adfs/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/adfs/inode.c')
-rw-r--r--fs/adfs/inode.c63
1 files changed, 23 insertions, 40 deletions
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
index 09fe40198d1c..d5250c5aae21 100644
--- a/fs/adfs/inode.c
+++ b/fs/adfs/inode.c
@@ -72,32 +72,18 @@ static sector_t _adfs_bmap(struct address_space *mapping, sector_t block)
72static const struct address_space_operations adfs_aops = { 72static const struct address_space_operations adfs_aops = {
73 .readpage = adfs_readpage, 73 .readpage = adfs_readpage,
74 .writepage = adfs_writepage, 74 .writepage = adfs_writepage,
75 .sync_page = block_sync_page,
76 .write_begin = adfs_write_begin, 75 .write_begin = adfs_write_begin,
77 .write_end = generic_write_end, 76 .write_end = generic_write_end,
78 .bmap = _adfs_bmap 77 .bmap = _adfs_bmap
79}; 78};
80 79
81static inline unsigned int
82adfs_filetype(struct inode *inode)
83{
84 unsigned int type;
85
86 if (ADFS_I(inode)->stamped)
87 type = (ADFS_I(inode)->loadaddr >> 8) & 0xfff;
88 else
89 type = (unsigned int) -1;
90
91 return type;
92}
93
94/* 80/*
95 * Convert ADFS attributes and filetype to Linux permission. 81 * Convert ADFS attributes and filetype to Linux permission.
96 */ 82 */
97static umode_t 83static umode_t
98adfs_atts2mode(struct super_block *sb, struct inode *inode) 84adfs_atts2mode(struct super_block *sb, struct inode *inode)
99{ 85{
100 unsigned int filetype, attr = ADFS_I(inode)->attr; 86 unsigned int attr = ADFS_I(inode)->attr;
101 umode_t mode, rmask; 87 umode_t mode, rmask;
102 struct adfs_sb_info *asb = ADFS_SB(sb); 88 struct adfs_sb_info *asb = ADFS_SB(sb);
103 89
@@ -106,9 +92,7 @@ adfs_atts2mode(struct super_block *sb, struct inode *inode)
106 return S_IFDIR | S_IXUGO | mode; 92 return S_IFDIR | S_IXUGO | mode;
107 } 93 }
108 94
109 filetype = adfs_filetype(inode); 95 switch (ADFS_I(inode)->filetype) {
110
111 switch (filetype) {
112 case 0xfc0: /* LinkFS */ 96 case 0xfc0: /* LinkFS */
113 return S_IFLNK|S_IRWXUGO; 97 return S_IFLNK|S_IRWXUGO;
114 98
@@ -174,50 +158,48 @@ adfs_mode2atts(struct super_block *sb, struct inode *inode)
174 158
175/* 159/*
176 * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time 160 * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time
177 * referenced to 1 Jan 1900 (til 2248) 161 * referenced to 1 Jan 1900 (til 2248) so we need to discard 2208988800 seconds
162 * of time to convert from RISC OS epoch to Unix epoch.
178 */ 163 */
179static void 164static void
180adfs_adfs2unix_time(struct timespec *tv, struct inode *inode) 165adfs_adfs2unix_time(struct timespec *tv, struct inode *inode)
181{ 166{
182 unsigned int high, low; 167 unsigned int high, low;
168 /* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since
169 * 01 Jan 1900 00:00:00 (RISC OS epoch)
170 */
171 static const s64 nsec_unix_epoch_diff_risc_os_epoch =
172 2208988800000000000LL;
173 s64 nsec;
183 174
184 if (ADFS_I(inode)->stamped == 0) 175 if (ADFS_I(inode)->stamped == 0)
185 goto cur_time; 176 goto cur_time;
186 177
187 high = ADFS_I(inode)->loadaddr << 24; 178 high = ADFS_I(inode)->loadaddr & 0xFF; /* top 8 bits of timestamp */
188 low = ADFS_I(inode)->execaddr; 179 low = ADFS_I(inode)->execaddr; /* bottom 32 bits of timestamp */
189 180
190 high |= low >> 8; 181 /* convert 40-bit centi-seconds to 32-bit seconds
191 low &= 255; 182 * going via nanoseconds to retain precision
183 */
184 nsec = (((s64) high << 32) | (s64) low) * 10000000; /* cs to ns */
192 185
193 /* Files dated pre 01 Jan 1970 00:00:00. */ 186 /* Files dated pre 01 Jan 1970 00:00:00. */
194 if (high < 0x336e996a) 187 if (nsec < nsec_unix_epoch_diff_risc_os_epoch)
195 goto too_early; 188 goto too_early;
196 189
197 /* Files dated post 18 Jan 2038 03:14:05. */ 190 /* convert from RISC OS to Unix epoch */
198 if (high >= 0x656e9969) 191 nsec -= nsec_unix_epoch_diff_risc_os_epoch;
199 goto too_late;
200 192
201 /* discard 2208988800 (0x336e996a00) seconds of time */ 193 *tv = ns_to_timespec(nsec);
202 high -= 0x336e996a;
203
204 /* convert 40-bit centi-seconds to 32-bit seconds */
205 tv->tv_sec = (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
206 tv->tv_nsec = 0;
207 return; 194 return;
208 195
209 cur_time: 196 cur_time:
210 *tv = CURRENT_TIME_SEC; 197 *tv = CURRENT_TIME;
211 return; 198 return;
212 199
213 too_early: 200 too_early:
214 tv->tv_sec = tv->tv_nsec = 0; 201 tv->tv_sec = tv->tv_nsec = 0;
215 return; 202 return;
216
217 too_late:
218 tv->tv_sec = 0x7ffffffd;
219 tv->tv_nsec = 0;
220 return;
221} 203}
222 204
223/* 205/*
@@ -279,7 +261,8 @@ adfs_iget(struct super_block *sb, struct object_info *obj)
279 ADFS_I(inode)->loadaddr = obj->loadaddr; 261 ADFS_I(inode)->loadaddr = obj->loadaddr;
280 ADFS_I(inode)->execaddr = obj->execaddr; 262 ADFS_I(inode)->execaddr = obj->execaddr;
281 ADFS_I(inode)->attr = obj->attr; 263 ADFS_I(inode)->attr = obj->attr;
282 ADFS_I(inode)->stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000); 264 ADFS_I(inode)->filetype = obj->filetype;
265 ADFS_I(inode)->stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000);
283 266
284 inode->i_mode = adfs_atts2mode(sb, inode); 267 inode->i_mode = adfs_atts2mode(sb, inode);
285 adfs_adfs2unix_time(&inode->i_mtime, inode); 268 adfs_adfs2unix_time(&inode->i_mtime, inode);