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
*),
65 adfs_error(sb
, "not enough memory for"
66 " dir object %X (%d blocks)", id
, size
);
69 dir
->bh_fplus
= bh_fplus
;
70 /* copy over the pointer to the block that we've already read */
71 dir
->bh_fplus
[0] = dir
->bh
[0];
74 for (blk
= 1; blk
< size
; blk
++) {
75 block
= __adfs_block_map(sb
, id
, blk
);
77 adfs_error(sb
, "dir object %X has a hole at offset %d", id
, blk
);
81 dir
->bh_fplus
[blk
] = sb_bread(sb
, block
);
82 if (!dir
->bh_fplus
[blk
]) {
83 adfs_error(sb
, "dir object %x failed read for offset %d, mapped block %lX",
91 t
= (struct adfs_bigdirtail
*)
92 (dir
->bh_fplus
[size
- 1]->b_data
+ (sb
->s_blocksize
- 8));
94 if (t
->bigdirendname
!= cpu_to_le32(BIGDIRENDNAME
) ||
95 t
->bigdirendmasseq
!= h
->startmasseq
||
96 t
->reserved
[0] != 0 || t
->reserved
[1] != 0) {
97 printk(KERN_WARNING
"adfs: dir object %X has "
98 "malformed dir end\n", id
);
102 dir
->parent_id
= le32_to_cpu(h
->bigdirparent
);
108 for (i
= 0; i
< dir
->nr_buffers
; i
++)
109 brelse(dir
->bh_fplus
[i
]);
111 if (&dir
->bh
[0] != dir
->bh_fplus
)
112 kfree(dir
->bh_fplus
);
114 dir
->bh_fplus
= NULL
;
123 adfs_fplus_setpos(struct adfs_dir
*dir
, unsigned int fpos
)
125 struct adfs_bigdirheader
*h
=
126 (struct adfs_bigdirheader
*) dir
->bh_fplus
[0]->b_data
;
129 if (fpos
<= le32_to_cpu(h
->bigdirentries
)) {
138 dir_memcpy(struct adfs_dir
*dir
, unsigned int offset
, void *to
, int len
)
140 struct super_block
*sb
= dir
->sb
;
141 unsigned int buffer
, partial
, remainder
;
143 buffer
= offset
>> sb
->s_blocksize_bits
;
144 offset
&= sb
->s_blocksize
- 1;
146 partial
= sb
->s_blocksize
- offset
;
149 memcpy(to
, dir
->bh_fplus
[buffer
]->b_data
+ offset
, len
);
151 char *c
= (char *)to
;
153 remainder
= len
- partial
;
156 dir
->bh_fplus
[buffer
]->b_data
+ offset
,
160 dir
->bh_fplus
[buffer
+ 1]->b_data
,
166 adfs_fplus_getnext(struct adfs_dir
*dir
, struct object_info
*obj
)
168 struct adfs_bigdirheader
*h
=
169 (struct adfs_bigdirheader
*) dir
->bh_fplus
[0]->b_data
;
170 struct adfs_bigdirentry bde
;
172 int i
, ret
= -ENOENT
;
174 if (dir
->pos
>= le32_to_cpu(h
->bigdirentries
))
177 offset
= offsetof(struct adfs_bigdirheader
, bigdirname
);
178 offset
+= ((le32_to_cpu(h
->bigdirnamelen
) + 4) & ~3);
179 offset
+= dir
->pos
* sizeof(struct adfs_bigdirentry
);
181 dir_memcpy(dir
, offset
, &bde
, sizeof(struct adfs_bigdirentry
));
183 obj
->loadaddr
= le32_to_cpu(bde
.bigdirload
);
184 obj
->execaddr
= le32_to_cpu(bde
.bigdirexec
);
185 obj
->size
= le32_to_cpu(bde
.bigdirlen
);
186 obj
->file_id
= le32_to_cpu(bde
.bigdirindaddr
);
187 obj
->attr
= le32_to_cpu(bde
.bigdirattr
);
188 obj
->name_len
= le32_to_cpu(bde
.bigdirobnamelen
);
190 offset
= offsetof(struct adfs_bigdirheader
, bigdirname
);
191 offset
+= ((le32_to_cpu(h
->bigdirnamelen
) + 4) & ~3);
192 offset
+= le32_to_cpu(h
->bigdirentries
) * sizeof(struct adfs_bigdirentry
);
193 offset
+= le32_to_cpu(bde
.bigdirobnameptr
);
195 dir_memcpy(dir
, offset
, obj
->name
, obj
->name_len
);
196 for (i
= 0; i
< obj
->name_len
; i
++)
197 if (obj
->name
[i
] == '/')
203 * object is a file and is filetyped and timestamped?
204 * RISC OS 12-bit filetype is stored in load_address[19:8]
206 if ((0 == (obj
->attr
& ADFS_NDA_DIRECTORY
)) &&
207 (0xfff00000 == (0xfff00000 & obj
->loadaddr
))) {
208 obj
->filetype
= (__u16
) ((0x000fff00 & obj
->loadaddr
) >> 8);
210 /* optionally append the ,xyz hex filetype suffix */
211 if (ADFS_SB(dir
->sb
)->s_ftsuffix
)
213 append_filetype_suffix(
214 &obj
->name
[obj
->name_len
],
225 adfs_fplus_sync(struct adfs_dir
*dir
)
230 for (i
= dir
->nr_buffers
- 1; i
>= 0; i
--) {
231 struct buffer_head
*bh
= dir
->bh_fplus
[i
];
232 sync_dirty_buffer(bh
);
233 if (buffer_req(bh
) && !buffer_uptodate(bh
))
241 adfs_fplus_free(struct adfs_dir
*dir
)
246 for (i
= 0; i
< dir
->nr_buffers
; i
++)
247 brelse(dir
->bh_fplus
[i
]);
249 if (&dir
->bh
[0] != dir
->bh_fplus
)
250 kfree(dir
->bh_fplus
);
252 dir
->bh_fplus
= NULL
;
259 struct adfs_dir_ops adfs_fplus_dir_ops
= {
260 .read
= adfs_fplus_read
,
261 .setpos
= adfs_fplus_setpos
,
262 .getnext
= adfs_fplus_getnext
,
263 .sync
= adfs_fplus_sync
,
264 .free
= adfs_fplus_free