diff -urN linux-2.4.21/Documentation/Configure.help linux-2.4.21-asfs-0.6/Documentation/Configure.help --- linux-2.4.21/Documentation/Configure.help Fri Jun 13 16:51:29 2003 +++ linux-2.4.21-asfs-0.6/Documentation/Configure.help Sat Jul 12 14:14:39 2003 @@ -15959,6 +15959,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 Macintosh file system support CONFIG_HFS_FS If you say Y here, you will be able to mount Macintosh-formatted diff -urN linux-2.4.21/Documentation/filesystems/00-INDEX linux-2.4.21-asfs-0.6/Documentation/filesystems/00-INDEX --- linux-2.4.21/Documentation/filesystems/00-INDEX Fri Nov 29 00:53:08 2002 +++ linux-2.4.21-asfs-0.6/Documentation/filesystems/00-INDEX Sat Jul 12 14:14:39 2003 @@ -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 -urN linux-2.4.21/Documentation/filesystems/asfs.txt linux-2.4.21-asfs-0.6/Documentation/filesystems/asfs.txt --- linux-2.4.21/Documentation/filesystems/asfs.txt Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-asfs-0.6/Documentation/filesystems/asfs.txt Tue Sep 2 14:01:07 2003 @@ -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 ASFS or 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 -urN linux-2.4.21/fs/Config.in linux-2.4.21-asfs-0.6/fs/Config.in --- linux-2.4.21/fs/Config.in Fri Nov 29 00:53:15 2002 +++ linux-2.4.21-asfs-0.6/fs/Config.in Sat Jul 12 14:14:39 2003 @@ -17,6 +17,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 Macintosh file system support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL dep_tristate 'BeOS file systemv(BeFS) support (read only) (EXPERIMENTAL)' CONFIG_BEFS_FS $CONFIG_EXPERIMENTAL diff -urN linux-2.4.21/fs/Makefile linux-2.4.21-asfs-0.6/fs/Makefile --- linux-2.4.21/fs/Makefile Fri Nov 29 00:53:15 2002 +++ linux-2.4.21-asfs-0.6/fs/Makefile Sat Jul 12 14:14:39 2003 @@ -57,6 +57,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 -urN linux-2.4.21/fs/asfs/Makefile linux-2.4.21-asfs-0.6/fs/asfs/Makefile --- linux-2.4.21/fs/asfs/Makefile Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-asfs-0.6/fs/asfs/Makefile Sat Jul 12 14:14:39 2003 @@ -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 -urN linux-2.4.21/fs/asfs/inode.c linux-2.4.21-asfs-0.6/fs/asfs/inode.c --- linux-2.4.21/fs/asfs/inode.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-asfs-0.6/fs/asfs/inode.c Thu Sep 4 12:15:56 2003 @@ -0,0 +1,846 @@ +/* + * + * Amiga Smart File System, Linux implementation + * + * version: 0.6 (04.09.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.6 (04.09.2003) - final read-only version + * - added support for HashTables, directory scaning should be + * MUCH faster now + * - added checking of block IDs before reading any data from block + * + * v0.5 (19.07.2003) + * - added simple but effective extent cache - 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... + */ + +#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, u32 id) +{ + if (asfs_calcchecksum((struct fsBlockHeader *)block, blocksize) == + be32_to_cpu(((struct fsBlockHeader *)block)->checksum) && + n == be32_to_cpu(((struct fsBlockHeader *)block)->ownblock) && + id == be32_to_cpu(((struct fsBlockHeader *)block)->id)) + return 1; + return 0; +} + +/* get fs structure from block and do some checks... */ +static int asfs_get_contblock(struct super_block *sb, u32 n, u32 type, 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, type)) { + 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; + struct fsObjectContainer *block; + struct fsObject *obj; + + asfs_debug("asfs_get_root_inode\n"); + + block = kmalloc(sb->s_blocksize, GFP_KERNEL); + if (asfs_get_contblock(sb, ASFS_SB->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID, block) < 0) + goto free_and_return; + + obj = &(block->object[0]); + from32be(obj->objectnode); + from32be(obj->object.dir.firstdirblock); + from32be(obj->object.dir.hashtable); + from32be(obj->datemodified); + + 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); + ASFS_SB->objectnoderoot = be32_to_cpu(rootblock->objectnoderoot); + ASFS_SB->flags = rootblock->bits; + 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, ASFS_ROOTID)) + 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; + u32 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, ASFS_OBJECTCONTAINER_ID, objcont) < 0) + goto free_and_end; + block = be32_to_cpu(objcont->previous); + } while (block != 0); + obj_skip = 0; + } else { + block = f_pos; + if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) + goto free_and_end; + obj_skip = (int)filp->private_data; + } + + i=0; + block = objcont->bheader.ownblock; + + do { + if (i != 0) { + if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) + goto free_and_end; + } + + i = 0; + obj = &(objcont->object[0]); + + while (be32_to_cpu(obj->objectnode) > 0 && + ((char *)obj - (char *)objcont) + sizeof(struct fsObject) < sb->s_blocksize) { + from32be(obj->objectnode); + + 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 %u offset %u), type %d\n", \ + stored, i, obj->name, 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 = be32_to_cpu(objcont->next); + + } while (block != 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, int casesensitive) +{ + if (casesensitive) { + while (*s == *ct && *ct != '\0' && *ct != '/') { + s++; ct++; + } + } else { + while (upperchar(*s) == upperchar(*ct) && *ct != '\0' && *ct != '/') { + s++; ct++; + } + } + return (*s == '\0' && (*ct == '\0' || *ct == '/')) ? 0 : *ct - *s; +} + +#define HASHCHAIN(x) (u16)(x % (u16)(((sb->s_blocksize) - sizeof(struct fsHashTable))>>2)) + +static inline u16 hash(u8 *name, int casesensitive) +{ + u16 hashval=0; + while (name[hashval] != 0 && name[hashval] != '/') + hashval++; + if (casesensitive) { + u8 c = *name; + while (c != 0 && c != '/') { + hashval = hashval*13 + c; + c = *++name; + } + } else { + u8 c = *name; + while (c != 0 && c != '/') { + hashval = hashval*13 + upperchar(c); + c = *++name; + } + } + return hashval; +} + +/* Finds a specific node by number. */ +static struct fsObjectNode * +asfs_get_Node(struct super_block *sb, u32 nodeindex, u16 nodesize, u32 nodeno, + struct fsObjectNode *dest_node) +{ + struct fsNodeContainer *nodecont; + nodecont = kmalloc(sb->s_blocksize, GFP_KERNEL); + + while (asfs_get_contblock(sb, nodeindex, ASFS_NODECONTAINER_ID, nodecont) > 0) { + if (be32_to_cpu(nodecont->nodes) == 1) { + *dest_node = *((struct fsObjectNode *) ((u8 *) nodecont->node + nodesize * \ + (nodeno - be32_to_cpu(nodecont->nodenumber)))); + kfree(nodecont); + from32be(dest_node->node.data); + from32be(dest_node->next); + from16be(dest_node->hash16); + return dest_node; + } else { + u16 containerentry = (nodeno - be32_to_cpu(nodecont->nodenumber)) / + be32_to_cpu(nodecont->nodes); + nodeindex = be32_to_cpu(nodecont->node[containerentry]) >> + (sb->s_blocksize_bits - ASFS_BLCKFACCURACY); + } + } + kfree(nodecont); + return NULL; +} + +struct fsObject * +asfs_find_obj_in_cont(struct super_block *sb, struct fsObjectContainer *objcont, + u8 *name) +{ + struct fsObject *obj; + + obj = &(objcont->object[0]); + while (be32_to_cpu(obj->objectnode) > 0 && + ((char *)obj - (char *)objcont) + sizeof(struct fsObject) < sb->s_blocksize) { + if (namecmp(obj->name, name, ASFS_SB->flags & ASFS_ROOTBITS_CASESENSITIVE) == 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! Node %u, Name %s, Type: %d, inCont %u\n", \ + obj->objectnode, obj->name, obj->bits, objcont->bheader.ownblock); + return obj; + } + obj = (struct fsObject *)((u8 *)(obj) + asfs_get_fsObject_varlen(obj)); + } + return NULL; +} + +static struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry) +{ + int res = -EACCES; /* placeholder for "no data here" */ + struct inode *inode; + struct super_block *sb = dir->i_sb; + u8 *name = (u8 *) dentry->d_name.name; + struct fsObjectContainer *objcont; + struct fsObject *obj; + struct fsObjectNode obj_node; + u32 block, node; + u16 hash16; + + asfs_debug("asfs_lookup: (searching \"%s\"...) ", name); + objcont = kmalloc(dir->i_sb->s_blocksize, GFP_KERNEL); + + if (ASFS_DIR->hashtable != 0) { /* hashtable block is available, quick search */ + + if (asfs_get_contblock(sb, ASFS_DIR->hashtable, ASFS_HASHTABLE_ID, objcont) < 0) + goto free_and_error; + hash16 = hash(name, ASFS_SB->flags & ASFS_ROOTBITS_CASESENSITIVE); + node = be32_to_cpu(((struct fsHashTable *)objcont)->hashentry[HASHCHAIN(hash16)]); + + while (node != 0) { + if (asfs_get_Node(sb, ASFS_SB->objectnoderoot, sizeof(struct fsObjectNode), + node, &obj_node) == NULL) + goto not_found; + if (obj_node.hash16 == hash16) { + if (asfs_get_contblock(sb, obj_node.node.data, ASFS_OBJECTCONTAINER_ID, objcont) < 0) + goto free_and_error; + if ((obj = asfs_find_obj_in_cont(sb, objcont, name)) != NULL) + goto found_inode; + } + node = obj_node.next; + } + } else { /* hashtable not available, long search */ + + block = ASFS_DIR->firstblock; + do { + if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) + goto free_and_error; + block = be32_to_cpu(objcont->previous); + } while (block != 0); + block = objcont->bheader.ownblock; + + do { + if (block != objcont->bheader.ownblock) + if (asfs_get_contblock(sb, block, ASFS_OBJECTCONTAINER_ID, objcont) < 0) + goto free_and_error; + if ((obj = asfs_find_obj_in_cont(sb, objcont, name)) != NULL) + goto found_inode; + block = be32_to_cpu(objcont->next); + } while (block != 0); + } + +not_found: + inode = NULL; + asfs_debug("object not found.\n"); + if (0) { +found_inode: + if (!(inode = iget4(sb, obj->objectnode, NULL, obj))) { + asfs_debug("ASFS: Strange - no inode allocated.\n"); + goto free_and_error; + } + } + 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, ASFS_BNODECONTAINER_ID, 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); + if (exbnode->key == key) { + from32be(exbnode->next); + from32be(exbnode->prev); + from16be(exbnode->blocks); + *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, ASFS_SOFTLINK_ID, 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 v0.6 (04.09.2003)"); +MODULE_LICENSE("GPL"); + +module_init(init_asfs_fs) +module_exit(exit_asfs_fs) diff -urN linux-2.4.21/include/linux/asfs_fs.h linux-2.4.21-asfs-0.6/include/linux/asfs_fs.h --- linux-2.4.21/include/linux/asfs_fs.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-asfs-0.6/include/linux/asfs_fs.h Tue Sep 2 13:38:37 2003 @@ -0,0 +1,213 @@ +#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 block IDs */ + +#define ASFS_ROOTID ASFS_MAKE_ID('S','F','S','\0') +#define ASFS_OBJECTCONTAINER_ID ASFS_MAKE_ID('O','B','J','C') +#define ASFS_BNODECONTAINER_ID ASFS_MAKE_ID('B','N','D','C') +#define ASFS_NODECONTAINER_ID ASFS_MAKE_ID('N','D','C',' ') +#define ASFS_HASHTABLE_ID ASFS_MAKE_ID('H','T','A','B') +#define ASFS_SOFTLINK_ID ASFS_MAKE_ID('S','L','N','K') + +/* Amiga SFS defines */ + +#define ASFS_MAGIC 0xa0ff +#define ASFS_MAXFN 105 + +#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 ASFS_BLCKFACCURACY (5) + +#define ASFS_ROOTBITS_CASESENSITIVE (128) + +#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]; +}; + +/* */ + +struct fsHashTable { + struct fsBlockHeader bheader; + u32 parent; + u32 hashentry[0]; +}; + +/* On disk block with node index and some helper structures */ + +struct fsNodeContainer { + struct fsBlockHeader bheader; + u32 nodenumber; + u32 nodes; + u32 node[0]; +}; + +struct fsNode { + u32 data; +}; + +struct fsObjectNode { + struct fsNode node; + u32 next; + u16 hash16; +} +__attribute__((packed)); + +/* 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 -urN linux-2.4.21/include/linux/asfs_fs_i.h linux-2.4.21-asfs-0.6/include/linux/asfs_fs_i.h --- linux-2.4.21/include/linux/asfs_fs_i.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-asfs-0.6/include/linux/asfs_fs_i.h Fri Jul 25 15:13:10 2003 @@ -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 -urN linux-2.4.21/include/linux/asfs_fs_sb.h linux-2.4.21-asfs-0.6/include/linux/asfs_fs_sb.h --- linux-2.4.21/include/linux/asfs_fs_sb.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-asfs-0.6/include/linux/asfs_fs_sb.h Tue Sep 2 13:47:58 2003 @@ -0,0 +1,20 @@ +#ifndef __ASFS_FS_SB +#define __ASFS_FS_SB + +/* Amiga SFS superblock in-core data */ + +struct asfs_sb_info { + u32 totalblocks; + u32 rootobjectcontainer; + u32 extentbnoderoot; + u32 objectnoderoot; + uid_t uid; + gid_t gid; + umode_t mode; + char *prefix; + u8 flags; +}; + +#define ASFS_SB (&sb->u.asfs_sb) + +#endif diff -urN linux-2.4.21/include/linux/fs.h linux-2.4.21-asfs-0.6/include/linux/fs.h --- linux-2.4.21/include/linux/fs.h Fri Jun 13 16:51:38 2003 +++ linux-2.4.21-asfs-0.6/include/linux/fs.h Sat Jul 12 14:14:39 2003 @@ -303,6 +303,7 @@ #include #include #include +#include #include #include #include @@ -494,6 +495,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; @@ -691,6 +693,7 @@ #include #include #include +#include #include #include #include @@ -748,6 +751,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;