2 * linux/fs/adfs/dir_f.c
4 * Copyright (C) 1997-1999 Russell King
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * E and F format directory handling
12 #include <linux/errno.h>
14 #include <linux/adfs_fs.h>
15 #include <linux/time.h>
16 #include <linux/stat.h>
17 #include <linux/spinlock.h>
18 #include <linux/buffer_head.h>
19 #include <linux/string.h>
24 static void adfs_f_free(struct adfs_dir
*dir
);
27 * Read an (unaligned) value of length 1..4 bytes
29 static inline unsigned int adfs_readval(unsigned char *p
, int len
)
34 case 4: val
|= p
[3] << 24;
35 case 3: val
|= p
[2] << 16;
36 case 2: val
|= p
[1] << 8;
42 static inline void adfs_writeval(unsigned char *p
, int len
, unsigned int val
)
45 case 4: p
[3] = val
>> 24;
46 case 3: p
[2] = val
>> 16;
47 case 2: p
[1] = val
>> 8;
52 static inline int adfs_readname(char *buf
, char *ptr
, int maxlen
)
56 while (*ptr
>= ' ' && maxlen
--) {
68 static inline void adfs_writename(char *to
, char *from
, int maxlen
)
72 for (i
= 0; i
< maxlen
; i
++) {
81 for (; i
< maxlen
; i
++)
85 #define ror13(v) ((v >> 13) | (v << 19))
88 ({ int _buf = idx >> blocksize_bits; \
89 int _off = idx - (_buf << blocksize_bits);\
90 *(u8 *)(bh[_buf]->b_data + _off); \
93 #define dir_u32(idx) \
94 ({ int _buf = idx >> blocksize_bits; \
95 int _off = idx - (_buf << blocksize_bits);\
96 *(__le32 *)(bh[_buf]->b_data + _off); \
99 #define bufoff(_bh,_idx) \
100 ({ int _buf = _idx >> blocksize_bits; \
101 int _off = _idx - (_buf << blocksize_bits);\
102 (u8 *)(_bh[_buf]->b_data + _off); \
106 * There are some algorithms that are nice in
107 * assembler, but a bitch in C... This is one
111 adfs_dir_checkbyte(const struct adfs_dir
*dir
)
113 struct buffer_head
* const *bh
= dir
->bh
;
114 const int blocksize_bits
= dir
->sb
->s_blocksize_bits
;
115 union { __le32
*ptr32
; u8
*ptr8
; } ptr
, end
;
121 * Accumulate each word up to the last whole
122 * word of the last directory entry. This
123 * can spread across several buffer heads.
128 dircheck
= le32_to_cpu(dir_u32(i
)) ^ ror13(dircheck
);
131 } while (i
< (last
& ~3));
132 } while (dir_u8(last
) != 0);
135 * Accumulate the last few bytes. These
136 * bytes will be within the same bh.
139 ptr
.ptr8
= bufoff(bh
, i
);
140 end
.ptr8
= ptr
.ptr8
+ last
- i
;
143 dircheck
= *ptr
.ptr8
++ ^ ror13(dircheck
);
144 while (ptr
.ptr8
< end
.ptr8
);
148 * The directory tail is in the final bh
149 * Note that contary to the RISC OS PRMs,
150 * the first few bytes are NOT included
151 * in the check. All bytes are in the
154 ptr
.ptr8
= bufoff(bh
, 2008);
155 end
.ptr8
= ptr
.ptr8
+ 36;
158 __le32 v
= *ptr
.ptr32
++;
159 dircheck
= le32_to_cpu(v
) ^ ror13(dircheck
);
160 } while (ptr
.ptr32
< end
.ptr32
);
162 return (dircheck
^ (dircheck
>> 8) ^ (dircheck
>> 16) ^ (dircheck
>> 24)) & 0xff;
166 * Read and check that a directory is valid
169 adfs_dir_read(struct super_block
*sb
, unsigned long object_id
,
170 unsigned int size
, struct adfs_dir
*dir
)
172 const unsigned int blocksize_bits
= sb
->s_blocksize_bits
;
176 * Directories which are not a multiple of 2048 bytes
177 * are considered bad v2 [3.6]
182 size
>>= blocksize_bits
;
187 for (blk
= 0; blk
< size
; blk
++) {
190 phys
= __adfs_block_map(sb
, object_id
, blk
);
192 adfs_error(sb
, "dir object %lX has a hole at offset %d",
194 goto release_buffers
;
197 dir
->bh
[blk
] = sb_bread(sb
, phys
);
199 goto release_buffers
;
202 memcpy(&dir
->dirhead
, bufoff(dir
->bh
, 0), sizeof(dir
->dirhead
));
203 memcpy(&dir
->dirtail
, bufoff(dir
->bh
, 2007), sizeof(dir
->dirtail
));
205 if (dir
->dirhead
.startmasseq
!= dir
->dirtail
.new.endmasseq
||
206 memcmp(&dir
->dirhead
.startname
, &dir
->dirtail
.new.endname
, 4))
209 if (memcmp(&dir
->dirhead
.startname
, "Nick", 4) &&
210 memcmp(&dir
->dirhead
.startname
, "Hugo", 4))
213 if (adfs_dir_checkbyte(dir
) != dir
->dirtail
.new.dircheckbyte
)
216 dir
->nr_buffers
= blk
;
221 adfs_error(sb
, "corrupted directory fragment %lX",
224 for (blk
-= 1; blk
>= 0; blk
-= 1)
225 brelse(dir
->bh
[blk
]);
233 * convert a disk-based directory entry to a Linux ADFS directory entry
236 adfs_dir2obj(struct object_info
*obj
, struct adfs_direntry
*de
)
238 obj
->name_len
= adfs_readname(obj
->name
, de
->dirobname
, ADFS_F_NAME_LEN
);
239 obj
->file_id
= adfs_readval(de
->dirinddiscadd
, 3);
240 obj
->loadaddr
= adfs_readval(de
->dirload
, 4);
241 obj
->execaddr
= adfs_readval(de
->direxec
, 4);
242 obj
->size
= adfs_readval(de
->dirlen
, 4);
243 obj
->attr
= de
->newdiratts
;
247 * convert a Linux ADFS directory entry to a disk-based directory entry
250 adfs_obj2dir(struct adfs_direntry
*de
, struct object_info
*obj
)
252 adfs_writeval(de
->dirinddiscadd
, 3, obj
->file_id
);
253 adfs_writeval(de
->dirload
, 4, obj
->loadaddr
);
254 adfs_writeval(de
->direxec
, 4, obj
->execaddr
);
255 adfs_writeval(de
->dirlen
, 4, obj
->size
);
256 de
->newdiratts
= obj
->attr
;
260 * get a directory entry. Note that the caller is responsible
261 * for holding the relevant locks.
264 __adfs_dir_get(struct adfs_dir
*dir
, int pos
, struct object_info
*obj
)
266 struct super_block
*sb
= dir
->sb
;
267 struct adfs_direntry de
;
268 int thissize
, buffer
, offset
;
270 buffer
= pos
>> sb
->s_blocksize_bits
;
272 if (buffer
> dir
->nr_buffers
)
275 offset
= pos
& (sb
->s_blocksize
- 1);
276 thissize
= sb
->s_blocksize
- offset
;
280 memcpy(&de
, dir
->bh
[buffer
]->b_data
+ offset
, thissize
);
282 memcpy(((char *)&de
) + thissize
, dir
->bh
[buffer
+ 1]->b_data
,
285 if (!de
.dirobname
[0])
288 adfs_dir2obj(obj
, &de
);
294 __adfs_dir_put(struct adfs_dir
*dir
, int pos
, struct object_info
*obj
)
296 struct super_block
*sb
= dir
->sb
;
297 struct adfs_direntry de
;
298 int thissize
, buffer
, offset
;
300 buffer
= pos
>> sb
->s_blocksize_bits
;
302 if (buffer
> dir
->nr_buffers
)
305 offset
= pos
& (sb
->s_blocksize
- 1);
306 thissize
= sb
->s_blocksize
- offset
;
311 * Get the entry in total
313 memcpy(&de
, dir
->bh
[buffer
]->b_data
+ offset
, thissize
);
315 memcpy(((char *)&de
) + thissize
, dir
->bh
[buffer
+ 1]->b_data
,
321 adfs_obj2dir(&de
, obj
);
324 * Put the new entry back
326 memcpy(dir
->bh
[buffer
]->b_data
+ offset
, &de
, thissize
);
328 memcpy(dir
->bh
[buffer
+ 1]->b_data
, ((char *)&de
) + thissize
,
335 * the caller is responsible for holding the necessary
339 adfs_dir_find_entry(struct adfs_dir
*dir
, unsigned long object_id
)
345 for (pos
= 5; pos
< ADFS_NUM_DIR_ENTRIES
* 26 + 5; pos
+= 26) {
346 struct object_info obj
;
348 if (!__adfs_dir_get(dir
, pos
, &obj
))
351 if (obj
.file_id
== object_id
) {
361 adfs_f_read(struct super_block
*sb
, unsigned int id
, unsigned int sz
, struct adfs_dir
*dir
)
365 if (sz
!= ADFS_NEWDIR_SIZE
)
368 ret
= adfs_dir_read(sb
, id
, sz
, dir
);
370 adfs_error(sb
, "unable to read directory");
372 dir
->parent_id
= adfs_readval(dir
->dirtail
.new.dirparent
, 3);
378 adfs_f_setpos(struct adfs_dir
*dir
, unsigned int fpos
)
380 if (fpos
>= ADFS_NUM_DIR_ENTRIES
)
383 dir
->pos
= 5 + fpos
* 26;
388 adfs_f_getnext(struct adfs_dir
*dir
, struct object_info
*obj
)
392 ret
= __adfs_dir_get(dir
, dir
->pos
, obj
);
400 adfs_f_update(struct adfs_dir
*dir
, struct object_info
*obj
)
402 struct super_block
*sb
= dir
->sb
;
405 ret
= adfs_dir_find_entry(dir
, obj
->file_id
);
407 adfs_error(dir
->sb
, "unable to locate entry to update");
411 __adfs_dir_put(dir
, ret
, obj
);
414 * Increment directory sequence number
416 dir
->bh
[0]->b_data
[0] += 1;
417 dir
->bh
[dir
->nr_buffers
- 1]->b_data
[sb
->s_blocksize
- 6] += 1;
419 ret
= adfs_dir_checkbyte(dir
);
421 * Update directory check byte
423 dir
->bh
[dir
->nr_buffers
- 1]->b_data
[sb
->s_blocksize
- 1] = ret
;
427 const unsigned int blocksize_bits
= sb
->s_blocksize_bits
;
429 memcpy(&dir
->dirhead
, bufoff(dir
->bh
, 0), sizeof(dir
->dirhead
));
430 memcpy(&dir
->dirtail
, bufoff(dir
->bh
, 2007), sizeof(dir
->dirtail
));
432 if (dir
->dirhead
.startmasseq
!= dir
->dirtail
.new.endmasseq
||
433 memcmp(&dir
->dirhead
.startname
, &dir
->dirtail
.new.endname
, 4))
436 if (memcmp(&dir
->dirhead
.startname
, "Nick", 4) &&
437 memcmp(&dir
->dirhead
.startname
, "Hugo", 4))
440 if (adfs_dir_checkbyte(dir
) != dir
->dirtail
.new.dircheckbyte
)
444 for (i
= dir
->nr_buffers
- 1; i
>= 0; i
--)
445 mark_buffer_dirty(dir
->bh
[i
]);
452 adfs_error(dir
->sb
, "whoops! I broke a directory!");
458 adfs_f_free(struct adfs_dir
*dir
)
462 for (i
= dir
->nr_buffers
- 1; i
>= 0; i
--) {
471 struct adfs_dir_ops adfs_f_dir_ops
= {
473 .setpos
= adfs_f_setpos
,
474 .getnext
= adfs_f_getnext
,
475 .update
= adfs_f_update
,