1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/fs/adfs/dir_f.c
5 * Copyright (C) 1997-1999 Russell King
7 * E and F format directory handling
12 static void adfs_f_free(struct adfs_dir
*dir
);
15 * Read an (unaligned) value of length 1..4 bytes
17 static inline unsigned int adfs_readval(unsigned char *p
, int len
)
22 case 4: val
|= p
[3] << 24;
24 case 3: val
|= p
[2] << 16;
26 case 2: val
|= p
[1] << 8;
33 static inline void adfs_writeval(unsigned char *p
, int len
, unsigned int val
)
36 case 4: p
[3] = val
>> 24;
38 case 3: p
[2] = val
>> 16;
40 case 2: p
[1] = val
>> 8;
46 #define ror13(v) ((v >> 13) | (v << 19))
49 ({ int _buf = idx >> blocksize_bits; \
50 int _off = idx - (_buf << blocksize_bits);\
51 *(u8 *)(bh[_buf]->b_data + _off); \
54 #define dir_u32(idx) \
55 ({ int _buf = idx >> blocksize_bits; \
56 int _off = idx - (_buf << blocksize_bits);\
57 *(__le32 *)(bh[_buf]->b_data + _off); \
60 #define bufoff(_bh,_idx) \
61 ({ int _buf = _idx >> blocksize_bits; \
62 int _off = _idx - (_buf << blocksize_bits);\
63 (u8 *)(_bh[_buf]->b_data + _off); \
67 * There are some algorithms that are nice in
68 * assembler, but a bitch in C... This is one
72 adfs_dir_checkbyte(const struct adfs_dir
*dir
)
74 struct buffer_head
* const *bh
= dir
->bh
;
75 const int blocksize_bits
= dir
->sb
->s_blocksize_bits
;
76 union { __le32
*ptr32
; u8
*ptr8
; } ptr
, end
;
82 * Accumulate each word up to the last whole
83 * word of the last directory entry. This
84 * can spread across several buffer heads.
89 dircheck
= le32_to_cpu(dir_u32(i
)) ^ ror13(dircheck
);
92 } while (i
< (last
& ~3));
93 } while (dir_u8(last
) != 0);
96 * Accumulate the last few bytes. These
97 * bytes will be within the same bh.
100 ptr
.ptr8
= bufoff(bh
, i
);
101 end
.ptr8
= ptr
.ptr8
+ last
- i
;
104 dircheck
= *ptr
.ptr8
++ ^ ror13(dircheck
);
105 } while (ptr
.ptr8
< end
.ptr8
);
109 * The directory tail is in the final bh
110 * Note that contary to the RISC OS PRMs,
111 * the first few bytes are NOT included
112 * in the check. All bytes are in the
115 ptr
.ptr8
= bufoff(bh
, 2008);
116 end
.ptr8
= ptr
.ptr8
+ 36;
119 __le32 v
= *ptr
.ptr32
++;
120 dircheck
= le32_to_cpu(v
) ^ ror13(dircheck
);
121 } while (ptr
.ptr32
< end
.ptr32
);
123 return (dircheck
^ (dircheck
>> 8) ^ (dircheck
>> 16) ^ (dircheck
>> 24)) & 0xff;
126 /* Read and check that a directory is valid */
127 static int adfs_dir_read(struct super_block
*sb
, u32 indaddr
,
128 unsigned int size
, struct adfs_dir
*dir
)
130 const unsigned int blocksize_bits
= sb
->s_blocksize_bits
;
134 * Directories which are not a multiple of 2048 bytes
135 * are considered bad v2 [3.6]
140 size
>>= blocksize_bits
;
145 for (blk
= 0; blk
< size
; blk
++) {
148 phys
= __adfs_block_map(sb
, indaddr
, blk
);
150 adfs_error(sb
, "dir %06x has a hole at offset %d",
152 goto release_buffers
;
155 dir
->bh
[blk
] = sb_bread(sb
, phys
);
157 goto release_buffers
;
160 memcpy(&dir
->dirhead
, bufoff(dir
->bh
, 0), sizeof(dir
->dirhead
));
161 memcpy(&dir
->dirtail
, bufoff(dir
->bh
, 2007), sizeof(dir
->dirtail
));
163 if (dir
->dirhead
.startmasseq
!= dir
->dirtail
.new.endmasseq
||
164 memcmp(&dir
->dirhead
.startname
, &dir
->dirtail
.new.endname
, 4))
167 if (memcmp(&dir
->dirhead
.startname
, "Nick", 4) &&
168 memcmp(&dir
->dirhead
.startname
, "Hugo", 4))
171 if (adfs_dir_checkbyte(dir
) != dir
->dirtail
.new.dircheckbyte
)
174 dir
->nr_buffers
= blk
;
179 adfs_error(sb
, "dir %06x is corrupted", indaddr
);
181 for (blk
-= 1; blk
>= 0; blk
-= 1)
182 brelse(dir
->bh
[blk
]);
190 * convert a disk-based directory entry to a Linux ADFS directory entry
193 adfs_dir2obj(struct adfs_dir
*dir
, struct object_info
*obj
,
194 struct adfs_direntry
*de
)
196 unsigned int name_len
;
198 for (name_len
= 0; name_len
< ADFS_F_NAME_LEN
; name_len
++) {
199 if (de
->dirobname
[name_len
] < ' ')
202 obj
->name
[name_len
] = de
->dirobname
[name_len
];
205 obj
->name_len
= name_len
;
206 obj
->indaddr
= adfs_readval(de
->dirinddiscadd
, 3);
207 obj
->loadaddr
= adfs_readval(de
->dirload
, 4);
208 obj
->execaddr
= adfs_readval(de
->direxec
, 4);
209 obj
->size
= adfs_readval(de
->dirlen
, 4);
210 obj
->attr
= de
->newdiratts
;
212 adfs_object_fixup(dir
, obj
);
216 * convert a Linux ADFS directory entry to a disk-based directory entry
219 adfs_obj2dir(struct adfs_direntry
*de
, struct object_info
*obj
)
221 adfs_writeval(de
->dirinddiscadd
, 3, obj
->indaddr
);
222 adfs_writeval(de
->dirload
, 4, obj
->loadaddr
);
223 adfs_writeval(de
->direxec
, 4, obj
->execaddr
);
224 adfs_writeval(de
->dirlen
, 4, obj
->size
);
225 de
->newdiratts
= obj
->attr
;
229 * get a directory entry. Note that the caller is responsible
230 * for holding the relevant locks.
233 __adfs_dir_get(struct adfs_dir
*dir
, int pos
, struct object_info
*obj
)
235 struct super_block
*sb
= dir
->sb
;
236 struct adfs_direntry de
;
237 int thissize
, buffer
, offset
;
239 buffer
= pos
>> sb
->s_blocksize_bits
;
241 if (buffer
> dir
->nr_buffers
)
244 offset
= pos
& (sb
->s_blocksize
- 1);
245 thissize
= sb
->s_blocksize
- offset
;
249 memcpy(&de
, dir
->bh
[buffer
]->b_data
+ offset
, thissize
);
251 memcpy(((char *)&de
) + thissize
, dir
->bh
[buffer
+ 1]->b_data
,
254 if (!de
.dirobname
[0])
257 adfs_dir2obj(dir
, obj
, &de
);
263 __adfs_dir_put(struct adfs_dir
*dir
, int pos
, struct object_info
*obj
)
265 struct super_block
*sb
= dir
->sb
;
266 struct adfs_direntry de
;
267 int thissize
, buffer
, offset
;
269 buffer
= pos
>> sb
->s_blocksize_bits
;
271 if (buffer
> dir
->nr_buffers
)
274 offset
= pos
& (sb
->s_blocksize
- 1);
275 thissize
= sb
->s_blocksize
- offset
;
280 * Get the entry in total
282 memcpy(&de
, dir
->bh
[buffer
]->b_data
+ offset
, thissize
);
284 memcpy(((char *)&de
) + thissize
, dir
->bh
[buffer
+ 1]->b_data
,
290 adfs_obj2dir(&de
, obj
);
293 * Put the new entry back
295 memcpy(dir
->bh
[buffer
]->b_data
+ offset
, &de
, thissize
);
297 memcpy(dir
->bh
[buffer
+ 1]->b_data
, ((char *)&de
) + thissize
,
304 * the caller is responsible for holding the necessary
307 static int adfs_dir_find_entry(struct adfs_dir
*dir
, u32 indaddr
)
313 for (pos
= 5; pos
< ADFS_NUM_DIR_ENTRIES
* 26 + 5; pos
+= 26) {
314 struct object_info obj
;
316 if (!__adfs_dir_get(dir
, pos
, &obj
))
319 if (obj
.indaddr
== indaddr
) {
328 static int adfs_f_read(struct super_block
*sb
, u32 indaddr
, unsigned int size
,
329 struct adfs_dir
*dir
)
333 if (size
!= ADFS_NEWDIR_SIZE
)
336 ret
= adfs_dir_read(sb
, indaddr
, size
, dir
);
338 adfs_error(sb
, "unable to read directory");
340 dir
->parent_id
= adfs_readval(dir
->dirtail
.new.dirparent
, 3);
346 adfs_f_setpos(struct adfs_dir
*dir
, unsigned int fpos
)
348 if (fpos
>= ADFS_NUM_DIR_ENTRIES
)
351 dir
->pos
= 5 + fpos
* 26;
356 adfs_f_getnext(struct adfs_dir
*dir
, struct object_info
*obj
)
360 ret
= __adfs_dir_get(dir
, dir
->pos
, obj
);
368 adfs_f_update(struct adfs_dir
*dir
, struct object_info
*obj
)
370 struct super_block
*sb
= dir
->sb
;
373 ret
= adfs_dir_find_entry(dir
, obj
->indaddr
);
375 adfs_error(dir
->sb
, "unable to locate entry to update");
379 __adfs_dir_put(dir
, ret
, obj
);
382 * Increment directory sequence number
384 dir
->bh
[0]->b_data
[0] += 1;
385 dir
->bh
[dir
->nr_buffers
- 1]->b_data
[sb
->s_blocksize
- 6] += 1;
387 ret
= adfs_dir_checkbyte(dir
);
389 * Update directory check byte
391 dir
->bh
[dir
->nr_buffers
- 1]->b_data
[sb
->s_blocksize
- 1] = ret
;
395 const unsigned int blocksize_bits
= sb
->s_blocksize_bits
;
397 memcpy(&dir
->dirhead
, bufoff(dir
->bh
, 0), sizeof(dir
->dirhead
));
398 memcpy(&dir
->dirtail
, bufoff(dir
->bh
, 2007), sizeof(dir
->dirtail
));
400 if (dir
->dirhead
.startmasseq
!= dir
->dirtail
.new.endmasseq
||
401 memcmp(&dir
->dirhead
.startname
, &dir
->dirtail
.new.endname
, 4))
404 if (memcmp(&dir
->dirhead
.startname
, "Nick", 4) &&
405 memcmp(&dir
->dirhead
.startname
, "Hugo", 4))
408 if (adfs_dir_checkbyte(dir
) != dir
->dirtail
.new.dircheckbyte
)
412 for (i
= dir
->nr_buffers
- 1; i
>= 0; i
--)
413 mark_buffer_dirty(dir
->bh
[i
]);
420 adfs_error(dir
->sb
, "whoops! I broke a directory!");
426 adfs_f_sync(struct adfs_dir
*dir
)
431 for (i
= dir
->nr_buffers
- 1; i
>= 0; i
--) {
432 struct buffer_head
*bh
= dir
->bh
[i
];
433 sync_dirty_buffer(bh
);
434 if (buffer_req(bh
) && !buffer_uptodate(bh
))
442 adfs_f_free(struct adfs_dir
*dir
)
446 for (i
= dir
->nr_buffers
- 1; i
>= 0; i
--) {
455 const struct adfs_dir_ops adfs_f_dir_ops
= {
457 .setpos
= adfs_f_setpos
,
458 .getnext
= adfs_f_getnext
,
459 .update
= adfs_f_update
,