diff -Naur linux-2.4.22-ppc-dev.orig/Documentation/Configure.help linux-2.4.22-ppc-dev/Documentation/Configure.help --- linux-2.4.22-ppc-dev.orig/Documentation/Configure.help 2003-08-27 15:23:58.000000000 +0200 +++ linux-2.4.22-ppc-dev/Documentation/Configure.help 2003-08-27 15:36:53.000000000 +0200 @@ -16295,6 +16295,26 @@ say M here and read . If unsure, say N. +Amiga SFS file system support +CONFIG_ASFS_FS + The Amiga Smart FileSystem (SFS) is the file system used on hard + disks by Amiga(tm) and MorphOS(tm) systems. Say Y if you want + to be able to read files from an Amiga SFS partition on your hard + drive. + + This file system driver is in EXPERIMENTAL state. Use it with care. + Although it CANNOT destroy your data (because it is a read-only + driver) it MIGHT cause a crash in some circumstances. + + For more information read + + This file system is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module is called affs.o. If you want to compile it as a module, + say M here and read . + + If unsure, say N. + Apple HFS file system support CONFIG_HFS_FS If you say Y here, you will be able to mount Macintosh-formatted diff -Naur linux-2.4.22-ppc-dev.orig/Documentation/filesystems/00-INDEX linux-2.4.22-ppc-dev/Documentation/filesystems/00-INDEX --- linux-2.4.22-ppc-dev.orig/Documentation/filesystems/00-INDEX 2002-11-29 00:53:08.000000000 +0100 +++ linux-2.4.22-ppc-dev/Documentation/filesystems/00-INDEX 2003-08-27 15:34:12.000000000 +0200 @@ -6,6 +6,8 @@ - info and mount options for the Acorn Advanced Disc Filing System. affs.txt - info and mount options for the Amiga Fast File System. +asfs.txt + - info and mount options for the Amiga Smart File System. befs.txt - info for the BeOS file system (BFS) bfs.txt diff -Naur linux-2.4.22-ppc-dev.orig/Documentation/filesystems/asfs.txt linux-2.4.22-ppc-dev/Documentation/filesystems/asfs.txt --- linux-2.4.22-ppc-dev.orig/Documentation/filesystems/asfs.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/Documentation/filesystems/asfs.txt 2003-08-27 15:34:12.000000000 +0200 @@ -0,0 +1,68 @@ + +Amiga SmartFileSystem, Linux implementation +=========================================== + +This is a simple read-only driver. It support reading files and directories. +Symbolic links (in AmigaOS called soft links) are also supported. Read notes +below about symlinks support. + + +Mount options for the ASFS +========================== + +setuid[=uid] + This sets the owner of all files and directories in the file + system to uid or the uid of the current user, respectively. + +setgid[=gid] + Same as above, but for gid. + +mode=mode + Sets the mode flags to the given (octal) value. Directories + will get an x permission if the corresponding r bit is set. + The default mode is 0444, which means all r bits are set + (for directories this means also that all x bits are set). + +prefix=path Path will be prefixed to every absolute path name of + symbolic links on an AFFS partition. Default = "/". + (See below.) + + +Symbolic links +============== + +Although the Amiga and Linux file systems resemble each other, there +are some, not always subtle, differences. One of them becomes apparent +with symbolic links. While Linux has a file system with exactly one +root directory, the Amiga has a separate root directory for each +file system (for example, partition, floppy disk, ...). With the Amiga, +these entities are called "volumes". They have symbolic names which +can be used to access them. Thus, symbolic links can point to a +different volume. ASFS turns the volume name into a directory name +and prepends the prefix path (see prefix option) to it. + +Example: +You mount all your Amiga partitions under /amiga/ (where + is the name of the volume), and you give the option +"prefix=/amiga/" when mounting all your AFFS partitions. (They +might be "User", "WB" and "Graphics", the mount points /amiga/User, +/amiga/WB and /amiga/Graphics). A symbolic link referring to +"User:sc/include/dos/dos.h" will be followed to +"/amiga/User/sc/include/dos/dos.h". + + +Other information +================= + +Supported block sizes are: 512, 1024, 2048 and 4096 bytes. Larger blocks +speed up almost everything at the expense of wasted disk space. The speed +gain above 4K seems not really worth the price, so you don't lose too +much here, either. + +This file system hasn't been well tested yet. Athough it CANNOT destroy +your data, it MIGHT cause a crash. Sorry. + +This file system has been tested on Motorola PPC and 68k, as well as +Intel x86 systems. I don't know, if it works on other Linux systems. + +Some parts of this documentation has been adapted from AFFS driver docs. diff -Naur linux-2.4.22-ppc-dev.orig/arch/ppc64/defconfig linux-2.4.22-ppc-dev/arch/ppc64/defconfig --- linux-2.4.22-ppc-dev.orig/arch/ppc64/defconfig 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc64/defconfig 2003-08-27 15:34:12.000000000 +0200 @@ -585,6 +585,8 @@ # CONFIG_ADFS_FS is not set # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set +CONFIG_AFFS_FS=y +CONFIG_ASFS_FS=y # CONFIG_HFS_FS is not set # CONFIG_BEFS_FS is not set # CONFIG_BEFS_DEBUG is not set diff -Naur linux-2.4.22-ppc-dev.orig/fs/Config.in linux-2.4.22-ppc-dev/fs/Config.in --- linux-2.4.22-ppc-dev.orig/fs/Config.in 2003-08-27 15:18:03.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/Config.in 2003-08-27 15:35:03.000000000 +0200 @@ -19,6 +19,8 @@ dep_tristate 'Amiga FFS file system support (EXPERIMENTAL)' CONFIG_AFFS_FS $CONFIG_EXPERIMENTAL +dep_tristate 'Amiga SFS file system support (read-only) (EXPERIMENTAL)' CONFIG_ASFS_FS $CONFIG_EXPERIMENTAL + dep_tristate 'Apple HFS file system support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL dep_tristate 'Apple HFS+ (Extended HFS) file system support (EXPERIMENTAL)' CONFIG_HFSPLUS_FS $CONFIG_EXPERIMENTAL diff -Naur linux-2.4.22-ppc-dev.orig/fs/Makefile linux-2.4.22-ppc-dev/fs/Makefile --- linux-2.4.22-ppc-dev.orig/fs/Makefile 2003-08-27 15:18:03.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/Makefile 2003-08-27 15:34:12.000000000 +0200 @@ -55,6 +55,7 @@ subdir-$(CONFIG_JFFS_FS) += jffs subdir-$(CONFIG_JFFS2_FS) += jffs2 subdir-$(CONFIG_AFFS_FS) += affs +subdir-$(CONFIG_ASFS_FS) += asfs subdir-$(CONFIG_ROMFS_FS) += romfs subdir-$(CONFIG_QNX4FS_FS) += qnx4 subdir-$(CONFIG_UDF_FS) += udf diff -Naur linux-2.4.22-ppc-dev.orig/fs/asfs/Makefile linux-2.4.22-ppc-dev/fs/asfs/Makefile --- linux-2.4.22-ppc-dev.orig/fs/asfs/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/asfs/Makefile 2003-08-27 15:34:12.000000000 +0200 @@ -0,0 +1,15 @@ +# +# Makefile for the linux asfs filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile. + +O_TARGET := asfs.o + +obj-y := inode.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -Naur linux-2.4.22-ppc-dev.orig/fs/asfs/inode.c linux-2.4.22-ppc-dev/fs/asfs/inode.c --- linux-2.4.22-ppc-dev.orig/fs/asfs/inode.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/asfs/inode.c 2003-08-27 15:34:12.000000000 +0200 @@ -0,0 +1,775 @@ +/* + * + * Amiga Smart File System, Linux implementation + * + * version: 0.5 (19.07.2003) + * + * Copyright (C) 2003 Marek 'March' Szyprowski + * + * + * Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help. + * + * Based on the Linux implementation of the ROMFS file system + * Copyright (C) 1997-1999 Janos Farkas + * + * Using parts of the Amiga FFS filesystem + * Copyright (C) 1993 Ray Burr + * Copyright (C) 1996 Hans-Joachim Widmaier + * + * and parts of the smbfs filesystem additionally + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * History: + * + * v0.5 (19.07.2003) + * - added simple but effective extend caching - real speed-up + * in reading large files + * - added read support for symlinks - based on AFFS symlinks + * + * v0.4 (10.07.2003) + * - third code clean-up (thanks to Roman Zippel for advice) + * - now uses generic readpage and readinode routines + * + * v0.3beta (17.06.2003) + * - second code clean-up + * + * v0.2beta2 (15.06.2003) + * - fixed yet another stupid bug - driver can't read root block on little-endian systems + * v0.2beta (15.06.2003) + * - fixed stupid bug - now files have 'file' flag (S_IFREG) set... + * - added mount options to set uid, gid and mode of files and dirs + * - made hidden files & dirs really hidden (= not listed in directories) + * - code clean-up + * + * v0.1beta (11.06.2003) + * - after many kernel crashes, finally got it! + * - first working read-only filesystem driver + * + */ + +/* todo: + * - remove bugs + * - considering write access... + * - speed-up entry lookup (use HashTables...) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u32 asfs_calcchecksum(struct fsBlockHeader *block, u32 blocksize) +{ + u32 *data=(void *)block, checksum = 1, tmpchecksum = block->checksum; + + block->checksum=0; + while(blocksize > 0) { + checksum+=be32_to_cpu(*data++); + blocksize-=4; + } + block->checksum=tmpchecksum; + return(-checksum); +} + +static inline int +asfs_check_block(struct fsBlockHeader *block, u32 blocksize, u32 n) +{ + if (asfs_calcchecksum((struct fsBlockHeader *)block, blocksize) == + be32_to_cpu(((struct fsBlockHeader *)block)->checksum) && + n == be32_to_cpu(((struct fsBlockHeader *)block)->ownblock)) + return 1; + return 0; +} + +/* get fs structure from block and do some checks... */ +static int asfs_get_contblock(struct super_block *sb, u32 n, void *dest) +{ + struct buffer_head *bh; + + bh = sb_bread(sb, n); + if (!bh) + return -1; /* error */ + memcpy(dest, ((char *)bh->b_data), sb->s_blocksize); + brelse(bh); + + if (asfs_check_block(dest, sb->s_blocksize, n)) { + from32be(((struct fsBlockHeader *)dest)->ownblock); + return 1; /* all okay */ + } + return -1; /* error */ +} + +static inline struct inode *asfs_get_root_inode(struct super_block *sb) +{ + struct inode *result = NULL; + void *block; + struct fsObject *obj; + + asfs_debug("asfs_get_root_inode\n"); + + block = kmalloc(sb->s_blocksize, GFP_KERNEL); + if (asfs_get_contblock(sb, sb->u.asfs_sb.rootobjectcontainer, block) < 0) + goto free_and_return; + + obj = &(((struct fsObjectContainer *)block)->object[0]); + from32be(obj->objectnode); + from32be(obj->object.dir.firstdirblock); + + if (obj->objectnode > 0) + result = iget4(sb, obj->objectnode, NULL, obj); + +free_and_return: + kfree(block); + return result; +} + +static struct super_operations asfs_ops; + +static int asfs_parse_options(char *options, struct super_block *sb) +{ + char *this_char, *value, *optn; + int f; + + /* Fill in defaults */ + ASFS_SB->uid = ASFS_DEFAULT_UID; + ASFS_SB->gid = ASFS_DEFAULT_GID; + ASFS_SB->mode = ASFS_DEFAULT_MODE; + ASFS_SB->prefix = NULL; + + if (!options) + return 1; + for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { + f = 0; + if ((value = strchr(this_char,'=')) != NULL) + *value++ = 0; + if ((f = !strcmp(this_char,"setuid")) || !strcmp(this_char,"setgid")) { + if (value) { + if (!*value) { + printk("ASFS: Argument for set[ug]id option missing\n"); + return 0; + } else { + (f ? ASFS_SB->uid : ASFS_SB->gid) = simple_strtoul(value,&value,0); + if (*value) { + printk("ASFS: Bad set[ug]id argument\n"); + return 0; + } + } + } + } else if (!strcmp(this_char,"mode")) { + optn = "mode"; + if (!value || !*value) + goto out_no_arg; + ASFS_SB->mode = simple_strtoul(value,&value,8) & 0777; + if (*value) + return 0; + } else if (!strcmp(this_char,"prefix")) { + optn = "prefix"; + if (!value || !*value) + goto out_no_arg; + ASFS_SB->prefix = kmalloc(strlen(value) + 1,GFP_KERNEL); + if (!ASFS_SB->prefix) + return 0; + strcpy(ASFS_SB->prefix,value); + } else { + printk("ASFS: Unrecognized mount option %s\n", this_char); + return 0; + } + } + return 1; + +out_no_arg: + printk("ASFS: The %s option requires an argument\n", optn); + return 0; +} + +static struct super_block * +asfs_read_super(struct super_block *sb, void *data, int silent) +{ + struct buffer_head *bh; + kdev_t dev = sb->s_dev; + struct fsRootBlock *rootblock; + + if (!asfs_parse_options(data, sb)) { + printk(KERN_ERR "ASFS: Error parsing options\n"); + return NULL; + } + + if (!sb_set_blocksize(sb, 512)) + return NULL; + sb->s_maxbytes = 0xFFFFFFFF; + + bh = sb_bread(sb, 0); + if (!bh) { + printk(KERN_ERR "ASFS: unable to read superblock\n"); + goto outnobh; + } + + rootblock = (struct fsRootBlock *)bh->b_data; + + if (be32_to_cpu(rootblock->bheader.id) == ASFS_ROOTID && + be16_to_cpu(rootblock->version) == ASFS_STRUCTURE_VERISON) { + + sb->s_blocksize = be32_to_cpu(rootblock->blocksize); + ASFS_SB->totalblocks = be32_to_cpu(rootblock->totalblocks); + ASFS_SB->rootobjectcontainer = be32_to_cpu(rootblock->rootobjectcontainer); + ASFS_SB->extentbnoderoot = be32_to_cpu(rootblock->extentbnoderoot); + + brelse(bh); + + if (!sb_set_blocksize(sb, sb->s_blocksize)) { + printk(KERN_ERR "ASFS: Found Amiga SFS RootBlock on dev %s, but blocksize %ld is not supported!\n", \ + bdevname(dev), sb->s_blocksize); + return NULL; + } + + bh = sb_bread(sb, 0); + if (!bh) { + printk(KERN_ERR "ASFS: unable to read superblock\n"); + goto out; + } + rootblock = (struct fsRootBlock *)bh->b_data; + + if (asfs_check_block((void *)rootblock, sb->s_blocksize, 0)) + printk(KERN_NOTICE "ASFS: Found Amiga SFS RootBlock on dev %s. Checksum okay. Mounting...\n", \ + bdevname(dev)); + else { + if (!silent) + printk(KERN_ERR "ASFS: Found Amiga SFS RootBlock on dev %s, but it has checksum error!\n", \ + bdevname(dev)); + goto out; + } + } else { + if (!silent) + printk(KERN_ERR "ASFS: Can't find a valid Amiga SFS filesystem on dev %s.\n", \ + bdevname(dev)); + goto out; + } + + brelse(bh); + + sb->s_magic = ASFS_MAGIC; + sb->s_flags |= MS_RDONLY | MS_NODEV | MS_NOSUID; + sb->s_op = &asfs_ops; + sb->s_root = d_alloc_root(asfs_get_root_inode(sb)); + if (!sb->s_root) + goto outnobh; + + /* Ehrhm; sorry.. :) */ + if (0) { +out: + brelse(bh); +outnobh: + sb = NULL; + } + return sb; +} + +static void asfs_put_super(struct super_block *sb) +{ + if (ASFS_SB->prefix) + kfree(ASFS_SB->prefix); + return; +} + +/* That's simple too. */ + +static int asfs_statfs(struct super_block *sb, struct statfs *buf) +{ + buf->f_type = ASFS_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_bfree = buf->f_bavail = buf->f_ffree; + buf->f_blocks = ASFS_SB->totalblocks; + buf->f_namelen = ASFS_MAXFN; + return 0; +} + +static int asfs_get_fsObject_varlen(struct fsObject *obj) +{ + int len, i; + u8 *p = obj->name; + for (i=2; i > 0; p++) + if (*p == '\0') + i--; + len = (p - (u8 *)obj); + if (len & 1) + len++; + return len; +} + +static int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *dir = filp->f_dentry->d_inode; + struct super_block *sb = dir->i_sb; + unsigned long f_pos; + int stored = 0; + int obj_skip; + + struct fsObjectContainer *objcont; + struct fsObject *obj; + int i; + unsigned long block; + + asfs_debug("asfs_readdir:\n"); + + if (filp->f_pos == ASFS_SB->totalblocks) + return stored; + + f_pos = filp->f_pos; + + if (f_pos == 0) { + filp->private_data = (void *)0; + if (filldir(dirent, ".", 1, f_pos, dir->i_ino, DT_DIR) < 0) + return 0; + filp->f_pos = f_pos = 1; + stored++; + } + if (f_pos == 1) { + if (filldir(dirent, "..", 2, f_pos, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) + return stored; + filp->f_pos = f_pos = 2; + stored++; + } + + if (ASFS_DIR->firstblock == 0) { /* empty directory */ + filp->f_pos = ASFS_SB->totalblocks; + return stored; + } + + objcont = kmalloc(sb->s_blocksize, GFP_KERNEL); + + if (f_pos == 2) { /* reading directory from its beginning */ + block = ASFS_DIR->firstblock; + do { + if (asfs_get_contblock(sb, block, (void *)objcont) < 0) + goto free_and_end; + from32be(objcont->previous); + from32be(objcont->next); + block = objcont->previous; + } while (objcont->previous != 0); + obj_skip = 0; + } else { + block = f_pos; + if (asfs_get_contblock(sb, block, (void *)objcont) < 0) + goto free_and_end; + from32be(objcont->previous); + from32be(objcont->next); + obj_skip = (int)filp->private_data; + } + + i=0; + block = ((struct fsBlockHeader *)objcont)->ownblock; + + do { + if (i != 0) { + if (asfs_get_contblock(sb, block, (void *)objcont) < 0) + goto free_and_end; + from32be(objcont->previous); + from32be(objcont->next); + } + + i = 0; + obj = &(objcont->object[0]); + from32be(obj->objectnode); + + while (obj->objectnode > 0 && + ((char *)obj - (char *)objcont) + sizeof(struct fsObject) < sb->s_blocksize) { + if (obj_skip > 0) + obj_skip--; + else if (!(obj->bits & OTYPE_HIDDEN)) { + unsigned int type; + asfs_debug("ASFS: DirFilling: entry #%d (in_cont: %d) \"%s\" (node %lu offset %lu), type %d\n", \ + stored, i, obj->name, (unsigned long) obj->objectnode, block, obj->bits); + filp->f_pos = block; + if (obj->bits & OTYPE_DIR) + type = DT_DIR; + else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK)) + type = DT_LNK; + else + type = DT_REG; + if (filldir(dirent, obj->name, strlen(obj->name), block, obj->objectnode, type) < 0) { + filp->private_data = (void *)i; + asfs_debug("ASFS: DirFilling: to be continued...\n"); + goto free_and_end; + } + stored++; + } + obj = (struct fsObject *)((char *)(obj) + asfs_get_fsObject_varlen(obj)); + i++; + } + block = objcont->next; + + } while (objcont->next != 0); + + filp->f_pos = ASFS_SB->totalblocks; + +free_and_end: + kfree(objcont); + return stored; +} + +static inline u8 upperchar(u8 c) +{ + if((c>=224 && c<=254 && c!=247) || (c>='a' && c<='z')) + c-=32; + return(c); +} + +static int namecmp(u8 *s, u8 *ct) +{ + while (upperchar(*s) == upperchar(*ct) && *ct != '\0' && *ct != '/') { + s++; ct++; + } + return (*s == '\0' && (*ct == '\0' || *ct == '/')) ? 0 : *ct - *s; +} + +static struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry) +{ + int res; + struct inode *inode; + unsigned char *name; /* got from dentry */ + + struct fsObjectContainer *objcont; + struct fsObject *obj; + int i; + unsigned long block; + + name = (unsigned char *)dentry->d_name.name; + res = -EACCES; /* placeholder for "no data here" */ + + asfs_debug("asfs_lookup: (searching \"%s\"...) ", name); + + objcont = kmalloc(dir->i_sb->s_blocksize, GFP_KERNEL); + + block = ASFS_DIR->firstblock; + + do { + if (asfs_get_contblock(dir->i_sb, block, (void *)objcont) < 0) + goto free_and_error; + from32be(objcont->previous); + from32be(objcont->next); + block = objcont->previous; + } while (objcont->previous != 0); + + i=0; + block = ((struct fsBlockHeader *)objcont)->ownblock; + + do { + if (i != 0) { + if (asfs_get_contblock(dir->i_sb, block, (void *)objcont) < 0) + goto free_and_error; + from32be(objcont->previous); + from32be(objcont->next); + } + + i = 0; + obj = &(objcont->object[0]); + while (be32_to_cpu(obj->objectnode) > 0 && + ((char *)obj - (char *)objcont) + sizeof(struct fsObject) < dir->i_sb->s_blocksize) { + if (namecmp(obj->name, name) == 0) { + from32be(obj->objectnode); + /* these two below also convert file.size and file.firstdatablock */ + from32be(obj->object.dir.firstdirblock); + from32be(obj->object.dir.hashtable); + from32be(obj->datemodified); + asfs_debug("Object found! #%d: Node %u, Name %s, Type: %d, inCont %lu\n", \ + i, obj->objectnode, obj->name, obj->bits, block); + + if ((inode = iget4(dir->i_sb, obj->objectnode, NULL, obj))) + goto found_inode; + + asfs_debug("ASFS: Strange - no inode allocated... :(\n"); + goto free_and_error; + } + obj = (struct fsObject *)((char *)(obj) + asfs_get_fsObject_varlen(obj)); + i++; + } + block = objcont->next; + + } while (objcont->next != 0); + + /* + * it's a bit funky, _lookup needs to return an error code + * (negative) or a NULL, both as a dentry. ENOENT should not + * be returned, instead we need to create a negative dentry by + * d_add(dentry, NULL); and return 0 as no error. + * (Although as I see, it only matters on writable file + * systems). + */ + + inode = NULL; + asfs_debug("ASFS: object not found.\n"); +found_inode: + res = 0; + d_add(dentry, inode); +free_and_error: + kfree(objcont); + return ERR_PTR(res); +} + +static struct fsExtentBNode * +asfs_search_BNodeTree(struct super_block *sb, u32 key, struct fsExtentBNode *dest) +{ + struct fsBNodeContainer *bnodecont; + struct fsExtentBNode *result = dest; + unsigned long block = ASFS_SB->extentbnoderoot; + + bnodecont = kmalloc(sb->s_blocksize, GFP_KERNEL); + while (asfs_get_contblock(sb, block, bnodecont) > 0) { + from16be(bnodecont->btc.nodecount); + if (bnodecont->btc.isleaf) { + int i; + struct fsExtentBNode *exbnode; + exbnode = (void *)bnodecont->btc.bnode; + for (i=0; i < bnodecont->btc.nodecount; i++) { + from32be(exbnode->key); + from32be(exbnode->next); + from32be(exbnode->prev); + from16be(exbnode->blocks); + if (exbnode->key == key) { + *dest = *exbnode; + goto found; + } + exbnode = (void *)exbnode + bnodecont->btc.nodesize; + } + } else { + int i; + struct BNode *bnode; + bnode = (void *)bnodecont->btc.bnode; + for (i=0; i < bnodecont->btc.nodecount && key >= be32_to_cpu(bnode->key); i++) + bnode = (void *)bnode + bnodecont->btc.nodesize; + + bnode = (void *)bnode - bnodecont->btc.nodesize; + block = be32_to_cpu(bnode->data); + } + } + /* read error or key not found */ + result = NULL; +found: + kfree(bnodecont); + return result; +} + +static int +asfs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create) +{ + struct fsExtentBNode extend; + u32 filedata; + unsigned long pos; + + asfs_debug("ASFS: get_block(%lu, %ld)\n", inode->i_ino, block); + + if (block < 0) { + printk(KERN_ERR "ASFS: asfsget_block: requested block (%ld) < 0!\n", block); + return -EIO; + } + + if (block >= inode->i_blocks) { + printk(KERN_ERR "ASFS: asfsget_block: strange block request %ld!\n", block); + return -EIO; + } + + if (ASFS_INODE->ext_cache.key > 0 && ASFS_INODE->ext_cache.startblock <= block) { + extend.key = ASFS_INODE->ext_cache.key; + extend.next = ASFS_INODE->ext_cache.next; + extend.blocks = ASFS_INODE->ext_cache.blocks; + pos = ASFS_INODE->ext_cache.startblock; + } else { + if (asfs_search_BNodeTree(inode->i_sb, ASFS_INODE->firstblock, &extend) == NULL) + return -EIO; + pos = 0; + } + filedata = extend.next; + + while (pos + extend.blocks <= block && extend.next != 0 && pos < inode->i_blocks) { + pos += extend.blocks; + if (asfs_search_BNodeTree(inode->i_sb, filedata, &extend) == NULL) + return -EIO; + filedata = extend.next; + } + + bh_result->b_blocknr = extend.key + block - pos; + bh_result->b_dev = inode->i_dev; + bh_result->b_state |= (1UL << BH_Mapped); + + ASFS_INODE->ext_cache.startblock = pos; + ASFS_INODE->ext_cache.key = extend.key; + ASFS_INODE->ext_cache.next = extend.next; + ASFS_INODE->ext_cache.blocks = extend.blocks; + + return 0; +} + +static int asfs_readpage(struct file *file, struct page *page) +{ + return block_read_full_page(page, asfs_get_block); +} +static int asfs_bmap(struct address_space *mapping, long block) +{ + return generic_block_bmap(mapping,block,asfs_get_block); +} + +/* Mapping from our types to the kernel */ + +static struct address_space_operations asfs_aops = { + .readpage = asfs_readpage, + .sync_page = block_sync_page, + .bmap = asfs_bmap, +}; + +struct file_operations asfs_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .mmap = generic_file_mmap, +}; + +static struct file_operations asfs_dir_operations = { + .read = generic_read_dir, + .readdir = asfs_readdir, +}; + +static struct inode_operations asfs_dir_inode_operations = { + .lookup = asfs_lookup, +}; + +static int asfs_symlink_readpage(struct file *file, struct page *page) +{ + struct fsSoftLink *slinkcont; + struct inode *inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + char *link = kmap(page); + int i = 0, j = 0; + char c, lc = 0, *pf, *lf; + + slinkcont = kmalloc(sb->s_blocksize, GFP_KERNEL); + + if (asfs_get_contblock(sb, ASFS_INODE->firstblock, (void *)slinkcont) < 0) + goto free_and_error; + + lf = slinkcont->string; + pf = ASFS_SB->prefix ? ASFS_SB->prefix : "/"; + + if (strchr(lf,':')) { /* Handle assign or volume name */ + while (i < 1023 && (c = pf[i])) + link[i++] = c; + while (i < 1023 && lf[j] != ':') + link[i++] = lf[j++]; + if (i < 1023) + link[i++] = '/'; + j++; + lc = '/'; + } + while (i < 1023 && (c = lf[j])) { + if (c == '/' && lc == '/' && i < 1020) { /* parent dir */ + link[i++] = '.'; + link[i++] = '.'; + } + link[i++] = c; + lc = c; + j++; + } + link[i] = '\0'; + SetPageUptodate(page); + kunmap(page); + UnlockPage(page); + kfree(slinkcont); + return 0; +free_and_error: + kfree(slinkcont); + SetPageError(page); + kunmap(page); + UnlockPage(page); + return -EIO; +} + +static struct address_space_operations asfs_symlink_aops = { + .readpage = asfs_symlink_readpage, +}; + +static void asfs_read_inode2(struct inode *inode, void *arg) +{ + struct super_block *sb = inode->i_sb; + struct fsObject *obj = arg; + + inode->i_mode = ASFS_SB->mode; + inode->i_mtime = inode->i_atime = inode->i_ctime = obj->datemodified + (365*8+2)*24*60*60; + /* Linux: seconds since 01-01-1970, AmigaSFS: seconds since 01-01-1978 */ + inode->i_uid = ASFS_SB->uid; + inode->i_gid = ASFS_SB->gid; + + asfs_debug("asfs_read_inode2: Setting-up node %lu... ", inode->i_ino); + + if (obj->bits & OTYPE_DIR) { + asfs_debug("dir (FirstdirBlock: %u, HashTable %u)\n", \ + obj->object.dir.firstdirblock, obj->object.dir.hashtable); + + inode->i_size = 0; + inode->i_op = &asfs_dir_inode_operations; + inode->i_fop = &asfs_dir_operations; + inode->i_mode |= S_IFDIR | ((inode->i_mode & 0400) ? 0100 : 0) | + ((inode->i_mode & 0040) ? 0010 : 0) | ((inode->i_mode & 0004) ? 0001 : 0); + ASFS_INODE->firstblock = obj->object.dir.firstdirblock; + ASFS_INODE->hashtable = obj->object.dir.hashtable; + } else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK)) { + asfs_debug("symlink\n"); + inode->i_size = 0; + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &asfs_symlink_aops; + inode->i_mode |= S_IFLNK | S_IRWXUGO; + ASFS_INODE->firstblock = obj->object.file.data; + } else { + asfs_debug("file (Size: %u, FirstBlock: %u)\n", obj->object.file.size, obj->object.file.data); + + inode->i_size = obj->object.file.size; + inode->i_blocks = (obj->object.file.size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + inode->i_fop = &asfs_file_operations; + inode->i_mapping->a_ops = &asfs_aops; + inode->i_mode |= S_IFREG; + ASFS_INODE->firstblock = obj->object.file.data; + ASFS_INODE->ext_cache.startblock = 0; + ASFS_INODE->ext_cache.key = 0; + } + return; +} + +static struct super_operations asfs_ops = { + .read_inode2 = asfs_read_inode2, + .put_super = asfs_put_super, + .statfs = asfs_statfs, +}; + +static DECLARE_FSTYPE_DEV(asfs_fs_type, "asfs", asfs_read_super); + +static int __init init_asfs_fs(void) +{ + return register_filesystem(&asfs_fs_type); +} + +static void __exit exit_asfs_fs(void) +{ + unregister_filesystem(&asfs_fs_type); +} + +/* Yes, works even as a module... :) */ + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("Amiga Smart File System support for Linux"); +MODULE_LICENSE("GPL"); + +module_init(init_asfs_fs) +module_exit(exit_asfs_fs) diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/asfs_fs.h linux-2.4.22-ppc-dev/include/linux/asfs_fs.h --- linux-2.4.22-ppc-dev.orig/include/linux/asfs_fs.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/asfs_fs.h 2003-08-27 15:34:12.000000000 +0200 @@ -0,0 +1,174 @@ +#ifndef __LINUX_ASFS_FS_H +#define __LINUX_ASFS_FS_H + +#include +#include + +#define asfs_debug(fmt,arg...) pr_debug(fmt,##arg) +//#define asfs_debug(fmt,arg...) printk(fmt,##arg) + +/* some helper macros... */ + +#define from32be(t) ((t)=be32_to_cpu(t)) +#define from16be(t) ((t)=be16_to_cpu(t)) + +#define ASFS_MAKE_ID(a,b,c,d) (((a)&0xff)<<24|((b)&0xff)<<16|((c)&0xff)<<8|((d)&0xff)) + +/* Amiga SFS defines */ + +#define ASFS_MAGIC 0xa0ff +#define ASFS_MAXFN 105 + +#define ASFS_ROOTID ASFS_MAKE_ID('S','F','S',0) + +#define ASFS_DEFAULT_UID 0 +#define ASFS_DEFAULT_GID 0 +#define ASFS_DEFAULT_MODE 0444 /* default permission bits for files, dirs have same permission, but with "x" set */ + +#define ASFS_STRUCTURE_VERISON (3) + +#define OTYPE_HIDDEN (1) +#define OTYPE_HARDLINK (32) +#define OTYPE_LINK (64) +#define OTYPE_DIR (128) + +#define MSB_MASK (1ul << 31) + +/* Each block has its own header with checksum and id, its called fsBlockHeader */ + +struct fsBlockHeader { + u32 id; /* 4 character id string of this block */ + u32 checksum; /* The checksum */ + u32 ownblock; /* The blocknumber of the block this block is stored at */ +}; + +/* On-disk "super block", called fsRootBlock */ + +struct fsRootBlock { + struct fsBlockHeader bheader; + + u16 version; /* Version number of the filesystem block structure */ + u16 sequencenumber; /* The Root with the highest sequencenumber is valid */ + + u32 datecreated; /* Creation date (when first formatted). Cannot be changed. */ + u8 bits; /* various settings, see defines below. */ + u8 pad1; + u16 pad2; + + u32 reserved1[2]; + + u32 firstbyteh; /* The first byte of our partition from the start of the */ + u32 firstbyte; /* disk. firstbyteh = upper 32 bits, firstbyte = lower 32 bits. */ + + u32 lastbyteh; /* The last byte of our partition, excluding this one. */ + u32 lastbyte; + + u32 totalblocks; /* size of this partition in blocks */ + u32 blocksize; /* blocksize used */ + + u32 reserved2[2]; + u32 reserved3[8]; + + u32 bitmapbase; /* location of the bitmap */ + u32 adminspacecontainer; /* location of first adminspace container */ + u32 rootobjectcontainer; /* location of the root objectcontainer */ + u32 extentbnoderoot; /* location of the root of the extentbnode B-tree */ + u32 objectnoderoot; /* location of the root of the objectnode tree */ + + u32 reserved4[3]; +}; + +/* On disk inode, called fsObject */ + +struct fsObject { + u16 owneruid; + u16 ownergid; + u32 objectnode; + u32 protection; + + union { + struct { + u32 data; + u32 size; + } file; + + struct { + u32 hashtable; /* for directories & root, 0 means no hashblock */ + u32 firstdirblock; + } dir; + } object; + + u32 datemodified; + u8 bits; + + u8 name[0]; + u8 comment[0]; +}; + +/* On disk block containging a number of fsObjects */ + +struct fsObjectContainer { + struct fsBlockHeader bheader; + + u32 parent; + u32 next; + u32 previous; /* 0 for the first block in the directory chain */ + + struct fsObject object[0]; +}; + +/* BTree structures, used to collect file data position on disk */ + +struct fsExtentBNode { + u32 key; /* data! */ + u32 next; + u32 prev; + u16 blocks; /* The size in blocks of the region this Extent controls */ +}; + +struct BNode { + u32 key; + u32 data; +}; + +struct BTreeContainer { + u16 nodecount; + u8 isleaf; + u8 nodesize; /* Must be a multiple of 2 */ + + struct BNode bnode[0]; +}; + +/* On disk block with BTreeContainer */ + +struct fsBNodeContainer { + struct fsBlockHeader bheader; + struct BTreeContainer btc; +}; + +/* On disk block with soft link data */ + +struct fsSoftLink { + struct fsBlockHeader bheader; + u32 parent; + u32 next; + u32 previous; + u8 string[0]; +}; + +/* Extent structure located in RAM (e.g. inside inode structure), + currently used to store last used extent */ + +struct inramExtent { + u32 startblock; /* Block from begginig of the file */ + u32 key; + u32 next; + u16 blocks; +}; + +#ifdef __KERNEL__ + +/* Not much now */ + +#endif /* __KERNEL__ */ +#endif diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/asfs_fs_i.h linux-2.4.22-ppc-dev/include/linux/asfs_fs_i.h --- linux-2.4.22-ppc-dev.orig/include/linux/asfs_fs_i.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/asfs_fs_i.h 2003-08-27 15:34:12.000000000 +0200 @@ -0,0 +1,16 @@ +#ifndef __ASFS_FS_I +#define __ASFS_FS_I + +#include + +/* inode in-kernel data */ + +struct asfs_inode_info { + u32 firstblock; + u32 hashtable; + struct inramExtent ext_cache; +}; + +#define ASFS_INODE (&inode->u.asfs_i) +#define ASFS_DIR (&dir->u.asfs_i) +#endif diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/asfs_fs_sb.h linux-2.4.22-ppc-dev/include/linux/asfs_fs_sb.h --- linux-2.4.22-ppc-dev.orig/include/linux/asfs_fs_sb.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/asfs_fs_sb.h 2003-08-27 15:34:12.000000000 +0200 @@ -0,0 +1,18 @@ +#ifndef __ASFS_FS_SB +#define __ASFS_FS_SB + +/* Amiga SFS superblock in-core data */ + +struct asfs_sb_info { + u32 totalblocks; + u32 rootobjectcontainer; + u32 extentbnoderoot; + uid_t uid; + gid_t gid; + umode_t mode; + char *prefix; +}; + +#define ASFS_SB (&sb->u.asfs_sb) + +#endif diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/fs.h linux-2.4.22-ppc-dev/include/linux/fs.h --- linux-2.4.22-ppc-dev.orig/include/linux/fs.h 2003-08-27 15:24:00.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/fs.h 2003-08-27 15:34:12.000000000 +0200 @@ -305,6 +305,7 @@ #include #include #include +#include #include #include #include @@ -500,6 +501,7 @@ struct nfs_inode_info nfs_i; struct sysv_inode_info sysv_i; struct affs_inode_info affs_i; + struct asfs_inode_info asfs_i; struct ufs_inode_info ufs_i; struct efs_inode_info efs_i; struct romfs_inode_info romfs_i; @@ -717,6 +719,7 @@ #include #include #include +#include #include #include #include @@ -775,6 +778,7 @@ struct nfs_sb_info nfs_sb; struct sysv_sb_info sysv_sb; struct affs_sb_info affs_sb; + struct asfs_sb_info asfs_sb; struct ufs_sb_info ufs_sb; struct efs_sb_info efs_sb; struct shmem_sb_info shmem_sb;