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
13 * Read an (unaligned) value of length 1..4 bytes
15 static inline unsigned int adfs_readval(unsigned char *p
, int len
)
20 case 4: val
|= p
[3] << 24;
22 case 3: val
|= p
[2] << 16;
24 case 2: val
|= p
[1] << 8;
31 static inline void adfs_writeval(unsigned char *p
, int len
, unsigned int val
)
34 case 4: p
[3] = val
>> 24;
36 case 3: p
[2] = val
>> 16;
38 case 2: p
[1] = val
>> 8;
44 #define ror13(v) ((v >> 13) | (v << 19))
47 ({ int _buf = idx >> blocksize_bits; \
48 int _off = idx - (_buf << blocksize_bits);\
49 *(u8 *)(bh[_buf]->b_data + _off); \
52 #define dir_u32(idx) \
53 ({ int _buf = idx >> blocksize_bits; \
54 int _off = idx - (_buf << blocksize_bits);\
55 *(__le32 *)(bh[_buf]->b_data + _off); \
58 #define bufoff(_bh,_idx) \
59 ({ int _buf = _idx >> blocksize_bits; \
60 int _off = _idx - (_buf << blocksize_bits);\
61 (void *)(_bh[_buf]->b_data + _off); \
65 * There are some algorithms that are nice in
66 * assembler, but a bitch in C... This is one
70 adfs_dir_checkbyte(const struct adfs_dir
*dir
)
72 struct buffer_head
* const *bh
= dir
->bh
;
73 const int blocksize_bits
= dir
->sb
->s_blocksize_bits
;
74 union { __le32
*ptr32
; u8
*ptr8
; } ptr
, end
;
80 * Accumulate each word up to the last whole
81 * word of the last directory entry. This
82 * can spread across several buffer heads.
87 dircheck
= le32_to_cpu(dir_u32(i
)) ^ ror13(dircheck
);
90 } while (i
< (last
& ~3));
91 } while (dir_u8(last
) != 0);
94 * Accumulate the last few bytes. These
95 * bytes will be within the same bh.
98 ptr
.ptr8
= bufoff(bh
, i
);
99 end
.ptr8
= ptr
.ptr8
+ last
- i
;
102 dircheck
= *ptr
.ptr8
++ ^ ror13(dircheck
);
103 } while (ptr
.ptr8
< end
.ptr8
);
107 * The directory tail is in the final bh
108 * Note that contary to the RISC OS PRMs,
109 * the first few bytes are NOT included
110 * in the check. All bytes are in the
113 ptr
.ptr8
= bufoff(bh
, 2008);
114 end
.ptr8
= ptr
.ptr8
+ 36;
117 __le32 v
= *ptr
.ptr32
++;
118 dircheck
= le32_to_cpu(v
) ^ ror13(dircheck
);
119 } while (ptr
.ptr32
< end
.ptr32
);
121 return (dircheck
^ (dircheck
>> 8) ^ (dircheck
>> 16) ^ (dircheck
>> 24)) & 0xff;
124 static int adfs_f_validate(struct adfs_dir
*dir
)
126 struct adfs_dirheader
*head
= dir
->dirhead
;
127 struct adfs_newdirtail
*tail
= dir
->newtail
;
129 if (head
->startmasseq
!= tail
->endmasseq
||
130 tail
->dirlastmask
|| tail
->reserved
[0] || tail
->reserved
[1] ||
131 (memcmp(&head
->startname
, "Nick", 4) &&
132 memcmp(&head
->startname
, "Hugo", 4)) ||
133 memcmp(&head
->startname
, &tail
->endname
, 4) ||
134 adfs_dir_checkbyte(dir
) != tail
->dircheckbyte
)
140 /* Read and check that a directory is valid */
141 static int adfs_f_read(struct super_block
*sb
, u32 indaddr
, unsigned int size
,
142 struct adfs_dir
*dir
)
144 const unsigned int blocksize_bits
= sb
->s_blocksize_bits
;
147 if (size
&& size
!= ADFS_NEWDIR_SIZE
)
150 ret
= adfs_dir_read_buffers(sb
, indaddr
, ADFS_NEWDIR_SIZE
, dir
);
154 dir
->dirhead
= bufoff(dir
->bh
, 0);
155 dir
->newtail
= bufoff(dir
->bh
, 2007);
157 if (adfs_f_validate(dir
))
160 dir
->parent_id
= adfs_readval(dir
->newtail
->dirparent
, 3);
165 adfs_error(sb
, "dir %06x is corrupted", indaddr
);
172 * convert a disk-based directory entry to a Linux ADFS directory entry
175 adfs_dir2obj(struct adfs_dir
*dir
, struct object_info
*obj
,
176 struct adfs_direntry
*de
)
178 unsigned int name_len
;
180 for (name_len
= 0; name_len
< ADFS_F_NAME_LEN
; name_len
++) {
181 if (de
->dirobname
[name_len
] < ' ')
184 obj
->name
[name_len
] = de
->dirobname
[name_len
];
187 obj
->name_len
= name_len
;
188 obj
->indaddr
= adfs_readval(de
->dirinddiscadd
, 3);
189 obj
->loadaddr
= adfs_readval(de
->dirload
, 4);
190 obj
->execaddr
= adfs_readval(de
->direxec
, 4);
191 obj
->size
= adfs_readval(de
->dirlen
, 4);
192 obj
->attr
= de
->newdiratts
;
194 adfs_object_fixup(dir
, obj
);
198 * convert a Linux ADFS directory entry to a disk-based directory entry
201 adfs_obj2dir(struct adfs_direntry
*de
, struct object_info
*obj
)
203 adfs_writeval(de
->dirinddiscadd
, 3, obj
->indaddr
);
204 adfs_writeval(de
->dirload
, 4, obj
->loadaddr
);
205 adfs_writeval(de
->direxec
, 4, obj
->execaddr
);
206 adfs_writeval(de
->dirlen
, 4, obj
->size
);
207 de
->newdiratts
= obj
->attr
;
211 * get a directory entry. Note that the caller is responsible
212 * for holding the relevant locks.
215 __adfs_dir_get(struct adfs_dir
*dir
, int pos
, struct object_info
*obj
)
217 struct adfs_direntry de
;
220 ret
= adfs_dir_copyfrom(&de
, dir
, pos
, 26);
224 if (!de
.dirobname
[0])
227 adfs_dir2obj(dir
, obj
, &de
);
233 adfs_f_setpos(struct adfs_dir
*dir
, unsigned int fpos
)
235 if (fpos
>= ADFS_NUM_DIR_ENTRIES
)
238 dir
->pos
= 5 + fpos
* 26;
243 adfs_f_getnext(struct adfs_dir
*dir
, struct object_info
*obj
)
247 ret
= __adfs_dir_get(dir
, dir
->pos
, obj
);
254 static int adfs_f_iterate(struct adfs_dir
*dir
, struct dir_context
*ctx
)
256 struct object_info obj
;
257 int pos
= 5 + (ctx
->pos
- 2) * 26;
259 while (ctx
->pos
< 2 + ADFS_NUM_DIR_ENTRIES
) {
260 if (__adfs_dir_get(dir
, pos
, &obj
))
262 if (!dir_emit(ctx
, obj
.name
, obj
.name_len
,
263 obj
.indaddr
, DT_UNKNOWN
))
271 static int adfs_f_update(struct adfs_dir
*dir
, struct object_info
*obj
)
273 struct adfs_direntry de
;
276 offset
= 5 - (int)sizeof(de
);
279 offset
+= sizeof(de
);
280 ret
= adfs_dir_copyfrom(&de
, dir
, offset
, sizeof(de
));
282 adfs_error(dir
->sb
, "error reading directory entry");
285 if (!de
.dirobname
[0]) {
286 adfs_error(dir
->sb
, "unable to locate entry to update");
289 } while (adfs_readval(de
.dirinddiscadd
, 3) != obj
->indaddr
);
291 /* Update the directory entry with the new object state */
292 adfs_obj2dir(&de
, obj
);
294 /* Write the directory entry back to the directory */
295 return adfs_dir_copyto(dir
, offset
, &de
, 26);
298 static int adfs_f_commit(struct adfs_dir
*dir
)
302 /* Increment directory sequence number */
303 dir
->dirhead
->startmasseq
+= 1;
304 dir
->newtail
->endmasseq
+= 1;
306 /* Update directory check byte */
307 dir
->newtail
->dircheckbyte
= adfs_dir_checkbyte(dir
);
309 /* Make sure the directory still validates correctly */
310 ret
= adfs_f_validate(dir
);
312 adfs_msg(dir
->sb
, KERN_ERR
, "error: update broke directory");
317 const struct adfs_dir_ops adfs_f_dir_ops
= {
319 .iterate
= adfs_f_iterate
,
320 .setpos
= adfs_f_setpos
,
321 .getnext
= adfs_f_getnext
,
322 .update
= adfs_f_update
,
323 .commit
= adfs_f_commit
,