1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/fs/adfs/dir_fplus.c
5 * Copyright (C) 1997-1999 Russell King
7 #include <linux/slab.h>
12 adfs_fplus_read(struct super_block
*sb
, unsigned int id
, unsigned int sz
, struct adfs_dir
*dir
)
14 struct adfs_bigdirheader
*h
;
15 struct adfs_bigdirtail
*t
;
17 unsigned int blk
, size
;
22 /* start off using fixed bh set - only alloc for big dirs */
23 dir
->bh_fplus
= &dir
->bh
[0];
25 block
= __adfs_block_map(sb
, id
, 0);
27 adfs_error(sb
, "dir object %X has a hole at offset 0", id
);
31 dir
->bh_fplus
[0] = sb_bread(sb
, block
);
32 if (!dir
->bh_fplus
[0])
36 h
= (struct adfs_bigdirheader
*)dir
->bh_fplus
[0]->b_data
;
37 size
= le32_to_cpu(h
->bigdirsize
);
39 adfs_msg(sb
, KERN_WARNING
,
40 "directory header size %X does not match directory size %X",
44 if (h
->bigdirversion
[0] != 0 || h
->bigdirversion
[1] != 0 ||
45 h
->bigdirversion
[2] != 0 || size
& 2047 ||
46 h
->bigdirstartname
!= cpu_to_le32(BIGDIRSTARTNAME
)) {
47 adfs_error(sb
, "dir %06x has malformed header", id
);
51 size
>>= sb
->s_blocksize_bits
;
52 if (size
> ARRAY_SIZE(dir
->bh
)) {
53 /* this directory is too big for fixed bh set, must allocate */
54 struct buffer_head
**bh_fplus
=
55 kcalloc(size
, sizeof(struct buffer_head
*),
58 adfs_msg(sb
, KERN_ERR
,
59 "not enough memory for dir object %X (%d blocks)",
64 dir
->bh_fplus
= bh_fplus
;
65 /* copy over the pointer to the block that we've already read */
66 dir
->bh_fplus
[0] = dir
->bh
[0];
69 for (blk
= 1; blk
< size
; blk
++) {
70 block
= __adfs_block_map(sb
, id
, blk
);
72 adfs_error(sb
, "dir object %X has a hole at offset %d", id
, blk
);
76 dir
->bh_fplus
[blk
] = sb_bread(sb
, block
);
77 if (!dir
->bh_fplus
[blk
]) {
78 adfs_error(sb
, "dir object %x failed read for offset %d, mapped block %lX",
86 t
= (struct adfs_bigdirtail
*)
87 (dir
->bh_fplus
[size
- 1]->b_data
+ (sb
->s_blocksize
- 8));
89 if (t
->bigdirendname
!= cpu_to_le32(BIGDIRENDNAME
) ||
90 t
->bigdirendmasseq
!= h
->startmasseq
||
91 t
->reserved
[0] != 0 || t
->reserved
[1] != 0) {
92 adfs_error(sb
, "dir %06x has malformed tail", id
);
96 dir
->parent_id
= le32_to_cpu(h
->bigdirparent
);
102 for (i
= 0; i
< dir
->nr_buffers
; i
++)
103 brelse(dir
->bh_fplus
[i
]);
105 if (&dir
->bh
[0] != dir
->bh_fplus
)
106 kfree(dir
->bh_fplus
);
108 dir
->bh_fplus
= NULL
;
117 adfs_fplus_setpos(struct adfs_dir
*dir
, unsigned int fpos
)
119 struct adfs_bigdirheader
*h
=
120 (struct adfs_bigdirheader
*) dir
->bh_fplus
[0]->b_data
;
123 if (fpos
<= le32_to_cpu(h
->bigdirentries
)) {
132 dir_memcpy(struct adfs_dir
*dir
, unsigned int offset
, void *to
, int len
)
134 struct super_block
*sb
= dir
->sb
;
135 unsigned int buffer
, partial
, remainder
;
137 buffer
= offset
>> sb
->s_blocksize_bits
;
138 offset
&= sb
->s_blocksize
- 1;
140 partial
= sb
->s_blocksize
- offset
;
143 memcpy(to
, dir
->bh_fplus
[buffer
]->b_data
+ offset
, len
);
145 char *c
= (char *)to
;
147 remainder
= len
- partial
;
150 dir
->bh_fplus
[buffer
]->b_data
+ offset
,
154 dir
->bh_fplus
[buffer
+ 1]->b_data
,
160 adfs_fplus_getnext(struct adfs_dir
*dir
, struct object_info
*obj
)
162 struct adfs_bigdirheader
*h
=
163 (struct adfs_bigdirheader
*) dir
->bh_fplus
[0]->b_data
;
164 struct adfs_bigdirentry bde
;
168 if (dir
->pos
>= le32_to_cpu(h
->bigdirentries
))
171 offset
= offsetof(struct adfs_bigdirheader
, bigdirname
);
172 offset
+= ((le32_to_cpu(h
->bigdirnamelen
) + 4) & ~3);
173 offset
+= dir
->pos
* sizeof(struct adfs_bigdirentry
);
175 dir_memcpy(dir
, offset
, &bde
, sizeof(struct adfs_bigdirentry
));
177 obj
->loadaddr
= le32_to_cpu(bde
.bigdirload
);
178 obj
->execaddr
= le32_to_cpu(bde
.bigdirexec
);
179 obj
->size
= le32_to_cpu(bde
.bigdirlen
);
180 obj
->indaddr
= le32_to_cpu(bde
.bigdirindaddr
);
181 obj
->attr
= le32_to_cpu(bde
.bigdirattr
);
182 obj
->name_len
= le32_to_cpu(bde
.bigdirobnamelen
);
184 offset
= offsetof(struct adfs_bigdirheader
, bigdirname
);
185 offset
+= ((le32_to_cpu(h
->bigdirnamelen
) + 4) & ~3);
186 offset
+= le32_to_cpu(h
->bigdirentries
) * sizeof(struct adfs_bigdirentry
);
187 offset
+= le32_to_cpu(bde
.bigdirobnameptr
);
189 dir_memcpy(dir
, offset
, obj
->name
, obj
->name_len
);
190 adfs_object_fixup(dir
, obj
);
199 adfs_fplus_sync(struct adfs_dir
*dir
)
204 for (i
= dir
->nr_buffers
- 1; i
>= 0; i
--) {
205 struct buffer_head
*bh
= dir
->bh_fplus
[i
];
206 sync_dirty_buffer(bh
);
207 if (buffer_req(bh
) && !buffer_uptodate(bh
))
215 adfs_fplus_free(struct adfs_dir
*dir
)
220 for (i
= 0; i
< dir
->nr_buffers
; i
++)
221 brelse(dir
->bh_fplus
[i
]);
223 if (&dir
->bh
[0] != dir
->bh_fplus
)
224 kfree(dir
->bh_fplus
);
226 dir
->bh_fplus
= NULL
;
233 const struct adfs_dir_ops adfs_fplus_dir_ops
= {
234 .read
= adfs_fplus_read
,
235 .setpos
= adfs_fplus_setpos
,
236 .getnext
= adfs_fplus_getnext
,
237 .sync
= adfs_fplus_sync
,
238 .free
= adfs_fplus_free