diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/affs |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/affs')
-rw-r--r-- | fs/affs/Changes | 343 | ||||
-rw-r--r-- | fs/affs/Makefile | 9 | ||||
-rw-r--r-- | fs/affs/affs.h | 304 | ||||
-rw-r--r-- | fs/affs/amigaffs.c | 509 | ||||
-rw-r--r-- | fs/affs/bitmap.c | 390 | ||||
-rw-r--r-- | fs/affs/dir.c | 155 | ||||
-rw-r--r-- | fs/affs/file.c | 920 | ||||
-rw-r--r-- | fs/affs/inode.c | 411 | ||||
-rw-r--r-- | fs/affs/namei.c | 443 | ||||
-rw-r--r-- | fs/affs/super.c | 569 | ||||
-rw-r--r-- | fs/affs/symlink.c | 78 |
11 files changed, 4131 insertions, 0 deletions
diff --git a/fs/affs/Changes b/fs/affs/Changes new file mode 100644 index 00000000000..a29409c1ffe --- /dev/null +++ b/fs/affs/Changes @@ -0,0 +1,343 @@ +(Note: I consider version numbers as cheap. That means +that I do not like numbers like 0.1 and the like for +things that can be used since quite some time. But +then, 3.1 doesn't mean 'perfectly stable', too.) + +Known bugs: +----------- + +- Doesn't work on the alpha. The only 64/32-bit + problem that I'm aware of (pointer/int conversion + in readdir()) gives compiler warnings but is + apparently not causing the failure, as directory + reads basically work (but all files are of size 0). + Alas, I've got no alpha to debug. :-( + +- The partition checker (drivers/block/genhd.c) + doesn't work with devices which have 256 byte + blocks (some very old SCSI drives). + +- The feature to automatically make the fs clean + might leave a trashed file system with the + bitmap flag set valid. + +- When a file is truncated to a size that is not + a multiple of the blocksize, the rest of the + last allocated block is not cleared. Well, + this fs never claimed to be Posix conformant. + +Please direct bug reports to: zippel@linux-m68k.org + +Version 3.20 +------------ +- kill kernel lock +- fix for a possible bitmap corruption + +Version 3.19 +------------ + +- sizeof changes from Kernel Janitor Project +- several bug fixes found with fsx + +Version 3.18 +------------ + +- change to global min macro + warning fixes +- add module tags + +Version 3.17 +------------ + +- locking fixes +- wrong sign in __affs_hash_dentry +- remove unnecessary check in affs_new_inode +- enable international mode for dircache fs + +Version 3.16 +------------ + +- use mark_buffer_dirty_inode instead of mark_buffer_dirty. +- introduce affs_lock_{link|dir|ext}. + +Version 3.15 +------------ + +- disable link to directories until we can properly support them. +- locking fixes for link creation/removal. + +Version 3.14 +------------ + +- correctly cut off long file names for compares +- correctly initialize s_last_bmap + +Version 3.13 +------------ + +Major cleanup for 2.4 [Roman Zippel] +- new extended block handling +- new bitmap allocation functions +- locking should be safe for the future +- cleanup of some interfaces + +Version 3.12 +------------ + +more 2.4 fixes: [Roman Zippel] +- s_lock changes +- increased getblock mess +- clear meta blocks + +Version 3.11 +------------ + +- Converted to use 2.3.x page cache [Dave Jones <dave@powertweak.com>] +- Corruption in truncate() bugfix [Ken Tyler <kent@werple.net.au>] + +Version 3.10 +------------ + +- Changed partition checker to allow devices + with physical blocks != 512 bytes. + +- The partition checker now also ignores the + word at 0xd0 that Windows likes to write to. + +Version 3.9 +----------- + +- Moved cleanup from release_file() to put_inode(). + This makes the first one obsolete. + +- truncate() zeroes the unused remainder of a + partially used last block when a file is truncated. + It also marks the inode dirty now (which is not + really necessary as notify_change() will do + it anyway). + +- Added a few comments, fixed some typos (and + introduced some new ones), made the debug messages + more consistent. Changed a bad example in the + doc file (affs.txt). + +- Sets the NOEXEC flag in read_super() for old file + systems, since you can't run programs on them. + +Version 3.8 +----------- +Bill Hawes kindly reviewed the affs and sent me the +patches he did. They're marked (BH). Thanks, Bill! + +- Cleanup of error handling in read_super(). + Didn't release all resources in case of an + error. (BH) + +- put_inode() releases the ext cache only if it's + no longer needed. (BH) + +- One set of dentry callbacks is enough. (BH) + +- Cleanup of error handling in namei.c. (BH) + +- Cleanup of error handling in file.c. (BH) + +- The original blocksize of the device is + restored when the fs is unmounted. (BH) + +- getblock() did not invalidate the key cache + when it allocated a new block. + +- Removed some unnecessary locks as Bill + suggested. + +- Simplified match_name(), changed all hashing + and case insensitive name comparisons to use + uppercase. This makes the tolower() routines + obsolete. + +- Added mount option 'mufs' to force muFS + uid/gid interpretation. + +- File mode changes were not updated on disk. + This was fixed before, but somehow got lost. + +Version 3.7 +----------- + +- Added dentry callbacks to allow the dcache to + operate case insensitive and length ignorant + like the affs itself. + +- getblock() didn't update the lastblock field in the + inode if the fs was not an OFS. This bug only shows + up if a file was enlarged via truncate() and there + was not enough space. + +- Remove some more superfluous code left over from + the old link days ... + +- Fixed some oversights which were in patch 2.1.78. + +- Fixed a few typos. + +Version 3.6 +----------- + +- dentry changes. (Thanks to Jes Sorensen for his help.) + +- Fixed bug in balloc(): Superblock was not set dirty after + the bitmap was changed, so the bitmap wasn't sync'd. + +- Fixed nasty bug in find_new_zone(): If the current + zone number was zero, the loop didn't terminate, + causing a solid lock-up. + +- Removed support for old-style directory reads. + +- Fixed bug in add_entry(): When doing a sorted insert, + the pointer to the next entry in the hash chain wasn't + correctly byte-swapped. Since most of the users of the + affs use it on a 68k, they didn't notice. But why did + I not find this during my tests? + +- Fixed some oversights (version wasn't updated on some + directory changes). + +- Handling of hard links rewritten. To the VFS + they appear now as normal Unix links. They are + now resolved only once in lookup(). The backside + is that unlink(), rename() and rmdir() have to + be smart about them, but the result is worth the + effort. This also led to some code cleanup. + +- Changed name type to unsigned char; the test for + invalid filenames didn't work correctly. + (Thanks to Michael Krause for pointing at this.) + +- Changed mapping of executable flag. + +- Changed all network byte-order macros to the + recommended ones. + +- Added a remount function, so attempts to remount + a dircache filesystem or one with errors read/write + can be trapped. Previously, ro remounts didn't + flush the super block, and rw remounts didn't + create allocation zones ... + +- Call shrink_dcache_parent() in rmdir(). + (Thanks to Bill Hawes.) + +- Permission checks in unlink(). + +- Allow mounting of volumes with superfluous + bitmap pointers read only, also allows them + to be remounted read/write. + +- Owner/Group defaults now to the fs user (i.e. + the one that mounted it) instead of root. This + obsoletes the mount options uid and gid. + +- Argument to volume option could overflow the + name buffer. It is now silently truncated to + 30 characters. (Damn it! This kind of bug + is too embarrassing.) + +- Split inode.c into 2 files, the superblock + routines desperately wanted their own file. + +- truncate() didn't allocate an extension block + cache. If a file was extended by means of + truncate(), this led to an Oops. + +- fsuser is now checked last. + +- rename() will not ignore changes in filename + casing any more (though mv(1) still won't allow + you to do "mv oldname OldName"). + +Version 3.5 +----------- + +- Extension block caches are now allocated on + demand instead of when a file is opened, as + files can be read and written without opening + them (e. g. the loopback device does this). + +- Removed an unused function. + +Version 3.4 +----------- + +- Hash chains are now sorted by block numbers. + (Thanks to Kars de Jong for finding this.) +- Removed all unnecessary external symbols. + +Version 3.3 +----------- + +- Tried to make all types 'correct' and consistent. +- Errors and warnings are now reported via a + function. They are all prefixed by a severity + and have the same appearance: + "AFFS: <function>: <error message>" + (There's one exception to this, as in that function + is no pointer to the super block available.) +- The filesystem is remounted read-only after an + error. +- The names of newly created filesystem objects are + now checked for validity. +- Minor cleanups in comments. +- Added this Changes file. At last! + +Version 3.2 +----------- + +- Extension block cache: Reading/writing of huge files + (several MB) is much faster (of course the added + overhead slows down opening, but this is hardly + noticeable). +- The same get_block()-routine can now be used for + both OFS and FFS. +- The super block is now searched in the block that + was calculated and in the one following. This + should remedy the round-off error introduced by + the 1-k blocks that Linux uses. +- Minor changes to adhere to the new VFS interface. +- The number of used blocks is now also calculated + if the filesystem is mounted read-only. +- Prefixed some constants with AFFS_ to avoid name + clashes. +- Removed 'EXPERIMENTAL' status. + +Version 3.1 +----------- + +- Fixed a nasty bug which didn't allow read-only + mounts. +- Allow dir-cache filesystems to be mounted + read only. +- OFS support. +- Several other changes I just cannot remember + any more. + +Version 3.0 +----------- + +- Almost complete rewrite for the new VFS + interface in Linux 1.3. +- Write support. +- Support for hard and symbolic links. +- Lots of things I remember even less ... + +Version 2.0 +----------- + +- Fixed a few things to get it compiled. +- Automatic root block calculation. +- Partition checker for genhd.c + +======================================== + +Let's just call Ray Burr's original affs +'Version 1.0'. diff --git a/fs/affs/Makefile b/fs/affs/Makefile new file mode 100644 index 00000000000..b2c4f54446f --- /dev/null +++ b/fs/affs/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Linux affs filesystem routines. +# + +#EXTRA_CFLAGS=-DDEBUG=1 + +obj-$(CONFIG_AFFS_FS) += affs.o + +affs-objs := super.o namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o diff --git a/fs/affs/affs.h b/fs/affs/affs.h new file mode 100644 index 00000000000..0c6799f2137 --- /dev/null +++ b/fs/affs/affs.h @@ -0,0 +1,304 @@ +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/affs_fs.h> +#include <linux/amigaffs.h> + +/* AmigaOS allows file names with up to 30 characters length. + * Names longer than that will be silently truncated. If you + * want to disallow this, comment out the following #define. + * Creating filesystem objects with longer names will then + * result in an error (ENAMETOOLONG). + */ +/*#define AFFS_NO_TRUNCATE */ + +/* Ugly macros make the code more pretty. */ + +#define GET_END_PTR(st,p,sz) ((st *)((char *)(p)+((sz)-sizeof(st)))) +#define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey]) +#define AFFS_BLOCK(sb, bh, blk) (AFFS_HEAD(bh)->table[AFFS_SB(sb)->s_hashsize-1-(blk)]) + +#ifdef __LITTLE_ENDIAN +#define BO_EXBITS 0x18UL +#elif defined(__BIG_ENDIAN) +#define BO_EXBITS 0x00UL +#else +#error Endianness must be known for affs to work. +#endif + +#define AFFS_HEAD(bh) ((struct affs_head *)(bh)->b_data) +#define AFFS_TAIL(sb, bh) ((struct affs_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_tail))) +#define AFFS_ROOT_HEAD(bh) ((struct affs_root_head *)(bh)->b_data) +#define AFFS_ROOT_TAIL(sb, bh) ((struct affs_root_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_root_tail))) +#define AFFS_DATA_HEAD(bh) ((struct affs_data_head *)(bh)->b_data) +#define AFFS_DATA(bh) (((struct affs_data_head *)(bh)->b_data)->data) + +#define AFFS_CACHE_SIZE PAGE_SIZE + +#define AFFS_MAX_PREALLOC 32 +#define AFFS_LC_SIZE (AFFS_CACHE_SIZE/sizeof(u32)/2) +#define AFFS_AC_SIZE (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2) +#define AFFS_AC_MASK (AFFS_AC_SIZE-1) + +struct affs_ext_key { + u32 ext; /* idx of the extended block */ + u32 key; /* block number */ +}; + +/* + * affs fs inode data in memory + */ +struct affs_inode_info { + u32 i_opencnt; + struct semaphore i_link_lock; /* Protects internal inode access. */ + struct semaphore i_ext_lock; /* Protects internal inode access. */ +#define i_hash_lock i_ext_lock + u32 i_blkcnt; /* block count */ + u32 i_extcnt; /* extended block count */ + u32 *i_lc; /* linear cache of extended blocks */ + u32 i_lc_size; + u32 i_lc_shift; + u32 i_lc_mask; + struct affs_ext_key *i_ac; /* associative cache of extended blocks */ + u32 i_ext_last; /* last accessed extended block */ + struct buffer_head *i_ext_bh; /* bh of last extended block */ + loff_t mmu_private; + u32 i_protect; /* unused attribute bits */ + u32 i_lastalloc; /* last allocated block */ + int i_pa_cnt; /* number of preallocated blocks */ + struct inode vfs_inode; +}; + +/* short cut to get to the affs specific inode data */ +static inline struct affs_inode_info *AFFS_I(struct inode *inode) +{ + return list_entry(inode, struct affs_inode_info, vfs_inode); +} + +/* + * super-block data in memory + * + * Block numbers are adjusted for their actual size + * + */ + +struct affs_bm_info { + u32 bm_key; /* Disk block number */ + u32 bm_free; /* Free blocks in here */ +}; + +struct affs_sb_info { + int s_partition_size; /* Partition size in blocks. */ + int s_reserved; /* Number of reserved blocks. */ + //u32 s_blksize; /* Initial device blksize */ + u32 s_data_blksize; /* size of the data block w/o header */ + u32 s_root_block; /* FFS root block number. */ + int s_hashsize; /* Size of hash table. */ + unsigned long s_flags; /* See below. */ + uid_t s_uid; /* uid to override */ + gid_t s_gid; /* gid to override */ + umode_t s_mode; /* mode to override */ + struct buffer_head *s_root_bh; /* Cached root block. */ + struct semaphore s_bmlock; /* Protects bitmap access. */ + struct affs_bm_info *s_bitmap; /* Bitmap infos. */ + u32 s_bmap_count; /* # of bitmap blocks. */ + u32 s_bmap_bits; /* # of bits in one bitmap blocks */ + u32 s_last_bmap; + struct buffer_head *s_bmap_bh; + char *s_prefix; /* Prefix for volumes and assigns. */ + int s_prefix_len; /* Length of prefix. */ + char s_volume[32]; /* Volume prefix for absolute symlinks. */ +}; + +#define SF_INTL 0x0001 /* International filesystem. */ +#define SF_BM_VALID 0x0002 /* Bitmap is valid. */ +#define SF_IMMUTABLE 0x0004 /* Protection bits cannot be changed */ +#define SF_QUIET 0x0008 /* chmod errors will be not reported */ +#define SF_SETUID 0x0010 /* Ignore Amiga uid */ +#define SF_SETGID 0x0020 /* Ignore Amiga gid */ +#define SF_SETMODE 0x0040 /* Ignore Amiga protection bits */ +#define SF_MUFS 0x0100 /* Use MUFS uid/gid mapping */ +#define SF_OFS 0x0200 /* Old filesystem */ +#define SF_PREFIX 0x0400 /* Buffer for prefix is allocated */ +#define SF_VERBOSE 0x0800 /* Talk about fs when mounting */ + +/* short cut to get to the affs specific sb data */ +static inline struct affs_sb_info *AFFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +/* amigaffs.c */ + +extern int affs_insert_hash(struct inode *inode, struct buffer_head *bh); +extern int affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh); +extern int affs_remove_header(struct dentry *dentry); +extern u32 affs_checksum_block(struct super_block *sb, struct buffer_head *bh); +extern void affs_fix_checksum(struct super_block *sb, struct buffer_head *bh); +extern void secs_to_datestamp(time_t secs, struct affs_date *ds); +extern mode_t prot_to_mode(u32 prot); +extern void mode_to_prot(struct inode *inode); +extern void affs_error(struct super_block *sb, const char *function, const char *fmt, ...); +extern void affs_warning(struct super_block *sb, const char *function, const char *fmt, ...); +extern int affs_check_name(const unsigned char *name, int len); +extern int affs_copy_name(unsigned char *bstr, struct dentry *dentry); + +/* bitmap. c */ + +extern u32 affs_count_free_blocks(struct super_block *s); +extern void affs_free_block(struct super_block *sb, u32 block); +extern u32 affs_alloc_block(struct inode *inode, u32 goal); +extern int affs_init_bitmap(struct super_block *sb, int *flags); +extern void affs_free_bitmap(struct super_block *sb); + +/* namei.c */ + +extern int affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len); +extern struct dentry *affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *); +extern int affs_unlink(struct inode *dir, struct dentry *dentry); +extern int affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *); +extern int affs_mkdir(struct inode *dir, struct dentry *dentry, int mode); +extern int affs_rmdir(struct inode *dir, struct dentry *dentry); +extern int affs_link(struct dentry *olddentry, struct inode *dir, + struct dentry *dentry); +extern int affs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname); +extern int affs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); + +/* inode.c */ + +extern unsigned long affs_parent_ino(struct inode *dir); +extern struct inode *affs_new_inode(struct inode *dir); +extern int affs_notify_change(struct dentry *dentry, struct iattr *attr); +extern void affs_put_inode(struct inode *inode); +extern void affs_delete_inode(struct inode *inode); +extern void affs_clear_inode(struct inode *inode); +extern void affs_read_inode(struct inode *inode); +extern int affs_write_inode(struct inode *inode, int); +extern int affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type); + +/* file.c */ + +void affs_free_prealloc(struct inode *inode); +extern void affs_truncate(struct inode *); + +/* dir.c */ + +extern void affs_dir_truncate(struct inode *); + +/* jump tables */ + +extern struct inode_operations affs_file_inode_operations; +extern struct inode_operations affs_dir_inode_operations; +extern struct inode_operations affs_symlink_inode_operations; +extern struct file_operations affs_file_operations; +extern struct file_operations affs_file_operations_ofs; +extern struct file_operations affs_dir_operations; +extern struct address_space_operations affs_symlink_aops; +extern struct address_space_operations affs_aops; +extern struct address_space_operations affs_aops_ofs; + +extern struct dentry_operations affs_dentry_operations; +extern struct dentry_operations affs_dentry_operations_intl; + +static inline void +affs_set_blocksize(struct super_block *sb, int size) +{ + sb_set_blocksize(sb, size); +} +static inline struct buffer_head * +affs_bread(struct super_block *sb, int block) +{ + pr_debug("affs_bread: %d\n", block); + if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) + return sb_bread(sb, block); + return NULL; +} +static inline struct buffer_head * +affs_getblk(struct super_block *sb, int block) +{ + pr_debug("affs_getblk: %d\n", block); + if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) + return sb_getblk(sb, block); + return NULL; +} +static inline struct buffer_head * +affs_getzeroblk(struct super_block *sb, int block) +{ + struct buffer_head *bh; + pr_debug("affs_getzeroblk: %d\n", block); + if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) { + 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; + } + return NULL; +} +static inline struct buffer_head * +affs_getemptyblk(struct super_block *sb, int block) +{ + struct buffer_head *bh; + pr_debug("affs_getemptyblk: %d\n", block); + if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) { + bh = sb_getblk(sb, block); + wait_on_buffer(bh); + set_buffer_uptodate(bh); + return bh; + } + return NULL; +} +static inline void +affs_brelse(struct buffer_head *bh) +{ + if (bh) + pr_debug("affs_brelse: %lld\n", (long long) bh->b_blocknr); + brelse(bh); +} + +static inline void +affs_adjust_checksum(struct buffer_head *bh, u32 val) +{ + u32 tmp = be32_to_cpu(((__be32 *)bh->b_data)[5]); + ((__be32 *)bh->b_data)[5] = cpu_to_be32(tmp - val); +} +static inline void +affs_adjust_bitmapchecksum(struct buffer_head *bh, u32 val) +{ + u32 tmp = be32_to_cpu(((__be32 *)bh->b_data)[0]); + ((__be32 *)bh->b_data)[0] = cpu_to_be32(tmp - val); +} + +static inline void +affs_lock_link(struct inode *inode) +{ + down(&AFFS_I(inode)->i_link_lock); +} +static inline void +affs_unlock_link(struct inode *inode) +{ + up(&AFFS_I(inode)->i_link_lock); +} +static inline void +affs_lock_dir(struct inode *inode) +{ + down(&AFFS_I(inode)->i_hash_lock); +} +static inline void +affs_unlock_dir(struct inode *inode) +{ + up(&AFFS_I(inode)->i_hash_lock); +} +static inline void +affs_lock_ext(struct inode *inode) +{ + down(&AFFS_I(inode)->i_ext_lock); +} +static inline void +affs_unlock_ext(struct inode *inode) +{ + up(&AFFS_I(inode)->i_ext_lock); +} diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c new file mode 100644 index 00000000000..ccd624ef427 --- /dev/null +++ b/fs/affs/amigaffs.c @@ -0,0 +1,509 @@ +/* + * linux/fs/affs/amigaffs.c + * + * (c) 1996 Hans-Joachim Widmaier - Rewritten + * + * (C) 1993 Ray Burr - Amiga FFS filesystem. + * + * Please send bug reports to: hjw@zvw.de + */ + +#include "affs.h" + +extern struct timezone sys_tz; + +static char ErrorBuffer[256]; + +/* + * Functions for accessing Amiga-FFS structures. + */ + + +/* Insert a header block bh into the directory dir + * caller must hold AFFS_DIR->i_hash_lock! + */ + +int +affs_insert_hash(struct inode *dir, struct buffer_head *bh) +{ + struct super_block *sb = dir->i_sb; + struct buffer_head *dir_bh; + u32 ino, hash_ino; + int offset; + + ino = bh->b_blocknr; + offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]); + + pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino); + + dir_bh = affs_bread(sb, dir->i_ino); + if (!dir_bh) + return -EIO; + + hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]); + while (hash_ino) { + affs_brelse(dir_bh); + dir_bh = affs_bread(sb, hash_ino); + if (!dir_bh) + return -EIO; + hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain); + } + AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino); + AFFS_TAIL(sb, bh)->hash_chain = 0; + affs_fix_checksum(sb, bh); + + if (dir->i_ino == dir_bh->b_blocknr) + AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino); + else + AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino); + + affs_adjust_checksum(dir_bh, ino); + mark_buffer_dirty_inode(dir_bh, dir); + affs_brelse(dir_bh); + + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + dir->i_version++; + mark_inode_dirty(dir); + + return 0; +} + +/* Remove a header block from its directory. + * caller must hold AFFS_DIR->i_hash_lock! + */ + +int +affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh) +{ + struct super_block *sb; + struct buffer_head *bh; + u32 rem_ino, hash_ino; + __be32 ino; + int offset, retval; + + sb = dir->i_sb; + rem_ino = rem_bh->b_blocknr; + offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]); + pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset); + + bh = affs_bread(sb, dir->i_ino); + if (!bh) + return -EIO; + + retval = -ENOENT; + hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]); + while (hash_ino) { + if (hash_ino == rem_ino) { + ino = AFFS_TAIL(sb, rem_bh)->hash_chain; + if (dir->i_ino == bh->b_blocknr) + AFFS_HEAD(bh)->table[offset] = ino; + else + AFFS_TAIL(sb, bh)->hash_chain = ino; + affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino); + mark_buffer_dirty_inode(bh, dir); + AFFS_TAIL(sb, rem_bh)->parent = 0; + retval = 0; + break; + } + affs_brelse(bh); + bh = affs_bread(sb, hash_ino); + if (!bh) + return -EIO; + hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); + } + + affs_brelse(bh); + + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + dir->i_version++; + mark_inode_dirty(dir); + + return retval; +} + +static void +affs_fix_dcache(struct dentry *dentry, u32 entry_ino) +{ + struct inode *inode = dentry->d_inode; + void *data = dentry->d_fsdata; + struct list_head *head, *next; + + spin_lock(&dcache_lock); + head = &inode->i_dentry; + next = head->next; + while (next != head) { + dentry = list_entry(next, struct dentry, d_alias); + if (entry_ino == (u32)(long)dentry->d_fsdata) { + dentry->d_fsdata = data; + break; + } + next = next->next; + } + spin_unlock(&dcache_lock); +} + + +/* Remove header from link chain */ + +static int +affs_remove_link(struct dentry *dentry) +{ + struct inode *dir, *inode = dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct buffer_head *bh = NULL, *link_bh = NULL; + u32 link_ino, ino; + int retval; + + pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino); + retval = -EIO; + bh = affs_bread(sb, inode->i_ino); + if (!bh) + goto done; + + link_ino = (u32)(long)dentry->d_fsdata; + if (inode->i_ino == link_ino) { + /* we can't remove the head of the link, as its blocknr is still used as ino, + * so we remove the block of the first link instead. + */ + link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain); + link_bh = affs_bread(sb, link_ino); + if (!link_bh) + goto done; + + dir = iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent)); + if (!dir) + goto done; + + affs_lock_dir(dir); + affs_fix_dcache(dentry, link_ino); + retval = affs_remove_hash(dir, link_bh); + if (retval) + goto done; + mark_buffer_dirty_inode(link_bh, inode); + + memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32); + retval = affs_insert_hash(dir, bh); + if (retval) + goto done; + mark_buffer_dirty_inode(bh, inode); + + affs_unlock_dir(dir); + iput(dir); + } else { + link_bh = affs_bread(sb, link_ino); + if (!link_bh) + goto done; + } + + while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain)) != 0) { + if (ino == link_ino) { + __be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain; + AFFS_TAIL(sb, bh)->link_chain = ino2; + affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino); + mark_buffer_dirty_inode(bh, inode); + retval = 0; + /* Fix the link count, if bh is a normal header block without links */ + switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { + case ST_LINKDIR: + case ST_LINKFILE: + break; + default: + if (!AFFS_TAIL(sb, bh)->link_chain) + inode->i_nlink = 1; + } + affs_free_block(sb, link_ino); + goto done; + } + affs_brelse(bh); + bh = affs_bread(sb, ino); + if (!bh) + goto done; + } + retval = -ENOENT; +done: + affs_brelse(link_bh); + affs_brelse(bh); + return retval; +} + + +static int +affs_empty_dir(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh; + int retval, size; + + retval = -EIO; + bh = affs_bread(sb, inode->i_ino); + if (!bh) + goto done; + + retval = -ENOTEMPTY; + for (size = AFFS_SB(sb)->s_hashsize - 1; size >= 0; size--) + if (AFFS_HEAD(bh)->table[size]) + goto not_empty; + retval = 0; +not_empty: + affs_brelse(bh); +done: + return retval; +} + + +/* Remove a filesystem object. If the object to be removed has + * links to it, one of the links must be changed to inherit + * the file or directory. As above, any inode will do. + * The buffer will not be freed. If the header is a link, the + * block will be marked as free. + * This function returns a negative error number in case of + * an error, else 0 if the inode is to be deleted or 1 if not. + */ + +int +affs_remove_header(struct dentry *dentry) +{ + struct super_block *sb; + struct inode *inode, *dir; + struct buffer_head *bh = NULL; + int retval; + + dir = dentry->d_parent->d_inode; + sb = dir->i_sb; + + retval = -ENOENT; + inode = dentry->d_inode; + if (!inode) + goto done; + + pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino); + retval = -EIO; + bh = affs_bread(sb, (u32)(long)dentry->d_fsdata); + if (!bh) + goto done; + + affs_lock_link(inode); + affs_lock_dir(dir); + switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { + case ST_USERDIR: + /* if we ever want to support links to dirs + * i_hash_lock of the inode must only be + * taken after some checks + */ + affs_lock_dir(inode); + retval = affs_empty_dir(inode); + affs_unlock_dir(inode); + if (retval) + goto done_unlock; + break; + default: + break; + } + + retval = affs_remove_hash(dir, bh); + if (retval) + goto done_unlock; + mark_buffer_dirty_inode(bh, inode); + + affs_unlock_dir(dir); + + if (inode->i_nlink > 1) + retval = affs_remove_link(dentry); + else + inode->i_nlink = 0; + affs_unlock_link(inode); + inode->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + +done: + affs_brelse(bh); + return retval; + +done_unlock: + affs_unlock_dir(dir); + affs_unlock_link(inode); + goto done; +} + +/* Checksum a block, do various consistency checks and optionally return + the blocks type number. DATA points to the block. If their pointers + are non-null, *PTYPE and *STYPE are set to the primary and secondary + block types respectively, *HASHSIZE is set to the size of the hashtable + (which lets us calculate the block size). + Returns non-zero if the block is not consistent. */ + +u32 +affs_checksum_block(struct super_block *sb, struct buffer_head *bh) +{ + __be32 *ptr = (__be32 *)bh->b_data; + u32 sum; + int bsize; + + sum = 0; + for (bsize = sb->s_blocksize / sizeof(__be32); bsize > 0; bsize--) + sum += be32_to_cpu(*ptr++); + return sum; +} + +/* + * Calculate the checksum of a disk block and store it + * at the indicated position. + */ + +void +affs_fix_checksum(struct super_block *sb, struct buffer_head *bh) +{ + int cnt = sb->s_blocksize / sizeof(__be32); + __be32 *ptr = (__be32 *)bh->b_data; + u32 checksum; + __be32 *checksumptr; + + checksumptr = ptr + 5; + *checksumptr = 0; + for (checksum = 0; cnt > 0; ptr++, cnt--) + checksum += be32_to_cpu(*ptr); + *checksumptr = cpu_to_be32(-checksum); +} + +void +secs_to_datestamp(time_t secs, struct affs_date *ds) +{ + u32 days; + u32 minute; + + secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60); + if (secs < 0) + secs = 0; + days = secs / 86400; + secs -= days * 86400; + minute = secs / 60; + secs -= minute * 60; + + ds->days = cpu_to_be32(days); + ds->mins = cpu_to_be32(minute); + ds->ticks = cpu_to_be32(secs * 50); +} + +mode_t +prot_to_mode(u32 prot) +{ + int mode = 0; + + if (!(prot & FIBF_NOWRITE)) + mode |= S_IWUSR; + if (!(prot & FIBF_NOREAD)) + mode |= S_IRUSR; + if (!(prot & FIBF_NOEX |