mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Linux-2.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!
This commit is contained in:
commit
1da177e4c3
17291 changed files with 6718755 additions and 0 deletions
343
fs/affs/Changes
Normal file
343
fs/affs/Changes
Normal file
|
|
@ -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: [email protected]
|
||||
|
||||
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 <[email protected]>]
|
||||
- Corruption in truncate() bugfix [Ken Tyler <[email protected]>]
|
||||
|
||||
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'.
|
||||
9
fs/affs/Makefile
Normal file
9
fs/affs/Makefile
Normal file
|
|
@ -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
|
||||
304
fs/affs/affs.h
Normal file
304
fs/affs/affs.h
Normal file
|
|
@ -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);
|
||||
}
|
||||
509
fs/affs/amigaffs.c
Normal file
509
fs/affs/amigaffs.c
Normal file
|
|
@ -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_NOEXECUTE))
|
||||
mode |= S_IXUSR;
|
||||
if (prot & FIBF_GRP_WRITE)
|
||||
mode |= S_IWGRP;
|
||||
if (prot & FIBF_GRP_READ)
|
||||
mode |= S_IRGRP;
|
||||
if (prot & FIBF_GRP_EXECUTE)
|
||||
mode |= S_IXGRP;
|
||||
if (prot & FIBF_OTR_WRITE)
|
||||
mode |= S_IWOTH;
|
||||
if (prot & FIBF_OTR_READ)
|
||||
mode |= S_IROTH;
|
||||
if (prot & FIBF_OTR_EXECUTE)
|
||||
mode |= S_IXOTH;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
void
|
||||
mode_to_prot(struct inode *inode)
|
||||
{
|
||||
u32 prot = AFFS_I(inode)->i_protect;
|
||||
mode_t mode = inode->i_mode;
|
||||
|
||||
if (!(mode & S_IXUSR))
|
||||
prot |= FIBF_NOEXECUTE;
|
||||
if (!(mode & S_IRUSR))
|
||||
prot |= FIBF_NOREAD;
|
||||
if (!(mode & S_IWUSR))
|
||||
prot |= FIBF_NOWRITE;
|
||||
if (mode & S_IXGRP)
|
||||
prot |= FIBF_GRP_EXECUTE;
|
||||
if (mode & S_IRGRP)
|
||||
prot |= FIBF_GRP_READ;
|
||||
if (mode & S_IWGRP)
|
||||
prot |= FIBF_GRP_WRITE;
|
||||
if (mode & S_IXOTH)
|
||||
prot |= FIBF_OTR_EXECUTE;
|
||||
if (mode & S_IROTH)
|
||||
prot |= FIBF_OTR_READ;
|
||||
if (mode & S_IWOTH)
|
||||
prot |= FIBF_OTR_WRITE;
|
||||
|
||||
AFFS_I(inode)->i_protect = prot;
|
||||
}
|
||||
|
||||
void
|
||||
affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args,fmt);
|
||||
vsprintf(ErrorBuffer,fmt,args);
|
||||
va_end(args);
|
||||
|
||||
printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", sb->s_id,
|
||||
function,ErrorBuffer);
|
||||
if (!(sb->s_flags & MS_RDONLY))
|
||||
printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
|
||||
sb->s_flags |= MS_RDONLY;
|
||||
}
|
||||
|
||||
void
|
||||
affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args,fmt);
|
||||
vsprintf(ErrorBuffer,fmt,args);
|
||||
va_end(args);
|
||||
|
||||
printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", sb->s_id,
|
||||
function,ErrorBuffer);
|
||||
}
|
||||
|
||||
/* Check if the name is valid for a affs object. */
|
||||
|
||||
int
|
||||
affs_check_name(const unsigned char *name, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (len > 30)
|
||||
#ifdef AFFS_NO_TRUNCATE
|
||||
return -ENAMETOOLONG;
|
||||
#else
|
||||
len = 30;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (name[i] < ' ' || name[i] == ':'
|
||||
|| (name[i] > 0x7e && name[i] < 0xa0))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function copies name to bstr, with at most 30
|
||||
* characters length. The bstr will be prepended by
|
||||
* a length byte.
|
||||
* NOTE: The name will must be already checked by
|
||||
* affs_check_name()!
|
||||
*/
|
||||
|
||||
int
|
||||
affs_copy_name(unsigned char *bstr, struct dentry *dentry)
|
||||
{
|
||||
int len = min(dentry->d_name.len, 30u);
|
||||
|
||||
*bstr++ = len;
|
||||
memcpy(bstr, dentry->d_name.name, len);
|
||||
return len;
|
||||
}
|
||||
390
fs/affs/bitmap.c
Normal file
390
fs/affs/bitmap.c
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* linux/fs/affs/bitmap.c
|
||||
*
|
||||
* (c) 1996 Hans-Joachim Widmaier
|
||||
*
|
||||
* bitmap.c contains the code that handles all bitmap related stuff -
|
||||
* block allocation, deallocation, calculation of free space.
|
||||
*/
|
||||
|
||||
#include "affs.h"
|
||||
|
||||
/* This is, of course, shamelessly stolen from fs/minix */
|
||||
|
||||
static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||
|
||||
static u32
|
||||
affs_count_free_bits(u32 blocksize, const void *data)
|
||||
{
|
||||
const u32 *map;
|
||||
u32 free;
|
||||
u32 tmp;
|
||||
|
||||
map = data;
|
||||
free = 0;
|
||||
for (blocksize /= 4; blocksize > 0; blocksize--) {
|
||||
tmp = *map++;
|
||||
while (tmp) {
|
||||
free += nibblemap[tmp & 0xf];
|
||||
tmp >>= 4;
|
||||
}
|
||||
}
|
||||
|
||||
return free;
|
||||
}
|
||||
|
||||
u32
|
||||
affs_count_free_blocks(struct super_block *sb)
|
||||
{
|
||||
struct affs_bm_info *bm;
|
||||
u32 free;
|
||||
int i;
|
||||
|
||||
pr_debug("AFFS: count_free_blocks()\n");
|
||||
|
||||
if (sb->s_flags & MS_RDONLY)
|
||||
return 0;
|
||||
|
||||
down(&AFFS_SB(sb)->s_bmlock);
|
||||
|
||||
bm = AFFS_SB(sb)->s_bitmap;
|
||||
free = 0;
|
||||
for (i = AFFS_SB(sb)->s_bmap_count; i > 0; bm++, i--)
|
||||
free += bm->bm_free;
|
||||
|
||||
up(&AFFS_SB(sb)->s_bmlock);
|
||||
|
||||
return free;
|
||||
}
|
||||
|
||||
void
|
||||
affs_free_block(struct super_block *sb, u32 block)
|
||||
{
|
||||
struct affs_sb_info *sbi = AFFS_SB(sb);
|
||||
struct affs_bm_info *bm;
|
||||
struct buffer_head *bh;
|
||||
u32 blk, bmap, bit, mask, tmp;
|
||||
__be32 *data;
|
||||
|
||||
pr_debug("AFFS: free_block(%u)\n", block);
|
||||
|
||||
if (block > sbi->s_partition_size)
|
||||
goto err_range;
|
||||
|
||||
blk = block - sbi->s_reserved;
|
||||
bmap = blk / sbi->s_bmap_bits;
|
||||
bit = blk % sbi->s_bmap_bits;
|
||||
bm = &sbi->s_bitmap[bmap];
|
||||
|
||||
down(&sbi->s_bmlock);
|
||||
|
||||
bh = sbi->s_bmap_bh;
|
||||
if (sbi->s_last_bmap != bmap) {
|
||||
affs_brelse(bh);
|
||||
bh = affs_bread(sb, bm->bm_key);
|
||||
if (!bh)
|
||||
goto err_bh_read;
|
||||
sbi->s_bmap_bh = bh;
|
||||
sbi->s_last_bmap = bmap;
|
||||
}
|
||||
|
||||
mask = 1 << (bit & 31);
|
||||
data = (__be32 *)bh->b_data + bit / 32 + 1;
|
||||
|
||||
/* mark block free */
|
||||
tmp = be32_to_cpu(*data);
|
||||
if (tmp & mask)
|
||||
goto err_free;
|
||||
*data = cpu_to_be32(tmp | mask);
|
||||
|
||||
/* fix checksum */
|
||||
tmp = be32_to_cpu(*(__be32 *)bh->b_data);
|
||||
*(__be32 *)bh->b_data = cpu_to_be32(tmp - mask);
|
||||
|
||||
mark_buffer_dirty(bh);
|
||||
sb->s_dirt = 1;
|
||||
bm->bm_free++;
|
||||
|
||||
up(&sbi->s_bmlock);
|
||||
return;
|
||||
|
||||
err_free:
|
||||
affs_warning(sb,"affs_free_block","Trying to free block %u which is already free", block);
|
||||
up(&sbi->s_bmlock);
|
||||
return;
|
||||
|
||||
err_bh_read:
|
||||
affs_error(sb,"affs_free_block","Cannot read bitmap block %u", bm->bm_key);
|
||||
sbi->s_bmap_bh = NULL;
|
||||
sbi->s_last_bmap = ~0;
|
||||
up(&sbi->s_bmlock);
|
||||
return;
|
||||
|
||||
err_range:
|
||||
affs_error(sb, "affs_free_block","Block %u outside partition", block);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a block in the given allocation zone.
|
||||
* Since we have to byte-swap the bitmap on little-endian
|
||||
* machines, this is rather expensive. Therefor we will
|
||||
* preallocate up to 16 blocks from the same word, if
|
||||
* possible. We are not doing preallocations in the
|
||||
* header zone, though.
|
||||
*/
|
||||
|
||||
u32
|
||||
affs_alloc_block(struct inode *inode, u32 goal)
|
||||
{
|
||||
struct super_block *sb;
|
||||
struct affs_sb_info *sbi;
|
||||
struct affs_bm_info *bm;
|
||||
struct buffer_head *bh;
|
||||
__be32 *data, *enddata;
|
||||
u32 blk, bmap, bit, mask, mask2, tmp;
|
||||
int i;
|
||||
|
||||
sb = inode->i_sb;
|
||||
sbi = AFFS_SB(sb);
|
||||
|
||||
pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal);
|
||||
|
||||
if (AFFS_I(inode)->i_pa_cnt) {
|
||||
pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1);
|
||||
AFFS_I(inode)->i_pa_cnt--;
|
||||
return ++AFFS_I(inode)->i_lastalloc;
|
||||
}
|
||||
|
||||
if (!goal || goal > sbi->s_partition_size) {
|
||||
if (goal)
|
||||
affs_warning(sb, "affs_balloc", "invalid goal %d", goal);
|
||||
//if (!AFFS_I(inode)->i_last_block)
|
||||
// affs_warning(sb, "affs_balloc", "no last alloc block");
|
||||
goal = sbi->s_reserved;
|
||||
}
|
||||
|
||||
blk = goal - sbi->s_reserved;
|
||||
bmap = blk / sbi->s_bmap_bits;
|
||||
bm = &sbi->s_bitmap[bmap];
|
||||
|
||||
down(&sbi->s_bmlock);
|
||||
|
||||
if (bm->bm_free)
|
||||
goto find_bmap_bit;
|
||||
|
||||
find_bmap:
|
||||
/* search for the next bmap buffer with free bits */
|
||||
i = sbi->s_bmap_count;
|
||||
do {
|
||||
if (--i < 0)
|
||||
goto err_full;
|
||||
bmap++;
|
||||
bm++;
|
||||
if (bmap < sbi->s_bmap_count)
|
||||
continue;
|
||||
/* restart search at zero */
|
||||
bmap = 0;
|
||||
bm = sbi->s_bitmap;
|
||||
} while (!bm->bm_free);
|
||||
blk = bmap * sbi->s_bmap_bits;
|
||||
|
||||
find_bmap_bit:
|
||||
|
||||
bh = sbi->s_bmap_bh;
|
||||
if (sbi->s_last_bmap != bmap) {
|
||||
affs_brelse(bh);
|
||||
bh = affs_bread(sb, bm->bm_key);
|
||||
if (!bh)
|
||||
goto err_bh_read;
|
||||
sbi->s_bmap_bh = bh;
|
||||
sbi->s_last_bmap = bmap;
|
||||
}
|
||||
|
||||
/* find an unused block in this bitmap block */
|
||||
bit = blk % sbi->s_bmap_bits;
|
||||
data = (__be32 *)bh->b_data + bit / 32 + 1;
|
||||
enddata = (__be32 *)((u8 *)bh->b_data + sb->s_blocksize);
|
||||
mask = ~0UL << (bit & 31);
|
||||
blk &= ~31UL;
|
||||
|
||||
tmp = be32_to_cpu(*data);
|
||||
if (tmp & mask)
|
||||
goto find_bit;
|
||||
|
||||
/* scan the rest of the buffer */
|
||||
do {
|
||||
blk += 32;
|
||||
if (++data >= enddata)
|
||||
/* didn't find something, can only happen
|
||||
* if scan didn't start at 0, try next bmap
|
||||
*/
|
||||
goto find_bmap;
|
||||
} while (!*data);
|
||||
tmp = be32_to_cpu(*data);
|
||||
mask = ~0;
|
||||
|
||||
find_bit:
|
||||
/* finally look for a free bit in the word */
|
||||
bit = ffs(tmp & mask) - 1;
|
||||
blk += bit + sbi->s_reserved;
|
||||
mask2 = mask = 1 << (bit & 31);
|
||||
AFFS_I(inode)->i_lastalloc = blk;
|
||||
|
||||
/* prealloc as much as possible within this word */
|
||||
while ((mask2 <<= 1)) {
|
||||
if (!(tmp & mask2))
|
||||
break;
|
||||
AFFS_I(inode)->i_pa_cnt++;
|
||||
mask |= mask2;
|
||||
}
|
||||
bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1;
|
||||
|
||||
*data = cpu_to_be32(tmp & ~mask);
|
||||
|
||||
/* fix checksum */
|
||||
tmp = be32_to_cpu(*(__be32 *)bh->b_data);
|
||||
*(__be32 *)bh->b_data = cpu_to_be32(tmp + mask);
|
||||
|
||||
mark_buffer_dirty(bh);
|
||||
sb->s_dirt = 1;
|
||||
|
||||
up(&sbi->s_bmlock);
|
||||
|
||||
pr_debug("%d\n", blk);
|
||||
return blk;
|
||||
|
||||
err_bh_read:
|
||||
affs_error(sb,"affs_read_block","Cannot read bitmap block %u", bm->bm_key);
|
||||
sbi->s_bmap_bh = NULL;
|
||||
sbi->s_last_bmap = ~0;
|
||||
err_full:
|
||||
up(&sbi->s_bmlock);
|
||||
pr_debug("failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int affs_init_bitmap(struct super_block *sb, int *flags)
|
||||
{
|
||||
struct affs_bm_info *bm;
|
||||
struct buffer_head *bmap_bh = NULL, *bh = NULL;
|
||||
__be32 *bmap_blk;
|
||||
u32 size, blk, end, offset, mask;
|
||||
int i, res = 0;
|
||||
struct affs_sb_info *sbi = AFFS_SB(sb);
|
||||
|
||||
if (*flags & MS_RDONLY)
|
||||
return 0;
|
||||
|
||||
if (!AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag) {
|
||||
printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
|
||||
sb->s_id);
|
||||
*flags |= MS_RDONLY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sbi->s_last_bmap = ~0;
|
||||
sbi->s_bmap_bh = NULL;
|
||||
sbi->s_bmap_bits = sb->s_blocksize * 8 - 32;
|
||||
sbi->s_bmap_count = (sbi->s_partition_size - sbi->s_reserved +
|
||||
sbi->s_bmap_bits - 1) / sbi->s_bmap_bits;
|
||||
size = sbi->s_bmap_count * sizeof(*bm);
|
||||
bm = sbi->s_bitmap = kmalloc(size, GFP_KERNEL);
|
||||
if (!sbi->s_bitmap) {
|
||||
printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(sbi->s_bitmap, 0, size);
|
||||
|
||||
bmap_blk = (__be32 *)sbi->s_root_bh->b_data;
|
||||
blk = sb->s_blocksize / 4 - 49;
|
||||
end = blk + 25;
|
||||
|
||||
for (i = sbi->s_bmap_count; i > 0; bm++, i--) {
|
||||
affs_brelse(bh);
|
||||
|
||||
bm->bm_key = be32_to_cpu(bmap_blk[blk]);
|
||||
bh = affs_bread(sb, bm->bm_key);
|
||||
if (!bh) {
|
||||
printk(KERN_ERR "AFFS: Cannot read bitmap\n");
|
||||
res = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (affs_checksum_block(sb, bh)) {
|
||||
printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n",
|
||||
bm->bm_key, sb->s_id);
|
||||
*flags |= MS_RDONLY;
|
||||
goto out;
|
||||
}
|
||||
pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key);
|
||||
bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
|
||||
|
||||
/* Don't try read the extension if this is the last block,
|
||||
* but we also need the right bm pointer below
|
||||
*/
|
||||
if (++blk < end || i == 1)
|
||||
continue;
|
||||
if (bmap_bh)
|
||||
affs_brelse(bmap_bh);
|
||||
bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk]));
|
||||
if (!bmap_bh) {
|
||||
printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
|
||||
res = -EIO;
|
||||
goto out;
|
||||
}
|
||||
bmap_blk = (__be32 *)bmap_bh->b_data;
|
||||
blk = 0;
|
||||
end = sb->s_blocksize / 4 - 1;
|
||||
}
|
||||
|
||||
offset = (sbi->s_partition_size - sbi->s_reserved) % sbi->s_bmap_bits;
|
||||
mask = ~(0xFFFFFFFFU << (offset & 31));
|
||||
pr_debug("last word: %d %d %d\n", offset, offset / 32 + 1, mask);
|
||||
offset = offset / 32 + 1;
|
||||
|
||||
if (mask) {
|
||||
u32 old, new;
|
||||
|
||||
/* Mark unused bits in the last word as allocated */
|
||||
old = be32_to_cpu(((__be32 *)bh->b_data)[offset]);
|
||||
new = old & mask;
|
||||
//if (old != new) {
|
||||
((__be32 *)bh->b_data)[offset] = cpu_to_be32(new);
|
||||
/* fix checksum */
|
||||
//new -= old;
|
||||
//old = be32_to_cpu(*(__be32 *)bh->b_data);
|
||||
//*(__be32 *)bh->b_data = cpu_to_be32(old - new);
|
||||
//mark_buffer_dirty(bh);
|
||||
//}
|
||||
/* correct offset for the bitmap count below */
|
||||
//offset++;
|
||||
}
|
||||
while (++offset < sb->s_blocksize / 4)
|
||||
((__be32 *)bh->b_data)[offset] = 0;
|
||||
((__be32 *)bh->b_data)[0] = 0;
|
||||
((__be32 *)bh->b_data)[0] = cpu_to_be32(-affs_checksum_block(sb, bh));
|
||||
mark_buffer_dirty(bh);
|
||||
|
||||
/* recalculate bitmap count for last block */
|
||||
bm--;
|
||||
bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
|
||||
|
||||
out:
|
||||
affs_brelse(bh);
|
||||
affs_brelse(bmap_bh);
|
||||
return res;
|
||||
}
|
||||
|
||||
void affs_free_bitmap(struct super_block *sb)
|
||||
{
|
||||
struct affs_sb_info *sbi = AFFS_SB(sb);
|
||||
|
||||
if (!sbi->s_bitmap)
|
||||
return;
|
||||
|
||||
affs_brelse(sbi->s_bmap_bh);
|
||||
sbi->s_bmap_bh = NULL;
|
||||
sbi->s_last_bmap = ~0;
|
||||
kfree(sbi->s_bitmap);
|
||||
sbi->s_bitmap = NULL;
|
||||
}
|
||||
155
fs/affs/dir.c
Normal file
155
fs/affs/dir.c
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* linux/fs/affs/dir.c
|
||||
*
|
||||
* (c) 1996 Hans-Joachim Widmaier - Rewritten
|
||||
*
|
||||
* (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
|
||||
*
|
||||
* (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
|
||||
*
|
||||
* (C) 1991 Linus Torvalds - minix filesystem
|
||||
*
|
||||
* affs directory handling functions
|
||||
*
|
||||
*/
|
||||
|
||||
#include "affs.h"
|
||||
|
||||
static int affs_readdir(struct file *, void *, filldir_t);
|
||||
|
||||
struct file_operations affs_dir_operations = {
|
||||
.read = generic_read_dir,
|
||||
.readdir = affs_readdir,
|
||||
.fsync = file_fsync,
|
||||
};
|
||||
|
||||
/*
|
||||
* directories can handle most operations...
|
||||
*/
|
||||
struct inode_operations affs_dir_inode_operations = {
|
||||
.create = affs_create,
|
||||
.lookup = affs_lookup,
|
||||
.link = affs_link,
|
||||
.unlink = affs_unlink,
|
||||
.symlink = affs_symlink,
|
||||
.mkdir = affs_mkdir,
|
||||
.rmdir = affs_rmdir,
|
||||
.rename = affs_rename,
|
||||
.setattr = affs_notify_change,
|
||||
};
|
||||
|
||||
static int
|
||||
affs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
{
|
||||
struct inode *inode = filp->f_dentry->d_inode;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *dir_bh;
|
||||
struct buffer_head *fh_bh;
|
||||
unsigned char *name;
|
||||
int namelen;
|
||||
u32 i;
|
||||
int hash_pos;
|
||||
int chain_pos;
|
||||
u32 f_pos;
|
||||
u32 ino;
|
||||
int stored;
|
||||
int res;
|
||||
|
||||
pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos);
|
||||
|
||||
stored = 0;
|
||||
res = -EIO;
|
||||
dir_bh = NULL;
|
||||
fh_bh = NULL;
|
||||
f_pos = filp->f_pos;
|
||||
|
||||
if (f_pos == 0) {
|
||||
filp->private_data = (void *)0;
|
||||
if (filldir(dirent, ".", 1, f_pos, inode->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++;
|
||||
}
|
||||
|
||||
affs_lock_dir(inode);
|
||||
chain_pos = (f_pos - 2) & 0xffff;
|
||||
hash_pos = (f_pos - 2) >> 16;
|
||||
if (chain_pos == 0xffff) {
|
||||
affs_warning(sb, "readdir", "More than 65535 entries in chain");
|
||||
chain_pos = 0;
|
||||
hash_pos++;
|
||||
filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
|
||||
}
|
||||
dir_bh = affs_bread(sb, inode->i_ino);
|
||||
if (!dir_bh)
|
||||
goto readdir_out;
|
||||
|
||||
/* If the directory hasn't changed since the last call to readdir(),
|
||||
* we can jump directly to where we left off.
|
||||
*/
|
||||
ino = (u32)(long)filp->private_data;
|
||||
if (ino && filp->f_version == inode->i_version) {
|
||||
pr_debug("AFFS: readdir() left off=%d\n", ino);
|
||||
goto inside;
|
||||
}
|
||||
|
||||
ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
|
||||
for (i = 0; ino && i < chain_pos; i++) {
|
||||
fh_bh = affs_bread(sb, ino);
|
||||
if (!fh_bh) {
|
||||
affs_error(sb, "readdir","Cannot read block %d", i);
|
||||
goto readdir_out;
|
||||
}
|
||||
ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
|
||||
affs_brelse(fh_bh);
|
||||
fh_bh = NULL;
|
||||
}
|
||||
if (ino)
|
||||
goto inside;
|
||||
hash_pos++;
|
||||
|
||||
for (; hash_pos < AFFS_SB(sb)->s_hashsize; hash_pos++) {
|
||||
ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
|
||||
if (!ino)
|
||||
continue;
|
||||
f_pos = (hash_pos << 16) + 2;
|
||||
inside:
|
||||
do {
|
||||
fh_bh = affs_bread(sb, ino);
|
||||
if (!fh_bh) {
|
||||
affs_error(sb, "readdir","Cannot read block %d", ino);
|
||||
goto readdir_done;
|
||||
}
|
||||
|
||||
namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30);
|
||||
name = AFFS_TAIL(sb, fh_bh)->name + 1;
|
||||
pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n",
|
||||
namelen, name, ino, hash_pos, f_pos);
|
||||
if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0)
|
||||
goto readdir_done;
|
||||
stored++;
|
||||
f_pos++;
|
||||
ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
|
||||
affs_brelse(fh_bh);
|
||||
fh_bh = NULL;
|
||||
} while (ino);
|
||||
}
|
||||
readdir_done:
|
||||
filp->f_pos = f_pos;
|
||||
filp->f_version = inode->i_version;
|
||||
filp->private_data = (void *)(long)ino;
|
||||
res = stored;
|
||||
|
||||
readdir_out:
|
||||
affs_brelse(dir_bh);
|
||||
affs_brelse(fh_bh);
|
||||
affs_unlock_dir(inode);
|
||||
pr_debug("AFFS: readdir()=%d\n", stored);
|
||||
return res;
|
||||
}
|
||||
920
fs/affs/file.c
Normal file
920
fs/affs/file.c
Normal file
|
|
@ -0,0 +1,920 @@
|
|||
/*
|
||||
* linux/fs/affs/file.c
|
||||
*
|
||||
* (c) 1996 Hans-Joachim Widmaier - Rewritten
|
||||
*
|
||||
* (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
|
||||
*
|
||||
* (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
|
||||
*
|
||||
* (C) 1991 Linus Torvalds - minix filesystem
|
||||
*
|
||||
* affs regular file handling primitives
|
||||
*/
|
||||
|
||||
#include "affs.h"
|
||||
|
||||
#if PAGE_SIZE < 4096
|
||||
#error PAGE_SIZE must be at least 4096
|
||||
#endif
|
||||
|
||||
static int affs_grow_extcache(struct inode *inode, u32 lc_idx);
|
||||
static struct buffer_head *affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext);
|
||||
static inline struct buffer_head *affs_get_extblock(struct inode *inode, u32 ext);
|
||||
static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext);
|
||||
static ssize_t affs_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos);
|
||||
static int affs_file_open(struct inode *inode, struct file *filp);
|
||||
static int affs_file_release(struct inode *inode, struct file *filp);
|
||||
|
||||
struct file_operations affs_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_file_read,
|
||||
.write = affs_file_write,
|
||||
.mmap = generic_file_mmap,
|
||||
.open = affs_file_open,
|
||||
.release = affs_file_release,
|
||||
.fsync = file_fsync,
|
||||
.sendfile = generic_file_sendfile,
|
||||
};
|
||||
|
||||
struct inode_operations affs_file_inode_operations = {
|
||||
.truncate = affs_truncate,
|
||||
.setattr = affs_notify_change,
|
||||
};
|
||||
|
||||
static int
|
||||
affs_file_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
if (atomic_read(&filp->f_count) != 1)
|
||||
return 0;
|
||||
pr_debug("AFFS: open(%d)\n", AFFS_I(inode)->i_opencnt);
|
||||
AFFS_I(inode)->i_opencnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
affs_file_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
if (atomic_read(&filp->f_count) != 0)
|
||||
return 0;
|
||||
pr_debug("AFFS: release(%d)\n", AFFS_I(inode)->i_opencnt);
|
||||
AFFS_I(inode)->i_opencnt--;
|
||||
if (!AFFS_I(inode)->i_opencnt)
|
||||
affs_free_prealloc(inode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
affs_grow_extcache(struct inode *inode, u32 lc_idx)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh;
|
||||
u32 lc_max;
|
||||
int i, j, key;
|
||||
|
||||
if (!AFFS_I(inode)->i_lc) {
|
||||
char *ptr = (char *)get_zeroed_page(GFP_NOFS);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
AFFS_I(inode)->i_lc = (u32 *)ptr;
|
||||
AFFS_I(inode)->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
|
||||
}
|
||||
|
||||
lc_max = AFFS_LC_SIZE << AFFS_I(inode)->i_lc_shift;
|
||||
|
||||
if (AFFS_I(inode)->i_extcnt > lc_max) {
|
||||
u32 lc_shift, lc_mask, tmp, off;
|
||||
|
||||
/* need to recalculate linear cache, start from old size */
|
||||
lc_shift = AFFS_I(inode)->i_lc_shift;
|
||||
tmp = (AFFS_I(inode)->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
|
||||
for (; tmp; tmp >>= 1)
|
||||
lc_shift++;
|
||||
lc_mask = (1 << lc_shift) - 1;
|
||||
|
||||
/* fix idx and old size to new shift */
|
||||
lc_idx >>= (lc_shift - AFFS_I(inode)->i_lc_shift);
|
||||
AFFS_I(inode)->i_lc_size >>= (lc_shift - AFFS_I(inode)->i_lc_shift);
|
||||
|
||||
/* first shrink old cache to make more space */
|
||||
off = 1 << (lc_shift - AFFS_I(inode)->i_lc_shift);
|
||||
for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off)
|
||||
AFFS_I(inode)->i_ac[i] = AFFS_I(inode)->i_ac[j];
|
||||
|
||||
AFFS_I(inode)->i_lc_shift = lc_shift;
|
||||
AFFS_I(inode)->i_lc_mask = lc_mask;
|
||||
}
|
||||
|
||||
/* fill cache to the needed index */
|
||||
i = AFFS_I(inode)->i_lc_size;
|
||||
AFFS_I(inode)->i_lc_size = lc_idx + 1;
|
||||
for (; i <= lc_idx; i++) {
|
||||
if (!i) {
|
||||
AFFS_I(inode)->i_lc[0] = inode->i_ino;
|
||||
continue;
|
||||
}
|
||||
key = AFFS_I(inode)->i_lc[i - 1];
|
||||
j = AFFS_I(inode)->i_lc_mask + 1;
|
||||
// unlock cache
|
||||
for (; j > 0; j--) {
|
||||
bh = affs_bread(sb, key);
|
||||
if (!bh)
|
||||
goto err;
|
||||
key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
|
||||
affs_brelse(bh);
|
||||
}
|
||||
// lock cache
|
||||
AFFS_I(inode)->i_lc[i] = key;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
// lock cache
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static struct buffer_head *
|
||||
affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *new_bh;
|
||||
u32 blocknr, tmp;
|
||||
|
||||
blocknr = affs_alloc_block(inode, bh->b_blocknr);
|
||||
if (!blocknr)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
|
||||
new_bh = affs_getzeroblk(sb, blocknr);
|
||||
if (!new_bh) {
|
||||
affs_free_block(sb, blocknr);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
AFFS_HEAD(new_bh)->ptype = cpu_to_be32(T_LIST);
|
||||
AFFS_HEAD(new_bh)->key = cpu_to_be32(blocknr);
|
||||
AFFS_TAIL(sb, new_bh)->stype = cpu_to_be32(ST_FILE);
|
||||
AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino);
|
||||
affs_fix_checksum(sb, new_bh);
|
||||
|
||||
mark_buffer_dirty_inode(new_bh, inode);
|
||||
|
||||
tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
|
||||
if (tmp)
|
||||
affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp);
|
||||
AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr);
|
||||
affs_adjust_checksum(bh, blocknr - tmp);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
|
||||
AFFS_I(inode)->i_extcnt++;
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
return new_bh;
|
||||
}
|
||||
|
||||
static inline struct buffer_head *
|
||||
affs_get_extblock(struct inode *inode, u32 ext)
|
||||
{
|
||||
/* inline the simplest case: same extended block as last time */
|
||||
struct buffer_head *bh = AFFS_I(inode)->i_ext_bh;
|
||||
if (ext == AFFS_I(inode)->i_ext_last)
|
||||
atomic_inc(&bh->b_count);
|
||||
else
|
||||
/* we have to do more (not inlined) */
|
||||
bh = affs_get_extblock_slow(inode, ext);
|
||||
|
||||
return bh;
|
||||
}
|
||||
|
||||
static struct buffer_head *
|
||||
affs_get_extblock_slow(struct inode *inode, u32 ext)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh;
|
||||
u32 ext_key;
|
||||
u32 lc_idx, lc_off, ac_idx;
|
||||
u32 tmp, idx;
|
||||
|
||||
if (ext == AFFS_I(inode)->i_ext_last + 1) {
|
||||
/* read the next extended block from the current one */
|
||||
bh = AFFS_I(inode)->i_ext_bh;
|
||||
ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
|
||||
if (ext < AFFS_I(inode)->i_extcnt)
|
||||
goto read_ext;
|
||||
if (ext > AFFS_I(inode)->i_extcnt)
|
||||
BUG();
|
||||
bh = affs_alloc_extblock(inode, bh, ext);
|
||||
if (IS_ERR(bh))
|
||||
return bh;
|
||||
goto store_ext;
|
||||
}
|
||||
|
||||
if (ext == 0) {
|
||||
/* we seek back to the file header block */
|
||||
ext_key = inode->i_ino;
|
||||
goto read_ext;
|
||||
}
|
||||
|
||||
if (ext >= AFFS_I(inode)->i_extcnt) {
|
||||
struct buffer_head *prev_bh;
|
||||
|
||||
/* allocate a new extended block */
|
||||
if (ext > AFFS_I(inode)->i_extcnt)
|
||||
BUG();
|
||||
|
||||
/* get previous extended block */
|
||||
prev_bh = affs_get_extblock(inode, ext - 1);
|
||||
if (IS_ERR(prev_bh))
|
||||
return prev_bh;
|
||||
bh = affs_alloc_extblock(inode, prev_bh, ext);
|
||||
affs_brelse(prev_bh);
|
||||
if (IS_ERR(bh))
|
||||
return bh;
|
||||
goto store_ext;
|
||||
}
|
||||
|
||||
again:
|
||||
/* check if there is an extended cache and whether it's large enough */
|
||||
lc_idx = ext >> AFFS_I(inode)->i_lc_shift;
|
||||
lc_off = ext & AFFS_I(inode)->i_lc_mask;
|
||||
|
||||
if (lc_idx >= AFFS_I(inode)->i_lc_size) {
|
||||
int err;
|
||||
|
||||
err = affs_grow_extcache(inode, lc_idx);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* every n'th key we find in the linear cache */
|
||||
if (!lc_off) {
|
||||
ext_key = AFFS_I(inode)->i_lc[lc_idx];
|
||||
goto read_ext;
|
||||
}
|
||||
|
||||
/* maybe it's still in the associative cache */
|
||||
ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK;
|
||||
if (AFFS_I(inode)->i_ac[ac_idx].ext == ext) {
|
||||
ext_key = AFFS_I(inode)->i_ac[ac_idx].key;
|
||||
goto read_ext;
|
||||
}
|
||||
|
||||
/* try to find one of the previous extended blocks */
|
||||
tmp = ext;
|
||||
idx = ac_idx;
|
||||
while (--tmp, --lc_off > 0) {
|
||||
idx = (idx - 1) & AFFS_AC_MASK;
|
||||
if (AFFS_I(inode)->i_ac[idx].ext == tmp) {
|
||||
ext_key = AFFS_I(inode)->i_ac[idx].key;
|
||||
goto find_ext;
|
||||
}
|
||||
}
|
||||
|
||||
/* fall back to the linear cache */
|
||||
ext_key = AFFS_I(inode)->i_lc[lc_idx];
|
||||
find_ext:
|
||||
/* read all extended blocks until we find the one we need */
|
||||
//unlock cache
|
||||
do {
|
||||
bh = affs_bread(sb, ext_key);
|
||||
if (!bh)
|
||||
goto err_bread;
|
||||
ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
|
||||
affs_brelse(bh);
|
||||
tmp++;
|
||||
} while (tmp < ext);
|
||||
//lock cache
|
||||
|
||||
/* store it in the associative cache */
|
||||
// recalculate ac_idx?
|
||||
AFFS_I(inode)->i_ac[ac_idx].ext = ext;
|
||||
AFFS_I(inode)->i_ac[ac_idx].key = ext_key;
|
||||
|
||||
read_ext:
|
||||
/* finally read the right extended block */
|
||||
//unlock cache
|
||||
bh = affs_bread(sb, ext_key);
|
||||
if (!bh)
|
||||
goto err_bread;
|
||||
//lock cache
|
||||
|
||||
store_ext:
|
||||
/* release old cached extended block and store the new one */
|
||||
affs_brelse(AFFS_I(inode)->i_ext_bh);
|
||||
AFFS_I(inode)->i_ext_last = ext;
|
||||
AFFS_I(inode)->i_ext_bh = bh;
|
||||
atomic_inc(&bh->b_count);
|
||||
|
||||
return bh;
|
||||
|
||||
err_bread:
|
||||
affs_brelse(bh);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
static int
|
||||
affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *ext_bh;
|
||||
u32 ext;
|
||||
|
||||
pr_debug("AFFS: get_block(%u, %lu)\n", (u32)inode->i_ino, (unsigned long)block);
|
||||
|
||||
|
||||
if (block > (sector_t)0x7fffffffUL)
|
||||
BUG();
|
||||
|
||||
if (block >= AFFS_I(inode)->i_blkcnt) {
|
||||
if (block > AFFS_I(inode)->i_blkcnt || !create)
|
||||
goto err_big;
|
||||
} else
|
||||
create = 0;
|
||||
|
||||
//lock cache
|
||||
affs_lock_ext(inode);
|
||||
|
||||
ext = (u32)block / AFFS_SB(sb)->s_hashsize;
|
||||
block -= ext * AFFS_SB(sb)->s_hashsize;
|
||||
ext_bh = affs_get_extblock(inode, ext);
|
||||
if (IS_ERR(ext_bh))
|
||||
goto err_ext;
|
||||
map_bh(bh_result, sb, (sector_t)be32_to_cpu(AFFS_BLOCK(sb, ext_bh, block)));
|
||||
|
||||
if (create) {
|
||||
u32 blocknr = affs_alloc_block(inode, ext_bh->b_blocknr);
|
||||
if (!blocknr)
|
||||
goto err_alloc;
|
||||
set_buffer_new(bh_result);
|
||||
AFFS_I(inode)->mmu_private += AFFS_SB(sb)->s_data_blksize;
|
||||
AFFS_I(inode)->i_blkcnt++;
|
||||
|
||||
/* store new block */
|
||||
if (bh_result->b_blocknr)
|
||||
affs_warning(sb, "get_block", "block already set (%x)", bh_result->b_blocknr);
|
||||
AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr);
|
||||
AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1);
|
||||
affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1);
|
||||
bh_result->b_blocknr = blocknr;
|
||||
|
||||
if (!block) {
|
||||
/* insert first block into header block */
|
||||
u32 tmp = be32_to_cpu(AFFS_HEAD(ext_bh)->first_data);
|
||||
if (tmp)
|
||||
affs_warning(sb, "get_block", "first block already set (%d)", tmp);
|
||||
AFFS_HEAD(ext_bh)->first_data = cpu_to_be32(blocknr);
|
||||
affs_adjust_checksum(ext_bh, blocknr - tmp);
|
||||
}
|
||||
}
|
||||
|
||||
affs_brelse(ext_bh);
|
||||
//unlock cache
|
||||
affs_unlock_ext(inode);
|
||||
return 0;
|
||||
|
||||
err_big:
|
||||
affs_error(inode->i_sb,"get_block","strange block request %d", block);
|
||||
return -EIO;
|
||||
err_ext:
|
||||
// unlock cache
|
||||
affs_unlock_ext(inode);
|
||||
return PTR_ERR(ext_bh);
|
||||
err_alloc:
|
||||
brelse(ext_bh);
|
||||
clear_buffer_mapped(bh_result);
|
||||
bh_result->b_bdev = NULL;
|
||||
// unlock cache
|
||||
affs_unlock_ext(inode);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static int affs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
return block_write_full_page(page, affs_get_block, wbc);
|
||||
}
|
||||
static int affs_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
return block_read_full_page(page, affs_get_block);
|
||||
}
|
||||
static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
|
||||
{
|
||||
return cont_prepare_write(page, from, to, affs_get_block,
|
||||
&AFFS_I(page->mapping->host)->mmu_private);
|
||||
}
|
||||
static sector_t _affs_bmap(struct address_space *mapping, sector_t block)
|
||||
{
|
||||
return generic_block_bmap(mapping,block,affs_get_block);
|
||||
}
|
||||
struct address_space_operations affs_aops = {
|
||||
.readpage = affs_readpage,
|
||||
.writepage = affs_writepage,
|
||||
.sync_page = block_sync_page,
|
||||
.prepare_write = affs_prepare_write,
|
||||
.commit_write = generic_commit_write,
|
||||
.bmap = _affs_bmap
|
||||
};
|
||||
|
||||
static inline struct buffer_head *
|
||||
affs_bread_ino(struct inode *inode, int block, int create)
|
||||
{
|
||||
struct buffer_head *bh, tmp_bh;
|
||||
int err;
|
||||
|
||||
tmp_bh.b_state = 0;
|
||||
err = affs_get_block(inode, block, &tmp_bh, create);
|
||||
if (!err) {
|
||||
bh = affs_bread(inode->i_sb, tmp_bh.b_blocknr);
|
||||
if (bh) {
|
||||
bh->b_state |= tmp_bh.b_state;
|
||||
return bh;
|
||||
}
|
||||
err = -EIO;
|
||||
}
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static inline struct buffer_head *
|
||||
affs_getzeroblk_ino(struct inode *inode, int block)
|
||||
{
|
||||
struct buffer_head *bh, tmp_bh;
|
||||
int err;
|
||||
|
||||
tmp_bh.b_state = 0;
|
||||
err = affs_get_block(inode, block, &tmp_bh, 1);
|
||||
if (!err) {
|
||||
bh = affs_getzeroblk(inode->i_sb, tmp_bh.b_blocknr);
|
||||
if (bh) {
|
||||
bh->b_state |= tmp_bh.b_state;
|
||||
return bh;
|
||||
}
|
||||
err = -EIO;
|
||||
}
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static inline struct buffer_head *
|
||||
affs_getemptyblk_ino(struct inode *inode, int block)
|
||||
{
|
||||
struct buffer_head *bh, tmp_bh;
|
||||
int err;
|
||||
|
||||
tmp_bh.b_state = 0;
|
||||
err = affs_get_block(inode, block, &tmp_bh, 1);
|
||||
if (!err) {
|
||||
bh = affs_getemptyblk(inode->i_sb, tmp_bh.b_blocknr);
|
||||
if (bh) {
|
||||
bh->b_state |= tmp_bh.b_state;
|
||||
return bh;
|
||||
}
|
||||
err = -EIO;
|
||||
}
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
affs_file_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t retval;
|
||||
|
||||
retval = generic_file_write (file, buf, count, ppos);
|
||||
if (retval >0) {
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
affs_do_readpage_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh;
|
||||
char *data;
|
||||
u32 bidx, boff, bsize;
|
||||
u32 tmp;
|
||||
|
||||
pr_debug("AFFS: read_page(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
|
||||
if (from > to || to > PAGE_CACHE_SIZE)
|
||||
BUG();
|
||||
kmap(page);
|
||||
data = page_address(page);
|
||||
bsize = AFFS_SB(sb)->s_data_blksize;
|
||||
tmp = (page->index << PAGE_CACHE_SHIFT) + from;
|
||||
bidx = tmp / bsize;
|
||||
boff = tmp % bsize;
|
||||
|
||||
while (from < to) {
|
||||
bh = affs_bread_ino(inode, bidx, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
tmp = min(bsize - boff, to - from);
|
||||
if (from + tmp > to || tmp > bsize)
|
||||
BUG();
|
||||
memcpy(data + from, AFFS_DATA(bh) + boff, tmp);
|
||||
affs_brelse(bh);
|
||||
bidx++;
|
||||
from += tmp;
|
||||
boff = 0;
|
||||
}
|
||||
flush_dcache_page(page);
|
||||
kunmap(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
affs_extent_file_ofs(struct inode *inode, u32 newsize)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh, *prev_bh;
|
||||
u32 bidx, boff;
|
||||
u32 size, bsize;
|
||||
u32 tmp;
|
||||
|
||||
pr_debug("AFFS: extent_file(%u, %d)\n", (u32)inode->i_ino, newsize);
|
||||
bsize = AFFS_SB(sb)->s_data_blksize;
|
||||
bh = NULL;
|
||||
size = AFFS_I(inode)->mmu_private;
|
||||
bidx = size / bsize;
|
||||
boff = size % bsize;
|
||||
if (boff) {
|
||||
bh = affs_bread_ino(inode, bidx, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
tmp = min(bsize - boff, newsize - size);
|
||||
if (boff + tmp > bsize || tmp > bsize)
|
||||
BUG();
|
||||
memset(AFFS_DATA(bh) + boff, 0, tmp);
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
|
||||
affs_fix_checksum(sb, bh);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
size += tmp;
|
||||
bidx++;
|
||||
} else if (bidx) {
|
||||
bh = affs_bread_ino(inode, bidx - 1, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
}
|
||||
|
||||
while (size < newsize) {
|
||||
prev_bh = bh;
|
||||
bh = affs_getzeroblk_ino(inode, bidx);
|
||||
if (IS_ERR(bh))
|
||||
goto out;
|
||||
tmp = min(bsize, newsize - size);
|
||||
if (tmp > bsize)
|
||||
BUG();
|
||||
AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
|
||||
AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
|
||||
AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
|
||||
affs_fix_checksum(sb, bh);
|
||||
bh->b_state &= ~(1UL << BH_New);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
if (prev_bh) {
|
||||
u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
|
||||
if (tmp)
|
||||
affs_warning(sb, "extent_file_ofs", "next block already set for %d (%d)", bidx, tmp);
|
||||
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
|
||||
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
|
||||
mark_buffer_dirty_inode(prev_bh, inode);
|
||||
affs_brelse(prev_bh);
|
||||
}
|
||||
size += bsize;
|
||||
bidx++;
|
||||
}
|
||||
affs_brelse(bh);
|
||||
inode->i_size = AFFS_I(inode)->mmu_private = newsize;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
inode->i_size = AFFS_I(inode)->mmu_private = newsize;
|
||||
return PTR_ERR(bh);
|
||||
}
|
||||
|
||||
static int
|
||||
affs_readpage_ofs(struct file *file, struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
u32 to;
|
||||
int err;
|
||||
|
||||
pr_debug("AFFS: read_page(%u, %ld)\n", (u32)inode->i_ino, page->index);
|
||||
to = PAGE_CACHE_SIZE;
|
||||
if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) {
|
||||
to = inode->i_size & ~PAGE_CACHE_MASK;
|
||||
memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to);
|
||||
}
|
||||
|
||||
err = affs_do_readpage_ofs(file, page, 0, to);
|
||||
if (!err)
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
u32 size, offset;
|
||||
u32 tmp;
|
||||
int err = 0;
|
||||
|
||||
pr_debug("AFFS: prepare_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
|
||||
offset = page->index << PAGE_CACHE_SHIFT;
|
||||
if (offset + from > AFFS_I(inode)->mmu_private) {
|
||||
err = affs_extent_file_ofs(inode, offset + from);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
size = inode->i_size;
|
||||
|
||||
if (PageUptodate(page))
|
||||
return 0;
|
||||
|
||||
if (from) {
|
||||
err = affs_do_readpage_ofs(file, page, 0, from);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (to < PAGE_CACHE_SIZE) {
|
||||
char *kaddr = kmap_atomic(page, KM_USER0);
|
||||
|
||||
memset(kaddr + to, 0, PAGE_CACHE_SIZE - to);
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
if (size > offset + to) {
|
||||
if (size < offset + PAGE_CACHE_SIZE)
|
||||
tmp = size & ~PAGE_CACHE_MASK;
|
||||
else
|
||||
tmp = PAGE_CACHE_SIZE;
|
||||
err = affs_do_readpage_ofs(file, page, to, tmp);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int affs_commit_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh, *prev_bh;
|
||||
char *data;
|
||||
u32 bidx, boff, bsize;
|
||||
u32 tmp;
|
||||
int written;
|
||||
|
||||
pr_debug("AFFS: commit_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
|
||||
bsize = AFFS_SB(sb)->s_data_blksize;
|
||||
data = page_address(page);
|
||||
|
||||
bh = NULL;
|
||||
written = 0;
|
||||
tmp = (page->index << PAGE_CACHE_SHIFT) + from;
|
||||
bidx = tmp / bsize;
|
||||
boff = tmp % bsize;
|
||||
if (boff) {
|
||||
bh = affs_bread_ino(inode, bidx, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
tmp = min(bsize - boff, to - from);
|
||||
if (boff + tmp > bsize || tmp > bsize)
|
||||
BUG();
|
||||
memcpy(AFFS_DATA(bh) + boff, data + from, tmp);
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
|
||||
affs_fix_checksum(sb, bh);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
written += tmp;
|
||||
from += tmp;
|
||||
bidx++;
|
||||
} else if (bidx) {
|
||||
bh = affs_bread_ino(inode, bidx - 1, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
}
|
||||
while (from + bsize <= to) {
|
||||
prev_bh = bh;
|
||||
bh = affs_getemptyblk_ino(inode, bidx);
|
||||
if (IS_ERR(bh))
|
||||
goto out;
|
||||
memcpy(AFFS_DATA(bh), data + from, bsize);
|
||||
if (buffer_new(bh)) {
|
||||
AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
|
||||
AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
|
||||
AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(bsize);
|
||||
AFFS_DATA_HEAD(bh)->next = 0;
|
||||
bh->b_state &= ~(1UL << BH_New);
|
||||
if (prev_bh) {
|
||||
u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
|
||||
if (tmp)
|
||||
affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp);
|
||||
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
|
||||
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
|
||||
mark_buffer_dirty_inode(prev_bh, inode);
|
||||
}
|
||||
}
|
||||
affs_brelse(prev_bh);
|
||||
affs_fix_checksum(sb, bh);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
written += bsize;
|
||||
from += bsize;
|
||||
bidx++;
|
||||
}
|
||||
if (from < to) {
|
||||
prev_bh = bh;
|
||||
bh = affs_bread_ino(inode, bidx, 1);
|
||||
if (IS_ERR(bh))
|
||||
goto out;
|
||||
tmp = min(bsize, to - from);
|
||||
if (tmp > bsize)
|
||||
BUG();
|
||||
memcpy(AFFS_DATA(bh), data + from, tmp);
|
||||
if (buffer_new(bh)) {
|
||||
AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
|
||||
AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
|
||||
AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
|
||||
AFFS_DATA_HEAD(bh)->next = 0;
|
||||
bh->b_state &= ~(1UL << BH_New);
|
||||
if (prev_bh) {
|
||||
u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
|
||||
if (tmp)
|
||||
affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp);
|
||||
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
|
||||
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
|
||||
mark_buffer_dirty_inode(prev_bh, inode);
|
||||
}
|
||||
} else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp)
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
|
||||
affs_brelse(prev_bh);
|
||||
affs_fix_checksum(sb, bh);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
written += tmp;
|
||||
from += tmp;
|
||||
bidx++;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
|
||||
done:
|
||||
affs_brelse(bh);
|
||||
tmp = (page->index << PAGE_CACHE_SHIFT) + from;
|
||||
if (tmp > inode->i_size)
|
||||
inode->i_size = AFFS_I(inode)->mmu_private = tmp;
|
||||
|
||||
return written;
|
||||
|
||||
out:
|
||||
bh = prev_bh;
|
||||
if (!written)
|
||||
written = PTR_ERR(bh);
|
||||
goto done;
|
||||
}
|
||||
|
||||
struct address_space_operations affs_aops_ofs = {
|
||||
.readpage = affs_readpage_ofs,
|
||||
//.writepage = affs_writepage_ofs,
|
||||
//.sync_page = affs_sync_page_ofs,
|
||||
.prepare_write = affs_prepare_write_ofs,
|
||||
.commit_write = affs_commit_write_ofs
|
||||
};
|
||||
|
||||
/* Free any preallocated blocks. */
|
||||
|
||||
void
|
||||
affs_free_prealloc(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
||||
pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
|
||||
|
||||
while (AFFS_I(inode)->i_pa_cnt) {
|
||||
AFFS_I(inode)->i_pa_cnt--;
|
||||
affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Truncate (or enlarge) a file to the requested size. */
|
||||
|
||||
void
|
||||
affs_truncate(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
u32 ext, ext_key;
|
||||
u32 last_blk, blkcnt, blk;
|
||||
u32 size;
|
||||
struct buffer_head *ext_bh;
|
||||
int i;
|
||||
|
||||
pr_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n",
|
||||
(u32)inode->i_ino, (u32)AFFS_I(inode)->mmu_private, (u32)inode->i_size);
|
||||
|
||||
last_blk = 0;
|
||||
ext = 0;
|
||||
if (inode->i_size) {
|
||||
last_blk = ((u32)inode->i_size - 1) / AFFS_SB(sb)->s_data_blksize;
|
||||
ext = last_blk / AFFS_SB(sb)->s_hashsize;
|
||||
}
|
||||
|
||||
if (inode->i_size > AFFS_I(inode)->mmu_private) {
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct page *page;
|
||||
u32 size = inode->i_size - 1;
|
||||
int res;
|
||||
|
||||
page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT);
|
||||
if (!page)
|
||||
return;
|
||||
size = (size & (PAGE_CACHE_SIZE - 1)) + 1;
|
||||
res = mapping->a_ops->prepare_write(NULL, page, size, size);
|
||||
if (!res)
|
||||
res = mapping->a_ops->commit_write(NULL, page, size, size);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
mark_inode_dirty(inode);
|
||||
return;
|
||||
} else if (inode->i_size == AFFS_I(inode)->mmu_private)
|
||||
return;
|
||||
|
||||
// lock cache
|
||||
ext_bh = affs_get_extblock(inode, ext);
|
||||
if (IS_ERR(ext_bh)) {
|
||||
affs_warning(sb, "truncate", "unexpected read error for ext block %u (%d)",
|
||||
ext, PTR_ERR(ext_bh));
|
||||
return;
|
||||
}
|
||||
if (AFFS_I(inode)->i_lc) {
|
||||
/* clear linear cache */
|
||||
i = (ext + 1) >> AFFS_I(inode)->i_lc_shift;
|
||||
if (AFFS_I(inode)->i_lc_size > i) {
|
||||
AFFS_I(inode)->i_lc_size = i;
|
||||
for (; i < AFFS_LC_SIZE; i++)
|
||||
AFFS_I(inode)->i_lc[i] = 0;
|
||||
}
|
||||
/* clear associative cache */
|
||||
for (i = 0; i < AFFS_AC_SIZE; i++)
|
||||
if (AFFS_I(inode)->i_ac[i].ext >= ext)
|
||||
AFFS_I(inode)->i_ac[i].ext = 0;
|
||||
}
|
||||
ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
|
||||
|
||||
blkcnt = AFFS_I(inode)->i_blkcnt;
|
||||
i = 0;
|
||||
blk = last_blk;
|
||||
if (inode->i_size) {
|
||||
i = last_blk % AFFS_SB(sb)->s_hashsize + 1;
|
||||
blk++;
|
||||
} else
|
||||
AFFS_HEAD(ext_bh)->first_data = 0;
|
||||
size = AFFS_SB(sb)->s_hashsize;
|
||||
if (size > blkcnt - blk + i)
|
||||
size = blkcnt - blk + i;
|
||||
for (; i < size; i++, blk++) {
|
||||
affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i)));
|
||||
AFFS_BLOCK(sb, ext_bh, i) = 0;
|
||||
}
|
||||
AFFS_TAIL(sb, ext_bh)->extension = 0;
|
||||
affs_fix_checksum(sb, ext_bh);
|
||||
mark_buffer_dirty_inode(ext_bh, inode);
|
||||
affs_brelse(ext_bh);
|
||||
|
||||
if (inode->i_size) {
|
||||
AFFS_I(inode)->i_blkcnt = last_blk + 1;
|
||||
AFFS_I(inode)->i_extcnt = ext + 1;
|
||||
if (AFFS_SB(sb)->s_flags & SF_OFS) {
|
||||
struct buffer_head *bh = affs_bread_ino(inode, last_blk, 0);
|
||||
u32 tmp;
|
||||
if (IS_ERR(ext_bh)) {
|
||||
affs_warning(sb, "truncate", "unexpected read error for last block %u (%d)",
|
||||
ext, PTR_ERR(ext_bh));
|
||||
return;
|
||||
}
|
||||
tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next);
|
||||
AFFS_DATA_HEAD(bh)->next = 0;
|
||||
affs_adjust_checksum(bh, -tmp);
|
||||
affs_brelse(bh);
|
||||
}
|
||||
} else {
|
||||
AFFS_I(inode)->i_blkcnt = 0;
|
||||
AFFS_I(inode)->i_extcnt = 1;
|
||||
}
|
||||
AFFS_I(inode)->mmu_private = inode->i_size;
|
||||
// unlock cache
|
||||
|
||||
while (ext_key) {
|
||||
ext_bh = affs_bread(sb, ext_key);
|
||||
size = AFFS_SB(sb)->s_hashsize;
|
||||
if (size > blkcnt - blk)
|
||||
size = blkcnt - blk;
|
||||
for (i = 0; i < size; i++, blk++)
|
||||
affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i)));
|
||||
affs_free_block(sb, ext_key);
|
||||
ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
|
||||
affs_brelse(ext_bh);
|
||||
}
|
||||
affs_free_prealloc(inode);
|
||||
}
|
||||
411
fs/affs/inode.c
Normal file
411
fs/affs/inode.c
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* linux/fs/affs/inode.c
|
||||
*
|
||||
* (c) 1996 Hans-Joachim Widmaier - Rewritten
|
||||
*
|
||||
* (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
|
||||
*
|
||||
* (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
|
||||
*
|
||||
* (C) 1991 Linus Torvalds - minix filesystem
|
||||
*/
|
||||
|
||||
#include "affs.h"
|
||||
|
||||
extern struct inode_operations affs_symlink_inode_operations;
|
||||
extern struct timezone sys_tz;
|
||||
|
||||
void
|
||||
affs_read_inode(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct affs_sb_info *sbi = AFFS_SB(sb);
|
||||
struct buffer_head *bh;
|
||||
struct affs_head *head;
|
||||
struct affs_tail *tail;
|
||||
u32 block;
|
||||
u32 size;
|
||||
u32 prot;
|
||||
u16 id;
|
||||
|
||||
pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
|
||||
|
||||
block = inode->i_ino;
|
||||
bh = affs_bread(sb, block);
|
||||
if (!bh) {
|
||||
affs_warning(sb, "read_inode", "Cannot read block %d", block);
|
||||
goto bad_inode;
|
||||
}
|
||||
if (affs_checksum_block(sb, bh) || be32_to_cpu(AFFS_HEAD(bh)->ptype) != T_SHORT) {
|
||||
affs_warning(sb,"read_inode",
|
||||
"Checksum or type (ptype=%d) error on inode %d",
|
||||
AFFS_HEAD(bh)->ptype, block);
|
||||
goto bad_inode;
|
||||
}
|
||||
|
||||
head = AFFS_HEAD(bh);
|
||||
tail = AFFS_TAIL(sb, bh);
|
||||
prot = be32_to_cpu(tail->protect);
|
||||
|
||||
inode->i_size = 0;
|
||||
inode->i_nlink = 1;
|
||||
inode->i_mode = 0;
|
||||
AFFS_I(inode)->i_extcnt = 1;
|
||||
AFFS_I(inode)->i_ext_last = ~1;
|
||||
AFFS_I(inode)->i_protect = prot;
|
||||
AFFS_I(inode)->i_opencnt = 0;
|
||||
AFFS_I(inode)->i_blkcnt = 0;
|
||||
AFFS_I(inode)->i_lc = NULL;
|
||||
AFFS_I(inode)->i_lc_size = 0;
|
||||
AFFS_I(inode)->i_lc_shift = 0;
|
||||
AFFS_I(inode)->i_lc_mask = 0;
|
||||
AFFS_I(inode)->i_ac = NULL;
|
||||
AFFS_I(inode)->i_ext_bh = NULL;
|
||||
AFFS_I(inode)->mmu_private = 0;
|
||||
AFFS_I(inode)->i_lastalloc = 0;
|
||||
AFFS_I(inode)->i_pa_cnt = 0;
|
||||
|
||||
if (sbi->s_flags & SF_SETMODE)
|
||||
inode->i_mode = sbi->s_mode;
|
||||
else
|
||||
inode->i_mode = prot_to_mode(prot);
|
||||
|
||||
id = be16_to_cpu(tail->uid);
|
||||
if (id == 0 || sbi->s_flags & SF_SETUID)
|
||||
inode->i_uid = sbi->s_uid;
|
||||
else if (id == 0xFFFF && sbi->s_flags & SF_MUFS)
|
||||
inode->i_uid = 0;
|
||||
else
|
||||
inode->i_uid = id;
|
||||
|
||||
id = be16_to_cpu(tail->gid);
|
||||
if (id == 0 || sbi->s_flags & SF_SETGID)
|
||||
inode->i_gid = sbi->s_gid;
|
||||
else if (id == 0xFFFF && sbi->s_flags & SF_MUFS)
|
||||
inode->i_gid = 0;
|
||||
else
|
||||
inode->i_gid = id;
|
||||
|
||||
switch (be32_to_cpu(tail->stype)) {
|
||||
case ST_ROOT:
|
||||
inode->i_uid = sbi->s_uid;
|
||||
inode->i_gid = sbi->s_gid;
|
||||
/* fall through */
|
||||
case ST_USERDIR:
|
||||
if (be32_to_cpu(tail->stype) == ST_USERDIR ||
|
||||
sbi->s_flags & SF_SETMODE) {
|
||||
if (inode->i_mode & S_IRUSR)
|
||||
inode->i_mode |= S_IXUSR;
|
||||
if (inode->i_mode & S_IRGRP)
|
||||
inode->i_mode |= S_IXGRP;
|
||||
if (inode->i_mode & S_IROTH)
|
||||
inode->i_mode |= S_IXOTH;
|
||||
inode->i_mode |= S_IFDIR;
|
||||
} else
|
||||
inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
|
||||
if (tail->link_chain)
|
||||
inode->i_nlink = 2;
|
||||
/* Maybe it should be controlled by mount parameter? */
|
||||
//inode->i_mode |= S_ISVTX;
|
||||
inode->i_op = &affs_dir_inode_operations;
|
||||
inode->i_fop = &affs_dir_operations;
|
||||
break;
|
||||
case ST_LINKDIR:
|
||||
#if 0
|
||||
affs_warning(sb, "read_inode", "inode is LINKDIR");
|
||||
goto bad_inode;
|
||||
#else
|
||||
inode->i_mode |= S_IFDIR;
|
||||
inode->i_op = NULL;
|
||||
inode->i_fop = NULL;
|
||||
break;
|
||||
#endif
|
||||
case ST_LINKFILE:
|
||||
affs_warning(sb, "read_inode", "inode is LINKFILE");
|
||||
goto bad_inode;
|
||||
case ST_FILE:
|
||||
size = be32_to_cpu(tail->size);
|
||||
inode->i_mode |= S_IFREG;
|
||||
AFFS_I(inode)->mmu_private = inode->i_size = size;
|
||||
if (inode->i_size) {
|
||||
AFFS_I(inode)->i_blkcnt = (size - 1) /
|
||||
sbi->s_data_blksize + 1;
|
||||
AFFS_I(inode)->i_extcnt = (AFFS_I(inode)->i_blkcnt - 1) /
|
||||
sbi->s_hashsize + 1;
|
||||
}
|
||||
if (tail->link_chain)
|
||||
inode->i_nlink = 2;
|
||||
inode->i_mapping->a_ops = (sbi->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
|
||||
inode->i_op = &affs_file_inode_operations;
|
||||
inode->i_fop = &affs_file_operations;
|
||||
break;
|
||||
case ST_SOFTLINK:
|
||||
inode->i_mode |= S_IFLNK;
|
||||
inode->i_op = &affs_symlink_inode_operations;
|
||||
inode->i_data.a_ops = &affs_symlink_aops;
|
||||
break;
|
||||
}
|
||||
|
||||
inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec
|
||||
= (be32_to_cpu(tail->change.days) * (24 * 60 * 60) +
|
||||
be32_to_cpu(tail->change.mins) * 60 +
|
||||
be32_to_cpu(tail->change.ticks) / 50 +
|
||||
((8 * 365 + 2) * 24 * 60 * 60)) +
|
||||
sys_tz.tz_minuteswest * 60;
|
||||
inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0;
|
||||
affs_brelse(bh);
|
||||
return;
|
||||
|
||||
bad_inode:
|
||||
make_bad_inode(inode);
|
||||
affs_brelse(bh);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
affs_write_inode(struct inode *inode, int unused)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh;
|
||||
struct affs_tail *tail;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino);
|
||||
|
||||
if (!inode->i_nlink)
|
||||
// possibly free block
|
||||
return 0;
|
||||
bh = affs_bread(sb, inode->i_ino);
|
||||
if (!bh) {
|
||||
affs_error(sb,"write_inode","Cannot read block %lu",inode->i_ino);
|
||||
return -EIO;
|
||||
}
|
||||
tail = AFFS_TAIL(sb, bh);
|
||||
if (tail->stype == cpu_to_be32(ST_ROOT)) {
|
||||
secs_to_datestamp(inode->i_mtime.tv_sec,&AFFS_ROOT_TAIL(sb, bh)->root_change);
|
||||
} else {
|
||||
tail->protect = cpu_to_be32(AFFS_I(inode)->i_protect);
|
||||
tail->size = cpu_to_be32(inode->i_size);
|
||||
secs_to_datestamp(inode->i_mtime.tv_sec,&tail->change);
|
||||
if (!(inode->i_ino == AFFS_SB(sb)->s_root_block)) {
|
||||
uid = inode->i_uid;
|
||||
gid = inode->i_gid;
|
||||
if (AFFS_SB(sb)->s_flags & SF_MUFS) {
|
||||
if (inode->i_uid == 0 || inode->i_uid == 0xFFFF)
|
||||
uid = inode->i_uid ^ ~0;
|
||||
if (inode->i_gid == 0 || inode->i_gid == 0xFFFF)
|
||||
gid = inode->i_gid ^ ~0;
|
||||
}
|
||||
if (!(AFFS_SB(sb)->s_flags & SF_SETUID))
|
||||
tail->uid = cpu_to_be16(uid);
|
||||
if (!(AFFS_SB(sb)->s_flags & SF_SETGID))
|
||||
tail->gid = cpu_to_be16(gid);
|
||||
}
|
||||
}
|
||||
affs_fix_checksum(sb, bh);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
affs_brelse(bh);
|
||||
affs_free_prealloc(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
affs_notify_change(struct dentry *dentry, struct iattr *attr)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int error;
|
||||
|
||||
pr_debug("AFFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid);
|
||||
|
||||
error = inode_change_ok(inode,attr);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (((attr->ia_valid & ATTR_UID) && (AFFS_SB(inode->i_sb)->s_flags & SF_SETUID)) ||
|
||||
((attr->ia_valid & ATTR_GID) && (AFFS_SB(inode->i_sb)->s_flags & SF_SETGID)) ||
|
||||
((attr->ia_valid & ATTR_MODE) &&
|
||||
(AFFS_SB(inode->i_sb)->s_flags & (SF_SETMODE | SF_IMMUTABLE)))) {
|
||||
if (!(AFFS_SB(inode->i_sb)->s_flags & SF_QUIET))
|
||||
error = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = inode_setattr(inode, attr);
|
||||
if (!error && (attr->ia_valid & ATTR_MODE))
|
||||
mode_to_prot(inode);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
affs_put_inode(struct inode *inode)
|
||||
{
|
||||
pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
|
||||
affs_free_prealloc(inode);
|
||||
if (atomic_read(&inode->i_count) == 1) {
|
||||
down(&inode->i_sem);
|
||||
if (inode->i_size != AFFS_I(inode)->mmu_private)
|
||||
affs_truncate(inode);
|
||||
up(&inode->i_sem);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
affs_delete_inode(struct inode *inode)
|
||||
{
|
||||
pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
|
||||
inode->i_size = 0;
|
||||
if (S_ISREG(inode->i_mode))
|
||||
affs_truncate(inode);
|
||||
clear_inode(inode);
|
||||
affs_free_block(inode->i_sb, inode->i_ino);
|
||||
}
|
||||
|
||||
void
|
||||
affs_clear_inode(struct inode *inode)
|
||||
{
|
||||
unsigned long cache_page = (unsigned long) AFFS_I(inode)->i_lc;
|
||||
|
||||
pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
|
||||
if (cache_page) {
|
||||
pr_debug("AFFS: freeing ext cache\n");
|
||||
AFFS_I(inode)->i_lc = NULL;
|
||||
AFFS_I(inode)->i_ac = NULL;
|
||||
free_page(cache_page);
|
||||
}
|
||||
affs_brelse(AFFS_I(inode)->i_ext_bh);
|
||||
AFFS_I(inode)->i_ext_last = ~1;
|
||||
AFFS_I(inode)->i_ext_bh = NULL;
|
||||
}
|
||||
|
||||
struct inode *
|
||||
affs_new_inode(struct inode *dir)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode;
|
||||
u32 block;
|
||||
struct buffer_head *bh;
|
||||
|
||||
if (!(inode = new_inode(sb)))
|
||||
goto err_inode;
|
||||
|
||||
if (!(block = affs_alloc_block(dir, dir->i_ino)))
|
||||
goto err_block;
|
||||
|
||||
bh = affs_getzeroblk(sb, block);
|
||||
if (!bh)
|
||||
goto err_bh;
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
affs_brelse(bh);
|
||||
|
||||
inode->i_uid = current->fsuid;
|
||||
inode->i_gid = current->fsgid;
|
||||
inode->i_ino = block;
|
||||
inode->i_nlink = 1;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
AFFS_I(inode)->i_opencnt = 0;
|
||||
AFFS_I(inode)->i_blkcnt = 0;
|
||||
AFFS_I(inode)->i_lc = NULL;
|
||||
AFFS_I(inode)->i_lc_size = 0;
|
||||
AFFS_I(inode)->i_lc_shift = 0;
|
||||
AFFS_I(inode)->i_lc_mask = 0;
|
||||
AFFS_I(inode)->i_ac = NULL;
|
||||
AFFS_I(inode)->i_ext_bh = NULL;
|
||||
AFFS_I(inode)->mmu_private = 0;
|
||||
AFFS_I(inode)->i_protect = 0;
|
||||
AFFS_I(inode)->i_lastalloc = 0;
|
||||
AFFS_I(inode)->i_pa_cnt = 0;
|
||||
AFFS_I(inode)->i_extcnt = 1;
|
||||
AFFS_I(inode)->i_ext_last = ~1;
|
||||
|
||||
insert_inode_hash(inode);
|
||||
|
||||
return inode;
|
||||
|
||||
err_bh:
|
||||
affs_free_block(sb, block);
|
||||
err_block:
|
||||
iput(inode);
|
||||
err_inode:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entry to a directory. Create the header block
|
||||
* and insert it into the hash table.
|
||||
*/
|
||||
|
||||
int
|
||||
affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct buffer_head *inode_bh = NULL;
|
||||
struct buffer_head *bh = NULL;
|
||||
u32 block = 0;
|
||||
int retval;
|
||||
|
||||
pr_debug("AFFS: add_entry(dir=%u, inode=%u, \"%*s\", type=%d)\n", (u32)dir->i_ino,
|
||||
(u32)inode->i_ino, (int)dentry->d_name.len, dentry->d_name.name, type);
|
||||
|
||||
retval = -EIO;
|
||||
bh = affs_bread(sb, inode->i_ino);
|
||||
if (!bh)
|
||||
goto done;
|
||||
|
||||
affs_lock_link(inode);
|
||||
switch (type) {
|
||||
case ST_LINKFILE:
|
||||
case ST_LINKDIR:
|
||||
inode_bh = bh;
|
||||
retval = -ENOSPC;
|
||||
block = affs_alloc_block(dir, dir->i_ino);
|
||||
if (!block)
|
||||
goto err;
|
||||
retval = -EIO;
|
||||
bh = affs_getzeroblk(sb, block);
|
||||
if (!bh)
|
||||
goto err;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
AFFS_HEAD(bh)->ptype = cpu_to_be32(T_SHORT);
|
||||
AFFS_HEAD(bh)->key = cpu_to_be32(bh->b_blocknr);
|
||||
affs_copy_name(AFFS_TAIL(sb, bh)->name, dentry);
|
||||
AFFS_TAIL(sb, bh)->stype = cpu_to_be32(type);
|
||||
AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
|
||||
|
||||
if (inode_bh) {
|
||||
__be32 chain;
|
||||
chain = AFFS_TAIL(sb, inode_bh)->link_chain;
|
||||
AFFS_TAIL(sb, bh)->original = cpu_to_be32(inode->i_ino);
|
||||
AFFS_TAIL(sb, bh)->link_chain = chain;
|
||||
AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block);
|
||||
affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain));
|
||||
mark_buffer_dirty_inode(inode_bh, inode);
|
||||
inode->i_nlink = 2;
|
||||
atomic_inc(&inode->i_count);
|
||||
}
|
||||
affs_fix_checksum(sb, bh);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
dentry->d_fsdata = (void *)(long)bh->b_blocknr;
|
||||
|
||||
affs_lock_dir(dir);
|
||||
retval = affs_insert_hash(dir, bh);
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
affs_unlock_dir(dir);
|
||||
affs_unlock_link(inode);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
done:
|
||||
affs_brelse(inode_bh);
|
||||
affs_brelse(bh);
|
||||
return retval;
|
||||
err:
|
||||
if (block)
|
||||
affs_free_block(sb, block);
|
||||
affs_unlock_link(inode);
|
||||
goto done;
|
||||
}
|
||||
443
fs/affs/namei.c
Normal file
443
fs/affs/namei.c
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
* linux/fs/affs/namei.c
|
||||
*
|
||||
* (c) 1996 Hans-Joachim Widmaier - Rewritten
|
||||
*
|
||||
* (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
|
||||
*
|
||||
* (C) 1991 Linus Torvalds - minix filesystem
|
||||
*/
|
||||
|
||||
#include "affs.h"
|
||||
|
||||
typedef int (*toupper_t)(int);
|
||||
|
||||
static int affs_toupper(int ch);
|
||||
static int affs_hash_dentry(struct dentry *, struct qstr *);
|
||||
static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
|
||||
static int affs_intl_toupper(int ch);
|
||||
static int affs_intl_hash_dentry(struct dentry *, struct qstr *);
|
||||
static int affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
|
||||
|
||||
struct dentry_operations affs_dentry_operations = {
|
||||
.d_hash = affs_hash_dentry,
|
||||
.d_compare = affs_compare_dentry,
|
||||
};
|
||||
|
||||
static struct dentry_operations affs_intl_dentry_operations = {
|
||||
.d_hash = affs_intl_hash_dentry,
|
||||
.d_compare = affs_intl_compare_dentry,
|
||||
};
|
||||
|
||||
|
||||
/* Simple toupper() for DOS\1 */
|
||||
|
||||
static int
|
||||
affs_toupper(int ch)
|
||||
{
|
||||
return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
|
||||
}
|
||||
|
||||
/* International toupper() for DOS\3 ("international") */
|
||||
|
||||
static int
|
||||
affs_intl_toupper(int ch)
|
||||
{
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
|
||||
&& ch <= 0xFE && ch != 0xF7) ?
|
||||
ch - ('a' - 'A') : ch;
|
||||
}
|
||||
|
||||
static inline toupper_t
|
||||
affs_get_toupper(struct super_block *sb)
|
||||
{
|
||||
return AFFS_SB(sb)->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: the dentry argument is the parent dentry.
|
||||
*/
|
||||
static inline int
|
||||
__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
|
||||
{
|
||||
const u8 *name = qstr->name;
|
||||
unsigned long hash;
|
||||
int i;
|
||||
|
||||
i = affs_check_name(qstr->name,qstr->len);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
hash = init_name_hash();
|
||||
i = min(qstr->len, 30u);
|
||||
for (; i > 0; name++, i--)
|
||||
hash = partial_name_hash(toupper(*name), hash);
|
||||
qstr->hash = end_name_hash(hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
return __affs_hash_dentry(dentry, qstr, affs_toupper);
|
||||
}
|
||||
static int
|
||||
affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
|
||||
{
|
||||
const u8 *aname = a->name;
|
||||
const u8 *bname = b->name;
|
||||
int len;
|
||||
|
||||
/* 'a' is the qstr of an already existing dentry, so the name
|
||||
* must be valid. 'b' must be validated first.
|
||||
*/
|
||||
|
||||
if (affs_check_name(b->name,b->len))
|
||||
return 1;
|
||||
|
||||
/* If the names are longer than the allowed 30 chars,
|
||||
* the excess is ignored, so their length may differ.
|
||||
*/
|
||||
len = a->len;
|
||||
if (len >= 30) {
|
||||
if (b->len < 30)
|
||||
return 1;
|
||||
len = 30;
|
||||
} else if (len != b->len)
|
||||
return 1;
|
||||
|
||||
for (; len > 0; len--)
|
||||
if (toupper(*aname++) != toupper(*bname++))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
{
|
||||
return __affs_compare_dentry(dentry, a, b, affs_toupper);
|
||||
}
|
||||
static int
|
||||
affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
{
|
||||
return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
|
||||
*/
|
||||
|
||||
static inline int
|
||||
affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
|
||||
{
|
||||
const u8 *name = dentry->d_name.name;
|
||||
int len = dentry->d_name.len;
|
||||
|
||||
if (len >= 30) {
|
||||
if (*name2 < 30)
|
||||
return 0;
|
||||
len = 30;
|
||||
} else if (len != *name2)
|
||||
return 0;
|
||||
|
||||
for (name2++; len > 0; len--)
|
||||
if (toupper(*name++) != toupper(*name2++))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
|
||||
{
|
||||
toupper_t toupper = affs_get_toupper(sb);
|
||||
int hash;
|
||||
|
||||
hash = len = min(len, 30u);
|
||||
for (; len > 0; len--)
|
||||
hash = (hash * 13 + toupper(*name++)) & 0x7ff;
|
||||
|
||||
return hash % AFFS_SB(sb)->s_hashsize;
|
||||
}
|
||||
|
||||
static struct buffer_head *
|
||||
affs_find_entry(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct buffer_head *bh;
|
||||
toupper_t toupper = affs_get_toupper(sb);
|
||||
u32 key;
|
||||
|
||||
pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
bh = affs_bread(sb, dir->i_ino);
|
||||
if (!bh)
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
|
||||
|
||||
for (;;) {
|
||||
affs_brelse(bh);
|
||||
if (key == 0)
|
||||
return NULL;
|
||||
bh = affs_bread(sb, key);
|
||||
if (!bh)
|
||||
return ERR_PTR(-EIO);
|
||||
if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
|
||||
return bh;
|
||||
key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
|
||||
}
|
||||
}
|
||||
|
||||
struct dentry *
|
||||
affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct buffer_head *bh;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
|
||||
|
||||
affs_lock_dir(dir);
|
||||
bh = affs_find_entry(dir, dentry);
|
||||
affs_unlock_dir(dir);
|
||||
if (IS_ERR(bh)) {
|
||||
return ERR_PTR(PTR_ERR(bh));
|
||||
}
|
||||
if (bh) {
|
||||
u32 ino = bh->b_blocknr;
|
||||
|
||||
/* store the real header ino in d_fsdata for faster lookups */
|
||||
dentry->d_fsdata = (void *)(long)ino;
|
||||
switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
|
||||
//link to dirs disabled
|
||||
//case ST_LINKDIR:
|
||||
case ST_LINKFILE:
|
||||
ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
|
||||
}
|
||||
affs_brelse(bh);
|
||||
inode = iget(sb, ino);
|
||||
if (!inode) {
|
||||
return ERR_PTR(-EACCES);
|
||||
}
|
||||
}
|
||||
dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
affs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino,
|
||||
(int)dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
return affs_remove_header(dentry);
|
||||
}
|
||||
|
||||
int
|
||||
affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode;
|
||||
int error;
|
||||
|
||||
pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
|
||||
dentry->d_name.name,mode);
|
||||
|
||||
inode = affs_new_inode(dir);
|
||||
if (!inode)
|
||||
return -ENOSPC;
|
||||
|
||||
inode->i_mode = mode;
|
||||
mode_to_prot(inode);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
inode->i_op = &affs_file_inode_operations;
|
||||
inode->i_fop = &affs_file_operations;
|
||||
inode->i_mapping->a_ops = (AFFS_SB(sb)->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
|
||||
error = affs_add_entry(dir, inode, dentry, ST_FILE);
|
||||
if (error) {
|
||||
inode->i_nlink = 0;
|
||||
iput(inode);
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
struct inode *inode;
|
||||
int error;
|
||||
|
||||
pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
|
||||
(int)dentry->d_name.len,dentry->d_name.name,mode);
|
||||
|
||||
inode = affs_new_inode(dir);
|
||||
if (!inode)
|
||||
return -ENOSPC;
|
||||
|
||||
inode->i_mode = S_IFDIR | mode;
|
||||
mode_to_prot(inode);
|
||||
|
||||
inode->i_op = &affs_dir_inode_operations;
|
||||
inode->i_fop = &affs_dir_operations;
|
||||
|
||||
error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
|
||||
if (error) {
|
||||
inode->i_nlink = 0;
|
||||
mark_inode_dirty(inode);
|
||||
iput(inode);
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
affs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino,
|
||||
(int)dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
return affs_remove_header(dentry);
|
||||
}
|
||||
|
||||
int
|
||||
affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct buffer_head *bh;
|
||||
struct inode *inode;
|
||||
char *p;
|
||||
int i, maxlen, error;
|
||||
char c, lc;
|
||||
|
||||
pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
|
||||
(int)dentry->d_name.len,dentry->d_name.name,symname);
|
||||
|
||||
maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1;
|
||||
inode = affs_new_inode(dir);
|
||||
if (!inode)
|
||||
return -ENOSPC;
|
||||
|
||||
inode->i_op = &affs_symlink_inode_operations;
|
||||
inode->i_data.a_ops = &affs_symlink_aops;
|
||||
inode->i_mode = S_IFLNK | 0777;
|
||||
mode_to_prot(inode);
|
||||
|
||||
error = -EIO;
|
||||
bh = affs_bread(sb, inode->i_ino);
|
||||
if (!bh)
|
||||
goto err;
|
||||
i = 0;
|
||||
p = (char *)AFFS_HEAD(bh)->table;
|
||||
lc = '/';
|
||||
if (*symname == '/') {
|
||||
while (*symname == '/')
|
||||
symname++;
|
||||
while (AFFS_SB(sb)->s_volume[i]) /* Cannot overflow */
|
||||
*p++ = AFFS_SB(sb)->s_volume[i++];
|
||||
}
|
||||
while (i < maxlen && (c = *symname++)) {
|
||||
if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
|
||||
*p++ = '/';
|
||||
i++;
|
||||
symname += 2;
|
||||
lc = '/';
|
||||
} else if (c == '.' && lc == '/' && *symname == '/') {
|
||||
symname++;
|
||||
lc = '/';
|
||||
} else {
|
||||
*p++ = c;
|
||||
lc = c;
|
||||
i++;
|
||||
}
|
||||
if (lc == '/')
|
||||
while (*symname == '/')
|
||||
symname++;
|
||||
}
|
||||
*p = 0;
|
||||
mark_buffer_dirty_inode(bh, inode);
|
||||
affs_brelse(bh);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
|
||||
if (error)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
inode->i_nlink = 0;
|
||||
mark_inode_dirty(inode);
|
||||
iput(inode);
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = old_dentry->d_inode;
|
||||
|
||||
pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
|
||||
(int)dentry->d_name.len,dentry->d_name.name);
|
||||
|
||||
return affs_add_entry(dir, inode, dentry, ST_LINKFILE);
|
||||
}
|
||||
|
||||
int
|
||||
affs_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 *bh = NULL;
|
||||
int retval;
|
||||
|
||||
pr_debug("AFFS: 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);
|
||||
|
||||
retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Unlink destination if it already exists */
|
||||
if (new_dentry->d_inode) {
|
||||
retval = affs_remove_header(new_dentry);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = -EIO;
|
||||
bh = affs_bread(sb, old_dentry->d_inode->i_ino);
|
||||
if (!bh)
|
||||
goto done;
|
||||
|
||||
/* Remove header from its parent directory. */
|
||||
affs_lock_dir(old_dir);
|
||||
retval = affs_remove_hash(old_dir, bh);
|
||||
affs_unlock_dir(old_dir);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
/* And insert it into the new directory with the new name. */
|
||||
affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
|
||||
affs_fix_checksum(sb, bh);
|
||||
affs_lock_dir(new_dir);
|
||||
retval = affs_insert_hash(new_dir, bh);
|
||||
affs_unlock_dir(new_dir);
|
||||
/* TODO: move it back to old_dir, if error? */
|
||||
|
||||
done:
|
||||
mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
|
||||
affs_brelse(bh);
|
||||
return retval;
|
||||
}
|
||||
569
fs/affs/super.c
Normal file
569
fs/affs/super.c
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
* linux/fs/affs/inode.c
|
||||
*
|
||||
* (c) 1996 Hans-Joachim Widmaier - Rewritten
|
||||
*
|
||||
* (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
|
||||
*
|
||||
* (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
|
||||
*
|
||||
* (C) 1991 Linus Torvalds - minix filesystem
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/parser.h>
|
||||
#include "affs.h"
|
||||
|
||||
extern struct timezone sys_tz;
|
||||
|
||||
static int affs_statfs(struct super_block *sb, struct kstatfs *buf);
|
||||
static int affs_remount (struct super_block *sb, int *flags, char *data);
|
||||
|
||||
static void
|
||||
affs_put_super(struct super_block *sb)
|
||||
{
|
||||
struct affs_sb_info *sbi = AFFS_SB(sb);
|
||||
pr_debug("AFFS: put_super()\n");
|
||||
|
||||
if (!(sb->s_flags & MS_RDONLY)) {
|
||||
AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag = cpu_to_be32(1);
|
||||
secs_to_datestamp(get_seconds(),
|
||||
&AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->disk_change);
|
||||
affs_fix_checksum(sb, sbi->s_root_bh);
|
||||
mark_buffer_dirty(sbi->s_root_bh);
|
||||
}
|
||||
|
||||
if (sbi->s_prefix)
|
||||
kfree(sbi->s_prefix);
|
||||
affs_free_bitmap(sb);
|
||||
affs_brelse(sbi->s_root_bh);
|
||||
kfree(sbi);
|
||||
sb->s_fs_info = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
affs_write_super(struct super_block *sb)
|
||||
{
|
||||
int clean = 2;
|
||||
struct affs_sb_info *sbi = AFFS_SB(sb);
|
||||
|
||||
if (!(sb->s_flags & MS_RDONLY)) {
|
||||
// if (sbi->s_bitmap[i].bm_bh) {
|
||||
// if (buffer_dirty(sbi->s_bitmap[i].bm_bh)) {
|
||||
// clean = 0;
|
||||
AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag = cpu_to_be32(clean);
|
||||
secs_to_datestamp(get_seconds(),
|
||||
&AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->disk_change);
|
||||
affs_fix_checksum(sb, sbi->s_root_bh);
|
||||
mark_buffer_dirty(sbi->s_root_bh);
|
||||
sb->s_dirt = !clean; /* redo until bitmap synced */
|
||||
} else
|
||||
sb->s_dirt = 0;
|
||||
|
||||
pr_debug("AFFS: write_super() at %lu, clean=%d\n", get_seconds(), clean);
|
||||
}
|
||||
|
||||
static kmem_cache_t * affs_inode_cachep;
|
||||
|
||||
static struct inode *affs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct affs_inode_info *ei;
|
||||
ei = (struct affs_inode_info *)kmem_cache_alloc(affs_inode_cachep, SLAB_KERNEL);
|
||||
if (!ei)
|
||||
return NULL;
|
||||
ei->vfs_inode.i_version = 1;
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void affs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
|
||||
}
|
||||
|
||||
static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
|
||||
{
|
||||
struct affs_inode_info *ei = (struct affs_inode_info *) foo;
|
||||
|
||||
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
|
||||
SLAB_CTOR_CONSTRUCTOR) {
|
||||
init_MUTEX(&ei->i_link_lock);
|
||||
init_MUTEX(&ei->i_ext_lock);
|
||||
inode_init_once(&ei->vfs_inode);
|
||||
}
|
||||
}
|
||||
|
||||
static int init_inodecache(void)
|
||||
{
|
||||
affs_inode_cachep = kmem_cache_create("affs_inode_cache",
|
||||
sizeof(struct affs_inode_info),
|
||||
0, SLAB_RECLAIM_ACCOUNT,
|
||||
init_once, NULL);
|
||||
if (affs_inode_cachep == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_inodecache(void)
|
||||
{
|
||||
if (kmem_cache_destroy(affs_inode_cachep))
|
||||
printk(KERN_INFO "affs_inode_cache: not all structures were freed\n");
|
||||
}
|
||||
|
||||
static struct super_operations affs_sops = {
|
||||
.alloc_inode = affs_alloc_inode,
|
||||
.destroy_inode = affs_destroy_inode,
|
||||
.read_inode = affs_read_inode,
|
||||
.write_inode = affs_write_inode,
|
||||
.put_inode = affs_put_inode,
|
||||
.delete_inode = affs_delete_inode,
|
||||
.clear_inode = affs_clear_inode,
|
||||
.put_super = affs_put_super,
|
||||
.write_super = affs_write_super,
|
||||
.statfs = affs_statfs,
|
||||
.remount_fs = affs_remount,
|
||||
};
|
||||
|
||||
enum {
|
||||
Opt_bs, Opt_mode, Opt_mufs, Opt_prefix, Opt_protect,
|
||||
Opt_reserved, Opt_root, Opt_setgid, Opt_setuid,
|
||||
Opt_verbose, Opt_volume, Opt_ignore, Opt_err,
|
||||
};
|
||||
|
||||
static match_table_t tokens = {
|
||||
{Opt_bs, "bs=%u"},
|
||||
{Opt_mode, "mode=%o"},
|
||||
{Opt_mufs, "mufs"},
|
||||
{Opt_prefix, "prefix=%s"},
|
||||
{Opt_protect, "protect"},
|
||||
{Opt_reserved, "reserved=%u"},
|
||||
{Opt_root, "root=%u"},
|
||||
{Opt_setgid, "setgid=%u"},
|
||||
{Opt_setuid, "setuid=%u"},
|
||||
{Opt_verbose, "verbose"},
|
||||
{Opt_volume, "volume=%s"},
|
||||
{Opt_ignore, "grpquota"},
|
||||
{Opt_ignore, "noquota"},
|
||||
{Opt_ignore, "quota"},
|
||||
{Opt_ignore, "usrquota"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
static int
|
||||
parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root,
|
||||
int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
|
||||
{
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
/* Fill in defaults */
|
||||
|
||||
*uid = current->uid;
|
||||
*gid = current->gid;
|
||||
*reserved = 2;
|
||||
*root = -1;
|
||||
*blocksize = -1;
|
||||
volume[0] = ':';
|
||||
volume[1] = 0;
|
||||
*mount_opts = 0;
|
||||
if (!options)
|
||||
return 1;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token, n, option;
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case Opt_bs:
|
||||
if (match_int(&args[0], &n))
|
||||
return -EINVAL;
|
||||
if (n != 512 && n != 1024 && n != 2048
|
||||
&& n != 4096) {
|
||||
printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed)\n");
|
||||
return 0;
|
||||
}
|
||||
*blocksize = n;
|
||||
break;
|
||||
case Opt_mode:
|
||||
if (match_octal(&args[0], &option))
|
||||
return 1;
|
||||
*mode = option & 0777;
|
||||
*mount_opts |= SF_SETMODE;
|
||||
break;
|
||||
case Opt_mufs:
|
||||
*mount_opts |= SF_MUFS;
|
||||
break;
|
||||
case Opt_prefix:
|
||||
if (*prefix) { /* Free any previous prefix */
|
||||
kfree(*prefix);
|
||||
*prefix = NULL;
|
||||
}
|
||||
*prefix = match_strdup(&args[0]);
|
||||
if (!*prefix)
|
||||
return 0;
|
||||
*mount_opts |= SF_PREFIX;
|
||||
break;
|
||||
case Opt_protect:
|
||||
*mount_opts |= SF_IMMUTABLE;
|
||||
break;
|
||||
case Opt_reserved:
|
||||
if (match_int(&args[0], reserved))
|
||||
return 1;
|
||||
break;
|
||||
case Opt_root:
|
||||
if (match_int(&args[0], root))
|
||||
return 1;
|
||||
break;
|
||||
case Opt_setgid:
|
||||
if (match_int(&args[0], &option))
|
||||
return 1;
|
||||
*gid = option;
|
||||
*mount_opts |= SF_SETGID;
|
||||
break;
|
||||
case Opt_setuid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
*uid = option;
|
||||
*mount_opts |= SF_SETUID;
|
||||
break;
|
||||
case Opt_verbose:
|
||||
*mount_opts |= SF_VERBOSE;
|
||||
break;
|
||||
case Opt_volume: {
|
||||
char *vol = match_strdup(&args[0]);
|
||||
strlcpy(volume, vol, 32);
|
||||
kfree(vol);
|
||||
break;
|
||||
}
|
||||
case Opt_ignore:
|
||||
/* Silently ignore the quota options */
|
||||
break;
|
||||
default:
|
||||
printk("AFFS: Unrecognized mount option \"%s\" "
|
||||
"or missing value\n", p);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This function definitely needs to be split up. Some fine day I'll
|
||||
* hopefully have the guts to do so. Until then: sorry for the mess.
|
||||
*/
|
||||
|
||||
static int affs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct affs_sb_info *sbi;
|
||||
struct buffer_head *root_bh = NULL;
|
||||
struct buffer_head *boot_bh;
|
||||
struct inode *root_inode = NULL;
|
||||
s32 root_block;
|
||||
int size, blocksize;
|
||||
u32 chksum;
|
||||
int num_bm;
|
||||
int i, j;
|
||||
s32 key;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int reserved;
|
||||
unsigned long mount_flags;
|
||||
int tmp_flags; /* fix remount prototype... */
|
||||
|
||||
pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options");
|
||||
|
||||
sb->s_magic = AFFS_SUPER_MAGIC;
|
||||
sb->s_op = &affs_sops;
|
||||
sb->s_flags |= MS_NODIRATIME;
|
||||
|
||||
sbi = kmalloc(sizeof(struct affs_sb_info), GFP_KERNEL);
|
||||
if (!sbi)
|
||||
return -ENOMEM;
|
||||
sb->s_fs_info = sbi;
|
||||
memset(sbi, 0, sizeof(*sbi));
|
||||
init_MUTEX(&sbi->s_bmlock);
|
||||
|
||||
if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
|
||||
&blocksize,&sbi->s_prefix,
|
||||
sbi->s_volume, &mount_flags)) {
|
||||
printk(KERN_ERR "AFFS: Error parsing options\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* N.B. after this point s_prefix must be released */
|
||||
|
||||
sbi->s_flags = mount_flags;
|
||||
sbi->s_mode = i;
|
||||
sbi->s_uid = uid;
|
||||
sbi->s_gid = gid;
|
||||
sbi->s_reserved= reserved;
|
||||
|
||||
/* Get the size of the device in 512-byte blocks.
|
||||
* If we later see that the partition uses bigger
|
||||
* blocks, we will have to change it.
|
||||
*/
|
||||
|
||||
size = sb->s_bdev->bd_inode->i_size >> 9;
|
||||
pr_debug("AFFS: initial blocksize=%d, #blocks=%d\n", 512, size);
|
||||
|
||||
affs_set_blocksize(sb, PAGE_SIZE);
|
||||
/* Try to find root block. Its location depends on the block size. */
|
||||
|
||||
i = 512;
|
||||
j = 4096;
|
||||
if (blocksize > 0) {
|
||||
i = j = blocksize;
|
||||
size = size / (blocksize / 512);
|
||||
}
|
||||
for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
|
||||
sbi->s_root_block = root_block;
|
||||
if (root_block < 0)
|
||||
sbi->s_root_block = (reserved + size - 1) / 2;
|
||||
pr_debug("AFFS: setting blocksize to %d\n", blocksize);
|
||||
affs_set_blocksize(sb, blocksize);
|
||||
sbi->s_partition_size = size;
|
||||
|
||||
/* The root block location that was calculated above is not
|
||||
* correct if the partition size is an odd number of 512-
|
||||
* byte blocks, which will be rounded down to a number of
|
||||
* 1024-byte blocks, and if there were an even number of
|
||||
* reserved blocks. Ideally, all partition checkers should
|
||||
* report the real number of blocks of the real blocksize,
|
||||
* but since this just cannot be done, we have to try to
|
||||
* find the root block anyways. In the above case, it is one
|
||||
* block behind the calculated one. So we check this one, too.
|
||||
*/
|
||||
for (num_bm = 0; num_bm < 2; num_bm++) {
|
||||
pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
|
||||
"size=%d, reserved=%d\n",
|
||||
sb->s_id,
|
||||
sbi->s_root_block + num_bm,
|
||||
blocksize, size, reserved);
|
||||
root_bh = affs_bread(sb, sbi->s_root_block + num_bm);
|
||||
if (!root_bh)
|
||||
continue;
|
||||
if (!affs_checksum_block(sb, root_bh) &&
|
||||
be32_to_cpu(AFFS_ROOT_HEAD(root_bh)->ptype) == T_SHORT &&
|
||||
be32_to_cpu(AFFS_ROOT_TAIL(sb, root_bh)->stype) == ST_ROOT) {
|
||||
sbi->s_hashsize = blocksize / 4 - 56;
|
||||
sbi->s_root_block += num_bm;
|
||||
key = 1;
|
||||
goto got_root;
|
||||
}
|
||||
affs_brelse(root_bh);
|
||||
root_bh = NULL;
|
||||
}
|
||||
}
|
||||
if (!silent)
|
||||
printk(KERN_ERR "AFFS: No valid root block on device %s\n",
|
||||
sb->s_id);
|
||||
goto out_error;
|
||||
|
||||
/* N.B. after this point bh must be released */
|
||||
got_root:
|
||||
root_block = sbi->s_root_block;
|
||||
|
||||
/* Find out which kind of FS we have */
|
||||
boot_bh = sb_bread(sb, 0);
|
||||
if (!boot_bh) {
|
||||
printk(KERN_ERR "AFFS: Cannot read boot block\n");
|
||||
goto out_error;
|
||||
}
|
||||
chksum = be32_to_cpu(*(__be32 *)boot_bh->b_data);
|
||||
brelse(boot_bh);
|
||||
|
||||
/* Dircache filesystems are compatible with non-dircache ones
|
||||
* when reading. As long as they aren't supported, writing is
|
||||
* not recommended.
|
||||
*/
|
||||
if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
|
||||
|| chksum == MUFS_DCOFS) && !(sb->s_flags & MS_RDONLY)) {
|
||||
printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
|
||||
sb->s_id);
|
||||
sb->s_flags |= MS_RDONLY;
|
||||
}
|
||||
switch (chksum) {
|
||||
case MUFS_FS:
|
||||
case MUFS_INTLFFS:
|
||||
case MUFS_DCFFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
/* fall thru */
|
||||
case FS_INTLFFS:
|
||||
case FS_DCFFS:
|
||||
sbi->s_flags |= SF_INTL;
|
||||
break;
|
||||
case MUFS_FFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
break;
|
||||
case FS_FFS:
|
||||
break;
|
||||
case MUFS_OFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
/* fall thru */
|
||||
case FS_OFS:
|
||||
sbi->s_flags |= SF_OFS;
|
||||
sb->s_flags |= MS_NOEXEC;
|
||||
break;
|
||||
case MUFS_DCOFS:
|
||||
case MUFS_INTLOFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
case FS_DCOFS:
|
||||
case FS_INTLOFS:
|
||||
sbi->s_flags |= SF_INTL | SF_OFS;
|
||||
sb->s_flags |= MS_NOEXEC;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
|
||||
sb->s_id, chksum);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (mount_flags & SF_VERBOSE) {
|
||||
chksum = cpu_to_be32(chksum);
|
||||
printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
|
||||
AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0],
|
||||
AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1,
|
||||
(char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
|
||||
}
|
||||
|
||||
sb->s_flags |= MS_NODEV | MS_NOSUID;
|
||||
|
||||
sbi->s_data_blksize = sb->s_blocksize;
|
||||
if (sbi->s_flags & SF_OFS)
|
||||
sbi->s_data_blksize -= 24;
|
||||
|
||||
/* Keep super block in cache */
|
||||
sbi->s_root_bh = root_bh;
|
||||
/* N.B. after this point s_root_bh must be released */
|
||||
|
||||
tmp_flags = sb->s_flags;
|
||||
if (affs_init_bitmap(sb, &tmp_flags))
|
||||
goto out_error;
|
||||
sb->s_flags = tmp_flags;
|
||||
|
||||
/* set up enough so that it can read an inode */
|
||||
|
||||
root_inode = iget(sb, root_block);
|
||||
sb->s_root = d_alloc_root(root_inode);
|
||||
if (!sb->s_root) {
|
||||
printk(KERN_ERR "AFFS: Get root inode failed\n");
|
||||
goto out_error;
|
||||
}
|
||||
sb->s_root->d_op = &affs_dentry_operations;
|
||||
|
||||
pr_debug("AFFS: s_flags=%lX\n",sb->s_flags);
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Begin the cascaded cleanup ...
|
||||
*/
|
||||
out_error:
|
||||
if (root_inode)
|
||||
iput(root_inode);
|
||||
if (sbi->s_bitmap)
|
||||
kfree(sbi->s_bitmap);
|
||||
affs_brelse(root_bh);
|
||||
if (sbi->s_prefix)
|
||||
kfree(sbi->s_prefix);
|
||||
kfree(sbi);
|
||||
sb->s_fs_info = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
affs_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
struct affs_sb_info *sbi = AFFS_SB(sb);
|
||||
int blocksize;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int mode;
|
||||
int reserved;
|
||||
int root_block;
|
||||
unsigned long mount_flags;
|
||||
int res = 0;
|
||||
|
||||
pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
|
||||
|
||||
*flags |= MS_NODIRATIME;
|
||||
|
||||
if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block,
|
||||
&blocksize,&sbi->s_prefix,sbi->s_volume,&mount_flags))
|
||||
return -EINVAL;
|
||||
sbi->s_flags = mount_flags;
|
||||
sbi->s_mode = mode;
|
||||
sbi->s_uid = uid;
|
||||
sbi->s_gid = gid;
|
||||
|
||||
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
|
||||
return 0;
|
||||
if (*flags & MS_RDONLY) {
|
||||
sb->s_dirt = 1;
|
||||
while (sb->s_dirt)
|
||||
affs_write_super(sb);
|
||||
affs_free_bitmap(sb);
|
||||
} else
|
||||
res = affs_init_bitmap(sb, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
affs_statfs(struct super_block *sb, struct kstatfs *buf)
|
||||
{
|
||||
int free;
|
||||
|
||||
pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",AFFS_SB(sb)->s_partition_size,
|
||||
AFFS_SB(sb)->s_reserved);
|
||||
|
||||
free = affs_count_free_blocks(sb);
|
||||
buf->f_type = AFFS_SUPER_MAGIC;
|
||||
buf->f_bsize = sb->s_blocksize;
|
||||
buf->f_blocks = AFFS_SB(sb)->s_partition_size - AFFS_SB(sb)->s_reserved;
|
||||
buf->f_bfree = free;
|
||||
buf->f_bavail = free;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct super_block *affs_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, affs_fill_super);
|
||||
}
|
||||
|
||||
static struct file_system_type affs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "affs",
|
||||
.get_sb = affs_get_sb,
|
||||
.kill_sb = kill_block_super,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
};
|
||||
|
||||
static int __init init_affs_fs(void)
|
||||
{
|
||||
int err = init_inodecache();
|
||||
if (err)
|
||||
goto out1;
|
||||
err = register_filesystem(&affs_fs_type);
|
||||
if (err)
|
||||
goto out;
|
||||
return 0;
|
||||
out:
|
||||
destroy_inodecache();
|
||||
out1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit exit_affs_fs(void)
|
||||
{
|
||||
unregister_filesystem(&affs_fs_type);
|
||||
destroy_inodecache();
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("Amiga filesystem support for Linux");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(init_affs_fs)
|
||||
module_exit(exit_affs_fs)
|
||||
78
fs/affs/symlink.c
Normal file
78
fs/affs/symlink.c
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* linux/fs/affs/symlink.c
|
||||
*
|
||||
* 1995 Hans-Joachim Widmaier - Modified for affs.
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*
|
||||
* affs symlink handling code
|
||||
*/
|
||||
|
||||
#include "affs.h"
|
||||
|
||||
static int affs_symlink_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
struct inode *inode = page->mapping->host;
|
||||
char *link = kmap(page);
|
||||
struct slink_front *lf;
|
||||
int err;
|
||||
int i, j;
|
||||
char c;
|
||||
char lc;
|
||||
char *pf;
|
||||
|
||||
pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino);
|
||||
|
||||
err = -EIO;
|
||||
bh = affs_bread(inode->i_sb, inode->i_ino);
|
||||
if (!bh)
|
||||
goto fail;
|
||||
i = 0;
|
||||
j = 0;
|
||||
lf = (struct slink_front *)bh->b_data;
|
||||
lc = 0;
|
||||
pf = AFFS_SB(inode->i_sb)->s_prefix ? AFFS_SB(inode->i_sb)->s_prefix : "/";
|
||||
|
||||
if (strchr(lf->symname,':')) { /* Handle assign or volume name */
|
||||
while (i < 1023 && (c = pf[i]))
|
||||
link[i++] = c;
|
||||
while (i < 1023 && lf->symname[j] != ':')
|
||||
link[i++] = lf->symname[j++];
|
||||
if (i < 1023)
|
||||
link[i++] = '/';
|
||||
j++;
|
||||
lc = '/';
|
||||
}
|
||||
while (i < 1023 && (c = lf->symname[j])) {
|
||||
if (c == '/' && lc == '/' && i < 1020) { /* parent dir */
|
||||
link[i++] = '.';
|
||||
link[i++] = '.';
|
||||
}
|
||||
link[i++] = c;
|
||||
lc = c;
|
||||
j++;
|
||||
}
|
||||
link[i] = '\0';
|
||||
affs_brelse(bh);
|
||||
SetPageUptodate(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
fail:
|
||||
SetPageError(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct address_space_operations affs_symlink_aops = {
|
||||
.readpage = affs_symlink_readpage,
|
||||
};
|
||||
|
||||
struct inode_operations affs_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = page_follow_link_light,
|
||||
.put_link = page_put_link,
|
||||
.setattr = affs_notify_change,
|
||||
};
|
||||
Loading…
Reference in a new issue