Line 0
Link Here
|
|
|
1 |
/* |
2 |
* |
3 |
* Amiga Smart File System, Linux implementation |
4 |
* |
5 |
* version: 0.6 (04.09.2003) |
6 |
* |
7 |
* Copyright (C) 2003 Marek 'March' Szyprowski <marek@amiga.pl> |
8 |
* |
9 |
* |
10 |
* Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help. |
11 |
* |
12 |
* Based on the Linux implementation of the ROMFS file system |
13 |
* Copyright (C) 1997-1999 Janos Farkas <chexum@shadow.banki.hu> |
14 |
* |
15 |
* Using parts of the Amiga FFS filesystem |
16 |
* Copyright (C) 1993 Ray Burr |
17 |
* Copyright (C) 1996 Hans-Joachim Widmaier |
18 |
* |
19 |
* and parts of the smbfs filesystem additionally |
20 |
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke |
21 |
* Copyright (C) 1997 by Volker Lendecke |
22 |
* |
23 |
* |
24 |
* This program is free software; you can redistribute it and/or |
25 |
* modify it under the terms of the GNU General Public License |
26 |
* as published by the Free Software Foundation; either version |
27 |
* 2 of the License, or (at your option) any later version. |
28 |
* |
29 |
* |
30 |
* History: |
31 |
* |
32 |
* v0.6 (04.09.2003) - final read-only version |
33 |
* - added support for HashTables, directory scaning should be |
34 |
* MUCH faster now |
35 |
* - added checking of block IDs before reading any data from block |
36 |
* |
37 |
* v0.5 (19.07.2003) |
38 |
* - added simple but effective extent cache - real speed-up |
39 |
* in reading large files |
40 |
* - added read support for symlinks - based on AFFS symlinks |
41 |
* |
42 |
* v0.4 (10.07.2003) |
43 |
* - third code clean-up (thanks to Roman Zippel for advice) |
44 |
* - now uses generic readpage and readinode routines |
45 |
* |
46 |
* v0.3beta (17.06.2003) |
47 |
* - second code clean-up |
48 |
* |
49 |
* v0.2beta2 (15.06.2003) |
50 |
* - fixed yet another stupid bug - driver can't read root block on little-endian systems |
51 |
* v0.2beta (15.06.2003) |
52 |
* - fixed stupid bug - now files have 'file' flag (S_IFREG) set... |
53 |
* - added mount options to set uid, gid and mode of files and dirs |
54 |
* - made hidden files & dirs really hidden (= not listed in directories) |
55 |
* - code clean-up |
56 |
* |
57 |
* v0.1beta (11.06.2003) |
58 |
* - after many kernel crashes, finally got it! |
59 |
* - first working read-only filesystem driver |
60 |
* |
61 |
*/ |
62 |
|
63 |
/* todo: |
64 |
* - remove bugs |
65 |
* - considering write access... |
66 |
*/ |
67 |
|
68 |
#include <linux/module.h> |
69 |
#include <linux/types.h> |
70 |
#include <linux/errno.h> |
71 |
#include <linux/slab.h> |
72 |
#include <linux/asfs_fs.h> |
73 |
#include <linux/fs.h> |
74 |
#include <linux/locks.h> |
75 |
#include <linux/init.h> |
76 |
#include <linux/smp_lock.h> |
77 |
|
78 |
#include <asm/byteorder.h> |
79 |
#include <asm/uaccess.h> |
80 |
|
81 |
static u32 asfs_calcchecksum(struct fsBlockHeader *block, u32 blocksize) |
82 |
{ |
83 |
u32 *data=(void *)block, checksum = 1, tmpchecksum = block->checksum; |
84 |
|
85 |
block->checksum=0; |
86 |
while(blocksize > 0) { |
87 |
checksum+=be32_to_cpu(*data++); |
88 |
blocksize-=4; |
89 |
} |
90 |
block->checksum=tmpchecksum; |
91 |
return(-checksum); |
92 |
} |
93 |
|
94 |
static inline int |
95 |
asfs_check_block(struct fsBlockHeader *block, u32 blocksize, u32 n, u32 id) |
96 |
{ |
97 |
if (asfs_calcchecksum((struct fsBlockHeader *)block, blocksize) == |
98 |
be32_to_cpu(((struct fsBlockHeader *)block)->checksum) && |
99 |
n == be32_to_cpu(((struct fsBlockHeader *)block)->ownblock) && |
100 |
id == be32_to_cpu(((struct fsBlockHeader *)block)->id)) |
101 |
return 1; |
102 |
return 0; |
103 |
} |
104 |
|
105 |
/* get fs structure from block and do some checks... */ |
106 |
static int asfs_get_contblock(struct super_block *sb, u32 n, u32 type, void *dest) |
107 |
{ |
108 |
struct buffer_head *bh; |
109 |
|
110 |
bh = sb_bread(sb, n); |
111 |
if (!bh) |
112 |
return -1; /* error */ |
113 |
memcpy(dest, ((char *)bh->b_data), sb->s_blocksize); |
114 |
brelse(bh); |
115 |
|
116 |
if (asfs_check_block(dest, sb->s_blocksize, n, type)) { |
117 |
from32be(((struct fsBlockHeader *)dest)->ownblock); |
118 |
return 1; /* all okay */ |
119 |
} |
120 |
return -1; /* error */ |
121 |
} |
122 |
|
123 |
static inline struct inode *asfs_get_root_inode(struct super_block *sb) |
124 |
{ |
125 |
struct inode *result = NULL; |
126 |
struct fsObjectContainer *block; |
127 |
struct fsObject *obj; |
128 |
|
129 |
asfs_debug("asfs_get_root_inode\n"); |
130 |
|
131 |
block = kmalloc(sb->s_blocksize, GFP_KERNEL); |
132 |
if (asfs_get_contblock(sb, ASFS_SB->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID, block) < 0) |
133 |
goto free_and_return; |
134 |
|
135 |
obj = &(block->object[0]); |
136 |
from32be(obj->objectnode); |
137 |
from32be(obj->object.dir.firstdirblock); |
138 |
from32be(obj->object.dir.hashtable); |
139 |
from32be(obj->datemodified); |
140 |
|
141 |
if (obj->objectnode > 0) |
142 |
result = iget4(sb, obj->objectnode, NULL, obj); |
143 |
|
144 |
free_and_return: |
145 |
kfree(block); |
146 |
return result; |
147 |
} |
148 |
|
149 |
static struct super_operations asfs_ops; |
150 |
|
151 |
static int asfs_parse_options(char *options, struct super_block *sb) |
152 |
{ |
153 |
char *this_char, *value, *optn; |
154 |
int f; |
155 |
|
156 |
/* Fill in defaults */ |
157 |
ASFS_SB->uid = ASFS_DEFAULT_UID; |
158 |
ASFS_SB->gid = ASFS_DEFAULT_GID; |
159 |
ASFS_SB->mode = ASFS_DEFAULT_MODE; |
160 |
ASFS_SB->prefix = NULL; |
161 |
|
162 |
if (!options) |
163 |
return 1; |
164 |
for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { |
165 |
f = 0; |
166 |
if ((value = strchr(this_char,'=')) != NULL) |
167 |
*value++ = 0; |
168 |
if ((f = !strcmp(this_char,"setuid")) || !strcmp(this_char,"setgid")) { |
169 |
if (value) { |
170 |
if (!*value) { |
171 |
printk("ASFS: Argument for set[ug]id option missing\n"); |
172 |
return 0; |
173 |
} else { |
174 |
(f ? ASFS_SB->uid : ASFS_SB->gid) = simple_strtoul(value,&value,0); |
175 |
if (*value) { |
176 |
printk("ASFS: Bad set[ug]id argument\n"); |
177 |
return 0; |
178 |
} |
179 |
} |
180 |
} |
181 |
} else if (!strcmp(this_char,"mode")) { |
182 |
optn = "mode"; |
183 |
if (!value || !*value) |
184 |
goto out_no_arg; |
185 |
ASFS_SB->mode = simple_strtoul(value,&value,8) & 0777; |
186 |
if (*value) |
187 |
return 0; |
188 |
} else if (!strcmp(this_char,"prefix")) { |
189 |
optn = "prefix"; |
190 |
if (!value || !*value) |
191 |
goto out_no_arg; |
192 |
ASFS_SB->prefix = kmalloc(strlen(value) + 1,GFP_KERNEL); |
193 |
if (!ASFS_SB->prefix) |
194 |
return 0; |
195 |
strcpy(ASFS_SB->prefix,value); |
196 |
} else { |
197 |
printk("ASFS: Unrecognized mount option %s\n", this_char); |
198 |
return 0; |
199 |
} |
200 |
} |
201 |
return 1; |
202 |
|
203 |
out_no_arg: |
204 |
printk("ASFS: The %s option requires an argument\n", optn); |
205 |
return 0; |
206 |
} |
207 |
|
208 |
static struct super_block * |
209 |
asfs_read_super(struct super_block *sb, void *data, int silent) |
210 |
{ |
211 |
struct buffer_head *bh; |
212 |
kdev_t dev = sb->s_dev; |
213 |
struct fsRootBlock *rootblock; |
214 |
|
215 |
if (!asfs_parse_options(data, sb)) { |
216 |
printk(KERN_ERR "ASFS: Error parsing options\n"); |
217 |
return NULL; |
218 |
} |
219 |
|
220 |
if (!sb_set_blocksize(sb, 512)) |
221 |
return NULL; |
222 |
sb->s_maxbytes = 0xFFFFFFFF; |
223 |
|
224 |
bh = sb_bread(sb, 0); |
225 |
if (!bh) { |
226 |
printk(KERN_ERR "ASFS: unable to read superblock\n"); |
227 |
goto outnobh; |
228 |
} |
229 |
|
230 |
rootblock = (struct fsRootBlock *)bh->b_data; |
231 |
|
232 |
if (be32_to_cpu(rootblock->bheader.id) == ASFS_ROOTID && |
233 |
be16_to_cpu(rootblock->version) == ASFS_STRUCTURE_VERISON) { |
234 |
|
235 |
sb->s_blocksize = be32_to_cpu(rootblock->blocksize); |
236 |
ASFS_SB->totalblocks = be32_to_cpu(rootblock->totalblocks); |
237 |
ASFS_SB->rootobjectcontainer = be32_to_cpu(rootblock->rootobjectcontainer); |
238 |
ASFS_SB->extentbnoderoot = be32_to_cpu(rootblock->extentbnoderoot); |
239 |
ASFS_SB->objectnoderoot = be32_to_cpu(rootblock->objectnoderoot); |
240 |
ASFS_SB->flags = rootblock->bits; |
241 |
brelse(bh); |
242 |
|
243 |
if (!sb_set_blocksize(sb, sb->s_blocksize)) { |
244 |
printk(KERN_ERR "ASFS: Found Amiga SFS RootBlock on dev %s, but blocksize %ld is not supported!\n", \ |
245 |
bdevname(dev), sb->s_blocksize); |
246 |
return NULL; |
247 |
} |
248 |
|
249 |
bh = sb_bread(sb, 0); |
250 |
if (!bh) { |
251 |
printk(KERN_ERR "ASFS: unable to read superblock\n"); |
252 |
goto out; |
253 |
} |
254 |
rootblock = (struct fsRootBlock *)bh->b_data; |
255 |
|
256 |
if (asfs_check_block((void *)rootblock, sb->s_blocksize, 0, ASFS_ROOTID)) |
257 |
printk(KERN_NOTICE "ASFS: Found Amiga SFS RootBlock on dev %s. Checksum okay. Mounting...\n", \ |
258 |
bdevname(dev)); |
259 |
else { |
260 |
if (!silent) |
261 |
printk(KERN_ERR "ASFS: Found Amiga SFS RootBlock on dev %s, but it has checksum error!\n", \ |
262 |
bdevname(dev)); |
263 |
goto out; |
264 |
} |
265 |
} else { |
266 |
if (!silent) |
267 |
printk(KERN_ERR "ASFS: Can't find a valid Amiga SFS filesystem on dev %s.\n", \ |
268 |
bdevname(dev)); |
269 |
goto out; |
270 |
} |
271 |
|
272 |
brelse(bh); |
273 |
|
274 |
sb->s_magic = ASFS_MAGIC; |
275 |
sb->s_flags |= MS_RDONLY | MS_NODEV | MS_NOSUID; |
276 |
sb->s_op = &asfs_ops; |
277 |
sb->s_root = d_alloc_root(asfs_get_root_inode(sb)); |
278 |
if (!sb->s_root) |
279 |
goto outnobh; |
280 |
|
281 |
/* Ehrhm; sorry.. :) */ |
282 |
if (0) { |
283 |
out: |
284 |
brelse(bh); |
285 |
outnobh: |
286 |
sb = NULL; |
287 |
} |
288 |
return sb; |
289 |
} |
290 |
|
291 |
static void asfs_put_super(struct super_block *sb) |
292 |
{ |
293 |
if (ASFS_SB->prefix) |
294 |
kfree(ASFS_SB->prefix); |
295 |
return; |
296 |
} |
297 |
|
298 |
/* That's simple too. */ |
299 |
|
300 |
static int asfs_statfs(struct super_block *sb, struct statfs *buf) |
301 |
{ |
302 |
buf->f_type = ASFS_MAGIC; |
303 |
buf->f_bsize = sb->s_blocksize; |
304 |
buf->f_bfree = buf->f_bavail = buf->f_ffree; |
305 |
buf->f_blocks = ASFS_SB->totalblocks; |
306 |
buf->f_namelen = ASFS_MAXFN; |
307 |
return 0; |
308 |
} |
309 |
|
310 |
static int asfs_get_fsObject_varlen(struct fsObject *obj) |
311 |
{ |
312 |
int len, i; |
313 |
u8 *p = obj->name; |
314 |
for (i=2; i > 0; p++) |
315 |
if (*p == '\0') |
316 |
i--; |
317 |
len = (p - (u8 *)obj); |
318 |
if (len & 1) |
319 |
len++; |
320 |
return len; |
321 |
} |
322 |
|
323 |
static int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir) |
324 |
{ |
325 |
struct inode *dir = filp->f_dentry->d_inode; |
326 |
struct super_block *sb = dir->i_sb; |
327 |
unsigned long f_pos; |
328 |
int stored = 0; |
329 |
int obj_skip; |
330 |
|
331 |
struct fsObjectContainer *objcont; |
332 |
struct fsObject *obj; |
333 |
int i; |
334 |
u32 block; |
335 |
|
336 |
asfs_debug("asfs_readdir:\n"); |
337 |
|
338 |
if (filp->f_pos == ASFS_SB->totalblocks) |
339 |
return stored; |
340 |
|
341 |
f_pos = filp->f_pos; |
342 |
|
343 |
if (f_pos == 0) { |
344 |
filp->private_data = (void *)0; |
345 |
if (filldir(dirent, ".", 1, f_pos, dir->i_ino, DT_DIR) < 0) |
346 |
return 0; |
347 |
filp->f_pos = f_pos = 1; |
348 |
stored++; |
349 |
} |
350 |
if (f_pos == 1) { |
351 |
if (filldir(dirent, "..", 2, f_pos, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) |
352 |
return stored; |
353 |
filp->f_pos = f_pos = 2; |
354 |
stored++; |
355 |
} |
356 |
|
357 |
if (ASFS_DIR->firstblock == 0) { /* empty directory */ |
358 |
filp->f_pos = ASFS_SB->totalblocks; |
359 |
return stored; |
360 |
} |
361 |
|
362 |
objcont = kmalloc(sb->s_blocksize, GFP_KERNEL); |
363 |
|
364 |
if (f_pos == 2) { /* reading directory from its beginning */ |
365 |
block = ASFS_DIR->firstblock; |
366 |
do { |
367 |
if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) |
368 |
goto free_and_end; |
369 |
block = be32_to_cpu(objcont->previous); |
370 |
} while (block != 0); |
371 |
obj_skip = 0; |
372 |
} else { |
373 |
block = f_pos; |
374 |
if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) |
375 |
goto free_and_end; |
376 |
obj_skip = (int)filp->private_data; |
377 |
} |
378 |
|
379 |
i=0; |
380 |
block = objcont->bheader.ownblock; |
381 |
|
382 |
do { |
383 |
if (i != 0) { |
384 |
if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) |
385 |
goto free_and_end; |
386 |
} |
387 |
|
388 |
i = 0; |
389 |
obj = &(objcont->object[0]); |
390 |
|
391 |
while (be32_to_cpu(obj->objectnode) > 0 && |
392 |
((char *)obj - (char *)objcont) + sizeof(struct fsObject) < sb->s_blocksize) { |
393 |
from32be(obj->objectnode); |
394 |
|
395 |
if (obj_skip > 0) |
396 |
obj_skip--; |
397 |
else if (!(obj->bits & OTYPE_HIDDEN)) { |
398 |
unsigned int type; |
399 |
asfs_debug("ASFS: DirFilling: entry #%d (in_cont: %d) \"%s\" (node %u offset %u), type %d\n", \ |
400 |
stored, i, obj->name, obj->objectnode, block, obj->bits); |
401 |
filp->f_pos = block; |
402 |
if (obj->bits & OTYPE_DIR) |
403 |
type = DT_DIR; |
404 |
else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK)) |
405 |
type = DT_LNK; |
406 |
else |
407 |
type = DT_REG; |
408 |
if (filldir(dirent, obj->name, strlen(obj->name), block, obj->objectnode, type) < 0) { |
409 |
filp->private_data = (void *)i; |
410 |
asfs_debug("ASFS: DirFilling: to be continued...\n"); |
411 |
goto free_and_end; |
412 |
} |
413 |
stored++; |
414 |
} |
415 |
obj = (struct fsObject *)((char *)(obj) + asfs_get_fsObject_varlen(obj)); |
416 |
i++; |
417 |
} |
418 |
block = be32_to_cpu(objcont->next); |
419 |
|
420 |
} while (block != 0); |
421 |
|
422 |
filp->f_pos = ASFS_SB->totalblocks; |
423 |
|
424 |
free_and_end: |
425 |
kfree(objcont); |
426 |
return stored; |
427 |
} |
428 |
|
429 |
static inline u8 upperchar(u8 c) |
430 |
{ |
431 |
if((c>=224 && c<=254 && c!=247) || (c>='a' && c<='z')) |
432 |
c-=32; |
433 |
return(c); |
434 |
} |
435 |
|
436 |
static int namecmp(u8 *s, u8 *ct, int casesensitive) |
437 |
{ |
438 |
if (casesensitive) { |
439 |
while (*s == *ct && *ct != '\0' && *ct != '/') { |
440 |
s++; ct++; |
441 |
} |
442 |
} else { |
443 |
while (upperchar(*s) == upperchar(*ct) && *ct != '\0' && *ct != '/') { |
444 |
s++; ct++; |
445 |
} |
446 |
} |
447 |
return (*s == '\0' && (*ct == '\0' || *ct == '/')) ? 0 : *ct - *s; |
448 |
} |
449 |
|
450 |
#define HASHCHAIN(x) (u16)(x % (u16)(((sb->s_blocksize) - sizeof(struct fsHashTable))>>2)) |
451 |
|
452 |
static inline u16 hash(u8 *name, int casesensitive) |
453 |
{ |
454 |
u16 hashval=0; |
455 |
while (name[hashval] != 0 && name[hashval] != '/') |
456 |
hashval++; |
457 |
if (casesensitive) { |
458 |
u8 c = *name; |
459 |
while (c != 0 && c != '/') { |
460 |
hashval = hashval*13 + c; |
461 |
c = *++name; |
462 |
} |
463 |
} else { |
464 |
u8 c = *name; |
465 |
while (c != 0 && c != '/') { |
466 |
hashval = hashval*13 + upperchar(c); |
467 |
c = *++name; |
468 |
} |
469 |
} |
470 |
return hashval; |
471 |
} |
472 |
|
473 |
/* Finds a specific node by number. */ |
474 |
static struct fsObjectNode * |
475 |
asfs_get_Node(struct super_block *sb, u32 nodeindex, u16 nodesize, u32 nodeno, |
476 |
struct fsObjectNode *dest_node) |
477 |
{ |
478 |
struct fsNodeContainer *nodecont; |
479 |
nodecont = kmalloc(sb->s_blocksize, GFP_KERNEL); |
480 |
|
481 |
while (asfs_get_contblock(sb, nodeindex, ASFS_NODECONTAINER_ID, nodecont) > 0) { |
482 |
if (be32_to_cpu(nodecont->nodes) == 1) { |
483 |
*dest_node = *((struct fsObjectNode *) ((u8 *) nodecont->node + nodesize * \ |
484 |
(nodeno - be32_to_cpu(nodecont->nodenumber)))); |
485 |
kfree(nodecont); |
486 |
from32be(dest_node->node.data); |
487 |
from32be(dest_node->next); |
488 |
from16be(dest_node->hash16); |
489 |
return dest_node; |
490 |
} else { |
491 |
u16 containerentry = (nodeno - be32_to_cpu(nodecont->nodenumber)) / |
492 |
be32_to_cpu(nodecont->nodes); |
493 |
nodeindex = be32_to_cpu(nodecont->node[containerentry]) >> |
494 |
(sb->s_blocksize_bits - ASFS_BLCKFACCURACY); |
495 |
} |
496 |
} |
497 |
kfree(nodecont); |
498 |
return NULL; |
499 |
} |
500 |
|
501 |
struct fsObject * |
502 |
asfs_find_obj_in_cont(struct super_block *sb, struct fsObjectContainer *objcont, |
503 |
u8 *name) |
504 |
{ |
505 |
struct fsObject *obj; |
506 |
|
507 |
obj = &(objcont->object[0]); |
508 |
while (be32_to_cpu(obj->objectnode) > 0 && |
509 |
((char *)obj - (char *)objcont) + sizeof(struct fsObject) < sb->s_blocksize) { |
510 |
if (namecmp(obj->name, name, ASFS_SB->flags & ASFS_ROOTBITS_CASESENSITIVE) == 0) { |
511 |
from32be(obj->objectnode); |
512 |
/* these two below also convert file.size and file.firstdatablock */ |
513 |
from32be(obj->object.dir.firstdirblock); |
514 |
from32be(obj->object.dir.hashtable); |
515 |
from32be(obj->datemodified); |
516 |
asfs_debug("Object found! Node %u, Name %s, Type: %d, inCont %u\n", \ |
517 |
obj->objectnode, obj->name, obj->bits, objcont->bheader.ownblock); |
518 |
return obj; |
519 |
} |
520 |
obj = (struct fsObject *)((u8 *)(obj) + asfs_get_fsObject_varlen(obj)); |
521 |
} |
522 |
return NULL; |
523 |
} |
524 |
|
525 |
static struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry) |
526 |
{ |
527 |
int res = -EACCES; /* placeholder for "no data here" */ |
528 |
struct inode *inode; |
529 |
struct super_block *sb = dir->i_sb; |
530 |
u8 *name = (u8 *) dentry->d_name.name; |
531 |
struct fsObjectContainer *objcont; |
532 |
struct fsObject *obj; |
533 |
struct fsObjectNode obj_node; |
534 |
u32 block, node; |
535 |
u16 hash16; |
536 |
|
537 |
asfs_debug("asfs_lookup: (searching \"%s\"...) ", name); |
538 |
objcont = kmalloc(dir->i_sb->s_blocksize, GFP_KERNEL); |
539 |
|
540 |
if (ASFS_DIR->hashtable != 0) { /* hashtable block is available, quick search */ |
541 |
|
542 |
if (asfs_get_contblock(sb, ASFS_DIR->hashtable, ASFS_HASHTABLE_ID, objcont) < 0) |
543 |
goto free_and_error; |
544 |
hash16 = hash(name, ASFS_SB->flags & ASFS_ROOTBITS_CASESENSITIVE); |
545 |
node = be32_to_cpu(((struct fsHashTable *)objcont)->hashentry[HASHCHAIN(hash16)]); |
546 |
|
547 |
while (node != 0) { |
548 |
if (asfs_get_Node(sb, ASFS_SB->objectnoderoot, sizeof(struct fsObjectNode), |
549 |
node, &obj_node) == NULL) |
550 |
goto not_found; |
551 |
if (obj_node.hash16 == hash16) { |
552 |
if (asfs_get_contblock(sb, obj_node.node.data, ASFS_OBJECTCONTAINER_ID, objcont) < 0) |
553 |
goto free_and_error; |
554 |
if ((obj = asfs_find_obj_in_cont(sb, objcont, name)) != NULL) |
555 |
goto found_inode; |
556 |
} |
557 |
node = obj_node.next; |
558 |
} |
559 |
} else { /* hashtable not available, long search */ |
560 |
|
561 |
block = ASFS_DIR->firstblock; |
562 |
do { |
563 |
if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) |
564 |
goto free_and_error; |
565 |
block = be32_to_cpu(objcont->previous); |
566 |
} while (block != 0); |
567 |
block = objcont->bheader.ownblock; |
568 |
|
569 |
do { |
570 |
if (block != objcont->bheader.ownblock) |
571 |
if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) |
572 |
goto free_and_error; |
573 |
if ((obj = asfs_find_obj_in_cont(sb, objcont, name)) != NULL) |
574 |
goto found_inode; |
575 |
block = be32_to_cpu(objcont->next); |
576 |
} while (block != 0); |
577 |
} |
578 |
|
579 |
not_found: |
580 |
inode = NULL; |
581 |
asfs_debug("object not found.\n"); |
582 |
if (0) { |
583 |
found_inode: |
584 |
if (!(inode = iget4(sb, obj->objectnode, NULL, obj))) { |
585 |
asfs_debug("ASFS: Strange - no inode allocated.\n"); |
586 |
goto free_and_error; |
587 |
} |
588 |
} |
589 |
res = 0; |
590 |
d_add(dentry, inode); |
591 |
free_and_error: |
592 |
kfree(objcont); |
593 |
return ERR_PTR(res); |
594 |
} |
595 |
|
596 |
static struct fsExtentBNode * |
597 |
asfs_search_BNodeTree(struct super_block *sb, u32 key, struct fsExtentBNode *dest) |
598 |
{ |
599 |
struct fsBNodeContainer *bnodecont; |
600 |
struct fsExtentBNode *result = dest; |
601 |
unsigned long block = ASFS_SB->extentbnoderoot; |
602 |
|
603 |
bnodecont = kmalloc(sb->s_blocksize, GFP_KERNEL); |
604 |
while (asfs_get_contblock(sb, block, ASFS_BNODECONTAINER_ID, bnodecont) > 0) { |
605 |
from16be(bnodecont->btc.nodecount); |
606 |
if (bnodecont->btc.isleaf) { |
607 |
int i; |
608 |
struct fsExtentBNode *exbnode; |
609 |
exbnode = (void *)bnodecont->btc.bnode; |
610 |
for (i=0; i < bnodecont->btc.nodecount; i++) { |
611 |
from32be(exbnode->key); |
612 |
if (exbnode->key == key) { |
613 |
from32be(exbnode->next); |
614 |
from32be(exbnode->prev); |
615 |
from16be(exbnode->blocks); |
616 |
*dest = *exbnode; |
617 |
goto found; |
618 |
} |
619 |
exbnode = (void *)exbnode + bnodecont->btc.nodesize; |
620 |
} |
621 |
} else { |
622 |
int i; |
623 |
struct BNode *bnode; |
624 |
bnode = (void *)bnodecont->btc.bnode; |
625 |
for (i=0; i < bnodecont->btc.nodecount && key >= be32_to_cpu(bnode->key); i++) |
626 |
bnode = (void *)bnode + bnodecont->btc.nodesize; |
627 |
|
628 |
bnode = (void *)bnode - bnodecont->btc.nodesize; |
629 |
block = be32_to_cpu(bnode->data); |
630 |
} |
631 |
} |
632 |
/* read error or key not found */ |
633 |
result = NULL; |
634 |
found: |
635 |
kfree(bnodecont); |
636 |
return result; |
637 |
} |
638 |
|
639 |
static int |
640 |
asfs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create) |
641 |
{ |
642 |
struct fsExtentBNode extend; |
643 |
u32 filedata; |
644 |
unsigned long pos; |
645 |
|
646 |
asfs_debug("ASFS: get_block(%lu, %ld)\n", inode->i_ino, block); |
647 |
|
648 |
if (block < 0) { |
649 |
printk(KERN_ERR "ASFS: asfsget_block: requested block (%ld) < 0!\n", block); |
650 |
return -EIO; |
651 |
} |
652 |
|
653 |
if (block >= inode->i_blocks) { |
654 |
printk(KERN_ERR "ASFS: asfsget_block: strange block request %ld!\n", block); |
655 |
return -EIO; |
656 |
} |
657 |
|
658 |
if (ASFS_INODE->ext_cache.key > 0 && ASFS_INODE->ext_cache.startblock <= block) { |
659 |
extend.key = ASFS_INODE->ext_cache.key; |
660 |
extend.next = ASFS_INODE->ext_cache.next; |
661 |
extend.blocks = ASFS_INODE->ext_cache.blocks; |
662 |
pos = ASFS_INODE->ext_cache.startblock; |
663 |
} else { |
664 |
if (asfs_search_BNodeTree(inode->i_sb, ASFS_INODE->firstblock, &extend) == NULL) |
665 |
return -EIO; |
666 |
pos = 0; |
667 |
} |
668 |
filedata = extend.next; |
669 |
|
670 |
while (pos + extend.blocks <= block && extend.next != 0 && pos < inode->i_blocks) { |
671 |
pos += extend.blocks; |
672 |
if (asfs_search_BNodeTree(inode->i_sb, filedata, &extend) == NULL) |
673 |
return -EIO; |
674 |
filedata = extend.next; |
675 |
} |
676 |
|
677 |
bh_result->b_blocknr = extend.key + block - pos; |
678 |
bh_result->b_dev = inode->i_dev; |
679 |
bh_result->b_state |= (1UL << BH_Mapped); |
680 |
|
681 |
ASFS_INODE->ext_cache.startblock = pos; |
682 |
ASFS_INODE->ext_cache.key = extend.key; |
683 |
ASFS_INODE->ext_cache.next = extend.next; |
684 |
ASFS_INODE->ext_cache.blocks = extend.blocks; |
685 |
|
686 |
return 0; |
687 |
} |
688 |
|
689 |
static int asfs_readpage(struct file *file, struct page *page) |
690 |
{ |
691 |
return block_read_full_page(page, asfs_get_block); |
692 |
} |
693 |
static int asfs_bmap(struct address_space *mapping, long block) |
694 |
{ |
695 |
return generic_block_bmap(mapping,block,asfs_get_block); |
696 |
} |
697 |
|
698 |
/* Mapping from our types to the kernel */ |
699 |
|
700 |
static struct address_space_operations asfs_aops = { |
701 |
.readpage = asfs_readpage, |
702 |
.sync_page = block_sync_page, |
703 |
.bmap = asfs_bmap, |
704 |
}; |
705 |
|
706 |
struct file_operations asfs_file_operations = { |
707 |
.llseek = generic_file_llseek, |
708 |
.read = generic_file_read, |
709 |
.mmap = generic_file_mmap, |
710 |
}; |
711 |
|
712 |
static struct file_operations asfs_dir_operations = { |
713 |
.read = generic_read_dir, |
714 |
.readdir = asfs_readdir, |
715 |
}; |
716 |
|
717 |
static struct inode_operations asfs_dir_inode_operations = { |
718 |
.lookup = asfs_lookup, |
719 |
}; |
720 |
|
721 |
static int asfs_symlink_readpage(struct file *file, struct page *page) |
722 |
{ |
723 |
struct fsSoftLink *slinkcont; |
724 |
struct inode *inode = page->mapping->host; |
725 |
struct super_block *sb = inode->i_sb; |
726 |
char *link = kmap(page); |
727 |
int i = 0, j = 0; |
728 |
char c, lc = 0, *pf, *lf; |
729 |
|
730 |
slinkcont = kmalloc(sb->s_blocksize, GFP_KERNEL); |
731 |
|
732 |
if (asfs_get_contblock(sb, ASFS_INODE->firstblock, ASFS_SOFTLINK_ID, slinkcont) < 0) |
733 |
goto free_and_error; |
734 |
|
735 |
lf = slinkcont->string; |
736 |
pf = ASFS_SB->prefix ? ASFS_SB->prefix : "/"; |
737 |
|
738 |
if (strchr(lf,':')) { /* Handle assign or volume name */ |
739 |
while (i < 1023 && (c = pf[i])) |
740 |
link[i++] = c; |
741 |
while (i < 1023 && lf[j] != ':') |
742 |
link[i++] = lf[j++]; |
743 |
if (i < 1023) |
744 |
link[i++] = '/'; |
745 |
j++; |
746 |
lc = '/'; |
747 |
} |
748 |
while (i < 1023 && (c = lf[j])) { |
749 |
if (c == '/' && lc == '/' && i < 1020) { /* parent dir */ |
750 |
link[i++] = '.'; |
751 |
link[i++] = '.'; |
752 |
} |
753 |
link[i++] = c; |
754 |
lc = c; |
755 |
j++; |
756 |
} |
757 |
link[i] = '\0'; |
758 |
SetPageUptodate(page); |
759 |
kunmap(page); |
760 |
UnlockPage(page); |
761 |
kfree(slinkcont); |
762 |
return 0; |
763 |
free_and_error: |
764 |
kfree(slinkcont); |
765 |
SetPageError(page); |
766 |
kunmap(page); |
767 |
UnlockPage(page); |
768 |
return -EIO; |
769 |
} |
770 |
|
771 |
static struct address_space_operations asfs_symlink_aops = { |
772 |
.readpage = asfs_symlink_readpage, |
773 |
}; |
774 |
|
775 |
static void asfs_read_inode2(struct inode *inode, void *arg) |
776 |
{ |
777 |
struct super_block *sb = inode->i_sb; |
778 |
struct fsObject *obj = arg; |
779 |
|
780 |
inode->i_mode = ASFS_SB->mode; |
781 |
inode->i_mtime = inode->i_atime = inode->i_ctime = obj->datemodified + (365*8+2)*24*60*60; |
782 |
/* Linux: seconds since 01-01-1970, AmigaSFS: seconds since 01-01-1978 */ |
783 |
inode->i_uid = ASFS_SB->uid; |
784 |
inode->i_gid = ASFS_SB->gid; |
785 |
|
786 |
asfs_debug("asfs_read_inode2: Setting-up node %lu... ", inode->i_ino); |
787 |
|
788 |
if (obj->bits & OTYPE_DIR) { |
789 |
asfs_debug("dir (FirstdirBlock: %u, HashTable %u)\n", \ |
790 |
obj->object.dir.firstdirblock, obj->object.dir.hashtable); |
791 |
|
792 |
inode->i_size = 0; |
793 |
inode->i_op = &asfs_dir_inode_operations; |
794 |
inode->i_fop = &asfs_dir_operations; |
795 |
inode->i_mode |= S_IFDIR | ((inode->i_mode & 0400) ? 0100 : 0) | |
796 |
((inode->i_mode & 0040) ? 0010 : 0) | ((inode->i_mode & 0004) ? 0001 : 0); |
797 |
ASFS_INODE->firstblock = obj->object.dir.firstdirblock; |
798 |
ASFS_INODE->hashtable = obj->object.dir.hashtable; |
799 |
} else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK)) { |
800 |
asfs_debug("symlink\n"); |
801 |
inode->i_size = 0; |
802 |
inode->i_op = &page_symlink_inode_operations; |
803 |
inode->i_mapping->a_ops = &asfs_symlink_aops; |
804 |
inode->i_mode |= S_IFLNK | S_IRWXUGO; |
805 |
ASFS_INODE->firstblock = obj->object.file.data; |
806 |
} else { |
807 |
asfs_debug("file (Size: %u, FirstBlock: %u)\n", obj->object.file.size, obj->object.file.data); |
808 |
|
809 |
inode->i_size = obj->object.file.size; |
810 |
inode->i_blocks = (obj->object.file.size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; |
811 |
inode->i_fop = &asfs_file_operations; |
812 |
inode->i_mapping->a_ops = &asfs_aops; |
813 |
inode->i_mode |= S_IFREG; |
814 |
ASFS_INODE->firstblock = obj->object.file.data; |
815 |
ASFS_INODE->ext_cache.startblock = 0; |
816 |
ASFS_INODE->ext_cache.key = 0; |
817 |
} |
818 |
return; |
819 |
} |
820 |
|
821 |
static struct super_operations asfs_ops = { |
822 |
.read_inode2 = asfs_read_inode2, |
823 |
.put_super = asfs_put_super, |
824 |
.statfs = asfs_statfs, |
825 |
}; |
826 |
|
827 |
static DECLARE_FSTYPE_DEV(asfs_fs_type, "asfs", asfs_read_super); |
828 |
|
829 |
static int __init init_asfs_fs(void) |
830 |
{ |
831 |
return register_filesystem(&asfs_fs_type); |
832 |
} |
833 |
|
834 |
static void __exit exit_asfs_fs(void) |
835 |
{ |
836 |
unregister_filesystem(&asfs_fs_type); |
837 |
} |
838 |
|
839 |
/* Yes, works even as a module... :) */ |
840 |
|
841 |
EXPORT_NO_SYMBOLS; |
842 |
MODULE_DESCRIPTION("Amiga Smart File System support for Linux v0.6 (04.09.2003)"); |
843 |
MODULE_LICENSE("GPL"); |
844 |
|
845 |
module_init(init_asfs_fs) |
846 |
module_exit(exit_asfs_fs) |