diff options
Diffstat (limited to 'fs/affs/amigaffs.c')
-rw-r--r-- | fs/affs/amigaffs.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c new file mode 100644 index 000000000..63d76a86e --- /dev/null +++ b/fs/affs/amigaffs.c @@ -0,0 +1,273 @@ +/* + * linux/fs/affs/amigaffs.c + * + * (c) 1996 Hans-Joachim Widmaier - Rewritten + * + * (C) 1993 Ray Burr - Amiga FFS filesystem. + * + */ + +#include <linux/stat.h> +#include <linux/sched.h> +#include <linux/affs_fs.h> +#include <linux/string.h> +#include <linux/locks.h> +#include <linux/mm.h> +#include <linux/amigaffs.h> + +extern struct timezone sys_tz; + +/* + * Functions for accessing Amiga-FFS structures. + * + */ + +/* Find the next used hash entry at or after *HASH_POS in a directory's hash + table. *HASH_POS is assigned that entry's number. DIR_DATA points to + the directory header block in memory. If there are no more entries, + 0 is returned. Otherwise, the key number in the next used hash slot + is returned. */ + +int +affs_find_next_hash_entry(int hsize, void *dir_data, int *hash_pos) +{ + struct dir_front *dir_front = dir_data; + int i; + + for (i = *hash_pos; i < hsize; i++) + if (dir_front->hashtable[i] != 0) + break; + if (i >= hsize) + return 0; + *hash_pos = i; + return htonl(dir_front->hashtable[i]); +} + +/* Set *NAME to point to the file name in a file header block in memory + pointed to by FH_DATA. The length of the name is returned. */ + +int +affs_get_file_name(int bsize, void *fh_data, char **name) +{ + struct file_end *file_end; + + file_end = GET_END_PTR(struct file_end, fh_data, bsize); + if (file_end->file_name[0] == 0 + || file_end->file_name[0] > 30) { + printk ("affs_get_file_name: OOPS! bad filename\n"); + printk (" file_end->file_name[0] = %d\n", + file_end->file_name[0]); + *name = "***BAD_FILE***"; + return 14; + } + *name = (char *) &file_end->file_name[1]; + return file_end->file_name[0]; +} + +/* Find the predecessor in the hash chain */ + +int +affs_fix_hash_pred(struct inode *startino, int startoffset, int key, int newkey) +{ + struct buffer_head *bh = NULL; + int nextkey; + int ptype, stype; + int retval; + + nextkey = startino->i_ino; + retval = -ENOENT; + lock_super(startino->i_sb); + while (1) { + pr_debug("AFFS: fix_hash_pred(): next key=%d, offset=%d\n",nextkey,startoffset); + if (nextkey == 0) + break; + if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino)))) + break; + if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype) + || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR && + stype != ST_LINKFILE && stype != ST_LINKDIR && + stype != ST_ROOT && stype != ST_SOFTLINK)) { + printk("AFFS: bad block found in link chain (ptype=%d, stype=%d)\n", + ptype,stype); + affs_brelse(bh); + break; + } + nextkey = htonl(((__u32 *)bh->b_data)[startoffset]); + if (nextkey == key) { + ((__u32 *)bh->b_data)[startoffset] = newkey; + affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5); + mark_buffer_dirty(bh,1); + affs_brelse(bh); + retval = 0; + break; + } + affs_brelse(bh); + startoffset = AFFS_I2BSIZE(startino) / 4 - 4; + } + unlock_super(startino->i_sb); + + return retval; +} + +/* Remove inode from link chain */ + +int +affs_fix_link_pred(struct inode *startino, int key, int newkey) +{ + struct buffer_head *bh = NULL; + int nextkey; + int offset; + int etype = 0; + int ptype, stype; + int retval; + + offset = AFFS_I2BSIZE(startino) / 4 - 10; + nextkey = startino->i_ino; + retval = -ENOENT; + lock_super(startino->i_sb); + while (1) { + if (nextkey == 0) + break; + pr_debug("AFFS: find_link_pred(): next key=%d\n",nextkey); + if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino)))) + break; + if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype) + || ptype != T_SHORT) { + affs_brelse(bh); + break; + } + if (!etype) { + if (stype != ST_FILE && stype != ST_USERDIR) { + affs_brelse(bh); + break; + } + if (stype == ST_FILE) + etype = ST_LINKFILE; + else + etype = ST_LINKDIR; + } else if (stype != etype) { + affs_brelse(bh); + retval = -EPERM; + break; + } + nextkey = htonl(((__u32 *)bh->b_data)[offset]); + if (nextkey == key) { + FILE_END(bh->b_data,startino)->link_chain = newkey; + affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5); + mark_buffer_dirty(bh,1); + affs_brelse(bh); + retval = 0; + break; + } + affs_brelse(bh); + } + unlock_super(startino->i_sb); + return retval; +} + +/* 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(int bsize, void *data, int *ptype, int *stype) +{ + __u32 sum; + __u32 *p; + + bsize /= 4; + if (ptype) + *ptype = htonl(((__s32 *)data)[0]); + if (stype) + *stype = htonl(((__s32 *)data)[bsize - 1]); + + sum = 0; + p = data; + while (bsize--) + sum += htonl(*p++); + return sum; +} + +void +affs_fix_checksum(int bsize, void *data, int cspos) +{ + __u32 ocs; + __u32 cs; + + cs = affs_checksum_block(bsize,data,NULL,NULL); + ocs = htonl (((__u32 *)data)[cspos]); + ocs -= cs; + ((__u32 *)data)[cspos] = htonl(ocs); +} + +void +secs_to_datestamp(int secs, struct DateStamp *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->ds_Days = htonl(days); + ds->ds_Minute = htonl(minute); + ds->ds_Tick = htonl(secs * 50); +} + +int +prot_to_mode(__u32 prot) +{ + int mode = 0; + + if (AFFS_UMAYWRITE(prot)) + mode |= S_IWUSR; + if (AFFS_UMAYREAD(prot)) + mode |= S_IRUSR; + if (AFFS_UMAYEXECUTE(prot)) + mode |= S_IXUSR; + if (AFFS_GMAYWRITE(prot)) + mode |= S_IWGRP; + if (AFFS_GMAYREAD(prot)) + mode |= S_IRGRP; + if (AFFS_GMAYEXECUTE(prot)) + mode |= S_IXGRP; + if (AFFS_OMAYWRITE(prot)) + mode |= S_IWOTH; + if (AFFS_OMAYREAD(prot)) + mode |= S_IROTH; + if (AFFS_OMAYEXECUTE(prot)) + mode |= S_IXOTH; + + return mode; +} + +unsigned int +mode_to_prot(int mode) +{ + unsigned int prot = 0; + + if (mode & S_IXUSR) + prot |= FIBF_SCRIPT; + if (mode & S_IRUSR) + prot |= FIBF_READ; + if (mode & S_IWUSR) + prot |= FIBF_WRITE | FIBF_DELETE; + if (mode & S_IRGRP) + prot |= FIBF_GRP_READ; + if (mode & S_IWGRP) + prot |= FIBF_GRP_WRITE; + if (mode & S_IROTH) + prot |= FIBF_OTR_READ; + if (mode & S_IWOTH) + prot |= FIBF_OTR_WRITE; + + return prot; +} |