aboutsummaryrefslogtreecommitdiffstats
path: root/src/dblspace_tables.c
diff options
context:
space:
mode:
authorcc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
committercc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
commitf5450bfd35a6410528d124f534c2b1a958cafe51 (patch)
treea808b12d6ad5343fabdec7b8918df6b4d844e03f /src/dblspace_tables.c
parent5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff)
downloaddmsdosnow-dmsdos-0.9.2.2.tar.gz
dmsdos-0.9.2.2 addeddmsdos-0.9.2.2
Diffstat (limited to 'src/dblspace_tables.c')
-rw-r--r--src/dblspace_tables.c760
1 files changed, 760 insertions, 0 deletions
diff --git a/src/dblspace_tables.c b/src/dblspace_tables.c
new file mode 100644
index 0000000..492e3ff
--- /dev/null
+++ b/src/dblspace_tables.c
@@ -0,0 +1,760 @@
+/*
+dblspace_tables.c
+
+DMSDOS CVF-FAT module: [d|md|bit]fat access functions.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#include <asm/semaphore.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<time.h>
+#include<errno.h>
+#endif
+
+
+Acache mdfat[MDFATCACHESIZE];
+Acache dfat[DFATCACHESIZE];
+Acache bitfat[BITFATCACHESIZE];
+
+extern unsigned long int dmsdos_speedup;
+
+#ifdef __DMSDOS_LIB__
+
+/* we don't need locking in the library */
+void lock_mdfat(void) {}
+void unlock_mdfat(void) {}
+void lock_dfat(void) {}
+void unlock_dfat(void) {}
+void lock_bitfat(void) {}
+void unlock_bitfat(void) {}
+
+#else
+
+DECLARE_MUTEX(mdfat_sem); /* Must be initialized to green light */
+void lock_mdfat(void) {down(&mdfat_sem);}
+void unlock_mdfat(void) {up(&mdfat_sem);}
+
+DECLARE_MUTEX(dfat_sem); /* Must be initialized to green light */
+void lock_dfat(void) {down(&dfat_sem);}
+void unlock_dfat(void) {up(&dfat_sem);}
+
+DECLARE_MUTEX(bitfat_sem); /* Must be initialized to green light */
+void lock_bitfat(void) {down(&bitfat_sem);}
+void unlock_bitfat(void) {up(&bitfat_sem);}
+
+#endif /* else / __DMSDOS_LIB__ */
+
+int acache_get(struct super_block*sb, Acache*acache, int area, int never,
+ int cachesize)
+{ unsigned long min_time;
+ unsigned int min_acc;
+ int index;
+ int i;
+
+ LOG_ACACHE("DMSDOS: acache_get area=%d never=%d\n",area,never);
+
+ min_time=acache[0].a_time;
+ min_acc=acache[0].a_acc;
+ index=0;
+ if(never==0)
+ { min_time=acache[1].a_time;
+ min_acc=acache[1].a_acc;
+ index=1;
+ }
+ /* find area and dev in cache */
+ for(i=0;i<cachesize;++i)
+ { if( ( acache[i].a_time<min_time||
+ (acache[i].a_time==min_time&&acache[i].a_acc<min_acc)
+ ) &&never!=i)
+ { min_time=acache[i].a_time;
+ min_acc=acache[i].a_acc;
+ index=i;
+ }
+ if(acache[i].a_buffer!=NULL&&area==acache[i].a_area&&sb->s_dev==acache[i].a_sb->s_dev)
+ { /* found */
+ if(acache[i].a_time==CURRENT_TIME)++acache[i].a_acc;
+ else
+ { acache[i].a_time=CURRENT_TIME;
+ acache[i].a_acc=0;
+ }
+ index=i;
+ return index;
+ }
+ }
+ /* index = least recently used entry number */
+ if(acache[index].a_buffer!=NULL)
+ raw_brelse(acache[index].a_sb,acache[index].a_buffer);
+ LOG_ACACHE("DMSDOS: acache_get: reading area %d\n",area);
+ if((acache[index].a_buffer=raw_bread(sb,area))==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read acache area=%d\n",area);
+ return -EIO;
+ }
+ acache[index].a_area=area;
+ acache[index].a_time=CURRENT_TIME;
+ acache[index].a_acc=0;
+ acache[index].a_sb=sb;
+ return index;
+}
+
+void u_dumpcache(Acache*c)
+{ printk(KERN_INFO "area=%d time=%ld acc=%d buffer=%p dev=0x%x\n",c->a_area,c->a_time,
+ c->a_acc,c->a_buffer,
+ /* check validity of sb before dereferencing to s_dev :) */
+ (c->a_buffer!=NULL&&c->a_sb!=NULL)?c->a_sb->s_dev:0);
+}
+
+void dumpcache(void)
+{ int i;
+
+ printk(KERN_INFO "DMSDOS: mdfat cache:\n");
+ for(i=0;i<MDFATCACHESIZE;++i)u_dumpcache(&(mdfat[i]));
+ printk(KERN_INFO "DMSDOS: dfat cache:\n");
+ for(i=0;i<DFATCACHESIZE;++i)u_dumpcache(&(dfat[i]));
+ printk(KERN_INFO "DMSDOS: bitfat cache:\n");
+ for(i=0;i<BITFATCACHESIZE;++i)u_dumpcache(&(bitfat[i]));
+}
+
+#ifndef __DMSDOS_LIB__
+void get_memory_usage_acache(int*size,int*max)
+{ int i;
+ int used=0;
+
+ for(i=0;i<MDFATCACHESIZE;++i)if(mdfat[i].a_buffer)++used;
+ for(i=0;i<DFATCACHESIZE;++i)if(dfat[i].a_buffer)++used;
+ for(i=0;i<BITFATCACHESIZE;++i)if(bitfat[i].a_buffer)++used;
+
+ if(size)*size=used*SECTOR_SIZE;
+ if(max)*max=(MDFATCACHESIZE+DFATCACHESIZE+BITFATCACHESIZE)*SECTOR_SIZE;
+}
+
+void u_free_idle_cache(Acache*c)
+{ if(c->a_buffer!=NULL&&c->a_time-CURRENT_TIME>MAX_CACHE_TIME)
+ { raw_brelse(c->a_sb,c->a_buffer);
+ c->a_buffer=NULL;
+ c->a_time=0;
+ c->a_acc=0;
+ }
+}
+
+void free_idle_cache(void)
+{ int i;
+
+ lock_mdfat();
+ for(i=0;i<MDFATCACHESIZE;++i)u_free_idle_cache(&(mdfat[i]));
+ unlock_mdfat();
+ lock_dfat();
+ for(i=0;i<DFATCACHESIZE;++i)u_free_idle_cache(&(dfat[i]));
+ unlock_dfat();
+ lock_bitfat();
+ for(i=0;i<BITFATCACHESIZE;++i)u_free_idle_cache(&(bitfat[i]));
+ unlock_bitfat();
+
+ /* handle cluster cache */
+ free_idle_ccache();
+}
+#endif
+
+int dbl_mdfat_value(struct super_block* sb,int clusternr,
+ Mdfat_entry*new,
+ Mdfat_entry*mde)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+#ifdef DMSDOS_CONFIG_STAC
+ int i;
+ int merk_i2;
+ int nr_of_bytes;
+#endif
+#ifdef DMSDOS_CONFIG_DBL
+ unsigned char * pp;
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ int res;
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ unsigned char mdfat_raw_field[5];
+#endif
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr<2||clusternr>dblsb->s_max_cluster2)
+ { printk(KERN_ERR "DMSDOS: illegal mdfat access (cluster=%d max_cluster2=%d)\n",
+ clusternr,dblsb->s_max_cluster2);
+ goto fake_mde;
+ }
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_STAC
+ case STAC3:
+ case STAC4:
+ if(dblsb->s_16bitfat)pos=clusternr*4;
+ else pos=clusternr*3;
+ area=pos/SECTOR_SIZE;
+ offset=pos%SECTOR_SIZE;
+ area=(area/6)*9+(area%6)+3+dblsb->s_fatstart; /* yes!!! */
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ nr_of_bytes=3;
+ if(dblsb->s_16bitfat)nr_of_bytes=4;
+
+ /* read 2nd sector if necessary */
+ if(offset+nr_of_bytes-1>511)
+ { merk_i2=acache_get(sb,mdfat,area+1,merk_i,MDFATCACHESIZE);
+ if(merk_i2<0){++area;goto mdfat_error;}
+ }
+ else merk_i2=merk_i;
+
+ /* copy data in mdfat_raw_field (nr_of_bytes byte) */
+ mdfat_raw_field[3]=0; /* in case only 3 bytes to read */
+ for(i=0;i<nr_of_bytes;++i)
+ { if(i+offset<512)
+ mdfat_raw_field[i]=mdfat[merk_i].a_buffer->b_data[i+offset];
+ else
+ mdfat_raw_field[i]=mdfat[merk_i2].a_buffer->b_data[i+offset-512];
+ }
+
+ /* setup mde */
+ mde->sector_minus_1=CHS(mdfat_raw_field)+((mdfat_raw_field[3]&0x3f)<<16)-1;
+ mde->unknown=(mdfat_raw_field[2])&0xf0; /* same as flags here */
+ mde->size_lo_minus_1=(mdfat_raw_field[2]&0xf)+((mdfat_raw_field[3]>>2)&0x30);
+ mde->size_hi_minus_1=mde->size_lo_minus_1; /* for compatibility */
+ mde->flags=(mdfat_raw_field[2])&0xf0; /* unshifted like in sd4_c.cc */
+ /* set used and compressed bits in flags (for compatibility) */
+ /* Hi Pavel: Is there a bug here? sector seems to be sometimes 1 for
+ empty stacker mdfat slots. I think it should be 0 ? This showed up in
+ the new fs checking code as dead sectors. Hmm.... Should we tolerate
+ 0 and 1 here ? But then the stacker fs check complains later... */
+ /* Hi Frank, they are normal deleted clusters, stored for DOS undel.
+ They should be deleted automaticaly, when free space becomes low.
+ At this moment is best to ignore them in your fat simple check,
+ they are counted by stac simple check */
+ if(mde->sector_minus_1+1)mde->flags|=2; /*set 'used' flag*/
+ switch(mde->flags&0xa0)
+ { case 0x80:
+ case 0x00:
+ if(mde->size_lo_minus_1+1==dblsb->s_sectperclust)
+ mde->flags|=1;
+ break;
+ case 0x20:
+ mde->flags|=1;
+ break;
+ default:
+ /* correct length for compatibility */
+ mde->size_hi_minus_1=dblsb->s_sectperclust-1;
+ }
+
+ LOG_MDFAT("DMSDOS: dbl_mdfat_value: cluster %u\n",
+ (unsigned)clusternr);
+ LOG_MDFAT(" sector %u len %u flags 0x%X raw 0x%02X 0x%02X 0x%02X 0x%02X\n",
+ (unsigned)(mde->sector_minus_1+1),(unsigned)(mde->size_lo_minus_1+1),
+ (unsigned)mde->flags,
+ (unsigned)(mdfat_raw_field[0]),(unsigned)(mdfat_raw_field[1]),
+ (unsigned)(mdfat_raw_field[2]),(unsigned)(mdfat_raw_field[3]));
+ LOG_MDFAT(" pos %u area %u offset %u\n",
+ (unsigned)pos,(unsigned)area,(unsigned)offset);
+
+ /* if new!=NULL, setup nr_of_bytes byte from new in mdfat_raw_field */
+ if(new)
+ { mdfat_raw_field[0]=(new->sector_minus_1+1);
+ /* unknown bits ignored */
+ mdfat_raw_field[1]=(new->sector_minus_1+1)>>8;
+ mdfat_raw_field[3]=((new->sector_minus_1+1)>>16)&0x3f;
+ mdfat_raw_field[3]|=(new->size_lo_minus_1<<2)&0xc0;
+ mdfat_raw_field[2]=new->size_lo_minus_1&0x0f;
+ mdfat_raw_field[2]|=new->flags&0xf0; /* unshifted like in sd4_c.cc */
+
+ /* write back mdfat_raw_entry */
+ for(i=0;i<nr_of_bytes;++i)
+ { if(i+offset<512)
+ mdfat[merk_i].a_buffer->b_data[i+offset]=mdfat_raw_field[i];
+ else
+ mdfat[merk_i2].a_buffer->b_data[i+offset-512]=mdfat_raw_field[i];
+ }
+
+ /* mark buffer dirty (both if necessary) */
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ if(merk_i!=merk_i2)
+ raw_mark_buffer_dirty(sb,mdfat[merk_i2].a_buffer,1);
+ /* write second mdfat if it exists */
+ if(dblsb->s_2nd_fat_offset)
+ { struct buffer_head* bh;
+
+ bh=raw_getblk(sb,
+ mdfat[merk_i].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second mdfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,mdfat[merk_i].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ if(merk_i!=merk_i2)
+ { bh=raw_getblk(sb,
+ mdfat[merk_i2].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second mdfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,mdfat[merk_i2].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ give_up: ;
+ }
+
+ unlock_mdfat();
+ return 0;
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ case DBLSP:
+ case DRVSP:
+ pos=(dblsb->s_dcluster+clusternr)*4+512*dblsb->s_mdfatstart;
+ area=pos/SECTOR_SIZE;
+ offset=(pos%SECTOR_SIZE);
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ pp=&(mdfat[merk_i].a_buffer->b_data[offset]);
+ res=CHL(pp);
+ mde->sector_minus_1=res&0x1fffff;
+ mde->unknown=(res&0x00200000)>>21;
+ mde->size_lo_minus_1=(res&0x03c00000)>>22;
+ mde->size_hi_minus_1=(res&0x3c000000)>>26;
+ mde->flags=((res&0xC0000000)>>30)&3;
+ if(new)
+ { res=new->sector_minus_1;res&=0x1fffff;
+ /* unknown bit ??? don't know... set to zero */
+ res|=new->size_lo_minus_1<<22;res&=0x03ffffff;
+ res|=new->size_hi_minus_1<<26;res&=0x3fffffff;
+ res|=new->flags<<30;
+ pp[0]=res;pp[1]=res>>8;pp[2]=res>>16;pp[3]=res>>24;
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ }
+ unlock_mdfat();
+ return 0;
+#endif
+#ifdef DMSDOS_CONFIG_DRVSP3
+ case DRVSP3:
+ pos=(dblsb->s_dcluster+clusternr)*5
+ +((dblsb->s_dcluster+clusternr)/102)*2
+ +512*dblsb->s_mdfatstart;
+ area=pos/SECTOR_SIZE;
+ offset=(pos%SECTOR_SIZE);
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ pp=&(mdfat[merk_i].a_buffer->b_data[offset]);
+
+ /* setup mde */
+ mde->sector_minus_1=CHL(pp)&0xffffff;
+ mde->unknown=(CHL(pp)&0x3000000)>>24;
+ mde->size_lo_minus_1=(pp[3]>>2)&0x3f;
+ mde->size_hi_minus_1=pp[4]&0x3f;
+ mde->flags=(pp[4]>>6)&3;
+
+ /* if new!=NULL, setup 5 byte from new in pp */
+ if(new)
+ { pp[0]=new->sector_minus_1/*&0xffffff ??? */;
+ pp[1]=new->sector_minus_1>>8;
+ pp[2]=new->sector_minus_1>>16;
+ /*pp[3]=(new->sector_minus_1>>24)&3; ??? */
+ pp[3]=new->unknown&3; /* we need the fragmented bit here :) */
+ pp[3]|=new->size_lo_minus_1<<2;
+ pp[4]=new->size_hi_minus_1&0x3f;
+ pp[4]|=new->flags<<6;
+
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ }
+
+ unlock_mdfat();
+ return 0;
+#endif
+ } /* end switch(dblsb->s_cvf_version) */
+
+ printk(KERN_ERR "DMSDOS: dbl_mdfat_value: unknown version?? This is a bug.\n");
+ goto fake_mde;
+
+ mdfat_error:
+ unlock_mdfat();
+ printk(KERN_ERR "DMSDOS: unable to read mdfat area %d for cluster %d\n",area,
+ clusternr);
+
+ fake_mde:
+ mde->sector_minus_1=0;
+ mde->unknown=0;
+ mde->size_lo_minus_1=0;
+ mde->size_hi_minus_1=0;
+ mde->flags=0;
+
+ return -1;
+}
+
+int dbl_mdfat_cluster2sector(struct super_block*sb,int clusternr)
+{ Mdfat_entry mde;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr==0)return dblsb->s_rootdir;
+ if(dbl_mdfat_value(sb,clusternr,NULL,&mde)==0)
+ return mde.sector_minus_1+1;
+ return -1;
+}
+
+int dbl_fat_nextcluster(struct super_block*sb,int clusternr,int*new)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+ int res;
+ int newval;
+ int merk_i2;
+ int offset2;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr<2||clusternr>dblsb->s_max_cluster2)
+ { printk(KERN_ERR "DMSDOS: illegal dfat access (cluster=%d max_cluster2=%d)\n",
+ clusternr,dblsb->s_max_cluster2);
+ return -1;
+ }
+
+ pos= dblsb->s_16bitfat ? clusternr<<1/**2*/ : (clusternr*3)>>1/*/2*/;
+ area=pos>>SECTOR_BITS/*pos/SECTOR_SIZE*/;
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+
+ /* adapt area for stacker */
+ if(dblsb->s_cvf_version>=STAC3)area=(area/3)*9+(area%3);
+
+ area+=dblsb->s_fatstart;
+
+ lock_dfat();
+
+ merk_i=acache_get(sb,dfat,area,-1,DFATCACHESIZE);
+ if(merk_i<0)
+ {
+ dfat_error:
+ unlock_dfat();
+ printk(KERN_ERR "DMSDOS: unable to read dfat area %d for cluster %d\n",area,
+ clusternr);
+ return -1;
+ }
+ if(offset==511)
+ { /* overlap, must read following sector also */
+ merk_i2=acache_get(sb,dfat,area+1,merk_i,DFATCACHESIZE);
+ if(merk_i2<0){++area;goto dfat_error;}
+ offset2=0;
+ }
+ else
+ { /* normal */
+ merk_i2=merk_i;
+ offset2=offset+1;
+ }
+
+LOG_DFAT("DMSDOS: FAT lookup: area=%d merk_i=%d merk_i2=%d offset=%d offset2=%d\n",
+ area,merk_i,merk_i2,offset,offset2);
+LOG_DFAT("DMSDOS: FAT lookup: cluster=%d value(low=%d high=%d)\n",
+ clusternr,
+ dfat[merk_i].a_buffer->b_data[offset],
+ dfat[merk_i2].a_buffer->b_data[offset2]);
+
+ res=dfat[merk_i].a_buffer->b_data[offset];
+ res&=0xff; /* grmpf... sign problems !!!! this is brutal but it works */
+ res|=dfat[merk_i2].a_buffer->b_data[offset2]<<8;
+ res&=0xffff;
+
+ if(new)
+ { if(dblsb->s_16bitfat) newval=*new&0xFFFF;
+ else
+ { if(clusternr&1) newval=(res&0xF) | ((*new&0xFFF)<<4);
+ else newval=(res&0xF000) | (*new&0xFFF);
+ }
+ dfat[merk_i].a_buffer->b_data[offset]=newval;
+ dfat[merk_i2].a_buffer->b_data[offset2]=newval>>8;
+ raw_mark_buffer_dirty(sb,dfat[merk_i].a_buffer,1);
+ if(merk_i!=merk_i2)
+ raw_mark_buffer_dirty(sb,dfat[merk_i2].a_buffer,1);
+ /* write second fat if it exists */
+ if(dblsb->s_2nd_fat_offset)
+ { struct buffer_head* bh;
+
+ bh=raw_getblk(sb,
+ dfat[merk_i].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second dfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,dfat[merk_i].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ if(merk_i!=merk_i2)
+ { bh=raw_getblk(sb,
+ dfat[merk_i2].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second dfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,dfat[merk_i2].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ give_up: ;
+ }
+ unlock_dfat();
+ if(dblsb->s_16bitfat)return res>=0xFFF7 ? -1 : res;
+ if(clusternr&1)res>>=4; else res&=0xfff;
+ return res>=0xff7 ? -1 : res;
+}
+
+int dbl_bitfat_value(struct super_block*sb,int sectornr,int*new)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+#ifdef DMSDOS_CONFIG_DBL
+ unsigned char * pp;
+ int newval;
+#endif
+ int bitmask;
+#ifdef DMSDOS_CONFIG_STAC
+ int shiftval;
+#endif
+ int res;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(sectornr<dblsb->s_datastart)return -1;
+ if(sectornr>dblsb->s_dataend)return -1;
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_STAC3
+ case STAC3:
+ pos=((sectornr-dblsb->s_datastart)>>3);
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+ area=(pos>>SECTOR_BITS)/*pos/SECTOR_SIZE*/+dblsb->s_mdfatstart; /* misused */
+ shiftval=((sectornr-dblsb->s_datastart)&7);
+ bitmask=0x1;
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ res=(bitfat[merk_i].a_buffer->b_data[offset]>>shiftval)&bitmask;
+ if(new)
+ { if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ bitfat[merk_i].a_buffer->b_data[offset]&=~(bitmask<<shiftval);
+ bitfat[merk_i].a_buffer->b_data[offset]|=(*new&bitmask)<<shiftval;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ unlock_bitfat();
+ return res;
+#endif
+#ifdef DMSDOS_CONFIG_STAC4
+ case STAC4:
+ pos=((sectornr-dblsb->s_datastart)>>2);
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+ area=(pos>>SECTOR_BITS)/*pos/SECTOR_SIZE*/+dblsb->s_mdfatstart; /* misused */
+ shiftval=((sectornr-dblsb->s_datastart)&3)<<1/**2*/;
+ bitmask=0x3;
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ res=(bitfat[merk_i].a_buffer->b_data[offset]>>shiftval)&bitmask;
+ if(new)
+ { if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ bitfat[merk_i].a_buffer->b_data[offset]&=~(bitmask<<shiftval);
+ bitfat[merk_i].a_buffer->b_data[offset]|=(*new&bitmask)<<shiftval;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ unlock_bitfat();
+ return res;
+#endif
+#ifdef DMSDOS_CONFIG_DBL
+ case DBLSP:
+ case DRVSP:
+ case DRVSP3:
+ pos=SECTOR_SIZE+((sectornr-dblsb->s_datastart)>>4)*2;
+ area=pos>>SECTOR_BITS/*pos/SECTOR_SIZE*/;
+ offset=pos&(SECTOR_SIZE-1)/*(pos%SECTOR_SIZE)*/;
+ bitmask=0x8000>>((sectornr-dblsb->s_datastart)&15);
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ pp=&(bitfat[merk_i].a_buffer->b_data[offset]);
+ res=CHS(pp);
+ if(new)
+ { newval= (*new) ? (res|bitmask) : (res&~bitmask);
+ pp[0]=newval;pp[1]=newval>>8;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ res=(res&bitmask) ? 1 : 0;
+ if(new)
+ { /* WARNING: variable res is different from above */
+ if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ }
+ unlock_bitfat();
+ return res;
+#endif
+ }
+
+ printk(KERN_ERR "DMSDOS: dbl_bitfat_value: version not found?? cannot happen\n");
+ return -1;
+
+ bitfat_error:
+ unlock_bitfat();
+ printk(KERN_ERR "DMSDOS: unable to read bitfat area %d for sector %d\n",area,
+ sectornr);
+ return -1;
+}
+
+#ifndef __DMSDOS_LIB__
+int dblspace_fat_access(struct super_block*sb, int clusternr, int newval)
+{ int cl;
+
+ cl=dbl_fat_nextcluster(sb,clusternr,NULL);
+
+ if(newval==-1) return cl;
+
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: dblspace_fat_access: READ-ONLY filesystem\n");
+ /* This is a bad hack in order to work around a problem with the
+ FAT driver: The FAT driver assumes fat_access never fails. Thus
+ returning -EROFS results in an endless loop (i.e. system hang)
+ at least in fat_free. We return -1 here in order to simulate EOF
+ which should break any loop in the FAT driver. */
+ return /* -EROFS */ -1;
+ }
+
+ if(newval==0)delete_cache_cluster(sb,clusternr);
+ dbl_fat_nextcluster(sb,clusternr,&newval);
+ if(cl<0)return -1; /* see comment above -- just to be sure :) */
+ /* if cl _is_ -1 (EOF) this is ok. */
+ /* if it is a negative error it is replaced by EOF. */
+ return cl;
+}
+
+int dblspace_bmap(struct inode*inode, int block)
+{
+ #ifdef __FOR_KERNEL_2_3_10
+ return dblspace_smap(inode,block);
+ #else
+ printk(KERN_WARNING "DMSDOS: bmap called, unsupported!\n");
+ return -EIO;
+ #endif
+}
+
+int dblspace_smap(struct inode*inode, int block)
+{ int cluster;
+ int sect_offs;
+ Dblsb*dblsb=MSDOS_SB(inode->i_sb)->private_data;
+
+ cluster=block/dblsb->s_sectperclust;
+ sect_offs=block%dblsb->s_sectperclust;
+
+ LOG_FS("DMSDOS: smap called, block=%d cluster_offs=%d sector_offs=%d\n",
+ block,cluster,sect_offs);
+
+ if (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+ !MSDOS_I(inode)->i_start))
+ {
+ if (block >= MSDOS_SB(inode->i_sb)->dir_entries >> MSDOS_DPS_BITS)
+ { LOG_FS("DMSDOS: smap: root dir beyond end, returning 0\n");
+ return 0;
+ }
+ LOG_FS("DMSDOS: smap: root dir, returning %d\n",block+FAKED_ROOT_DIR_OFFSET);
+ return block+FAKED_ROOT_DIR_OFFSET;
+ }
+
+ if (!(cluster = get_cluster(inode,cluster)))
+ { LOG_FS("DMSDOS: smap: get_cluster returned 0\n");
+ return 0;
+ }
+ LOG_FS("DMSDOS: smap: returning vsector(cluster=%d sector=%d)\n",
+ cluster,sect_offs);
+ return ((cluster-2)*dblsb->s_sectperclust)+sect_offs+FAKED_DATA_START_OFFSET;
+}
+
+/* clusternr is absolute, not relative to inode */
+void dblspace_zero_new_cluster(struct inode*inode, int clusternr)
+{ /* we decide upon the inode whether this is a dir cluster */
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ Cluster_head*ch;
+ int i;
+
+ if(!S_ISDIR(inode->i_mode))
+ { /* we just throw the cluster away if it has an mdfat entry */
+ delete_cache_cluster(sb,clusternr);
+ }
+ else
+ { /* it may be in cache, so ... */
+ ch=ch_read(sb,clusternr,C_NO_READ|C_KEEP_LOCK); /* I hope that noread is OK there, Pavel */
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: zero_new_cluster: ch_noread failed???\n");
+ return;
+ }
+ memset(ch->c_data,0,dblsb->s_sectperclust*SECTOR_SIZE);
+ if(DIR_MAY_BE_SHORT(dblsb))ch->c_length=SECTOR_SIZE;
+ else ch->c_length=dblsb->s_sectperclust*SECTOR_SIZE;
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ i=ch_dirty_locked(ch,0,
+ DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ if(i<0&&i!=-EROFS) /* don't try until death on a read-only filesystem */
+ ch_dirty_retry_until_success(ch,0,
+ DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ ch_free(ch);
+ }
+}
+#endif