/*
|
/*
|
* linux/fs/affs/amigaffs.c
|
* linux/fs/affs/amigaffs.c
|
*
|
*
|
* (c) 1996 Hans-Joachim Widmaier - Rewritten
|
* (c) 1996 Hans-Joachim Widmaier - Rewritten
|
*
|
*
|
* (C) 1993 Ray Burr - Amiga FFS filesystem.
|
* (C) 1993 Ray Burr - Amiga FFS filesystem.
|
*
|
*
|
*/
|
*/
|
|
|
#include <linux/stat.h>
|
#include <linux/stat.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/affs_fs.h>
|
#include <linux/affs_fs.h>
|
#include <linux/string.h>
|
#include <linux/string.h>
|
#include <linux/locks.h>
|
#include <linux/locks.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
#include <linux/amigaffs.h>
|
#include <linux/amigaffs.h>
|
|
|
extern struct timezone sys_tz;
|
extern struct timezone sys_tz;
|
|
|
/*
|
/*
|
* Functions for accessing Amiga-FFS structures.
|
* Functions for accessing Amiga-FFS structures.
|
*
|
*
|
*/
|
*/
|
|
|
/* Find the next used hash entry at or after *HASH_POS in a directory's hash
|
/* 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
|
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,
|
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
|
0 is returned. Otherwise, the key number in the next used hash slot
|
is returned. */
|
is returned. */
|
|
|
int
|
int
|
affs_find_next_hash_entry(int hsize, void *dir_data, int *hash_pos)
|
affs_find_next_hash_entry(int hsize, void *dir_data, int *hash_pos)
|
{
|
{
|
struct dir_front *dir_front = dir_data;
|
struct dir_front *dir_front = dir_data;
|
int i;
|
int i;
|
|
|
for (i = *hash_pos; i < hsize; i++)
|
for (i = *hash_pos; i < hsize; i++)
|
if (dir_front->hashtable[i] != 0)
|
if (dir_front->hashtable[i] != 0)
|
break;
|
break;
|
if (i >= hsize)
|
if (i >= hsize)
|
return 0;
|
return 0;
|
*hash_pos = i;
|
*hash_pos = i;
|
return htonl(dir_front->hashtable[i]);
|
return htonl(dir_front->hashtable[i]);
|
}
|
}
|
|
|
/* Set *NAME to point to the file name in a file header block in memory
|
/* 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. */
|
pointed to by FH_DATA. The length of the name is returned. */
|
|
|
int
|
int
|
affs_get_file_name(int bsize, void *fh_data, char **name)
|
affs_get_file_name(int bsize, void *fh_data, char **name)
|
{
|
{
|
struct file_end *file_end;
|
struct file_end *file_end;
|
|
|
file_end = GET_END_PTR(struct file_end, fh_data, bsize);
|
file_end = GET_END_PTR(struct file_end, fh_data, bsize);
|
if (file_end->file_name[0] == 0
|
if (file_end->file_name[0] == 0
|
|| file_end->file_name[0] > 30) {
|
|| file_end->file_name[0] > 30) {
|
printk ("affs_get_file_name: OOPS! bad filename\n");
|
printk ("affs_get_file_name: OOPS! bad filename\n");
|
printk (" file_end->file_name[0] = %d\n",
|
printk (" file_end->file_name[0] = %d\n",
|
file_end->file_name[0]);
|
file_end->file_name[0]);
|
*name = "***BAD_FILE***";
|
*name = "***BAD_FILE***";
|
return 14;
|
return 14;
|
}
|
}
|
*name = (char *) &file_end->file_name[1];
|
*name = (char *) &file_end->file_name[1];
|
return file_end->file_name[0];
|
return file_end->file_name[0];
|
}
|
}
|
|
|
/* Find the predecessor in the hash chain */
|
/* Find the predecessor in the hash chain */
|
|
|
int
|
int
|
affs_fix_hash_pred(struct inode *startino, int startoffset, int key, int newkey)
|
affs_fix_hash_pred(struct inode *startino, int startoffset, int key, int newkey)
|
{
|
{
|
struct buffer_head *bh = NULL;
|
struct buffer_head *bh = NULL;
|
int nextkey;
|
int nextkey;
|
int ptype, stype;
|
int ptype, stype;
|
int retval;
|
int retval;
|
|
|
nextkey = startino->i_ino;
|
nextkey = startino->i_ino;
|
retval = -ENOENT;
|
retval = -ENOENT;
|
lock_super(startino->i_sb);
|
lock_super(startino->i_sb);
|
while (1) {
|
while (1) {
|
pr_debug("AFFS: fix_hash_pred(): next key=%d, offset=%d\n",nextkey,startoffset);
|
pr_debug("AFFS: fix_hash_pred(): next key=%d, offset=%d\n",nextkey,startoffset);
|
if (nextkey == 0)
|
if (nextkey == 0)
|
break;
|
break;
|
if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
|
if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
|
break;
|
break;
|
if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
|
if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
|
|| ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
|
|| ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
|
stype != ST_LINKFILE && stype != ST_LINKDIR &&
|
stype != ST_LINKFILE && stype != ST_LINKDIR &&
|
stype != ST_ROOT && stype != ST_SOFTLINK)) {
|
stype != ST_ROOT && stype != ST_SOFTLINK)) {
|
printk("AFFS: bad block found in link chain (ptype=%d, stype=%d)\n",
|
printk("AFFS: bad block found in link chain (ptype=%d, stype=%d)\n",
|
ptype,stype);
|
ptype,stype);
|
affs_brelse(bh);
|
affs_brelse(bh);
|
break;
|
break;
|
}
|
}
|
nextkey = htonl(((__u32 *)bh->b_data)[startoffset]);
|
nextkey = htonl(((__u32 *)bh->b_data)[startoffset]);
|
if (nextkey == key) {
|
if (nextkey == key) {
|
((__u32 *)bh->b_data)[startoffset] = newkey;
|
((__u32 *)bh->b_data)[startoffset] = newkey;
|
affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
|
affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
|
mark_buffer_dirty(bh,1);
|
mark_buffer_dirty(bh,1);
|
affs_brelse(bh);
|
affs_brelse(bh);
|
retval = 0;
|
retval = 0;
|
break;
|
break;
|
}
|
}
|
affs_brelse(bh);
|
affs_brelse(bh);
|
startoffset = AFFS_I2BSIZE(startino) / 4 - 4;
|
startoffset = AFFS_I2BSIZE(startino) / 4 - 4;
|
}
|
}
|
unlock_super(startino->i_sb);
|
unlock_super(startino->i_sb);
|
|
|
return retval;
|
return retval;
|
}
|
}
|
|
|
/* Remove inode from link chain */
|
/* Remove inode from link chain */
|
|
|
int
|
int
|
affs_fix_link_pred(struct inode *startino, int key, int newkey)
|
affs_fix_link_pred(struct inode *startino, int key, int newkey)
|
{
|
{
|
struct buffer_head *bh = NULL;
|
struct buffer_head *bh = NULL;
|
int nextkey;
|
int nextkey;
|
int offset;
|
int offset;
|
int etype = 0;
|
int etype = 0;
|
int ptype, stype;
|
int ptype, stype;
|
int retval;
|
int retval;
|
|
|
offset = AFFS_I2BSIZE(startino) / 4 - 10;
|
offset = AFFS_I2BSIZE(startino) / 4 - 10;
|
nextkey = startino->i_ino;
|
nextkey = startino->i_ino;
|
retval = -ENOENT;
|
retval = -ENOENT;
|
lock_super(startino->i_sb);
|
lock_super(startino->i_sb);
|
while (1) {
|
while (1) {
|
if (nextkey == 0)
|
if (nextkey == 0)
|
break;
|
break;
|
pr_debug("AFFS: find_link_pred(): next key=%d\n",nextkey);
|
pr_debug("AFFS: find_link_pred(): next key=%d\n",nextkey);
|
if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
|
if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
|
break;
|
break;
|
if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
|
if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
|
|| ptype != T_SHORT) {
|
|| ptype != T_SHORT) {
|
affs_brelse(bh);
|
affs_brelse(bh);
|
break;
|
break;
|
}
|
}
|
if (!etype) {
|
if (!etype) {
|
if (stype != ST_FILE && stype != ST_USERDIR) {
|
if (stype != ST_FILE && stype != ST_USERDIR) {
|
affs_brelse(bh);
|
affs_brelse(bh);
|
break;
|
break;
|
}
|
}
|
if (stype == ST_FILE)
|
if (stype == ST_FILE)
|
etype = ST_LINKFILE;
|
etype = ST_LINKFILE;
|
else
|
else
|
etype = ST_LINKDIR;
|
etype = ST_LINKDIR;
|
} else if (stype != etype) {
|
} else if (stype != etype) {
|
affs_brelse(bh);
|
affs_brelse(bh);
|
retval = -EPERM;
|
retval = -EPERM;
|
break;
|
break;
|
}
|
}
|
nextkey = htonl(((__u32 *)bh->b_data)[offset]);
|
nextkey = htonl(((__u32 *)bh->b_data)[offset]);
|
if (nextkey == key) {
|
if (nextkey == key) {
|
FILE_END(bh->b_data,startino)->link_chain = newkey;
|
FILE_END(bh->b_data,startino)->link_chain = newkey;
|
affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
|
affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
|
mark_buffer_dirty(bh,1);
|
mark_buffer_dirty(bh,1);
|
affs_brelse(bh);
|
affs_brelse(bh);
|
retval = 0;
|
retval = 0;
|
break;
|
break;
|
}
|
}
|
affs_brelse(bh);
|
affs_brelse(bh);
|
}
|
}
|
unlock_super(startino->i_sb);
|
unlock_super(startino->i_sb);
|
return retval;
|
return retval;
|
}
|
}
|
|
|
/* Checksum a block, do various consistency checks and optionally return
|
/* Checksum a block, do various consistency checks and optionally return
|
the blocks type number. DATA points to the block. If their pointers
|
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
|
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
|
block types respectively, *HASHSIZE is set to the size of the hashtable
|
(which lets us calculate the block size).
|
(which lets us calculate the block size).
|
Returns non-zero if the block is not consistent. */
|
Returns non-zero if the block is not consistent. */
|
|
|
__u32
|
__u32
|
affs_checksum_block(int bsize, void *data, int *ptype, int *stype)
|
affs_checksum_block(int bsize, void *data, int *ptype, int *stype)
|
{
|
{
|
__u32 sum;
|
__u32 sum;
|
__u32 *p;
|
__u32 *p;
|
|
|
bsize /= 4;
|
bsize /= 4;
|
if (ptype)
|
if (ptype)
|
*ptype = htonl(((__s32 *)data)[0]);
|
*ptype = htonl(((__s32 *)data)[0]);
|
if (stype)
|
if (stype)
|
*stype = htonl(((__s32 *)data)[bsize - 1]);
|
*stype = htonl(((__s32 *)data)[bsize - 1]);
|
|
|
sum = 0;
|
sum = 0;
|
p = data;
|
p = data;
|
while (bsize--)
|
while (bsize--)
|
sum += htonl(*p++);
|
sum += htonl(*p++);
|
return sum;
|
return sum;
|
}
|
}
|
|
|
void
|
void
|
affs_fix_checksum(int bsize, void *data, int cspos)
|
affs_fix_checksum(int bsize, void *data, int cspos)
|
{
|
{
|
__u32 ocs;
|
__u32 ocs;
|
__u32 cs;
|
__u32 cs;
|
|
|
cs = affs_checksum_block(bsize,data,NULL,NULL);
|
cs = affs_checksum_block(bsize,data,NULL,NULL);
|
ocs = htonl (((__u32 *)data)[cspos]);
|
ocs = htonl (((__u32 *)data)[cspos]);
|
ocs -= cs;
|
ocs -= cs;
|
((__u32 *)data)[cspos] = htonl(ocs);
|
((__u32 *)data)[cspos] = htonl(ocs);
|
}
|
}
|
|
|
void
|
void
|
secs_to_datestamp(int secs, struct DateStamp *ds)
|
secs_to_datestamp(int secs, struct DateStamp *ds)
|
{
|
{
|
__u32 days;
|
__u32 days;
|
__u32 minute;
|
__u32 minute;
|
|
|
secs -= sys_tz.tz_minuteswest * 60 +((8 * 365 + 2) * 24 * 60 * 60);
|
secs -= sys_tz.tz_minuteswest * 60 +((8 * 365 + 2) * 24 * 60 * 60);
|
if (secs < 0)
|
if (secs < 0)
|
secs = 0;
|
secs = 0;
|
days = secs / 86400;
|
days = secs / 86400;
|
secs -= days * 86400;
|
secs -= days * 86400;
|
minute = secs / 60;
|
minute = secs / 60;
|
secs -= minute * 60;
|
secs -= minute * 60;
|
|
|
ds->ds_Days = htonl(days);
|
ds->ds_Days = htonl(days);
|
ds->ds_Minute = htonl(minute);
|
ds->ds_Minute = htonl(minute);
|
ds->ds_Tick = htonl(secs * 50);
|
ds->ds_Tick = htonl(secs * 50);
|
}
|
}
|
|
|
int
|
int
|
prot_to_mode(__u32 prot)
|
prot_to_mode(__u32 prot)
|
{
|
{
|
int mode = 0;
|
int mode = 0;
|
|
|
if (AFFS_UMAYWRITE(prot))
|
if (AFFS_UMAYWRITE(prot))
|
mode |= S_IWUSR;
|
mode |= S_IWUSR;
|
if (AFFS_UMAYREAD(prot))
|
if (AFFS_UMAYREAD(prot))
|
mode |= S_IRUSR;
|
mode |= S_IRUSR;
|
if (AFFS_UMAYEXECUTE(prot))
|
if (AFFS_UMAYEXECUTE(prot))
|
mode |= S_IXUSR;
|
mode |= S_IXUSR;
|
if (AFFS_GMAYWRITE(prot))
|
if (AFFS_GMAYWRITE(prot))
|
mode |= S_IWGRP;
|
mode |= S_IWGRP;
|
if (AFFS_GMAYREAD(prot))
|
if (AFFS_GMAYREAD(prot))
|
mode |= S_IRGRP;
|
mode |= S_IRGRP;
|
if (AFFS_GMAYEXECUTE(prot))
|
if (AFFS_GMAYEXECUTE(prot))
|
mode |= S_IXGRP;
|
mode |= S_IXGRP;
|
if (AFFS_OMAYWRITE(prot))
|
if (AFFS_OMAYWRITE(prot))
|
mode |= S_IWOTH;
|
mode |= S_IWOTH;
|
if (AFFS_OMAYREAD(prot))
|
if (AFFS_OMAYREAD(prot))
|
mode |= S_IROTH;
|
mode |= S_IROTH;
|
if (AFFS_OMAYEXECUTE(prot))
|
if (AFFS_OMAYEXECUTE(prot))
|
mode |= S_IXOTH;
|
mode |= S_IXOTH;
|
|
|
return mode;
|
return mode;
|
}
|
}
|
|
|
unsigned int
|
unsigned int
|
mode_to_prot(int mode)
|
mode_to_prot(int mode)
|
{
|
{
|
unsigned int prot = 0;
|
unsigned int prot = 0;
|
|
|
if (mode & S_IXUSR)
|
if (mode & S_IXUSR)
|
prot |= FIBF_SCRIPT;
|
prot |= FIBF_SCRIPT;
|
if (mode & S_IRUSR)
|
if (mode & S_IRUSR)
|
prot |= FIBF_READ;
|
prot |= FIBF_READ;
|
if (mode & S_IWUSR)
|
if (mode & S_IWUSR)
|
prot |= FIBF_WRITE | FIBF_DELETE;
|
prot |= FIBF_WRITE | FIBF_DELETE;
|
if (mode & S_IRGRP)
|
if (mode & S_IRGRP)
|
prot |= FIBF_GRP_READ;
|
prot |= FIBF_GRP_READ;
|
if (mode & S_IWGRP)
|
if (mode & S_IWGRP)
|
prot |= FIBF_GRP_WRITE;
|
prot |= FIBF_GRP_WRITE;
|
if (mode & S_IROTH)
|
if (mode & S_IROTH)
|
prot |= FIBF_OTR_READ;
|
prot |= FIBF_OTR_READ;
|
if (mode & S_IWOTH)
|
if (mode & S_IWOTH)
|
prot |= FIBF_OTR_WRITE;
|
prot |= FIBF_OTR_WRITE;
|
|
|
return prot;
|
return prot;
|
}
|
}
|
|
|