1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/fs/adfs/dir_fplus.c
5 * Copyright (C) 1997-1999 Russell King
10 /* Return the byte offset to directory entry pos */
11 static unsigned int adfs_fplus_offset(const struct adfs_bigdirheader
*h
,
14 return offsetof(struct adfs_bigdirheader
, bigdirname
) +
15 ALIGN(le32_to_cpu(h
->bigdirnamelen
), 4) +
16 pos
* sizeof(struct adfs_bigdirentry
);
19 static int adfs_fplus_validate_header(const struct adfs_bigdirheader
*h
)
21 unsigned int size
= le32_to_cpu(h
->bigdirsize
);
24 if (h
->bigdirversion
[0] != 0 || h
->bigdirversion
[1] != 0 ||
25 h
->bigdirversion
[2] != 0 ||
26 h
->bigdirstartname
!= cpu_to_le32(BIGDIRSTARTNAME
) ||
27 !size
|| size
& 2047 || size
> SZ_4M
)
30 size
-= sizeof(struct adfs_bigdirtail
) +
31 offsetof(struct adfs_bigdirheader
, bigdirname
);
33 /* Check that bigdirnamelen fits within the directory */
34 len
= ALIGN(le32_to_cpu(h
->bigdirnamelen
), 4);
40 /* Check that bigdirnamesize fits within the directory */
41 len
= le32_to_cpu(h
->bigdirnamesize
);
48 * Avoid division, we know that absolute maximum number of entries
49 * can not be so large to cause overflow of the multiplication below.
51 len
= le32_to_cpu(h
->bigdirentries
);
52 if (len
> SZ_4M
/ sizeof(struct adfs_bigdirentry
) ||
53 len
* sizeof(struct adfs_bigdirentry
) > size
)
59 static int adfs_fplus_validate_tail(const struct adfs_bigdirheader
*h
,
60 const struct adfs_bigdirtail
*t
)
62 if (t
->bigdirendname
!= cpu_to_le32(BIGDIRENDNAME
) ||
63 t
->bigdirendmasseq
!= h
->startmasseq
||
64 t
->reserved
[0] != 0 || t
->reserved
[1] != 0)
70 static u8
adfs_fplus_checkbyte(struct adfs_dir
*dir
)
72 struct adfs_bigdirheader
*h
= dir
->bighead
;
73 struct adfs_bigdirtail
*t
= dir
->bigtail
;
74 unsigned int end
, bs
, bi
, i
;
78 end
= adfs_fplus_offset(h
, le32_to_cpu(h
->bigdirentries
)) +
79 le32_to_cpu(h
->bigdirnamesize
);
81 /* Accumulate the contents of the header, entries and names */
82 for (dircheck
= 0, bi
= 0; end
; bi
++) {
83 bp
= (void *)dir
->bhs
[bi
]->b_data
;
84 bs
= dir
->bhs
[bi
]->b_size
;
88 for (i
= 0; i
< bs
; i
+= sizeof(u32
))
89 dircheck
= ror32(dircheck
, 13) ^ le32_to_cpup(bp
++);
94 /* Accumulate the contents of the tail except for the check byte */
95 dircheck
= ror32(dircheck
, 13) ^ le32_to_cpu(t
->bigdirendname
);
96 dircheck
= ror32(dircheck
, 13) ^ t
->bigdirendmasseq
;
97 dircheck
= ror32(dircheck
, 13) ^ t
->reserved
[0];
98 dircheck
= ror32(dircheck
, 13) ^ t
->reserved
[1];
100 return dircheck
^ dircheck
>> 8 ^ dircheck
>> 16 ^ dircheck
>> 24;
103 static int adfs_fplus_read(struct super_block
*sb
, u32 indaddr
,
104 unsigned int size
, struct adfs_dir
*dir
)
106 struct adfs_bigdirheader
*h
;
107 struct adfs_bigdirtail
*t
;
108 unsigned int dirsize
;
111 /* Read first buffer */
112 ret
= adfs_dir_read_buffers(sb
, indaddr
, sb
->s_blocksize
, dir
);
116 dir
->bighead
= h
= (void *)dir
->bhs
[0]->b_data
;
117 ret
= adfs_fplus_validate_header(h
);
119 adfs_error(sb
, "dir %06x has malformed header", indaddr
);
123 dirsize
= le32_to_cpu(h
->bigdirsize
);
124 if (size
&& dirsize
!= size
) {
125 adfs_msg(sb
, KERN_WARNING
,
126 "dir %06x header size %X does not match directory size %X",
127 indaddr
, dirsize
, size
);
130 /* Read remaining buffers */
131 ret
= adfs_dir_read_buffers(sb
, indaddr
, dirsize
, dir
);
135 dir
->bigtail
= t
= (struct adfs_bigdirtail
*)
136 (dir
->bhs
[dir
->nr_buffers
- 1]->b_data
+ (sb
->s_blocksize
- 8));
138 ret
= adfs_fplus_validate_tail(h
, t
);
140 adfs_error(sb
, "dir %06x has malformed tail", indaddr
);
144 if (adfs_fplus_checkbyte(dir
) != t
->bigdircheckbyte
) {
145 adfs_error(sb
, "dir %06x checkbyte mismatch\n", indaddr
);
149 dir
->parent_id
= le32_to_cpu(h
->bigdirparent
);
159 adfs_fplus_setpos(struct adfs_dir
*dir
, unsigned int fpos
)
163 if (fpos
<= le32_to_cpu(dir
->bighead
->bigdirentries
)) {
172 adfs_fplus_getnext(struct adfs_dir
*dir
, struct object_info
*obj
)
174 struct adfs_bigdirheader
*h
= dir
->bighead
;
175 struct adfs_bigdirentry bde
;
179 if (dir
->pos
>= le32_to_cpu(h
->bigdirentries
))
182 offset
= adfs_fplus_offset(h
, dir
->pos
);
184 ret
= adfs_dir_copyfrom(&bde
, dir
, offset
,
185 sizeof(struct adfs_bigdirentry
));
189 obj
->loadaddr
= le32_to_cpu(bde
.bigdirload
);
190 obj
->execaddr
= le32_to_cpu(bde
.bigdirexec
);
191 obj
->size
= le32_to_cpu(bde
.bigdirlen
);
192 obj
->indaddr
= le32_to_cpu(bde
.bigdirindaddr
);
193 obj
->attr
= le32_to_cpu(bde
.bigdirattr
);
194 obj
->name_len
= le32_to_cpu(bde
.bigdirobnamelen
);
196 offset
= adfs_fplus_offset(h
, le32_to_cpu(h
->bigdirentries
));
197 offset
+= le32_to_cpu(bde
.bigdirobnameptr
);
199 ret
= adfs_dir_copyfrom(obj
->name
, dir
, offset
, obj
->name_len
);
203 adfs_object_fixup(dir
, obj
);
210 static int adfs_fplus_iterate(struct adfs_dir
*dir
, struct dir_context
*ctx
)
212 struct object_info obj
;
214 if ((ctx
->pos
- 2) >> 32)
217 if (adfs_fplus_setpos(dir
, ctx
->pos
- 2))
220 while (!adfs_fplus_getnext(dir
, &obj
)) {
221 if (!dir_emit(ctx
, obj
.name
, obj
.name_len
,
222 obj
.indaddr
, DT_UNKNOWN
))
230 static int adfs_fplus_update(struct adfs_dir
*dir
, struct object_info
*obj
)
232 struct adfs_bigdirheader
*h
= dir
->bighead
;
233 struct adfs_bigdirentry bde
;
234 int offset
, end
, ret
;
236 offset
= adfs_fplus_offset(h
, 0) - sizeof(bde
);
237 end
= adfs_fplus_offset(h
, le32_to_cpu(h
->bigdirentries
));
240 offset
+= sizeof(bde
);
242 adfs_error(dir
->sb
, "unable to locate entry to update");
245 ret
= adfs_dir_copyfrom(&bde
, dir
, offset
, sizeof(bde
));
247 adfs_error(dir
->sb
, "error reading directory entry");
250 } while (le32_to_cpu(bde
.bigdirindaddr
) != obj
->indaddr
);
252 bde
.bigdirload
= cpu_to_le32(obj
->loadaddr
);
253 bde
.bigdirexec
= cpu_to_le32(obj
->execaddr
);
254 bde
.bigdirlen
= cpu_to_le32(obj
->size
);
255 bde
.bigdirindaddr
= cpu_to_le32(obj
->indaddr
);
256 bde
.bigdirattr
= cpu_to_le32(obj
->attr
);
258 return adfs_dir_copyto(dir
, offset
, &bde
, sizeof(bde
));
261 static int adfs_fplus_commit(struct adfs_dir
*dir
)
265 /* Increment directory sequence number */
266 dir
->bighead
->startmasseq
+= 1;
267 dir
->bigtail
->bigdirendmasseq
+= 1;
269 /* Update directory check byte */
270 dir
->bigtail
->bigdircheckbyte
= adfs_fplus_checkbyte(dir
);
272 /* Make sure the directory still validates correctly */
273 ret
= adfs_fplus_validate_header(dir
->bighead
);
275 ret
= adfs_fplus_validate_tail(dir
->bighead
, dir
->bigtail
);
280 const struct adfs_dir_ops adfs_fplus_dir_ops
= {
281 .read
= adfs_fplus_read
,
282 .iterate
= adfs_fplus_iterate
,
283 .setpos
= adfs_fplus_setpos
,
284 .getnext
= adfs_fplus_getnext
,
285 .update
= adfs_fplus_update
,
286 .commit
= adfs_fplus_commit
,