From: Marek Szyprowski Amiga SFS is native filesystem on MorphOS, OS that can be installed on Pegasos / Open Desktop Workstation (http://www.pegasosppc.com/). This fs is also widely used on all Amiga systems. It becomes even more popular than the old AFFS. This fs has been included in debian kernel tree for over a year. Signed-off-by: Andrew Morton --- Documentation/filesystems/00-INDEX | 2 Documentation/filesystems/asfs.txt | 161 +++++++ fs/Kconfig | 47 ++ fs/Makefile | 1 fs/asfs/Changes | 112 +++++ fs/asfs/Makefile | 8 fs/asfs/adminspace.c | 446 +++++++++++++++++++++ fs/asfs/asfs_fs.h | 234 +++++++++++ fs/asfs/bitfuncs.c | 171 ++++++++ fs/asfs/bitfuncs.h | 59 ++ fs/asfs/dir.c | 240 +++++++++++ fs/asfs/extents.c | 586 ++++++++++++++++++++++++++++ fs/asfs/file.c | 251 ++++++++++++ fs/asfs/inode.c | 426 ++++++++++++++++++++ fs/asfs/namei.c | 197 +++++++++ fs/asfs/nodes.c | 455 ++++++++++++++++++++++ fs/asfs/objects.c | 765 +++++++++++++++++++++++++++++++++++++ fs/asfs/super.c | 488 +++++++++++++++++++++++ fs/asfs/symlink.c | 235 +++++++++++ include/linux/amigasfs.h | 276 +++++++++++++ 20 files changed, 5160 insertions(+) diff -puN Documentation/filesystems/00-INDEX~asfs-filesystem-driver Documentation/filesystems/00-INDEX --- 25/Documentation/filesystems/00-INDEX~asfs-filesystem-driver 2005-06-24 23:48:28.000000000 -0700 +++ 25-akpm/Documentation/filesystems/00-INDEX 2005-06-24 23:48:28.000000000 -0700 @@ -6,6 +6,8 @@ adfs.txt - 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. bfs.txt - info for the SCO UnixWare Boot Filesystem (BFS). cifs.txt diff -puN /dev/null Documentation/filesystems/asfs.txt --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/Documentation/filesystems/asfs.txt 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,161 @@ + +Amiga SmartFileSystem, Linux implementation +=========================================== + +ASFS is a Amiga Smart FileSystem driver for Linux. It supports reading +files and directories. From version 1.0 there is also an experimental +(almost full) write support. Experimental means that it hasn't been +tested enough yet, so use it with care. Symbolic links (in AmigaOS +called soft links) are also supported read/write. Read notes below +about symlinks support. + + +Unsupported features of Amiga SFS +================================ + +ASFS currently does not support safe-delete feature of Amiga SFS +filesystem. It simply deletes files instead of moving them to +".recycled" directory. It also doesn't remove files from ".recycled" +directory, when there is no space left on drive. + +If there is no space left, you need to manually remove files from +".recycled" directory. Also if you want to delete a file in a safe +way, you need to move it to ".recycled" directory by hand. + +Because of all of above, the amount of free space on disk does not +include space used by all files from ".recycled" directory. + + +Limitations +=========== + +There is no Amiga protection bits into Linux permission bits tranlation +and vice versa. If you need this feature, mail me. + +ASFS will always keep some amount of blocks free. This means that you +cannot fill the drive completely. It is because Amiga SFS uses some +special methods of writing data (called safe write), which needs some +additional free space. + +File systems with unfinished transactions (this happens when system crashed +during writing data to disk on AmigaOS/MorphOS) will be mounted read-only +to protect data. The only way to fix such filesystem is to mount it under +AmigaOS or MorphOS. + +Do not try to mount and write to filesystem with errors. Bad things will +happen. + + +Mount options for the ASFS +========================== + +setuid=uid + This sets the owner of all files and directories in the file + system to uid. + +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 0644, which means that everybody are allowed + to read files, but only root can write to them. + (for directories this means also that search bits are set). + +prefix=path + Path will be prefixed to every absolute path name of symbolic + links on an ASFS/AFFS partition. Default = "/". (See below.) + +volume=name + When symbolic links with an absolute path are created + on an ASFS/AFFS partition, name will be prepended as the + volume name. Default = "" (empty string). (See below.) + +lowercasevol + Translate all volume names in symlinks to lower case. + Disabled by default. (See below.) + +iocharset=name + Character set to use for converting file names. Specifies + character set used by your Linux system. +codepage=name + Set the codepage number for converting file names. Specifies + character set used by your Amiga. Use full name (for example + 'cp1251' instead of '1251') here, this allows to specify any + character set, not only numbered one (like 'iso8859-2'). + Use special name 'none' to disable the NLS file name + translation. + +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. When option +"lowercasevol" is set, it also translates volume names to lower case. +If the volume name is the same as a name given in "volume" option, +it will be ignored and an absolute path will be created. + +Example: +You mount all your Amiga partitions under /amiga/ (where + is the name of the volume), and you give options +`prefix="/amiga/",volume="Linux",lowercasevol' when mounting all your +ASFS 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 +translated to "/amiga/user/sc/include/dos/dos.h". +A symbolic link referring to "Linux:etc/fstab" will be translated to +"/etc/fstab". +If you create a symlink referring to "/amiga/graphics/data/pict.jpg", +it will be saved as "graphics:data/pict.jpg". +If you create a symlink referring to "/boot/System.map", it will be +saved as "Linux:boot/System.map". + + +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 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. + +This filesystem is in BETA STAGE. This means that driver MIGHT corrupt +or damage data on your disk. Remember! YOU USE IT ON YOUR OWN RISK! + +I made almost all I could to minimalize this risk. On my systems several +gigabytes has been succesfully copied from and to SFS disks. I would also +appreciate any infomation if this filesystem works on your system or not. +See next paragraph for my email. + +Some parts of this documentation has been adapted from AFFS driver docs. + + +Author, contact and copyright infos +=================================== + +ASFS has been written by Marek 'March' Szyprowski . +Mail me if you have any suggestions or found a bug. + +Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski + +Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help and parts +of original amiga version of SmartFilesystem source code. + +SmartFilesystem is copyrighted (C) 2003,2004 by: John Hendrikx, +Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek + +The ASFS driver is realased under the terms of of the GNU General +Public License. See source code for more details. + diff -puN /dev/null fs/asfs/adminspace.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/adminspace.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,446 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta7 + * + * This file contains some parts of the original amiga version of + * SmartFilesystem source code. + * + * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, + * Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek + * + * Adapted and modified by Marek 'March' Szyprowski + * + */ + +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" +#include "bitfuncs.h" + +#include + +#ifdef CONFIG_ASFS_RW + +static int setfreeblocks(struct super_block *sb, u32 freeblocks) +{ + struct buffer_head *bh; + if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { + struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo)); + ASFS_SB(sb)->freeblocks = freeblocks; + ri->freeblocks = cpu_to_be32(freeblocks); + asfs_bstore(sb, bh); + asfs_brelse(bh); + return 0; + } + return -EIO; +} + +static inline int enoughspace(struct super_block *sb, u32 blocks) +{ + if (ASFS_SB(sb)->freeblocks - ASFS_ALWAYSFREE < blocks) + return FALSE; + + return TRUE; +} + + /* Determines the amount of free blocks starting from block /block/. + If there are no blocks found or if there was an error -1 is returned, + otherwise this function will count the number of free blocks until + an allocated block is encountered or until maxneeded has been + exceeded. */ + +static int availablespace(struct super_block *sb, u32 block, u32 maxneeded) +{ + struct buffer_head *bh = NULL; + struct fsBitmap *b; + u32 longs = ASFS_SB(sb)->blocks_inbitmap >> 5; + u32 maxbitmapblock = ASFS_SB(sb)->bitmapbase + ASFS_SB(sb)->blocks_bitmap; + int blocksfound = 0; + u32 bitstart; + int bitend; + u32 nextblock = ASFS_SB(sb)->bitmapbase + block / ASFS_SB(sb)->blocks_inbitmap; + + bitstart = block % ASFS_SB(sb)->blocks_inbitmap; + + while (nextblock < maxbitmapblock && (bh = asfs_breadcheck(sb, nextblock++, ASFS_BITMAP_ID))) { + b = (void *) bh->b_data; + + if ((bitend = bmffz(b->bitmap, longs, bitstart)) >= 0) { + blocksfound += bitend - bitstart; + asfs_brelse(bh); + return blocksfound; + } + blocksfound += ASFS_SB(sb)->blocks_inbitmap - bitstart; + if (blocksfound >= maxneeded) { + asfs_brelse(bh); + return blocksfound; + } + bitstart = 0; + asfs_brelse(bh); + } + + if (bh == NULL) + return (-1); + + return (blocksfound); +} + +int asfs_findspace(struct super_block *sb, u32 maxneeded, u32 start, u32 end, u32 * returned_block, u32 * returned_blocks) +{ + struct buffer_head *bh; + u32 longs = ASFS_SB(sb)->blocks_inbitmap >> 5; + u32 space = 0; + u32 block; + u32 bitmapblock = ASFS_SB(sb)->bitmapbase + start / ASFS_SB(sb)->blocks_inbitmap; + u32 breakpoint; + int bitstart, bitend; + int reads; + + if (enoughspace(sb, maxneeded) == FALSE) { + *returned_block = 0; + *returned_blocks = 0; + return -ENOSPC; + } + + if (start >= ASFS_SB(sb)->totalblocks) + start -= ASFS_SB(sb)->totalblocks; + + if (end == 0) + end = ASFS_SB(sb)->totalblocks; + + reads = ((end - 1) / ASFS_SB(sb)->blocks_inbitmap) + 1 - start / ASFS_SB(sb)->blocks_inbitmap; + + if (start >= end) + reads += (ASFS_SB(sb)->totalblocks - 1) / ASFS_SB(sb)->blocks_inbitmap + 1; + + breakpoint = (start < end ? end : ASFS_SB(sb)->totalblocks); + + *returned_block = 0; + *returned_blocks = 0; + + bitend = start % ASFS_SB(sb)->blocks_inbitmap; + block = start - bitend; + + while ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) { + struct fsBitmap *b = (void *) bh->b_data; + u32 localbreakpoint = breakpoint - block; + + if (localbreakpoint > ASFS_SB(sb)->blocks_inbitmap) + localbreakpoint = ASFS_SB(sb)->blocks_inbitmap; + + /* At this point space contains the amount of free blocks at + the end of the previous bitmap block. If there are no + free blocks at the start of this bitmap block, space will + be set to zero, since in that case the space isn't adjacent. */ + + while ((bitstart = bmffo(b->bitmap, longs, bitend)) < ASFS_SB(sb)->blocks_inbitmap) { + /* found the start of an empty space, now find out how large it is */ + + if (bitstart >= localbreakpoint) + break; + + if (bitstart != 0) + space = 0; + + bitend = bmffz(b->bitmap, longs, bitstart); + + if (bitend > localbreakpoint) + bitend = localbreakpoint; + + space += bitend - bitstart; + + if (*returned_blocks < space) { + *returned_block = block + bitend - space; + if (space >= maxneeded) { + *returned_blocks = maxneeded; + asfs_brelse(bh); + return 0; + } + *returned_blocks = space; + } + + if (bitend >= localbreakpoint) + break; + } + + if (--reads == 0) + break; + + /* no (more) empty spaces found in this block */ + + if (bitend != ASFS_SB(sb)->blocks_inbitmap) + space = 0; + + bitend = 0; + block += ASFS_SB(sb)->blocks_inbitmap; + + if (block >= ASFS_SB(sb)->totalblocks) { + block = 0; + space = 0; + breakpoint = end; + bitmapblock = ASFS_SB(sb)->bitmapbase; + } + asfs_brelse(bh); + } + + if (bh == NULL) + return -EIO; + + asfs_brelse(bh); + + if (*returned_blocks == 0) + return -ENOSPC; + else + return 0; +} + +int asfs_markspace(struct super_block *sb, u32 block, u32 blocks) +{ + int errorcode; + + asfs_debug("markspace: Marking %d blocks from block %d\n", blocks, block); + + if ((availablespace(sb, block, blocks)) < blocks) { + printk("ASFS: Attempted to mark %d blocks from block %d, but some of them were already full!\n", blocks, block); + return -EIO; + } + + if ((errorcode = setfreeblocks(sb, ASFS_SB(sb)->freeblocks - blocks)) == 0) { + struct buffer_head *bh; + u32 skipblocks = block / ASFS_SB(sb)->blocks_inbitmap; + u32 longs = (sb->s_blocksize - sizeof(struct fsBitmap)) >> 2; + u32 bitmapblock; + + block -= skipblocks * ASFS_SB(sb)->blocks_inbitmap; + bitmapblock = ASFS_SB(sb)->bitmapbase + skipblocks; + + while (blocks > 0) { + if ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) { + struct fsBitmap *b = (void *) bh->b_data; + + blocks -= bmclr(b->bitmap, longs, block, blocks); + block = 0; + + asfs_bstore(sb, bh); + asfs_brelse(bh); + } else + return -EIO; + } + } + + return (errorcode); +} + + /* This function checks the bitmap and tries to locate at least /blocksneeded/ + adjacent unused blocks. If found it sets returned_block to the start block + and returns no error. If not found, ERROR_DISK_IS_FULL is returned and + returned_block is set to zero. Any other errors are returned as well. */ + +static inline int internalfindspace(struct super_block *sb, u32 blocksneeded, u32 startblock, u32 endblock, u32 * returned_block) +{ + u32 blocks; + int errorcode; + + if ((errorcode = asfs_findspace(sb, blocksneeded, startblock, endblock, returned_block, &blocks)) == 0) + if (blocks != blocksneeded) + return -ENOSPC; + + return errorcode; +} + +static int findandmarkspace(struct super_block *sb, u32 blocksneeded, u32 * returned_block) +{ + int errorcode; + + if (enoughspace(sb, blocksneeded) != FALSE) { + if ((errorcode = internalfindspace(sb, blocksneeded, 0, ASFS_SB(sb)->totalblocks, returned_block)) == 0) + errorcode = asfs_markspace(sb, *returned_block, blocksneeded); + } else + errorcode = -ENOSPC; + + return (errorcode); +} + +/* ************************** */ + +int asfs_freespace(struct super_block *sb, u32 block, u32 blocks) +{ + int errorcode; + + asfs_debug("freespace: Freeing %d blocks from block %d\n", blocks, block); + + if ((errorcode = setfreeblocks(sb, ASFS_SB(sb)->freeblocks + blocks)) == 0) { + struct buffer_head *bh; + u32 skipblocks = block / ASFS_SB(sb)->blocks_inbitmap; + u32 longs = (sb->s_blocksize - sizeof(struct fsBitmap)) >> 2; + u32 bitmapblock; + + block -= skipblocks * ASFS_SB(sb)->blocks_inbitmap; + bitmapblock = ASFS_SB(sb)->bitmapbase + skipblocks; + + while (blocks > 0) { + if ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) { + struct fsBitmap *b = (void *) bh->b_data; + + blocks -= bmset(b->bitmap, longs, block, blocks); + block = 0; + + asfs_bstore(sb, bh); + asfs_brelse(bh); + } else + return -EIO; + } + } + + return (errorcode); +} + +/*************** admin space containers ****************/ + +int asfs_allocadminspace(struct super_block *sb, u32 *returned_block) +{ + struct buffer_head *bh; + u32 adminspaceblock = ASFS_SB(sb)->adminspacecontainer; + int errorcode = -EIO; + + asfs_debug("allocadminspace: allocating new block\n"); + + while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) { + struct fsAdminSpaceContainer *asc1 = (void *) bh->b_data; + struct fsAdminSpace *as1 = asc1->adminspace; + int adminspaces1 = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace); + + while (adminspaces1-- > 0) { + s16 bitoffset; + + if (as1->space != 0 && (bitoffset = bfffz(be32_to_cpu(as1->bits), 0)) >= 0) { + u32 emptyadminblock = be32_to_cpu(as1->space) + bitoffset; + as1->bits |= cpu_to_be32(1 << (31 - bitoffset)); + asfs_bstore(sb, bh); + *returned_block = emptyadminblock; + asfs_brelse(bh); + asfs_debug("allocadminspace: found block %d\n", *returned_block); + return 0; + } + as1++; + } + + adminspaceblock = be32_to_cpu(asc1->next); + asfs_brelse(bh); + + if (adminspaceblock == 0) { + u32 startblock; + + asfs_debug("allocadminspace: allocating new adminspace area\n"); + + /* If we get here it means current adminspace areas are all filled. + We would now need to find a new area and create a fsAdminSpace + structure in one of the AdminSpaceContainer blocks. If these + don't have any room left for new adminspace areas a new + AdminSpaceContainer would have to be created first which is + placed as the first block in the newly found admin area. */ + + adminspaceblock = ASFS_SB(sb)->adminspacecontainer; + + if ((errorcode = findandmarkspace(sb, 32, &startblock))) + return errorcode; + + while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) { + struct fsAdminSpaceContainer *asc2 = (void *) bh->b_data; + struct fsAdminSpace *as2 = asc2->adminspace; + int adminspaces2 = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace); + + while (adminspaces2-- > 0 && as2->space != 0) + as2++; + + if (adminspaces2 >= 0) { /* Found a unused AdminSpace in this AdminSpaceContainer! */ + as2->space = cpu_to_be32(startblock); + as2->bits = 0; + asfs_bstore(sb, bh); + asfs_brelse(bh); + break; + } + + if (asc2->next == 0) { + /* Oh-oh... we marked our new adminspace area in use, but we couldn't + find space to store a fsAdminSpace structure in the existing + fsAdminSpaceContainer blocks. This means we need to create and + link a new fsAdminSpaceContainer as the first block in our newly + marked adminspace. */ + + asc2->next = cpu_to_be32(startblock); + asfs_bstore(sb, bh); + asfs_brelse(bh); + + /* Now preparing new AdminSpaceContainer */ + + if ((bh = asfs_getzeroblk(sb, startblock)) == NULL) + return -EIO; + + asc2 = (void *) bh->b_data; + asc2->bheader.id = cpu_to_be32(ASFS_ADMINSPACECONTAINER_ID); + asc2->bheader.ownblock = cpu_to_be32(startblock); + asc2->previous = cpu_to_be32(adminspaceblock); + asc2->adminspace[0].space = cpu_to_be32(startblock); + asc2->adminspace[0].bits = cpu_to_be32(0x80000000); + asc2->bits = 32; + + asfs_bstore(sb, bh); + asfs_brelse(bh); + + adminspaceblock = startblock; + break; /* Breaks through to outer loop! */ + } + adminspaceblock = be32_to_cpu(asc2->next); + asfs_brelse(bh); + } + } + } + return errorcode; +} + +int asfs_freeadminspace(struct super_block *sb, u32 block) +{ + struct buffer_head *bh; + u32 adminspaceblock = ASFS_SB(sb)->adminspacecontainer; + + asfs_debug("freeadminspace: Entry -- freeing block %d\n", block); + + while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) { + struct fsAdminSpaceContainer *asc = (void *) bh->b_data; + struct fsAdminSpace *as = asc->adminspace; + int adminspaces = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace); + + while (adminspaces-- > 0) { + if (block >= be32_to_cpu(as->space) && block < be32_to_cpu(as->space) + 32) { + s16 bitoffset = block - be32_to_cpu(as->space); + asfs_debug("freeadminspace: Block to be freed is located in AdminSpaceContainer block at %d\n", adminspaceblock); + as->bits &= cpu_to_be32(~(1 << (31 - bitoffset))); + asfs_bstore(sb, bh); + asfs_brelse(bh); + return 0; + } + as++; + } + + if ((adminspaceblock = be32_to_cpu(asc->next)) == 0) + break; + + asfs_brelse(bh); + } + + if (bh != NULL) { + asfs_brelse(bh); + printk("ASFS: Unable to free an administration block. The block cannot be found."); + return -ENOENT; + } + + return -EIO; +} + +#endif diff -puN /dev/null fs/asfs/asfs_fs.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/asfs_fs.h 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,234 @@ +#ifndef __LINUX_ASFS_FS_H +#define __LINUX_ASFS_FS_H + +#include +#include +#include +#include +#include + +#define asfs_debug(fmt,arg...) /* no debug at all */ +//#define asfs_debug(fmt,arg...) printk(fmt,##arg) /* general debug infos */ + +#if !defined (__BIG_ENDIAN) && !defined (__LITTLE_ENDIAN) +#error Endianes must be known for ASFS to work. Sorry. +#endif + +#define ASFS_MAXFN_BUF (ASFS_MAXFN + 4) +#define ASFS_DEFAULT_UID 0 +#define ASFS_DEFAULT_GID 0 +#define ASFS_DEFAULT_MODE 0644 /* default permission bits for files, dirs have same permission, but with "x" set */ + +/* 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; +}; + +/* inode in-kernel data */ + +struct asfs_inode_info { + u32 firstblock; + u32 hashtable; + int modified; + loff_t mmu_private; + struct inramExtent ext_cache; + struct inode vfs_inode; +}; + +/* short cut to get to the asfs specific inode data */ +static inline struct asfs_inode_info *ASFS_I(struct inode *inode) +{ + return list_entry(inode, struct asfs_inode_info, vfs_inode); +} + +/* Amiga SFS superblock in-core data */ + +struct asfs_sb_info { + u32 totalblocks; + u32 rootobjectcontainer; + u32 extentbnoderoot; + u32 objectnoderoot; + + u32 adminspacecontainer; + u32 bitmapbase; + u32 freeblocks; + u32 blocks_inbitmap; + u32 blocks_bitmap; + u32 block_rovingblockptr; + + uid_t uid; + gid_t gid; + umode_t mode; + u16 flags; + char *prefix; + char *root_volume; /* Volume prefix for absolute symlinks. */ + char *iocharset; + char *codepage; + struct nls_table *nls_io; + struct nls_table *nls_disk; +}; + +/* short cut to get to the asfs specific sb data */ +static inline struct asfs_sb_info *ASFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +/* io inline code */ + +u32 asfs_calcchecksum(void *block, u32 blocksize); + +static inline int +asfs_check_block(struct fsBlockHeader *block, u32 blocksize, u32 n, u32 id) +{ + if (asfs_calcchecksum(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 TRUE; + return FALSE; +} + +/* get fs structure from block and do some checks... */ +static inline struct buffer_head * +asfs_breadcheck(struct super_block *sb, u32 n, u32 type) +{ + struct buffer_head *bh; + if ((bh = sb_bread(sb, n))) { + if (asfs_check_block ((void *)bh->b_data, sb->s_blocksize, n, type)) { + return bh; /* all okay */ + } + brelse(bh); + } + return NULL; /* error */ +} + +static inline struct buffer_head * +asfs_getzeroblk(struct super_block *sb, int block) +{ + struct buffer_head *bh; + bh = sb_getblk(sb, block); + lock_buffer(bh); + memset(bh->b_data, 0, sb->s_blocksize); + set_buffer_uptodate(bh); + unlock_buffer(bh); + return bh; +} + +static inline void +asfs_bstore(struct super_block *sb, struct buffer_head *bh) +{ + ((struct fsBlockHeader *) (bh->b_data))->checksum = + cpu_to_be32(asfs_calcchecksum(bh->b_data, sb->s_blocksize)); + mark_buffer_dirty(bh); +} + +static inline void asfs_brelse(struct buffer_head *bh) +{ + brelse(bh); +} + +static inline void dec_count(struct inode *inode) +{ + inode->i_nlink--; + mark_inode_dirty(inode); +} + +/* all prototypes */ + +/* adminspace.c */ +int asfs_allocadminspace(struct super_block *sb, u32 * block); +int asfs_freeadminspace(struct super_block *sb, u32 block); +int asfs_markspace(struct super_block *sb, u32 block, u32 blocks); +int asfs_freespace(struct super_block *sb, u32 block, u32 blocks); +int asfs_findspace(struct super_block *sb, u32 maxneeded, u32 start, u32 end, + u32 * returned_block, u32 * returned_blocks); + +/* dir.c */ +int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir); +struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd); + +/* extents.c */ +int asfs_getextent(struct super_block *sb, u32 key, struct buffer_head **ret_bh, + struct fsExtentBNode **ret_ebn); +int asfs_deletebnode(struct super_block *sb, struct buffer_head *cb, u32 key); +int asfs_deleteextents(struct super_block *sb, u32 key); +int asfs_addblocks(struct super_block *sb, u16 blocks, u32 newspace, + u32 objectnode, u32 * io_lastextentbnode); + +/* file.c */ +int asfs_readpage(struct file *file, struct page *page); +sector_t asfs_bmap(struct address_space *mapping, sector_t block); +int asfs_writepage(struct page *page, struct writeback_control *wbc); +int asfs_prepare_write(struct file *file, struct page *page, unsigned from, + unsigned to); +void asfs_truncate(struct inode *inode); +int asfs_file_open(struct inode *inode, struct file *filp); +int asfs_file_release(struct inode *inode, struct file *filp); + +/* inode.c */ +struct inode *asfs_get_root_inode(struct super_block *sb); +void asfs_read_locked_inode(struct inode *inode, void *arg); +int asfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd); +int asfs_mkdir(struct inode *dir, struct dentry *dentry, int mode); +int asfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); +int asfs_rmdir(struct inode *dir, struct dentry *dentry); +int asfs_unlink(struct inode *dir, struct dentry *dentry); +int asfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +int asfs_notify_change(struct dentry *dentry, struct iattr *attr); + +/* namei */ +u8 asfs_lowerchar(u8 c); +int asfs_check_name(const u8 *name, int len); +int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t); +u16 asfs_hash(u8 *name, int casesensitive); +void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from, int limit); + +/* nodes */ +int asfs_getnode(struct super_block *sb, u32 nodeno, + struct buffer_head **ret_bh, struct fsObjectNode **ret_node); +int asfs_createnode(struct super_block *sb, struct buffer_head **returned_cb, + struct fsNode **returned_node, u32 * returned_nodeno); +int asfs_deletenode(struct super_block *sb, u32 objectnode); + +/* objects */ +struct fsObject *asfs_nextobject(struct fsObject *obj); +struct fsObject *asfs_find_obj_by_name(struct super_block *sb, + struct fsObjectContainer *objcont, u8 * name); +int asfs_readobject(struct super_block *sb, u32 objectnode, + struct buffer_head **cb, struct fsObject **returned_object); +int asfs_createobject(struct super_block *sb, struct buffer_head **io_cb, + struct fsObject **io_o, struct fsObject *src_o, + u8 * objname, int force); +int asfs_deleteobject(struct super_block *sb, struct buffer_head *cb, + struct fsObject *o); +int asfs_renameobject(struct super_block *sb, struct buffer_head *cb1, + struct fsObject *o1, struct buffer_head *cbparent, + struct fsObject *oparent, u8 * newname); + +int asfs_addblockstofile(struct super_block *sb, struct buffer_head *objcb, + struct fsObject *o, u32 blocks, u32 * newspace, + u32 * addedblocks); +int asfs_truncateblocksinfile(struct super_block *sb, struct buffer_head *bh, + struct fsObject *o, u32 newsize); + +/* super.c */ +struct super_block *asfs_read_super(struct super_block *sb, void *data, + int silent); +void asfs_put_super(struct super_block *sb); +int asfs_statfs(struct super_block *sb, struct kstatfs *buf); +int asfs_remount(struct super_block *sb, int *flags, char *data); +struct inode *asfs_alloc_inode(struct super_block *sb); +void asfs_destroy_inode(struct inode *inode); + +/* symlink.c */ +int asfs_symlink_readpage(struct file *file, struct page *page); +int asfs_write_symlink(struct inode *symfile, const char *symname); + +#endif diff -puN /dev/null fs/asfs/bitfuncs.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/bitfuncs.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,171 @@ +/* + * + * 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. + * + */ + +#include +#include +#include "bitfuncs.h" + +/* Bitmap (bm) functions: + These functions perform bit-operations on regions of memory which + are a multiple of 4 bytes in length. Bitmap is in bigendian byte order. +*/ + +/* This function finds the first set bit in a region of memory starting + with /bitoffset/. The region of memory is /longs/ longs long. It + returns the bitoffset of the first set bit it finds. */ + +int bmffo(u32 *bitmap, int longs, int bitoffset) +{ + u32 *scan = bitmap; + int longoffset, bit; + + longoffset = bitoffset >> 5; + longs -= longoffset; + scan += longoffset; + + bitoffset = bitoffset & 0x1F; + + if (bitoffset != 0) { + if ((bit = bfffo(be32_to_cpu(*scan), bitoffset)) >= 0) { + return (bit + ((scan - bitmap) << 5)); + } + scan++; + longs--; + } + + while (longs-- > 0) { + if (*scan++ != 0) { + return (bfffo(be32_to_cpu(*--scan), 0) + ((scan - bitmap) << 5)); + } + } + + return (-1); +} + +/* This function finds the first unset bit in a region of memory starting + with /bitoffset/. The region of memory is /longs/ longs long. It + returns the bitoffset of the first unset bit it finds. */ + +int bmffz(u32 *bitmap, int longs, int bitoffset) +{ + u32 *scan = bitmap; + int longoffset, bit; + + longoffset = bitoffset >> 5; + longs -= longoffset; + scan += longoffset; + + bitoffset = bitoffset & 0x1F; + + if (bitoffset != 0) { + if ((bit = bfffz(be32_to_cpu(*scan), bitoffset)) >= 0) { + return (bit + ((scan - bitmap) << 5)); + } + scan++; + longs--; + } + + while (longs-- > 0) { + if (*scan++ != 0xFFFFFFFF) { + return (bfffz(be32_to_cpu(*--scan), 0) + ((scan - bitmap) << 5)); + } + } + + return (-1); +} + +/* This function clears /bits/ bits in a region of memory starting + with /bitoffset/. The region of memory is /longs/ longs long. If + the region of memory is too small to clear /bits/ bits then this + function exits after having cleared all bits till the end of the + memory region. In any case it returns the number of bits which + were actually cleared. */ + +int bmclr(u32 *bitmap, int longs, int bitoffset, int bits) +{ + u32 *scan = bitmap; + int longoffset; + int orgbits = bits; + + longoffset = bitoffset >> 5; + longs -= longoffset; + scan += longoffset; + + bitoffset = bitoffset & 0x1F; + + if (bitoffset != 0) { + if (bits < 32) { + *scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), bitoffset, bits)); + } else { + *scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), bitoffset, 32)); + } + scan++; + longs--; + bits -= 32 - bitoffset; + } + + while (bits > 0 && longs-- > 0) { + if (bits > 31) { + *scan++ = 0; + } else { + *scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), 0, bits)); + } + bits -= 32; + } + + if (bits <= 0) { + return (orgbits); + } + return (orgbits - bits); +} + +/* This function sets /bits/ bits in a region of memory starting + with /bitoffset/. The region of memory is /longs/ longs long. If + the region of memory is too small to set /bits/ bits then this + function exits after having set all bits till the end of the + memory region. In any case it returns the number of bits which + were actually set. */ + +int bmset(u32 *bitmap, int longs, int bitoffset, int bits) +{ + u32 *scan = bitmap; + int longoffset; + int orgbits = bits; + + longoffset = bitoffset >> 5; + longs -= longoffset; + scan += longoffset; + + bitoffset = bitoffset & 0x1F; + + if (bitoffset != 0) { + if (bits < 32) { + *scan = cpu_to_be32(bfset(be32_to_cpu(*scan), bitoffset, bits)); + } else { + *scan = cpu_to_be32(bfset(be32_to_cpu(*scan), bitoffset, 32)); + } + scan++; + longs--; + bits -= 32 - bitoffset; + } + + while (bits > 0 && longs-- > 0) { + if (bits > 31) { + *scan++ = 0xFFFFFFFF; + } else { + *scan = cpu_to_be32(bfset(be32_to_cpu(*scan), 0, bits)); + } + bits -= 32; + } + + if (bits <= 0) { + return (orgbits); + } + return (orgbits - bits); +} diff -puN /dev/null fs/asfs/bitfuncs.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/bitfuncs.h 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,59 @@ +/* + * + * 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. + * + */ + +#ifndef __BITFUNCS_H +#define __BITFUNCS_H + +#include +#include + +#include +#include + +/* Finds first set bit in /data/ starting at /bitoffset/. This function + considers the MSB to be the first bit. */ +static inline int bfffo(u32 data, int bitoffset) +{ + u32 mask = 0xffffffff >> bitoffset; + data &= mask; + return data == 0 ? -1 : 32-fls(data); +} + +/* Finds first zero bit in /data/ starting at /bitoffset/. This function + considers the MSB to be the first bit. */ +static inline int bfffz(u32 data, int bitoffset) +{ + return bfffo(~data, bitoffset); +} + +/* Sets /bits/ bits starting from /bitoffset/ in /data/. + /bits/ must be between 1 and 32. */ +static inline u32 bfset(u32 data, int bitoffset, int bits) +{ + u32 mask = ~((1 << (32 - bits)) - 1); + mask >>= bitoffset; + return data | mask; +} + +/* Clears /bits/ bits starting from /bitoffset/ in /data/. + /bits/ must be between 1 and 32. */ +static inline u32 bfclr(u32 data, int bitoffset, int bits) +{ + u32 mask = ~((1 << (32 - bits)) - 1); + mask >>= bitoffset; + return data & ~mask; +} + +/* bm??? functions assumes that in-memory bitmap is in bigendian byte order */ +int bmffo(u32 *, int, int); +int bmffz(u32 *, int, int); +int bmclr(u32 *, int, int, int); +int bmset(u32 *, int, int, int); + +#endif diff -puN /dev/null fs/asfs/Changes --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/Changes 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,112 @@ + +Amiga Smart File System, Linux implementation + +Please direct bug reports to: marek@amiga.pl + +History: + +v1.0beta10 (13.06.2005) +- fixed ugly bug introduced in beta9 that caused kernel crash on x86 + (thanks to Emiliano for reporting it!) + +v1.0beta9 (17.03.2005) +- added NLS support (thanks to Pavel Fedin!) + +v1.0beta8 (07.01.2005) +- adapted to 2.6.10 kernel VFS changes +- added workaround for buggy Mandrake kernel headers + +v1.0beta7 (25.06.2004) +- small changes in documentation +- code clean up: bitfuncs.c, super.c, inode.c, *.h, Makefile, added + asfs_ prefix to function names, made some functions static + (big thanks to Christoph Hellwig for advice!) +- fixed minor bugs (inode leak in super.c, not-realesed buffer during + object renaming in inode.c) +- now files/dirs are created with global ownership/permission bits + +v1.0beta6 (04.06.2004) +- fixed: ASFS_SB(sb)->flags was always zero in 2.6.x code + +v1.0beta5 (07.05.2004) +- finally fixed a problem with file size attrib. not being written + to disk +- fixed some problems with GCC 3.x and debug enabled + +v1.0beta4 (12.04.2004) +- removed dummy asfs_notify_change (this fixes major bug introduced + in 1.0beta3 - file size wasn't written to disk) until it will + be implemented completely + +v1.0beta3 (22.03.2004) - still beta +- updated for 2.6.x kernels VFS changes +- code clean-up +- added dummy asfs_notify_change (chmod now returns no errors) +- added symlinks write support +- fixed: ASFS_SB(sb)->flags was always zero + +v1.0beta2 (11.01.2004) - special version for Pegasos][ kernel +- separated read and write functions, can be compiled also + as read-only fs + +v1.0beta1 (02.12.2003) - first public beta with write support +- added dentry hashing/comparing routines +- code clean-up + +v1.0aplha4 (30.11.2003) - preparing for first public beta +- fixed some problems with renaming/moving files +- fixed two major bugs, which didn't occur when fs was mounted + on loopback device (newly allocated blocks were not written to + disk and state bits were not set correctly on newly mapped file + blocks) +- fixed many small bugs in io code (some buffers were not freed) +- added/modified sb locks in asfs_lookup and asfs_getblock +- fixed serious bug in file block allocation routines + +v1.0aplha3 (23.11.2003) +- added (hopefully) all byteswap code, should now work again on + little-endian systems (also with write support!) +- updated documentation + +v1.0alpha2 (13.11.2003) +- now alocates file blocks in chunks during one request +- fixed some dead-locks, other fixes + +v1.0alpha (02.11.2003) - first working version with full write support +- too much to list it here ;) + +... (working on write support) + +v0.7 (12.10.2003) - internal realase +- added asfs_breadcheck, modified asfs_get_node, asfs_search_BTree, + no more from_be32/16 macros, other... +- code splitted into several files + +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 diff -puN /dev/null fs/asfs/dir.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/dir.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,240 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta7 + * + * Copyright (C) 2003,2004 Marek 'March' Szyprowski + * + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +#include + +extern struct dentry_operations asfs_dentry_operations; + +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; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; + u8 buf[512]; + unsigned long f_pos; + int stored = 0; + + struct buffer_head *bh; + struct fsObjectContainer *objcont; + struct fsObject *obj; + u32 block; + int startnode; + int add; + + asfs_debug("asfs_readdir:\n"); + + if (filp->f_pos == ASFS_SB(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, parent_ino(filp->f_dentry), DT_DIR) < 0) + return stored; + filp->f_pos = f_pos = 2; + stored++; + } + + if (ASFS_I(dir)->firstblock == 0) { /* empty directory */ + filp->f_pos = ASFS_SB(sb)->totalblocks; + ASFS_I(dir)->modified = 0; + return stored; + } + + if (f_pos == 2) { /* reading directory from its beginning */ + block = ASFS_I(dir)->firstblock; + add = 1; + startnode = 0; + } else { + startnode = (int)filp->private_data; + add = 0; + if (ASFS_I(dir)->modified == 0) + block = f_pos; + else + block = ASFS_I(dir)->firstblock; + } + + do { + if (!(bh = asfs_breadcheck(sb, block, ASFS_OBJECTCONTAINER_ID))) + return stored; + objcont = (struct fsObjectContainer *) bh->b_data; + obj = &(objcont->object[0]); + + while (be32_to_cpu(obj->objectnode) > 0 && + ((char *)obj - (char *)objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { + + if (!add && be32_to_cpu(obj->objectnode) == startnode) + add++; + + if (add && !(obj->bits & OTYPE_HIDDEN)) { + unsigned int type; + asfs_translate(buf, obj->name, nls_io, nls_disk, 512); + asfs_debug("ASFS: DirFilling: entry #%d \"%s\" (node %u offset %u), type %x\n", \ + stored, buf, be32_to_cpu(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, buf, strlen(buf), block, be32_to_cpu(obj->objectnode), type) < 0) { + filp->private_data = (void *)be32_to_cpu(obj->objectnode); + ASFS_I(dir)->modified = 0; + asfs_debug("ASFS: DirFilling: to be continued...\n"); + asfs_brelse(bh); + return stored; + } + stored++; + } + obj = asfs_nextobject(obj); + } + block = be32_to_cpu(objcont->next); + asfs_brelse(bh); + + } while (block != 0); + + filp->f_pos = ASFS_SB(sb)->totalblocks; + ASFS_I(dir)->modified = 0; + + return stored; +} + +static struct fsObject *asfs_find_obj_by_name_nls(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name) +{ + struct fsObject *obj; + u8 buf[512]; + + obj = &(objcont->object[0]); + while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { + asfs_translate(buf, obj->name, ASFS_SB(sb)->nls_io, ASFS_SB(sb)->nls_disk, 512); + if (asfs_namecmp(buf, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE, ASFS_SB(sb)->nls_io) == 0) { + asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj->objectnode), obj->name, obj->bits, be32_to_cpu(objcont->bheader.ownblock)); + return obj; + } + obj = asfs_nextobject(obj); + } + return NULL; +} + +struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + 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 buffer_head *bh; + struct fsObject *obj; + u8 bufname[ASFS_MAXFN_BUF]; + + asfs_translate(bufname, name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF); + + asfs_debug("asfs_lookup: (searching \"%s\"...) ", name); + + lock_super(sb); + + if ((!strchr(name, '?')) && (ASFS_I(dir)->hashtable != 0)) { /* hashtable block is available and name can be reverse translated, quick search */ + struct fsObjectNode *node_p; + struct buffer_head *node_bh; + u32 node; + u16 hash16; + + asfs_debug("(quick search) "); + + if (!(bh = asfs_breadcheck(sb, ASFS_I(dir)->hashtable, ASFS_HASHTABLE_ID))) { + unlock_super(sb); + return ERR_PTR(res); + } + hash16 = asfs_hash(bufname, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE); + node = be32_to_cpu(((struct fsHashTable *) bh->b_data)->hashentry[HASHCHAIN(hash16)]); + asfs_brelse(bh); + + while (node != 0) { + if (asfs_getnode(sb, node, &node_bh, &node_p) != 0) + goto not_found; + if (be16_to_cpu(node_p->hash16) == hash16) { + if (!(bh = asfs_breadcheck(sb, be32_to_cpu(node_p->node.data), ASFS_OBJECTCONTAINER_ID))) { + asfs_brelse(node_bh); + unlock_super(sb); + return ERR_PTR(res); + } + if ((obj = asfs_find_obj_by_name(sb, (struct fsObjectContainer *) bh->b_data, bufname)) != NULL) { + asfs_brelse(node_bh); + goto found_inode; + } + asfs_brelse(bh); + } + node = be32_to_cpu(node_p->next); + asfs_brelse(node_bh); + } + } else { /* hashtable not available or name can't be reverse-translated, long search */ + struct fsObjectContainer *objcont; + u32 block; + + asfs_debug("(long search) "); + block = ASFS_I(dir)->firstblock; + while (block != 0) { + if (!(bh = asfs_breadcheck(sb, block, ASFS_OBJECTCONTAINER_ID))) { + unlock_super(sb); + return ERR_PTR(res); + } + objcont = (struct fsObjectContainer *) bh->b_data; + if ((obj = asfs_find_obj_by_name_nls(sb, objcont, name)) != NULL) + goto found_inode; + block = be32_to_cpu(objcont->next); + asfs_brelse(bh); + } + } + +not_found: + unlock_super(sb); + inode = NULL; + asfs_debug("object not found.\n"); + if (0) { +found_inode: + unlock_super(sb); + if (!(inode = iget_locked(sb, be32_to_cpu(obj->objectnode)))) { + asfs_debug("ASFS: Strange - no inode allocated.\n"); + return ERR_PTR(res); + } + if (inode->i_state & I_NEW) { + asfs_read_locked_inode(inode, obj); + unlock_new_inode(inode); + } + asfs_brelse(bh); + } + res = 0; + dentry->d_op = &asfs_dentry_operations; + d_add(dentry, inode); + return ERR_PTR(res); +} diff -puN /dev/null fs/asfs/extents.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/extents.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,586 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta7 + * + * This file contains some parts of the original amiga version of + * SmartFilesystem source code. + * + * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, + * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek + * + * Adapted and modified by Marek 'March' Szyprowski + * + */ + +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +#include + + /* This function looks for the BNode equal to the key. If no + exact match is available then the BNode which is slightly + lower than key will be returned. If no such BNode exists + either, then the first BNode in this block is returned. + + This function will return the first BNode even if there + are no BNode's at all in this block (this can only happen + for the Root of the tree). Be sure to check if the Root + is not empty before calling this function. */ + +static struct BNode *searchforbnode(u32 key, struct BTreeContainer *tc) +{ + struct BNode *tn; + s16 n = be16_to_cpu(tc->nodecount) - 1; + + tn = (struct BNode *) ((u8 *) tc->bnode + n * tc->nodesize); + for (;;) { + if (n <= 0 || key >= be32_to_cpu(tn->key)) + return tn; + + tn = (struct BNode *) ((u8 *) tn - tc->nodesize); + n--; + } +} + +/* This function finds the BNode with the given key. If no exact match can be + found then this function will return either the next or previous closest + match (don't rely on this). + + If there were no BNode's at all, then *returned_bh will be NULL. */ + +static int findbnode(struct super_block *sb, u32 key, struct buffer_head **returned_bh, struct BNode **returned_bnode) +{ + u32 rootblock = ASFS_SB(sb)->extentbnoderoot; + + asfs_debug("findbnode: Looking for BNode with key %d\n", key); + + while ((*returned_bh = asfs_breadcheck(sb, rootblock, ASFS_BNODECONTAINER_ID))) { + struct fsBNodeContainer *bnc = (void *) (*returned_bh)->b_data; + struct BTreeContainer *btc = &bnc->btc; + + if (btc->nodecount == 0) { + *returned_bnode = NULL; + break; + } + + *returned_bnode = searchforbnode(key, btc); + if (btc->isleaf == TRUE) + break; + + rootblock = be32_to_cpu((*returned_bnode)->data); + asfs_brelse(*returned_bh); + } + + if (*returned_bh == NULL) + return -EIO; + + return 0; +} + +int asfs_getextent(struct super_block *sb, u32 key, struct buffer_head **ret_bh, struct fsExtentBNode **ret_ebn) +{ + int result; + if ((result = findbnode(sb, key, ret_bh, (struct BNode **)ret_ebn)) == 0) + if (be32_to_cpu((*ret_ebn)->key) != key) { + brelse(*ret_bh); + *ret_bh = NULL; + return -ENOENT; + } + + return result; +} + +#ifdef CONFIG_ASFS_RW + + /* This routine inserts a node sorted into a BTreeContainer. It does + this by starting at the end, and moving the nodes one by one to + a higher slot until the empty slot has the correct position for + this key. Donot use this function on completely filled + BTreeContainers! */ + +static struct BNode *insertbnode(u32 key, struct BTreeContainer *btc) +{ + struct BNode *bn; + bn = (struct BNode *) ((u8 *) btc->bnode + btc->nodesize * (be16_to_cpu(btc->nodecount) - 1)); + + for (;;) { + if (bn < btc->bnode || key > be32_to_cpu(bn->key)) { + bn = (struct BNode *) ((u8 *) bn + btc->nodesize); + bn->key = cpu_to_be32(key); + btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + 1); + break; + } else + memmove((u8 *)bn + btc->nodesize, bn, btc->nodesize); + + bn = (struct BNode *) ((u8 *) bn - btc->nodesize); + } + + return bn; +} + +static int getparentbtreecontainer(struct super_block *sb, struct buffer_head *bh, struct buffer_head **parent_bh) +{ + u32 rootblock = ASFS_SB(sb)->extentbnoderoot; + u32 childkey = be32_to_cpu(((struct fsBNodeContainer *) bh->b_data)->btc.bnode[0].key); + u32 childblock = be32_to_cpu(((struct fsBNodeContainer *) bh->b_data)->bheader.ownblock); + + asfs_debug("getparentbtreecontainer: Getting parent of block %d\n", childblock); + + /* This function gets the BTreeContainer parent of the passed in buffer_head. If + there is no parent this function sets dest_cont io_bh to NULL */ + + if (rootblock != childblock) { + while ((*parent_bh = asfs_breadcheck(sb, rootblock, ASFS_BNODECONTAINER_ID))) { + struct fsBNodeContainer *bnc = (void *) (*parent_bh)->b_data; + struct BTreeContainer *btc = &bnc->btc; + struct BNode *bn; + s16 n = be16_to_cpu(btc->nodecount); + + if (btc->isleaf == TRUE) { + asfs_brelse(*parent_bh); + break; + } + + while (n-- > 0) + if (be32_to_cpu(btc->bnode[n].data) == childblock) + return 0; /* Found parent!! */ + + bn = searchforbnode(childkey, btc); /* This searchforbnode() doesn't have to get EXACT key matches. */ + rootblock = be32_to_cpu(bn->data); + asfs_brelse(*parent_bh); + } + if (*parent_bh == NULL) + return -EIO; + } + + *parent_bh = NULL; + return 0; +} + +/* Spits a btreecontainer. It realses passed in bh! */ + +static int splitbtreecontainer(struct super_block *sb, struct buffer_head *bh) +{ + struct buffer_head *bhparent; + struct BNode *bn; + int errorcode; + + asfs_debug("splitbtreecontainer: splitting block %u\n", be32_to_cpu(((struct fsBlockHeader *) bh->b_data)->ownblock)); + + if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) { + if (bhparent == NULL) { + u32 newbcontblock; + u32 bcontblock; + /* We need to create Root tree-container - adding new level to extent tree */ + + asfs_debug("splitbtreecontainer: creating root tree-container.\n"); + + bhparent = bh; + if ((errorcode = asfs_allocadminspace(sb, &newbcontblock)) == 0 && (bh = asfs_getzeroblk(sb, newbcontblock))) { + struct fsBNodeContainer *bnc = (void *) bh->b_data; + struct fsBNodeContainer *bncparent = (void *) bhparent->b_data; + struct BTreeContainer *btcparent = &bncparent->btc; + + bcontblock = be32_to_cpu(bncparent->bheader.ownblock); + memcpy(bh->b_data, bhparent->b_data, sb->s_blocksize); + bnc->bheader.ownblock = cpu_to_be32(newbcontblock); + asfs_bstore(sb, bh); + + memset(bhparent->b_data, '\0', sb->s_blocksize); /* Not strictly needed, but makes things more clear. */ + bncparent->bheader.id = cpu_to_be32(ASFS_BNODECONTAINER_ID); + bncparent->bheader.ownblock = cpu_to_be32(bcontblock); + btcparent->isleaf = FALSE; + btcparent->nodesize = sizeof(struct BNode); + btcparent->nodecount = 0; + + bn = insertbnode(0, btcparent); + bn->data = cpu_to_be32(newbcontblock); + + asfs_bstore(sb, bhparent); + } + if (bh == NULL) + errorcode = -EIO; + } + + if (errorcode == 0) { + struct fsBNodeContainer *bncparent = (void *) bhparent->b_data; + struct BTreeContainer *btcparent = &bncparent->btc; + int branches1 = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btcparent->nodesize; + + if (be16_to_cpu(btcparent->nodecount) == branches1) { + /* We need to split the parent tree-container first! */ + if ((errorcode = splitbtreecontainer(sb, bhparent)) == 0) { + /* bhparent might have changed after the split and has been released */ + if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) { + bncparent = (void *) bhparent->b_data; + btcparent = &bncparent->btc; + } + } + } + + if (errorcode == 0) { + u32 newbcontblock; + struct buffer_head *bhnew; + + /* We can split this container and add it to the parent + because the parent has enough room. */ + + if ((errorcode = asfs_allocadminspace(sb, &newbcontblock)) == 0 && (bhnew = asfs_getzeroblk(sb, newbcontblock))) { + struct fsBNodeContainer *bncnew = (void *) bhnew->b_data; + struct BTreeContainer *btcnew = &bncnew->btc; + struct fsBNodeContainer *bnc = (void *) bh->b_data; + struct BTreeContainer *btc = &bnc->btc; + int branches2 = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize; + u32 newkey; + + bncnew->bheader.id = cpu_to_be32(ASFS_BNODECONTAINER_ID); + bncnew->bheader.ownblock = cpu_to_be32(newbcontblock); + + btcnew->isleaf = btc->isleaf; + btcnew->nodesize = btc->nodesize; + + btcnew->nodecount = cpu_to_be16(branches2 - branches2 / 2); + + memcpy(btcnew->bnode, (u8 *) btc->bnode + branches2 / 2 * btc->nodesize, (branches2 - branches2 / 2) * btc->nodesize); + newkey = be32_to_cpu(btcnew->bnode[0].key); + + asfs_bstore(sb, bhnew); + asfs_brelse(bhnew); + + btc->nodecount = cpu_to_be16(branches2 / 2); + asfs_bstore(sb, bh); + + bn = insertbnode(newkey, btcparent); + bn->data = cpu_to_be32(newbcontblock); + asfs_bstore(sb, bhparent); + } + } + } + asfs_brelse(bhparent); + } + asfs_brelse(bh); + + return errorcode; +} + +/* Returns created extentbnode - returned_bh need to saved and realesed in caller funkction! */ + +int createextentbnode(struct super_block *sb, u32 key, struct buffer_head **returned_bh, struct BNode **returned_bnode) +{ + int errorcode; + + asfs_debug("createbnode: Creating BNode with key %d\n", key); + + while ((errorcode = findbnode(sb, key, returned_bh, returned_bnode)) == 0) { + struct fsBNodeContainer *bnc = (void *) (*returned_bh)->b_data; + struct BTreeContainer *btc = &bnc->btc; + int extbranches = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize; + + asfs_debug("createbnode: findbnode found block %d\n", be32_to_cpu(((struct fsBlockHeader *) (*returned_bh)->b_data)->ownblock)); + + if (be16_to_cpu(btc->nodecount) < extbranches) { + /* Simply insert new node in this BTreeContainer */ + asfs_debug("createbnode: Simple insert\n"); + *returned_bnode = insertbnode(key, btc); + break; + } else if ((errorcode = splitbtreecontainer(sb, *returned_bh)) != 0) + break; + + /* Loop and try insert it the normal way again :-) */ + } + + return (errorcode); +} + + +/* This routine removes a node from a BTreeContainer indentified + by its key. If no such key exists this routine does nothing. + It correctly handles empty BTreeContainers. */ + +static void removebnode(u32 key, struct BTreeContainer *btc) +{ + struct BNode *bn = btc->bnode; + int n = 0; + + asfs_debug("removebnode: key %d\n", key); + + while (n < be16_to_cpu(btc->nodecount)) { + if (be32_to_cpu(bn->key) == key) { + btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) - 1); + memmove(bn, (u8 *) bn + btc->nodesize, (be16_to_cpu(btc->nodecount) - n) * btc->nodesize); + break; + } + bn = (struct BNode *) ((u8 *) bn + btc->nodesize); + n++; + } +} + +int asfs_deletebnode(struct super_block *sb, struct buffer_head *bh, u32 key) +{ + struct fsBNodeContainer *bnc1 = (void *) bh->b_data; + struct BTreeContainer *btc = &bnc1->btc; + u16 branches = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize; + int errorcode = 0; + + /* Deletes specified internal node. */ + + removebnode(key, btc); + asfs_bstore(sb, bh); + + /* Now checks if the container still contains enough nodes, + and takes action accordingly. */ + + asfs_debug("deletebnode: branches = %d, btc->nodecount = %d\n", branches, be16_to_cpu(btc->nodecount)); + + if (be16_to_cpu(btc->nodecount) < (branches + 1) / 2) { + struct buffer_head *bhparent; + struct buffer_head *bhsec; + + /* nodecount has become to low. We need to merge this Container + with a neighbouring Container, or we need to steal a few nodes + from a neighbouring Container. */ + + /* We get the parent of the container here, so we can find out what + containers neighbour the container which currently hasn't got enough nodes. */ + + if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) { + if (bhparent != NULL) { + struct fsBNodeContainer *bncparent = (void *) bhparent->b_data; + struct BTreeContainer *btcparent = &bncparent->btc; + s16 n; + + asfs_debug("deletebnode: get parent returned block %d.\n", be32_to_cpu(((struct fsBlockHeader *) bhparent->b_data)->ownblock)); + + for (n = 0; n < be16_to_cpu(btcparent->nodecount); n++) + if (btcparent->bnode[n].data == bnc1->bheader.ownblock) + break; + /* n is now the offset of our own bnode. */ + + if (n < be16_to_cpu(btcparent->nodecount) - 1) { /* Check if we have a next neighbour. */ + asfs_debug("deletebnode: using next container - merging blocks %d and %d\n", be32_to_cpu(bnc1->bheader.ownblock), be32_to_cpu(btcparent->bnode[n+1].data)); + + if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btcparent->bnode[n + 1].data), ASFS_BNODECONTAINER_ID))) { + struct fsBNodeContainer *bnc_next = (void *) bhsec->b_data; + struct BTreeContainer *btc_next = &bnc_next->btc; + + if (be16_to_cpu(btc_next->nodecount) + be16_to_cpu(btc->nodecount) > branches) { /* Check if we need to steal nodes. */ + s16 nodestosteal = (be16_to_cpu(btc_next->nodecount) + be16_to_cpu(btc->nodecount)) / 2 - be16_to_cpu(btc->nodecount); + + /* Merging them is not possible. Steal a few nodes then. */ + memcpy((u8 *) btc->bnode + be16_to_cpu(btc->nodecount) * btc->nodesize, btc_next->bnode, nodestosteal * btc->nodesize); + btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + nodestosteal); + asfs_bstore(sb, bh); + + memcpy(btc_next->bnode, (u8 *) btc_next->bnode + btc_next->nodesize * nodestosteal, + btc->nodesize * (be16_to_cpu(btc_next->nodecount) - nodestosteal)); + btc_next->nodecount = cpu_to_be16(be16_to_cpu(btc_next->nodecount) - nodestosteal); + asfs_bstore(sb, bhsec); + + btcparent->bnode[n + 1].key = btc_next->bnode[0].key; + asfs_bstore(sb, bhparent); + } else { /* Merging is possible. */ + memcpy((u8 *) btc->bnode + btc->nodesize * be16_to_cpu(btc->nodecount), btc_next->bnode, btc->nodesize * be16_to_cpu(btc_next->nodecount)); + btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + be16_to_cpu(btc_next->nodecount)); + asfs_bstore(sb, bh); + + if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock))) == 0) + errorcode = asfs_deletebnode(sb, bhparent, be32_to_cpu(btcparent->bnode[n + 1].key)); + } + asfs_brelse(bhsec); + } + } else if (n > 0) { /* Check if we have a previous neighbour. */ + asfs_debug("deletebnode: using prev container.\n"); + + if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btcparent->bnode[n - 1].data), ASFS_BNODECONTAINER_ID)) == 0) { + struct fsBNodeContainer *bnc2 = (void *) bhsec->b_data; + struct BTreeContainer *btc2 = &bnc2->btc; + + if (be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount) > branches) { + /* Merging them is not possible. Steal a few nodes then. */ + s16 nodestosteal = (be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount)) / 2 - be16_to_cpu(btc->nodecount); + + memmove((u8 *) btc->bnode + nodestosteal * btc->nodesize, btc->bnode, be16_to_cpu(btc->nodecount) * btc->nodesize); + btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + nodestosteal); + memcpy(btc->bnode, (u8 *) btc2->bnode + (be16_to_cpu(btc2->nodecount) - nodestosteal) * btc2->nodesize, nodestosteal * btc->nodesize); + + asfs_bstore(sb, bh); + + btc2->nodecount = cpu_to_be16(be16_to_cpu(btc2->nodecount) - nodestosteal); + asfs_bstore(sb, bhsec); + + btcparent->bnode[n].key = btc->bnode[0].key; + asfs_bstore(sb, bhparent); + } else { /* Merging is possible. */ + memcpy((u8 *) btc2->bnode + be16_to_cpu(btc2->nodecount) * btc2->nodesize, btc->bnode, be16_to_cpu(btc->nodecount) * btc->nodesize); + btc2->nodecount = cpu_to_be16(be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount)); + asfs_bstore(sb, bhsec); + + if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock))) == 0) + errorcode = asfs_deletebnode(sb, bhparent, be32_to_cpu(btcparent->bnode[n].key)); + } + asfs_brelse(bhsec); + } + } + /* else + { + // Never happens, except for root and then we don't care. + } */ + } else if (btc->nodecount == 1) { + /* No parent, so must be root. */ + + asfs_debug("deletebnode: no parent so must be root\n"); + + if (btc->isleaf == FALSE) { + struct fsBNodeContainer *bnc3 = (void *) bh->b_data; + + /* The current root has only 1 node. We now copy the data of this node into the + root and promote that data to be the new root. The rootblock number stays the + same that way. */ + + if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btc->bnode[0].data), ASFS_BNODECONTAINER_ID))) { + u32 blockno = be32_to_cpu(((struct fsBlockHeader *) bh->b_data)->ownblock); + memcpy(bh->b_data, bhsec->b_data, sb->s_blocksize); + bnc3->bheader.ownblock = cpu_to_be32(blockno); + + asfs_bstore(sb, bh); + errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock)); + asfs_brelse(bhsec); + } else + errorcode = -EIO; + } + /* If not, then root contains leafs. */ + } + + asfs_debug("deletebnode: almost done\n"); + /* otherwise, it must be the root, and the root is allowed + to contain less than the minimum amount of nodes. */ + + } + if (bhparent != NULL) + asfs_brelse(bhparent); + } + + return errorcode; +} + + /* Deletes an fsExtentBNode structure by key and any fsExtentBNodes linked to it. + This function DOES NOT fix the next pointer in a possible fsExtentBNode which + might have been pointing to the first BNode we are deleting. Make sure you check + this yourself, if needed. + + If key is zero, than this function does nothing. */ + +int asfs_deleteextents(struct super_block *sb, u32 key) +{ + struct buffer_head *bh; + struct fsExtentBNode *ebn; + int errorcode = 0; + + asfs_debug("deleteextents: Entry -- deleting extents from key %d\n", key); + + while (key != 0 && (errorcode = findbnode(sb, key, &bh, (struct BNode **) &ebn)) == 0) { + /* node to be deleted located. */ + key = be32_to_cpu(ebn->next); + if ((errorcode = asfs_freespace(sb, be32_to_cpu(ebn->key), be16_to_cpu(ebn->blocks))) != 0) + break; + + if ((errorcode = asfs_deletebnode(sb, bh, be32_to_cpu(ebn->key))) != 0) + break; + + asfs_brelse(bh); + } + + return (errorcode); +} + + /* This function adds /blocks/ blocks starting at block /newspace/ to a file + identified by /objectnode/ and /lastextentbnode/. /io_lastextentbnode/ can + be zero if there is no ExtentBNode chain attached to this file yet. + /blocks/ ranges from 1 to 8192. To be able to extend Extents which are + almost full, it is wise to make this value no higher than 8192 blocks. + /io_lastextentbnode/ will contain the new lastextentbnode value when this + function completes. + If there was no chain yet, then this function will create a new one. */ + +int asfs_addblocks(struct super_block *sb, u16 blocks, u32 newspace, u32 objectnode, u32 *io_lastextentbnode) +{ + struct buffer_head *bh; + struct fsExtentBNode *ebn; + int errorcode = 0; + + if (*io_lastextentbnode != 0) { + /* There was already a ExtentBNode chain for this file. Extending it. */ + + asfs_debug(" addblocks: Extending existing ExtentBNode chain.\n"); + + if ((errorcode = asfs_getextent(sb, *io_lastextentbnode, &bh, &ebn)) == 0) { + if (be32_to_cpu(ebn->key) + be16_to_cpu(ebn->blocks) == newspace && be16_to_cpu(ebn->blocks) + blocks < 65536) { + /* It is possible to extent the last ExtentBNode! */ + asfs_debug(" addblocks: Extending last ExtentBNode.\n"); + + ebn->blocks = cpu_to_be16(be16_to_cpu(ebn->blocks) + blocks); + + asfs_bstore(sb, bh); + asfs_brelse(bh); + } else { + /* It isn't possible to extent the last ExtentBNode so we create + a new one and link it to the last ExtentBNode. */ + + ebn->next = cpu_to_be32(newspace); + asfs_bstore(sb, bh); + asfs_brelse(bh); + + if ((errorcode = createextentbnode(sb, newspace, &bh, (struct BNode **) &ebn)) == 0) { + asfs_debug(" addblocks: Created new ExtentBNode.\n"); + + ebn->key = cpu_to_be32(newspace); + ebn->prev = cpu_to_be32(*io_lastextentbnode); + ebn->next = 0; + ebn->blocks = cpu_to_be16(blocks); + + *io_lastextentbnode = newspace; + + asfs_bstore(sb, bh); + asfs_brelse(bh); + + ASFS_SB(sb)->block_rovingblockptr = newspace + blocks; + + /* to be changed in the future */ +/* if (ASFS_SB(sb)->block_rovingblockptr >= ASFS_SB(sb)->totalblocks) + ASFS_SB(sb)->block_rovingblockptr = 0;*/ + } + } + } + } else { + /* There is no ExtentBNode chain yet for this file. Attaching one! */ + if ((errorcode = createextentbnode(sb, newspace, &bh, (struct BNode **) &ebn)) == 0) { + asfs_debug(" addblocks: Created new ExtentBNode chain.\n"); + + ebn->key = cpu_to_be32(newspace); + ebn->prev = cpu_to_be32(objectnode + 0x80000000); + ebn->next = 0; + ebn->blocks = cpu_to_be16(blocks); + + *io_lastextentbnode = newspace; + + asfs_bstore(sb, bh); + asfs_brelse(bh); + + ASFS_SB(sb)->block_rovingblockptr = newspace + blocks; + +/* if (ASFS_SB(sb)->block_rovingblockptr >= ASFS_SB(sb)->totalblocks) + ASFS_SB(sb)->block_rovingblockptr = 0;*/ + } + } + + asfs_debug(" addblocks: done.\n"); + + return errorcode; +} +#endif diff -puN /dev/null fs/asfs/file.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/file.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,251 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta7 + * + * Copyright (C) 2003,2004 Marek 'March' Szyprowski + * + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +#include + +static int +asfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) +{ + struct buffer_head *ebn_bh; + struct fsExtentBNode extent, *ebn_p; + u32 filedata; + unsigned long pos; + struct super_block *sb = inode->i_sb; +#ifdef CONFIG_ASFS_RW + int error; + struct buffer_head *bh; + struct fsObject *obj; +#endif + + asfs_debug("ASFS: get_block(%lu, %ld, %d)\n", inode->i_ino, block, create); + + if (block < 0) { + printk(KERN_ERR "ASFS: asfsget_block: requested block (%ld) < 0!\n", block); + return -EIO; + } else if (block >= inode->i_blocks && !create) { + printk(KERN_ERR "ASFS: asfsget_block: strange block request %ld!\n", block); + return -EIO; + } + + if (create) +#ifdef CONFIG_ASFS_RW + ASFS_I(inode)->modified = TRUE; +#else + return -EROFS; +#endif + + if (block < inode->i_blocks) + create = 0; + + lock_super(sb); + +#ifdef CONFIG_ASFS_RW + if (create) { + int blockstoadd; + u32 newspace, addedblocks; + + blockstoadd = block - inode->i_blocks + 1; + + if (blockstoadd < ASFS_BLOCKCHUNKS) + blockstoadd = ASFS_BLOCKCHUNKS; + + asfs_debug("ASFS get_block: Trying to add %d blocks to file\n", blockstoadd); + + if ((error = asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) { + unlock_super(sb); + return error; + } + + if ((error = asfs_addblockstofile(sb, bh, obj, blockstoadd, &newspace, &addedblocks)) != 0) { + asfs_brelse(bh); + unlock_super(sb); + return error; + } + ASFS_I(inode)->mmu_private += addedblocks * sb->s_blocksize; + inode->i_blocks += addedblocks; + ASFS_I(inode)->ext_cache.key = 0; + ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); + asfs_brelse(bh); + } +#endif + + if (ASFS_I(inode)->ext_cache.key > 0 && ASFS_I(inode)->ext_cache.startblock <= block) { + extent.key = ASFS_I(inode)->ext_cache.key; + extent.next = ASFS_I(inode)->ext_cache.next; + extent.blocks = ASFS_I(inode)->ext_cache.blocks; + pos = ASFS_I(inode)->ext_cache.startblock; + } else { + if (asfs_getextent(inode->i_sb, ASFS_I(inode)->firstblock, &ebn_bh, &ebn_p) != 0) { + unlock_super(sb); + return -EIO; + } + extent.key = be32_to_cpu(ebn_p->key); + extent.next = be32_to_cpu(ebn_p->next); + extent.blocks = be16_to_cpu(ebn_p->blocks); + pos = 0; + asfs_brelse(ebn_bh); + } + ebn_p = &extent; + filedata = ebn_p->next; + + while (pos + ebn_p->blocks <= block && ebn_p->next != 0 && pos < inode->i_blocks) { + pos += ebn_p->blocks; + if (asfs_getextent(inode->i_sb, filedata, &ebn_bh, &ebn_p) != 0) { + unlock_super(sb); + return -EIO; + } + extent.key = be32_to_cpu(ebn_p->key); + extent.next = be32_to_cpu(ebn_p->next); + extent.blocks = be16_to_cpu(ebn_p->blocks); + ebn_p = &extent; + filedata = ebn_p->next; + asfs_brelse(ebn_bh); + } + + unlock_super(sb); + + map_bh(bh_result, inode->i_sb, (sector_t) (ebn_p->key + block - pos)); + + if (create) + set_buffer_new(bh_result); + + asfs_debug("ASFS: get_block - mapped block %lu\n", ebn_p->key + block - pos); + + ASFS_I(inode)->ext_cache.startblock = pos; + ASFS_I(inode)->ext_cache.key = ebn_p->key; + ASFS_I(inode)->ext_cache.next = ebn_p->next; + ASFS_I(inode)->ext_cache.blocks = ebn_p->blocks; + + return 0; +} + +int asfs_readpage(struct file *file, struct page *page) +{ + asfs_debug("ASFS: %s\n", __FUNCTION__); + return block_read_full_page(page, asfs_get_block); +} + +sector_t asfs_bmap(struct address_space *mapping, sector_t block) +{ + asfs_debug("ASFS: %s\n", __FUNCTION__); + return generic_block_bmap(mapping,block,asfs_get_block); +} + +#ifdef CONFIG_ASFS_RW + +int asfs_writepage(struct page *page, struct writeback_control *wbc) +{ + asfs_debug("ASFS: %s\n", __FUNCTION__); + return block_write_full_page(page, asfs_get_block, wbc); +} + +int asfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +{ + asfs_debug("ASFS: %s\n", __FUNCTION__); + return cont_prepare_write(page, from, to, asfs_get_block, &ASFS_I(page->mapping->host)->mmu_private); +} + +void asfs_truncate(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh; + struct fsObject *obj; + + asfs_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n", + (u32)inode->i_ino, (u32)ASFS_I(inode)->mmu_private, (u32)inode->i_size); + + if (inode->i_size > ASFS_I(inode)->mmu_private) { + printk("ASFS: enlarging file is not supported yet\n"); + return; + } + + lock_super(sb); + + if ((asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) { + unlock_super(sb); + return; + } + + if (asfs_truncateblocksinfile(sb, bh, obj, inode->i_size) != 0) { + asfs_brelse(bh); + unlock_super(sb); + return; + } + + obj->object.file.size = cpu_to_be32(inode->i_size); + ASFS_I(inode)->mmu_private = inode->i_size; + ASFS_I(inode)->modified = TRUE; + inode->i_blocks = (be32_to_cpu(obj->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + asfs_bstore(sb, bh); + asfs_brelse(bh); + + unlock_super(sb); +} + +int asfs_file_open(struct inode *inode, struct file *filp) +{ + if (atomic_read(&filp->f_count) != 1) + return 0; + asfs_debug("ASFS: file open (node %d)\n", (int)inode->i_ino); + return 0; +} + +int asfs_file_release(struct inode *inode, struct file *filp) +{ + int error = 0; + + asfs_debug("ASFS: file release (node %d oc %d)\n", (int)inode->i_ino, atomic_read(&filp->f_count)); + + if (atomic_read(&filp->f_count) != 0) + return 0; + + if (ASFS_I(inode)->modified == TRUE) { + struct buffer_head *bh; + struct fsObject *obj; + lock_super(inode->i_sb); + + if ((error = asfs_readobject(inode->i_sb, inode->i_ino, &bh, &obj)) != 0) { + unlock_super(inode->i_sb); + return error; + } + + obj->datemodified = cpu_to_be32(inode->i_mtime.tv_sec - (365*8+2)*24*60*60); + if (inode->i_mode & S_IFREG) { + error = asfs_truncateblocksinfile(inode->i_sb, bh, obj, (u32)inode->i_size); + obj->object.file.size = cpu_to_be32(inode->i_size); + ASFS_I(inode)->mmu_private = inode->i_size; + inode->i_blocks = (be32_to_cpu(obj->object.file.size) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + } + asfs_bstore(inode->i_sb, bh); + + unlock_super(inode->i_sb); + + asfs_brelse(bh); + } + ASFS_I(inode)->modified = FALSE; + + return error; +} + +#endif diff -puN /dev/null fs/asfs/inode.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/inode.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,426 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta8 + * + * Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski + * + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +#include + +/* 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, +#ifdef CONFIG_ASFS_RW + .writepage = asfs_writepage, + .prepare_write = asfs_prepare_write, + .commit_write = generic_commit_write, +#endif +}; + +static struct file_operations asfs_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .mmap = generic_file_mmap, +#ifdef CONFIG_ASFS_RW + .write = generic_file_write, + .open = asfs_file_open, + .release = asfs_file_release, + .fsync = file_fsync, +#endif +}; + +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, +#ifdef CONFIG_ASFS_RW + .create = asfs_create, + .unlink = asfs_unlink, + .symlink = asfs_symlink, + .mkdir = asfs_mkdir, + .rmdir = asfs_rmdir, + .rename = asfs_rename, +/* .setattr = asfs_notify_change,*/ +#endif +}; + +static struct inode_operations asfs_file_inode_operations = { +#ifdef CONFIG_ASFS_RW + .truncate = asfs_truncate, +/* .setattr = asfs_notify_change,*/ +#endif +}; + +static struct address_space_operations asfs_symlink_aops = { + .readpage = asfs_symlink_readpage, +}; + +static struct inode_operations asfs_symlink_inode_operations = { + .readlink = page_readlink, + .follow_link = page_follow_link_light, + .put_link = page_put_link, +#ifdef CONFIG_ASFS_RW +/* .setattr = asfs_notify_change,*/ +#endif +}; + +void asfs_read_locked_inode(struct inode *inode, void *arg) +{ + struct super_block *sb = inode->i_sb; + struct fsObject *obj = arg; + + inode->i_mode = ASFS_SB(sb)->mode; + inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = be32_to_cpu(obj->datemodified) + (365*8+2)*24*60*60; + /* Linux: seconds since 01-01-1970, AmigaSFS: seconds since 01-01-1978 */ + inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0; + inode->i_uid = ASFS_SB(sb)->uid; + inode->i_gid = ASFS_SB(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", \ + be32_to_cpu(obj->object.dir.firstdirblock), be32_to_cpu(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_I(inode)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock); + ASFS_I(inode)->hashtable = be32_to_cpu(obj->object.dir.hashtable); + ASFS_I(inode)->modified = 0; + } else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK)) { + asfs_debug("symlink\n"); + inode->i_size = 0; + inode->i_op = &asfs_symlink_inode_operations; + inode->i_mapping->a_ops = &asfs_symlink_aops; + inode->i_mode |= S_IFLNK | S_IRWXUGO; + ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); + } else { + asfs_debug("file (Size: %u, FirstBlock: %u)\n", be32_to_cpu(obj->object.file.size), be32_to_cpu(obj->object.file.data)); + inode->i_size = be32_to_cpu(obj->object.file.size); + inode->i_blocks = (be32_to_cpu(obj->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + inode->i_op = &asfs_file_inode_operations; + inode->i_fop = &asfs_file_operations; + inode->i_mapping->a_ops = &asfs_aops; + inode->i_mode |= S_IFREG; + ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); + ASFS_I(inode)->ext_cache.startblock = 0; + ASFS_I(inode)->ext_cache.key = 0; + ASFS_I(inode)->mmu_private = inode->i_size; + } + return; +} + +struct inode *asfs_get_root_inode(struct super_block *sb) +{ + struct inode *result = NULL; + struct fsObject *obj; + struct buffer_head *bh; + + asfs_debug("asfs_get_root_inode\n"); + + if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { + obj = &(((struct fsObjectContainer *)bh->b_data)->object[0]); + if (be32_to_cpu(obj->objectnode) > 0) + result = iget_locked(sb, be32_to_cpu(obj->objectnode)); + + if (result != NULL && result->i_state & I_NEW) { + asfs_read_locked_inode(result, obj); + unlock_new_inode(result); + } + asfs_brelse(bh); + } + return result; +} + +#ifdef CONFIG_ASFS_RW + +static void asfs_sync_dir_inode(struct inode *dir, struct fsObject *obj) +{ + ASFS_I(dir)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock); + ASFS_I(dir)->modified = 1; + dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME; + obj->datemodified = cpu_to_be32(dir->i_mtime.tv_sec - (365*8+2)*24*60*60); +} + +enum { it_file, it_dir, it_link }; + +static int asfs_create_object(struct inode *dir, struct dentry *dentry, int mode, int type, const char *symname) +{ + int error; + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct buffer_head *bh, *dir_bh; + struct fsObject obj_data, *dir_obj, *obj; + u8 *name = (u8 *) dentry->d_name.name; + u8 bufname[ASFS_MAXFN_BUF]; + + asfs_debug("asfs_create_obj %s in dir node %d\n", name, (int)dir->i_ino); + + asfs_translate(bufname, name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF); + if ((error = asfs_check_name(bufname, strlen(bufname))) != 0) + return error; + + sb = dir->i_sb; + inode = new_inode(sb); + if (!inode) + return -ENOMEM; + + memset(&obj_data, 0, sizeof(struct fsObject)); + + obj_data.protection = cpu_to_be32(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE); + obj_data.datemodified = cpu_to_be32(inode->i_mtime.tv_sec - (365*8+2)*24*60*60); + switch (type) { + case it_dir: + obj_data.bits = OTYPE_DIR; + break; + case it_link: + obj_data.bits = OTYPE_LINK; + break; + default: + break; + } + + lock_super(sb); + + if ((error = asfs_readobject(sb, dir->i_ino, &dir_bh, &dir_obj)) != 0) { + dec_count(inode); + unlock_super(sb); + return error; + } + + bh = dir_bh; + obj = dir_obj; + + if ((error = asfs_createobject(sb, &bh, &obj, &obj_data, bufname, FALSE)) != 0) { + asfs_brelse(dir_bh); + dec_count(inode); + unlock_super(sb); + return error; + } + + inode->i_ino = be32_to_cpu(obj->objectnode); + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_size = inode->i_blocks = inode->i_blksize = 0; + inode->i_uid = dir->i_uid; + inode->i_gid = dir->i_gid; + inode->i_mode = mode | ASFS_SB(sb)->mode; + + switch (type) { + case it_dir: + inode->i_mode |= S_IFDIR; + inode->i_op = &asfs_dir_inode_operations; + inode->i_fop = &asfs_dir_operations; + ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock); + ASFS_I(inode)->hashtable = be32_to_cpu(obj->object.dir.hashtable); + ASFS_I(inode)->modified = 0; + break; + case it_file: + inode->i_mode |= S_IFREG; + inode->i_op = &asfs_file_inode_operations; + inode->i_fop = &asfs_file_operations; + inode->i_mapping->a_ops = &asfs_aops; + ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); + ASFS_I(inode)->ext_cache.startblock = 0; + ASFS_I(inode)->ext_cache.key = 0; + ASFS_I(inode)->mmu_private = inode->i_size; + break; + case it_link: + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &asfs_symlink_aops; + ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); + error = asfs_write_symlink(inode, symname); + break; + default: + break; + } + + asfs_bstore(sb, bh); + insert_inode_hash(inode); + mark_inode_dirty(inode); + d_instantiate(dentry, inode); + asfs_sync_dir_inode(dir, dir_obj); + asfs_bstore(sb, dir_bh); + + unlock_super(sb); + asfs_brelse(bh); + asfs_brelse(dir_bh); + + return error; +} + +int asfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) +{ + return asfs_create_object(dir, dentry, mode, it_file, NULL); +} + +int asfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + return asfs_create_object(dir, dentry, mode, it_dir, NULL); +} + +int asfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + return asfs_create_object(dir, dentry, 0, it_link, symname); +} + +int asfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + asfs_debug("ASFS: %s\n", __FUNCTION__); + + if (ASFS_I(dentry->d_inode)->firstblock != 0) + return -ENOTEMPTY; + + return asfs_unlink(dir, dentry); +} + +int asfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + int error; + struct super_block *sb = dir->i_sb; + struct buffer_head *bh, *dir_bh; + struct fsObject *dir_obj, *obj; + + asfs_debug("ASFS: %s\n", __FUNCTION__); + + lock_super(sb); + + if ((error = asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) { + unlock_super(sb); + return error; + } + if ((error = asfs_deleteobject(sb, bh, obj)) != 0) { + asfs_brelse(bh); + unlock_super(sb); + return error; + } + asfs_brelse(bh); + + /* directory data could change after removing the object */ + if ((error = asfs_readobject(sb, dir->i_ino, &dir_bh, &dir_obj)) != 0) { + unlock_super(sb); + return error; + } + + asfs_sync_dir_inode(dir, dir_obj); + asfs_bstore(sb, dir_bh); + + dec_count(inode); + unlock_super(sb); + asfs_brelse(dir_bh); + + return 0; +} + +int asfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) +{ + struct super_block *sb = old_dir->i_sb; + struct buffer_head *src_bh, *old_bh, *new_bh; + int error; + struct fsObject *src_obj, *old_obj, *new_obj; + u8 bufname[ASFS_MAXFN_BUF]; + + asfs_debug("ASFS: rename (old=%u,\"%*s\" to new=%u,\"%*s\")\n", + (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name, + (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name); + + asfs_translate(bufname, (u8 *) new_dentry->d_name.name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF); + if ((error = asfs_check_name(bufname, strlen(bufname))) != 0) + return error; + + + /* Unlink destination if it already exists */ + if (new_dentry->d_inode) + if ((error = asfs_unlink(new_dir, new_dentry)) != 0) + return error; + + lock_super(sb); + + if ((error = asfs_readobject(sb, old_dentry->d_inode->i_ino, &src_bh, &src_obj)) != 0) { + unlock_super(sb); + return error; + } + if ((error = asfs_readobject(sb, new_dir->i_ino, &new_bh, &new_obj)) != 0) { + asfs_brelse(src_bh); + unlock_super(sb); + return error; + } + + if ((error = asfs_renameobject(sb, src_bh, src_obj, new_bh, new_obj, bufname)) != 0) { + asfs_brelse(src_bh); + asfs_brelse(new_bh); + unlock_super(sb); + return error; + } + asfs_brelse(src_bh); + asfs_brelse(new_bh); + + if ((error = asfs_readobject(sb, old_dir->i_ino, &old_bh, &old_obj)) != 0) { + unlock_super(sb); + return error; + } + if ((error = asfs_readobject(sb, new_dir->i_ino, &new_bh, &new_obj)) != 0) { + asfs_brelse(old_bh); + unlock_super(sb); + return error; + } + + asfs_sync_dir_inode(old_dir, old_obj); + asfs_sync_dir_inode(new_dir, new_obj); + + asfs_bstore(sb, new_bh); + asfs_bstore(sb, old_bh); + + unlock_super(sb); + asfs_brelse(old_bh); + asfs_brelse(new_bh); + + mark_inode_dirty(old_dir); + mark_inode_dirty(new_dir); + + return 0; +} + +/* +int asfs_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + + asfs_debug("ASFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid); + + error = inode_change_ok(inode,attr); + + return error; +} +*/ +#endif diff -puN /dev/null fs/asfs/Makefile --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/Makefile 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,8 @@ +# +# Makefile for the linux asfs filesystem routines. +# + +obj-$(CONFIG_ASFS_FS) += asfs.o + +asfs-y += dir.o extents.o file.o inode.o namei.o nodes.o objects.o super.o symlink.o +asfs-$(CONFIG_ASFS_RW) += adminspace.o bitfuncs.o diff -puN /dev/null fs/asfs/namei.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/namei.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,197 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta10 + * + * Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski + * + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +static inline u8 asfs_upperchar(u8 c) +{ + if ((c >= 224 && c <= 254 && c != 247) || (c >= 'a' && c <= 'z')) + c -= 32; + return (c); +} + +u8 asfs_lowerchar(u8 c) +{ + if ((c >= 192 && c <= 222 && c != 215) || (c >= 'A' && c <= 'Z')) + c += 32; + return (c); +} + +static inline u8 asfs_nls_upperchar(u8 c, struct nls_table *t) +{ + if (t) { + u8 nc = t->charset2upper[c]; + return nc ? nc : c; + } else + return asfs_upperchar(c); +} + +/* Check if the name is valid for a asfs object. */ + +inline int asfs_check_name(const u8 *name, int len) +{ + int i; + + if (len > ASFS_MAXFN) + return -ENAMETOOLONG; + + for (i = 0; i < len; i++) + if (name[i] < ' ' || name[i] == ':' || (name[i] > 0x7e && name[i] < 0xa0)) + return -EINVAL; + + return 0; +} + +/* Note: the dentry argument is the parent dentry. */ + +static int asfs_hash_dentry(struct dentry *dentry, struct qstr *qstr) +{ + struct super_block *sb = dentry->d_inode->i_sb; + const u8 *name = qstr->name; + unsigned long hash; + int i; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + + i = asfs_check_name(qstr->name,qstr->len); + if (i) + return i; + + hash = init_name_hash(); + + if (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) + for (i=qstr->len; i > 0; name++, i--) + hash = partial_name_hash(*name, hash); + else + for (i=qstr->len; i > 0; name++, i--) + hash = partial_name_hash(asfs_nls_upperchar(*name, nls_io), hash); + + qstr->hash = end_name_hash(hash); + + return 0; +} + +static int asfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) +{ + struct super_block *sb = dentry->d_inode->i_sb; + const u8 *aname = a->name; + const u8 *bname = b->name; + int len; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + + /* 'a' is the qstr of an already existing dentry, so the name + * must be valid. 'b' must be validated first. + */ + + if (asfs_check_name(b->name,b->len)) + return 1; + + if (a->len != b->len) + return 1; + + if (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) { + for (len=a->len; len > 0; len--) + if (*aname++ != *bname++) + return 1; + } else { + for (len=a->len; len > 0; len--) + if (asfs_nls_upperchar(*aname++, nls_io) != asfs_nls_upperchar(*bname++, nls_io)) + return 1; + } + + return 0; +} + +struct dentry_operations asfs_dentry_operations = { + d_hash: asfs_hash_dentry, + d_compare: asfs_compare_dentry, +}; + +int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t) +{ + if (casesensitive) { + while (*s == *ct && *ct != '\0' && *ct != '/') { + s++; + ct++; + } + } else { + while (asfs_nls_upperchar(*s, t) == asfs_nls_upperchar(*ct, t) && *ct != '\0' + && *ct != '/') { + s++; + ct++; + } + } + return (*s == '\0' && (*ct == '\0' || *ct == '/')) ? 0 : *ct - *s; +} + +u16 asfs_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 + asfs_upperchar(c); + c = *++name; + } + } + return hashval; +} + +void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from, int limit) +{ + wchar_t uni; + int i, len; + int from_len, to_len = limit; + + if (nls_to) { + from_len = strlen(from); + for (i=0; i < from_len && to_len > 1; ) { + len = nls_from->char2uni(&from[i], from_len-i, &uni); + if (len > 0) { + i += len; + len = nls_to->uni2char(uni, to, to_len); + if (len > 0) { + to += len; + to_len -= len; + } + } else + i++; + if (len < 0) { + *to++ = '?'; + to_len--; + } + } + *to = '\0'; + } else { + strncpy (to, from, limit); + to[limit-1] = '\0'; + } +} diff -puN /dev/null fs/asfs/nodes.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/nodes.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,455 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta7 + * + * This file contains some parts of the original amiga version of + * SmartFilesystem source code. + * + * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, + * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek + * + * Adapted and modified by Marek 'March' Szyprowski + * + */ + +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +#include + +/* Finds a specific node by number. */ +int asfs_getnode(struct super_block *sb, u32 nodeno, struct buffer_head **ret_bh, struct fsObjectNode **ret_node) +{ + struct buffer_head *bh; + struct fsNodeContainer *nodecont; + u32 nodeindex = ASFS_SB(sb)->objectnoderoot; + + while ((bh = asfs_breadcheck(sb, nodeindex, ASFS_NODECONTAINER_ID))) { + nodecont = (struct fsNodeContainer *) bh->b_data; + + if (be32_to_cpu(nodecont->nodes) == 1) { + *ret_node = (struct fsObjectNode *) ((u8 *) nodecont->node + NODE_STRUCT_SIZE * (nodeno - be32_to_cpu(nodecont->nodenumber))); + *ret_bh = bh; + return 0; + } 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); + } + asfs_brelse(bh); + } + if (bh == NULL) + return -EIO; + return -ENOENT; +} + +#ifdef CONFIG_ASFS_RW + + /* Looks for the parent of the passed-in buffer_head (fsNodeContainer) + starting from the root. It returns an error if any error occured. + If error is 0 and io_bh is NULL as well, then there was no parent (ie, + you asked parent of the root). Otherwise io_bh should contain the + parent of the passed-in NodeContainer. */ + +static int parentnodecontainer(struct super_block *sb, struct buffer_head **io_bh) +{ + u32 noderoot = ASFS_SB(sb)->objectnoderoot; + u32 childblock = be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock); + u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) (*io_bh)->b_data)->nodenumber); + int errorcode = 0; + + if (noderoot == childblock) { + *io_bh = NULL; + return 0; + } + + while ((*io_bh = asfs_breadcheck(sb, noderoot, ASFS_NODECONTAINER_ID))) { + struct fsNodeContainer *nc = (void *) (*io_bh)->b_data; + + if (be32_to_cpu(nc->nodes) == 1) { + /* We've descended the tree to a leaf NodeContainer, something + which should never happen if the passed-in io_bh had + contained a valid fsNodeContainer. */ + printk("ASFS: Failed to locate the parent NodeContainer - node tree is corrupted!\n"); + *io_bh = NULL; + return -EIO; + } else { + u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes); + noderoot = be32_to_cpu(nc->node[containerentry]) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY); + } + + if (noderoot == childblock) + break; + + asfs_brelse(*io_bh); + } + + if (*io_bh == NULL) + return -EIO; + + return errorcode; +} + + +static int isfull(struct super_block *sb, struct fsNodeContainer *nc) +{ + u32 *p = nc->node; + s16 n = NODECONT_BLOCK_COUNT; + + while (--n >= 0) { + if (*p == 0 || (be32_to_cpu(*p) & 0x00000001) == 0) { + break; + } + p++; + } + + return n < 0; +} + +static int markparentfull(struct super_block *sb, struct buffer_head *bh) +{ + u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) (bh->b_data))->nodenumber); + int errorcode; + + if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != 0) { + struct fsNodeContainer *nc = (void *) bh->b_data; + u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes); + + nc->node[containerentry] = cpu_to_be32(be32_to_cpu(nc->node[containerentry]) | 0x00000001); + + asfs_bstore(sb, bh); + + if (isfull(sb, nc)) { /* This container now is full as well! Mark the next higher up container too then! */ + return markparentfull(sb, bh); + } + asfs_brelse(bh); + } + + return errorcode; +} + +static int addnewnodelevel(struct super_block *sb, u16 nodesize) +{ + struct buffer_head *bh; + u32 noderoot = ASFS_SB(sb)->objectnoderoot; + int errorcode; + + /* Adds a new level to the Node tree. */ + + asfs_debug("addnewnodelevel: Entry\n"); + + if ((bh = asfs_breadcheck(sb, noderoot, ASFS_NODECONTAINER_ID))) { + struct buffer_head *newbh; + u32 newblock; + + if ((errorcode = asfs_allocadminspace(sb, &newblock)) == 0 && (newbh = asfs_getzeroblk(sb, newblock))) { + struct fsNodeContainer *nc = (void *) bh->b_data; + struct fsNodeContainer *newnc = (void *) newbh->b_data; + + /* The newly allocated block will become a copy of the current root. */ + + newnc->bheader.id = cpu_to_be32(ASFS_NODECONTAINER_ID); + newnc->bheader.ownblock = cpu_to_be32(newblock); + newnc->nodenumber = nc->nodenumber; + newnc->nodes = nc->nodes; + memcpy(newnc->node, nc->node, sb->s_blocksize - sizeof(struct fsNodeContainer)); + + asfs_bstore(sb, newbh); + asfs_brelse(newbh); + + /* The current root will now be transformed into a new root. */ + + if (be32_to_cpu(nc->nodes) == 1) + nc->nodes = cpu_to_be32((sb->s_blocksize - sizeof(struct fsNodeContainer)) / nodesize); + else + nc->nodes = cpu_to_be32(be32_to_cpu(nc->nodes) * NODECONT_BLOCK_COUNT); + + nc->node[0] = cpu_to_be32((newblock << (sb->s_blocksize_bits - ASFS_BLCKFACCURACY)) + 1); /* Tree is full from that point! */ + memset(&nc->node[1], 0, sb->s_blocksize - sizeof(struct fsNodeContainer) - 4); + + asfs_bstore(sb, bh); + } + asfs_brelse(bh); + } else + errorcode = -EIO; + + return errorcode; +} + +static int createnodecontainer(struct super_block *sb, u32 nodenumber, u32 nodes, u32 * returned_block) +{ + struct buffer_head *bh; + int errorcode; + u32 newblock; + + asfs_debug("createnodecontainer: nodenumber = %u, nodes = %u\n", nodenumber, nodes); + + if ((errorcode = asfs_allocadminspace(sb, &newblock)) == 0 && (bh = asfs_getzeroblk(sb, newblock))) { + struct fsNodeContainer *nc = (void *) bh->b_data; + + nc->bheader.id = cpu_to_be32(ASFS_NODECONTAINER_ID); + nc->bheader.ownblock = cpu_to_be32(newblock); + + nc->nodenumber = cpu_to_be32(nodenumber); + nc->nodes = cpu_to_be32(nodes); + + asfs_bstore(sb, bh); + asfs_brelse(bh); + *returned_block = newblock; + } + + return errorcode; +} + + /* This function creates a new fsNode structure in a fsNodeContainer. If needed + it will create a new fsNodeContainers and a new fsNodeIndexContainer. */ + +int asfs_createnode(struct super_block *sb, struct buffer_head **returned_bh, struct fsNode **returned_node, u32 * returned_nodeno) +{ + u16 nodecount = (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE; + u32 noderoot = ASFS_SB(sb)->objectnoderoot; + u32 nodeindex = noderoot; + int errorcode = 0; + + while ((*returned_bh = asfs_breadcheck(sb, nodeindex, ASFS_NODECONTAINER_ID))) { + struct fsNodeContainer *nc = (void *) (*returned_bh)->b_data; + + if (be32_to_cpu(nc->nodes) == 1) { /* Is it a leaf-container? */ + struct fsNode *n; + s16 i = nodecount; + + n = (struct fsNode *) nc->node; + + while (i-- > 0) { + if (n->data == 0) + break; + + n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE); + } + + if (i >= 0) { + /* Found an empty fsNode structure! */ + *returned_node = n; + *returned_nodeno = be32_to_cpu(nc->nodenumber) + ((u8 *) n - (u8 *) nc->node) / NODE_STRUCT_SIZE; + + asfs_debug("createnode: Created Node %d\n", *returned_nodeno); + + /* Below we continue to look through the NodeContainer block. We skip the entry + we found to be unused, and see if there are any more unused entries. If we + do not find any more unused entries then this container is now full. */ + + n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE); + + while (i-- > 0) { + if (n->data == 0) + break; + + n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE); + } + + if (i < 0) { + /* No more empty fsNode structures in this block. Mark parent full. */ + errorcode = markparentfull(sb, *returned_bh); + } + + return errorcode; + } else { + /* What happened now is that we found a leaf-container which was + completely filled. In practice this should only happen when there + is only a single NodeContainer (only this container), or when there + was an error in one of the full-bits in a higher level container. */ + + if (noderoot != nodeindex) { + /*** Hmmm... it looks like there was a damaged full-bit or something. + In this case we'd probably better call markcontainerfull. */ + + printk("ASFS: Couldn't find empty Node in NodeContainer while NodeIndexContainer indicated there should be one!\n"); + + errorcode = -ENOSPC; + break; + } else { + /* Container is completely filled. */ + + if ((errorcode = addnewnodelevel(sb, NODE_STRUCT_SIZE)) != 0) + return errorcode; + + nodeindex = noderoot; + } + } + } else { /* This isn't a leaf container */ + u32 *p = nc->node; + s16 i = NODECONT_BLOCK_COUNT; + + /* We've read a normal container */ + + while (i-- > 0) { + if (*p != 0 && (be32_to_cpu(*p) & 0x00000001) == 0) + break; + + p++; + } + + if (i >= 0) { + /* Found a not completely filled Container */ + + nodeindex = be32_to_cpu(*p) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY); + } else { + /* Everything in the NodeIndexContainer was completely filled. There possibly + are some unused pointers in this block however. */ + + asfs_debug("createnode: NodeContainer at block has no empty Nodes.\n"); + + p = nc->node; + i = NODECONT_BLOCK_COUNT; + + while (i-- > 0) { + if (*p == 0) + break; + + p++; + } + + if (i >= 0) { + u32 newblock; + u32 nodes; + + /* Found an unused Container pointer */ + + if (be32_to_cpu(nc->nodes) == (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE) { + nodes = 1; + } else { + nodes = be32_to_cpu(nc->nodes) / NODECONT_BLOCK_COUNT; + } + + if ((errorcode = createnodecontainer(sb, be32_to_cpu(nc->nodenumber) + (p - nc->node) * be32_to_cpu(nc->nodes), nodes, &newblock)) != 0) { + break; + } + + *p = cpu_to_be32(newblock << (sb->s_blocksize_bits - ASFS_BLCKFACCURACY)); + + asfs_bstore(sb, *returned_bh); + } else { + /* Container is completely filled. This must be the top-level NodeIndex container + as otherwise the full-bit would have been wrong! */ + + if ((errorcode = addnewnodelevel(sb, NODE_STRUCT_SIZE)) != 0) + break; + + nodeindex = noderoot; + } + } + } + asfs_brelse(*returned_bh); + } + + if (*returned_bh == NULL) + return -EIO; + + return (errorcode); +} + +static int markparentempty(struct super_block *sb, struct buffer_head *bh) +{ + u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) bh->b_data)->nodenumber); + int errorcode; + + if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != 0) { + struct fsNodeContainer *nc = (void *) bh->b_data; + int wasfull; + u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes); + + wasfull = isfull(sb, nc); + + nc->node[containerentry] = cpu_to_be32(be32_to_cpu(nc->node[containerentry]) & ~0x00000001); + + asfs_bstore(sb, bh); + + if (wasfull) { + /* This container was completely full before! Mark the next higher up container too then! */ + return markparentempty(sb, bh); + } + asfs_brelse(bh); + } + + return errorcode; +} + +static int freecontainer(struct super_block *sb, struct buffer_head *bh) +{ + u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) bh->b_data)->nodenumber); + int errorcode; + + if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != NULL) { /* This line also prevents the freeing of the noderoot. */ + struct fsNodeContainer *nc = (void *) bh->b_data; + u16 containerindex = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes); + + if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(nc->node[containerindex]) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY))) == 0) { + u32 *p = nc->node; + s16 n = NODECONT_BLOCK_COUNT; + + nc->node[containerindex] = 0; + asfs_bstore(sb, bh); + + while (n-- > 0) + if (*p++ != 0) + break; + + if (n < 0) { /* This container is now completely empty! Free this NodeIndexContainer too then! */ + return freecontainer(sb, bh); + } + } + asfs_brelse(bh); + } + + return errorcode; +} + +static int internaldeletenode(struct super_block *sb, struct buffer_head *bh, struct fsNode *n) +{ + struct fsNodeContainer *nc = (void *) bh->b_data; + u16 nodecount = (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE; + s16 i = nodecount; + s16 empty = 0; + int errorcode = 0; + + n->data = 0; + n = (struct fsNode *) nc->node; + + while (i-- > 0) { + if (n->data == 0) + empty++; + + n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE); + } + + asfs_bstore(sb, bh); + + if (empty == 1) /* NodeContainer was completely full before, so we need to mark it empty now. */ + errorcode = markparentempty(sb, bh); + else if (empty == nodecount) /* NodeContainer is now completely empty! Free it! */ + errorcode = freecontainer(sb, bh); + + return (errorcode); +} + +int asfs_deletenode(struct super_block *sb, u32 objectnode) +{ + struct buffer_head *bh; + struct fsObjectNode *on; + int errorcode; + + asfs_debug("deletenode: Deleting Node %d\n", objectnode); + + if ((errorcode = asfs_getnode(sb, objectnode, &bh, &on)) == 0) + errorcode = internaldeletenode(sb, bh, (struct fsNode *) on); + + asfs_brelse(bh); + return (errorcode); +} + +#endif diff -puN /dev/null fs/asfs/objects.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/objects.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,765 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta7 + * + * This file contains some parts of the original amiga version of + * SmartFilesystem source code. + * + * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, + * Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek + * + * Adapted and modified by Marek 'March' Szyprowski + * + */ + +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +#include + +struct fsObject *asfs_nextobject(struct fsObject *obj) +{ + int i; + u8 *p = obj->name; + + for (i = 2; i > 0; p++) + if (*p == '\0') + i--; + if ((p - (u8 *) obj) & 0x01) + p++; + + return ((struct fsObject *) p); +} + +struct fsObject *asfs_find_obj_by_name(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) + 2 < sb->s_blocksize) { + if (asfs_namecmp(obj->name, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE, NULL) == 0) { + asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj->objectnode), obj->name, obj->bits, be32_to_cpu(objcont->bheader.ownblock)); + return obj; + } + obj = asfs_nextobject(obj); + } + return NULL; +} + +#ifdef CONFIG_ASFS_RW + +struct fsObject *find_obj_by_node(struct super_block *sb, struct fsObjectContainer *objcont, u32 objnode) +{ + struct fsObject *obj; + + obj = &(objcont->object[0]); + while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { + if (be32_to_cpu(obj->objectnode) == objnode) { + return obj; + } + obj = asfs_nextobject(obj); + } + return NULL; +} + +int asfs_readobject(struct super_block *sb, u32 objectnode, struct buffer_head **bh, struct fsObject **returned_object) +{ + struct fsObjectNode *on; + int errorcode; + u32 contblock; + + asfs_debug("Seaching object - node %d\n", objectnode); + + if ((errorcode = asfs_getnode(sb, objectnode, bh, &on)) != 0) + return errorcode; + contblock = be32_to_cpu(on->node.data); + asfs_brelse(*bh); + + if (contblock > 0 && (*bh = asfs_breadcheck(sb, contblock, ASFS_OBJECTCONTAINER_ID))) { + *returned_object = find_obj_by_node(sb, (void *) (*bh)->b_data, objectnode); + if (*returned_object == NULL) { + brelse(*bh); + *bh = NULL; + return -ENOENT; + } + return 0; + } else + return -EIO; +} + +static int removeobjectcontainer(struct super_block *sb, struct buffer_head *bh) +{ + struct fsObjectContainer *oc = (void *) bh->b_data; + int errorcode; + struct buffer_head *block; + + asfs_debug("removeobjectcontainer: block %u\n", be32_to_cpu(oc->bheader.ownblock)); + + if (oc->next != 0 && oc->next != oc->bheader.ownblock) { + struct fsObjectContainer *next_oc; + + if ((block = asfs_breadcheck(sb, be32_to_cpu(oc->next), ASFS_OBJECTCONTAINER_ID)) == NULL) + return -EIO; + + next_oc = (void *) block->b_data; + next_oc->previous = oc->previous; + + asfs_bstore(sb, block); + asfs_brelse(block); + } + + if (oc->previous != 0 && oc->previous != oc->bheader.ownblock) { + struct fsObjectContainer *previous_oc; + + if ((block = asfs_breadcheck(sb, be32_to_cpu(oc->previous), ASFS_OBJECTCONTAINER_ID)) == NULL) + return -EIO; + + previous_oc = (void *) block->b_data; + previous_oc->next = oc->next; + + asfs_bstore(sb, block); + asfs_brelse(block); + } else { + struct fsObject *parent_o; + + if ((errorcode = asfs_readobject(sb, be32_to_cpu(oc->parent), &block, &parent_o)) != 0) + return (errorcode); + + parent_o->object.dir.firstdirblock = oc->next; + + asfs_bstore(sb, block); + asfs_brelse(block); + } + + if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(oc->bheader.ownblock))) != 0) + return (errorcode); + + return (0); +} + +static int setrecycledinfodiff(struct super_block *sb, s32 deletedfiles, s32 deletedblocks) +{ + struct buffer_head *bh; + + if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { + struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo)); + + ri->deletedfiles = cpu_to_be32(be32_to_cpu(ri->deletedfiles) + deletedfiles); + ri->deletedblocks = cpu_to_be32(be32_to_cpu(ri->deletedblocks) + deletedblocks); + + asfs_bstore(sb, bh); + asfs_brelse(bh); + } else + return -EIO; + return 0; +} + + /* This function removes the fsObject structure passed in from the passed + buffer_head. If the ObjectContainer becomes completely empty it will be + delinked from the ObjectContainer chain and marked free for reuse. + This function doesn't delink the object from the hashchain! */ + +static int simpleremoveobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) +{ + struct fsObjectContainer *oc = (void *) bh->b_data; + int errorcode = 0; + + asfs_debug("simpleremoveobject:\n"); + + if (be32_to_cpu(oc->parent) == ASFS_RECYCLEDNODE) { + /* This object is removed from the Recycled directory. */ + if ((errorcode = setrecycledinfodiff(sb, -1, -((be32_to_cpu(o->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits))) != 0) + return errorcode; + } + + if ((asfs_nextobject(oc->object))->name[0] == '\0') + errorcode = removeobjectcontainer(sb, bh); + else { + struct fsObject *nexto; + int objlen; + + nexto = asfs_nextobject(o); + objlen = (u8 *) nexto - (u8 *) o; + + memmove(o, nexto, sb->s_blocksize - ((u8 *) nexto - (u8 *) oc)); + memset((u8 *) oc + sb->s_blocksize - objlen, 0, objlen); + + asfs_bstore(sb, bh); + } + return errorcode; +} + +/* This function delinks the passed in ObjectNode from its hash-chain. Handy when deleting + the object, or when renaming/moving it. */ + +static int dehashobjectquick(struct super_block *sb, u32 objectnode, u8 *name, u32 parentobjectnode) +{ + struct fsObject *o; + int errorcode = 0; + struct buffer_head *block; + + asfs_debug("dehashobject: Delinking object %d (=ObjectNode) from hashchain. Parentnode = %d\n", objectnode, parentobjectnode); + + if ((errorcode = asfs_readobject(sb, parentobjectnode, &block, &o)) == 0 && o->object.dir.hashtable != 0) { + u32 hashtable = be32_to_cpu(o->object.dir.hashtable); + asfs_brelse(block); + + if ((block = asfs_breadcheck(sb, hashtable, ASFS_HASHTABLE_ID))) { + struct buffer_head *node_bh; + struct fsObjectNode *onptr, on; + struct fsHashTable *ht = (void *) block->b_data; + u32 nexthash; + + if ((errorcode = asfs_getnode(sb, objectnode, &node_bh, &onptr)) == 0) { + u16 hashchain; + + asfs_debug("dehashobject: Read HashTable block of parent object of object to be delinked\n"); + + hashchain = HASHCHAIN(asfs_hash(name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE)); + nexthash = be32_to_cpu(ht->hashentry[hashchain]); + + if (nexthash == objectnode) { + /* The hashtable directly points to the fsObject to be delinked. We simply + modify the Hashtable to point to the new nexthash entry. */ + + asfs_debug("dehashobject: The hashtable points directly to the to be delinked object\n"); + + ht->hashentry[hashchain] = onptr->next; + asfs_bstore(sb, block); + } else { + struct fsObjectNode *onsearch = 0; + + on = *onptr; + + asfs_debug("dehashobject: Walking through hashchain\n"); + + while (nexthash != 0 && nexthash != objectnode) { + asfs_brelse(node_bh); + if ((errorcode = asfs_getnode(sb, nexthash, &node_bh, &onsearch)) != 0) + break; + nexthash = be32_to_cpu(onsearch->next); + } + + if (errorcode == 0) { + if (nexthash != 0) { + /* Previous fsObjectNode found in hash chain. Modify the fsObjectNode to 'skip' the + ObjectNode which is being delinked from the hash chain. */ + + onsearch->next = on.next; + asfs_bstore(sb, node_bh); + } else { + printk("ASFS: Hashchain of object %d is corrupt or incorrectly linked.", objectnode); + + /*** This is strange. We have been looking for the fsObjectNode which is located before the + passed in fsObjectNode in the hash-chain. However, we never found the + fsObjectNode reffered to in the hash-chain! Has to be somekind + of internal error... */ + + errorcode = -ENOENT; + } + } + } + asfs_brelse(node_bh); + } + asfs_brelse(block); + } + } + return errorcode; +} + + + /* This function removes an object from any directory. It takes care + of delinking the object from the hashchain and also frees the + objectnode number. */ + +static int removeobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) +{ + struct fsObjectContainer *oc = (void *) bh->b_data; + int errorcode; + + asfs_debug("removeobject\n"); + + if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o->objectnode), o->name, be32_to_cpu(oc->parent))) == 0) { + u32 objectnode = be32_to_cpu(o->objectnode); + + if ((errorcode = simpleremoveobject(sb, bh, o)) == 0) + errorcode = asfs_deletenode(sb, objectnode); + } + + return (errorcode); +} + + /* This function deletes the specified object. */ +int asfs_deleteobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) +{ + int errorcode = 0; + + asfs_debug("deleteobject: Entry -- deleting object %d (%s)\n", be32_to_cpu(o->objectnode), o->name); + + if ((o->bits & OTYPE_DIR) == 0 || o->object.dir.firstdirblock == 0) { + u8 bits = o->bits; + u32 hashblckno = be32_to_cpu(o->object.dir.hashtable); + u32 extentbnode = be32_to_cpu(o->object.file.data); + + if ((errorcode = removeobject(sb, bh, o)) == 0) { + if ((bits & OTYPE_LINK) != 0) { + asfs_debug("deleteobject: Object is soft link!\n"); + errorcode = asfs_freeadminspace(sb, extentbnode); + } else if ((bits & OTYPE_DIR) != 0) { + asfs_debug("deleteobject: Object is a directory!\n"); + errorcode = asfs_freeadminspace(sb, hashblckno); + } else { + asfs_debug("deleteobject: Object is a file\n"); + if (extentbnode != 0) + errorcode = asfs_deleteextents(sb, extentbnode); + } + } + } + + return (errorcode); +} + + /* This function takes a HashBlock pointer, an ObjectNode and an ObjectName. + If there is a hashblock, then this function will correctly link the object + into the hashchain. If there isn't a hashblock (=0) then this function + does nothing. */ + +static int hashobject(struct super_block *sb, u32 hashblock, struct fsObjectNode *on, u32 nodeno, u8 *objectname) +{ + struct buffer_head *hash_bh; + + asfs_debug("hashobject, using hashblock %d\n", hashblock); + if (hashblock == 0) + return 0; + + if ((hash_bh = asfs_breadcheck(sb, hashblock, ASFS_HASHTABLE_ID))) { + struct fsHashTable *ht = (void *) hash_bh->b_data; + u32 nexthash; + u16 hashvalue, hashchain; + + hashvalue = asfs_hash(objectname, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE); + hashchain = HASHCHAIN(hashvalue); + nexthash = be32_to_cpu(ht->hashentry[hashchain]); + + ht->hashentry[hashchain] = cpu_to_be32(nodeno); + + asfs_bstore(sb, hash_bh); + asfs_brelse(hash_bh); + + on->next = cpu_to_be32(nexthash); + on->hash16 = cpu_to_be16(hashvalue); + } else + return -EIO; + + return 0; +} + + /* This function returns a pointer to the first unused byte in + an ObjectContainer. */ + +static u8 *emptyspaceinobjectcontainer(struct super_block *sb, struct fsObjectContainer *oc) +{ + struct fsObject *o = oc->object; + u8 *endadr; + + endadr = (u8 *) oc + sb->s_blocksize - sizeof(struct fsObject) - 2; + + while ((u8 *) o < endadr && o->name[0] != 0) + o = asfs_nextobject(o); + + return (u8 *) o; +} + + /* This function will look in the directory indicated by io_o + for an ObjectContainer block which contains bytesneeded free + bytes. If none is found then this function simply creates a + new ObjectContainer and adds that to the indicated directory. */ + +static int findobjectspace(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, u32 bytesneeded) +{ + struct buffer_head *bhparent = *io_bh; + struct fsObject *oparent = *io_o; + struct buffer_head *bh; + u32 nextblock = be32_to_cpu(oparent->object.dir.firstdirblock); + int errorcode = 0; + + asfs_debug("findobjectspace: Looking for %u bytes in directory with ObjectNode number %d (in block %d)\n", bytesneeded, be32_to_cpu((*io_o)->objectnode), + be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock)); + + while (nextblock != 0 && (bh = asfs_breadcheck(sb, nextblock, ASFS_OBJECTCONTAINER_ID))) { + struct fsObjectContainer *oc = (void *) bh->b_data; + u8 *emptyspace; + + /* We need to find out how much free space this ObjectContainer has */ + + emptyspace = emptyspaceinobjectcontainer(sb, oc); + + if ((u8 *) oc + sb->s_blocksize - emptyspace >= bytesneeded) { + /* We found enough space in one of the ObjectContainer blocks!! + We return a struct fsObject *. */ + *io_bh = bh; + *io_o = (struct fsObject *) emptyspace; + break; + } + nextblock = be32_to_cpu(oc->next); + asfs_brelse(bh); + } + + if (nextblock == 0) { + u32 newcontblock; + /* If we get here, we traversed the *entire* directory (ough!) and found no empty + space large enough for our entry. We allocate new space and add it to this + directory. */ + + if ((errorcode = asfs_allocadminspace(sb, &newcontblock)) == 0 && (bh = asfs_getzeroblk(sb, newcontblock))) { + struct fsObjectContainer *oc = (void *) bh->b_data; + struct buffer_head *bhnext; + + asfs_debug("findobjectspace: No room was found, allocated new block at %u\n", newcontblock); + + /* Allocated new block. We will now link it to the START of the directory chain + so the new free space can be found quickly when more entries need to be added. */ + + oc->bheader.id = cpu_to_be32(ASFS_OBJECTCONTAINER_ID); + oc->bheader.ownblock = cpu_to_be32(newcontblock); + oc->parent = oparent->objectnode; + oc->next = oparent->object.dir.firstdirblock; + oc->previous = 0; + + oparent->object.dir.firstdirblock = cpu_to_be32(newcontblock); + + asfs_bstore(sb, bhparent); + + if (oc->next != 0 && (bhnext = asfs_breadcheck(sb, be32_to_cpu(oc->next), ASFS_OBJECTCONTAINER_ID))) { + struct fsObjectContainer *ocnext = (void *) bhnext->b_data; + ocnext->previous = cpu_to_be32(newcontblock); + asfs_bstore(sb, bhnext); + asfs_brelse(bhnext); + } + + *io_bh = bh; + *io_o = oc->object; + } + } + + asfs_debug("findobjectspace: new object will be in container block %u\n", be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock)); + + return (errorcode); +} + +/* io_bh & io_o refer to the direct parent of the new object. Objectname is the + name of the new object (name only). Does not realese io_bh !!! */ + +int asfs_createobject(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, struct fsObject *src_o, u8 *objectname, int force) +{ + int errorcode; + u32 object_size; + u32 hashblock = be32_to_cpu((*io_o)->object.dir.hashtable); + + asfs_debug("createobject: Creating object '%s' in dir '%s'.\n", objectname, (*io_o)->name); + + if (!force && ASFS_SB(sb)->freeblocks < ASFS_ALWAYSFREE) + return -ENOSPC; + + if (!force && be32_to_cpu((*io_o)->objectnode) == ASFS_RECYCLEDNODE) + return -EINVAL; + + object_size = sizeof(struct fsObject) + strlen(objectname) + 2; + + if ((errorcode = findobjectspace(sb, io_bh, io_o, object_size)) == 0) { + struct fsObject *o2 = *io_o; + u8 *name = o2->name; + u8 *objname = objectname; + struct buffer_head *node_bh; + struct fsObjectNode *on; + u32 nodeno; + + **io_o = *src_o; /* Copying whole object data... */ + + while (*objname != 0) /* Copying name */ + *name++ = *objname++; + + *name++ = 0; + *name = 0; /* zero byte for comment */ + + if (o2->objectnode != 0) /* ObjectNode reuse or creation */ + errorcode = asfs_getnode(sb, o2->objectnode, &node_bh, &on); + else { + if ((errorcode = asfs_createnode(sb, &node_bh, (struct fsNode **) &on, &nodeno)) == 0) { + on->hash16 = cpu_to_be16(asfs_hash(o2->name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE)); + o2->objectnode = cpu_to_be32(nodeno); + } + asfs_debug("createnode returned with errorcode: %d\n", errorcode); + } + + if (errorcode == 0) { /* in io_bh there is a container with created object */ + on->node.data = ((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock; + if ((errorcode = hashobject(sb, hashblock, on, be32_to_cpu(o2->objectnode), objectname)) == 0) { + asfs_bstore(sb, node_bh); + asfs_brelse(node_bh); + } else + errorcode = -EIO; + } + + if (errorcode == 0) { /* HashBlock reuse or creation:*/ + + if ((o2->bits & OTYPE_DIR) != 0 && o2->object.dir.hashtable == 0) { + struct buffer_head *hashbh; + u32 hashblock; + + asfs_debug("creating Hashblock\n"); + + if ((errorcode = asfs_allocadminspace(sb, &hashblock)) == 0 && (hashbh = asfs_getzeroblk(sb, hashblock))) { + struct fsHashTable *ht = (void *) hashbh->b_data; + + o2->object.dir.hashtable = cpu_to_be32(hashblock); + + ht->bheader.id = cpu_to_be32(ASFS_HASHTABLE_ID); + ht->bheader.ownblock = cpu_to_be32(hashblock); + ht->parent = o2->objectnode; + + asfs_bstore(sb, hashbh); + asfs_brelse(hashbh); + } + } + } + + if (errorcode == 0) { /* SoftLink creation: */ + if ((o2->bits & (OTYPE_LINK | OTYPE_HARDLINK)) == OTYPE_LINK && o2->object.file.data == 0) { + struct buffer_head *bh2; + u32 slinkblock; + + if ((errorcode = asfs_allocadminspace(sb, &slinkblock)) == 0 && (bh2 = asfs_getzeroblk(sb, slinkblock))) { + struct fsSoftLink *sl = (void *) bh2->b_data; + o2->object.file.data = cpu_to_be32(slinkblock); + sl->bheader.id = cpu_to_be32(ASFS_SOFTLINK_ID); + sl->bheader.ownblock = cpu_to_be32(slinkblock); + sl->parent = o2->objectnode; + sl->next = 0; + sl->previous = 0; + asfs_bstore(sb, bh2); + asfs_brelse(bh2); + } + } + } + } + asfs_debug("createobject: done.\n"); + + return (errorcode); +} + + /* This function extends the file object 'o' with a number of blocks + (hopefully, if any blocks has been found!). Only new Extents will + be created -- the size of the file will not be altered, and changing + it is left up to the caller. If the file did not have any blocks + yet, then the o->object.file.data will be set to the first (new) + ExtentBNode. It returns the number of added blocks through + addedblocks pointer */ + +int asfs_addblockstofile(struct super_block *sb, struct buffer_head *objbh, struct fsObject *o, u32 blocks, u32 * newspace, u32 * addedblocks) +{ + u32 lastextentbnode; + int errorcode = 0; + struct fsExtentBNode *ebnp; + struct buffer_head *block = NULL; + + + asfs_debug("extendblocksinfile: Trying to increasing number of blocks by %d.\n", blocks); + + lastextentbnode = be32_to_cpu(o->object.file.data); + + if (lastextentbnode != 0) { + while (lastextentbnode != 0 && errorcode == 0) { + if (block != NULL) + asfs_brelse(block); + errorcode = asfs_getextent(sb, lastextentbnode, &block, &ebnp); + lastextentbnode = be32_to_cpu(ebnp->next); + } + lastextentbnode = be32_to_cpu(ebnp->key); + } + + if (errorcode == 0) { + u32 searchstart; + + u32 found_block; + u32 found_blocks; + + *addedblocks = 0; + *newspace = 0; + + if (lastextentbnode != 0) + searchstart = be32_to_cpu(ebnp->key) + be16_to_cpu(ebnp->blocks); + else + searchstart = 0; //ASFS_SB(sb)->block_rovingblockptr; + + if ((errorcode = asfs_findspace(sb, blocks, searchstart, searchstart, &found_block, &found_blocks)) != 0) { + asfs_brelse(block); + asfs_debug("extendblocksinfile: findspace returned %s\n", errorcode == -ENOSPC ? "ENOSPC" : "error"); + return errorcode; + } + + blocks = found_blocks; + errorcode = asfs_markspace(sb, found_block, found_blocks); + *addedblocks = found_blocks; + *newspace = found_block; + + asfs_debug("extendblocksinfile: block = %u, lastextentbnode = %u, extentblocks = %d\n", found_block, lastextentbnode, blocks); + + if ((errorcode = asfs_addblocks(sb, blocks, found_block, be32_to_cpu(o->objectnode), &lastextentbnode)) != 0) { + asfs_debug("extendblocksinfile: addblocks returned errorcode %d\n", errorcode); + return errorcode; + } + + if (o->object.file.data == 0) + o->object.file.data = cpu_to_be32(lastextentbnode); + } + + if (block) + asfs_brelse(block); + asfs_bstore(sb, objbh); + + asfs_debug("addblockstofile: done. added %d blocks\n", *addedblocks); + + return errorcode; +} + + /* The Object indicated by bh1 & o1, gets renamed to newname and placed + in the directory indicated by bhparent & oparent. */ + +int asfs_renameobject(struct super_block *sb, struct buffer_head *bh1, struct fsObject *o1, struct buffer_head *bhparent, struct fsObject *oparent, u8 * newname) +{ + struct fsObject object; + u32 oldparentnode = be32_to_cpu(((struct fsObjectContainer *) bh1->b_data)->parent); + u8 oldname[107]; + int errorcode; + + asfs_debug("renameobject: Renaming '%s' to '%s' in dir '%s'\n", o1->name, newname, oparent->name); + + object = *o1; + strcpy(oldname, o1->name); + + if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o1->objectnode), o1->name, oldparentnode)) == 0) { + u32 parentobjectnode = be32_to_cpu(oparent->objectnode); + + if ((errorcode = simpleremoveobject(sb, bh1, o1)) == 0) { + struct buffer_head *bh2 = bhparent; + struct fsObject *o2; + + /* oparent might changed after simpleremoveobject */ + oparent = o2 = find_obj_by_node(sb, (struct fsObjectContainer *) bhparent->b_data, parentobjectnode); + + /* In goes the Parent bh & o, out comes the New object's bh & o :-) */ + if ((errorcode = asfs_createobject(sb, &bh2, &o2, &object, newname, TRUE)) == 0) { + asfs_bstore(sb, bh2); + if (be32_to_cpu(oparent->objectnode) == ASFS_RECYCLEDNODE) { + asfs_debug("renameobject: Updating recycled dir info\n"); + if ((errorcode = setrecycledinfodiff(sb, 1, (be32_to_cpu(o2->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits)) != 0) { + brelse(bh2); + return errorcode; + } + } + brelse(bh2); + asfs_debug("renameobject: Succesfully created & stored new object.\n"); + } else { /* recreate object in old place, maybe this will not fail, but who knows... */ + asfs_debug("renameobject: Creating new object failed. Trying to recreate it in source directory.\n"); + if (asfs_readobject(sb, oldparentnode, &bh1, &o1) == 0) { + struct buffer_head *bh2 = bh1; + if (asfs_createobject(sb, &bh2, &o1, &object, oldname, TRUE) == 0) { + asfs_bstore(sb, bh2); + if (oldparentnode == ASFS_RECYCLEDNODE) { + asfs_debug("renameobject: Updating recycled dir info\n"); + setrecycledinfodiff(sb, 1, (be32_to_cpu(o1->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits); + } + brelse(bh2); + } + brelse(bh1); + } + } + } + } + return errorcode; +} + + /* Truncates the specified file to /newsize/ bytes */ + +int asfs_truncateblocksinfile(struct super_block *sb, struct buffer_head *bh, struct fsObject *o, u32 newsize) +{ + struct buffer_head *ebh; + struct fsExtentBNode *ebn; + int errorcode; + u32 pos = 0; + u32 newblocks = (newsize + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + u32 filedata = be32_to_cpu(o->object.file.data); + u32 eprev, ekey; + u16 eblocks; + + asfs_debug("trucateblocksinfile: newsize %u\n", newsize); + + if (filedata == 0) + return 0; + + for (;;) { + if ((errorcode = asfs_getextent(sb, filedata, &ebh, &ebn)) != 0) + return errorcode; + if (pos + be16_to_cpu(ebn->blocks) >= newblocks) + break; + pos += be16_to_cpu(ebn->blocks); + if ((filedata = be32_to_cpu(ebn->next)) == 0) + break; + asfs_brelse(ebh); + }; + + eblocks = newblocks - pos; + ekey = be32_to_cpu(ebn->key); + eprev = be32_to_cpu(ebn->prev); + + if (be16_to_cpu(ebn->blocks) < eblocks) { + printk("ASFS: Extent chain is too short or damaged!\n"); + asfs_brelse(ebh); + return -ENOENT; + } + if (be16_to_cpu(ebn->blocks) - eblocks > 0 && (errorcode = asfs_freespace(sb, be32_to_cpu(ebn->key) + eblocks, be16_to_cpu(ebn->blocks) - eblocks)) != 0) { + asfs_brelse(ebh); + return errorcode; + } + if (be32_to_cpu(ebn->next) > 0 && (errorcode = asfs_deleteextents(sb, be32_to_cpu(ebn->next))) != 0) { + asfs_brelse(ebh); + return errorcode; + } + ebn->blocks = cpu_to_be16(eblocks); + ebn->next = 0; + asfs_bstore(sb, ebh); + + if (eblocks == 0) { + if (eprev & MSB_MASK) { + o->object.file.data = 0; + asfs_bstore(sb, bh); + } else { + struct buffer_head *ebhp; + struct fsExtentBNode *ebnp; + + if ((errorcode = asfs_getextent(sb, eprev & !MSB_MASK, &ebhp, &ebnp)) != 0) { + asfs_brelse(ebh); + return errorcode; + } + + ebnp->next = 0; + asfs_bstore(sb, ebhp); + asfs_brelse(ebhp); + } + if ((errorcode = asfs_deletebnode(sb, ebh, ekey)) != 0) { + asfs_brelse(ebh); + return errorcode; + } + } + asfs_brelse(ebh); + + return 0; +} +#endif diff -puN /dev/null fs/asfs/super.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/super.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,488 @@ +/* + * + * Amiga Smart File System, Linux implementation + * + * version: 1.0beta10 for 2.6.xx kernel + * + * Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski + * + * NLS support by Pavel Fedin (C) 2005 + * + * + * Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help and parts + * of original amiga version of SmartFilesystem source code. + * + * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, + * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek + * + * + * ASFS is based on the Amiga FFS filesystem for Linux + * Copyright (C) 1993 Ray Burr + * Copyright (C) 1996 Hans-Joachim Widmaier + * + * Earlier versions were based on the Linux implementation of + * the ROMFS file system + * Copyright (C) 1997-1999 Janos Farkas + * + * ASFS used some parts of the smbfs filesystem: + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * and parts of the Minix filesystem additionally + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1996 Gertjan van Wingerde + * + * + * 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. + * + */ + +/* todo: + * - remove bugs + * - add missing features (maybe safe-delete, other...) + * - create other fs tools like mkfs.asfs and fsck.asfs, some data-recovery tools + */ + +#define ASFS_VERSION "1.0beta10 (13.06.2005)" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +#include +#include + +static char asfs_default_codepage[] = CONFIG_ASFS_DEFAULT_CODEPAGE; +static char asfs_default_iocharset[] = CONFIG_NLS_DEFAULT; + +u32 asfs_calcchecksum(void *block, u32 blocksize) +{ + u32 *data = block, checksum = 1; + while (blocksize > 0) { + checksum += be32_to_cpu(*data++); + blocksize -= 4; + } + checksum -= be32_to_cpu(((struct fsBlockHeader *)block)->checksum); + return -checksum; +} + +static struct super_operations asfs_ops = { + .alloc_inode = asfs_alloc_inode, + .destroy_inode = asfs_destroy_inode, + .put_super = asfs_put_super, + .statfs = asfs_statfs, +#ifdef CONFIG_ASFS_RW + .remount_fs = asfs_remount, +#endif +}; + +extern struct dentry_operations asfs_dentry_operations; + +enum { + Opt_mode, Opt_setgid, Opt_setuid, Opt_prefix, Opt_volume, + Opt_lcvol, Opt_iocharset, Opt_codepage, Opt_ignore, Opt_err +}; + +static match_table_t tokens = { + {Opt_mode, "mode=%o"}, + {Opt_setgid, "setgid=%u"}, + {Opt_setuid, "setuid=%u"}, + {Opt_prefix, "prefix=%s"}, + {Opt_volume, "volume=%s"}, + {Opt_lcvol, "lowercasevol"}, + {Opt_iocharset, "iocharset=%s"}, + {Opt_codepage, "codepage=%s"}, + {Opt_ignore, "grpquota"}, + {Opt_ignore, "noquota"}, + {Opt_ignore, "quota"}, + {Opt_ignore, "usrquota"}, + {Opt_err, NULL}, +}; + +static int asfs_parse_options(char *options, struct super_block *sb) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + + if (!options) + return 1; + while ((p = strsep(&options, ",")) != NULL) { + int token, option; + if (!*p) + continue; + token = match_token(p, tokens, args); + + switch (token) { + case Opt_mode: + if (match_octal(&args[0], &option)) + goto no_arg; + ASFS_SB(sb)->mode = option & 0777; + break; + case Opt_setgid: + if (match_int(&args[0], &option)) + goto no_arg; + ASFS_SB(sb)->gid = option; + break; + case Opt_setuid: + if (match_int(&args[0], &option)) + goto no_arg; + ASFS_SB(sb)->uid = option; + break; + case Opt_prefix: + if (ASFS_SB(sb)->prefix) { + kfree(ASFS_SB(sb)->prefix); + ASFS_SB(sb)->prefix = NULL; + } + ASFS_SB(sb)->prefix = match_strdup(&args[0]); + if (! ASFS_SB(sb)->prefix) + return 0; + break; + case Opt_volume: + if (ASFS_SB(sb)->root_volume) { + kfree(ASFS_SB(sb)->root_volume); + ASFS_SB(sb)->root_volume = NULL; + } + ASFS_SB(sb)->root_volume = match_strdup(&args[0]); + if (! ASFS_SB(sb)->root_volume) + return 0; + break; + case Opt_lcvol: + ASFS_SB(sb)->flags |= ASFS_VOL_LOWERCASE; + break; + case Opt_iocharset: + if (ASFS_SB(sb)->iocharset != asfs_default_iocharset) + kfree(ASFS_SB(sb)->iocharset); + ASFS_SB(sb)->iocharset = match_strdup(&args[0]); + if (!ASFS_SB(sb)->iocharset) + return 0; + break; + case Opt_codepage: + if (ASFS_SB(sb)->codepage != asfs_default_codepage) + kfree(ASFS_SB(sb)->codepage); + ASFS_SB(sb)->codepage = match_strdup(&args[0]); + if (!ASFS_SB(sb)->codepage) + return 0; + case Opt_ignore: + /* Silently ignore the quota options */ + break; + default: +no_arg: + printk("ASFS: Unrecognized mount option \"%s\" " + "or missing value\n", p); + return 0; + } + } + return 1; +} + +static int asfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct asfs_sb_info *sbi; + struct buffer_head *bh; + struct fsRootBlock *rootblock; + struct inode *rootinode; + + sbi = kmalloc(sizeof(struct asfs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + + /* Fill in defaults */ + ASFS_SB(sb)->uid = ASFS_DEFAULT_UID; + ASFS_SB(sb)->gid = ASFS_DEFAULT_GID; + ASFS_SB(sb)->mode = ASFS_DEFAULT_MODE; + ASFS_SB(sb)->prefix = NULL; + ASFS_SB(sb)->root_volume = NULL; + ASFS_SB(sb)->flags = 0; + ASFS_SB(sb)->iocharset = asfs_default_iocharset; + ASFS_SB(sb)->codepage = asfs_default_codepage; + + if (!asfs_parse_options(data, sb)) { + printk(KERN_ERR "ASFS: Error parsing options\n"); + return -EINVAL; + } + + if (!sb_set_blocksize(sb, 512)) + return -EINVAL; + sb->s_maxbytes = ASFS_MAXFILESIZE; + + bh = sb_bread(sb, 0); + if (!bh) { + printk(KERN_ERR "ASFS: unable to read superblock\n"); + return -EINVAL; + } + + 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(sb)->totalblocks = be32_to_cpu(rootblock->totalblocks); + ASFS_SB(sb)->rootobjectcontainer = be32_to_cpu(rootblock->rootobjectcontainer); + ASFS_SB(sb)->extentbnoderoot = be32_to_cpu(rootblock->extentbnoderoot); + ASFS_SB(sb)->objectnoderoot = be32_to_cpu(rootblock->objectnoderoot); + ASFS_SB(sb)->flags |= 0xff & rootblock->bits; + ASFS_SB(sb)->adminspacecontainer = be32_to_cpu(rootblock->adminspacecontainer); + ASFS_SB(sb)->bitmapbase = be32_to_cpu(rootblock->bitmapbase); + ASFS_SB(sb)->blocks_inbitmap = (sb->s_blocksize - sizeof(struct fsBitmap))<<3; /* must be a multiple of 32 !! */ + ASFS_SB(sb)->blocks_bitmap = (ASFS_SB(sb)->totalblocks + ASFS_SB(sb)->blocks_inbitmap - 1) / ASFS_SB(sb)->blocks_inbitmap; + ASFS_SB(sb)->block_rovingblockptr = 0; + asfs_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", \ + sb->s_id, sb->s_blocksize); + return -EINVAL; + } + + 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)) { +#ifdef CONFIG_ASFS_RW + struct buffer_head *tmpbh; + if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { + struct fsRootInfo *ri = (struct fsRootInfo *)((u8 *)tmpbh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo)); + ASFS_SB(sb)->freeblocks = be32_to_cpu(ri->freeblocks); + asfs_brelse(tmpbh); + } else + ASFS_SB(sb)->freeblocks = 0; + + if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer+2, ASFS_TRANSACTIONFAILURE_ID))) { + printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s, but it has unfinished transaction. Mounting read-only.\n", sb->s_id); + ASFS_SB(sb)->flags |= ASFS_READONLY; + asfs_brelse(tmpbh); + } + + if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->totalblocks-1, ASFS_ROOTID)) == NULL) { + printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s, but there is no second RootBlock! Mounting read-only.\n", sb->s_id); + ASFS_SB(sb)->flags |= ASFS_READONLY; + asfs_brelse(tmpbh); + } + if (!(ASFS_SB(sb)->flags & ASFS_READONLY)) + printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s.\n", sb->s_id); +#else + ASFS_SB(sb)->freeblocks = 0; + ASFS_SB(sb)->flags |= ASFS_READONLY; + printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s.\n", sb->s_id); +#endif + } else { + if (!silent) + printk(KERN_ERR "VFS: Found Amiga SFS RootBlock on dev %s, but it has checksum error!\n", \ + sb->s_id); + goto out; + } + } else { + if (!silent) + printk(KERN_ERR "VFS: Can't find a valid Amiga SFS filesystem on dev %s.\n", \ + sb->s_id); + goto out; + } + + asfs_brelse(bh); + + sb->s_magic = ASFS_MAGIC; + sb->s_flags |= MS_NODEV | MS_NOSUID; + if (ASFS_SB(sb)->flags & ASFS_READONLY) + sb->s_flags |= MS_RDONLY; + sb->s_op = &asfs_ops; + asfs_debug("Case sensitive: %s\n", (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) ? "yes" : "no"); + + if (ASFS_SB(sb)->codepage[0] != '\0' && strcmp(ASFS_SB(sb)->codepage, "none") != 0) { + ASFS_SB(sb)->nls_disk = load_nls(ASFS_SB(sb)->codepage); + if (!ASFS_SB(sb)->nls_disk) { + printk(KERN_ERR "ASFS: codepage %s not found\n", ASFS_SB(sb)->codepage); + return -EINVAL; + } + ASFS_SB(sb)->nls_io = load_nls(ASFS_SB(sb)->iocharset); + if (!ASFS_SB(sb)->nls_io) { + printk(KERN_ERR "ASFS: IO charset %s not found\n", ASFS_SB(sb)->iocharset); + goto out2; + } + } else { + ASFS_SB(sb)->nls_io = NULL; + ASFS_SB(sb)->nls_disk = NULL; + } + + if ((rootinode = asfs_get_root_inode(sb))) { + if ((sb->s_root = d_alloc_root(rootinode))) { + sb->s_root->d_op = &asfs_dentry_operations; + return 0; + } + iput(rootinode); + } + unload_nls(ASFS_SB(sb)->nls_io); +out2: + unload_nls(ASFS_SB(sb)->nls_disk); + return -EINVAL; + +out: + asfs_brelse(bh); + return -EINVAL; + +} + +#ifdef CONFIG_ASFS_RW +int asfs_remount(struct super_block *sb, int *flags, char *data) +{ + asfs_debug("ASFS: remount (flags=0x%x, opts=\"%s\")\n",*flags,data); + + if (!asfs_parse_options(data,sb)) + return -EINVAL; + + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + return 0; + + if (*flags & MS_RDONLY) { + sb->s_flags |= MS_RDONLY; + } else if (!(ASFS_SB(sb)->flags & ASFS_READONLY)) { + sb->s_flags &= ~MS_RDONLY; + } else { + printk("VFS: Can't remount Amiga SFS on dev %s read/write because of errors.", sb->s_id); + return -EINVAL; + } + return 0; +} +#endif + +void asfs_put_super(struct super_block *sb) +{ + struct asfs_sb_info *sbi = ASFS_SB(sb); + + if (ASFS_SB(sb)->prefix) + kfree(ASFS_SB(sb)->prefix); + if (ASFS_SB(sb)->root_volume) + kfree(ASFS_SB(sb)->root_volume); + if (ASFS_SB(sb)->nls_disk) + unload_nls(ASFS_SB(sb)->nls_disk); + if (ASFS_SB(sb)->nls_io) + unload_nls(ASFS_SB(sb)->nls_io); + if (ASFS_SB(sb)->iocharset != asfs_default_iocharset) + kfree(ASFS_SB(sb)->iocharset); + if (ASFS_SB(sb)->codepage != asfs_default_codepage) + kfree(ASFS_SB(sb)->codepage); + + kfree(sbi); + sb->s_fs_info = NULL; + return; +} + +/* That's simple too. */ +int asfs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + buf->f_type = ASFS_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_bfree = buf->f_bavail = ASFS_SB(sb)->freeblocks; + buf->f_blocks = ASFS_SB(sb)->totalblocks; + buf->f_namelen = ASFS_MAXFN; + return 0; +} + +/* --- new in 2.6.x --- */ +static kmem_cache_t * asfs_inode_cachep; + +struct inode *asfs_alloc_inode(struct super_block *sb) +{ + struct asfs_inode_info *ei; + ei = (struct asfs_inode_info *)kmem_cache_alloc(asfs_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +void asfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(asfs_inode_cachep, ASFS_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct asfs_inode_info *ei = (struct asfs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + inode_init_once(&ei->vfs_inode); + } +} + +static int init_inodecache(void) +{ + asfs_inode_cachep = kmem_cache_create("asfs_inode_cache", + sizeof(struct asfs_inode_info), + 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + init_once, NULL); + if (asfs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(asfs_inode_cachep)) + printk(KERN_INFO "asfs_inode_cache: not all structures were freed\n"); +} + +static struct super_block *asfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, asfs_fill_super); +} + +static struct file_system_type asfs_fs_type = { + .owner = THIS_MODULE, + .name = "asfs", + .get_sb = asfs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_asfs_fs(void) +{ + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&asfs_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; +} + +static void __exit exit_asfs_fs(void) +{ + unregister_filesystem(&asfs_fs_type); + destroy_inodecache(); +} + +/* Yes, works even as a module... :) */ + +#ifdef CONFIG_ASFS_RW +MODULE_DESCRIPTION("Amiga Smart File System (read/write) support for Linux kernel 2.6.x v" ASFS_VERSION); +#else +MODULE_DESCRIPTION("Amiga Smart File System (read-only) support for Linux kernel 2.6.x v" ASFS_VERSION); +#endif +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Szyprowski "); + +module_init(init_asfs_fs) +module_exit(exit_asfs_fs) diff -puN /dev/null fs/asfs/symlink.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/asfs/symlink.c 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,235 @@ +/* + * + * Amiga Smart File System, Linux implementation + * version: 1.0beta9 + * + * Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski + * + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "asfs_fs.h" + +#include +#include + +int asfs_symlink_readpage(struct file *file, struct page *page) +{ + struct buffer_head *bh; + struct fsSoftLink *slinkcont; + struct inode *inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; + char *link = kmap(page); + int i = 0, j = 0; + char c, lc = 0, *prefix, *lf, *p; + wchar_t uni; + int clen; + + if (!(bh = asfs_breadcheck(sb, ASFS_I(inode)->firstblock, ASFS_SOFTLINK_ID))) { + SetPageError(page); + kunmap(page); + unlock_page(page); + return -EIO; + } + slinkcont = (struct fsSoftLink *) bh->b_data; + + lf = slinkcont->string; + prefix = ASFS_SB(sb)->prefix ? ASFS_SB(sb)->prefix : "/"; + + if ((p = strchr(lf,':'))) { /* Handle assign or volume name */ + if (ASFS_SB(sb)->root_volume && + strncmp(lf, ASFS_SB(sb)->root_volume, strlen(ASFS_SB(sb)->root_volume)) == 0) { + /* global root volume name found */ + link[i++] = '/'; + lf = p+1; + } else { + /* adding volume prefix */ + while (i < 1023 && (c = prefix[i])) + link[i++] = c; + while (i < 1023 && lf[j] != ':') + { + c = lf[j++]; + if (ASFS_SB(sb)->flags & ASFS_VOL_LOWERCASE) + c = asfs_lowerchar(c); + if (nls_io) + { + clen = nls_disk->char2uni(&c, 1, &uni); + if (clen>0) { + clen = nls_io->uni2char(uni, &link[i], NLS_MAX_CHARSET_SIZE); + if (clen>0) + i += clen; + } + if (clen<0) + link[i++] = '?'; + } else + link[i++] = c; + } + if (i < 1023) + link[i++] = '/'; + j++; + } + lc = '/'; + } + + while (i < 1023 && (c = lf[j])) { + if (c == '/' && lc == '/' && i < 1020) { /* parent dir */ + link[i++] = '.'; + link[i++] = '.'; + } + lc = c; + if (nls_io) + { + clen = nls_disk->char2uni(&c, 1, &uni); + if (clen>0) { + clen = nls_io->uni2char(uni, &link[i], NLS_MAX_CHARSET_SIZE); + if (clen>0) + i += clen; + } + if (clen<0) + link[i++] = '?'; + } else + link[i++] = c; + j++; + } + link[i] = '\0'; + SetPageUptodate(page); + kunmap(page); + unlock_page(page); + asfs_brelse(bh); + return 0; +} + +#ifdef CONFIG_ASFS_RW + +int asfs_write_symlink(struct inode *symfile, const char *symname) +{ + struct super_block *sb = symfile->i_sb; + struct buffer_head *bh; + struct fsSoftLink *slinkcont; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; + char *p, c, lc; + int i, maxlen, pflen; + wchar_t uni; + int clen, blen; + + asfs_debug("asfs_write_symlink %s to node %d\n", symname, (int)symfile->i_ino); + + if (!(bh = asfs_breadcheck(sb, ASFS_I(symfile)->firstblock, ASFS_SOFTLINK_ID))) { + unlock_super(sb); + return -EIO; + } + slinkcont = (struct fsSoftLink *) bh->b_data; + + /* translating symlink target path */ + + maxlen = sb->s_blocksize - sizeof(struct fsSoftLink) - 2; + i = 0; + p = slinkcont->string; + lc = '/'; + + if (*symname == '/') { + while (*symname == '/') + symname++; + if (ASFS_SB(sb)->prefix && + strncmp(symname-1, ASFS_SB(sb)->prefix, (pflen = strlen(ASFS_SB(sb)->prefix))) == 0) { + /* found volume prefix, ommiting it */ + symname += pflen; + blen = strlen(symname); + while (*symname != '/' && *symname != '\0') { + clen = nls_io->char2uni(symname, blen, &uni); + if (clen>0) { + symname += clen; + blen -= clen; + clen = nls_disk->uni2char(uni, p, NLS_MAX_CHARSET_SIZE); + if (clen>0) + p += clen; + } + else + { + symname++; + blen--; + } + if (clen<0) + *p++ = '?'; + i++; + } + symname++; + *p++ = ':'; + } else if (ASFS_SB(sb)->root_volume) { /* adding root volume name */ + while (ASFS_SB(sb)->root_volume[i]) + *p++ = ASFS_SB(sb)->root_volume[i++]; + *p++ = ':'; + } else { /* do nothing */ + *p++ = '/'; + } + i++; + } + + blen = strlen(symname); + while (i < maxlen && (c = *symname)) { + if (c == '.' && lc == '/' && symname[1] == '.' && symname[2] == '/') { + *p++ = '/'; + i++; + symname += 3; + blen -= 3; + lc = '/'; + } else if (c == '.' && lc == '/' && symname[1] == '/') { + symname += 2; + blen -= 2; + lc = '/'; + } else { + clen = nls_io->char2uni(symname, blen, &uni); + if (clen>0) { + symname += clen; + blen -= clen; + clen = nls_disk->uni2char(uni, p, NLS_MAX_CHARSET_SIZE); + if (clen>0) + lc = *p; + p += clen; + } + else + { + symname++; + blen--; + } + if (clen<0) + { + *p++ = '?'; + lc = '?'; + } + i++; + } + if (lc == '/') + while (*symname == '/') + { + symname++; + blen--; + } + } + *p = 0; + + asfs_bstore(sb, bh); + asfs_brelse(bh); + + unlock_super(sb); + + return 0; +} + +#endif diff -puN fs/Kconfig~asfs-filesystem-driver fs/Kconfig --- 25/fs/Kconfig~asfs-filesystem-driver 2005-06-24 23:48:28.000000000 -0700 +++ 25-akpm/fs/Kconfig 2005-06-24 23:48:28.000000000 -0700 @@ -993,6 +993,53 @@ config AFFS_FS To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. +config ASFS_FS + tristate "Amiga SFS file system support (EXPERIMENTAL)" + select NLS + depends on EXPERIMENTAL + help + + 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. + + For more information read + + To compile this file system support as a module, choose M here: the + module will be called asfs. + + If unsure, say N. + +config ASFS_DEFAULT_CODEPAGE + string "Default codepage for SFS" + depends on ASFS_FS + default "" + help + This option should be set to the codepage of your SFS filesystems. + It can be overridden with the 'codepage' mount option. Leave it blank + or enter 'none' to disable filename converting. + + Use full codepage name (for example 'cp1251' instead of '1251') here, + this allows to specify any character set, not only numbered one (like + 'iso8859-2'). + + If unsure, leave it blank. + +config ASFS_RW + bool "Amiga SFS write support (DANGEROUS)" + depends on ASFS_FS + help + + If you say Y here, you will be able to write to ASFS file + systems as well as read from them. The read-write support in ASFS + is in beta stage. This means that useing it to write files to SFS + partitions is DANGEROUS and COULD corrupt the filesystem. + + For more information read + + If unsure, say N. + config HFS_FS tristate "Apple Macintosh file system support (EXPERIMENTAL)" depends on EXPERIMENTAL diff -puN fs/Makefile~asfs-filesystem-driver fs/Makefile --- 25/fs/Makefile~asfs-filesystem-driver 2005-06-24 23:48:28.000000000 -0700 +++ 25-akpm/fs/Makefile 2005-06-24 23:48:28.000000000 -0700 @@ -85,6 +85,7 @@ obj-$(CONFIG_EFS_FS) += efs/ obj-$(CONFIG_JFFS_FS) += jffs/ obj-$(CONFIG_JFFS2_FS) += jffs2/ obj-$(CONFIG_AFFS_FS) += affs/ +obj-$(CONFIG_ASFS_FS) += asfs/ obj-$(CONFIG_ROMFS_FS) += romfs/ obj-$(CONFIG_QNX4FS_FS) += qnx4/ obj-$(CONFIG_AUTOFS_FS) += autofs/ diff -puN /dev/null include/linux/amigasfs.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/include/linux/amigasfs.h 2005-06-24 23:48:28.000000000 -0700 @@ -0,0 +1,276 @@ +#ifndef __LINUX_AMIGASFS_H +#define __LINUX_AMIGASFS_H + +#include + +/* some helper macros... */ +#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') +#define ASFS_ADMINSPACECONTAINER_ID ASFS_MAKE_ID('A','D','M','C') +#define ASFS_BITMAP_ID ASFS_MAKE_ID('B','T','M','P') +#define ASFS_TRANSACTIONFAILURE_ID ASFS_MAKE_ID('T','R','F','A') + +/* Amiga SFS defines and magic values */ + +#define ASFS_MAGIC 0xa0ff +#define ASFS_MAXFN (105u) +#define ASFS_MAXFILESIZE 0x8FFFFFFE + +#define ASFS_STRUCTURE_VERISON (3) +#define ASFS_BLCKFACCURACY (5) + +#define ASFS_ROOTBITS_CASESENSITIVE (128) +#define ASFS_READONLY (512) +#define ASFS_VOL_LOWERCASE (1024) + +#define ASFS_ROOTNODE (1) +#define ASFS_RECYCLEDNODE (2) + +#define OTYPE_HIDDEN (1) +#define OTYPE_HARDLINK (32) +#define OTYPE_LINK (64) +#define OTYPE_DIR (128) + +#define MSB_MASK (1ul << 31) + +#define NODE_STRUCT_SIZE (10) /* (sizeof(struct fsObjectNode)) */ +#define NODECONT_BLOCK_COUNT ((sb->s_blocksize - sizeof(struct fsNodeContainer)) / sizeof(u32)) + +#define ASFS_ALWAYSFREE (16) /* keep this amount of blocks free */ + +#define ASFS_BLOCKCHUNKS (16) /* try to allocate this number of blocks in one request */ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* amigados protection bits */ + +#define FIBB_SCRIPT 6 /* program is a script (execute) file */ +#define FIBB_PURE 5 /* program is reentrant and rexecutable */ +#define FIBB_ARCHIVE 4 /* cleared whenever file is changed */ +#define FIBB_READ 3 /* ignored by old filesystem */ +#define FIBB_WRITE 2 /* ignored by old filesystem */ +#define FIBB_EXECUTE 1 /* ignored by system, used by Shell */ +#define FIBB_DELETE 0 /* prevent file from being deleted */ + +#define FIBF_SCRIPT (1<s_blocksize) - sizeof(struct fsHashTable))>>2)) + +/* 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]; +}; + +/* On disk block with hashtable data */ + +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)); + +/* Some adminspace and bitmap block structures */ + +struct fsAdminSpace { + u32 space; + u32 bits; +/* Set bits are used blocks, bit 31 is the first block in the AdminSpace. */ +}; + +struct fsAdminSpaceContainer { + struct fsBlockHeader bheader; + + u32 next; + u32 previous; + + u8 bits; + u8 pad1; + u16 pad2; + + struct fsAdminSpace adminspace[0]; +}; + +struct fsBitmap { + struct fsBlockHeader bheader; + + u32 bitmap[0]; + +/* Bits are 1 if the block is free, and 0 if full. + Bitmap must consist of an integral number of longwords. */ +}; + +/* The fsRootInfo structure has all kinds of information about the format + of the disk. */ + +struct fsRootInfo { + u32 deletedblocks; /* Amount in blocks which deleted files consume. */ + u32 deletedfiles; /* Number of deleted files in recycled. */ + u32 freeblocks; /* Cached number of free blocks on disk. */ + + u32 datecreated; + + u32 lastallocatedblock; /* Block which was most recently allocated */ + u32 lastallocatedadminspace; /* AdminSpaceContainer which most recently was used to allocate a block */ + u32 lastallocatedextentnode; /* ExtentNode which was most recently created */ + u32 lastallocatedobjectnode; /* ObjectNode which was most recently created */ + + u32 rovingpointer; +}; + +#endif _