2 * linux/fs/adfs/dir_fplus.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 #include <linux/buffer_head.h>
11 #include <linux/slab.h>
13 #include "dir_fplus.h"
16 adfs_fplus_read(struct super_block
*sb
, unsigned int id
, unsigned int sz
, struct adfs_dir
*dir
)
18 struct adfs_bigdirheader
*h
;
19 struct adfs_bigdirtail
*t
;
21 unsigned int blk
, size
;
26 /* start off using fixed bh set - only alloc for big dirs */
27 dir
->bh_fplus
= &dir
->bh
[0];
29 block
= __adfs_block_map(sb
, id
, 0);
31 adfs_error(sb
, "dir object %X has a hole at offset 0", id
);
35 dir
->bh_fplus
[0] = sb_bread(sb
, block
);
36 if (!dir
->bh_fplus
[0])
40 h
= (struct adfs_bigdirheader
*)dir
->bh_fplus
[0]->b_data
;
41 size
= le32_to_cpu(h
->bigdirsize
);
43 printk(KERN_WARNING
"adfs: adfs_fplus_read:"
44 " directory header size %X\n"
45 " does not match directory size %X\n",
49 if (h
->bigdirversion
[0] != 0 || h
->bigdirversion
[1] != 0 ||
50 h
->bigdirversion
[2] != 0 || size
& 2047 ||
51 h
->bigdirstartname
!= cpu_to_le32(BIGDIRSTARTNAME
)) {
52 printk(KERN_WARNING
"adfs: dir object %X has"
53 " malformed dir header\n", id
);
57 size
>>= sb
->s_blocksize_bits
;
58 if (size
> ARRAY_SIZE(dir
->bh
)) {
59 /* this directory is too big for fixed bh set, must allocate */
60 struct buffer_head
**bh_fplus
=
61 kcalloc(size
, sizeof(struct buffer_head
*),
64 adfs_error(sb
, "not enough memory for"
65 " dir object %X (%d blocks)", id
, size
);
68 dir
->bh_fplus
= bh_fplus
;
69 /* copy over the pointer to the block that we've already read */
70 dir
->bh_fplus
[0] = dir
->bh
[0];
73 for (blk
= 1; blk
< size
; blk
++) {
74 block
= __adfs_block_map(sb
, id
, blk
);
76 adfs_error(sb
, "dir object %X has a hole at offset %d", id
, blk
);
80 dir
->bh_fplus
[blk
] = sb_bread(sb
, block
);
81 if (!dir
->bh_fplus
[blk
]) {
82 adfs_error(sb
, "dir object %x failed read for offset %d, mapped block %lX",
90 t
= (struct adfs_bigdirtail
*)
91 (dir
->bh_fplus
[size
- 1]->b_data
+ (sb
->s_blocksize
- 8));
93 if (t
->bigdirendname
!= cpu_to_le32(BIGDIRENDNAME
) ||
94 t
->bigdirendmasseq
!= h
->startmasseq
||
95 t
->reserved
[0] != 0 || t
->reserved
[1] != 0) {
96 printk(KERN_WARNING
"adfs: dir object %X has "
97 "malformed dir end\n", id
);
101 dir
->parent_id
= le32_to_cpu(h
->bigdirparent
);
107 for (i
= 0; i
< dir
->nr_buffers
; i
++)
108 brelse(dir
->bh_fplus
[i
]);
110 if (&dir
->bh
[0] != dir
->bh_fplus
)
111 kfree(dir
->bh_fplus
);
113 dir
->bh_fplus
= NULL
;
122 adfs_fplus_setpos(struct adfs_dir
*dir
, unsigned int fpos
)
124 struct adfs_bigdirheader
*h
=
125 (struct adfs_bigdirheader
*) dir
->bh_fplus
[0]->b_data
;
128 if (fpos
<= le32_to_cpu(h
->bigdirentries
)) {
137 dir_memcpy(struct adfs_dir
*dir
, unsigned int offset
, void *to
, int len
)
139 struct super_block
*sb
= dir
->sb
;
140 unsigned int buffer
, partial
, remainder
;
142 buffer
= offset
>> sb
->s_blocksize_bits
;
143 offset
&= sb
->s_blocksize
- 1;
145 partial
= sb
->s_blocksize
- offset
;
148 memcpy(to
, dir
->bh_fplus
[buffer
]->b_data
+ offset
, len
);
150 char *c
= (char *)to
;
152 remainder
= len
- partial
;
155 dir
->bh_fplus
[buffer
]->b_data
+ offset
,
159 dir
->bh_fplus
[buffer
+ 1]->b_data
,
165 adfs_fplus_getnext(struct adfs_dir
*dir
, struct object_info
*obj
)
167 struct adfs_bigdirheader
*h
=
168 (struct adfs_bigdirheader
*) dir
->bh_fplus
[0]->b_data
;
169 struct adfs_bigdirentry bde
;
171 int i
, ret
= -ENOENT
;
173 if (dir
->pos
>= le32_to_cpu(h
->bigdirentries
))
176 offset
= offsetof(struct adfs_bigdirheader
, bigdirname
);
177 offset
+= ((le32_to_cpu(h
->bigdirnamelen
) + 4) & ~3);
178 offset
+= dir
->pos
* sizeof(struct adfs_bigdirentry
);
180 dir_memcpy(dir
, offset
, &bde
, sizeof(struct adfs_bigdirentry
));
182 obj
->loadaddr
= le32_to_cpu(bde
.bigdirload
);
183 obj
->execaddr
= le32_to_cpu(bde
.bigdirexec
);
184 obj
->size
= le32_to_cpu(bde
.bigdirlen
);
185 obj
->file_id
= le32_to_cpu(bde
.bigdirindaddr
);
186 obj
->attr
= le32_to_cpu(bde
.bigdirattr
);
187 obj
->name_len
= le32_to_cpu(bde
.bigdirobnamelen
);
189 offset
= offsetof(struct adfs_bigdirheader
, bigdirname
);
190 offset
+= ((le32_to_cpu(h
->bigdirnamelen
) + 4) & ~3);
191 offset
+= le32_to_cpu(h
->bigdirentries
) * sizeof(struct adfs_bigdirentry
);
192 offset
+= le32_to_cpu(bde
.bigdirobnameptr
);
194 dir_memcpy(dir
, offset
, obj
->name
, obj
->name_len
);
195 for (i
= 0; i
< obj
->name_len
; i
++)
196 if (obj
->name
[i
] == '/')
202 * object is a file and is filetyped and timestamped?
203 * RISC OS 12-bit filetype is stored in load_address[19:8]
205 if ((0 == (obj
->attr
& ADFS_NDA_DIRECTORY
)) &&
206 (0xfff00000 == (0xfff00000 & obj
->loadaddr
))) {
207 obj
->filetype
= (__u16
) ((0x000fff00 & obj
->loadaddr
) >> 8);
209 /* optionally append the ,xyz hex filetype suffix */
210 if (ADFS_SB(dir
->sb
)->s_ftsuffix
)
212 append_filetype_suffix(
213 &obj
->name
[obj
->name_len
],
224 adfs_fplus_sync(struct adfs_dir
*dir
)
229 for (i
= dir
->nr_buffers
- 1; i
>= 0; i
--) {
230 struct buffer_head
*bh
= dir
->bh_fplus
[i
];
231 sync_dirty_buffer(bh
);
232 if (buffer_req(bh
) && !buffer_uptodate(bh
))
240 adfs_fplus_free(struct adfs_dir
*dir
)
245 for (i
= 0; i
< dir
->nr_buffers
; i
++)
246 brelse(dir
->bh_fplus
[i
]);
248 if (&dir
->bh
[0] != dir
->bh_fplus
)
249 kfree(dir
->bh_fplus
);
251 dir
->bh_fplus
= NULL
;
258 struct adfs_dir_ops adfs_fplus_dir_ops
= {
259 .read
= adfs_fplus_read
,
260 .setpos
= adfs_fplus_setpos
,
261 .getnext
= adfs_fplus_getnext
,
262 .sync
= adfs_fplus_sync
,
263 .free
= adfs_fplus_free