mirror of
https://github.com/deater/dos33fsprogs.git
synced 2024-12-26 11:30:12 +00:00
492 lines
15 KiB
C
492 lines
15 KiB
C
|
/*****************************************************************************
|
||
|
* file.c
|
||
|
* File and inode operations for regular files.
|
||
|
*
|
||
|
* Apple II DOS 3.3 Filesystem Driver for Linux 2.4.x
|
||
|
* Copyright (c) 2001 Vince Weaver
|
||
|
* Copyright (c) 2001 Matt Jensen.
|
||
|
* This program is free software distributed under the terms of the GPL.
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/*= Kernel Includes =========================================================*/
|
||
|
|
||
|
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/locks.h>
|
||
|
|
||
|
/*= DOS 3.3 Includes =======================================================*/
|
||
|
|
||
|
#include "dos33.h"
|
||
|
|
||
|
/*= Forward Declarations ====================================================*/
|
||
|
|
||
|
/* For dos33_address_space_operations. */
|
||
|
int dos33_readpage(struct file *,struct page *);
|
||
|
int dos33_writepage(struct page *);
|
||
|
int dos33_prepare_write(struct file *,struct page *,unsigned int, unsigned int);
|
||
|
int dos33_commit_write(struct file*,struct page *,unsigned int,unsigned int);
|
||
|
int dos33_bmap(struct address_space *,long);
|
||
|
|
||
|
/*= VFS Interface Structures ================================================*/
|
||
|
|
||
|
struct inode_operations dos33_file_inode_operations = {
|
||
|
|
||
|
};
|
||
|
|
||
|
struct file_operations dos33_file_operations = {
|
||
|
read: generic_file_read,
|
||
|
write: generic_file_write,
|
||
|
mmap: generic_file_mmap
|
||
|
};
|
||
|
|
||
|
struct address_space_operations dos33_address_space_operations = {
|
||
|
readpage: dos33_readpage,
|
||
|
writepage: dos33_writepage,
|
||
|
sync_page: block_sync_page,
|
||
|
prepare_write: dos33_prepare_write,
|
||
|
commit_write: dos33_commit_write,
|
||
|
bmap: dos33_bmap
|
||
|
};
|
||
|
|
||
|
/*= Interface Functions =====================================================*/
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* dos33_get_block()
|
||
|
* Translate a block number within an @inode into a logical disk block number.
|
||
|
*****************************************************************************/
|
||
|
int dos33_get_block(struct inode *inode,long block,struct buffer_head *bh_res,int create) {
|
||
|
|
||
|
int result = 0;
|
||
|
|
||
|
struct dos33_catalog_sector *catalog = NULL;
|
||
|
struct dos33_file_t *file_ent;
|
||
|
|
||
|
struct dos33_ts_list *tsl;
|
||
|
int i;
|
||
|
u32 catalog_block=(DOS33_INO_TRACK(inode->i_ino)<<4)+
|
||
|
DOS33_INO_SECTOR(inode->i_ino);
|
||
|
|
||
|
u32 data_block;
|
||
|
u32 odd,half_block;
|
||
|
|
||
|
u32 file_entry=DOS33_INO_ENTRY(inode->i_ino);
|
||
|
u32 tsl_block;
|
||
|
|
||
|
struct buffer_head *bh = NULL;
|
||
|
struct buffer_head *bh2 = NULL;
|
||
|
|
||
|
struct buffer_head *bh_temp=NULL;
|
||
|
|
||
|
|
||
|
|
||
|
DOS33_DEBUG("** get_block 0x%x from file 0x%x\n",(int)block,(int)inode->i_ino);
|
||
|
#if 0
|
||
|
/* Verify that the block number is valid. */
|
||
|
if ((block < 0) || (block >= inode->i_blocks)) {
|
||
|
printk("** bad block number: 0< 0x%x < 0x%x\n",(int)block,(int)inode->i_blocks);
|
||
|
result = -EIO;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (!bh) {
|
||
|
odd=catalog_block%2;
|
||
|
/* Load the block. */
|
||
|
DOS33_DEBUG("** Loading block %x\n",catalog_block);
|
||
|
bh = dos33_bread(inode->i_sb,catalog_block);
|
||
|
if (!bh) {
|
||
|
DOS33_ERROR("** dos33_bread() failed for block %d\n",catalog_block);
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
/* deal with 256byte sector size */
|
||
|
if (odd) {
|
||
|
catalog = (struct dos33_catalog_sector*)(bh->b_data+256);
|
||
|
}
|
||
|
else {
|
||
|
catalog = (struct dos33_catalog_sector*)(bh->b_data);
|
||
|
}
|
||
|
}
|
||
|
file_ent=&catalog->files[file_entry];
|
||
|
|
||
|
tsl_block= (file_ent->first_tsl.track<<4)+file_ent->first_tsl.sector;
|
||
|
|
||
|
DOS33_DEBUG("** going to try reading tsl from 0x%x\n",tsl_block);
|
||
|
|
||
|
odd=tsl_block%2;
|
||
|
/* Load the block. */
|
||
|
|
||
|
bh2 = dos33_bread(inode->i_sb,tsl_block);
|
||
|
if (!bh2) {
|
||
|
DOS33_ERROR("** dos33_bread() failed for block %d\n",tsl_block);
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
/* deal with 256byte sector size */
|
||
|
if (odd) {
|
||
|
tsl = (struct dos33_ts_list*)(bh2->b_data+256);
|
||
|
}
|
||
|
else {
|
||
|
tsl = (struct dos33_ts_list*)(bh2->b_data);
|
||
|
}
|
||
|
|
||
|
/* each tsl list only holds 122 sectors */
|
||
|
/* so if bigger, we must follow the chain to the proper tsl */
|
||
|
if ((block*2) > 122) {
|
||
|
DOS33_DEBUG("!! need tsl %i\n",(int)((block*2)/122));
|
||
|
|
||
|
for (i=0;i< ((block*2)/122);i++) {
|
||
|
tsl_block=((tsl->next.track)<<4)+tsl->next.sector;
|
||
|
if (tsl_block==0) {
|
||
|
DOS33_ERROR("dos33.o: NULL TSL BLOCK!\n");
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
DOS33_DEBUG("!! going to tsl in block 0x%x\n",tsl_block);
|
||
|
|
||
|
dos33_brelse(bh2);
|
||
|
bh2=NULL;
|
||
|
|
||
|
odd=tsl_block%2;
|
||
|
/* Load the block. */
|
||
|
|
||
|
bh2 = dos33_bread(inode->i_sb,tsl_block);
|
||
|
if (!bh2) {
|
||
|
DOS33_ERROR("dos33_bread() failed for block %d\n",tsl_block);
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
/* deal with 256byte sector size */
|
||
|
if (odd) {
|
||
|
tsl = (struct dos33_ts_list*)(bh2->b_data+256);
|
||
|
}
|
||
|
else {
|
||
|
tsl = (struct dos33_ts_list*)(bh2->b_data);
|
||
|
}
|
||
|
}
|
||
|
/* move block to the proper value */
|
||
|
block=block%61;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* OK, we know where the data is, not let's trick the VFS */
|
||
|
/* into thinking we can read 256 byte blocks */
|
||
|
|
||
|
for (half_block=0;half_block<2;half_block++) {
|
||
|
|
||
|
|
||
|
data_block=(u32)( ((tsl->data_location[(block*2)+half_block].track)<<4)+
|
||
|
tsl->data_location[(block*2)+half_block].sector);
|
||
|
DOS33_DEBUG("!!** found block: %x [%x %x] (pass %i request block %i)\n",
|
||
|
data_block,tsl->data_location[(block*2)+half_block].track,
|
||
|
tsl->data_location[(block*2)+half_block].sector,half_block,(int)block);
|
||
|
|
||
|
|
||
|
if (data_block==0) {
|
||
|
/* a hole.. but since we have to fake block size */
|
||
|
/* we fill it with zeros ourselves */
|
||
|
DOS33_DEBUG("** HOLE\n");
|
||
|
|
||
|
lock_buffer(bh_res);
|
||
|
memset(bh_res->b_data+(half_block*256),0,256);
|
||
|
bh_res->b_state |= (1 << BH_Mapped);
|
||
|
mark_buffer_uptodate(bh_res,1);
|
||
|
unlock_buffer(bh_res);
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
bh_temp = dos33_bread(inode->i_sb,data_block);
|
||
|
if (!bh_temp) {
|
||
|
DOS33_ERROR("dos33_bread() failed for block %d\n",data_block);
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
|
||
|
if (data_block%2) {
|
||
|
lock_buffer(bh_res);
|
||
|
memcpy(bh_res->b_data+(half_block*256),bh_temp->b_data+256,256);
|
||
|
bh_res->b_state |= (1 << BH_Mapped);
|
||
|
mark_buffer_uptodate(bh_res,1);
|
||
|
unlock_buffer(bh_res);
|
||
|
}
|
||
|
else {
|
||
|
lock_buffer(bh_res);
|
||
|
memcpy(bh_res->b_data+(half_block*256),bh_temp->b_data,256);
|
||
|
bh_res->b_state |= (1 << BH_Mapped);
|
||
|
mark_buffer_uptodate(bh_res,1);
|
||
|
unlock_buffer(bh_res);
|
||
|
}
|
||
|
DOS33_DEBUG("** copying 256bytes from sector 0x%x starting with %0x %0x\n",
|
||
|
data_block,
|
||
|
(char)*(bh_temp->b_data),(char)*(bh_temp->b_data+1));
|
||
|
|
||
|
}
|
||
|
}
|
||
|
out_cleanup:
|
||
|
|
||
|
if (bh) dos33_brelse(bh);
|
||
|
if (bh2) dos33_brelse(bh2);
|
||
|
if (bh_temp) dos33_brelse(bh_temp);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* prodos_get_block_write()
|
||
|
* get_block_t function for write operations. This function is ugly and
|
||
|
* probably temporary. Ultimately it should be merged with prodos_get_block()
|
||
|
* and thoroughly cleaned. What you see here is basically a quick and (very)
|
||
|
* dirty hack.
|
||
|
*****************************************************************************/
|
||
|
int dos33_get_block_write(struct inode *inode,long block,struct buffer_head *bh_res,int create) {
|
||
|
// SHORTCUT struct prodos_inode_info * const pi = PRODOS_I(inode);
|
||
|
int result = 0;
|
||
|
|
||
|
DOS33_DEBUG("get_block_write\n");
|
||
|
|
||
|
#if 0
|
||
|
/* Croak on non-regular files for now. */
|
||
|
if ((pi->i_stype < PRODOS_STYPE_SEEDLING) || (pi->i_stype > PRODOS_STYPE_TREE)) {
|
||
|
PRODOS_ERROR_0(inode->i_sb,"extended files not supported for writing");
|
||
|
result = -EPERM;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
|
||
|
/* Can't translate block numbers that are out of range. */
|
||
|
if (block >= PRODOS_MAX_FILE_SIZE >> PRODOS_BLOCK_SIZE_BITS) {
|
||
|
result = -EFBIG;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
|
||
|
/* Expand the file if necessary. */
|
||
|
if (block >= inode->i_blocks) {
|
||
|
u8 new_stype = pi->i_stype;
|
||
|
u16 new_key = pi->i_key;
|
||
|
u16 new_bused = inode->i_blocks;
|
||
|
|
||
|
/* May have to convert a seedling into a sapling. */
|
||
|
if ((block > 0) && (new_stype == PRODOS_STYPE_SEEDLING)) {
|
||
|
u16 key_ptr = 0;
|
||
|
struct buffer_head *bh = NULL;
|
||
|
|
||
|
/* Allocate the new indirect block. */
|
||
|
key_ptr = prodos_alloc_block(inode->i_sb);
|
||
|
if (!key_ptr) {
|
||
|
result = -ENOSPC;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
|
||
|
/* Load and initialize the indirect block. */
|
||
|
bh = prodos_bread(inode->i_sb,key_ptr);
|
||
|
if (!bh) {
|
||
|
result = -EIO;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
memset(bh->b_data,0,PRODOS_BLOCK_SIZE);
|
||
|
PRODOS_SET_INDIRECT_ENTRY(bh->b_data,0,new_key);
|
||
|
mark_buffer_dirty(bh);
|
||
|
prodos_brelse(bh);
|
||
|
|
||
|
new_stype = PRODOS_STYPE_SAPLING;
|
||
|
new_key = key_ptr;
|
||
|
new_bused++;
|
||
|
}
|
||
|
|
||
|
/* May have to convert a sapling into a tree. */
|
||
|
if ((block > 256) && (new_stype == PRODOS_STYPE_SAPLING)) {
|
||
|
u16 key_ptr = 0;
|
||
|
struct buffer_head *bh = NULL;
|
||
|
|
||
|
/* Allocate the new double indirect block. */
|
||
|
key_ptr = prodos_alloc_block(inode->i_sb);
|
||
|
if (!key_ptr) {
|
||
|
result = -ENOSPC;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
|
||
|
/* Load and initialize the double indirect block. */
|
||
|
bh = prodos_bread(inode->i_sb,key_ptr);
|
||
|
if (!bh) {
|
||
|
result = -EIO;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
memset(bh->b_data,0,PRODOS_BLOCK_SIZE);
|
||
|
PRODOS_SET_INDIRECT_ENTRY(bh->b_data,0,new_key);
|
||
|
mark_buffer_dirty(bh);
|
||
|
prodos_brelse(bh);
|
||
|
|
||
|
new_stype = PRODOS_STYPE_TREE;
|
||
|
new_key = key_ptr;
|
||
|
new_bused++;
|
||
|
}
|
||
|
|
||
|
/* Update the inode if storage type changed. */
|
||
|
if (new_stype != pi->i_stype) {
|
||
|
pi->i_stype = new_stype;
|
||
|
pi->i_key = new_key;
|
||
|
inode->i_blocks = new_bused;
|
||
|
mark_inode_dirty(inode);
|
||
|
}
|
||
|
|
||
|
/* May need to expand indexes for a tree file. */
|
||
|
if (new_stype == PRODOS_STYPE_TREE) {
|
||
|
u16 ind_existing = ((inode->i_blocks - 1) >> 8) + 1;
|
||
|
u16 ind_needed = block >> 8;
|
||
|
u16 ind_ptr = 0;
|
||
|
struct buffer_head *dind_bh = NULL;
|
||
|
struct buffer_head *ind_bh = NULL;
|
||
|
|
||
|
dind_bh = prodos_bread(inode->i_sb,pi->i_key);
|
||
|
if (!dind_bh) {
|
||
|
result = -EIO;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
for (;ind_existing < ind_needed;ind_existing++) {
|
||
|
ind_ptr = prodos_alloc_block(inode->i_sb);
|
||
|
if (!ind_ptr) {
|
||
|
result = -ENOSPC;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
ind_bh = prodos_bread(inode->i_sb,ind_ptr);
|
||
|
if (!ind_bh) {
|
||
|
result = -EIO;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
memset(ind_bh,0,PRODOS_BLOCK_SIZE);
|
||
|
PRODOS_SET_INDIRECT_ENTRY(dind_bh->b_data,ind_existing,ind_ptr);
|
||
|
prodos_brelse(ind_bh);
|
||
|
inode->i_blocks = ++new_bused;
|
||
|
}
|
||
|
prodos_brelse(dind_bh);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
{
|
||
|
u16 blk = pi->i_key;
|
||
|
struct buffer_head *bh = NULL;
|
||
|
/* Appropriate action depends upon the file's storage type. */
|
||
|
switch (pi->i_stype) {
|
||
|
case PRODOS_STYPE_TREE:
|
||
|
/* Load double indirect block. */
|
||
|
bh = prodos_bread(inode->i_sb,blk);
|
||
|
if (!bh) {
|
||
|
result = -EIO;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
|
||
|
/* Get indirect block and release double indirect block. */
|
||
|
blk = PRODOS_GET_INDIRECT_ENTRY(bh->b_data,block >> 8);
|
||
|
prodos_brelse(bh);
|
||
|
bh = NULL;
|
||
|
/* Fall through. */
|
||
|
case PRODOS_STYPE_SAPLING:
|
||
|
/* Load indirect block. */
|
||
|
bh = prodos_bread(inode->i_sb,blk);
|
||
|
if (!bh) {
|
||
|
result = -EIO;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
|
||
|
/* Get data block and release indirect block. */
|
||
|
blk = PRODOS_GET_INDIRECT_ENTRY(bh->b_data,block & 0xff);
|
||
|
if (!blk) {
|
||
|
bh_res->b_state |= (1UL << BH_New);
|
||
|
blk = prodos_alloc_block(inode->i_sb);
|
||
|
if (!blk) {
|
||
|
result = -ENOSPC;
|
||
|
goto out_cleanup;
|
||
|
}
|
||
|
PRODOS_SET_INDIRECT_ENTRY(bh->b_data,block & 0xff,blk);
|
||
|
inode->i_blocks++;
|
||
|
}
|
||
|
prodos_brelse(bh);
|
||
|
bh = NULL;
|
||
|
|
||
|
/* Fall through. */
|
||
|
case PRODOS_STYPE_SEEDLING:
|
||
|
/* Set bh_res fields to cause the block to be read from disk. */
|
||
|
bh_res->b_blocknr = PRODOS_SB(inode->i_sb)->s_part_start + blk;
|
||
|
bh_res->b_dev = inode->i_dev;
|
||
|
bh_res->b_state |= (1 << BH_Mapped);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mark_inode_dirty(inode);
|
||
|
|
||
|
out_cleanup:
|
||
|
#endif
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* dos33_readpage()
|
||
|
*****************************************************************************/
|
||
|
int dos33_readpage(struct file *filp,struct page *page) {
|
||
|
int result = 0;
|
||
|
|
||
|
DOS33_DEBUG("readpage\n");
|
||
|
|
||
|
/* Let the generic "read page" function do most of the work. */
|
||
|
result = block_read_full_page(page,dos33_get_block);
|
||
|
|
||
|
/* Propagate block_read_full_page() return value. */
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* prodos_writepage()
|
||
|
*****************************************************************************/
|
||
|
int dos33_writepage(struct page *page) {
|
||
|
DOS33_DEBUG("writepage\n");
|
||
|
|
||
|
/* Generic function does the work with the help of a get_block function. */
|
||
|
// PRODOS_ERROR_0(page->mapping->host->i_sb,"called");
|
||
|
// return block_write_full_page(page,prodos_get_block_write);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* prodos_prepare_write()
|
||
|
*****************************************************************************/
|
||
|
int dos33_prepare_write(struct file *file,struct page *page,unsigned int from,unsigned int to) {
|
||
|
DOS33_DEBUG("prepare_write\n");
|
||
|
|
||
|
//// return block_prepare_write(page,from,to,prodos_get_block_write);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* prodos_commit_write()
|
||
|
*****************************************************************************/
|
||
|
int dos33_commit_write(struct file *file,struct page *page,unsigned int from,unsigned int to) {
|
||
|
DOS33_DEBUG("commit write\n");
|
||
|
|
||
|
#if 0
|
||
|
/* Convert text files to ProDOS format. */
|
||
|
if (PRODOS_I(page->mapping->host)->i_flags & PRODOS_I_CRCONV) {
|
||
|
int i = 0;
|
||
|
char *pdata = kmap(page);
|
||
|
for (i = 0;i < PAGE_CACHE_SIZE;i++,pdata++)
|
||
|
if (*pdata == '\n') *pdata = '\r';
|
||
|
kunmap(page);
|
||
|
}
|
||
|
|
||
|
/* Let generic function do the real work. */
|
||
|
#endif
|
||
|
return generic_commit_write(file,page,from,to);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* prodos_bmap()
|
||
|
*****************************************************************************/
|
||
|
int dos33_bmap(struct address_space *mapping,long block) {
|
||
|
DOS33_DEBUG("bmap\n");
|
||
|
|
||
|
/* Generic function does the work with the help of a get_block function. */
|
||
|
// PRODOS_ERROR_0(mapping->host->i_sb,"called");
|
||
|
// return generic_block_bmap(mapping,block,prodos_get_block_write);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|