aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorcc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-12 08:39:37 +0000
committercc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-07-19 21:05:39 +0000
commit177f9cba3e9813cf82ea764799a12128f65572cf (patch)
tree553e4086a588d18f0e27d5d607a34d5b9bcd0e04 /src
parent5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff)
downloaddmsdosnow-177f9cba3e9813cf82ea764799a12128f65572cf.tar.gz
Build mcdmsdos
Diffstat (limited to 'src')
-rw-r--r--src/lib/dblspace_alloc.c711
-rw-r--r--src/lib/dblspace_chk.c559
-rw-r--r--src/lib/dblspace_compr.c711
-rw-r--r--src/lib/dblspace_dec.c671
-rw-r--r--src/lib/dblspace_interface.c1147
-rw-r--r--src/lib/dblspace_methsq.c1256
-rw-r--r--src/lib/dblspace_tables.c760
-rw-r--r--src/lib/dmsdos-config.h24
-rw-r--r--src/lib/dmsdos.h716
-rw-r--r--src/lib/dstacker_alloc.c477
-rw-r--r--src/lib/dstacker_compr.c1235
-rw-r--r--src/lib/dstacker_dec.c824
-rw-r--r--src/lib/lib_interface.c726
-rw-r--r--src/lib/lib_interface.h204
-rw-r--r--src/utils/Makefile22
-rw-r--r--src/utils/mcdmsdos.c409
16 files changed, 10452 insertions, 0 deletions
diff --git a/src/lib/dblspace_alloc.c b/src/lib/dblspace_alloc.c
new file mode 100644
index 0000000..b7bf433
--- /dev/null
+++ b/src/lib/dblspace_alloc.c
@@ -0,0 +1,711 @@
+/*
+dblspace_alloc.c
+
+DMSDOS CVF-FAT module: memory and sector allocation 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/fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/msdos_fs.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<errno.h>
+#include<string.h>
+#endif
+
+extern unsigned long dmsdos_speedup;
+
+#define NEAR_AREA 512
+/* no change for doublespace, but fixes drivespace 3 problems (was 50) */
+#define BIG_HOLE (dblsb->s_sectperclust*3+2)
+
+/* experimental new memory allocation routines */
+#if defined(USE_XMALLOC) && !defined(__DMSDOS_LIB__)
+
+int told=0;
+
+#include<linux/mm.h>
+#include<linux/malloc.h>
+
+#ifndef MAX_KMALLOC_SIZE
+#define MAX_KMALLOC_SIZE 128*1024
+#endif
+
+void* xmalloc(unsigned long size)
+{ void* result;
+
+ if(size<=MAX_KMALLOC_SIZE)
+ { result=kmalloc(size,GFP_KERNEL);
+ if(result)
+ { /* the xfree routine recognizes kmalloc'd memory due to that it is
+ usually not page-aligned -- we double-check here to be sure */
+ if ( ( ((unsigned long)result) & (PAGE_SIZE-1) ) ==0 )
+ { /* uhhh.... the trick is broken...
+ tell the user and go safe using vmalloc */
+ kfree(result);
+ if(told++)printk(KERN_ERR "DMSDOS: page-aligned memory returned by kmalloc - please disable XMALLOC\n");
+ }
+ else return result;
+ }
+ }
+ result=vmalloc(size);
+ /* again check alignment to be 100% sure */
+ if ( ((unsigned long)result) & (PAGE_SIZE-1) )
+ panic("DMSDOS: vmalloc returned unaligned memory - please disable XMALLOC\n");
+ return result;
+}
+
+void xfree(void * data)
+{ unsigned long address=(unsigned long)data;
+
+ /* we rely on the fact that kmalloc'ed memory is unaligned but
+ vmalloc'ed memory is page-aligned */
+ /* we are causing SERIOUS kernel problems if the wrong routine is called -
+ therefore xmalloc tests kmalloc's return address above */
+ if(address&(PAGE_SIZE-1))kfree(data);
+ else vfree(data);
+}
+#endif
+
+#ifdef __DMSDOS_LIB__
+/* we don't need locking in the library */
+void lock_mdfat_alloc(Dblsb*dblsb) {}
+void unlock_mdfat_alloc(Dblsb*dblsb) {}
+#endif
+
+#ifdef __KERNEL__
+void lock_mdfat_alloc(Dblsb*dblsb)
+{ struct semaphore*sem;
+
+ sem=dblsb->mdfat_alloc_semp;
+ down(sem);
+}
+void unlock_mdfat_alloc(Dblsb*dblsb)
+{ struct semaphore*sem;
+
+ sem=dblsb->mdfat_alloc_semp;
+ up(sem);
+}
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+void u_free_cluster_sectors(struct super_block*sb, int clusternr,
+ unsigned long* undo_list)
+{
+ Mdfat_entry mde,dummy,newmde;
+ int newval=0;
+ int i;
+ int sectors;
+ int sectornr;
+ int undo_pnt=0;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_ALLOC("DMSDOS: free_cluster_sectors: freeing cluster %d\n",clusternr);
+
+ /* read mdfat entry and clear */
+ newmde.sector_minus_1=0;
+ newmde.size_lo_minus_1=0;
+ newmde.size_hi_minus_1=0;
+ newmde.flags=0;
+ dbl_mdfat_value(sb,clusternr,NULL,&mde);
+ dbl_mdfat_value(sb,clusternr,&newmde,&dummy);
+ sectors=mde.size_lo_minus_1+1;
+ sectornr=mde.sector_minus_1+1;
+ if(mde.flags&2)
+ { if(mde.unknown&2)
+ { /* free fragmented cluster */
+ struct buffer_head*bh;
+ int fragcount;
+ int fragpnt;
+ int sec;
+ int cnt;
+
+ bh=raw_bread(sb,sectornr);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: fragmentation list unreadable in cluster %d\n",
+ clusternr);
+ goto nfree;
+ }
+ fragcount=bh->b_data[0];
+ if(fragcount<=0||fragcount>dblsb->s_sectperclust||
+ bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: error in fragmentation list in cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ goto nfree;
+ }
+ for(fragpnt=1;fragpnt<=fragcount;++fragpnt)
+ { cnt=bh->b_data[fragpnt*4+3];
+ cnt&=0xff;
+ cnt/=4;
+ cnt+=1;
+ sec=bh->b_data[fragpnt*4];
+ sec&=0xff;
+ sec+=bh->b_data[fragpnt*4+1]<<8;
+ sec&=0xffff;
+ sec+=bh->b_data[fragpnt*4+2]<<16;
+ sec&=0xffffff;
+ sec+=1;
+
+ if(fragpnt==1)
+ { if(sec!=sectornr||cnt!=sectors)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: first fragment wrong in cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ goto nfree;
+ }
+ }
+
+ for(i=0;i<cnt;++i)
+ { dbl_bitfat_value(sb,sec+i,&newval);
+ if(undo_list)undo_list[undo_pnt++]=sec+i;
+ }
+ }
+ }
+ else
+ { /* free sectors in bitfat */
+ nfree:
+ for(i=0;i<sectors;++i)
+ { dbl_bitfat_value(sb,sectornr+i,&newval);
+ if(undo_list)undo_list[undo_pnt++]=sectornr+i;
+ }
+ }
+
+ dblsb->s_full=0;
+ }
+ else
+ { LOG_CLUST("DMSDOS: stale MDFAT entry for cluster %d, zeroing.\n",
+ clusternr);
+ }
+
+ if(undo_list)undo_list[undo_pnt]=0;
+}
+
+void free_cluster_sectors(struct super_block*sb, int clusternr)
+{ lock_mdfat_alloc(MSDOS_SB(sb)->private_data);
+ u_free_cluster_sectors(sb,clusternr,NULL);
+ unlock_mdfat_alloc(MSDOS_SB(sb)->private_data);
+}
+#endif
+
+/* for statistics - just for interest */
+int nearfound=0;
+int bigfound=0;
+int exactfound=0;
+int anyfound=0;
+int notfound=0;
+int fragfound=0;
+
+/* this function must be called locked */
+int find_free_bitfat(struct super_block*sb, int sectornr, int size)
+{ int testsek;
+ int i;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(dblsb->s_free_sectors<0||dblsb->s_free_sectors>0x2000000)
+ { printk(KERN_NOTICE "DMSDOS: find_free_bitfat: free sectors=%d, cannot believe this. Counting...\n",
+ dblsb->s_free_sectors);
+ check_free_sectors(sb);
+ printk(KERN_NOTICE "DMSDOS: counted free sectors=%d\n",dblsb->s_free_sectors);
+ }
+
+ /* we needn't try in that case... */
+ if(dblsb->s_free_sectors<size)
+ { if(dblsb->s_full<2)printk(KERN_CRIT "DMSDOS: CVF full.\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+
+if(dmsdos_speedup&SP_FAST_BITFAT_ALLOC)
+{ /* new strategy: find any fitting hole beginning with last result */
+
+ testsek=dblsb->s_lastnear;
+ if(testsek<dblsb->s_datastart||testsek>dblsb->s_dataend-size)
+ testsek=dblsb->s_datastart;
+
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { ++nearfound;
+ dblsb->s_lastnear=testsek+size;
+ dblsb->s_full=0;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ /* not found, continue */
+ goto tryany;
+
+} /* end of new strategy */
+else
+{ /* old strategy: do a search in near environment first */
+
+ if(sectornr==0)sectornr=dblsb->s_lastnear;
+
+ if(sectornr>=dblsb->s_datastart&&sectornr<=dblsb->s_dataend-size)
+ { /* search exactly fitting hole near sectornr */
+ testsek=sectornr;
+ while(testsek<sectornr+NEAR_AREA)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++nearfound;
+ dblsb->s_lastnear=testsek;
+ return testsek;
+ }
+ testsek+=i;
+ }
+ testsek=sectornr;
+ while(testsek>sectornr-NEAR_AREA)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { --testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek-i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++nearfound;
+ dblsb->s_lastnear=testsek-i+1;
+ return testsek-i+1;
+ }
+ testsek-=i;
+ }
+ }
+ /* not found, continue */
+} /* end of old strategy */
+
+ /* search for a big hole */
+ if(dblsb->s_lastbig==-1)goto nobighole;
+
+ testsek=dblsb->s_lastbig;
+ if(testsek<dblsb->s_datastart||testsek+size>dblsb->s_dataend)
+ testsek=dblsb->s_datastart;
+
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<BIG_HOLE&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==BIG_HOLE)
+ { dblsb->s_full=0;
+ ++bigfound;
+ dblsb->s_lastbig=testsek;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ if(dblsb->s_lastbig==0)
+ dblsb->s_lastbig=-1; /*there's no big hole any more*/
+ else
+ dblsb->s_lastbig=0; /* next time try from the beginning */
+
+nobighole:
+
+if((dmsdos_speedup&SP_NO_EXACT_SEARCH)==0)
+{
+ /* search for an exactly fitting hole */
+ /* hmmm... now the search code becomes awfully slow */
+ testsek=dblsb->s_datastart;
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++exactfound;
+ return testsek;
+ }
+ testsek+=i;
+ }
+}
+
+ if(dblsb->s_full==0)
+ { printk(KERN_CRIT "DMSDOS: CVF almost full or highly fragmented at MDFAT level.\n");
+ dblsb->s_full=1;
+ }
+
+tryany:
+ /* last trial: search for any hole >= size */
+ testsek=dblsb->s_datastart;
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { ++anyfound;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ /* not found, means disk full or MDFAT too fragmented */
+ ++notfound;
+
+ if(dblsb->s_cvf_version==DRVSP3)
+ { if(dblsb->s_full==0)
+ { printk(KERN_CRIT "DMSDOS: CVF almost full or highly fragmented at MDFAT level.\n");
+ dblsb->s_full=1;
+ }
+ }
+ else /* this is for CVFs that cannot fragment cluster data */
+ { if(dblsb->s_full<2)
+ printk(KERN_CRIT "DMSDOS: CVF full or too fragmented at MDFAT level.\n");
+ dblsb->s_full=2;
+ }
+ return 0;
+}
+
+void log_found_statistics()
+{ printk(KERN_INFO "DMSDOS: free sector finding statistics:\n");
+ printk(KERN_INFO "nearfound=%d bigfound=%d exactfound=%d anyfound=%d fragfound=%d notfound=%d\n",
+ nearfound,bigfound,exactfound,anyfound,fragfound,notfound);
+}
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+int try_fragmented(struct super_block*sb,int anear,int nr,
+ unsigned char*fraglist)
+{
+ int i;
+ int sector=anear;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ int again=1;
+ int frags;
+ int cnt;
+
+ /* if you never want dmsdos to write fragmented clusters as a last resort
+ then uncomment the next return statement */
+
+ /* return -ENOSPC; */
+
+ if(dblsb->s_free_sectors<nr)
+ { if(dblsb->s_full<2)printk(KERN_CRIT "DMSDOS: CVF full.\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+
+ printk(KERN_DEBUG "DMSDOS: trying to allocate fragmented space...\n");
+ LOG_ALLOC("DMSDOS: try_fragmented: start, anear=%d nr=%d\n",anear,nr);
+
+ if(anear==0)sector=dblsb->s_lastnear;
+
+ if(sector<dblsb->s_datastart||sector>dblsb->s_dataend)
+ { sector=dblsb->s_datastart;
+ again=0;
+ }
+
+ retry:
+ frags=0;
+ fraglist[0]=0;
+ fraglist[1]=0;
+ fraglist[2]=0;
+ fraglist[3]=0;
+ cnt=nr;
+
+ while(cnt>0&&sector<=dblsb->s_dataend)
+ { if(dbl_bitfat_value(sb,sector,NULL))
+ { ++sector;
+ continue;
+ }
+ /* free sector found */
+ i=1;
+ while(dbl_bitfat_value(sb,sector+i,NULL)==0&&i<cnt)++i;
+ /* i=number of free sectors :) */
+ ++frags;
+ fraglist[frags*4]=sector-1;
+ fraglist[frags*4+1]=(sector-1)>>8;
+ fraglist[frags*4+2]=(sector-1)>>16;
+ fraglist[frags*4+3]=(sector-1)>>24;
+ fraglist[frags*4+3]|=(i-1)<<2;
+ fraglist[0]=frags;
+ sector+=i+1;
+ cnt-=i;
+ }
+ if(cnt>0&&again!=0)
+ { sector=dblsb->s_datastart;
+ again=0;
+ goto retry;
+ }
+
+ /* now evaluate the result, check for strange things */
+ if(cnt>0)
+ { if(dblsb->s_full<2)
+ printk(KERN_CRIT "DMSDOS: CVF full (cannot even allocate fragmented space)\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+ if(cnt<0)
+ { printk(KERN_ERR "DMSDOS: try_fragmented: cnt<0 ? This is a bug.\n");
+ return -EIO;
+ }
+ if(frags<2||frags>dblsb->s_sectperclust+1)
+ { printk(KERN_ERR "DMSDOS: try_fragmented: frags=%d ? Cannot happen.\n",frags);
+ return -EIO;
+ }
+
+ /* correct statistics */
+ ++fragfound;--notfound;
+ dblsb->s_lastnear=sector;
+ dblsb->s_full=1; /* uhh... 0 might be dangerous... */
+
+ /* fraglist must be written to disk in *any* case in order to
+ still represent a correct filesystem
+ this is handled by dbl_write_cluster to prevent too much overhead */
+
+ LOG_ALLOC("DMSDOS: try_fragmented: success, frags=%d\n",frags);
+ return 0;
+}
+#endif /* DMSDOS_CONFIG_DRVSP3 */
+
+#ifdef DMSDOS_CONFIG_DBL
+/* replaces an existing cluster;
+ this unusual function must be called before rewriting any file cluster;
+ *** size must be known (encoded in mde) ***
+ if fraglist!=NULL fragmented clusters are allowed for drivespace 3
+ returns first sector nr
+ changes mde and fraglist
+*/
+
+#define MAX_UNDO_LIST 70
+
+int dbl_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*mde,
+ unsigned char*fraglist)
+{ Mdfat_entry old_mde,dummy;
+ int i;
+ int newval;
+ int sector;
+ int old_sector;
+ int old_size;
+ int new_size;
+ unsigned long undo_list[MAX_UNDO_LIST];
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ lock_mdfat_alloc(dblsb);
+
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster cluster=%d near_sector=%d\n",
+ cluster,near_sector);
+ dbl_mdfat_value(sb,cluster,NULL,&old_mde);
+ old_size=old_mde.size_lo_minus_1+1;
+ old_sector=old_mde.sector_minus_1+1;
+ new_size=mde->size_lo_minus_1+1;
+ mde->unknown=0; /* ensure fragmented bit is clear */
+ if(old_mde.flags&2)
+ {
+ /* test whether same length (and not fragmented in drivespace 3) */
+ if(old_size==new_size&&
+ (dblsb->s_cvf_version!=DRVSP3||(old_mde.unknown&2)==0)
+ )
+ { LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: same length, ok\n");
+ sector=old_sector;
+ goto mdfat_update;
+ }
+ if(dblsb->s_cvf_version==DRVSP3&&(old_mde.unknown&2)!=0&&fraglist!=NULL)
+ { /*old cluster is fragmented and new *is allowed* to be fragmentd */
+ struct buffer_head*bh=raw_bread(sb,old_sector);
+ if(bh)
+ { int fragcount;
+ int cnt;
+ int sec;
+ int fragpnt;
+ int sects;
+ int m_cnt=0;
+ int m_sec=0;
+
+ fragcount=bh->b_data[0];
+ sects=0;
+ if(fragcount<2||fragcount>dblsb->s_sectperclust+1||
+ bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0
+ )
+ { raw_brelse(sb,bh);
+ goto check_failed;
+ }
+
+ for(fragpnt=1;fragpnt<=fragcount;++fragpnt)
+ { cnt=bh->b_data[fragpnt*4+3];
+ cnt&=0xff;
+ cnt/=4;
+ cnt+=1;
+ sec=bh->b_data[fragpnt*4];
+ sec&=0xff;
+ sec+=bh->b_data[fragpnt*4+1]<<8;
+ sec&=0xffff;
+ sec+=bh->b_data[fragpnt*4+2]<<16;
+ sec&=0xffffff;
+ sec+=1;
+
+ if(fragpnt==1)
+ { m_cnt=cnt;
+ m_sec=sec;
+ if(sec!=old_mde.sector_minus_1+1||cnt!=old_mde.size_lo_minus_1+1)
+ { printk(KERN_ERR "DMSDOS: dbl_replace_existing_cluster: checking old fraglist: first fragment wrong in cluster %d\n",
+ cluster);
+ raw_brelse(sb,bh);
+ goto check_failed;
+ }
+ }
+
+ sects+=cnt;
+ }
+ raw_brelse(sb,bh);
+ if(sects-1/*subtract space for fraglist*/==new_size)
+ { /* we can reuse it */
+ memcpy(fraglist,bh->b_data,4*(fragcount+1));
+ mde->unknown|=2;
+ mde->size_lo_minus_1=m_cnt-1;
+ sector=m_sec;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: same fragmented size, ok.\n");
+ goto mdfat_update;
+ }
+ check_failed: ; /*Win32 compiler wants a semicolon here */
+ /* fall through */
+ }
+ }
+ /* different length, replace mdfat entry */
+ newval=0;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: freeing old sectors...\n");
+ u_free_cluster_sectors(sb,cluster,undo_list);
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: freeing finished\n");
+ }
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: call find_free_bitfat...\n");
+ sector=find_free_bitfat(sb,near_sector,new_size);
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: find_free_bitfat returned %d\n",
+ sector);
+ if(sector<=0)
+ {
+#ifdef DMSDOS_CONFIG_DRVSP3
+ if(dblsb->s_cvf_version==DRVSP3&&fraglist!=NULL
+ &&(dmsdos_speedup&SP_NO_FRAG_WRITE)==0)
+ { i=try_fragmented(sb,near_sector,new_size+1,fraglist);/*yes one sector more*/
+ if(i==0) /* success */
+ { /* scan fraglist */
+ int frags;
+ int seccount;
+ int usector;
+ int j;
+
+ frags=fraglist[0];
+ for(i=1;i<=frags;++i)
+ { seccount=fraglist[i*4+3];
+ seccount&=0xff;
+ seccount/=4;
+ seccount+=1;
+ usector=fraglist[i*4];
+ usector&=0xff;
+ usector+=fraglist[i*4+1]<<8;
+ usector&=0xffff;
+ usector+=fraglist[i*4+2]<<16;
+ usector&=0xffffff;
+ usector+=1;
+
+ if(i==1) /* note values for mdfat */
+ { mde->size_lo_minus_1=seccount-1;
+ sector=usector;
+ }
+
+ /* allocate in bitfat */
+ newval=1;
+ for(j=0;j<seccount;++j)
+ { /* check whether sectors are really free */
+ if(dbl_bitfat_value(sb,usector+j,NULL))
+ { printk(KERN_EMERG "DMSDOS: try_fragmented returned non-free sectors!\n");
+ /* WARNING: bitfat is corrupt now */
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ dbl_bitfat_value(sb,usector+j,&newval);
+ }
+ }
+ mde->unknown|=2; /* set fragmented bit */
+ goto mdfat_update;
+ }
+ /* try_fragmented failed: fall through */
+ }
+#endif /* DMSDOS_CONFIG_DRVSP3 */
+ if(old_mde.flags&2)
+ { /* undo bitfat free */
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: undoing bitfat free...\n");
+ newval=1;
+ for(i=0;undo_list[i]!=0;++i)
+ dbl_bitfat_value(sb,undo_list[i],&newval);
+ }
+ unlock_mdfat_alloc(dblsb);
+ return -ENOSPC; /* disk full */
+ }
+ /* check whether really free (bug supposed in find_free_bitfat) */
+ for(i=0;i<new_size;++i)
+ { if(dbl_bitfat_value(sb,sector+i,NULL))
+ { printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sector %d size %d but they are not all free!\n",
+ sector,new_size);
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ }
+ newval=1;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: allocating in bitfat...\n");
+ for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sector+i,&newval);
+
+mdfat_update:
+ mde->sector_minus_1=sector-1;
+ mde->flags|=2;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: writing mdfat...\n");
+ dbl_mdfat_value(sb,cluster,mde,&dummy);
+ unlock_mdfat_alloc(dblsb);
+ return sector; /* okay */
+}
+#endif
diff --git a/src/lib/dblspace_chk.c b/src/lib/dblspace_chk.c
new file mode 100644
index 0000000..fe28d75
--- /dev/null
+++ b/src/lib/dblspace_chk.c
@@ -0,0 +1,559 @@
+/*
+dblspace_chk.c
+
+DMSDOS CVF-FAT module: bitfat check routines.
+
+******************************************************************************
+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>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#endif
+
+#define MAXMSG 20
+
+extern unsigned long loglevel;
+extern unsigned long dmsdos_speedup;
+extern int daemon_present;
+
+#ifdef DMSDOS_CONFIG_STAC
+/* reads stacker BITFAT sumary informations */
+__u8 *stac_bitfat_sumary(struct super_block*sb,
+ struct buffer_head**pbh)
+{ int pos,sector;
+ struct buffer_head *bh;
+ int bitfat2;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ bitfat2=dblsb->s_cvf_version>STAC3;
+ pos=dblsb->s_dataend-dblsb->s_datastart; /* number of data sectors */
+ pos=(pos+(bitfat2?4:8))>>(bitfat2?2:3);
+ pos=(pos+0xF)&~0xF;
+ sector=pos/SECTOR_SIZE+dblsb->s_mdfatstart; /* here it's AMAP start !!! */
+ *pbh=bh=raw_bread(sb,sector);
+ if(bh==NULL) return(NULL);
+ return(bh->b_data+pos%SECTOR_SIZE);
+};
+
+/* sets bitfat state, 0=query only, 1=clean, 2=dirty, 3=bad, 11=force clean */
+int stac_bitfat_state(struct super_block*sb,int new_state)
+{ int old_state;
+ __u8* pp;
+ struct buffer_head *bh;
+ static __u8 bitfat_up_to_date_fl[4]={0xAA,0xBB,0xAA,0xAA};
+ static __u8 bitfat_changing_fl[4]={0xAA,0xBB,0x55,0x55};
+ static __u8 bitfat_bad_fl[4]={0xAA,0xBB,0x00,0x00};
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(dblsb->s_cvf_version<STAC3) return 0;
+ if((pp=stac_bitfat_sumary(sb,&bh))==NULL)
+ { printk(KERN_ERR "DMSDOS: read BITFAT state error\n");
+ return -2;
+ };
+
+ if(!memcmp(pp,bitfat_up_to_date_fl,sizeof(bitfat_up_to_date_fl)))
+ old_state=1;
+ else if(!memcmp(pp,bitfat_changing_fl,sizeof(bitfat_changing_fl)))
+ old_state=2;
+ else old_state=3;
+
+ if(new_state&&(dblsb->s_comp!=READ_ONLY)
+ &&((old_state!=3)||(new_state&0xF0)))
+ {
+ if((new_state&0xF)==1)
+ memcpy(pp,bitfat_up_to_date_fl,sizeof(bitfat_up_to_date_fl));
+ else if((new_state&0xF)==2)
+ memcpy(pp,bitfat_changing_fl,sizeof(bitfat_changing_fl));
+ else memcpy(pp,bitfat_bad_fl,sizeof(bitfat_bad_fl));
+ raw_mark_buffer_dirty(sb,bh,1);
+ };
+
+ raw_brelse(sb,bh);
+ return old_state;
+};
+
+/* prepared for repair of BITFAT */
+int stac_simple_check(struct super_block*sb, int repair)
+{
+ unsigned char *sect_array;
+ int clust,i,j,val,sect;
+ int non_lin_alloc;
+ Stac_cwalk cw;
+ struct buffer_head*bh;
+ __u8* pp;
+ int free_sects;
+ int deleted_clusts;
+ int bitfat_dirty;
+ static __u8 inc_tab[4]={1,4,16,64};
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+#define set_bits(field,nr,val) field[nr>>2]|=val<<((nr&3)<<1)
+#define get_bits(field,nr) ((field[nr>>2]>>((nr&3)<<1))&3)
+#define inc_bits(field,nr) field[nr>>2]+=inc_tab[nr&3]
+
+ bitfat_dirty=0;
+ if(dblsb->s_comp==READ_ONLY) repair=0;
+
+ /* check bitfat mount id */
+
+ {
+
+ if((pp=stac_bitfat_sumary(sb,&bh))==NULL)
+ { printk(KERN_ERR "DMSDOS: simple_check: read BITFAT sumary error\n");
+ return -2;
+ };
+
+ if((i=stac_bitfat_state(sb,0))!=1)
+ { if(i>2)
+ { printk(KERN_WARNING "DMSDOS: simple_check: BITFAT abnormal state: ");
+ for(i=0;i<16;i++) printk(" %02X",(int)pp[i]);
+ printk("\n");
+ } else printk(KERN_NOTICE "DMSDOS: simple_check: BITFAT mounted/dirty\n");
+ if(repair)
+ { printk(KERN_INFO "DMSDOS: Updating BITFAT\n");
+ stac_bitfat_state(sb,0x12);
+ bitfat_dirty=1;
+ };
+ };
+
+ printk(KERN_INFO "DMSDOS: Sumary: info1 = %d\n",(int)CHL((pp+4)));
+ printk(KERN_INFO "DMSDOS: Sumary: info2 = %d\n",(int)(CHL((pp+8)))-(0xF<<28));
+ raw_brelse(sb,bh);
+ };
+
+ /* check mdfat */
+
+ val=dblsb->s_dataend/4 + 1;
+ sect_array=(unsigned char*)vmalloc(val);
+ if(sect_array==NULL)
+ { printk(KERN_WARNING "DMSDOS: simple_check: MDFAT+BITFAT test skipped (no memory)\n");
+ return 2;
+ }
+ for(i=0;i<val;++i)sect_array[i]=0;
+
+ deleted_clusts=0;non_lin_alloc=0;
+ for(clust=2;clust<=dblsb->s_max_cluster2;++clust)
+ { i=stac_cwalk_init(&cw,sb,clust,0);
+ if(i>0)
+ { if (cw.flags&0x40) deleted_clusts++;
+ while((sect=stac_cwalk_sector(&cw))>0)
+ {
+ if(sect>dblsb->s_dataend||sect<dblsb->s_datastart)
+ { printk(KERN_ERR "DMSDOS: MDFAT entry invalid (cluster %d, sect %d)\n",
+ clust,sect);
+ mde_sect_error:
+ stac_cwalk_done(&cw);
+ vfree(sect_array);
+ return -2;
+ }
+ val=get_bits(sect_array,sect);
+ if(val)
+ { if(dblsb->s_cvf_version==STAC3||
+ (((cw.flags&0xA0)!=0xA0)&&(cw.flen||cw.fcnt)))
+ { printk(KERN_ERR "DMSDOS: MDFAT crosslink detected (cluster %d)\n",
+ clust);
+ goto mde_sect_error;
+ };
+ if(((cw.flags&0xA0)!=0xA0)&&!non_lin_alloc)
+ { non_lin_alloc++;
+ printk(KERN_NOTICE "DMSDOS: Interesting MDFAT non-lin subalocation (cluster %d)\n",
+ clust);
+ };
+ };
+ inc_bits(sect_array,sect);
+ };
+ stac_cwalk_done(&cw);
+ }
+ else if (i<0)
+ { printk(KERN_ERR "DMSDOS: MDFAT bad allocation (cluster %d)\n",
+ clust);
+ vfree(sect_array);
+ return -2;
+ };
+ };
+
+ /* check bitfat */
+
+ j=0; /* count BITFAT mismatches */
+ free_sects=0;
+
+ for(i=dblsb->s_datastart;i<=dblsb->s_dataend;++i)
+ { if((val=get_bits(sect_array,i))==0) free_sects++;
+ if(dbl_bitfat_value(sb,i,NULL)!=val)
+ { if(repair)
+ { if(!j)
+ { /* repair of first mitchmatch in BITFAT */
+ printk(KERN_INFO "DMSDOS: Updating BITFAT.\n");
+ if(stac_bitfat_state(sb,0x12)<=0)
+ { printk(KERN_ERR "DMSDOS: simple_check: BITFAT state error\n");
+ vfree(sect_array);
+ return -3;
+ };
+ /* mark bitfat as dirty */
+ bitfat_dirty=1;
+ };
+ dbl_bitfat_value(sb,i,&val);
+ j++;
+ }
+ else
+ { printk(KERN_ERR "DMSDOS: BITFAT mismatches MDFAT (sector %d is %d and should be %d)\n",
+ i,dbl_bitfat_value(sb,i,NULL),(unsigned)get_bits(sect_array,i));
+ j++;
+ if(j==MAXMSG)
+ { vfree(sect_array);
+ printk(KERN_ERR "DMSDOS: Too many BITFAT mismatches in CVF, check aborted.\n");
+ return -3;
+ }
+ }
+ }
+ }
+ if(bitfat_dirty)
+ { printk(KERN_INFO "DMSDOS: Updating BITFAT finished\n");
+ stac_bitfat_state(sb,2);
+ };
+
+ if((dblsb->s_free_sectors!=-1)&&
+ (dblsb->s_free_sectors!=free_sects))
+ printk(KERN_INFO "DMSDOS: adapting free sectors count\n");
+
+ dblsb->s_free_sectors=free_sects;
+
+ printk(KERN_INFO "DMSDOS: Sumary: Free sectors = %d\n",free_sects);
+ printk(KERN_INFO "DMSDOS: Sumary: Deleted clusters = %d\n",deleted_clusts);
+
+ vfree(sect_array);
+ if(j!=0&&repair==0)return -3;
+ return 0;
+}
+#endif
+
+/* simple fs check routines (DON'T mount obviously damaged filesystems rw)
+*/
+int simple_check(struct super_block*sb,int repair)
+{ /* unsigned char field[512*1024]; grr... panics (stack overflow) */
+ unsigned char *field;
+ unsigned char bits[8]={1,2,4,8,16,32,64,128};
+ int i,val;
+ Mdfat_entry mde,dummy;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ int mdfat_dead_msg_count=0;
+ int maxmsg=0;
+ int errorcode=0;
+#ifdef DMSDOS_CONFIG_DBL
+ int free_sects;
+ int j;
+#endif
+
+ /* can't repair ro filesystems */
+ if(dblsb->s_comp==READ_ONLY)repair=0;
+
+#define setbit(nr) field[nr/8]|=bits[nr%8]
+#define getbit(nr) (field[nr/8]&bits[nr%8])
+
+ /* check fat */
+
+ /* get memory for field */
+ val=dblsb->s_max_cluster2/8 + 1;
+ field=(unsigned char*)vmalloc(val);
+ if(field==NULL)
+ { printk(KERN_WARNING "DMSDOS: simple_check aborted (no memory)\n");
+ return 1;
+ }
+ for(i=0;i<val;++i)field[i]=0;
+
+ for(i=2;i<=dblsb->s_max_cluster2&&maxmsg<=MAXMSG;++i)
+ { val=dbl_fat_nextcluster(sb,i,NULL);
+ dbl_mdfat_value(sb,i,NULL,&mde);
+ if(val!=0&&val!=-1)
+ {
+ if(getbit(val))
+ { printk(KERN_ERR "DMSDOS: FAT crosslink or loop in CVF detected (cluster %d), giving up.\n",
+ i);
+ ++maxmsg;
+ repair=0; /* unable to fix this - refuse to do further repair */
+ errorcode=-1;
+ break; /* we cannot continue here */
+ }
+ setbit(val);
+ }
+ if(val==0&&(mde.flags&2)!=0)
+ { if(repair==0)
+ { if(dblsb->s_cvf_version<STAC3)
+ { printk(KERN_ERR "DMSDOS: MDFAT-level dead sectors found in CVF (cluster %d)\n",
+ i);
+ ++maxmsg;
+ errorcode=-2;
+ }
+ }
+ else
+ { if(mdfat_dead_msg_count++==0) /* print message only once */
+ { if(dblsb->s_cvf_version<STAC3)
+ printk(KERN_NOTICE "DMSDOS: MDFAT-level dead sectors found, removing...\n");
+ else
+ printk(KERN_INFO "DMSDOS: Deleted clusters found, removing...\n");
+ }
+ mde.flags=0;
+ mde.size_lo_minus_1=0;
+ mde.size_hi_minus_1=0;
+ mde.sector_minus_1=(dblsb->s_cvf_version<STAC3)?0:-1;
+ dbl_mdfat_value(sb,i,&mde,&dummy);
+ }
+ }
+ }
+
+ vfree(field);
+
+ if(maxmsg>MAXMSG)
+ { printk(KERN_ERR "DMSDOS: giving up after %d errors. There may be more errors.\n",
+ maxmsg);
+ }
+ if(errorcode)
+ { printk(KERN_ERR "DMSDOS: part 1 of filesystem check failed, aborting.\n");
+ return errorcode;
+ }
+
+#ifdef DMSDOS_CONFIG_STAC
+ if(dblsb->s_cvf_version>=STAC3)
+ return stac_simple_check(sb,repair);
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+ /* check mdfat */
+
+ val=dblsb->s_dataend/8 + 1;
+ field=(unsigned char*)vmalloc(val);
+ if(field==NULL)
+ { printk(KERN_WARNING "DMSDOS: simple_check: MDFAT+BITFAT test skipped (no memory)\n");
+ return 2;
+ }
+ for(i=0;i<val;++i)field[i]=0;
+
+ for(i=2;i<=dblsb->s_max_cluster2&&maxmsg<=MAXMSG;++i)
+ { dbl_mdfat_value(sb,i,NULL,&mde);
+ if(mde.flags&2) /* 'used' bit set */
+ {
+ val=mde.sector_minus_1;
+ if(val+mde.size_lo_minus_1>=dblsb->s_dataend||
+ val+1<dblsb->s_datastart)
+ { printk(KERN_ERR "DMSDOS: MDFAT entry invalid in CVF (cluster %d)\n",
+ i);
+ ++maxmsg;
+ repair=0; /* refuse to repair */
+ errorcode=-2;
+ }
+ else
+ for(j=0;j<=mde.size_lo_minus_1;++j)
+ { ++val;
+ if(getbit(val))
+ { printk(KERN_ERR "DMSDOS: MDFAT crosslink in CVF detected (cluster %d)\n",
+ i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ setbit(val);
+ }
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+ /* check fragmented clusters */
+ if(mde.unknown&2)
+ { /* cluster is fragmented */
+ int fragcount;
+ int fragpnt;
+ int sector_minus_1;
+ int seccount_minus_1;
+ struct buffer_head*bh;
+
+ bh=raw_bread(sb,mde.sector_minus_1+1);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read fragmentation list of cluster %d.\n",
+ i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ else
+ {
+ fragcount=bh->b_data[0];
+ if(bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0
+ ||fragcount<=0||fragcount>dblsb->s_sectperclust)
+ { printk(KERN_ERR "DMSDOS: illegal fragcount in cluster %d\n",i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ raw_brelse(sb,bh);
+ }
+ else /* read list and scan all fragments */
+ { for(fragpnt=1;fragpnt<=fragcount;++fragpnt)
+ { sector_minus_1=bh->b_data[fragpnt*4+0];
+ sector_minus_1&=0xff;
+ sector_minus_1+=bh->b_data[fragpnt*4+1]<<8;
+ sector_minus_1&=0xffff;
+ sector_minus_1+=bh->b_data[fragpnt*4+2]<<16;
+ sector_minus_1&=0xffffff;
+ seccount_minus_1=bh->b_data[fragpnt*4+3];
+ seccount_minus_1&=0xff;
+ seccount_minus_1/=4;
+
+ /* test range */
+ val=sector_minus_1;
+ if(val+seccount_minus_1>=dblsb->s_dataend||
+ val+1<dblsb->s_datastart)
+ { printk(KERN_ERR "DMSDOS: MDFAT entry invalid in CVF (fragmented cluster %d fragpnt %d)\n",
+ i,fragpnt);
+ ++maxmsg;
+ repair=0; /* refuse to repair */
+ errorcode=-2;
+ break;
+ }
+ if(fragpnt==1)
+ { /* first is the sector itself */
+ if(sector_minus_1!=mde.sector_minus_1
+ ||seccount_minus_1!=mde.size_lo_minus_1)
+ { printk(KERN_ERR "DMSDOS: fraglist!=mde cluster %d sector %d!=%ld or count %d!=%d\n",
+ i,sector_minus_1+1,mde.sector_minus_1+1,
+ seccount_minus_1+1,mde.size_lo_minus_1);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ }
+ else for(j=0;j<=seccount_minus_1;++j)
+ { ++val;
+ if(getbit(val))
+ { printk(KERN_ERR "DMSDOS: MDFAT crosslink in CVF detected (cluster %d)\n",
+ i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ setbit(val);
+ }
+ }
+ raw_brelse(sb,bh);
+ }
+ }
+ } /* end check fragmented cluster */
+#endif
+
+ }
+/* Hmmm... this doesn't seem to be an error... dos tolerates this...
+ else / 'used' bit NOT set /
+ { if(mde.sector_minus_1!=0||mde.size_lo_minus_1!=0||mde.size_hi_minus_1!=0)
+ { printk(KERN_NOTICE "DMSDOS: non-zero unused(?) MDFAT entry in cluster %d\n",
+ i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ }
+*/
+/* Hmmm... unknown bits seem to contain nothing useful but are sometimes set...
+ if(mde.unknown) / treat unknown cases as error unless we know it better /
+ { printk(KERN_NOTICE "DMSDOS: unknown bits set to %d in cluster %d\n",
+ mde.unknown,i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+*/
+ }
+
+ if(maxmsg>MAXMSG)
+ { printk(KERN_ERR "DMSDOS: giving up after %d errors. There may be more errors.\n",
+ maxmsg);
+ }
+ if(errorcode)
+ { vfree(field);
+ printk(KERN_ERR "DMSDOS: part 2 of filesystem check failed, aborting.\n");
+ return errorcode;
+ }
+
+ /* check bitfat */
+
+ /* dataend-1 problem corrected above - dmsdos doesn't touch the
+ last sector now because it seems to be reserved... */
+
+ j=0; /* count BITFAT mismatches */
+ free_sects=0; /* count free sectors */
+
+ for(i=dblsb->s_datastart;i<=dblsb->s_dataend;++i)
+ { val=dbl_bitfat_value(sb,i,NULL);
+ if(val==0)++free_sects;
+ if(val!=(getbit(i)?1:0))
+ { if(repair)
+ { int newval;
+
+ if(j==0)printk(KERN_NOTICE "DMSDOS: BITFAT mismatches MDFAT, repairing...\n");
+ newval=(getbit(i)?1:0);
+ dbl_bitfat_value(sb,i,&newval);
+ ++j;
+ }
+ else
+ {
+ printk(KERN_ERR "DMSDOS: BITFAT mismatches MDFAT (sector %d)\n",
+ i);
+ ++j;
+ if(j==MAXMSG)
+ { vfree(field);
+ printk(KERN_ERR "DMSDOS: Too many BITFAT mismatches, check aborted.\n");
+ return -3;
+ }
+ }
+
+ }
+ }
+
+ vfree(field);
+ if(j!=0&&repair==0)return -3;
+ dblsb->s_free_sectors=free_sects;
+ printk(KERN_INFO "DMSDOS: free sectors=%d\n",dblsb->s_free_sectors);
+#endif
+
+ return 0;
+}
+
diff --git a/src/lib/dblspace_compr.c b/src/lib/dblspace_compr.c
new file mode 100644
index 0000000..e403d58
--- /dev/null
+++ b/src/lib/dblspace_compr.c
@@ -0,0 +1,711 @@
+/*
+dblspace_compr.c
+
+DMSDOS CVF-FAT module: [dbl|drv]space cluster write and compression routines.
+
+******************************************************************************
+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/errno.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <asm/byteorder.h>
+#endif
+
+#include "dmsdos.h"
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+extern unsigned long dmsdos_speedup;
+#endif
+
+#ifdef __DMSDOS_DAEMON__
+extern int cfaktor;
+void panic(char*);
+#include <malloc.h>
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#define MALLOC malloc
+#define FREE free
+#define SECTOR_SIZE 512
+#endif
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<errno.h>
+#endif
+
+int c_maxtrycount[12]={ 1, 2, 3, 4, 6, 8,10,14,18,22,28,40};
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers do not like inline */
+#define INLINE static
+#endif
+
+#if !defined(cpu_to_le16)
+ /* for old kernel versions - works only on i386 */
+ #define cpu_to_le16(v) (v)
+#endif
+
+
+/* we always need at least DS compression */
+
+/* for reading and writting from/to bitstream */
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* already written bits to buf */
+ __u16 *pd; /* data write pointer */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+/* initializes writting to bitstream */
+INLINE void dblb_wri(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->buf=0;
+ pbits->pb=0;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+}
+
+/* writes n<=16 bits to bitstream *pbits */
+INLINE void dblb_wrn(bits_t *pbits,int cod, int n)
+{
+ pbits->buf|=cod<<pbits->pb;
+ if((pbits->pb+=n)>=16)
+ {
+ if(pbits->pd<pbits->pe)
+ *(pbits->pd++)=cpu_to_le16((__u16)pbits->buf);
+ else if(pbits->pd==pbits->pe) pbits->pd++; /* output overflow */
+ pbits->buf>>=16;
+ pbits->pb-=16;
+ }
+}
+
+void write_byte(bits_t *pbits,int byte,int method)
+{ if(method!=JM_0_0&&method!=JM_0_1)
+ { if(byte<128)dblb_wrn(pbits,2,2);
+ else dblb_wrn(pbits,1,2);
+ }
+ else /* JM_0_0 */
+ { if(byte<128)dblb_wrn(pbits,0,1);
+ else dblb_wrn(pbits,3,2);
+ }
+ dblb_wrn(pbits,byte&0x7F,7);
+}
+
+void write_temp(bits_t *pbits,
+ unsigned char*tempstr,int len,int diff,int method)
+{ if(len==1)
+ { write_byte(pbits,tempstr[0],method);
+ return;
+ }
+ if(len==2&&(method==JM_0_0||method==JM_0_1))
+ { write_byte(pbits,tempstr[0],method);
+ write_byte(pbits,tempstr[1],method);
+ return;
+ }
+ if(method!=JM_0_0&&method!=JM_0_1)
+ { ++len; /* corrects different counting scheme */
+ if(diff<64)dblb_wrn(pbits,0,2);
+ else dblb_wrn(pbits,3,2);
+ }
+ else /* JM_0_0 */
+ { dblb_wrn(pbits,1,2);
+ dblb_wrn(pbits,(diff<64)?0:1,1);
+ }
+ if(diff<64)dblb_wrn(pbits,diff,6);
+ else
+ { if(diff<320)
+ { dblb_wrn(pbits,0,1);
+ dblb_wrn(pbits,diff-64,8);
+ }
+ else
+ { dblb_wrn(pbits,1,1);
+ dblb_wrn(pbits,diff-320,12);
+ }
+ }
+ /* okay, now encode len */
+ if(len==3)
+ { dblb_wrn(pbits,1,1);
+ return;
+ }
+ if(len<6)
+ { dblb_wrn(pbits,1<<1,2);
+ dblb_wrn(pbits,len-4,1);
+ return;
+ }
+ if(len<10)
+ { dblb_wrn(pbits,1<<2,3);
+ dblb_wrn(pbits,len-6,2);
+ return;
+ }
+ if(len<18)
+ { dblb_wrn(pbits,1<<3,4);
+ dblb_wrn(pbits,len-10,3);
+ return;
+ }
+ if(len<34)
+ { dblb_wrn(pbits,1<<4,5);
+ dblb_wrn(pbits,len-18,4);
+ return;
+ }
+ if(len<66)
+ { dblb_wrn(pbits,1<<5,6);
+ dblb_wrn(pbits,len-34,5);
+ return;
+ }
+ if(len<130)
+ { dblb_wrn(pbits,1<<6,7);
+ dblb_wrn(pbits,len-66,6);
+ return;
+ }
+ if(len<258)
+ { dblb_wrn(pbits,1<<7,8);
+ dblb_wrn(pbits,len-130,7);
+ return;
+ }
+ dblb_wrn(pbits,1<<8,9);
+ dblb_wrn(pbits,len-258,8);
+}
+
+void write_marker(bits_t *pbits,int method)
+{ if(method==JM_0_0||method==JM_0_1)dblb_wrn(pbits,13,4);
+ else /* DS_0_x */ dblb_wrn(pbits,7,3);
+ dblb_wrn(pbits,0xFFF,12);
+}
+
+typedef __u16 hash_t;
+
+/* hashing function is only 2 char because of DS min rep */
+INLINE unsigned dbl_hash(__u8 *p)
+{
+ return (((__u16)p[0]<<4)^((__u16)p[1]<<0))&0x3FF;
+};
+
+/* adds new hash and returns previous occurence */
+INLINE hash_t dbl_newhash(__u8*clusterd ,int pos,
+ hash_t* hash_tab,hash_t* hash_hist,unsigned hash_mask)
+{
+ hash_t* hash_ptr;
+ hash_t hash_cur;
+ hash_ptr=hash_tab+dbl_hash(&(clusterd[pos]));
+ hash_cur=*hash_ptr; /* find previous occurence of hash */
+ *hash_ptr=pos; /* store new occurence */
+ *(hash_hist+(pos&hash_mask))=hash_cur;
+ /* store previous in history table */
+ return(hash_cur);
+};
+
+/* compresses a doublespace/drivespace cluster
+ gets uncompressed size (number of used sectors)
+ returns compressed size (in number of used sectors) or -1 if failed
+*/
+int dbl_compress(__u8* clusterk, __u8* clusterd, int size,
+ int method,int cf)
+{
+ bits_t bits;
+ int i;
+ int pos;
+ int mark_pos; /* position of next marker */
+ hash_t *hash_tab;
+ /* [0x400] pointers to last occurrence of same hash, index hash */
+ hash_t *hash_hist;
+ /* [0x800] previous occurences of hash, index actual pointer&hash_mask */
+ unsigned hash_mask=0x7FF;/* mask for index into hash_hist */
+ int hash_cur;
+ int max_hash=0; /* GCC likes this */
+ int match;
+ int max_match;
+ int try_cn;
+ int try_count=c_maxtrycount[cf]; /* tunable parameter */
+
+ switch(method)
+ { case DS_0_0:
+ case DS_0_1:
+ case DS_0_2: /* handled together with JM_0_0 because similar */
+ case JM_0_0:
+ case JM_0_1:
+
+ /* maximal compression */
+ if(cf>8)hash_mask=0xFFF;
+
+ /* Input size in bytes */
+ size=size*SECTOR_SIZE;
+
+ /* initialize bitstream */
+ dblb_wri(&bits,clusterk,size-SECTOR_SIZE);
+
+ /* put magic number */
+ dblb_wrn(&bits,method,32);
+
+ hash_tab=MALLOC(0x400*sizeof(hash_t));
+ if(hash_tab==NULL) return -1;
+ hash_hist=MALLOC((hash_mask+1)*sizeof(hash_t));
+ if(hash_hist==NULL) {FREE(hash_tab);return -1;}
+
+ for(i=0;i<0x400;i++) hash_tab[i]=0xFFFF;
+ for(i=0;i<=hash_mask;i++) hash_hist[i]=0xFFFF;
+
+ pos=mark_pos=0;
+ while(pos<size)
+ { mark_pos+=SECTOR_SIZE;
+ while(pos<mark_pos)
+ {
+ if(bits.pd>bits.pe) goto error; /* incompressible data */
+
+ if(pos+1>=size) goto single_char; /* cannot be hashed */
+
+ hash_cur=dbl_newhash(clusterd,pos,hash_tab,hash_hist,hash_mask);
+ if(hash_cur>=pos) goto single_char;
+
+ try_cn=try_count;
+ max_match=1; /* minimal match - 1 */
+ do{ /* longer offsets are not allowed */
+ if(pos-hash_cur>=0x113F) break;
+ /* speedup heuristic : new tested hash occurence must be
+ at least one char longer */
+ if((clusterd[hash_cur+max_match]==clusterd[pos+max_match])&&
+ (clusterd[hash_cur+max_match-1]==clusterd[pos+max_match-1])&&
+ (clusterd[hash_cur]==clusterd[pos]))
+ /* second chars are equal from hash function */
+ {
+ for(match=0;match<mark_pos-pos;match++)
+ if(clusterd[hash_cur+match]!=clusterd[pos+match])break;
+ if(match>max_match) /* find maximal hash */
+ { max_hash=hash_cur;max_match=match;
+ if(match==mark_pos-pos) break;
+ };
+ };
+ i=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[i&hash_mask])<i));
+ if(max_match<2) goto single_char;
+
+ write_temp(&bits,&(clusterd[pos]),max_match,pos-max_hash,method);
+ pos++;max_match--;
+ i=max_match;if(pos+i+1>=size)i=size-pos-1;
+ max_match-=i; /* last char cannot be hashed */
+ while(i--)
+ dbl_newhash(clusterd,pos++,hash_tab,hash_hist,hash_mask);
+ pos+=max_match;
+ continue;
+
+ single_char:
+ write_byte(&bits,clusterd[pos++],method);
+
+ }
+ write_marker(&bits,method);
+ }
+
+ dblb_wrn(&bits,0,15); /* flush last bits from bits.buf */
+ if(bits.pd>bits.pe) goto error;
+ FREE(hash_tab);
+ FREE(hash_hist);
+ return (((__u8*)bits.pd-(__u8*)clusterk)-1)/512+1;
+
+ error:
+ FREE(hash_tab);
+ FREE(hash_hist);
+ return -1;
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+ case SQ_0_0:
+ size=size*SECTOR_SIZE;
+ /* sq_comp(void* pin,int lin, void* pout, int lout, int flg) */
+ i=sq_comp(clusterd,size,clusterk,size,cf);
+ if((i<=0)||(i+SECTOR_SIZE>size)) return -1;
+ return ((i-1)/SECTOR_SIZE+1);
+#endif
+
+ default:
+ /* sorry, other compression methods currently not available */
+ return -1;
+ }
+
+ return -1;
+}
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+#ifdef DMSDOS_CONFIG_DRVSP3
+int write_fragmented(struct super_block*sb,unsigned char*fraglist,
+ unsigned char*clusterk,Mdfat_entry*mde,int ksize)
+{ int i,j;
+ int frags;
+ int seccount;
+ int sector;
+ int bytecount=ksize*SECTOR_SIZE;
+ int koffset=SECTOR_SIZE;
+ struct buffer_head*bh;
+ int c;
+
+ frags=fraglist[0];
+ if((mde->flags&1)==0)koffset=4*(frags+1);
+
+ LOG_CLUST("DMSDOS: writing fragmented cluster, frags=%d\n",frags);
+
+ /* now we have all information and can write the cluster data */
+ for(i=1;i<=frags;++i)
+ { seccount=fraglist[i*4+3];
+ seccount&=0xff;
+ seccount/=4;
+ seccount+=1;
+ sector=fraglist[i*4];
+ sector&=0xff;
+ sector+=fraglist[i*4+1]<<8;
+ sector&=0xffff;
+ sector+=fraglist[i*4+2]<<16;
+ sector&=0xffffff;
+ sector+=1;
+
+ for(j=0;j<seccount;++j)
+ { bh=raw_getblk(sb,sector+j);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: write_fragmented: raw_getblk sector %d failed\n",
+ sector+j);
+ return -EIO;
+ }
+ if(i==1&&j==0)
+ { /* need to copy fraglist first */
+ memcpy(bh->b_data,fraglist,4*(frags+1));
+ if(koffset<SECTOR_SIZE)
+ { c=SECTOR_SIZE-koffset;
+ memcpy(bh->b_data,clusterk,c);
+ bytecount-=c;
+ clusterk+=c;
+ }
+ }
+ else
+ { c=SECTOR_SIZE;
+ if(c>bytecount)c=bytecount;
+ memcpy(bh->b_data,clusterk,c);
+ bytecount-=c;
+ clusterk+=c;
+ }
+
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/* write a dmsdos cluster, compress before if possible;
+ length is the number of used bytes, may be < SECTOR_SIZE*sectperclust only
+ in the last cluster of a file;
+ cluster must be allocated by allocate_cluster before if it is a new one;
+ unable to write dir clusters;
+ to avoid MDFAT level fragmentation, near_sector should be the sector no
+ of the preceeding cluster;
+ if ucflag==UC_UNCOMPR uncompressed write is forced.
+ if ucflag<0 raw write is forced with compressed size -ucflag (in bytes).
+ if ucflag==UC_TEST simulate write is done (checks for space or reserves
+ space for the cluster in the filesystem but does not actually write it).
+ if ucflag==UC_DIRECT write compressed but don't use daemon - this is to
+ guarantee that the data are on the disk when the function exits.
+
+ *********** This function is doublespace/drivespace specific ******
+*/
+
+#ifdef DMSDOS_CONFIG_DBL
+int dbl_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag)
+{ int method;
+ unsigned char* clusterk;
+ int size;
+ Mdfat_entry mde;
+ int sector;
+ int i;
+ int res;
+ int ksize;
+ struct buffer_head*bh;
+ unsigned char fraglist[66*4];
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_CLUST("DMSDOS dbl_write_cluster clusternr=%d length=%d near_sector=%d\n",
+ clusternr,length,near_sector);
+
+ /* are we deleting a cluster ? */
+ if(clusterd==NULL||length==0)
+ { free_cluster_sectors(sb,clusternr);
+ return 0;
+ }
+
+ if(ucflag==UC_TEST)
+ { if( dblsb->s_full==0 &&
+ /* well, this is estimated */
+ dblsb->s_sectperclust*CCACHESIZE+100<dblsb->s_free_sectors
+ ) return 0;
+ else return -ENOSPC;
+ }
+
+ /* guess compression method if not already known */
+ if(dblsb->s_comp==GUESS)
+ {
+ printk(KERN_INFO "DMSDOS: write_cluster: guessing compression method...\n");
+ { if(dblsb->s_cvf_version==DRVSP)
+ { dblsb->s_comp=JM_0_0;
+ /* no doubt here, there's only this one possible - so exit */
+ /* goto guess_ok; Hmm ... really...???? better let it scan */
+ }
+ if(dblsb->s_cvf_version==DRVSP3)
+ { dblsb->s_comp=SQ_0_0;
+ /* that's only a default in case the scan routine finds nothing */
+ }
+ /* DBLSP: we know nothing, it can be DS_0_2 for dos 6.0 and DS_0_0
+ for win95, let's scan it */
+ for(i=2;i<=dblsb->s_max_cluster;++i)
+ { dbl_mdfat_value(sb,i,NULL,&mde);
+ /*if((mdfat&0xC0000000)==0x80000000)*/
+ if(mde.flags==2)
+ { bh=raw_bread(sb,mde.sector_minus_1+1);
+ if(bh!=NULL)
+ {
+ res=CHL(bh->b_data);
+ raw_brelse(sb,bh);
+ if(res==DS_0_0){dblsb->s_comp=DS_0_0;goto guess_ok;}
+ if(res==DS_0_1){dblsb->s_comp=DS_0_1;goto guess_ok;}
+ if(res==DS_0_2){dblsb->s_comp=DS_0_2;goto guess_ok;}
+ if(res==JM_0_0){dblsb->s_comp=JM_0_0;goto guess_ok;}
+ if(res==JM_0_1){dblsb->s_comp=JM_0_1;goto guess_ok;}
+ if(res==SQ_0_0){dblsb->s_comp=SQ_0_0;goto guess_ok;}
+ }
+ }
+ }
+ if(dblsb->s_comp==GUESS) /* still unknown ? */
+ { printk(KERN_WARNING "DMSDOS: could not guess compression method for CVF\n");
+ dblsb->s_comp=UNCOMPRESSED;
+ }
+ }
+ guess_ok:
+ printk(KERN_INFO "DMSDOS: write_cluster: guessed 0x%08x.\n",dblsb->s_comp);
+/* we do not need this any longer...
+#ifdef GUESS_HACK
+ if(dblsb->s_comp==SQ_0_0)
+ { dblsb->s_comp=JM_0_1;
+ printk(KERN_WARNING "DMSDOS: guess_hack: guessed SQ-0-0 not supported, using JM-0-1 instead.\n");
+ }
+#endif
+*/
+ }
+
+ method=dblsb->s_comp; /* default compression method */
+
+ size=(length-1)/512+1;
+ if(size==1)method=UNCOMPRESSED; /* it will not become smaller :) */
+ if(ucflag<0||ucflag==UC_UNCOMPR)method=UNCOMPRESSED;/* no need to compress */
+
+ LOG_CLUST("DMSDOS: write_cluster: ucflag=%d, method=0x%x\n",ucflag,method);
+
+ if(method==UNCOMPRESSED) /* includes RAW writes */
+ { clusterk=clusterd;
+ if(ucflag<0)
+ { /* raw write of already compressed data (for dmsdosd/ioctl) */
+ ksize=-ucflag/SECTOR_SIZE; /* must be n*512 for doublespace */
+ mde.size_lo_minus_1=ksize-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=2;
+ }
+ else
+ { /* normal uncompressed write */
+ /*mdfat=0xC0000000|((size-1)<<26)|((size-1)<<22);*/
+ mde.size_lo_minus_1=size-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=3;
+ ksize=size;
+ }
+ }
+ else
+ { if((ucflag==UC_DIRECT)?0:try_daemon(sb,clusternr,length,method))goto wr_uc;
+ clusterk=(unsigned char*)MALLOC(size*SECTOR_SIZE);
+ if(clusterk==NULL)
+ { printk(KERN_WARNING "DMSDOS: write_cluster: no memory for compression, writing uncompressed!\n");
+ wr_uc:
+ clusterk=clusterd;
+ /*mdfat=0xC0000000|((size-1)<<26)|((size-1)<<22);*/
+ mde.size_lo_minus_1=size-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=3;
+ method=UNCOMPRESSED;
+ ksize=size;
+ }
+ else
+ { LOG_CLUST("DMSDOS: write_cluster: compressing...\n");
+ ksize=dbl_compress(clusterk,clusterd,size,method,dblsb->s_cfaktor);
+ LOG_CLUST("DMSDOS: write cluster: compressing finished\n");
+ if(ksize<0)
+ { /* compression failed */
+ FREE(clusterk);
+ clusterk=clusterd;
+ /*mdfat=0xC0000000|((size-1)<<26)|((size-1)<<22);*/
+ mde.size_lo_minus_1=size-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=3;
+ method=UNCOMPRESSED;
+ ksize=size;
+ }
+ else
+ { /*mdfat=0x80000000|((size-1)<<26)|((ksize-1)<<22);*/
+ mde.size_lo_minus_1=ksize-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=2;
+ }
+ }
+ }
+
+ LOG_CLUST("DMSDOS: write_cluster: call dbl_replace_existing_cluster\n");
+ sector=dbl_replace_existing_cluster(sb,clusternr,near_sector,&mde,fraglist);
+ LOG_CLUST("DMSDOS: write_cluster: dbl_replace_existing_cluster returned %d\n",
+ sector);
+ if(sector<0)res=-ENOSPC;
+ else
+ { res=0;
+ /* no SIMULATE write here, this is caught above */
+#ifdef DMSDOS_CONFIG_DRVSP3
+ if(mde.unknown&2) /* fragmented */
+ res=write_fragmented(sb,fraglist,clusterk,&mde,ksize);
+ else
+#endif
+ for(i=0;i<ksize;++i)
+ { bh=raw_getblk(sb,sector+i);
+ if(bh==NULL)res=-EIO;
+ else
+ {
+ memcpy(bh->b_data,&(clusterk[SECTOR_SIZE*i]),SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ }
+
+ if(method!=UNCOMPRESSED)FREE(clusterk);
+
+ return res;
+}
+#endif
+
+#define CHECK_INTERVAL 1000
+static int fsc_count=0;
+void check_free_sectors(struct super_block*sb)
+{ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ int i;
+ int c;
+
+ if(fsc_count>CHECK_INTERVAL||dblsb->s_free_sectors<0)
+ { c=0;
+ LOG_ALLOC("DMSDOS: checking free sectors...\n");
+ for(i=dblsb->s_datastart;i<=dblsb->s_dataend;++i)
+ { if(dbl_bitfat_value(sb,i,NULL)==0)++c;
+ }
+ LOG_ALLOC("DMSDOS: free sectors=%d\n",c);
+
+ if(dblsb->s_free_sectors>=0)
+ { if(dblsb->s_free_sectors!=c)
+ { printk(KERN_WARNING "DMSDOS: check_free_sectors: wrong count %d corrected to %d\n",
+ dblsb->s_free_sectors,c);
+ }
+ }
+
+ dblsb->s_free_sectors=c;
+ fsc_count=0;
+ }
+ else
+ ++fsc_count;
+}
+
+/* write a dmsdos cluster, compress before if possible;
+ length is the number of used bytes, may be < SECTOR_SIZE*sectperclust only
+ in the last cluster of a file;
+ cluster must be allocated by allocate_cluster before if it is a new one;
+ unable to write dir clusters;
+ to avoid MDFAT level fragmentation, near_sector should be the sector no
+ of the preceeding cluster;
+ if ucflag==1 uncompressed write is forced (only for umsdos --linux-.---)
+ if ucflag<0 raw write is forced with compressed size -ucflag (in bytes)
+ if ucflag==2 simulate write is done (checks for space or reserves space
+ for the cluster in the filesystem but does not actually write it)
+ length==0 or clusterd==NULL means remove the cluster
+ if ucflag==3 perform like ucflag==0 but don't use the daemon
+
+ *********** This function is a generic wrapper ******
+*/
+/* IMPORTANT: if calling ch related routines note that cluster is very
+ likely locked when write is called - it should be locked to prevent
+ modification while being written :)
+*/
+
+int dmsdos_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag)
+{ int ret;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_CLUST("DMSDOS: write_cluster clusternr=%d length=%d near_sector=%d\n",
+ clusternr,length,near_sector);
+
+ /* ensure the daemon doesn't use old data and overwrites our data again
+ but don't do this when called by the daemon itself :-/ uuhhh deadlock */
+ /* also don't do it for simulated writes - they change nothing.... */
+ if(ucflag>=0&&ucflag!=2)remove_from_daemon_list(sb,clusternr);
+
+ /* check whether using the daemon is not desired due to speedup bits */
+ if(ucflag==0&&(dmsdos_speedup&SP_USE_DAEMON)==0)ucflag=3;
+
+ check_free_sectors(sb);
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_DBL
+ case DBLSP:
+ case DRVSP:
+ case DRVSP3:
+ ret=dbl_write_cluster(sb,clusterd,length,clusternr,near_sector,
+ ucflag);
+ break;
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ case STAC3:
+ case STAC4:
+ ret=stac_write_cluster(sb,clusterd,length,clusternr,near_sector,
+ ucflag);
+ break;
+#endif
+ default:
+ printk(KERN_ERR "DMSDOS: write_cluster: illegal cvf_version flag!\n");
+ ret=-EIO;
+ }
+
+ return ret;
+}
+
+#endif /*__KERNEL__ || __DMSDOS_LIB__*/
diff --git a/src/lib/dblspace_dec.c b/src/lib/dblspace_dec.c
new file mode 100644
index 0000000..cec2d76
--- /dev/null
+++ b/src/lib/dblspace_dec.c
@@ -0,0 +1,671 @@
+/*
+dblspace_dec.c
+
+DMSDOS CVF-FAT module: [dbl|drv]space cluster read and decompression routines.
+
+******************************************************************************
+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/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/msdos_fs.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<string.h>
+#include<errno.h>
+#endif
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers may not like inline */
+#define INLINE static
+#endif
+
+/* we always need DS decompression */
+
+#if defined(__GNUC__) && defined(__i386__) && defined(USE_ASM)
+#define USE_GNU_ASM_i386
+
+/* copy block, overlaping part is replaced by repeat of previous part */
+/* pointers and counter are modified to point after block */
+#define M_MOVSB(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "movsb\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ :"memory")
+
+
+#else
+
+#ifdef __GNUC__
+/* non-gnu compilers may not like warning directive */
+#warning USE_GNU_ASM_I386 not defined, using "C" equivalent
+#endif
+
+#define M_MOVSB(D,S,C) for(;(C);(C)--) *((__u8*)(D)++)=*((__u8*)(S)++)
+
+#endif
+
+#if !defined(le16_to_cpu)
+ /* for old kernel versions - works only on i386 */
+ #define le16_to_cpu(v) (v)
+#endif
+
+/* for reading and writting from/to bitstream */
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* already readed bits from buf */
+ __u16 *pd; /* first not readed input data */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+const unsigned dblb_bmsk[]=
+ {0x0,0x1,0x3,0x7,0xF,0x1F,0x3F,0x7F,0xFF,
+ 0x1FF,0x3FF,0x7FF,0xFFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
+
+/* read next 16 bits from input */
+#define RDN_G16(bits) \
+ { \
+ (bits).buf>>=16; \
+ (bits).pb-=16; \
+ if((bits).pd<(bits).pe) \
+ { \
+ (bits).buf|=((__u32)(le16_to_cpu(*((bits).pd++))))<<16; \
+ }; \
+ }
+
+/* prepares at least 16 bits for reading */
+#define RDN_PR(bits,u) \
+ { \
+ if((bits).pb>=16) RDN_G16(bits); \
+ u=(bits).buf>>(bits).pb; \
+ }
+
+/* initializes reading from bitstream */
+INLINE void dblb_rdi(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->pb=32;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+}
+
+/* reads n<=16 bits from bitstream *pbits */
+INLINE unsigned dblb_rdn(bits_t *pbits,int n)
+{
+ unsigned u;
+ RDN_PR(*pbits,u);
+ pbits->pb+=n;
+ u&=dblb_bmsk[n];
+ return u;
+}
+
+INLINE int dblb_rdoffs(bits_t *pbits)
+{ unsigned u;
+ RDN_PR(*pbits,u);
+ switch (u&3)
+ {
+ case 0: case 2:
+ pbits->pb+=1+6; return 63&(u>>1);
+ case 1:
+ pbits->pb+=2+8; return (255&(u>>2))+64;
+ }
+ pbits->pb+=2+12; return (4095&(u>>2))+320;
+}
+
+INLINE int dblb_rdlen(bits_t *pbits)
+{ unsigned u;
+ RDN_PR(*pbits,u);
+ switch (u&15)
+ { case 1: case 3: case 5: case 7:
+ case 9: case 11: case 13: case 15:
+ pbits->pb++; return 3;
+ case 2: case 6:
+ case 10: case 14:
+ pbits->pb+=2+1; return (1&(u>>2))+4;
+ case 4: case 12:
+ pbits->pb+=3+2; return (3&(u>>3))+6;
+ case 8:
+ pbits->pb+=4+3; return (7&(u>>4))+10;
+ case 0: ;
+ }
+ switch ((u>>4)&15)
+ { case 1: case 3: case 5: case 7:
+ case 9: case 11: case 13: case 15:
+ pbits->pb+=5+4; return (15&(u>>5))+18;
+ case 2: case 6:
+ case 10: case 14:
+ pbits->pb+=6+5; return (31&(u>>6))+34;
+ case 4: case 12:
+ pbits->pb+=7+6; return (63&(u>>7))+66;
+ case 8:
+ pbits->pb+=8+7; return (127&(u>>8))+130;
+ case 0: ;
+ }
+ pbits->pb+=9;
+ if(u&256) return dblb_rdn(pbits,8)+258;
+ return -1;
+}
+
+INLINE int dblb_decrep(bits_t *pbits, __u8 **p, void *pout, __u8 *pend,
+ int repoffs, int k, int flg)
+{ int replen;
+ __u8 *r;
+
+ if(repoffs==0){LOG_DECOMP("DMSDOS: decrb: zero offset ?\n");return -2;}
+ if(repoffs==0x113f)
+ {
+ int pos=*p-(__u8*)pout;
+ LOG_DECOMP("DMSDOS: decrb: 0x113f sync found.\n");
+ if((pos%512) && !(flg&0x4000))
+ { LOG_DECOMP("DMSDOS: decrb: sync at decompressed pos %d ?\n",pos);
+ return -2;
+ }
+ return 0;
+ }
+ replen=dblb_rdlen(pbits)+k;
+
+ if(replen<=0)
+ {LOG_DECOMP("DMSDOS: decrb: illegal count ?\n");return -2;}
+ if((__u8*)pout+repoffs>*p)
+ {LOG_DECOMP("DMSDOS: decrb: of>pos ?\n");return -2;}
+ if(*p+replen>pend)
+ {LOG_DECOMP("DMSDOS: decrb: output overfill ?\n");return -2;}
+ r=*p-repoffs;
+ M_MOVSB(*p,r,replen);
+ return 0;
+}
+
+/* DS decompression */
+/* flg=0x4000 is used, when called from stacker_dec.c, because of
+ stacker does not store original cluster size and it can mean,
+ that last cluster in file can be ended by garbage */
+int ds_dec(void* pin,int lin, void* pout, int lout, int flg)
+{
+ __u8 *p, *pend;
+ unsigned u, repoffs;
+ int r;
+ bits_t bits;
+
+ dblb_rdi(&bits,pin,lin);
+ p=(__u8*)pout;pend=p+lout;
+ if((dblb_rdn(&bits,16))!=0x5344) return -1;
+
+ u=dblb_rdn(&bits,16);
+ LOG_DECOMP("DMSDOS: DS decompression version %d\n",u);
+
+ do
+ { r=0;
+ RDN_PR(bits,u);
+ switch(u&3)
+ {
+ case 0:
+ bits.pb+=2+6;
+ repoffs=(u>>2)&63;
+ r=dblb_decrep(&bits,&p,pout,pend,repoffs,-1,flg);
+ break;
+ case 1:
+ bits.pb+=2+7;
+ *(p++)=(u>>2)|128;
+ break;
+ case 2:
+ bits.pb+=2+7;
+ *(p++)=(u>>2)&127;
+ break;
+ case 3:
+ if(u&4) { bits.pb+=3+12; repoffs=((u>>3)&4095)+320; }
+ else { bits.pb+=3+8; repoffs=((u>>3)&255)+64; };
+ r=dblb_decrep(&bits,&p,pout,pend,repoffs,-1,flg);
+ break;
+ }
+ }while((r==0)&&(p<pend));
+
+ if(r<0) return r;
+
+ if(!(flg&0x4000))
+ {
+ u=dblb_rdn(&bits,3);if(u==7) u=dblb_rdn(&bits,12)+320;
+ if(u!=0x113f)
+ { LOG_DECOMP("DMSDOS: decrb: final sync not found?\n");
+ return -2;
+ }
+ }
+
+ return p-(__u8*)pout;
+}
+
+/* JM decompression */
+int jm_dec(void* pin,int lin, void* pout, int lout, int flg)
+{
+ __u8 *p, *pend;
+ unsigned u, repoffs;
+ int r;
+ bits_t bits;
+
+ dblb_rdi(&bits,pin,lin);
+ p=(__u8*)pout;pend=p+lout;
+ if((dblb_rdn(&bits,16))!=0x4D4A) return -1;
+
+ u=dblb_rdn(&bits,16);
+ LOG_DECOMP("DMSDOS: JM decompression version %d\n",u);
+
+ do
+ { r=0;
+ RDN_PR(bits,u);
+ switch(u&3)
+ {
+ case 0:
+ case 2:
+ bits.pb+=8;
+ *(p++)=(u>>1)&127;
+ break;
+ case 1:
+ bits.pb+=2;
+ repoffs=dblb_rdoffs(&bits);
+ r=dblb_decrep(&bits,&p,pout,pend,repoffs,0,flg);
+ break;
+ case 3:
+ bits.pb+=9;
+ *(p++)=((u>>2)&127)|128;
+ break;
+ }
+ }
+ while((r==0)&&(p<pend));
+
+ if(r<0) return r;
+
+ if(!(flg&0x4000))
+ {
+ u=dblb_rdn(&bits,2);if(u==1) u=dblb_rdoffs(&bits);
+ if(u!=0x113f)
+ { LOG_DECOMP("DMSDOS: decrb: final sync not found?\n");
+ return -2;
+ }
+ }
+
+ return p-(__u8*)pout;
+}
+
+
+/* decompress a compressed doublespace/drivespace cluster clusterk to clusterd
+*/
+int dbl_decompress(unsigned char*clusterd, unsigned char*clusterk,
+ Mdfat_entry*mde)
+{
+ int sekcount;
+ int r, lin, lout;
+
+ sekcount=mde->size_hi_minus_1+1;
+ lin=(mde->size_lo_minus_1+1)*SECTOR_SIZE;
+ lout=(mde->size_hi_minus_1+1)*SECTOR_SIZE;
+
+ switch(clusterk[0]+((int)clusterk[1]<<8)+
+ ((int)clusterk[2]<<16)+((int)clusterk[3]<<24))
+ {
+ case DS_0_0:
+ case DS_0_1:
+ case DS_0_2:
+ LOG_DECOMP("DMSDOS: decompressing DS-0-x\n");
+ r=ds_dec(clusterk,lin,clusterd,lout,0);
+ if(r<=0)
+ { printk(KERN_ERR "DMSDOS: error in DS-0-x compressed data.\n");
+ return -2;
+ }
+ LOG_DECOMP("DMSDOS: decompress finished.\n");
+ return 0;
+
+ case JM_0_0:
+ case JM_0_1:
+ LOG_DECOMP("DMSDOS: decompressing JM-0-x\n");
+ r=jm_dec(clusterk,lin,clusterd,lout,0);
+ if(r<=0)
+ { printk(KERN_ERR "DMSDOS: error in JM-0-x compressed data.\n");
+ return -2;
+ }
+ LOG_DECOMP("DMSDOS: decompress finished.\n");
+ return 0;
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+ case SQ_0_0:
+ LOG_DECOMP("DMSDOS: decompressing SQ-0-0\n");
+ r=sq_dec(clusterk,lin,clusterd,lout,0);
+ if(r<=0)
+ { printk(KERN_ERR "DMSDOS: SQ-0-0 decompression failed.\n");
+ return -1;
+ }
+ LOG_DECOMP("DMSDOS: decompress finished.\n");
+ return 0;
+#endif
+
+ default:
+ printk(KERN_ERR "DMSDOS: compression method not recognized.\n");
+ return -1;
+
+ } /* end switch */
+
+ return 0;
+}
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+/* read the fragments of a fragmented cluster and assemble them */
+/* warning: this is guessed from low level viewing drivespace 3 disks
+ and may be awfully wrong... we'll see... */
+int read_fragments(struct super_block*sb,Mdfat_entry*mde, unsigned char*data)
+{ struct buffer_head*bh;
+ struct buffer_head*bh2;
+ int fragcount;
+ int fragpnt;
+ int offset;
+ int sector;
+ int seccount;
+ int membytes;
+ int safety_counter;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ /* read first sector */
+ sector=mde->sector_minus_1+1;
+ bh=raw_bread(sb,sector);
+ if(bh==NULL)return -EIO;
+ fragcount=bh->b_data[0];
+ if(bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0||fragcount<=0||
+ fragcount>dblsb->s_sectperclust)
+ { printk(KERN_ERR "DMSDOS: read_fragments: cluster does not look fragmented!\n");
+ raw_brelse(sb,bh);
+ return -EIO;
+ }
+ membytes=dblsb->s_sectperclust*SECTOR_SIZE;
+ if(mde->flags&1)
+ { offset=0;
+ safety_counter=0;
+ }
+ else
+ { offset=(fragcount+1)*4;
+ /* copy the rest of the sector */
+ memcpy(data,&(bh->b_data[offset]),SECTOR_SIZE-offset);
+ data+=(SECTOR_SIZE-offset);
+ safety_counter=SECTOR_SIZE-offset;
+ }
+ ++sector;
+ seccount=mde->size_lo_minus_1;
+ fragpnt=1;
+ while(fragpnt<=fragcount)
+ { if(fragpnt>1)
+ { /* read next fragment pointers */
+ seccount=bh->b_data[fragpnt*4+3];
+ seccount&=0xff;
+ seccount/=4;
+ seccount+=1;
+ sector=bh->b_data[fragpnt*4];
+ sector&=0xff;
+ sector+=bh->b_data[fragpnt*4+1]<<8;
+ sector&=0xffff;
+ sector+=bh->b_data[fragpnt*4+2]<<16;
+ sector&=0xffffff;
+ sector+=1;
+ }
+ while(seccount)
+ { bh2=raw_bread(sb,sector);
+ if(bh2==NULL){raw_brelse(sb,bh);return -EIO;}
+ /*printk(KERN_DEBUG "DMSDOS: read_fragments: data=0x%p safety_counter=0x%x sector=%d\n",
+ data,safety_counter,sector);*/
+ if(safety_counter+SECTOR_SIZE>membytes)
+ { int maxbytes=membytes-safety_counter;
+ if(maxbytes<=0)
+ { printk(KERN_WARNING "DMSDOS: read_fragments: safety_counter exceeds membytes!\n");
+ raw_brelse(sb,bh2);
+ raw_brelse(sb,bh);
+ return -EIO;
+ }
+ printk(KERN_DEBUG "DMSDOS: read_fragments: size limit reached.\n");
+ memcpy(data,bh2->b_data,maxbytes);
+ raw_brelse(sb,bh2);
+ raw_brelse(sb,bh);
+ return membytes;
+ }
+ else memcpy(data,bh2->b_data,SECTOR_SIZE);
+ raw_brelse(sb,bh2);
+ data+=SECTOR_SIZE;
+ safety_counter+=SECTOR_SIZE;
+ ++sector;
+ --seccount;
+ }
+ ++fragpnt;
+ }
+ raw_brelse(sb,bh);
+
+ return safety_counter;
+}
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+/* read a complete file cluster and decompress it if necessary;
+ this function is unable to read cluster 0 (CVF root directory) */
+/* returns cluster length in bytes or error (<0) */
+/* this function is specific to doublespace/drivespace */
+int dbl_read_cluster(struct super_block*sb,
+ unsigned char*clusterd, int clusternr)
+{ Mdfat_entry mde;
+ unsigned char*clusterk;
+ int nr_of_sectors;
+ int i;
+ struct buffer_head*bh;
+ int membytes;
+ int sector;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_CLUST("DMSDOS: dbl_read_cluster %d\n",clusternr);
+
+ dbl_mdfat_value(sb,clusternr,NULL,&mde);
+
+ if((mde.flags&2)==0)
+ { /* hmm, cluster is unused (it's a lost or ghost cluster)
+ and contains undefined data, but it *is* readable */
+ /* oh no, it contains ZEROD data per definition...
+ this is really important */
+ if(clusterd) /*clusterd==NULL means read_ahead - don't do anything*/
+ memset(clusterd,0,dblsb->s_sectperclust*SECTOR_SIZE);
+ LOG_CLUST("DMSDOS: lost cluster %d detected\n",clusternr);
+ return 0; /* yes, has length zero */
+ }
+
+ sector=mde.sector_minus_1+1;
+ nr_of_sectors=mde.size_lo_minus_1+1;/* real sectors on disk */
+ if(nr_of_sectors>dblsb->s_sectperclust)
+ { printk(KERN_WARNING "DMSDOS: read_cluster: mdfat sectors > sectperclust, cutting\n");
+ nr_of_sectors=dblsb->s_sectperclust;
+ }
+
+ if(clusterd==NULL)
+ { /* read-ahead */
+ dblspace_reada(sb,sector,nr_of_sectors);
+ return 0;
+ }
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+ if(mde.unknown&2)
+ { /* we suppose this bit indicates a fragmented cluster */
+ /* this is *not sure* and may be awfully wrong - reports
+ whether success or not are welcome
+ */
+
+ LOG_CLUST("DMSDOS: cluster %d has unknown bit #1 set. Assuming fragmented cluster.\n",
+ clusternr);
+
+ if(mde.flags&1) /* not compressed */
+ { LOG_CLUST("DMSDOS: uncompressed fragmented cluster\n");
+ i=read_fragments(sb,&mde,clusterd);
+ if(i<0)
+ { printk(KERN_ERR "DMSDOS: read_fragments failed!\n");
+ return i;
+ }
+ }
+ else
+ { LOG_CLUST("DMSDOS: compressed fragmented cluster\n");
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+
+ clusterk=(unsigned char*)MALLOC(membytes);
+ if(clusterk==NULL)
+ { printk(KERN_ERR "DMSDOS: no memory for decompression!\n");
+ return -2;
+ }
+ /* returns length in bytes */
+ i=read_fragments(sb,&mde,clusterk);
+ if(i<0)
+ { printk(KERN_ERR "DMSDOS: read_fragments failed!\n");
+ return i;
+ }
+ /* correct wrong size_lo information (sq_dec needs it) */
+ if(i>0)mde.size_lo_minus_1=(i-1)/SECTOR_SIZE;
+ i=dbl_decompress(clusterd,clusterk,&mde);
+
+ FREE(clusterk);
+
+ if(i)
+ { printk(KERN_ERR "DMSDOS: decompression of cluster %d in CVF failed.\n",
+ clusternr);
+ return i;
+ }
+
+ }
+
+ /* the slack must be zerod out */
+ if(mde.size_hi_minus_1+1<dblsb->s_sectperclust)
+ { memset(clusterd+(mde.size_hi_minus_1+1)*SECTOR_SIZE,0,
+ (dblsb->s_sectperclust-mde.size_hi_minus_1-1)*
+ SECTOR_SIZE);
+ }
+
+ return (mde.size_hi_minus_1+1)*SECTOR_SIZE;
+
+ } /* end of read routine for fragmented cluster */
+#endif
+
+ if(mde.flags&1)
+ { /* cluster is not compressed */
+ for(i=0;i<nr_of_sectors;++i)
+ { bh=raw_bread(sb,sector+i);
+ if(bh==NULL)return -EIO;
+ memcpy(&clusterd[i*SECTOR_SIZE],bh->b_data,SECTOR_SIZE);
+ raw_brelse(sb,bh);
+ }
+ }
+ else
+ { /* cluster is compressed */
+
+ membytes=SECTOR_SIZE*nr_of_sectors;
+
+ clusterk=(unsigned char*)MALLOC(membytes);
+ if(clusterk==NULL)
+ { printk(KERN_ERR "DMSDOS: no memory for decompression!\n");
+ return -2;
+ }
+
+ for(i=0;i<nr_of_sectors;++i)
+ { bh=raw_bread(sb,sector+i);
+ if(bh==NULL)
+ { FREE(clusterk);
+ return -EIO;
+ }
+ memcpy(&clusterk[i*SECTOR_SIZE],bh->b_data,SECTOR_SIZE);
+ raw_brelse(sb,bh);
+ }
+
+ i=dbl_decompress(clusterd,clusterk,&mde);
+
+ FREE(clusterk);
+
+ if(i)
+ { printk(KERN_ERR "DMSDOS: decompression of cluster %d in CVF failed.\n",
+ clusternr);
+ return i;
+ }
+
+ }
+
+ /* the slack must be zerod out */
+ if(mde.size_hi_minus_1+1<dblsb->s_sectperclust)
+ { memset(clusterd+(mde.size_hi_minus_1+1)*SECTOR_SIZE,0,
+ (dblsb->s_sectperclust-mde.size_hi_minus_1-1)*
+ SECTOR_SIZE);
+ }
+
+ return (mde.size_hi_minus_1+1)*SECTOR_SIZE;
+}
+#endif
+
+/* read a complete file cluster and decompress it if necessary;
+ it must be able to read directories
+ this function is unable to read cluster 0 (CVF root directory) */
+/* returns cluster length in bytes or error (<0) */
+/* this function is a generic wrapper */
+int dmsdos_read_cluster(struct super_block*sb,
+ unsigned char*clusterd, int clusternr)
+{ int ret;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_CLUST("DMSDOS: read_cluster %d\n",clusternr);
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_DBL
+ case DBLSP:
+ case DRVSP:
+ case DRVSP3:
+ ret=dbl_read_cluster(sb,clusterd,clusternr);
+ break;
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ case STAC3:
+ case STAC4:
+ ret=stac_read_cluster(sb,clusterd,clusternr);
+ break;
+#endif
+ default:
+ printk(KERN_ERR "DMSDOS: read_cluster: illegal cvf version flag!\n");
+ ret=-EIO;
+ }
+
+ return ret;
+}
diff --git a/src/lib/dblspace_interface.c b/src/lib/dblspace_interface.c
new file mode 100644
index 0000000..39aade8
--- /dev/null
+++ b/src/lib/dblspace_interface.c
@@ -0,0 +1,1147 @@
+/*
+dblspace_interface.c
+
+DMSDOS CVF-FAT module: high-level interface 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/fs.h>
+# include <linux/blkdev.h>
+# include <linux/msdos_fs.h>
+# include <linux/msdos_fs_sb.h>
+# include <linux/fat_cvf.h>
+# include <linux/string.h>
+# include <linux/malloc.h>
+# include <asm/semaphore.h>
+# include <linux/module.h>
+#endif
+
+#include"dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+# include"lib_interface.h"
+# include<string.h>
+# include<malloc.h>
+# define MAJOR(x) 0
+# define MINOR(x) 0
+ extern long int blk_size[1][1];
+#endif
+
+extern Acache mdfat[];
+extern Acache dfat[];
+extern Acache bitfat[];
+
+unsigned long loglevel=DEFAULT_LOGLEVEL;
+unsigned long dmsdos_speedup=DEFAULT_SPEEDUP;
+
+/* evaluate numbers from options */
+char* read_number(char*p, unsigned long* n, int*error)
+{ *n=0;
+ *error=-1;
+ if(*p=='b'||*p=='B'||*p=='%')
+ { /*binary*/
+ ++p;
+ while(*p=='0'||*p=='1')
+ { (*n)*=2;
+ if(*p=='1')++(*n);
+ ++p;
+ *error=0;
+ }
+ }
+ else if(*p=='0'&&(*(p+1)=='x'||*(p+1)=='X'))
+ { /*hexadecimal*/
+ p+=2;
+ while((*p>='0'&&*p<='9')||(*p>='a'&&*p<='f')||(*p>='A'&&*p<='F'))
+ { (*n)*=16;
+ (*n)+=((*p<='9')?(*p):(*p)-'a'+10)&0xf;
+ ++p;
+ *error=0;
+ }
+ }
+ else if(*p=='0'||*p=='O'||*p=='o')
+ { /*octal*/
+ ++p;
+ while(*p>='0'&&*p<='8')
+ { (*n)*=8;
+ (*n)+=(*p)-'0';
+ ++p;
+ *error=0;
+ }
+ }
+ else
+ { /*decimal*/
+ while(*p>='0'&&*p<='9')
+ { (*n)*=10;
+ (*n)+=(*p)-'0';
+ ++p;
+ *error=0;
+ }
+ }
+ LOG_REST("DMSDOS: read_number: n=%lu=0x%lx error=%d\n",*n,*n,*error);
+ return p;
+}
+
+/* evaluates a single option (needn't be '\0' terminated) */
+int evaluate_option(char*option,Dblsb*dblsb,int*repair)
+{ int ret=0;
+
+ LOG_REST("DMSDOS: evaluate option: %s\n",option);
+ if(strncmp(option,"comp=",5)==0||strncmp(option,"comp:",5)==0)
+ { if(strncmp(option+5,"no",2)==0)dblsb->s_comp=UNCOMPRESSED;
+ /*else if(strncmp(option+5,"ro",2)==0)*comp=READ_ONLY;*/
+ else if(strncmp(option+5,"ds00",4)==0)dblsb->s_comp=DS_0_0;
+ else if(strncmp(option+5,"ds01",4)==0)dblsb->s_comp=DS_0_1;
+ else if(strncmp(option+5,"ds02",4)==0)dblsb->s_comp=DS_0_2;
+ else if(strncmp(option+5,"jm00",4)==0)dblsb->s_comp=JM_0_0;
+ else if(strncmp(option+5,"jm01",4)==0)dblsb->s_comp=JM_0_1;
+ else if(strncmp(option+5,"sq00",4)==0)dblsb->s_comp=SQ_0_0;
+ else if(strncmp(option+5,"sd3",3)==0)dblsb->s_comp=SD_3;
+ else if(strncmp(option+5,"sd4",3)==0)dblsb->s_comp=SD_4;
+ else if(strncmp(option+5,"guess",5)==0)dblsb->s_comp=GUESS;
+ else ret=-1;
+ }
+ else if(strncmp(option,"cf=",3)==0||strncmp(option,"cf:",3)==0)
+ { if(option[3]=='1'&&option[4]>='0'&&option[4]<='2')
+ dblsb->s_cfaktor=option[4]-'0'+9;
+ else if(option[3]>='1'&&option[3]<='9')
+ dblsb->s_cfaktor=option[3]-'0'-1;
+ else ret=-1;
+ }
+ else if(strncmp(option,"loglevel=",9)==0||strncmp(option,"loglevel:",9)==0)
+ { /* must be decimal or hexadecimal (0x preceeded) number */
+ read_number(option+9,&loglevel,&ret);
+ if(ret>=0)
+ LOG_REST("DMSDOS: evaluate_option: loglevel set to 0x%lx.\n",loglevel);
+ }
+ else if(strncmp(option,"speedup=",8)==0||strncmp(option,"speedup:",8)==0)
+ { /* must be decimal or hexadecimal (0x preceeded) number */
+ read_number(option+8,&dmsdos_speedup,&ret);
+ if(ret>=0)
+ LOG_REST("DMSDOS: evaluate_option: speedup set to 0x%lx.\n",dmsdos_speedup);
+ }
+ else if(strncmp(option,"bitfaterrs=",11)==0||strncmp(option,"bitfaterrs:",11)==0)
+ { if(strncmp(option+11,"repair",6)==0)*repair=1;
+ else if(strncmp(option+11,"ignore",6)==0)*repair=2;
+ else if(strncmp(option+11,"setro",5)==0)*repair=0;
+ else if(strncmp(option+11,"nocheck",7)==0)*repair=-1;
+ else ret=-1;
+ }
+ else
+ { printk(KERN_ERR "DMSDOS: unknown option %s, rejected\n",option);
+ ret=-1;
+ }
+ return ret;
+}
+
+int parse_dmsdos_options(char*options,Dblsb*dblsb,int*repair)
+{ if(options==NULL)return 0;
+
+ while(*options)
+ { if(evaluate_option(options,dblsb,repair)<0)return -1;
+ while(*options!='\0'&&*options!='.'&&*options!='+')++options;
+ while(*options=='.'||*options=='+')++options;
+ }
+ return 0;
+}
+
+int ilog2(int arg)
+{ /* integer log2 */
+ int i=0;
+
+ if(arg<=0)return -1;
+
+ while(arg>>=1)++i;
+ return i;
+}
+
+#ifndef __DMSDOS_LIB__
+void do_spc_init(void)
+{ /* first call of DMSDOS filesystem, initialising variables */
+ int i;
+
+ printk(KERN_NOTICE "DMSDOS CVF-FAT extension version %d.%d.%d" DMSDOS_VLT
+ " compiled " __DATE__ " " __TIME__ " with options:"
+#ifndef DBL_WRITEACCESS
+ " read-only"
+#else
+ " read-write"
+#endif
+#ifdef USE_XMALLOC
+ ", xmalloc"
+#else
+#ifdef USE_VMALLOC
+ ", vmalloc"
+#else
+ ", kmalloc"
+#endif
+#endif
+#ifdef DMSDOS_USE_READPAGE
+ ", readpage"
+#endif
+#ifdef USE_READA_LIST
+ ", reada list"
+#endif
+#ifdef INTERNAL_DAEMON
+ ", internal daemon"
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ ", doublespace/drivespace(<3)"
+#endif
+#ifdef DMSDOS_CONFIG_DRVSP3
+ ", drivespace 3"
+#endif
+#ifdef DMSDOS_CONFIG_STAC3
+ ", stacker 3"
+#endif
+#ifdef DMSDOS_CONFIG_STAC4
+ ", stacker 4"
+#endif
+ "\n",
+ DMSDOS_MAJOR,DMSDOS_MINOR,DMSDOS_ACT_REL);
+
+ /* init cluster cache */
+ ccache_init();
+
+/* no this is done by mount ... and removed by unmount
+ otherwise the module cannot be unloaded again if the
+ internal daemon is running
+ init_daemon();
+*/
+
+#ifdef USE_READA_LIST
+ init_reada_list();
+#endif
+
+ for(i=0;i<MDFATCACHESIZE;++i)
+ { mdfat[i].a_time=0;
+ mdfat[i].a_acc=0;
+ mdfat[i].a_buffer=NULL;
+ }
+ for(i=0;i<DFATCACHESIZE;++i)
+ { dfat[i].a_time=0;
+ dfat[i].a_acc=0;
+ dfat[i].a_buffer=NULL;
+ }
+ for(i=0;i<BITFATCACHESIZE;++i)
+ { bitfat[i].a_time=0;
+ bitfat[i].a_acc=0;
+ bitfat[i].a_buffer=NULL;
+ }
+}
+
+void do_spc_exit(void)
+{
+ force_exit_daemon();
+}
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+int detect_dblspace(struct super_block*sb)
+{ struct buffer_head*bh;
+
+ MOD_INC_USE_COUNT;
+ bh=raw_bread(sb,0);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read super block\n");
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+ if(strncmp(bh->b_data+3,"MSDBL6.0",8)==0
+ ||strncmp(bh->b_data+3,"MSDSP6.0",8)==0)
+ { raw_brelse(sb,bh);
+ MOD_DEC_USE_COUNT;
+ return 1;
+ }
+ raw_brelse(sb,bh);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* setup fresh dblsb structure */
+Dblsb* malloc_dblsb(void)
+{ Dblsb*dblsb;
+
+ dblsb=kmalloc(sizeof(Dblsb),GFP_KERNEL);
+ if(dblsb==NULL)return NULL;
+ dblsb->mdfat_alloc_semp=NULL;
+
+ return dblsb;
+}
+
+/* ensure all memory is released */
+void free_dblsb(Dblsb*dblsb)
+{ if(dblsb==NULL)return;
+ if(dblsb->mdfat_alloc_semp)
+ { kfree(dblsb->mdfat_alloc_semp);
+ dblsb->mdfat_alloc_semp=NULL;
+ }
+ kfree(dblsb);
+}
+
+int mount_dblspace(struct super_block*sb,char*options)
+{ struct buffer_head*bh;
+ struct buffer_head*bh2;
+ int i,mdfatb,fatb;
+ unsigned int version_flag;
+ unsigned char * pp;
+ Dblsb* dblsb;
+ int repair=0;
+ int mdrc,m_sector=0;
+
+ MOD_INC_USE_COUNT;
+ LOG_REST("DMSDOS: dblspace/drvspace module mounting...\n");
+
+ dblsb=malloc_dblsb();
+ if(dblsb==NULL)
+ { printk(KERN_ERR "DMSDOS: mount_dblspace: out of memory\n");
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ MSDOS_SB(sb)->private_data=dblsb;
+
+#ifdef __KERNEL__
+ { struct semaphore* sem;
+
+ sem=kmalloc(sizeof(struct semaphore),GFP_KERNEL);
+ if(sem==NULL)
+ { printk(KERN_ERR "DMSDOS: mount_dblspace: out of memory\n");
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ init_MUTEX(sem);
+ dblsb->mdfat_alloc_semp=sem;
+ }
+#endif
+
+ dblsb->s_comp=GUESS;
+ dblsb->s_cfaktor=DEFAULT_CF;
+
+ if(parse_dmsdos_options(options,dblsb,&repair))
+ {
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ dblsb->s_dataend=blk_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)]*2;
+
+ bh=raw_bread(sb,0);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read super block\n");
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ if(strncmp(bh->b_data+3,"MSDBL6.0",8)&&strncmp(bh->b_data+3,"MSDSP6.0",8))
+ { printk(KERN_ERR "DMSDOS: MSDBL/MSDSP signature not found, CVF skipped\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ if(sb->s_flags & MS_RDONLY)dblsb->s_comp=READ_ONLY;
+ printk(KERN_INFO "DMSDOS: mounting CVF on device 0x%x %s...\n",
+ sb->s_dev,
+ dblsb->s_comp==READ_ONLY?"read-only":"read-write");
+
+ /* dblspace correction was relocated. Pavel */
+ dblsb->s_dataend-=1;
+
+ pp=&(bh->b_data[45]);
+ dblsb->s_dcluster=CHS(pp);
+ if(dblsb->s_dcluster&0x8000)dblsb->s_dcluster|=0xffff0000;
+ pp=&(bh->b_data[36]);
+ dblsb->s_mdfatstart=CHS(pp)+1;
+ pp=&(bh->b_data[17]);
+ dblsb->s_rootdirentries=CHS(pp);
+ dblsb->s_sectperclust=((unsigned long)(bh->b_data[13]));
+ dblsb->s_spc_bits=ilog2(dblsb->s_sectperclust);
+ pp=&(bh->b_data[39]);i=CHS(pp);/*i=res0*/
+ dblsb->s_bootblock=i;
+ pp=&(bh->b_data[14]);
+ dblsb->s_fatstart=i+CHS(pp);
+ pp=&(bh->b_data[41]);
+ dblsb->s_rootdir=i+CHS(pp);
+ pp=&(bh->b_data[43]);
+ dblsb->s_datastart=i+CHS(pp)+2;
+ dblsb->s_2nd_fat_offset=0; /* dblsp doesn't have a second fat */
+ dblsb->s_cvf_version=DBLSP;
+ version_flag=bh->b_data[51];
+ if(version_flag==2)dblsb->s_cvf_version=DRVSP;
+ if(version_flag==3||dblsb->s_sectperclust>16)dblsb->s_cvf_version=DRVSP3;
+ if(version_flag>3)printk(KERN_WARNING "DMSDOS: strange version flag %d, assuming 0.\n",
+ version_flag);
+
+#ifndef DMSDOS_CONFIG_DBLSP_DRVSP
+ if(dblsb->s_cvf_version<=DRVSP)
+ { printk(KERN_ERR "DMSDOS: support for doublespace/drivespace(<3) not compiled in.\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+#endif
+#ifndef DMSDOS_CONFIG_DRVSP3
+ if(dblsb->s_cvf_version==DRVSP3)
+ { printk(KERN_ERR "DMSDOS: support for drivespace 3 not compiled in.\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+#endif
+
+ bh2=raw_bread(sb,dblsb->s_bootblock);
+ if(bh2==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read emulated boot block\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ pp=&(bh2->b_data[57]);
+ if(CHL(pp)==0x20203631)dblsb->s_16bitfat=1;
+ else if(CHL(pp)==0x20203231)dblsb->s_16bitfat=0;
+ else if(CHL(pp)==0x20203233)
+ { printk(KERN_ERR "DMSDOS: CVF has FAT32 signature, not mounted. Please report this.\n");
+ raw_brelse(sb,bh2);
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ else
+ { pp=&(bh->b_data[62]);
+ dblsb->s_16bitfat=(CHS(pp)>32) ? 1 : 0;
+ printk(KERN_WARNING "DMSDOS: FAT bit size not recognized, guessed %d bit\n",
+ CHS(pp)>32 ? 16 : 12 );
+ }
+ raw_brelse(sb,bh2);
+
+ /* try to verify correct end of CVF */
+ mdrc=0;
+ for(i=-1;i<=1;++i)
+ { bh2=raw_bread(sb,dblsb->s_dataend+i);
+ if(bh2==NULL)
+ { LOG_REST("DMSDOS: MDR test breaks at i=%d\n",i);
+ break;
+ }
+ if(strcmp(bh2->b_data,"MDR")==0)
+ { ++mdrc;
+ m_sector=dblsb->s_dataend+i;
+ LOG_REST("DMSDOS: MDR signature found at sector %d\n",m_sector);
+ }
+ raw_brelse(sb,bh2);
+ }
+ if(mdrc!=1)
+ printk(KERN_WARNING "DMSDOS: could not find MDR signature or found more than one, mdrc=%d (ignored)\n",
+ mdrc);
+ else
+ { if(dblsb->s_dataend!=m_sector-1)
+ { LOG_REST("DMSDOS: dataend corrected due to MDR signature old=%d new=%d\n",
+ dblsb->s_dataend,m_sector-1);
+ dblsb->s_dataend=m_sector-1;
+ }
+ }
+
+ dblsb->s_full=0;
+
+ /* calculate maximum cluster nr (fixes lost cluster messages) */
+ mdfatb=(dblsb->s_bootblock-dblsb->s_mdfatstart);
+ mdfatb*=((dblsb->s_sectperclust>16)?102:128);
+ mdfatb-=dblsb->s_dcluster;
+ fatb=512*(dblsb->s_rootdir-dblsb->s_fatstart);
+ if(dblsb->s_16bitfat)fatb/=2; else fatb=(2*fatb)/3;
+ dblsb->s_max_cluster=((mdfatb<fatb)?mdfatb:fatb)-1;
+ if(dblsb->s_16bitfat)
+ { if(dblsb->s_max_cluster>0xFFF6)dblsb->s_max_cluster=0xFFF6;
+ }
+ else
+ { if(dblsb->s_max_cluster>0xFF6)dblsb->s_max_cluster=0xFF6;
+ }
+
+ /* adapt max_cluster according to dos' limits */
+ dblsb->s_max_cluster2=dblsb->s_max_cluster;
+ pp=&(bh->b_data[32]);
+ i=CHL(pp);
+ pp=&(bh->b_data[22]);
+ i-=CHS(pp);
+ pp=&(bh->b_data[14]);
+ i-=CHS(pp);
+ i-=dblsb->s_rootdirentries>>4;
+ /*i=(i>>4)+1;*/
+ i=(i/dblsb->s_sectperclust)+1;
+ if(i<=dblsb->s_max_cluster)
+ { dblsb->s_max_cluster=i;
+ }
+ else
+ { printk(KERN_WARNING "DMSDOS: dos max_cluster=%d too large, cutting to %d.\n",
+ i,dblsb->s_max_cluster);
+ }
+
+ LOG_REST("DMSDOS: dcluster=%d\n",dblsb->s_dcluster);
+ LOG_REST("DMSDOS: mdfatstart=%d\n",dblsb->s_mdfatstart);
+ LOG_REST("DMSDOS: rootdirentries=%d\n",dblsb->s_rootdirentries);
+ LOG_REST("DMSDOS: sectperclust=%d\n",dblsb->s_sectperclust);
+ LOG_REST("DMSDOS: fatstart=%d\n",dblsb->s_fatstart);
+ LOG_REST("DMSDOS: rootdir=%d\n",dblsb->s_rootdir);
+ LOG_REST("DMSDOS: %d bit FAT\n",dblsb->s_16bitfat ? 16 : 12);
+
+ dblsb->s_lastnear=0;
+ dblsb->s_lastbig=0;
+ dblsb->s_free_sectors=-1; /* -1 means unknown */
+
+ /* error test (counts sectors) */
+ if(repair!=-1) /* repair==-1 means do not even check */
+ {
+ i=simple_check(sb,repair&1);
+ if(i==-1||i==-2)
+ { printk(KERN_WARNING "DMSDOS: CVF has serious errors or compatibility problems, setting to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+ if(i==-3)
+ { if(repair&2)
+ { printk(KERN_WARNING "DMSDOS: CVF has bitfat mismatches, ignored.\n");
+ }
+ else
+ { printk(KERN_WARNING "DMSDOS: CVF has bitfat mismatches, setting to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+ }
+ }
+
+ /* if still unknown then count now */
+ if(dblsb->s_free_sectors<0)check_free_sectors(sb);
+
+ /* print doublespace version */
+ if(dblsb->s_cvf_version==DBLSP&&dblsb->s_sectperclust==16)
+ { printk(KERN_INFO "DMSDOS: CVF is in doublespace format (version 1).\n");
+ }
+ else if(dblsb->s_cvf_version==DRVSP&&dblsb->s_sectperclust==16)
+ { printk(KERN_INFO "DMSDOS: CVF is in drivespace format (version 2).\n");
+ }
+ else if(dblsb->s_cvf_version==DRVSP3&&dblsb->s_sectperclust==64)
+ { printk(KERN_INFO "DMSDOS: CVF is in drivespace 3 format.\n");
+ }
+ else
+ { printk(KERN_INFO "DMSDOS: CVF is in unknown (new?) format, please report.\n");
+ printk(KERN_INFO "DMSDOS: version_flag=%d sectperclust=%d\n",version_flag,
+ dblsb->s_sectperclust);
+ printk(KERN_NOTICE "DMSDOS: CVF set to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+
+ raw_brelse(sb,bh);
+
+ /* set some msdos fs important stuff */
+ MSDOS_SB(sb)->dir_start=FAKED_ROOT_DIR_OFFSET;
+ MSDOS_SB(sb)->dir_entries=dblsb->s_rootdirentries;
+ MSDOS_SB(sb)->data_start=FAKED_DATA_START_OFFSET; /*begin of virtual cluster 2*/
+ MSDOS_SB(sb)->clusters=dblsb->s_max_cluster;
+ if(MSDOS_SB(sb)->fat_bits!=dblsb->s_16bitfat?16:12)
+ { LOG_REST("DMSDOS: fat bit size mismatch in fat driver, trying to correct\n");
+ MSDOS_SB(sb)->fat_bits=dblsb->s_16bitfat?16:12;
+ }
+ MSDOS_SB(sb)->cluster_size=dblsb->s_sectperclust;
+ #ifdef HAS_SB_CLUSTER_BITS
+ for(MSDOS_SB(sb)->cluster_bits=0;
+ (1<<MSDOS_SB(sb)->cluster_bits)<MSDOS_SB(sb)->cluster_size;)
+ MSDOS_SB(sb)->cluster_bits++;
+ MSDOS_SB(sb)->cluster_bits+=SECTOR_BITS;
+ #endif
+
+ /* these *must* always match */
+ if(dblsb->s_comp==READ_ONLY)sb->s_flags |= MS_RDONLY;
+
+ /* we allow using the daemon - calling this more than once doesn't matter */
+ init_daemon();
+
+ return 0;
+}
+#endif
+
+int unmount_dblspace(struct super_block*sb)
+{ int j;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_REST("DMSDOS: CVF on device 0x%x unmounted.\n",sb->s_dev);
+
+ /* discard/write cached clusters */
+ free_ccache_dev(sb);
+ /* the same for the daemon if it is running */
+ clear_list_dev(sb);
+
+#ifdef DMSDOS_CONFIG_STAC
+ /* mark stacker bitfat as up to date and unmounted */
+ if(dblsb->s_cvf_version>=STAC3)
+ stac_bitfat_state(sb,1);
+#endif
+
+ /* kill buffers used by unmounted cvf */
+ for(j=0;j<MDFATCACHESIZE;++j)
+ { if(mdfat[j].a_buffer!=NULL)
+ { if(mdfat[j].a_sb->s_dev==sb->s_dev)
+ { raw_brelse(sb,mdfat[j].a_buffer);
+ mdfat[j].a_buffer=NULL;
+ }
+ mdfat[j].a_time=0;
+ mdfat[j].a_acc=0;
+ }
+ }
+ for(j=0;j<DFATCACHESIZE;++j)
+ { if(dfat[j].a_buffer!=NULL)
+ { if(dfat[j].a_sb->s_dev==sb->s_dev)
+ { raw_brelse(sb,dfat[j].a_buffer);
+ dfat[j].a_buffer=NULL;
+ }
+ dfat[j].a_time=0;
+ dfat[j].a_acc=0;
+ }
+ }
+ for(j=0;j<BITFATCACHESIZE;++j)
+ { if(bitfat[j].a_buffer!=NULL)
+ { if(bitfat[j].a_sb->s_dev==sb->s_dev)
+ { raw_brelse(sb,bitfat[j].a_buffer);
+ bitfat[j].a_buffer=NULL;
+ }
+ bitfat[j].a_time=0;
+ bitfat[j].a_acc=0;
+ }
+ }
+
+#ifdef __KERNEL__
+#ifdef USE_READA_LIST
+ /* throw away all stacked reada entries for this dev */
+ kill_reada_list_dev(sb->s_dev);
+#endif
+ /* this is unused in the library */
+ /* looks like we don't need this here... */
+ /*kfree(dblsb->mdfat_alloc_semp);*/
+ /*dblsb->mdfat_alloc_semp=NULL;*/
+#endif
+ /*kfree(MSDOS_SB(sb)->private_data);*/
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ /*MSDOS_SB(sb)->cvf_format=NULL;*/ /*this causes a segfault in
+ dec_cvf_format_use_count_by_version*/
+ exit_daemon();
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef DMSDOS_CONFIG_STAC
+int detect_stacker(struct super_block*sb)
+{ struct buffer_head*bh;
+
+ MOD_INC_USE_COUNT;
+ bh=raw_bread(sb,0);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read super block\n");
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+ if(strncmp(bh->b_data,"STACKER",7)==0)
+ { raw_brelse(sb,bh);
+ MOD_DEC_USE_COUNT;
+ return 1;
+ }
+ raw_brelse(sb,bh);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+int mount_stacker(struct super_block*sb,char*options)
+{
+ struct buffer_head*bh;
+ struct buffer_head*bh2;
+ int i;
+ unsigned char * pp, *p;
+ unsigned char buf[512];
+ unsigned char b,c;
+ int SectSize, ClustSects, ClustSize, ReservSects, FATCnt;
+ int RootDirEnt, TotalSects, FATSize, HidenSects, FirstRootSect;
+ int FirstDataSect, FirstDataSect2, FAT12, FirstFATSect;
+ int StacVersion;
+ /* parameters of virtual DOS drive */
+ int BB_FirstDataSect, BB_ClustCnt, BB_SectSize, BB_TotalSects;
+ Dblsb*dblsb;
+ int repair=0;
+
+ MOD_INC_USE_COUNT;
+ LOG_REST("DMSDOS: stacker 3/4 module mounting...\n");
+
+ dblsb=malloc_dblsb();
+ if(dblsb==NULL)
+ { printk(KERN_ERR "DMSDOS: mount_stacker: out of memory\n");
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ MSDOS_SB(sb)->private_data=dblsb;
+
+#ifdef __KERNEL__
+ { struct semaphore* sem;
+
+ sem=kmalloc(sizeof(struct semaphore),GFP_KERNEL);
+ if(sem==NULL)
+ { printk(KERN_ERR "DMSDOS: mount_stacker: out of memory\n");
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ init_MUTEX(sem);
+ dblsb->mdfat_alloc_semp=sem;
+ }
+#endif
+
+ dblsb->s_comp=GUESS;
+ dblsb->s_cfaktor=DEFAULT_CF;
+
+ if(parse_dmsdos_options(options,dblsb,&repair))
+ {
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ dblsb->s_dataend=blk_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)]*2;
+
+ LOG_REST("DMSDOS: reading super block...\n");
+ bh=raw_bread(sb,0);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read super block of CVF\n");
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ LOG_REST("DMSDOS: super block read finished\n");
+ pp=&(bh->b_data[0]);
+ if(strncmp(pp,"STACKER",7)!=0)
+ { printk(KERN_ERR "DMSDOS: STACKER signature not found\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ /* copy block (must not directly modify kernel buffer!!!) */
+ memcpy(buf,bh->b_data,SECTOR_SIZE);
+ /* decode super block */
+ for(i=0x30,p=buf+0x50,b=buf[0x4c];i--;p++)
+ { b=0xc4-b;
+ b=b<0x80?b*2:b*2+1;
+ b^=c=*p;
+ *p=b;b=c;
+ }
+ if(buf[0x4e]!=0xa||buf[0x4f]!=0x1a)
+ { printk(KERN_ERR "DMSDOS: Stacker 0x1A0A signature not found\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ if(sb->s_flags & MS_RDONLY)dblsb->s_comp=READ_ONLY;
+ printk(KERN_NOTICE "DMSDOS: mounting CVF on device 0x%x %s...\n",
+ sb->s_dev,
+ dblsb->s_comp==READ_ONLY?"read-only":"read-write");
+
+ /* extract important info */
+ pp=&(buf[0x6C]);
+ TotalSects=CHL(pp);
+ pp=&(buf[0x70]);
+ dblsb->s_bootblock=CHS(pp);
+ pp=&(buf[0x74]);
+ dblsb->s_mdfatstart=CHS(pp); /* here it's AMAP start !!! */
+ pp=&(buf[0x76]);
+ FirstFATSect=dblsb->s_fatstart=CHS(pp);
+ pp=&(buf[0x7a]);
+ FirstDataSect2=dblsb->s_datastart=CHS(pp);
+ pp=&(buf[0x60]);
+ StacVersion=CHS(pp);
+ if(StacVersion>=410)dblsb->s_cvf_version=STAC4;
+ else dblsb->s_cvf_version=STAC3;
+ /* if(buf[0x64]==9)dblsb->s_cvf_version=STAC4;
+ else dblsb->s_cvf_version=STAC3; */
+
+#ifndef DMSDOS_CONFIG_STAC3
+ if(dblsb->s_cvf_version==STAC3)
+ { printk(KERN_ERR "DMSDOS: support for stacker 3 not compiled in.\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+#endif
+#ifndef DMSDOS_CONFIG_STAC4
+ if(dblsb->s_cvf_version==STAC4)
+ { printk(KERN_ERR "DMSDOS: support for stacker 4 not compiled in.\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+#endif
+
+ /* now we need the boot block */
+ bh2=raw_bread(sb,dblsb->s_bootblock);
+ if(bh2==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read emulated boot block of CVF\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ /* read values */
+ dblsb->s_sectperclust=bh2->b_data[0xd];
+ dblsb->s_spc_bits=ilog2(dblsb->s_sectperclust);
+ pp=&(bh2->b_data[0x11]);
+ dblsb->s_rootdirentries=CHS(pp);
+
+ pp=&(buf[0x62]); SectSize=CHS(pp);
+ pp=&(bh2->b_data[0xB]); BB_SectSize=CHS(pp);
+ if(SectSize!=SECTOR_SIZE||BB_SectSize!=SECTOR_SIZE)
+ printk(KERN_WARNING "DMSDOS: Stacker sector size not 512 bytes, hmm...\n");
+ ClustSects=bh2->b_data[0xD];
+ ClustSize=ClustSects*SectSize;
+ pp=&(bh2->b_data[0xE]); ReservSects=CHS(pp);
+ FATCnt=bh2->b_data[0x10];
+ pp=&(bh2->b_data[0x11]); RootDirEnt=CHS(pp);
+ pp=&(bh2->b_data[0x13]); BB_TotalSects=CHS(pp);
+ if(!BB_TotalSects)
+ { pp=&(bh2->b_data[0x20]); BB_TotalSects=CHL(pp);};
+ pp=&(bh2->b_data[0x16]); FATSize=CHS(pp);
+ pp=&(bh2->b_data[0x1B]); HidenSects=CHS(pp);
+ if(BB_SectSize!=SectSize)printk(KERN_WARNING "DMSDOS: Inconsistent sector length\n");
+ FirstRootSect=FirstFATSect+3*FATCnt*FATSize;
+
+ dblsb->s_2nd_fat_offset=3*(FATCnt-1)*FATSize;
+
+ /* Number of sectors in root directory */
+ FirstDataSect=((long)RootDirEnt*0x20+SectSize-1)/SectSize;
+ /* Emulated data start sector for DOS */
+ BB_FirstDataSect=FirstDataSect+FATCnt*FATSize+ReservSects;
+ /* ??? +HidenSects; */
+ /* Real data start sector */
+ FirstDataSect+=FirstRootSect;
+ /* Counting BB_ClustCnt from emulated boot block */
+ BB_ClustCnt=(BB_TotalSects-BB_FirstDataSect)/ClustSects;
+ if(BB_ClustCnt>=0xFED)FAT12=0; else FAT12=1;
+ if(BB_ClustCnt<2||BB_ClustCnt>0xfff7)
+ { printk(KERN_ERR "DMSDOS: BB_ClustCnt=0x%x impossible (FAT32?)\n",BB_ClustCnt);
+ raw_brelse(sb,bh2);
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ if(FirstDataSect2!=FirstDataSect)
+ { printk(KERN_WARNING "DMSDOS: Inconsistent first data sector number. Mounting READ ONLY.\n");
+ printk(KERN_WARNING "In header found %u but computed %u\n",(unsigned)FirstDataSect2,(unsigned)FirstDataSect);
+ dblsb->s_comp=READ_ONLY;
+ }
+
+ LOG_REST("DMSDOS: Stac version %u start of FAT %u, root %u, data %u; FATSize %u; FATCnt %u; clusts %u; sects %u\n",
+ (unsigned)StacVersion,(unsigned)FirstFATSect,(unsigned)FirstRootSect,
+ (unsigned)FirstDataSect,(unsigned)FATSize,(unsigned)FATCnt,
+ (unsigned)BB_ClustCnt,(unsigned)BB_TotalSects);
+
+ /* try dos standard method to detect fat bit size - does not work */
+ /* pp=&(bh2->b_data[57]); */
+ /* if(CHL(pp)==0x20203631)dblsb->s_16bitfat=1; */
+ /* else if(CHL(pp)==0x20203231)dblsb->s_16bitfat=0; else */
+
+ /* used only stacker method for fat entry size now */
+ dblsb->s_16bitfat=FAT12? 0: 1;
+ LOG_REST("DMSDOS: FAT bit size of CVF is %d bit\n",
+ (FAT12) ? 12 : 16 );
+
+ /* check if clusters fits in FAT */
+ if(BB_ClustCnt+2>(FAT12?(SECTOR_SIZE*FATSize*2)/3:(SECTOR_SIZE*FATSize)/2))
+ { printk(KERN_WARNING "DMSDOS: FAT size does not match cluster count. Mounting READ ONLY.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+
+ /* check size of physical media against stacvol parameters */
+ if((TotalSects<=0)||(TotalSects-1)>dblsb->s_dataend)
+ { printk(KERN_WARNING "DMSDOS: CVF is shorter about %d sectors. Mounting READ ONLY.\n",
+ (int)TotalSects-1-dblsb->s_dataend);
+ dblsb->s_comp=READ_ONLY;
+ }
+ else if((TotalSects-1)<dblsb->s_dataend)
+ { printk(KERN_INFO "DMSDOS: CVF end padding %d sectors.\n",
+ (int)dblsb->s_dataend-TotalSects+1);
+ dblsb->s_dataend=TotalSects-1;
+ }
+
+ raw_brelse(sb,bh2);
+ dblsb->s_full=0;
+ raw_brelse(sb,bh);
+
+ dblsb->s_rootdir=FirstRootSect;
+ dblsb->s_max_cluster=dblsb->s_max_cluster2=BB_ClustCnt+1;
+
+ LOG_REST("DMSDOS: mdfatstart=%d\n",dblsb->s_mdfatstart);
+ LOG_REST("DMSDOS: rootdirentries=%d\n",dblsb->s_rootdirentries);
+ LOG_REST("DMSDOS: sectperclust=%d\n",dblsb->s_sectperclust);
+ LOG_REST("DMSDOS: fatstart=%d\n",dblsb->s_fatstart);
+ LOG_REST("DMSDOS: rootdir=%d\n",dblsb->s_rootdir);
+ LOG_REST("DMSDOS: %d bit FAT\n",dblsb->s_16bitfat ? 16 : 12);
+
+ /* allocation informations */
+ dblsb->s_lastnear=0;
+ dblsb->s_lastbig=0;
+ dblsb->s_free_sectors=-1; /* -1 means unknown */
+
+ /* set some msdos fs important stuff */
+ MSDOS_SB(sb)->dir_start=FAKED_ROOT_DIR_OFFSET;
+ MSDOS_SB(sb)->dir_entries=dblsb->s_rootdirentries;
+ MSDOS_SB(sb)->data_start=FAKED_DATA_START_OFFSET; /*begin of virtual cluster 2*/
+ MSDOS_SB(sb)->clusters=BB_ClustCnt;
+ if(MSDOS_SB(sb)->fat_bits!=dblsb->s_16bitfat?16:12)
+ { LOG_REST("DMSDOS: fat bit size mismatch in fat driver, trying to correct\n");
+ MSDOS_SB(sb)->fat_bits=dblsb->s_16bitfat?16:12;
+ }
+ MSDOS_SB(sb)->cluster_size=dblsb->s_sectperclust;
+ #ifdef HAS_SB_CLUSTER_BITS
+ for(MSDOS_SB(sb)->cluster_bits=0;
+ (1<<MSDOS_SB(sb)->cluster_bits)<MSDOS_SB(sb)->cluster_size;)
+ MSDOS_SB(sb)->cluster_bits++;
+ MSDOS_SB(sb)->cluster_bits+=SECTOR_BITS;
+ #endif
+
+ /* error test */
+ if(repair!=-1) /* repair==-1 means do not even check */
+ {
+ i=simple_check(sb,repair&1);
+ if(i==-1||i==-2)
+ { printk(KERN_WARNING "DMSDOS: CVF has serious errors or compatibility problems, setting to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+ if(i==-3)
+ { if(repair&2)
+ { printk(KERN_WARNING "DMSDOS: CVF has bitfat mismatches, ignored.\n");
+ }
+ else
+ { printk(KERN_WARNING "DMSDOS: CVF has bitfat mismatches, setting to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+ }
+ }
+
+ /* print stacker version */
+ if(dblsb->s_cvf_version==STAC3)
+ { printk(KERN_NOTICE "DMSDOS: CVF is in stacker 3 format.\n");
+ }
+ else if(dblsb->s_cvf_version==STAC4)
+ { printk(KERN_NOTICE "DMSDOS: CVF is in stacker 4 format.\n");
+ }
+
+ /* if still unknown then count now */
+ if(dblsb->s_free_sectors<0)check_free_sectors(sb);
+
+ /* these *must* always match */
+ if(dblsb->s_comp==READ_ONLY)sb->s_flags |= MS_RDONLY;
+
+ /* mark stacker bitfat as mounted and changing */
+ /* if not regulary unmounted, it must be repaired before */
+ /* next write access */
+ if((sb->s_flags&MS_RDONLY)==0)stac_bitfat_state(sb,2);
+
+ /* we allow using the daemon - calling this more than once doesn't matter */
+ init_daemon();
+
+ return 0;
+}
+#endif
+
+#ifdef DMSDOS_USE_READPAGE
+#define READPAGE dblspace_readpage
+#define MMAP NULL
+#define RMFLAG CVF_USE_READPAGE
+#else
+#define READPAGE NULL
+#define MMAP dblspace_mmap
+#define RMFLAG 0
+#endif
+
+#ifndef __DMSDOS_LIB__
+#ifdef DMSDOS_CONFIG_DBL
+struct cvf_format dblspace_format = {
+ 0x0001, /* version id */
+ "dblspace", /* version text */
+ RMFLAG, /* flags */
+ detect_dblspace, /* detect */
+ mount_dblspace, /* mount */
+ unmount_dblspace, /* unmount */
+ dblspace_bread, /* bread */
+ dblspace_getblk, /* getblk */
+ dblspace_brelse, /* brelse */
+ dblspace_mark_buffer_dirty, /* mark_buffer_dirty */
+ dblspace_set_uptodate, /* set_uptodate */
+ dblspace_is_uptodate, /* is_uptodate */
+ dblspace_ll_rw_block, /* ll_rw_block */
+ dblspace_fat_access, /* fat_access */
+ NULL, /* statfs */
+ dblspace_bmap, /* bmap */
+ #ifndef __FOR_KERNEL_2_3_10
+ dblspace_smap, /* smap */
+ #endif
+ dblspace_file_read, /* file_read */
+ dblspace_file_write, /* file_write */
+ MMAP, /* mmap */
+ READPAGE, /* readpage */
+ NULL, /* writepage */
+ dmsdos_ioctl_dir, /* dir ioctl */
+ dblspace_zero_new_cluster /* zero_new_cluster */
+};
+#endif
+
+#ifdef DMSDOS_CONFIG_STAC
+struct cvf_format stacker_format = {
+ 0x0002, /* version id */ /**** only ****/
+ "stacker", /* version text */ /**** these ****/
+ RMFLAG, /* flags */
+ detect_stacker, /* detect */ /**** four ****/
+ mount_stacker, /* mount */ /**** differ :) ****/
+ unmount_dblspace, /* unmount */
+ dblspace_bread, /* bread */
+ dblspace_getblk, /* getblk */
+ dblspace_brelse, /* brelse */
+ dblspace_mark_buffer_dirty, /* mark_buffer_dirty */
+ dblspace_set_uptodate, /* set_uptodate */
+ dblspace_is_uptodate, /* is_uptodate */
+ dblspace_ll_rw_block, /* ll_rw_block */
+ dblspace_fat_access, /* fat_access */
+ NULL, /* statfs */
+ dblspace_bmap, /* bmap */
+ #ifndef __FOR_KERNEL_2_3_10
+ dblspace_smap, /* smap */
+ #endif
+ dblspace_file_read, /* file_read */
+ dblspace_file_write, /* file_write */
+ MMAP, /* mmap */
+ READPAGE, /* readpage */
+ NULL, /* writepage */
+ dmsdos_ioctl_dir, /* dir ioctl */
+ dblspace_zero_new_cluster /* zero_new_cluster */
+};
+#endif
+
+int init_dmsdos(void)
+{ int i;
+
+ do_spc_init();
+#ifdef DMSDOS_CONFIG_DBL
+ i=register_cvf_format(&dblspace_format);
+ if(i<0)
+ { printk(KERN_ERR "register_cvf_format failed, dmsdos not loaded successfully\n");
+ do_spc_exit();
+ return i;
+ }
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ i=register_cvf_format(&stacker_format);
+ if(i<0)
+ { printk(KERN_ERR "register_cvf_format failed, dmsdos not loaded successfully\n");
+ do_spc_exit();
+#ifdef DMSDOS_CONFIG_DBL
+ unregister_cvf_format(&dblspace_format);
+#endif
+ return i;
+ }
+#endif
+ LOG_REST("CVF format(s) successfully registered\n");
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{ return init_dmsdos();
+}
+
+void cleanup_module(void)
+{ do_spc_exit();
+#ifdef DMSDOS_CONFIG_DBL
+ unregister_cvf_format(&dblspace_format);
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ unregister_cvf_format(&stacker_format);
+#endif
+}
+#endif /* MODULE */
+#endif /* ifndef __DMSDOS_LIB__ */
+
+char seq[]="000000";
+
+#ifdef __DMSDOS_LIB__
+/* we don't need locking in the library */
+void lock_prseq(void) {}
+void unlock_prseq(void) {}
+#else
+DECLARE_MUTEX(prseq_sem); /* Must be initialized to green light */
+void lock_prseq(void) {down(&prseq_sem);}
+void unlock_prseq(void) {up(&prseq_sem);}
+#endif
+
+int log_prseq(void)
+{ int i;
+
+ lock_prseq();
+
+ i=5;
+ while(i>=0)
+ { ++seq[i];
+ if(seq[i]<='9')break;
+ seq[i]='0';
+ --i;
+ }
+
+ printk(seq);
+
+ unlock_prseq();
+
+ return 1;
+}
diff --git a/src/lib/dblspace_methsq.c b/src/lib/dblspace_methsq.c
new file mode 100644
index 0000000..0578cd8
--- /dev/null
+++ b/src/lib/dblspace_methsq.c
@@ -0,0 +1,1256 @@
+/*
+dblspace_methsq.c
+
+DMSDOS CVF-FAT module: drivespace SQ compression/decompression routines.
+
+******************************************************************************
+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 <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <linux/malloc.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_DAEMON__
+#include<malloc.h>
+#include<string.h>
+#include<asm/unaligned.h>
+#include<asm/types.h>
+#include <asm/byteorder.h>
+#define MALLOC malloc
+#define FREE free
+int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2)));
+extern int debug;
+#undef LOG_DECOMP
+#define LOG_DECOMP if(debug)printk
+#endif
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<string.h>
+#endif
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers may not like inline */
+#define INLINE static
+#endif
+
+/* store and load __u16 in any byteorder on any */
+/* address (odd or even). */
+/* this is problematic on architectures, */
+/* which cannot do __u16 access to odd address. */
+/* used for temporary storage of LZ intercode. */
+//#define C_ST_u16(p,v) {put_unaligned(v,((__u16*)p)++);}
+//#define C_LD_u16(p,v) {v=get_unaligned(((__u16*)p)++);}
+
+/* high speed compare and move routines */
+#if defined(__GNUC__) && defined(__i386__) && defined(USE_ASM)
+#define USE_GNU_ASM_i386
+
+#ifdef GAS_XLAT_BUG
+#define XLAT "xlatl\n\t"
+#else
+#define XLAT "xlat\n\t"
+#endif
+
+/* copy block, overlaping part is replaced by repeat of previous part */
+/* pointers and counter are modified to point after block */
+#define M_MOVSB(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "movsb\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ :"memory")
+
+/* compare blocks, overlaping repeat test */
+/* pointers and counter are modified to point after block */
+/* D and S points to first diff adr */
+#define M_FIRSTDIFF(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "cmpsb\n\t" \
+ "je 1f\n\t" \
+ "dec %0\n\t" \
+ "dec %1\n\t" \
+ "1:\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ )
+
+
+#else
+
+#ifdef __GNUC__
+/* non-gnu compilers may not like warning directive */
+#warning USE_GNU_ASM_I386 not defined, using "C" equivalent
+#endif
+
+#define M_MOVSB(D,S,C) for(;(C);(C)--) *((__u8*)(D)++)=*((__u8*)(S)++)
+#define M_FIRSTDIFF(D,S,C) for(;(*(__u8*)(D)==*(__u8*)(S))&&(C);\
+ (__u8*)(D)++,(__u8*)(S)++,(C)--)
+
+#endif
+
+#if !defined(cpu_to_le16)
+ /* for old kernel versions - works only on i386 */
+ #define le16_to_cpu(v) (v)
+ #define cpu_to_le16(v) (v)
+#endif
+
+/*==============================================================*/
+/* bitstream reading */
+
+/* for reading and writting from/to bitstream */
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* already readed bits from buf */
+ __u16 *pd; /* first not readed input data */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+const unsigned sq_bmsk[]=
+ {0x0,0x1,0x3,0x7,0xF,0x1F,0x3F,0x7F,0xFF,
+ 0x1FF,0x3FF,0x7FF,0xFFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
+
+/* read next 16 bits from input */
+#define RDN_G16(bits) \
+ { \
+ (bits).buf>>=16; \
+ (bits).pb-=16; \
+ if((bits).pd<(bits).pe) \
+ { \
+ (bits).buf|=((__u32)(le16_to_cpu(*((bits).pd++))))<<16; \
+ }; \
+ }
+
+/* prepares at least 16 bits for reading */
+#define RDN_PR(bits,u) \
+ { \
+ if((bits).pb>=16) RDN_G16(bits); \
+ u=(bits).buf>>(bits).pb; \
+ }
+
+/* initializes reading from bitstream */
+INLINE void sq_rdi(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->pb=32;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+/* reads n<=16 bits from bitstream *pbits */
+INLINE unsigned sq_rdn(bits_t *pbits,int n)
+{
+ unsigned u;
+ RDN_PR(*pbits,u);
+ pbits->pb+=n;
+ u&=sq_bmsk[n];
+ return u;
+};
+
+/*==============================================================*/
+/* huffman decoding */
+
+#define MAX_SPDA_BITS 10
+#define MAX_SPDA_LEN (1<<MAX_SPDA_BITS)
+#define MAX_BITS 16
+#define MAX_CODES 0x140
+#define OUT_OVER 0x100
+
+typedef
+ struct {
+ __s8 ln; /* character lens .. for tokens -0x40 */
+ __u8 ch; /* character/token code */
+ }huf_chln_t;
+
+typedef
+ struct {
+ unsigned cd_ln[MAX_BITS+1]; /* distribution of bits */
+ unsigned cd_ch[MAX_BITS+1]; /* distribution of codes codes */
+ int bn; /* chln array convert max bn bits codes */
+ huf_chln_t chln1[MAX_CODES]; /* for codes with more than bn bits */
+ huf_chln_t chln[0]; /* character codes decode array length SPDA_LEN */
+ }huf_rd_t;
+
+#define HUF_RD_SIZE(SPDA_LEN) \
+ ((sizeof(huf_rd_t)+SPDA_LEN*sizeof(huf_chln_t)+3)&~3)
+
+const __u8 swap_bits_xlat[]=
+ {0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,
+ 0x50,0xd0,0x30,0xb0,0x70,0xf0,0x08,0x88,0x48,0xc8,
+ 0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,
+ 0x78,0xf8,0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,
+ 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,0x0c,0x8c,
+ 0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,
+ 0x3c,0xbc,0x7c,0xfc,0x02,0x82,0x42,0xc2,0x22,0xa2,
+ 0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
+ 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,
+ 0x5a,0xda,0x3a,0xba,0x7a,0xfa,0x06,0x86,0x46,0xc6,
+ 0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,
+ 0x76,0xf6,0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,
+ 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,0x01,0x81,
+ 0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,
+ 0x31,0xb1,0x71,0xf1,0x09,0x89,0x49,0xc9,0x29,0xa9,
+ 0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
+ 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,
+ 0x55,0xd5,0x35,0xb5,0x75,0xf5,0x0d,0x8d,0x4d,0xcd,
+ 0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,
+ 0x7d,0xfd,0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,
+ 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,0x0b,0x8b,
+ 0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,
+ 0x3b,0xbb,0x7b,0xfb,0x07,0x87,0x47,0xc7,0x27,0xa7,
+ 0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
+ 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,
+ 0x5f,0xdf,0x3f,0xbf,0x7f,0xff};
+
+/* swap 16 bits order */
+INLINE unsigned swap_bits_order_16(unsigned d)
+{ unsigned r;
+ #ifdef USE_GNU_ASM_i386
+ __asm__ (
+ XLAT
+ "xchgb %%al,%%ah\n\t"
+ XLAT
+ :"=a"(r):"0"(d),"b"(swap_bits_xlat));
+ #else
+ r=((unsigned)swap_bits_xlat[(__u8)d])<<8;
+ r|=swap_bits_xlat[(__u8)(d>>8)];
+ #endif
+ return r;
+};
+
+/* swap bit order */
+INLINE unsigned swap_bits_order(unsigned d,int n)
+{ unsigned r=0;
+ while(n--) { r<<=1;r|=d&1;d>>=1;};
+ return r;
+};
+
+/* initializes huffman conversion structure *phuf for m codes,
+ *ca code and token bit lengths, ends with 0xFF,
+ bn predicated maximal bit length */
+int sq_rdhufi(huf_rd_t *phuf,int m,int bn,__u8 *ca)
+{
+ if(bn>MAX_SPDA_BITS) bn=MAX_SPDA_BITS;
+ phuf->bn=bn;
+ {
+ int i;
+ unsigned u,us,ut;
+ memset(phuf->cd_ln,0,sizeof(phuf->cd_ln));i=0;
+ while((u=ca[i++])<=MAX_BITS) phuf->cd_ln[u]++;
+ memset(phuf->cd_ch,0,sizeof(phuf->cd_ch));
+ phuf->cd_ln[0]=0;us=0;ut=0;
+ for(i=1;i<=MAX_BITS;i++)
+ {
+ u=phuf->cd_ln[i];phuf->cd_ln[i]=ut;
+ phuf->cd_ch[i]=us;ut+=u;us+=u;us<<=1;
+ };
+ /* if suceed, codespace should be full else report error */
+ if (us&((1<<MAX_BITS)-1))
+ if(us!=1)return(0); /* exeption, zip 2.0 allows one code one bit long */
+ };
+ {
+ int i,ln,l,ch,sh,cod;
+ for(i=0;(l=ln=ca[i])<=MAX_BITS;i++) if(ln)
+ {
+ sh=(bn-ln);
+ cod=(phuf->cd_ch[ln])++;
+ cod=swap_bits_order_16(cod)>>(16-ln);
+ if(i<m) ch=i; else {ch=i-m+1;ln-=0x40;};
+ if (sh>0)
+ {
+ sh=1<<sh;
+ l=1<<l;
+ while(sh--)
+ {
+ phuf->chln[cod].ch=ch;
+ phuf->chln[cod].ln=ln;
+ cod+=l;
+ };
+ } else if (sh==0) {
+ phuf->chln[cod].ch=ch;
+ phuf->chln[cod].ln=ln;
+ } else {
+ cod&=sq_bmsk[bn];
+ phuf->chln[cod].ch=0x00;
+ phuf->chln[cod].ln=-0x40;
+ cod=(phuf->cd_ln[l])++;
+ phuf->chln1[cod].ch=ch;
+ phuf->chln1[cod].ln=ln;
+ };
+ };
+ /* if suceed ln should be 0xFF */
+ };
+ return(1);
+};
+
+/* read and huffman decode of characters, stops on tokens or buffer ends */
+INLINE unsigned sq_rdh(bits_t *pbits,const huf_rd_t *phuf,__u8 **pout,__u8 *pend)
+{
+ unsigned ch;
+ unsigned bmsk=sq_bmsk[phuf->bn];
+
+ while(1)
+ {while(1)
+ {if(pbits->pb>=16)
+ RDN_G16(*pbits);
+ if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>pbits->pb)&bmsk;
+ if((pbits->pb+=phuf->chln[ch].ln)<0) break;
+ *((*pout)++)=phuf->chln[ch].ch;
+
+ if(pbits->pb<16)
+ {if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>pbits->pb)&bmsk;
+ if((pbits->pb+=phuf->chln[ch].ln)<0) break;
+ *((*pout)++)=phuf->chln[ch].ch;
+
+ if(pbits->pb<16)
+ {if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>pbits->pb)&bmsk;
+ if((pbits->pb+=phuf->chln[ch].ln)<0) break;
+ *((*pout)++)=phuf->chln[ch].ch;
+ };
+ };
+ };
+
+ ch=phuf->chln[ch].ch;
+ pbits->pb+=0x40; if(ch--) return ch;
+ /* code longer than phuf->bn */
+ if(pbits->pb>=16) RDN_G16(*pbits);
+ ch=swap_bits_order_16((__u16)(pbits->buf>>pbits->pb));
+ {
+ int i;
+ i=phuf->bn;
+ do
+ i++;
+ while((phuf->cd_ch[i]<=(ch>>(16-i)))&&(i<MAX_BITS));
+ ch=((ch>>(16-i)))-phuf->cd_ch[i]+phuf->cd_ln[i];
+ };
+ if((pbits->pb+=phuf->chln1[ch].ln)<0)
+ {pbits->pb+=0x40;
+ return phuf->chln1[ch].ch-1;
+ };
+ *((*pout)++)=phuf->chln1[ch].ch;
+ };
+};
+
+/* read one huffman encoded value */
+INLINE unsigned sq_rdh1(bits_t *pbits,const huf_rd_t *phuf)
+{unsigned ch;
+ if(pbits->pb>=16) RDN_G16(*pbits);
+ ch=(pbits->buf>>pbits->pb)&sq_bmsk[phuf->bn];
+ if((pbits->pb+=phuf->chln[ch].ln)>=0) return phuf->chln[ch].ch;
+ ch=phuf->chln[ch].ch;
+ pbits->pb+=0x40; if(ch) return ch+0x100-1;
+ ch=swap_bits_order_16((__u16)(pbits->buf>>pbits->pb));
+ {int i;
+ i=phuf->bn;
+ do
+ i++;
+ while((phuf->cd_ch[i]<=(ch>>(16-i)))&&(i<MAX_BITS));
+ ch=((ch>>(16-i)))-phuf->cd_ch[i]+phuf->cd_ln[i];
+ };
+ if((pbits->pb+=phuf->chln1[ch].ln)>=0) return phuf->chln1[ch].ch;
+ pbits->pb+=0x40;
+ return phuf->chln1[ch].ch+0x100-1;
+};
+
+/*==============================================================*/
+/* SQ decompression */
+
+/* index conversion table for first bitlen table */
+const int code_index_1[]={0x10,0x11,0x12,0x00,0x08,0x07,0x09,0x06,0x0A,0x05,
+ 0x0B,0x04,0x0C,0x03,0x0D,0x02,0x0E,0x01,0x0F};
+
+const unsigned sqt_repbas[]={
+ 0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0D,
+ 0x0F,0x11,0x13,0x17,0x1B,0x1F,0x23,0x2B,0x33,0x3B,
+ 0x43,0x53,0x63,0x73,0x83,0xA3,0xC3,0xE3,0x102,0x00,0x00};
+
+const unsigned char sqt_repbln[]={
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
+ 0x01,0x01,0x02,0x02,0x02,0x02,0x03,0x03,0x03,0x03,
+ 0x04,0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x00,0x63,0x63};
+
+const unsigned sqt_offbas[]={
+ 0x0001,0x0002,0x0003,0x0004,0x0005,0x0007,0x0009,0x000D,
+ 0x0011,0x0019,0x0021,0x0031,0x0041,0x0061,0x0081,0x00C1,
+ 0x0101,0x0181,0x0201,0x0301,0x0401,0x0601,0x0801,0x0C01,
+ 0x1001,0x1801,0x2001,0x3001,0x4001,0x6001,0x0000,0x0000};
+
+const unsigned char sqt_offbln[]={
+ 0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x02,
+ 0x03,0x03,0x04,0x04,0x05,0x05,0x06,0x06,
+ 0x07,0x07,0x08,0x08,0x09,0x09,0x0A,0x0A,
+ 0x0B,0x0B,0x0C,0x0C,0x0D,0x0D,0x00,0x00};
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+
+int sq_dec(void* pin,int lin, void* pout, int lout, int flg)
+{
+ __u8 *p, *pend, *r;
+ unsigned u, u1, repoffs, replen;
+ int i,bn_max;
+ bits_t bits;
+ int final_flag;
+ int count_1;
+ int count_2;
+ int count_3;
+ int method;
+ unsigned mask;
+ __u8 *code_bln; /* bitlengths of char, tokens and rep codes [0x150] */
+ huf_rd_t *huf1,*huf2; /* tables for huffman decoding */
+ char *work_mem;
+
+ sq_rdi(&bits,pin,lin);
+ p=(__u8*)pout;pend=p+lout;
+ if((sq_rdn(&bits,8)!='S')||(sq_rdn(&bits,8)!='Q'))
+ { printk(KERN_ERR "DMSDOS: Data are not SQ compressed\n");
+ return(0);
+ };
+ u=sq_rdn(&bits,16);
+ LOG_DECOMP("DMSDOS: sq_dec: version %X\n",u);
+ /* allocating work memory */
+ work_mem=(char*)MALLOC(0x150+HUF_RD_SIZE(MAX_SPDA_LEN)+HUF_RD_SIZE(256));
+ if(!work_mem)
+ {printk(KERN_ERR "DMSDOS: sq_dec: out of memory!\n");return 0;};
+ code_bln=work_mem;
+ huf1=(huf_rd_t*)(work_mem+0x150);
+ huf2=(huf_rd_t*)(work_mem+0x150+HUF_RD_SIZE(MAX_SPDA_LEN));
+ do
+ { final_flag=sq_rdn(&bits,1); LOG_DECOMP("DMSDOS: final_flag %d\n",final_flag);
+ method=sq_rdn(&bits,2); LOG_DECOMP("DMSDOS: method %d\n",method);
+ switch(method)
+ {
+ case 0:
+ printk(KERN_NOTICE "DMSDOS: dec_sq: submethod not tested - raw read\n");
+ /* go to byte boundary */
+ /* read 16 bits - count of raw bytes */
+ sq_rdn(&bits,(8-bits.pb)&7);
+ replen=sq_rdn(&bits,16);
+ if (replen+sq_rdn(&bits,16)!=0xFFFF) {FREE(work_mem);return 0;};
+ r=(__u8*)bits.pd-(32-bits.pb)/8;
+ if(r+replen>(__u8*)bits.pe) {FREE(work_mem);return 0;};
+ if(p+replen>pend) {FREE(work_mem);return 0;};
+ M_MOVSB(p,r,replen); /* copy/repeat function */
+ if((unsigned)r&1) bits.pb=32;
+ else {bits.pb=32+8;r--;};
+#if 0
+ /* some compilers seem to be confused by this (???) */
+ bits.pd=(typeof(bits.pd))r;
+#else
+ bits.pd=(__u16*)r;
+#endif
+ break;
+
+ case 1:
+ printk(KERN_NOTICE "DMSDOS: sq_dec: submethod not tested - fixed huffman\n");
+ /* 0x90*8 0x70*9 0x18*7 8*8 sqt_repbln sqt_repbas 0x101 0x120h */
+ /* 0x1E*5 offset sqt_offbln sqt_offbas 0 0x1Eh */
+ bn_max=9;
+ count_1=0x120;
+ count_2=0x20;
+ i=0;
+ while(i<0x90) code_bln[i++]=8;
+ while(i<0x100) code_bln[i++]=9;
+ while(i<0x118) code_bln[i++]=7;
+ while(i<0x120) code_bln[i++]=8;
+ while(i<0x140) code_bln[i++]=5;
+ goto case_1_cont;
+
+ case 2:
+ LOG_DECOMP("DMSDOS: sq_dec: submethod huffman\n");
+ count_1=sq_rdn(&bits,5)+0x101;
+ LOG_DECOMP("DMSDOS: count_1 %d\n",count_1);
+ if(count_1>0x11E)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff count_1 too big\n");
+ FREE(work_mem);return(0);
+ };
+ count_2=sq_rdn(&bits,5)+1;
+ LOG_DECOMP("DMSDOS: count_2 %d\n",count_2);
+ if(count_2>0x1E)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff count_2 too big\n");
+ FREE(work_mem);return(0);
+ };
+ count_3=sq_rdn(&bits,4)+4;
+ LOG_DECOMP("DMSDOS: count_3 %d\n",count_3);
+ bn_max=0;
+ for(i=0;i<count_3;i++)
+ { u=sq_rdn(&bits,3);
+ code_bln[code_index_1[i]]=u;
+ if(u>bn_max)bn_max=u;
+ };
+ while(i<19) code_bln[code_index_1[i++]]=0;code_bln[19]=0xFF;
+ i=sq_rdhufi(huf1,19,bn_max,code_bln);
+ if(!i)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff error in helper table\n");
+ FREE(work_mem);return 0;
+ };
+ mask=sq_bmsk[huf1->bn]; u1=0; bn_max=0;
+ for(i=0;i<count_1+count_2;)
+ { RDN_PR(bits,u);
+ bits.pb+=huf1->chln[u&mask].ln;
+ u=huf1->chln[u&mask].ch;
+ switch(u)
+ { case 16: /* 3 to 6 repeats of last */
+ u=sq_rdn(&bits,2)+3;
+ while(u--) code_bln[i++]=u1;
+ break;
+ case 17: /* 3 to 10 repeats of 0 */
+ u=sq_rdn(&bits,3)+3; u1=0;
+ while(u--) code_bln[i++]=u1;
+ break;
+ case 18: /* 11 to 139 repeats of 0 */
+ u=sq_rdn(&bits,7)+11; u1=0;
+ while(u--) code_bln[i++]=u1;
+ break;
+ default:
+ code_bln[i++]=u;
+ u1=u;
+ if(u>bn_max) bn_max=u;
+ };
+ };
+
+ case_1_cont:
+ /* code_bln+count_1 0x96 count_2 sqt_offbln sqt_offbas */
+ code_bln[count_1+count_2]=0xFF;
+ i=sq_rdhufi(huf2,0x100,8,code_bln+count_1);
+ if(!i)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff error in offset table\n");
+ FREE(work_mem);return 0;
+ };
+
+ /* code_bln 0x100 count_1 sqt_repbln sqt_repbas */
+ code_bln[count_1]=0xFF;
+ i=sq_rdhufi(huf1,0x100,bn_max,code_bln);
+ if(!i)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff error in char and len table\n");
+ FREE(work_mem);return 0;
+ };
+
+ while((u=sq_rdh(&bits,huf1,&p,pend))!=0)
+ { if(u==OUT_OVER){u=sq_rdh1(&bits,huf1)-0x100;break;};
+ u--;
+ replen=sqt_repbas[u]+sq_rdn(&bits,sqt_repbln[u]);
+ u=sq_rdh1(&bits,huf2);
+ repoffs=sqt_offbas[u]+sq_rdn(&bits,sqt_offbln[u]);
+ if(!repoffs)
+ { printk("DMSDOS: sq_dec: bad repoffs !!!\n\n");
+ FREE(work_mem);return(0);
+ };
+ if ((__u8*)pout+repoffs>p)
+ { repoffs=p-(__u8*)pout;
+ printk(KERN_INFO "DMSDOS: sq_dec: huff offset UNDER\n");
+ };
+ if (p+replen>pend)
+ { replen=pend-p;
+ printk(KERN_INFO "DMSDOS: sq_dec: huff offset OVER\n");
+ };
+ r=p-repoffs; M_MOVSB(p,r,replen); /* copy/repeat function */
+ };
+
+ if(u)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff BAD last token %x\n",u);
+ FREE(work_mem);return 0;
+ };
+ break;
+
+ case 3:
+ printk(KERN_INFO "DMSDOS: sq_dec: unknown submethod - 3\n");
+ FREE(work_mem);
+ return(0);
+ };
+ } while((!final_flag)&&(p<pend));
+ FREE(work_mem);
+ return(p-(__u8*)pout);
+};
+
+#endif /* __KERNEL__||__DMSDOS_LIB__ */
+
+/*==============================================================*/
+/* bitstream writting */
+
+/* initializes writting to bitstream */
+INLINE void sq_wri(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->buf=0;
+ pbits->pb=0;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+/* writes n<=16 bits to bitstream *pbits */
+INLINE void sq_wrn(bits_t *pbits,unsigned u, int n)
+{
+ pbits->buf|=(__u32)(u&sq_bmsk[n])<<pbits->pb;
+ if((pbits->pb+=n)>=16)
+ {
+ if(pbits->pd<pbits->pe)
+ *(pbits->pd++)=cpu_to_le16((__u16)pbits->buf);
+ else if(pbits->pd==pbits->pe) pbits->pd++; /* output overflow */
+ pbits->buf>>=16;
+ pbits->pb-=16;
+ }
+}
+
+/*==============================================================*/
+/* huffman encoding */
+
+typedef long int count_t;
+
+typedef
+ struct {
+ count_t cn;
+ unsigned ch;
+ } ch_tab_t;
+
+typedef
+ struct {
+ __u16 cod; /* character code */
+ __u16 ln; /* character len */
+ } huf_wr_t;
+
+/*** Generation of character codes ***/
+
+void sq_hsort1(ch_tab_t* ch_tab,int ch_num,int cl, ch_tab_t a)
+{
+ /* a eax */
+ /* cl di */
+ int ch; /* bp */
+ ch_tab_t b; /* ecx */
+ ch_tab_t c; /* esi */
+ ch=cl*2;
+ while(ch<ch_num)
+ {
+ b=ch_tab[ch-1];c=ch_tab[ch];
+ if((c.cn<b.cn)||((c.cn==b.cn)&&(c.ch<=b.ch))) {b=c;ch++;};
+ if((b.cn>a.cn)||((b.cn==a.cn)&&(b.ch>=a.ch))) {ch_tab[cl-1]=a;return;}
+ ch_tab[cl-1]=b;cl=ch;ch*=2;
+ };
+ if(ch==ch_num)
+ {
+ b=ch_tab[ch-1];
+ if((b.cn<a.cn)||((b.cn==a.cn)&&(b.ch<a.ch)))
+ {ch_tab[cl-1]=b;cl=ch;ch*=2;};
+ };
+ ch_tab[cl-1]=a;
+};
+
+int sq_huffman(count_t* ch_cn,__u8* ch_blen,unsigned* ch_blcn,int cod_num,ch_tab_t *ch_tab)
+{
+ int i,ch_num,cl;
+ ch_tab_t a;
+ ch_tab_t b;
+
+ redo_reduced:
+ ch_num=0;
+ for(i=0;i<cod_num;i++) if(ch_cn[i])
+ {ch_tab[ch_num].cn=ch_cn[i];ch_tab[ch_num].ch=i|0x800;ch_num++;};
+ ch_tab[ch_num].ch=0;
+ if(ch_num==0)
+ {
+ ch_tab[0].ch=0x800;
+ ch_tab[0].cn=1;
+ ch_num++;
+ }
+ if(ch_num==1)
+ {
+ ch_tab[ch_num]=ch_tab[ch_num-1];
+ ch_tab[ch_num].ch&=0x801;
+ ch_tab[ch_num].ch^=1;ch_num++;
+ };
+ cl=ch_num/2;
+ while(cl>1)
+ {
+ sq_hsort1(ch_tab,ch_num,cl,ch_tab[cl-1]);
+ cl--;
+ };
+
+ cl=ch_num; a=ch_tab[0];
+ while(cl>2)
+ {
+ sq_hsort1(ch_tab,cl,1,a);
+ b=ch_tab[0];
+ a=ch_tab[--cl];
+ ch_tab[cl].ch=b.ch;
+ sq_hsort1(ch_tab,cl,1,a);
+ a=ch_tab[0];
+ ch_tab[cl].cn=a.ch;
+ if(a.ch<=b.ch) {a.ch=b.ch;};
+ a.ch=(a.ch&0x7800)+cl+0x800;
+ if(a.ch>=0x8000u)
+ {
+ printk("DMSDOS: sq_huffman: Problems with number of bits\n");
+ for(i=0;i<cod_num;i++) ch_cn[i]=(ch_cn[i]+1)>>1;
+ goto redo_reduced;
+ };
+ a.ch+=0x8000u;
+ a.cn+=b.cn;
+ };
+ ch_tab[1].cn=a.ch;
+
+ {
+ int st[MAX_BITS+2];
+ int k=0,l=1,blen=0;
+
+ memset(ch_blcn,0,sizeof(ch_blcn[0])*(MAX_BITS+1));
+ memset(ch_blen,0,sizeof(ch_blen[0])*cod_num);
+ while(1)
+ {
+ do
+ {
+ k|=0x4000;
+ do
+ {
+ st[blen]=k;
+ blen++;
+ k=l&0x7FF;
+ l=ch_tab[k].ch&0x87FF;
+ }while(l&0x8000);
+ ch_blen[l]=blen;
+ ch_blcn[blen]++;
+ l=ch_tab[k].cn&0x87FF;
+ }while(l&0x8000);
+ do
+ {
+ ch_blen[l]=blen;
+ ch_blcn[blen]++;
+ do
+ {
+ if(!--blen) goto code_done;
+ k=st[blen];
+ }while(k&0x4000);
+ l=ch_tab[k].cn&0x87FF;
+ }while(!(l&0x8000));
+ };
+ code_done:;
+ };
+ return(0);
+};
+
+INLINE int sq_wrhufi(huf_wr_t *phuf, __u8* ch_blen,
+ unsigned* ch_blencn,int cod_num)
+{
+ unsigned i,u,t,blen;
+ u=0;
+ for(i=0;i<=MAX_BITS;i++) {u<<=1;t=u;u+=ch_blencn[i];ch_blencn[i]=t;};
+ if(u!=1u<<MAX_BITS) return(1);
+ for(i=0;i<cod_num;i++)
+ {
+ if((blen=ch_blen[i])!=0)
+ {
+ phuf[i].cod=swap_bits_order_16(ch_blencn[blen]++)>>(16-blen);
+ phuf[i].ln=blen;
+ };
+ };
+ return(0);
+};
+
+INLINE void sq_wrh(bits_t *pbits,const huf_wr_t *phuf,const unsigned ch)
+{
+ sq_wrn(pbits,phuf[ch].cod,phuf[ch].ln);
+};
+
+
+/*==============================================================*/
+/* SQ compression */
+
+typedef __u8* hash_t;
+
+#define TK_END 0x00
+#define TK_CHRS 0xF0
+#define TKWR_CHRS(p,v) {if(v<15) *(p++)=TK_CHRS+(__u8)v;\
+ else {*(p++)=TK_CHRS+15;C_ST_u16(p,v);};}
+
+#define HASH_TAB_ENT (1<<10)
+#define HASH_HIST_ENT (1<<12)
+#define MAX_OFFS 32768
+#define MAX_OFFS_BLN8 1024
+#define MIN_REP 3
+#define MAX_REP 258
+
+/* definition of data hash function, it can use max 3 chars */
+INLINE unsigned sq_hash(__u8 *p)
+{
+ return (((__u16)p[0]<<2)^((__u16)p[1]<<4)^(__u16)p[2])&(HASH_TAB_ENT-1);
+};
+
+/* store hash of chars at *p in hash_tab, previous occurence is stored */
+/* in hash_hist, which is indexed by next hash positions */
+/* returns previous occurence of same hash as is hash of chars at *p */
+INLINE hash_t sq_newhash(__u8 *p,hash_t* hash_tab,hash_t* hash_hist,unsigned hist_mask)
+{
+ hash_t* hash_ptr;
+ hash_t hash_cur;
+ hash_ptr=hash_tab+sq_hash(p);
+ hash_cur=*hash_ptr;
+ *hash_ptr=p;
+ *(hash_hist+((unsigned)p&hist_mask))=hash_cur;
+ return(hash_cur);
+};
+
+/* binary seeking of nearest less or equal base value */
+INLINE unsigned find_token(int token,const unsigned *tab_val,int tab_len)
+{
+ int half;
+ int beg=0;
+ do
+ {
+ half=tab_len>>1;
+ if(tab_val[beg+half]>token) tab_len=half;
+ else {beg+=half;tab_len-=half;};
+ }
+ while(tab_len>1);
+ return beg;
+};
+
+/* finds repetitions in *pin and writes intermediate code to *pout */
+unsigned sq_complz(void* pin,int lin,void* pout,int lout,int flg,
+ count_t* ch_cn, count_t* offs_cn, void *work_mem)
+{
+ int try_count; /* number of compares to find best match */
+ int hash_skiped; /* last bytes of repetition are hashed too */
+ hash_t *hash_tab; /* [HASH_TAB_ENT] */
+ /* pointers to last occurrence of same hash, index hash */
+ hash_t *hash_hist; /* [HASH_HIST_ENT] */
+ /* previous occurences of hash, index actual pointer&hist_mask */
+ unsigned hist_mask=(HASH_HIST_ENT-1); /* mask for index into hash_hist */
+ __u8 *pi, *po, *pc, *pd, *pend, *poend;
+ hash_t hash_cur;
+ unsigned cn;
+ unsigned max_match, match, token;
+ int try_cn;
+ hash_t max_hash=NULL;
+
+ int delay_count=0; /* test next # characters for better match */
+ int delay_cn;
+ int delay_best;
+
+ hash_tab/*[HASH_TAB_ENT]*/=(hash_t*)work_mem;
+ hash_hist/*[HASH_HIST_ENT]*/=(hash_t*)work_mem+HASH_TAB_ENT;
+
+ try_count=2<<((flg>>2)&0x7); /* number of tested entries with same hash */
+ hash_skiped=((flg>>5)+1)&0xF;/* when rep found last # bytes are procesed */
+
+ if(flg&0x4000)
+ {
+ /* maximal compression */
+ delay_count=2;
+ try_count*=4;
+ hash_skiped*=4;
+ };
+
+ pi=(__u8*)pin;
+ po=(__u8*)pout;
+ if(!lin) return(0);
+ pend=pi+(lin-1);
+ if(lout<0x20) return(0); /* some minimal space for lz interform buffer */
+ poend=po+(lout-0x20);
+ for(cn=0;cn<HASH_TAB_ENT;cn++) hash_tab[cn]=pend; /* none ocurence of hash */
+ for(cn=0;cn<=hist_mask;cn++) hash_hist[cn]=pend; /* should not be needed */
+ pend--; /* last two bytes cannot be hashed */
+ cn=0;
+ while(pi<pend)
+ {
+ hash_cur=sq_newhash(pi,hash_tab,hash_hist,hist_mask);
+ /* goto single_char; */ /* to by pass LZ for tests */
+ if(hash_cur>=pi) goto single_char;
+ try_cn=try_count;
+ max_match=MIN_REP-1;
+ do{
+ if(pi-hash_cur>MAX_OFFS) break; /* longer offsets are not allowed */
+ if((hash_cur[max_match]==pi[max_match])&&
+ (hash_cur[max_match-1]==pi[max_match-1])&&
+ (hash_cur[0]==pi[0])&&(hash_cur[1]==pi[1]))
+ /* pi[2]=hash_cur[2] from hash function */
+ {
+ match=pend-pi; /* length of rest of data */
+ if(match>MAX_REP-2) match=MAX_REP-2;
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match); /* compare */
+ match=pd-pi; /* found match length */
+ if((match>max_match)&&((match>3)||(pi-hash_cur<=MAX_OFFS_BLN8)))
+ {
+ max_hash=hash_cur; /* found maximal hash */
+ max_match=match;
+ if(match==MAX_REP)break; /* longer match cannot be encoded */
+ if(pd>pend+1)break; /* match to end of block */
+ };
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hist_mask])<pc));
+ if(max_match<MIN_REP) goto single_char;
+
+ /* tests if better matchs on next characters */
+ delay_cn=0;
+ if(delay_count)
+ {
+ delay_best=0;
+ while((delay_cn<delay_count)&&(pi+max_match<pend)&&
+ (max_match<0x100))
+ {
+ pi++;delay_cn++;
+ hash_cur=sq_newhash(pi,hash_tab,hash_hist,hist_mask);
+ try_cn=try_count;
+ if (hash_cur<pi) do
+ {
+ if(pi-hash_cur>MAX_OFFS) break; /* longer offsets are not allowed */
+ if((hash_cur[max_match]==pi[max_match])&&
+ (hash_cur[max_match-1]==pi[max_match-1])&&
+ (hash_cur[0]==pi[0])&&(hash_cur[1]==pi[1])&&
+ /* pi[2]=hash_cur[2] from hash function */
+ (hash_cur!=max_hash+delay_cn-delay_best))
+ { /* do not test actual max match */
+ match=pend-pi; /* length of rest of data */
+ if(match>MAX_REP-2) match=MAX_REP-2;
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match);/* compare */
+ match=pd-pi; /* found match length */
+ if((match>max_match+delay_cn)&&((match>3)||(pi-hash_cur<=MAX_OFFS_BLN8)))
+ {
+ max_hash=hash_cur;max_match=match; /* find maximal hash */
+ delay_best=delay_cn;
+ if(match==MAX_REP)break;/* longer match cannot be encoded */
+ if(pd>pend+1)break; /* match to end of block */
+ };
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hist_mask])<pc));
+ };
+ if(delay_best)
+ LOG_DECOMP("DMSDOS: sq_complz: Delayed match %i is better\n",delay_best);
+ pi-=delay_cn;
+ delay_cn-=delay_best;
+ while(delay_best)
+ {
+ delay_best--;
+ ch_cn[*(pi++)]++;
+ if(++cn>=0x8000u)
+ {TKWR_CHRS(po,0x8000u);cn-=0x8000u;if(poend<po) return(0);};
+ }
+ };
+
+ if(cn) TKWR_CHRS(po,cn);
+ cn=pi-max_hash; /* history offset */
+ pi+=max_match; /* skip repeated bytes */
+
+ /* store information about match len into *po */
+ token=find_token(max_match,sqt_repbas,29); /* for max match */
+ ch_cn[token+0x101]++;
+ *po++=token+1;
+ if(sqt_repbln[token]) *po++=max_match-sqt_repbas[token];
+ /* store information about match offset into *po */
+ token=find_token(cn,sqt_offbas,30); /* for history offset */
+ offs_cn[token]++;
+ *po++=token;
+ if(sqt_offbln[token])
+ {
+ if(sqt_offbln[token]<=8)
+ *po++=cn-sqt_offbas[token];
+ else
+ C_ST_u16(po,cn-sqt_offbas[token]);
+ };
+ if(hash_skiped&&(pi<pend))
+ {
+ max_match-=delay_cn;
+ if(--max_match>hash_skiped) max_match=hash_skiped;
+ pi-=max_match;
+ while(max_match--) sq_newhash(pi++,hash_tab,hash_hist,hist_mask);
+ };
+ if(poend<po) return(0);
+ cn=0;
+ continue;
+ single_char:
+ ch_cn[*(pi++)]++;
+ if(++cn>=0x8000u)
+ {TKWR_CHRS(po,0x8000u);cn-=0x8000u;if(poend<po) return(0);};
+ };
+
+ pend+=2;
+ while(pi!=pend) {ch_cn[*(pi++)]++;cn++;};
+ if(cn)
+ {
+ if(cn>=0x8000u) {TKWR_CHRS(po,0x8000u);cn-=0x8000u;};
+ TKWR_CHRS(po,cn);
+ };
+
+ ch_cn[TK_END+0x100]++;
+ *po++=TK_END;
+ return(po-(__u8*)pout);
+};
+
+/*** Main compression routine ***/
+
+__u16 sq_comp_rat_tab[]=
+ {0x7F9,0x7F9,0x621,0x625,
+ 0x665,0x669,0x6E9,0x6ED,
+ 0x7D1,0x7D9,0x6E9,0x47D9,
+ 0x46E9}; /* compression ratio to seek lengths */
+
+typedef
+ struct{
+ count_t ch_cn[0x120]; /* counts of characters and rep length codes */
+ count_t offs_cn[0x20]; /* counts of offset codes */
+ union {
+ struct {
+ __u8 ch_blen[0x120+0x20]; /* bitlengths of character codes and tokens */
+ __u8 code_buf[0x120+0x20]; /* precompressed decompression table */
+ ch_tab_t ch_tab[0x120+0x20];/* temporrary table for huffman */
+ huf_wr_t ch_huf[0x120]; /* character and token encoding table */
+ huf_wr_t offs_huf[0x20]; /* repeat encoding table */
+ } a;
+ hash_t lz_tabs[HASH_TAB_ENT+HASH_HIST_ENT];
+ } a;
+ }sq_comp_work_t;
+
+int sq_comp(void* pin,int lin, void* pout, int lout, int flg)
+{
+ count_t *ch_cn; /* [0x120] counts of characters and rep length codes */
+ count_t *offs_cn; /* [0x20] counts of offset codes */
+ unsigned lz_length; /* length of intermediate reprezentation */
+ __u8* lz_pos; /* possition of intermediate data in pout */
+
+ __u8 *ch_blen; /* [0x120] bitlengths of character codes and tokens */
+ __u8 *offs_blen; /* [0x20] bitlengths of ofset codes are stored in */
+ /* end of ch_blen */
+ unsigned ch_blcn[MAX_BITS+1]; /* counts of bitlengths of chars */
+ unsigned offs_blcn[MAX_BITS+1]; /* counts of bitlengths of offs */
+ huf_wr_t *ch_huf; /* [0x120] character and token encoding table */
+ huf_wr_t *offs_huf; /* [0x20] repeat encoding table */
+
+ ch_tab_t *ch_tab; /* [0x120+0x20] temporrary table for huffman */
+ __u8 *code_buf; /* [0x120+0x20] precompressed decompression table */
+ count_t code_cn[0x20];
+ __u8 code_blen[0x20];
+ unsigned code_blcn[MAX_BITS+1];
+ unsigned code_buf_len;
+ sq_comp_work_t *work_mem=NULL;
+
+ int count_1, count_2, count_3;
+ bits_t bits;
+ int i;
+
+
+ /* allocating work memory */
+ work_mem=(sq_comp_work_t*)MALLOC(sizeof(sq_comp_work_t));
+ if(!work_mem)
+ { printk("DMSDOS: sq_comp: Not enough memory\n");
+ return 0;
+ };
+
+ ch_cn=work_mem->ch_cn;
+ offs_cn=work_mem->offs_cn;
+ ch_blen=work_mem->a.a.ch_blen;
+ code_buf=work_mem->a.a.code_buf;
+ ch_tab=work_mem->a.a.ch_tab;
+ ch_huf=work_mem->a.a.ch_huf;
+ offs_huf=work_mem->a.a.offs_huf;
+ memset(ch_cn,0,sizeof(work_mem->ch_cn));
+ memset(offs_cn,0,sizeof(work_mem->offs_cn));
+
+ /* find repetitions in input data block */
+ lz_length=sq_complz(pin,lin,pout,lout,sq_comp_rat_tab[flg&0xf],
+ ch_cn,offs_cn,work_mem->a.lz_tabs);
+ LOG_DECOMP("DMSDOS: sq_comp: lz_length %d\n",lz_length);
+ if(lz_length==0) {FREE(work_mem);return(0);};
+
+ /* move intermediate data to end of output buffer */
+ lz_pos=(__u8*)pout+lout-lz_length;
+ memmove(lz_pos,pout,lz_length);
+
+ {
+ count_1=0x11E;
+ while(!ch_cn[count_1-1])count_1--;
+ count_2=0x1E;
+ while(!offs_cn[count_2-1]&&(count_2>2))count_2--;
+ /* offset bitlengths are stored exactly after count_1 character bitlengths */
+ offs_blen=ch_blen+count_1;
+ sq_huffman(ch_cn,ch_blen,ch_blcn,count_1,ch_tab);
+ sq_huffman(offs_cn,offs_blen,offs_blcn,count_2,ch_tab);
+
+ LOG_DECOMP("DMSDOS: sq_comp: count_1 %d\n",count_1);
+ LOG_DECOMP("DMSDOS: sq_comp: count_2 %d\n",count_2);
+ }
+
+ {
+ __u8 *pi=ch_blen;
+ __u8 *pe=ch_blen+count_1+count_2;
+ __u8 *po=code_buf;
+ int code, rep;
+
+ for(code=19;code--;) code_cn[code]=0;
+ code=0;
+ while(pi<pe)
+ {
+ if(*pi==0)
+ {
+ code=0;
+ rep=1; pi++;
+ while((pi<pe)&&(rep<138)&&(*pi==0)) {pi++;rep++;}
+ if (rep<=2) {code_cn[0]+=rep; while(rep--) *po++=0;}
+ /* code 17 - 3 to 10 repeats of 0 - (3)+3 */
+ else if(rep<=10) {code_cn[17]++;*po++=17;*po++=rep-3;}
+ /* code 18 - 11 to 139 repeats of 0 - (7)+11 */
+ else {code_cn[18]++;*po++=18;*po++=rep-11;}
+ continue;
+ }
+ if(*pi==code)
+ {
+ rep=1; pi++;
+ while((pi<pe)&&(rep<6)&&(*pi==code)) {pi++;rep++;}
+ if (rep<=2) {code_cn[code]+=rep; while(rep--) *po++=code;}
+ /* code 16 - 3 to 6 repeats of last - (2)+3 */
+ else {code_cn[16]++;*po++=16;*po++=rep-3;}
+ continue;
+ }
+ code=*pi++;
+ *po++=code;
+ code_cn[code]++;
+ };
+ code_buf_len=po-code_buf;
+
+ do{
+ sq_huffman(code_cn,code_blen,code_blcn,19,ch_tab);
+ code=1;
+ /* not elegant way to limit helper table blen by 7 */
+ for(i=0;i<19;i++) if(code_blen[i]>7) code=0;
+ if(code) break;
+ for(i=0;i<19;i++) code_cn[i]=(code_cn[i]+1)>>1;
+ }while(1);
+
+ count_3=19;
+ while(!code_blen[code_index_1[count_3-1]]) count_3--;
+
+ LOG_DECOMP("DMSDOS: sq_comp: count_3 %d\n",count_3);
+ }
+
+ /* prepare output bitstream for writting */
+ sq_wri(&bits,pout,lout);
+
+ sq_wrn(&bits,'S',8);
+ sq_wrn(&bits,'Q',8);
+ sq_wrn(&bits,0,16);
+ sq_wrn(&bits,1,1); /* final flag */
+ sq_wrn(&bits,2,2); /* huffman */
+ sq_wrn(&bits,count_1-0x101,5);
+ sq_wrn(&bits,count_2-1,5);
+ sq_wrn(&bits,count_3-4,4);
+ for(i=0;i<count_3;i++) sq_wrn(&bits,code_blen[code_index_1[i]],3);
+
+ { /* compressed code table write */
+ __u8 *pi=code_buf;
+ __u8 *pe=code_buf+code_buf_len;
+ int code;
+
+ if(sq_wrhufi(ch_huf,code_blen,code_blcn,19))
+ { printk("DMSDOS: sq_comp: Huffman code leakage in table 1\n");
+ FREE(work_mem);return(0);
+ };
+ while(pi<pe)
+ {
+ sq_wrh(&bits,ch_huf,code=*pi++);
+ switch(code)
+ {
+ case 16: sq_wrn(&bits,*pi++,2); break;
+ case 17: sq_wrn(&bits,*pi++,3); break;
+ case 18: sq_wrn(&bits,*pi++,7); break;
+ default: ;
+ }
+ }
+ }
+
+ { /* real data write */
+ int cod;
+ int len;
+ __u8 *pi=(__u8*)pin;
+
+ if(sq_wrhufi(ch_huf,ch_blen,ch_blcn,count_1))
+ { printk("DMSDOS: sq_comp: Huffman code leakage in table 2\n");
+ FREE(work_mem);return(0);
+ };
+ if(sq_wrhufi(offs_huf,offs_blen,offs_blcn,count_2))
+ { printk("DMSDOS: sq_comp: Huffman code leakage in table 3\n");
+ FREE(work_mem);return(0);
+ };
+
+ while((cod=*(lz_pos++))!=TK_END)
+ {
+ if((__u8*)bits.pd+0x20>=lz_pos)
+ {
+ LOG_DECOMP("DMSDOS: sq_comp: Data overwrites intermediate code\n");
+ FREE(work_mem);return 0;
+ };
+ if(cod>=TK_CHRS)
+ { /* characters */
+ len=cod-TK_CHRS;
+ if(len==15) C_LD_u16(lz_pos,len);
+ while(len--) sq_wrh(&bits,ch_huf,*(pi++));
+ }else{ /* tokens */
+ sq_wrh(&bits,ch_huf,cod+0x100);
+ cod--;
+ len=sqt_repbas[cod];
+ if(sqt_repbln[cod])
+ {
+ sq_wrn(&bits,*lz_pos,sqt_repbln[cod]);
+ len+=*(lz_pos++);
+ };
+ pi+=len;
+ cod=*(lz_pos++);
+ sq_wrh(&bits,offs_huf,cod);
+ if(sqt_offbln[cod])
+ {
+ if(sqt_offbln[cod]<=8) sq_wrn(&bits,*(lz_pos++),sqt_offbln[cod]);
+ else { C_LD_u16(lz_pos,len);sq_wrn(&bits,len,sqt_offbln[cod]);};
+ };
+ };
+ }
+ sq_wrh(&bits,ch_huf,TK_END+0x100);
+ sq_wrn(&bits,0,16);
+ if(pi-(__u8*)pin!=lin)
+ {
+ printk("DMSDOS: sq_comp: ERROR: Processed only %d bytes !!!!!!\n",pi-(__u8*)pin);
+ FREE(work_mem);return 0;
+ };
+ FREE(work_mem);
+ return((__u8*)bits.pd-(__u8*)pout);
+ };
+ FREE(work_mem);return 0;
+};
+
+#endif /* DMSDOS_CONFIG_DRVSP3 */
diff --git a/src/lib/dblspace_tables.c b/src/lib/dblspace_tables.c
new file mode 100644
index 0000000..492e3ff
--- /dev/null
+++ b/src/lib/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
diff --git a/src/lib/dmsdos-config.h b/src/lib/dmsdos-config.h
new file mode 100644
index 0000000..08ff27e
--- /dev/null
+++ b/src/lib/dmsdos-config.h
@@ -0,0 +1,24 @@
+/*
+ * Automatically generated C config: don't edit
+ * Run Configure instead
+ */
+#undef DMSDOS_EXPERT
+
+/*
+ * CVF formats to be supported
+ */
+#define DMSDOS_CONFIG_DBLSP_DRVSP 1
+#define DMSDOS_CONFIG_DRVSP3 1
+#define DMSDOS_CONFIG_STAC3 1
+#define DMSDOS_CONFIG_STAC4 1
+
+/*
+ * Misc Options
+ */
+#define DBL_WRITEACCESS 1
+#undef NOLOG
+
+/*
+ * Internal Compression Daemon
+ */
+#undef INTERNAL_DAEMON
diff --git a/src/lib/dmsdos.h b/src/lib/dmsdos.h
new file mode 100644
index 0000000..5606f52
--- /dev/null
+++ b/src/lib/dmsdos.h
@@ -0,0 +1,716 @@
+/*
+dmsdos.h
+
+DMSDOS CVF-FAT module: declaration of dmsdos functions and structures.
+
+******************************************************************************
+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.
+******************************************************************************
+
+*/
+
+#ifndef _DMSDOS_H
+#define _DMSDOS_H
+
+/* version number hacks */
+#define LVC(x,y,z) ((x)*65536+(y)*256+(z))
+
+#ifdef __KERNEL__
+
+#ifndef LINUX_VERSION_CODE
+ #include<linux/version.h>
+#endif
+#if LINUX_VERSION_CODE == LVC(2,2,1)
+ /* this works around a bug in Linux 2.2.1 */
+ #include<asm/page.h>
+#endif
+#include<asm/semaphore.h>
+#include<linux/fs.h>
+#if LINUX_VERSION_CODE < LVC(2,3,3)
+ #define init_MUTEX(sem) (*sem=MUTEX)
+ #define DECLARE_MUTEX(name) struct semaphore name=MUTEX
+ #define DECLARE_WAIT_QUEUE_HEAD(name) \
+ struct wait_queue * name=NULL
+#endif
+
+#if LINUX_VERSION_CODE >= LVC(2,1,78)
+ #define __FOR_KERNEL_2_1_80
+ #if LINUX_VERSION_CODE < LVC(2,1,80)
+ #define READPAGE_DENTRY
+ #else
+ #define FAT_GET_CLUSTER
+ #if LINUX_VERSION_CODE < LVC(2,3,0)
+ #define READPAGE_INODE
+ #else
+ #if LINUX_VERSION_CODE < LVC(2,3,10)
+ #define READPAGE_FILE
+ #else
+ #define __FOR_KERNEL_2_3_10
+ #if LINUX_VERSION_CODE >= LVC(2,3,30)
+ #define __FOR_KERNEL_2_3_30
+ #define READPAGE_DENTRY
+ #define HAS_SB_CLUSTER_BITS
+ #endif
+ #endif
+ #endif
+ #endif
+#endif
+#if (LINUX_VERSION_CODE >= LVC(2,1,0)) && (LINUX_VERSION_CODE < LVC(2,1,78))
+ #error dmsdos 0.9.x needs kernel >= 2.1.80 or use 2.0.33
+#endif
+
+#ifdef FAT_GET_CLUSTER
+#define get_cluster fat_get_cluster
+#endif
+
+#endif /* __KERNEL__*/
+
+#include "dmsdos-config.h"
+/* hacks for new Configure */
+#ifndef DMSDOS_EXPERT
+#define USE_VMALLOC
+#define LISTSIZE 1024
+#define MDFATCACHESIZE 40
+#define DFATCACHESIZE 20
+#define BITFATCACHESIZE 10
+#define MAX_CACHE_TIME 60
+#define CCACHESIZE 64
+#define MAX_CCACHE_TIME 240
+#define MAX_READA 64
+#define USE_READA_LIST
+#define READA_LIST_SIZE 256
+#define READA_THRESHOLD 4095
+#define DEFAULT_LOGLEVEL 0
+#define DEFAULT_CF 11
+#define DMSDOS_USE_READPAGE
+#define IDMSDOSD_TIME 30
+#define SP_BIT0 /* never compress dir */
+#define SP_BIT1 /* never compress EMD */
+#define SP_BIT4 /* write-back caching */
+#define SP_BIT5 /* read-ahead */
+#define SP_BIT7 /* daemon compresses */
+#endif /* DMSDOS_EXPERT */
+
+#ifndef SP_BIT0
+#define SP_BIT0 0
+#else
+#undef SP_BIT0
+#define SP_BIT0 1
+#endif
+
+#ifndef SP_BIT1
+#define SP_BIT1 0
+#else
+#undef SP_BIT1
+#define SP_BIT1 1
+#endif
+
+#ifndef SP_BIT2
+#define SP_BIT2 0
+#else
+#undef SP_BIT2
+#define SP_BIT2 1
+#endif
+
+#ifndef SP_BIT3
+#define SP_BIT3 0
+#else
+#undef SP_BIT3
+#define SP_BIT3 1
+#endif
+
+#ifndef SP_BIT4
+#define SP_BIT4 0
+#else
+#undef SP_BIT4
+#define SP_BIT4 1
+#endif
+
+#ifndef SP_BIT5
+#define SP_BIT5 0
+#else
+#undef SP_BIT5
+#define SP_BIT5 1
+#endif
+
+#ifndef SP_BIT6
+#define SP_BIT6 0
+#else
+#undef SP_BIT6
+#define SP_BIT6 1
+#endif
+
+#ifndef SP_BIT7
+#define SP_BIT7 0
+#else
+#undef SP_BIT7
+#define SP_BIT7 1
+#endif
+
+#ifndef SP_BIT8
+#define SP_BIT8 0
+#else
+#undef SP_BIT8
+#define SP_BIT8 1
+#endif
+
+#ifndef DEFAULT_SPEEDUP
+#define DEFAULT_SPEEDUP ((SP_BIT8<<8)|(SP_BIT7<<7)|(SP_BIT6<<6)|(SP_BIT5<<5)|(SP_BIT4<<4)|(SP_BIT3<<3)|(SP_BIT2<<2)|(SP_BIT1<<1)|(SP_BIT0))
+#endif
+#ifndef DEFAULT_COMP
+#define DEFAULT_COMP GUESS
+#endif
+#ifndef LISTSIZE
+#define LISTSIZE 1024
+#endif
+#ifndef MDFATCACHESIZE
+#define MDFATCACHESIZE 40
+#endif
+#ifndef DFATCACHESIZE
+#define DFATCACHESIZE 20
+#endif
+#ifndef BITFATCACHESIZE
+#define BITFATCACHESIZE 10
+#endif
+#ifndef MAX_CACHE_TIME
+#define MAX_CACHE_TIME 60
+#endif
+#ifndef CCACHESIZE
+#define CCACHESIZE 64
+#endif
+#ifndef MAX_CCACHE_TIME
+#define MAX_CCACHE_TIME 240
+#endif
+#ifndef MAX_READA
+#define MAX_READA 64
+#endif
+#ifndef READA_LIST_SIZE
+#define READA_LIST_SIZE 256
+#endif
+#ifndef READA_THRESHOLD
+#define READA_THRESHOLD 4095
+#endif
+#ifndef DEFAULT_LOGLEVEL
+#define DEFAULT_LOGLEVEL 0
+#endif
+#ifndef DEFAULT_CF
+#define DEFAULT_CF 11
+#endif
+#ifndef IDMSDOSD_TIME
+#define IDMSDOSD_TIME 30
+#endif
+
+
+#define DMSDOS_MAJOR 0
+#define DMSDOS_MINOR 9
+#define DMSDOS_ACT_REL 2
+#define DMSDOS_COMP_REL 2
+#define DMSDOS_PL "2"
+#define DMSDOS_EXTRA "(alpha test)"
+
+#define DMSDOS_VERSION ((DMSDOS_MAJOR<<16)|(DMSDOS_MINOR<<8)|DMSDOS_ACT_REL)
+#define DMSDOS_LOWEST_COMPATIBLE_VERSION ((DMSDOS_MAJOR<<16)|(DMSDOS_MINOR<<8)|DMSDOS_COMP_REL)
+#define DMSDOS_VLT "pl" DMSDOS_PL DMSDOS_EXTRA
+
+/* config hacks */
+#if (defined(DMSDOS_CONFIG_DBLSP_DRVSP) || defined(DMSDOS_CONFIG_DRVSP3))
+#define DMSDOS_CONFIG_DBL
+#endif
+#if (defined(DMSDOS_CONFIG_STAC3) || defined(DMSDOS_CONFIG_STAC4))
+#define DMSDOS_CONFIG_STAC
+#endif
+#if (!defined(DMSDOS_CONFIG_DBL) && !defined(DMSDOS_CONFIG_STAC))
+#error configuration: no CVF format to compile in !!!
+#endif
+
+/* known compression methods */
+#define DS_0_0 0x00005344
+#define DS_0_1 0x01005344
+#define DS_0_2 0x02005344
+#define JM_0_0 0x00004D4A
+/* drivespace 3 high compression */
+#define JM_0_1 0x01004D4A
+/* drivespace 3 ultra compression */
+#define SQ_0_0 0x00005153
+/* stacker 3 compression (no header) */
+#define SD_3 0x00000000
+/* stacker 4 compression */
+#define SD_4 0x00000081
+
+/* other defines for options */
+#define READ_ONLY -1
+#define UNCOMPRESSED -2
+#define GUESS -3
+
+#define MIN_FREE_SECTORS ( (dblsb->s_cvf_version==DRVSP3 \
+ &&(dmsdos_speedup&SP_NO_FRAG_WRITE)==0) \
+ ?dblsb->s_sectperclust+1 \
+ :10*dblsb->s_sectperclust )
+
+typedef struct
+{ unsigned long free_sectors;
+ unsigned long used_sectors;
+ unsigned long max_hole;
+ unsigned long free_clusters;
+ unsigned long used_clusters;
+ unsigned long lost_clusters;
+ unsigned long sectors_lo;
+ unsigned long sectors_hi;
+ unsigned long compressed_clusters;
+ unsigned long uncompressed_clusters;
+} Dblstat;
+
+typedef struct
+{ long sector_minus_1;
+ short size_lo_minus_1;
+ short size_hi_minus_1;
+ short unknown; /* some bits I don't know to handle.... */
+ short flags; /* 00...0uc - u=used, c=compressed */
+} Mdfat_entry;
+
+/* flag values */
+#define D_EMPTY 0
+#define D_VALID 1 /* entry is valid -> cluster to be compressed */
+#define D_IN_D_ACTION 2 /* is being compressed by daemon */
+#define D_OVERWRITTEN 3 /* has been overwritten by dmsdos while daemon
+ is compressing it -> throw away the result from
+ the daemon */
+#ifdef __KERNEL__
+# ifdef USE_XMALLOC
+ void* xmalloc(unsigned long);
+ void xfree(void*);
+# define MALLOC(x) xmalloc(x)
+# define FREE(x) xfree(x)
+# else
+# ifdef USE_VMALLOC
+# include<linux/mm.h>
+# ifdef __FOR_KERNEL_2_1_80
+# include<linux/vmalloc.h>
+# endif
+# define MALLOC(x) vmalloc(x)
+# define FREE(x) vfree(x)
+# else
+# include<linux/malloc.h>
+# define MALLOC(x) kmalloc(x,GFP_KERNEL)
+# define FREE(x) kfree(x)
+# endif
+# endif
+#endif
+
+/* this must be known outside the kernel too */
+typedef struct {
+ int s_dcluster;/*[45-46]*/
+ int s_mdfatstart;/*[36-37]+1*/
+ int s_fatstart;/*[39-40]+[14-15]*/
+ int s_rootdir;/*[41-42]+[39-40]*/
+ int s_rootdirentries;
+ int s_sectperclust;
+ int s_spc_bits;
+ int s_16bitfat;
+ int s_datastart;
+ int s_dataend;
+ int s_comp;
+ int s_bootblock;/*[39-40]*/
+ int s_cfaktor;
+ int s_full;
+ int s_max_cluster;
+ int s_max_cluster2;
+ int s_cvf_version; /* dblsp/drvsp/drvsp3/stac3/stac4 */
+ int s_2nd_fat_offset;
+ int s_lastnear;
+ int s_lastbig;
+ int s_free_sectors;
+ void * mdfat_alloc_semp;
+} Dblsb;
+
+#define DBLSP 0
+#define DRVSP 1
+#define DRVSP3 2
+#define STAC3 3
+#define STAC4 4
+
+#define UC_NORMAL 0
+#define UC_UNCOMPR 1
+#define UC_TEST 2
+#define UC_DIRECT 3
+
+/* cvf version capabilities - boolean values */
+#define DIR_MAY_BE_SHORT(dblsb) (dblsb->s_cvf_version==DRVSP3)
+#define DIR_MAY_BE_COMPRESSED(dblsb) (dblsb->s_cvf_version>=DRVSP3&&(dmsdos_speedup&SP_NO_DIR_COMPR)==0)
+#define UMOUNT_UCFLAG ((dmsdos_speedup&SP_NO_UNMOUNT_COMPR)?UC_UNCOMPR:UC_DIRECT)
+
+typedef struct {
+ struct buffer_head * a_buffer;
+ unsigned int a_area;
+ unsigned long a_time;
+ struct super_block* a_sb;
+ unsigned int a_acc;
+} Acache;
+
+#define C_FREE 0 /* cluster cache entry is free */
+#define C_VALID 1 /* data points to valid cluster data */
+#define C_DIRTY_NORMAL 2 /* like VALID but data need to be written */
+#define C_DIRTY_UNCOMPR 3 /* like VALID but data need to be written */
+#define C_DELETED 4 /* like VALID but last request was a delete */
+#define C_INVALID 5 /* data are junk but valid memory adress */
+#define C_NOT_MALLOCD 6 /* data pointer is not a valid address */
+
+#define C_KEEP_LOCK 1
+#define C_NO_READ 2
+
+#ifdef __KERNEL__
+typedef struct {
+ unsigned int c_time;
+ unsigned int c_flags;
+ unsigned int c_count;
+ unsigned int c_length;
+ unsigned int c_clusternr;
+ struct super_block* c_sb;
+ unsigned char* c_data;
+ struct semaphore c_sem;
+ unsigned int c_errors;
+} Cluster_head;
+#endif /*__KERNEL__*/
+
+int dbl_mdfat_value(struct super_block*sb, int clusternr,
+ Mdfat_entry*new,Mdfat_entry*mde);
+int dbl_fat_nextcluster(struct super_block*sb,int clusternr,int*);
+int dbl_bitfat_value(struct super_block*sb,int sektornr,int*);
+void exit_dbl(struct super_block*sb);
+int find_free_bitfat(struct super_block*sb, int sektornr, int size);
+int dbl_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*,unsigned char*);
+int stac_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*);
+int dbl_compress(unsigned char* clusterk, unsigned char* clusterd,
+ int size, int method,int);
+#if 0
+int stac_compress(void* pin,int lin, void* pout, int lout,
+ int method, int cfaktor);
+#else
+int stac_compress(unsigned char* pin,int lin, unsigned char* pout, int lout,
+ int method, int cfaktor);
+#endif
+int sq_comp(void* pin,int lin, void* pout, int lout, int flg);
+int dbl_decompress(unsigned char*clusterd, unsigned char*clusterk,
+ Mdfat_entry*mde);
+int dmsdos_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag);
+
+#define CHS(i) ( (unsigned short)i[0]|(unsigned short)i[1]<<8 )
+#define CHL(i) ( (unsigned long)i[0]|(unsigned long)i[1]<<8| \
+ (unsigned long)i[2]<<16|(unsigned long)i[3]<<24 )
+
+int dbl_mdfat_cluster2sector(struct super_block*sb,int clusternr);
+int simple_check(struct super_block*sb,int repair);
+void do_spc_init(void);
+void do_spc_exit(void);
+void lock_mdfat_alloc(Dblsb*);
+void unlock_mdfat_alloc(Dblsb*);
+void free_cluster_sectors(struct super_block*sb, int clusternr);
+void stac_special_free(struct super_block*sb, int clusternr);
+int stac_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag);
+int stac_read_cluster(struct super_block*sb,unsigned char*clusterd,
+ int clusternr);
+void free_idle_cache(void);
+void free_idle_ccache(void);
+void ccache_init(void);
+void free_ccache_dev(struct super_block*sb);
+#ifdef __KERNEL__
+Cluster_head* ch_read(struct super_block*sb,int clusternr,int flag);
+Cluster_head* find_in_ccache(struct super_block*sb,
+ int clusternr,Cluster_head**lastfree,
+ Cluster_head**oldest);
+int ch_dirty(Cluster_head*,int near,int ucflag);
+int ch_dirty_locked(Cluster_head*,int near,int ucflag);
+void lock_ch(Cluster_head*);
+void unlock_ch(Cluster_head*);
+void ch_dirty_retry_until_success(Cluster_head*,int near,int ucflag);
+void ch_free(Cluster_head*);
+#endif
+void sync_cluster_cache(int allow_daemon);
+void delete_cache_cluster(struct super_block*sb, int clusternr);
+void log_list_statistics(void);
+void log_ccache_statistics(void);
+void log_found_statistics(void);
+int sq_dec(void* pin,int lin, void* pout, int lout, int flg);
+
+/* Stacker cluster allocation types access */
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+/* structure for walking/working with each sector of cluster */
+typedef struct {
+ struct super_block*sb;
+ int clusternr;
+ int start_sect;
+ int start_len;
+ int flags;
+ int sect_cnt;
+ int compressed;
+ int bytes_in_last;
+ int bytes_in_clust;
+ struct buffer_head *fbh; /* first sector of fragmented clust */
+ /* changes during fragments reads */
+ int fcnt; /* count of unreaded fragments */
+ int flen; /* rest sectors in fragment */
+ int sect; /* actual sector */
+ int offset; /* byte offset in sector */
+ int bytes; /* number of data bytes in sector */
+ unsigned char* finfo; /* points to actual field in fbh */
+} Stac_cwalk;
+
+int stac_cwalk_init(Stac_cwalk *cw,struct super_block*sb,
+ int clusternr,int flg);
+int stac_cwalk_sector(Stac_cwalk *cw);
+void stac_cwalk_done(Stac_cwalk *cw);
+#endif /* __KERNEL__||__DMSDOS_LIB__*/
+
+/* loglevel defines */
+
+extern unsigned long loglevel;
+
+#ifdef SEQLOG
+int log_prseq(void);
+#define LOGCMD if(log_prseq())printk
+#else
+#define LOGCMD printk
+#endif
+
+#ifndef NOLOG
+#define LOG_FS if(loglevel&0x00000001)LOGCMD
+#define LOG_CLUST if(loglevel&0x00000002)LOGCMD
+#define LOG_LLRW if(loglevel&0x00000008)LOGCMD
+#define LOG_DFAT if(loglevel&0x00000010)LOGCMD
+#define LOG_MDFAT if(loglevel&0x00000020)LOGCMD
+#define LOG_BITFAT if(loglevel&0x00000040)LOGCMD
+#define LOG_DECOMP if(loglevel&0x00000080)LOGCMD
+#define LOG_COMP if(loglevel&0x00000100)LOGCMD
+#define LOG_ALLOC if(loglevel&0x00000200)LOGCMD
+#define LOG_DAEMON if(loglevel&0x00000400)LOGCMD
+#define LOG_CCACHE if(loglevel&0x00000800)LOGCMD
+#define LOG_ACACHE if(loglevel&0x00001000)LOGCMD
+#define LOG_REST if(loglevel&0x80000000)LOGCMD
+#else
+#define LOG_FS(x,args...)
+#define LOG_CLUST(x,args...)
+#define LOG_LLRW(x,args...)
+#define LOG_DFAT(x,args...)
+#define LOG_MDFAT(x,args...)
+#define LOG_BITFAT(x,args...)
+#define LOG_DECOMP(x,args...)
+#define LOG_COMP(x,args...)
+#define LOG_ALLOC(x,args...)
+#define LOG_DAEMON(x,args...)
+#define LOG_CCACHE(x,args...)
+#define LOG_ACACHE(x,args...)
+#define LOG_REST(x,args...)
+#endif
+
+#ifdef __FOR_KERNEL_2_1_80
+/* some hacks since the memcpy_from/tofs functions have changed here */
+#include <asm/uaccess.h>
+#define memcpy_fromfs copy_from_user
+#define memcpy_tofs copy_to_user
+#endif
+
+struct buffer_head *raw_bread (
+ struct super_block *sb,
+ int block);
+struct buffer_head *raw_getblk (
+ struct super_block *sb,
+ int block);
+void raw_brelse (
+ struct super_block *sb,
+ struct buffer_head *bh);
+void raw_mark_buffer_dirty (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int dirty_val);
+void raw_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val);
+int raw_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh);
+void raw_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32]);
+
+#define FAKED_ROOT_DIR_OFFSET 1
+#define FAKED_DATA_START_OFFSET 1000
+int dmsdos_read_cluster(struct super_block*sb,
+ unsigned char*clusterd, int clusternr);
+
+struct buffer_head* dblspace_bread(struct super_block*sb,int vsector);
+struct buffer_head *dblspace_getblk (
+ struct super_block *sb,
+ int block);
+void dblspace_brelse(struct super_block* sb,struct buffer_head*bh);
+void dblspace_mark_buffer_dirty(struct super_block*sb,struct buffer_head*bh,
+ int dirty_val);
+void dblspace_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val);
+int dblspace_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh);
+void dblspace_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32]);
+int stac_bitfat_state(struct super_block*sb,int new_state);
+int dblspace_fat_access(struct super_block*sb, int clusternr,int newval);
+#ifdef __KERNEL__
+int dblspace_bmap(struct inode*inode, int block);
+int dblspace_smap(struct inode*inode, int block);
+#endif
+int ds_dec(void* pin,int lin, void* pout, int lout, int flg);
+#ifdef __KERNEL__
+void dblspace_zero_new_cluster(struct inode*,int clusternr);
+#ifdef __FOR_KERNEL_2_1_80
+ssize_t dblspace_file_read(struct file *filp,char *buf,size_t count,
+ loff_t *ppos);
+ssize_t dblspace_file_write(struct file *filp,const char *buf,size_t count,
+ loff_t *ppos);
+int dblspace_mmap(struct file*file,
+ struct vm_area_struct*vma);
+#else
+int dblspace_file_read(struct inode *inode,struct file *filp,char *buf,
+ int count);
+int dblspace_file_write(struct inode *inode,struct file *filp,const char *buf,
+ int count);
+int dblspace_mmap(struct inode*inode,struct file*file,
+ struct vm_area_struct*vma);
+#endif
+
+#ifdef READPAGE_DENTRY
+ int dblspace_readpage(struct dentry*dentry, struct page *page);
+#else
+ #ifdef READPAGE_FILE
+ int dblspace_readpage(struct file *file, struct page *page);
+ #else
+ #ifdef READPAGE_INODE
+ int dblspace_readpage(struct inode *inode, struct page *page);
+ #else
+ #error Unknown readpage parameters
+ #endif
+ #endif
+#endif
+
+int dmsdos_ioctl_dir(struct inode *dir,struct file *filp,
+ unsigned int cmd, unsigned long data);
+#endif /* __KERNEL__ */
+int try_daemon(struct super_block*sb,int clusternr, int length, int method);
+void remove_from_daemon_list(struct super_block*sb,int clusternr);
+void force_exit_daemon(void);
+void dblspace_reada(struct super_block*sb, int sector,int count);
+void init_reada_list(void);
+void kill_reada_list_dev(int dev);
+int daemon_write_cluster(struct super_block*sb,unsigned char*data,
+ int len, int clusternr, int rawlen);
+void check_free_sectors(struct super_block*sb);
+void get_memory_usage_acache(int*, int*max);
+void get_memory_usage_ccache(int*, int*max);
+int mount_dblspace(struct super_block*sb,char*options);
+int mount_stacker(struct super_block*sb,char*options);
+int detect_dblspace(struct super_block*sb);
+int detect_stacker(struct super_block*sb);
+int unmount_dblspace(struct super_block*sb);
+
+typedef struct
+{ int clusternr;
+ struct super_block*sb;
+ int length; /* in bytes */
+ char flag;
+ int method;
+} Rwlist;
+
+void init_daemon(void);
+void exit_daemon(void);
+void clear_list_dev(struct super_block*sb);
+
+/* speedup bits */
+#define SP_NO_DIR_COMPR 0x0001
+#define SP_NO_EMD_COMPR 0x0002
+#define SP_NO_EXACT_SEARCH 0x0004
+#define SP_NO_UNMOUNT_COMPR 0x0008
+#define SP_USE_WRITE_BACK 0x0010
+#define SP_USE_READ_AHEAD 0x0020
+#define SP_FAST_BITFAT_ALLOC 0x0040
+#define SP_USE_DAEMON 0x0080
+#define SP_NO_FRAG_WRITE 0x0100
+
+typedef struct
+{ int ccachebytes;
+ int max_ccachebytes;
+ int acachebytes;
+ int max_acachebytes;
+} Memuse;
+
+
+#define DMSDOS_IOCTL_MIN 0x2000
+#define DMSDOS_IOCTL_MAX 0x201F
+#define DMSDOS_GET_DBLSB 0x2000
+#define DMSDOS_EXTRA_STATFS 0x2001
+#define DMSDOS_READ_BLOCK 0x2002
+#define DMSDOS_WRITE_BLOCK 0x2003
+#define DMSDOS_READ_DIRENTRY 0x2004 /* obsolete */
+#define DMSDOS_WRITE_DIRENTRY 0x2005 /* obsolete */
+#define DMSDOS_READ_BITFAT 0x2006
+#define DMSDOS_WRITE_BITFAT 0x2007
+#define DMSDOS_READ_MDFAT 0x2008
+#define DMSDOS_WRITE_MDFAT 0x2009
+#define DMSDOS_READ_DFAT 0x200a
+#define DMSDOS_WRITE_DFAT 0x200b
+#define DMSDOS_SET_COMP 0x200c
+#define DMSDOS_SET_CF 0x200d
+#define DMSDOS_SIMPLE_CHECK 0x200e
+#define DMSDOS_DUMPCACHE 0x200f
+#define DMSDOS_D_ASK 0x2010
+#define DMSDOS_D_READ 0x2011
+#define DMSDOS_D_WRITE 0x2012
+#define DMSDOS_D_EXIT 0x2013
+#define DMSDOS_MOVEBACK 0x2014 /* obsolete */
+#define DMSDOS_SET_MAXCLUSTER 0x2015 /* currently not supported */
+#define DMSDOS_READ_CLUSTER 0x2016
+#define DMSDOS_FREE_IDLE_CACHE 0x2017
+#define DMSDOS_SET_LOGLEVEL 0x2018
+#define DMSDOS_SYNC_CCACHE 0x2019
+#define DMSDOS_LOG_STATISTICS 0x201a
+#define DMSDOS_SET_SPEEDUP 0x201b
+#define DMSDOS_RECOMPRESS 0x201c /* obsolete */
+#define DMSDOS_REPORT_MEMORY 0x201d
+#define IS_DMSDOS_IOCTL(cmd) ((cmd)>=DMSDOS_IOCTL_MIN&&(cmd)<=DMSDOS_IOCTL_MAX)
+
+/* dmsdos library interface */
+struct super_block* open_cvf(char*filename,int rwflag);
+void close_cvf(struct super_block*sb);
+
+#endif
diff --git a/src/lib/dstacker_alloc.c b/src/lib/dstacker_alloc.c
new file mode 100644
index 0000000..a0b3762
--- /dev/null
+++ b/src/lib/dstacker_alloc.c
@@ -0,0 +1,477 @@
+/*
+dstacker_alloc.c
+
+DMSDOS CVF-FAT module: stacker allocation routines.
+
+******************************************************************************
+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>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<errno.h>
+#endif
+
+#ifdef DMSDOS_CONFIG_STAC
+
+/* initializes Stac_cwalk structure, which can be used for sequential
+ access to all sectors of cluster and when needed informs about
+ all data areas in every sector. flg parameter speedups initialization
+ when some informations are not necessarry
+ flg = 0 .. only sector numbers
+ flg = 1 .. sector numbers and data without ending suballocation
+ flg = 2 .. sector numbers and exact data areas
+ flg = 3 .. same as 2 but more checking
+*/
+int stac_cwalk_init(Stac_cwalk *cw,struct super_block*sb,
+ int clusternr,int flg)
+{
+ __u8 *pp;
+ unsigned u,v;
+ int i;
+ int last_sect;
+ Mdfat_entry mde;
+ struct buffer_head *bh;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ cw->finfo=NULL;
+ cw->fbh=NULL;
+ cw->sect=0;
+ cw->sb=sb;
+ cw->clusternr=clusternr;
+ /* -------------------------------------------- */
+ dbl_mdfat_value(sb,clusternr,NULL,&mde);
+ cw->start_sect=mde.sector_minus_1+1;
+ cw->sect_cnt=cw->start_len=mde.size_lo_minus_1+1;
+ cw->flags=mde.flags;
+ if(!cw->start_sect) {cw->fcnt=0;return 0;};
+ /* -------------------------------------------- */
+ cw->fcnt=1;
+ cw->flen=cw->sect_cnt=cw->start_len;
+ cw->bytes_in_clust=cw->sect_cnt*SECTOR_SIZE;
+ cw->offset=0;
+ cw->bytes=SECTOR_SIZE;
+ cw->bytes_in_last=0;
+ last_sect=cw->start_sect+cw->sect_cnt-1;
+ /* -------------------------------------------- */
+ u=(cw->flags&0xe0)>>5;
+ if(cw->start_len==dblsb->s_sectperclust) u|=0x8;
+ switch(u)
+ { case 0x0: /* ffff=000x, len<ClustSize, compressed, regular file/dir */
+ case 0x2: /* ffff=010x, deleted 0 */
+ cw->compressed=1;
+ break;
+
+ case 0x1: /* ffff=001x, not compressed but not full size, regular file/dir */
+ case 0x3: /* ffff=011x, deleted 1 */
+ case 0x8: /* ffff=000x, len=ClustSize, not compressed, regular file/dir */
+ case 0xa: /* ffff=010x, deleted 8 */
+ case 0xc: /* ffff=100x, len=ClustSize, directory, never compressed */
+ case 0xe: /* ffff=110x, deleted c */
+ cw->compressed=0;
+ break;
+
+ case 0x4: /* ffff=100x, len<ClustSize, fragmented, regular file/dir */
+ case 0x6: /* ffff=110x, deleted 4 */
+ bh=raw_bread(sb,cw->start_sect);
+ if(bh==NULL)return -1;
+ pp=bh->b_data;
+ if(pp[0]!=0xed) /* check fragment signature */
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: fragment signature not found cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ cw->fcnt=pp[1]+1; /* number of pieces */
+ if((cw->fcnt>dblsb->s_sectperclust)||(cw->fcnt*4>SECTOR_SIZE))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: too much fragmented cluster=%d!\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ };
+ cw->compressed=!(pp[2]&0x80);
+ v=cw->sect_cnt;
+ cw->sect_cnt+=(pp[2]&0x3F)+1;
+ for(i=1;i<cw->fcnt;++i)
+ { pp+=4;
+ u=(pp[2]&0xf)+((pp[3]>>2)&0x30);
+ v+=u+1;
+ };
+ last_sect=pp[0]+(pp[1]<<8)+((pp[3]&0x3f)<<16)+u; /* for suballocation tests */
+ if(v!=cw->sect_cnt)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: sector count mismash fragmented cluster=%d!\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ cw->fbh=bh;
+ cw->finfo=bh->b_data+4;
+ cw->offset=4*cw->fcnt;
+ cw->bytes=SECTOR_SIZE-cw->offset;
+ cw->bytes_in_clust=(cw->sect_cnt-1)*SECTOR_SIZE+cw->bytes;
+ break;
+
+ case 0x5: /* ffff=101x, len<ClustSize, suballocated, regular file/dir(?) */
+ case 0x7: /* ffff=111x, deleted 5 */
+ case 0xd: /* ffff=101x, len=ClustSize, suballocated, regular file/dir(?) */
+ case 0xf: /* ffff=111x, deleted d */
+ if(flg==0) {cw->bytes_in_clust=0;cw->bytes=0;return 1;};
+ bh=raw_bread(sb,cw->start_sect);
+ if(bh==NULL)return -1;
+ pp=&(bh->b_data[SECTOR_SIZE-2]);
+ if(CHS(pp)!=0x1234)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation signature not found cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ if(cw->start_len==1)
+ { /* short suballocation */
+ pp = &(bh->b_data[SECTOR_SIZE-6]);
+ cw->offset= CHS(pp) & (SECTOR_SIZE - 1); /* begin of area */
+ cw->compressed= !(CHS(pp) & 0x8000);
+ if(CHS(pp)&0x4000)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation not present, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh); return -1;
+ };
+ pp = &(bh->b_data[SECTOR_SIZE-8]);
+ cw->bytes=CHS(pp) & (SECTOR_SIZE - 1); /* end of area */
+ /* some more paranoic checking of allocated space */
+ if (cw->bytes&&(cw->bytes<=SECTOR_SIZE-8)&&(cw->bytes>cw->offset))
+ cw->bytes-=cw->offset;
+ else
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: count = %d < 0 in short subalocated\n", cw->bytes);
+ printk(KERN_ERR "DMSDOS: cluster %d read error\n",clusternr);
+ raw_brelse(sb,bh); return -1;
+ }
+ cw->bytes_in_clust=cw->bytes;
+ last_sect=0;
+ }
+ else
+ { /* long suballocation */
+ pp = &(bh->b_data[SECTOR_SIZE-8]);
+ cw->offset = CHS(pp) & (SECTOR_SIZE - 1);
+ cw->compressed = !(CHS(pp) & 0x8000);
+ if(CHS(pp)&0x4000)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation not present, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh); return -1;
+ };
+ cw->bytes=SECTOR_SIZE-cw->offset-8;
+ cw->bytes_in_clust+=cw->bytes-SECTOR_SIZE;
+ if (cw->bytes < 0) {
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: count = %d < 0 in long subalocated\n", cw->bytes);
+ printk(KERN_ERR "DMSDOS: cluster %d read error\n",clusternr);
+ raw_brelse(sb,bh); return -1;
+ }
+ };
+ raw_brelse(sb,bh);
+ break;
+
+ case 0x9: /* ffff=001x, contradiction ??? */
+ case 0xb: /* ffff=011x, deleted 9 */
+ default:
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: unknown flags 0x%2x cluster %d\n",
+ mde.flags,clusternr);
+ return -1;
+ };
+
+ /* if flg>=2 we are checking subalocated end of cluster */
+ /* because of we did not know if stacker can subalocate clusters
+ which are not in order we must check nonlinear
+ suballocation */
+ /* text above is question but now I know that stacker is able to do everything
+ nonlinear suballocation regulary exist */
+ if(last_sect&&(dblsb->s_cvf_version>=STAC4)&&(flg>=2))
+ { /* check for subalocated end of cluster */
+ /* 1) check start of next cluster for linear subalocation */
+ if(clusternr<dblsb->s_max_cluster)
+ { dbl_mdfat_value(sb,clusternr+1,NULL,&mde);
+ i=(mde.sector_minus_1+1)==last_sect;
+ if(i&&((mde.flags&0xA0)!=0xA0))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: wrong cluster types for subalocation, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ } else i=0;
+ /* 2) check for nonlinear subalocation */
+ if((u=dbl_bitfat_value(sb,last_sect,NULL))==0) if(flg>=3)goto error1;
+ if((u>1)||(i))
+ { if((u<=1)&&(flg>=3))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 1, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ if(!i)LOG_ALLOC("DMSDOS: stac_cwalk_init: nonlinear suballocation, cluster %d\n",
+ clusternr);
+ /* now we know that our cluster is subalocated, we must find
+ number of bytes in last sector of cluster */
+ bh=raw_bread(sb,last_sect);
+ if(bh==NULL)return -1;
+ pp=&(bh->b_data[SECTOR_SIZE-2]);
+ if(CHS(pp)!=0x1234)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 2, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ };
+ pp = &(bh->b_data[SECTOR_SIZE-6]); /* begin of short suball. clust */
+ u = CHS(pp) & (SECTOR_SIZE - 1);
+ pp = &(bh->b_data[SECTOR_SIZE-8]); /* begin of long suball. clust */
+ v = CHS(pp) & (SECTOR_SIZE - 1);
+ raw_brelse(sb,bh);
+ /* u contains number of bytes of our cluster in last_sect */
+ if(v<u)
+ {
+ pp = &(bh->b_data[SECTOR_SIZE-6]);u = CHS(pp);
+ pp = &(bh->b_data[SECTOR_SIZE-8]);v = CHS(pp);
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 3, cluster %d, zerro offset 0x%X 0x%X\n",
+ clusternr,u,v);
+ return -1;
+ };
+ cw->bytes_in_last=u;
+ if(cw->sect_cnt>1)
+ cw->bytes_in_clust-=SECTOR_SIZE-u;
+ else if((i=cw->bytes+cw->offset-cw->bytes_in_last)>0)
+ { if((cw->bytes-=i)<=0)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 4, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ cw->bytes_in_clust-=i;
+ };
+ cw->bytes_in_last|=0x4000;
+ };
+ };
+
+ if ((i=cw->bytes_in_clust-dblsb->s_sectperclust*SECTOR_SIZE)>0)
+ { cw->bytes_in_clust-=i;
+ if(!cw->bytes_in_last) cw->bytes_in_last=SECTOR_SIZE-i;
+ else if((cw->bytes_in_last-=i)<0x4000)
+ { error1:
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: bad bytes_in_cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ };
+
+ return cw->bytes_in_clust;
+};
+
+/* returns in order all sectors of cluster */
+/* in Stac_cwalk updates fields sect, offset and bytes */
+int stac_cwalk_sector(Stac_cwalk *cw)
+{ if(!cw->sect)
+ { if(!cw->fcnt) return 0;
+ cw->fcnt--;
+ cw->flen--;
+ cw->sect=cw->start_sect;
+ }
+ else
+ { if(!cw->flen)
+ { if(!cw->fcnt) return 0;
+ cw->fcnt--;
+ if(cw->finfo==NULL)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_sector: finfo==NULL, cluster %d\n",
+ cw->clusternr);
+ return 0;
+ };
+ cw->sect=cw->finfo[0]+(cw->finfo[1]<<8)+((cw->finfo[3]&0x3f)<<16);
+ cw->flen=(cw->finfo[2]&0xf)+((cw->finfo[3]>>2)&0x30);
+ cw->finfo+=4;
+ }
+ else
+ { cw->sect++;
+ cw->flen--;
+ };
+ cw->offset=0;
+ if(!cw->flen&&!cw->fcnt&&cw->bytes_in_last)
+ cw->bytes=cw->bytes_in_last&(SECTOR_SIZE-1);
+ else cw->bytes=SECTOR_SIZE;
+ };
+ return cw->sect;
+};
+
+void stac_cwalk_done(Stac_cwalk *cw)
+{
+ if(cw->fbh!=NULL) raw_brelse(cw->sb,cw->fbh);
+};
+
+
+void stac_special_free(struct super_block*sb, int clusternr)
+{
+ int val;
+ int sect;
+ Mdfat_entry newmde,dummy_mde;
+ Stac_cwalk cw;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ val=stac_cwalk_init(&cw,sb,clusternr,0);
+ if(val<=0)
+ { if(val<0)
+ printk(KERN_ERR "DMSDOS: stac_special_free: alloc error in cluster %d\n", clusternr);
+ else
+ LOG_CLUST("DMSDOS: stac_special_free: already free cluster %d\n", clusternr);
+ return;
+ };
+ newmde.sector_minus_1=-1;
+ newmde.size_lo_minus_1=0;
+ newmde.size_hi_minus_1=0;
+ newmde.flags=0;
+
+ dbl_mdfat_value(sb,clusternr,&newmde,&dummy_mde);
+ if((cw.flags&0xA0)==0xA0)
+ { /* mark part of suballocated sector as free */
+ struct buffer_head *bh;
+ bh=raw_bread(sb,cw.start_sect);
+ if(bh!=NULL)
+ { if(cw.start_len==1)
+ bh->b_data[SECTOR_SIZE-6+1] |= 0x40;
+ else
+ bh->b_data[SECTOR_SIZE-8+1] |= 0x40;
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ /* free sectors from BITFAT */
+ while((sect=stac_cwalk_sector(&cw))>0)
+ {
+ val=dbl_bitfat_value(sb,sect,NULL);
+ if(val>0)
+ { --val;
+ dbl_bitfat_value(sb,sect,&val);
+ dblsb->s_full=0;
+ /* adapt s_free_sectors, -1 unknown */
+ /*if(val==0&&dblsb->s_free_sectors>=0) dblsb->s_free_sectors++;*/
+ /* Hi Pavel,
+ I have commented this out since free sector count is now
+ maintained in dbl_bitfat_value.
+ */
+ } else LOG_CLUST("DMSDOS: stac_special_free: sector not alocated\n");
+ }
+
+ stac_cwalk_done(&cw);
+
+}
+
+/* replaces an existing cluster for stacker;
+ this unusual function must be called before rewriting any file cluster;
+ *** size must be known (encoded in mde) ***
+ it does nothing if called too often;
+ returns first sector nr
+*/
+
+int stac_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*mde)
+{ Mdfat_entry old_mde,new_mde,dummy;
+ int i;
+ int newval;
+ int sector;
+ int old_sector;
+ int old_size;
+ int new_size;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ lock_mdfat_alloc(dblsb);
+
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster cluster=%d near_sector=%d\n",
+ cluster,near_sector);
+ dbl_mdfat_value(sb,cluster,NULL,&old_mde);
+ old_size=old_mde.size_lo_minus_1+1;
+ old_sector=old_mde.sector_minus_1+1;
+ new_size=mde->size_lo_minus_1+1;
+ if(old_mde.flags&2)
+ {
+ /* stacker routines always replace mdfat entry */
+ newval=0;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: freeing old sectors...\n");
+ stac_special_free(sb,cluster);
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: freeing finished\n");
+ }
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: call find_free_bitfat...\n");
+ sector=find_free_bitfat(sb,near_sector,new_size);
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: find_free_bitfat returned %d\n",
+ sector);
+ if(sector<=0)
+ { if(old_mde.flags&2)
+ { /* Stacker routines don't have an undo list for now.
+ We cannot restore the state before. Sorry data are lost now. */
+ new_mde.sector_minus_1=0;
+ new_mde.size_lo_minus_1=0;
+ new_mde.size_hi_minus_1=0;
+ new_mde.flags=mde->flags=0;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: deleting mdfat entry...\n");
+ dbl_mdfat_value(sb,cluster,&new_mde,&dummy);
+ }
+ unlock_mdfat_alloc(dblsb);
+ return -ENOSPC; /* disk full */
+ }
+ /* check whether really free (bug supposed in find_free_bitfat) */
+ for(i=0;i<new_size;++i)
+ { if(dbl_bitfat_value(sb,sector+i,NULL))
+ { printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sector %d size %d but they are not all free!\n",
+ sector,new_size);
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: stac_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ }
+ newval=1;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: allocating in bitfat...\n");
+ for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sector+i,&newval);
+
+ new_mde.sector_minus_1=sector-1;
+ new_mde.size_lo_minus_1=mde->size_lo_minus_1;
+ new_mde.size_hi_minus_1=mde->size_hi_minus_1;
+ new_mde.flags=mde->flags|2;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: writing mdfat...\n");
+ dbl_mdfat_value(sb,cluster,&new_mde,&dummy);
+ unlock_mdfat_alloc(dblsb);
+ return sector; /* okay */
+}
+
+#endif /* DMSDOS_CONFIG_STAC */
diff --git a/src/lib/dstacker_compr.c b/src/lib/dstacker_compr.c
new file mode 100644
index 0000000..6c44930
--- /dev/null
+++ b/src/lib/dstacker_compr.c
@@ -0,0 +1,1235 @@
+/*
+dstacker_compr.c
+
+DMSDOS CVF-FAT module: stacker compression routines.
+
+******************************************************************************
+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/byteorder.h>
+#include <asm/unaligned.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<string.h>
+#include<errno.h>
+#endif
+
+
+#ifdef DMSDOS_CONFIG_STAC
+
+#ifdef __DMSDOS_DAEMON__
+#include<malloc.h>
+#include<string.h>
+#include<asm/unaligned.h>
+#include<asm/types.h>
+#include <asm/byteorder.h>
+#define MALLOC malloc
+#define FREE free
+int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2)));
+#endif
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers may not like inline */
+#define INLINE static
+#endif
+
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+
+#if defined(__GNUC__) && defined(__i386__) && defined(USE_ASM)
+#define USE_GNU_ASM_i386
+
+/* compare blocks, overlaping repeat test */
+/* pointers and counter are modified to point after block */
+/* D and S points to first diff adr */
+#define M_FIRSTDIFF(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "cmpsb\n\t" \
+ "je 1f\n\t" \
+ "dec %0\n\t" \
+ "dec %1\n\t" \
+ "1:\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ )
+
+INLINE __u16 swap_bytes_in_word(__u16 x)
+ {
+ __asm__("xchgb %b0,%h0" /* swap bytes */
+ : "=q" (x)
+ : "0" (x));
+ return x;
+ }
+
+#else
+
+#ifdef __GNUC__
+/* non-gnu compilers may not like warning directive */
+#warning USE_GNU_ASM_I386 not defined, using "C" equivalent
+#endif
+
+#define M_FIRSTDIFF(D,S,C) for(;(*(__u8*)(D)==*(__u8*)(S))&&(C);\
+ (__u8*)(D)++,(__u8*)(S)++,(C)--)
+
+INLINE __u16 swap_bytes_in_word(__u16 x)
+ {
+ return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
+ }
+
+#endif
+
+#if !defined(cpu_to_le16)
+ /* for old kernel versions - works only on i386 */
+ #define le16_to_cpu(v) (v)
+ #define cpu_to_le16(v) (v)
+ #define be16_to_cpu(v) (swap_bytes_in_word(v))
+ #define cpu_to_be16(v) (swap_bytes_in_word(v))
+#endif
+
+/* store and load __u16 in any byteorder on any */
+/* address (odd or even). */
+/* this is problematic on architectures, */
+/* which cannot do __u16 access to odd address. */
+/* used for temporary storage of LZ intercode. */
+//inline unsigned c_ld_u16(__u8* p) {
+// return get_unaligned((__u16*)p);
+//}
+
+/* for reading and writting from/to bitstream */
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* not read bits count in buffer */
+ __u16 *pd; /* first not readed input data */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+/*************************************************************************/
+/*************************************************************************/
+/*************** begin code from sd4_bs1.c *******************************/
+
+typedef unsigned int count_t;
+typedef __u8* hash_t;
+typedef
+ struct {
+ count_t cn;
+ unsigned ch;
+ } ch_tab_t;
+
+typedef
+ struct {
+ __u16 cod[0x180]; /* characters codes */
+ __u8 ln[0x180]; /* characters lens */
+ }huf_wr_t;
+
+#ifdef MAX_COMP
+ #define MAX_HASH_HIST 0x1000
+#else
+ #define MAX_HASH_HIST 0x800
+#endif
+
+#define TK_END 0x4F
+#define TK_CHRS 0xF0
+#define TKWR_CHRS(p,v) {if(v<15) *(p++)=TK_CHRS+(__u8)v;\
+ else {*(p++)=TK_CHRS+15;C_ST_u16(p,v);};}
+
+/* compression level table */
+ const unsigned comp_rat_tab[]=
+ { /*0*/ 0x7F9,0x7F9,0x621,0x625,
+ /*4*/ 0x665,0x669,0x6E9,0x6ED,
+ /*8*/ 0x7D1,0x7D9,0x6E9,0x47D9,
+ /*12*/0x46E9};
+
+/* token decoding tables */
+ const unsigned int sd4b_prog_len[]={ 5, 7, 9, 11};
+ const unsigned int sd4b_prog_add[]={ 0x1,0x21,0xA1,0x2A1};
+ const signed char sd4b_reps_div3[]={0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,
+ 6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,
+ 15,15,15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20};
+ const signed char sd4b_reps_n[] = {3-1,3-4,3-7,3-10,3-13,3-16,3-19,3-22,
+ 3-25,3-28,3-31,3-34,3-37,3-40,3-43,3-46,3-49,3-52,3-55,3-58,3-61};
+ const unsigned char sd4b_reps_b[] = {0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9};
+ const unsigned int sd4b_reps_m[] = {1,2,3,4,6,8,12,16,24,32,48,64,96,128,
+ 192,256,384,512,768,1024,1536};
+
+#if 0
+
+INLINE unsigned sd4_hash(__u8 *p)
+{
+ unsigned a;
+ /*a=(p[0]>>1)^(p[1]<<7)^(p[1]>>4)^(p[2]<<2);*/
+ a =p[1]; a<<=5;
+ a^=p[2]; a<<=2+6;
+ a^=p[1]; a>>=5;
+ a^=p[0]; a>>=1;
+ return a&0x3FF;
+};
+
+#else
+
+INLINE unsigned sd4_hash(__u8 *p)
+{
+ return (((__u16)p[0]<<2)^((__u16)p[1]<<4)^(__u16)p[2])&0x3FF;
+};
+
+#endif
+
+INLINE hash_t sd4_newhash(__u8 *p,hash_t* hash_tab,hash_t* hash_hist,unsigned hash_mask)
+{
+ hash_t* hash_ptr;
+ hash_t hash_cur;
+ hash_ptr=hash_tab+sd4_hash(p);
+ hash_cur=*hash_ptr;
+ *hash_ptr=p;
+ *(hash_hist+((unsigned)p&hash_mask))=hash_cur;
+ return(hash_cur);
+};
+
+/* finds repetitions in *pin and writes intermediate code to *pout */
+unsigned sd4_complz(void* pin,int lin,void* pout,int lout,int flg,count_t* ch_cn)
+{
+ void *work_mem;
+ int try_count; /* number of compares to find best match */
+ int hash_skiped; /* last bytes of repetition are hashed too */
+ hash_t *hash_tab; /* pointers to last occurrence of same hash, index hash */
+ hash_t *hash_hist;
+ /* previous occurences of hash, index actual pointer&hash_mask */
+ unsigned hash_mask=0x7FF;/* mask for index into hash_hist */
+ __u8 *pi, *po, *pc, *pd, *pend, *poend;
+ hash_t hash_cur;
+ unsigned cn;
+ unsigned max_match, match, token;
+ int try_cn;
+ hash_t max_hash=NULL;
+ #ifdef MAX_COMP
+ int delay_count=0; /* test next # characters for better match */
+ int delay_cn;
+ int delay_best;
+ #endif
+
+ try_count=2<<((flg>>2)&0x7); /* number of tested entries with same hash */
+ hash_skiped=((flg>>5)+1)&0xF;/* when rep found last # bytes are procesed */
+
+ #ifdef MAX_COMP
+ if(flg&0x4000)
+ {
+ hash_mask=MAX_HASH_HIST-1; /* maximal compression */
+ delay_count=2;
+ try_count*=4;
+ hash_skiped*=4;
+ };
+ #endif
+
+ /* stack is small in kernel space, using MALLOC */
+ work_mem=MALLOC((0x400+hash_mask+1)*sizeof(hash_t));
+ if(work_mem==NULL) return 0;
+ hash_tab =(hash_t*)work_mem;
+ hash_hist=((hash_t*)work_mem)+0x400;
+
+ pi=(__u8*)pin;
+ po=(__u8*)pout;
+ if(!lin) goto return_error;
+ pend=pi+(lin-1);
+
+ /*
+ printk("There we are,work_mem=%X hash_hist=%X pin=%X lin=%X pend=%X pout=%X\n",
+ work_mem,hash_hist,pin,lin,pend,pout);
+ */
+
+ if(lout<0x20) goto return_error; /* some minimal space for lz interform buffer */
+ poend=po+(lout-0x20);
+ for(cn=0;cn<0x400;cn++) hash_tab[cn]=pend; /* none ocurence of hash */
+ for(cn=0;cn<=hash_mask;cn++) hash_hist[cn]=pend; /* should not be be needed */
+ pend--; /* last two bytes cannot be hashed */
+ cn=0;
+ while(pi<pend)
+ {
+ hash_cur=sd4_newhash(pi,hash_tab,hash_hist,hash_mask);
+ /* goto single_char; */ /* to by pass LZ for tests */
+ if(hash_cur>=pi) goto single_char;
+ try_cn=try_count;
+ max_match=2; /* minimal coded match-1 */
+ do{
+ if(pi-hash_cur>=0xAA0) break; /* longer offsets are not allowed */
+ if((hash_cur[0]==pi[0])&&(hash_cur[1]==pi[1])&&
+ /* pi[2]=hash_cur[2] from hash function */
+ (hash_cur[max_match-1]==pi[max_match-1])&&
+ (hash_cur[max_match]==pi[max_match]))
+ {
+ match=pend-pi; /* length of rest of data */
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match);/* compare */
+ match=pd-pi; /* found match length */
+ if((match>max_match)&&((match>=6)||(pi-hash_cur<0x800)))
+ {
+ max_hash=hash_cur; /* find maximal hash */
+ max_match=match;
+ if(pd>pend+1)break; /* match to end of block */
+ };
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hash_mask])<pc));
+ if(max_match<3) goto single_char;
+
+ #ifdef MAX_COMP
+ /* tests if better matchs on next characters */
+ delay_cn=0;
+ if(delay_count)
+ {
+ delay_best=0;
+ while((delay_cn<delay_count)&&(pi+max_match<pend)&&
+ (max_match<0x100))
+ {
+ pi++;delay_cn++;
+ hash_cur=sd4_newhash(pi,hash_tab,hash_hist,hash_mask);
+ try_cn=try_count;
+ if (hash_cur<pi) do
+ {
+ if(pi-hash_cur>=0xAA0) break; /* longer offsets are not allowed */
+ if((hash_cur[0]==pi[0])&&(hash_cur[1]==pi[1])&&
+ /* pi[2]=hash_cur[2] from hash function */
+ (hash_cur[max_match-1]==pi[max_match-1])&&
+ (hash_cur[max_match]==pi[max_match])&&
+ (hash_cur!=max_hash+delay_cn-delay_best))
+ { /* do not test actual max match */
+ match=pend-pi; /* length of rest of data */
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match);/* compare */
+ match=pd-pi; /* found match length */
+ if((match>max_match+delay_cn)&&((match>=6)||(pi-hash_cur<0x800)))
+ {
+ max_hash=hash_cur;max_match=match; /* find maximal hash */
+ delay_best=delay_cn;
+ if(pd>pend+1)break; /* match to end of block */
+ };
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hash_mask])<pc));
+ };
+ pi-=delay_cn;
+ delay_cn-=delay_best;
+ while(delay_best)
+ {
+ delay_best--;
+ ch_cn[*(pi++)]++;
+ if(++cn>=0x8000u)
+ {TKWR_CHRS(po,0x8000u);cn-=0x8000u;if(poend<po) goto return_error;};
+ }
+ };
+ #endif
+
+ if(cn) TKWR_CHRS(po,cn);
+ cn=pi-max_hash-1; /* history offset */
+ pi+=max_match; /* skip repeated bytes */
+ if(max_match<6)
+ {
+ token=max_match-3;
+ if(cn<3){
+ token+=cn+(cn<<1);
+ *(po++)=token;
+ }else{
+ token=max_match-3+9;
+ cn-=3;
+ match=4;
+ while(cn>=match){token+=6;cn-=match;match<<=1;};
+ match>>=1;if(cn>=match){token+=3;cn-=match;};
+ *(po++)=token;
+ C_ST_u16(po,cn); /* history offset */
+ };
+ }else{
+ if(max_match<21)
+ {token=max_match-6+0x3F;*(po++)=token;C_ST_u16(po,cn);}
+ else{
+ token=0x4E;
+ *(po++)=token;
+ C_ST_u16(po,cn); /* history offset */
+ C_ST_u16(po,max_match); /* repeat count */
+ };
+ };
+ ch_cn[token+0x100]++;
+ if(hash_skiped&&(pi<pend))
+ {
+ #ifdef MAX_COMP
+ max_match-=delay_cn;
+ #endif
+ if(--max_match>hash_skiped) max_match=hash_skiped;
+ pi-=max_match;
+ while(max_match--) sd4_newhash(pi++,hash_tab,hash_hist,hash_mask);
+ };
+ if(poend<po) goto return_error;
+ cn=0;
+ continue;
+ single_char:
+ ch_cn[*(pi++)]++;
+ if(++cn>=0x8000u)
+ {TKWR_CHRS(po,0x8000u);cn-=0x8000u;if(poend<po) goto return_error;};
+ };
+
+ pend+=2;
+ while(pi!=pend) {ch_cn[*(pi++)]++;cn++;};
+ if(cn)
+ {
+ if(cn>=0x8000u) {TKWR_CHRS(po,0x8000u);cn-=0x8000u;};
+ TKWR_CHRS(po,cn);
+ };
+
+ ch_cn[TK_END+0x100]++;
+ *po++=TK_END;
+ FREE(work_mem);
+ return(po-(__u8*)pout);
+
+ return_error:
+ FREE(work_mem);
+ return(0);
+};
+
+INLINE void sd4b_wri(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->buf=0;
+ pbits->pb=32;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+INLINE void sd4b_wrn(bits_t *pbits,int cod, int n)
+{
+ pbits->pb-=n;
+ pbits->buf|=(__u32)cod<<pbits->pb;
+ if(pbits->pb<16)
+ {
+ *(pbits->pd++)=cpu_to_le16((__u16)(pbits->buf>>16));
+ pbits->buf<<=16;
+ pbits->pb+=16;
+ };
+};
+
+INLINE int sd4b_wrhufi(huf_wr_t *phuf,count_t* ch_cn, unsigned* ch_blcn,int cod_num)
+{
+ unsigned i,u,t,blen;
+ u=0;
+ for(i=0;i<16;i++) {u<<=1;t=u;u+=ch_blcn[i];ch_blcn[i]=t;};
+ if(u!=0x8000u) return(1);
+ for(i=0;i<cod_num;i++)
+ {
+ if((blen=ch_cn[i])!=0)
+ {
+ phuf->cod[i]=ch_blcn[blen]++;
+ phuf->ln[i]=blen;
+ };
+ };
+ return(0);
+};
+
+INLINE void sd4b_wrh(bits_t *pbits,const huf_wr_t *phuf,const unsigned ch)
+{
+ sd4b_wrn(pbits,phuf->cod[ch],phuf->ln[ch]);
+};
+
+/*** Hacked generation of character codes ***/
+
+void sd4_hsort1(ch_tab_t* ch_tab,int ch_num,int cl, ch_tab_t a)
+{
+ int ch;
+ ch_tab_t b;
+ ch_tab_t c;
+ ch=cl*2;
+ while(ch<ch_num)
+ {
+ b=ch_tab[ch-1];c=ch_tab[ch];
+ if((c.cn<b.cn)||((c.cn==b.cn)&&(c.ch<=b.ch))) {b=c;ch++;};
+ if((b.cn>a.cn)||((b.cn==a.cn)&&(b.ch>=a.ch))) {ch_tab[cl-1]=a;return;}
+ ch_tab[cl-1]=b;cl=ch;ch*=2;
+ };
+ if(ch==ch_num)
+ {
+ b=ch_tab[ch-1];
+ if((b.cn<a.cn)||((b.cn==a.cn)&&(b.ch<a.ch)))
+ {ch_tab[cl-1]=b;cl=ch;ch*=2;};
+ };
+ ch_tab[cl-1]=a;
+};
+
+int sd4_huffman(count_t* ch_cn,unsigned* ch_blcn,int cod_num,void *work_mem)
+{
+ ch_tab_t *ch_tab; /* normaly 0x152 entries */
+ int i,ch_num,cl;
+ ch_tab_t a;
+ ch_tab_t b;
+
+ ch_tab=(ch_tab_t*)work_mem;
+ redo_reduced:
+ ch_num=0;
+ for(i=0;i<cod_num;i++) if(ch_cn[i])
+ {ch_tab[ch_num].cn=ch_cn[i];ch_tab[ch_num].ch=i|0x800;ch_num++;};
+ ch_tab[ch_num].ch=0;
+ if(ch_num==1)
+ {
+ ch_tab[ch_num]=ch_tab[ch_num-1];
+ ch_tab[ch_num].ch&=0x801;
+ ch_tab[ch_num].ch^=1;ch_num++;
+ };
+ cl=ch_num/2;
+ while(cl>1)
+ {
+ sd4_hsort1(ch_tab,ch_num,cl,ch_tab[cl-1]);
+ cl--;
+ };
+
+ cl=ch_num; a=ch_tab[0];
+ while(cl>2)
+ {
+ sd4_hsort1(ch_tab,cl,1,a);
+ b=ch_tab[0];
+ a=ch_tab[--cl];
+ ch_tab[cl].ch=b.ch;
+ sd4_hsort1(ch_tab,cl,1,a);
+ a=ch_tab[0];
+ ch_tab[cl].cn=a.ch;
+ if(a.ch<=b.ch) {a.ch=b.ch;};
+ a.ch=(a.ch&0x7800)+cl+0x800;
+ if(a.ch>=0x8000u)
+ {
+ printk("DMSDOS: sd4_huffman: Problems with number of bits\n");
+ for(i=0;i<cod_num;i++) ch_cn[i]=(ch_cn[i]+1)>>1;
+ goto redo_reduced;
+ };
+ a.ch+=0x8000u;
+ a.cn+=b.cn;
+ };
+ ch_tab[1].cn=a.ch;
+
+ {
+ int st[0x20];
+ int k=0,l=1,blen=0;
+
+ memset(ch_blcn,0,sizeof(ch_blcn[0])*16);
+ while(1)
+ {
+ do
+ {
+ k|=0x4000;
+ do
+ {
+ st[blen]=k;
+ blen++;
+ k=l&0x7FF;
+ l=ch_tab[k].ch&0x87FF;
+ }while(l&0x8000);
+ ch_cn[l]=blen;
+ ch_blcn[blen]++;
+ l=ch_tab[k].cn&0x87FF;
+ }while(l&0x8000);
+ do
+ {
+ ch_cn[l]=blen;
+ ch_blcn[blen]++;
+ do
+ {
+ if(!--blen) goto code_done;
+ k=st[blen];
+ }while(k&0x4000);
+ l=ch_tab[k].cn&0x87FF;
+ }while(!(l&0x8000));
+ };
+ code_done:;
+ };
+ return(0);
+};
+
+/*** Main compression routine ***/
+
+int sd4_comp(void* pin,int lin, void* pout, int lout, int flg)
+{
+ count_t *ch_cn=NULL;
+ char *work_mem=NULL;
+ unsigned lz_length;
+
+ ch_cn=(count_t*)MALLOC(sizeof(count_t)*0x151);
+ memset(ch_cn,0,sizeof(count_t)*0x151);
+
+ if((lz_length=sd4_complz(pin,lin,pout,lout,flg,ch_cn))==0) goto return_error;
+ {
+ unsigned ch_blcn[16]; /* bitlength couts for codes */
+ int i,j;
+ int bl_dat;
+ unsigned *bl_buf; /* prepared data of table 2 with tokens */
+ count_t act_bl;
+ count_t bl_cn[0x16]; /* occurecces of bit lens */
+ unsigned bl_blcn[0x16]; /* bitlength couts for bit lens */
+ int min_bl, max_bl;
+ __u8* lz_pos;
+ __u8* pdata;
+ bits_t bits;
+ unsigned token, u;
+ huf_wr_t *huf;
+
+/* for converting local variables to allocated memory - kernel stack
+ is too small */
+#define SIZE_OF_bl_buf (sizeof(unsigned)*0x151)
+#define SIZE_OF_huf (((MAX(sizeof(huf_wr_t),sizeof(ch_tab_t)*0x152)+63)/64)*64)
+ work_mem=(char*)MALLOC(SIZE_OF_huf+SIZE_OF_bl_buf);
+ huf=(huf_wr_t*)work_mem;
+ bl_buf=(unsigned*)(work_mem+SIZE_OF_huf);
+ memset(bl_buf,0,SIZE_OF_bl_buf);
+
+ /* ch_cn .. ocurrences of codes */
+ sd4_huffman(ch_cn,ch_blcn,0x150,work_mem);
+ /* ch_cn .. bit lengths of codes */
+ /* ch_blcn .. counts of specific values of bit length */
+ memset(bl_cn,0,sizeof(bl_cn));
+ i=0;bl_dat=0;
+ min_bl=8;max_bl=0;
+ while(i<0x150)
+ {
+ /* case 0x10: 2 times zerro */
+ /* case 0x11: 3 times zerro */
+ /* case 0x12: zerro fill */
+ /* case 0x13: 2 times last char */
+ /* case 0x14: 3 times last char */
+ /* case 0x15: repeat last chr */
+
+ act_bl=ch_cn[i++]; j=i;
+ while((i<0x150)&&(act_bl==ch_cn[i])) i++;
+ j=i-j;
+ if(!act_bl)
+ {
+ if(!j--) {bl_cn[act_bl]++;bl_buf[bl_dat++]=act_bl;}
+ else if(!j--) {bl_cn[0x10]++;bl_buf[bl_dat++]=0x10;}
+ else if(!j--) {bl_cn[0x11]++;bl_buf[bl_dat++]=0x11;}
+ else {bl_cn[0x12]++;bl_buf[bl_dat++]=0x12;bl_buf[bl_dat++]=j;};
+ }else{
+ bl_cn[act_bl]++;bl_buf[bl_dat++]=act_bl;
+ if(act_bl<min_bl) min_bl=act_bl;
+ if(act_bl>max_bl) max_bl=act_bl;
+ if(j--)
+ {
+ if(!j--) {bl_cn[act_bl]++;bl_buf[bl_dat++]=act_bl;}
+ else if(!j--) {bl_cn[0x13]++;bl_buf[bl_dat++]=0x13;}
+ else if(!j--) {bl_cn[0x14]++;bl_buf[bl_dat++]=0x14;}
+ else {bl_cn[0x15]++;bl_buf[bl_dat++]=0x15;bl_buf[bl_dat++]=j;};
+ };
+ };
+ };
+ sd4_huffman(bl_cn,bl_blcn,0x16,work_mem);
+
+ sd4b_wri(&bits,pout,lout-lz_length);
+ lz_pos=(__u8*)pout+lout-lz_length;
+ memmove(lz_pos,pout,lz_length);
+
+ /* write magic */
+ sd4b_wrn(&bits,0x81,16);
+
+ /* write table 1 */
+ sd4b_wrn(&bits,min_bl-1,3);
+ sd4b_wrn(&bits,max_bl,5);
+ sd4b_wrn(&bits,bl_cn[0],4);
+ for(i=min_bl;i<=max_bl;i++) sd4b_wrn(&bits,bl_cn[i],4);
+ for(i=0x10;i<=0x15;i++) sd4b_wrn(&bits,bl_cn[i],4);
+
+ /* write table 2 */
+ if(sd4b_wrhufi(huf,bl_cn,bl_blcn,0x16))
+ {printk("DMSDOS: sd4_comp: Hufman code leakage in table 1\n");goto return_error;};
+ for(i=0;i<bl_dat;)
+ {
+ sd4b_wrh(&bits,huf,j=bl_buf[i++]);
+ if(j==0x12)
+ {
+ j=bl_buf[i++];
+ if(j>=7)
+ {
+ sd4b_wrn(&bits,7,3); j-=7;
+ while(j>=0x7F) {sd4b_wrn(&bits,0x7F,7);j-=0x7F;};
+ sd4b_wrn(&bits,j,7);
+ }
+ else sd4b_wrn(&bits,j,3);
+ }
+ else if(j==0x15)
+ {
+ j=bl_buf[i++];
+ while(j>=7) {sd4b_wrn(&bits,7,3);j-=7;};
+ sd4b_wrn(&bits,j,3);
+ };
+ };
+
+ /* write compressed data */
+ {
+ pdata=(__u8*)pin;
+ if(sd4b_wrhufi(huf,ch_cn,ch_blcn,0x150))
+ {printk("DMSDOS: sd4_comp: Hufman code leakage in table 2\n");goto return_error;};
+ while(1)
+ {
+ /* check of LZ and huff contact in output buffer */
+ if((__u8*)bits.pd+0x20>=lz_pos) goto return_error;
+ token=*(lz_pos++);
+ if(token>TK_CHRS)
+ { /* characters */
+ u=token-TK_CHRS;
+ if(u==15)
+ {
+ C_LD_u16(lz_pos, u);
+ while(u--)
+ if((__u8*)bits.pd+1>=lz_pos) goto return_error;
+ else sd4b_wrh(&bits,huf,*(pdata++));
+ }
+ else while(u--) sd4b_wrh(&bits,huf,*(pdata++));
+ }
+ else
+ { /* repetitions coded as tokens */
+ sd4b_wrh(&bits,huf,token+0x100);
+ if(token<0x3F)
+ { /* short repeat tokens */
+ token++;
+ u=sd4b_reps_div3[token];
+ pdata+=token+sd4b_reps_n[u];
+ i=sd4b_reps_b[u];
+ if(i)
+ {
+ C_LD_u16(lz_pos, u);
+ sd4b_wrn(&bits,u,i);
+ };
+ }
+ else if(token<TK_END)
+ { /* repeat n times last m characters */
+ C_LD_u16(lz_pos, u); u++; /* history offset */
+ if(u<0x21) i=0;
+ else if(u<0xA1) i=1;
+ else if(u<0x2A1) i=2;
+ else i=3;
+ sd4b_wrn(&bits,i,2);
+ sd4b_wrn(&bits,u-sd4b_prog_add[i],sd4b_prog_len[i]);
+ if(token==0x4E)
+ { /* repeat n>=21 */
+ C_LD_u16(lz_pos, u); /* repeat count */
+ pdata+=u;
+ u-=0x15;
+ if(u<0xF) sd4b_wrn(&bits,u,4);
+ else {u-=0xF;sd4b_wrn(&bits,0xF,4);if(u<0xFF) sd4b_wrn(&bits,u,8);
+ else {u-=0xFF;sd4b_wrn(&bits,0xFF,8);if(u<0xFFF) sd4b_wrn(&bits,u,12);
+ else {u-=0xFFF;sd4b_wrn(&bits,0xFFF,12);sd4b_wrn(&bits,u,16);};};};
+ } else pdata+=token+6-0x3F;
+ }
+ else break;
+ };
+ };
+ if((token!=TK_END)||(pdata-(__u8*)pin!=lin))
+ {
+ printk("DMSDOS: sd4_comp: Compression ends with mismash\n");
+ goto return_error;
+ };
+ };
+ sd4b_wrn(&bits,0,16);
+ FREE(ch_cn);
+ FREE(work_mem);
+ return((__u8*)bits.pd-(__u8*)pout);
+ };
+ return_error:
+ if(ch_cn!=NULL) FREE(ch_cn);
+ if(work_mem!=NULL) FREE(work_mem);
+ return(0);
+};
+
+/*************** end code from sd4_bs1.c *********************************/
+
+/*************************************************************************/
+/*************************************************************************/
+/*************** begin code from sd4_bs0.c *******************************/
+
+INLINE void sd3b_wri(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->buf=0;
+ pbits->pb=32;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+INLINE void sd3b_wrn(bits_t *pbits,int cod, int n)
+{
+ pbits->pb-=n;
+ pbits->buf|=(__u32)cod<<pbits->pb;
+ if(pbits->pb<16)
+ {
+ *(pbits->pd++)=cpu_to_be16((__u16)(pbits->buf>>16));
+ pbits->buf<<=16;
+ pbits->pb+=16;
+ };
+};
+
+INLINE __u8 sd3_xorsum(__u8 *data,int len)
+{
+ __u8 sum=0xFF;
+ while(len--) sum^=*(data++);
+ return(sum);
+};
+
+INLINE unsigned sd3_hash(__u8 *p)
+{
+ return (((__u16)p[0]<<4)^((__u16)p[1]<<0))&0x3FF;
+};
+
+INLINE hash_t sd3_newhash(__u8 *p,hash_t* hash_tab,hash_t* hash_hist,unsigned hash_mask)
+{
+ hash_t* hash_ptr;
+ hash_t hash_cur;
+ hash_ptr=hash_tab+sd3_hash(p);
+ hash_cur=*hash_ptr;
+ *hash_ptr=p;
+ *(hash_hist+((unsigned)p&hash_mask))=hash_cur;
+ return(hash_cur);
+};
+
+int sd3_comp(void* pin,int lin, void* pout, int lout, int flg)
+{
+ bits_t bits; /* output bitstream */
+ int try_count; /* number of compares to find best match */
+ int hash_skiped; /* last bytes of repetition are hashed too */
+ hash_t *hash_tab;
+ /* [0x400] pointers to last occurrence of same hash, index hash */
+ hash_t *hash_hist;
+ /* [0x800] previous occurences of hash, index actual pointer&hash_mask */
+ unsigned hash_mask=0x7FF;/* mask for index into hash_hist */
+ __u8 *pi, *pc, *pd, *pend;
+ hash_t hash_cur;
+ unsigned offs;
+ unsigned max_match, match, rep;
+ int try_cn;
+ hash_t max_hash=NULL;
+
+ try_count=2<<((flg>>2)&0x7); /* number of tested entries with same hash */
+ hash_skiped=((flg>>5)+1)&0xF;/* when rep found last # bytes are procesed */
+
+ pi=(__u8*)pin;
+ if(!lin) return(0);
+ pend=pi+(lin-1);
+ if(lout<0x20) return(0);
+ sd3b_wri(&bits,pout,lout-0x10); /* initialize output bitstream */
+ hash_tab=MALLOC(0x400*sizeof(hash_t));
+ if(hash_tab==NULL) return 0;
+ hash_hist=MALLOC(0x800*sizeof(hash_t));
+ if(hash_hist==NULL) {FREE(hash_tab);return 0;}
+
+ for(offs=0;offs<0x400;offs++) hash_tab[offs]=pend; /* none ocurence of hash */
+ for(offs=0;offs<=hash_mask;offs++) hash_hist[offs]=pend; /* should not be needed */
+ pend--; /* last two bytes cannot be hashed */
+ while(pi<pend)
+ {
+ if(bits.pd>bits.pe)
+ {/* aborting */
+ FREE(hash_hist);
+ FREE(hash_tab);
+ return 0;
+ };
+ hash_cur=sd3_newhash(pi,hash_tab,hash_hist,hash_mask);
+ if(hash_cur>=pi) goto single_char;
+ try_cn=try_count;
+ max_match=0;
+ do{
+ if(pi-hash_cur>=0x800) break; /* longer offsets are not alloved */
+ if(hash_cur[0]==pi[0]) /* pi[1]=hash_cur[1] from hash function */
+ {
+ match=pend-pi; /* length of rest of data */
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match);/* compare */
+ match=pd-pi; /* found match length */
+ if(match>max_match)
+ {max_hash=hash_cur;max_match=match;}; /* find maximal hash */
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hash_mask])<pc));
+ if(max_match<2) goto single_char;
+
+ offs=pi-max_hash; /* history offset */
+ pi+=max_match; /* skip repeated bytes */
+
+ if(offs<0x80) sd3b_wrn(&bits,0x180+offs,9);
+ else
+ {sd3b_wrn(&bits,0x100+offs/16,9);
+ sd3b_wrn(&bits,offs%16,4);
+ };
+
+ rep=max_match-2;
+ if(rep<3)sd3b_wrn(&bits,rep,2);
+ else
+ {rep-=3;sd3b_wrn(&bits,3,2);
+ if(rep<3)sd3b_wrn(&bits,rep,2);
+ else
+ {rep-=3;sd3b_wrn(&bits,3,2);
+ for(;rep>=15;rep-=15)sd3b_wrn(&bits,15,4);
+ sd3b_wrn(&bits,rep,4);
+ };
+ };
+
+ if(hash_skiped&&(pi<pend))
+ {
+ if(--max_match>hash_skiped) max_match=hash_skiped;
+ pi-=max_match;
+ while(max_match--) sd3_newhash(pi++,hash_tab,hash_hist,hash_mask);
+ };
+ continue;
+ single_char:
+ sd3b_wrn(&bits,*pi++,9);
+ };
+
+ pend+=2;
+ while(pi!=pend) sd3b_wrn(&bits,*pi++,9);
+
+ sd3b_wrn(&bits,0x180,9);
+ bits.pb&=~7;
+ sd3b_wrn(&bits,sd3_xorsum(pin,lin),8);
+
+ sd3b_wrn(&bits,0,15);
+
+ FREE(hash_hist);
+ FREE(hash_tab);
+ return((__u8*)bits.pd-(__u8*)pout);
+
+};
+
+/*************** end code from sd4_bs0.c *********************************/
+
+
+/* This function will be called by the dmsdos driver *and* by the daemon
+ in order to compress stacker data (the daemon links the object file, too).
+ Decision can be made with ifdef __KERNEL__ .
+
+ Hi Frank,
+ I know, that it is different from doublespace, but I
+ decide change this, because stacker 4 compression likes know
+ exactly free space in output buffer. It uses this space for
+ intermediate data representation ( which is always shorter
+ then original data ), then moves it to end and writes new
+ compressed data from begining (if write catch read compression
+ returns false). Theoreticaly length -> length must sucees or
+ all compression is useless, but who knows. And there is such
+ lovely 32 kB buffer in daemon.
+
+*/
+
+#if 0
+/* this confuses some compilers in the memset command below */
+int stac_compress(void* pin,int lin, void* pout, int lout,
+ int method, int cfaktor)
+#else
+int stac_compress(unsigned char* pin,int lin, unsigned char* pout, int lout,
+ int method, int cfaktor)
+#endif
+{ int ret=-1;
+ int i;
+ if(((i=lin%512)!=0)||!lin) /* useless but stacker like it */
+ { memset(pin+lin,0,512-i);
+ lin+=512-i;
+ };
+ if((cfaktor<=0)||(cfaktor>12)) cfaktor=11;
+ if(method==SD_4) ret=sd4_comp(pin,lin,pout,lout,comp_rat_tab[cfaktor]);
+ else if(method==SD_3) ret=sd3_comp(pin,lin,pout,lout,comp_rat_tab[cfaktor]);
+ if(ret>lin-512) ret=0; /* for now */
+ return ret;
+}
+
+/* Specification:
+ This function writes a stacker cluster.
+ It must take care of clustermap and allocationmap manipulation
+ including freeing the old sectors.
+ It must not touch the FAT.
+ It must take care of compression, if implemented.
+ It must write uncompressed if ucflag==UC_UNCOMPR.
+ Depending on the daemon implementation, it should be able to process
+ raw writes (ucflag<0).
+ near_sector may be ignored in case this is too difficult to implement.
+ ucflag==UC_TEST means check for free space or reserve space for the
+ cluster on the disk, but don't actually write the data.
+ It is for write-back cluster caching. When a cluster is marked as dirty
+ by calling the ch_dirty function, the cluster caching code calls the
+ write_cluster function with the UC_TEST flag. The cluster
+ write access is delayed only if the UC_TEST call succeeds (returns a
+ value >=0). Otherwise the cluster caching code immediately falls back to
+ write-through mode and calls the write function again.
+ If the UC_TEST call succeeds, be prepared to be called again later
+ at any time without the UC_TEST flag when the cluster caching code has
+ decided to actually write the data back to disk.
+*/
+
+/* write a stacker cluster, compress before if possible;
+ length is the number of used bytes, may be < SECTOR_SIZE*sectperclust only
+ in the last cluster of a file;
+ cluster must be allocated by allocate_cluster before if it is a new one;
+ unable to write dir clusters;
+ to avoid MDFAT level fragmentation, near_sector should be the sector no
+ of the preceeding cluster;
+ if ucflag==UC_UNCOMPR uncompressed write is forced.
+ if ucflag==UC_TEST check for free space or reserve space on the
+ disk but don't actually write the data.
+
+ If ucflag<0 raw write is forced with compressed size -ucflag (in bytes),
+ in that case parameter length is *uncompressed* size. This is the new
+ dmsdosd/ioctl interface.
+
+ If clusterd==NULL the cluster is to be removed instead of written. This
+ is called by the rest of the dmsdos code when a file is deleted. So
+ the stacker code is expected to free up mdfat/bitfat allocation for the
+ cluster, but it must not touch the fat.
+
+ if ucflag==UC_DIRECT do like ucflag==UC_NORMAL but don't use the daemon
+ for compression.
+ This should guarantee that the data are written when the function exits.
+ It is unimportant whether compression fails or not - it's just important
+ that the data use *least* disk space. This must not override the
+ method=UNCOMPRESSED or compression level selected by user, though.
+ It is intended for error recovery when the filesystem gets full - if
+ we use the daemon, the uncompressed data might not fit to the disk while
+ the compressed data may still do.
+*/
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+
+int stac_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag)
+{
+ int method;
+ unsigned char* clusterk;
+ int size;
+ int sect,count;
+ int i,val;
+ int res;
+ struct buffer_head*bh;
+ int max_clen;
+ Stac_cwalk cw;
+ int cfaktor;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ /* check if we are deleting a cluster */
+ if(clusterd==NULL||length==0)
+ {
+ lock_mdfat_alloc(dblsb);
+ stac_special_free(sb,clusternr);
+ unlock_mdfat_alloc(dblsb);
+ return 0;
+ }
+
+ /* for now */
+ /*if(ucflag==UC_TEST) return -EIO;*/
+ if(ucflag==UC_TEST)
+ { if( dblsb->s_full==0 &&
+ /* well, this is estimated */
+ dblsb->s_sectperclust*CCACHESIZE+100<dblsb->s_free_sectors
+ ) return 0;
+ else return -ENOSPC;
+ }
+
+ if(dblsb->s_comp==GUESS)
+ {
+ if(dblsb->s_cvf_version==STAC3)
+ dblsb->s_comp=SD_3;
+ else
+ dblsb->s_comp=SD_4;
+ };
+
+ method=dblsb->s_comp; /* default compression method */
+ max_clen=dblsb->s_sectperclust*SECTOR_SIZE; /* maximal data size */
+
+ if( ( (dblsb->s_cvf_version==STAC3)&&(length<=SECTOR_SIZE) ) ||
+ (ucflag==UC_UNCOMPR)
+ ) /* uncompressed forced or no need to compress */
+ { method=UNCOMPRESSED;
+ clusterk=clusterd;
+ }
+ else if(ucflag<0)
+ { /* raw compressed data from daemon */
+ length=-ucflag;
+ method=UNCOMPRESSED^1; /* not uncompressed */ /* is this correct ??? */
+ /* Hi Pavel,
+ Please check the code whether it works
+ correctly for daemon writes. I think this may
+ cause a FREE(data not to free) at the very
+ end. I added a ucflag>=0 test there to avoid
+ the problem.
+ */
+ clusterk=clusterd;
+ }
+ else if(method!=UNCOMPRESSED)
+ { /* ucflag==3 test added - Frank */
+ if((ucflag==UC_DIRECT)?0:try_daemon(sb,clusternr,length,method)) clusterk=NULL;
+ else if((clusterk=(unsigned char*)MALLOC(max_clen))==NULL)
+ printk(KERN_WARNING "DMSDOS: stac_write_cluster: no memory for compression, writing uncompressed!\n");
+ if(clusterk==NULL) method=UNCOMPRESSED;
+ else
+ { /* We test possible compression */
+ /* stacker needs length before compression to be
+ multiple of SECTOR_SIZE */
+ if(((i=length%SECTOR_SIZE)!=0)||!length)
+ { memset(clusterd+length,0,SECTOR_SIZE-i);
+ i=length+SECTOR_SIZE-i;
+ } else i=length;
+
+ cfaktor=dblsb->s_cfaktor;
+ if((cfaktor<=0)||(cfaktor>12)) cfaktor=11;
+
+ if(method==SD_4)
+ { LOG_CLUST("DMSDOS: stac_write_cluster: compressing sd4...\n");
+ i=sd4_comp(clusterd,i,clusterk,max_clen,comp_rat_tab[cfaktor]);
+ LOG_CLUST("DMSDOS: stac_write_cluster: compressing finished\n");
+ }
+ else if(method==SD_3)
+ { LOG_CLUST("DMSDOS: stac_write_cluster: compressing sd3...\n");
+ i=sd3_comp(clusterd,i,clusterk,max_clen,comp_rat_tab[cfaktor]);
+ LOG_CLUST("DMSDOS: stac_write_cluster: compressing finished\n");
+ }
+ else if(method==DS_0_0)
+ { LOG_CLUST("DMSDOS: stac_write_cluster: compressing ds00...\n");
+ i=dbl_compress(clusterk,clusterd,(i+511)/512,method,cfaktor)*512;
+ LOG_CLUST("DMSDOS: stac_write_cluster: compressing finished\n");
+ }
+ else i=0;
+
+ LOG_CLUST("DMSDOS: Cluster %i compressed from %i to %i\n",clusternr,length,i);
+ if((i<=0)||(i>=length)||(i>max_clen-SECTOR_SIZE))
+ { method=UNCOMPRESSED;
+ FREE(clusterk);
+ }
+ else length=i;
+ }
+ }
+ if(method==UNCOMPRESSED) clusterk=clusterd;
+
+ /* Now we have data and must decide where to write them */
+ val=stac_cwalk_init(&cw,sb,clusternr,3);
+ if (val<0)
+ { printk(KERN_ERR "DMSDOS: stac_write_cluster: alloc error in cluster %d\n",
+ clusternr);
+ res=-EIO;
+ goto error_return;
+ };
+
+ /* decide if it is necessary to reallocate cluster */
+ if((val==0)||(cw.bytes_in_clust<length)||
+ (cw.bytes_in_clust>=length+SECTOR_SIZE)||(cw.flags&0x40)||
+ ((cw.compressed==0)!=(method==UNCOMPRESSED)))
+ { /* It is necessary realocate space */
+ /* this piece of code is dirty hack and must be rewriten */
+ Mdfat_entry mde;
+
+ stac_cwalk_done(&cw);
+
+ size=(length+511)/512;
+ if(!size) size=1;
+ mde.size_lo_minus_1=size-1;
+ mde.size_hi_minus_1=size-1;
+ if(method==UNCOMPRESSED)
+ if(size==dblsb->s_sectperclust)
+ mde.flags=2;
+ else
+ mde.flags=0x23;
+ else
+ mde.flags=2;
+
+ LOG_CLUST("DMSDOS: stac_write_cluster: Replace size %2i flg 0x%02X cluster %i\n",
+ size,mde.flags,clusternr);
+ sect=stac_replace_existing_cluster(sb,clusternr,near_sector,&mde);
+ LOG_CLUST("DMSDOS: stac_write_cluster: stac_replace_existing_cluster returned %d\n",
+ sect);
+
+ if(sect<0) {res=-ENOSPC;goto error_return;};
+
+ val=stac_cwalk_init(&cw,sb,clusternr,3);
+ if ((val<0)||(length>cw.bytes_in_clust))
+ { printk(KERN_ERR "DMSDOS: stac_write_cluster: alloc error in cluster %d\n",
+ clusternr);
+ res=-EIO;
+ goto error_return;
+ };
+ }
+
+ { res=0; count=0;
+ while((sect=stac_cwalk_sector(&cw))>0)
+ { if(cw.bytes==SECTOR_SIZE) bh=raw_getblk(sb,sect);
+ else bh=raw_bread(sb,sect);
+ if(bh==NULL)res=-EIO;
+ else
+ { if(count+cw.bytes>cw.bytes_in_clust)
+ { printk(KERN_ERR "DMSDOS: stac_write_cluster: internal cw error 1 cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ stac_cwalk_done(&cw);
+ goto error_return;
+ };
+ if(count+cw.bytes<=length)
+ memcpy(bh->b_data+cw.offset,clusterk+count,cw.bytes);
+ else
+ { if((i=length-count)>0)
+ { memcpy(bh->b_data+cw.offset,clusterk+count,i);
+ memset(bh->b_data+cw.offset+i,0,cw.bytes-i);
+ } else memset(bh->b_data+cw.offset,0,cw.bytes);
+ };
+ raw_set_uptodate(sb,bh,1);/*getblk needs this*/
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ };
+ count+=cw.bytes;
+ }
+ }
+ stac_cwalk_done(&cw);
+ if((count<length)||(count!=cw.bytes_in_clust))
+ printk(KERN_ERR "DMSDOS: stac_write_cluster: internal cw error 2 cluster=%d\n",
+ clusternr);
+
+ error_return:
+ if(method!=UNCOMPRESSED&&ucflag>=0)FREE(clusterk);
+ /* better not free the daemon raw data here - Frank */
+
+ return res;
+}
+
+#endif /*__KERNEL__||__DMSDOS_LIB__*/
+
+#endif /* DMSDOS_CONFIG_STAC */
diff --git a/src/lib/dstacker_dec.c b/src/lib/dstacker_dec.c
new file mode 100644
index 0000000..28ded1c
--- /dev/null
+++ b/src/lib/dstacker_dec.c
@@ -0,0 +1,824 @@
+/*
+dstacker_dec.c
+
+DMSDOS CVF-FAT module: stacker decompression routines.
+
+******************************************************************************
+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
+
+Stacker decompression (based on sd4_cc package):
+
+ (C) Copyright 1996 by Jaroslav Fojtik (stacker 3 decompression)
+
+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/byteorder.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<string.h>
+#include<errno.h>
+#endif
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers may not like inline */
+#define INLINE static
+#endif
+
+#ifdef DMSDOS_CONFIG_STAC
+
+#if defined(__GNUC__) && defined(__i386__) && defined(USE_ASM)
+#define USE_GNU_ASM_i386
+
+/* copy block, overlaping part is replaced by repeat of previous part */
+/* pointers and counter are modified to point after block */
+#define M_MOVSB(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "movsb\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ :"memory")
+
+INLINE __u16 swap_bytes_in_word(__u16 x)
+ {
+ __asm__("xchgb %b0,%h0" /* swap bytes */
+ : "=q" (x)
+ : "0" (x));
+ return x;
+ }
+
+
+#else
+
+#ifdef __GNUC__
+/* non-gnu compilers may not like warning directive */
+#warning USE_GNU_ASM_I386 not defined, using "C" equivalent
+#endif
+
+#define M_MOVSB(D,S,C) for(;(C);(C)--) *((__u8*)(D)++)=*((__u8*)(S)++)
+
+INLINE __u16 swap_bytes_in_word(__u16 x)
+ {
+ return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
+ }
+
+#endif
+
+#if !defined(cpu_to_le16)
+ /* for old kernel versions - works only on i386 */
+ #define le16_to_cpu(v) (v)
+ #define be16_to_cpu(v) (swap_bytes_in_word(v))
+#endif
+
+/***************************************************************************/
+/***************************************************************************/
+/********* begin code from sd3_bs0.c ***************************************/
+
+/*#define INLINE inline*/
+
+typedef struct
+ {
+ __u8 *ptr;
+ int x;
+ int pos;
+ int max_x;
+ }bitstreamC;
+
+void InitBitStream(bitstreamC *b,void *k,int max_x)
+{
+ b->ptr=(__u8 *)k;
+ b->pos=0x8;
+ b->x=0;
+ b->max_x=max_x;
+}
+
+
+INLINE int Read9BitC(bitstreamC *b)
+{
+unsigned int a;
+
+a = (unsigned) *(b->ptr++) << 8;
+a|= *b->ptr;
+a=(a >> (--b->pos));
+b->x++;
+if(b->pos==0)
+ {
+ (b->ptr)++;
+ b->x++;
+ b->pos=0x8;
+ }
+return(a & 0x1FF);
+}
+
+INLINE int Read4BitC(bitstreamC *b)
+{
+unsigned int a;
+
+if(b->pos<=3) {
+ b->pos+=4;
+ a = (unsigned)*(b->ptr++) << 8;
+ a|= *b->ptr;
+ a=(a >> (b->pos));
+ b->x++;
+ return(a & 0xF);
+ }
+ else
+ {
+ if(b->pos==4)
+ {
+ a=*(b->ptr);
+ (b->ptr)++;
+ b->x++;
+ b->pos=0x8;
+ return(a & 0x0F);
+ }
+
+ b->pos-=4;
+ return((*(b->ptr) >> (b->pos))& 0x0F);
+ }
+}
+
+INLINE int Read2BitC(bitstreamC *b)
+{
+unsigned char a;
+
+
+
+if(b->pos<=1) {
+ a=*(b->ptr++) << 1;
+ b->x++;
+ b->pos=0x7;
+ if(*b->ptr >=128) a++;
+ return(a & 0x3);
+ }
+ else
+ {
+ if(b->pos==2)
+ {
+ a=*(b->ptr);
+ (b->ptr)++;
+ b->x++;
+ b->pos=0x8;
+ return(a & 0x03);
+ }
+
+ b->pos-=2;
+ return((*(b->ptr) >> (b->pos))& 0x03);
+ }
+}
+
+int ReadBitC(bitstreamC *b)
+{
+int a;
+
+a=(*(b->ptr) >> --(b->pos)) & 1;
+if(b->pos==0)
+ {
+ (b->ptr)++;
+ b->x++;
+ b->pos=8;
+ }
+return(a);
+}
+
+/*---------------------------------------------------------*/
+
+int ReadNC(bitstreamC *b)
+{
+int repeater,rep;
+
+ rep=repeater=Read2BitC(b);
+ if (rep==3)
+ {
+ rep=Read2BitC(b);
+ repeater += rep;
+ if (rep==3)
+ {
+ rep=Read4BitC(b);
+ repeater += rep;
+ while(rep==15)
+ {
+ if(b->x>=b->max_x)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: ReadNC error!\n");
+ return(0);
+ };
+ rep=Read4BitC(b);
+ repeater += rep;
+ }
+ }
+ }
+return(repeater);
+}
+
+#define __dcflDebugInfo 0x8000
+
+INLINE __u8 sd3_xorsum_D(__u8 *data,int len)
+{
+ __u8 sum=0xFF;
+ while(len--) sum^=*(data++);
+ return(sum);
+};
+
+int sd3_decomp(void *data,int CompSize,void *DecompData,int DecompSize,
+ int Flags)
+{
+ bitstreamC bb;
+ int DataSize=DecompSize;
+ int token,repN;
+ __u8 *Decomp,*P;
+
+ InitBitStream(&bb,data,CompSize);
+ Decomp=(__u8 *)DecompData;
+
+ while(CompSize>bb.x+2)
+ {
+ token=Read9BitC(&bb);
+ if(DataSize<=0)
+ {
+ if(token!=0x180) printk(KERN_INFO "DMSDOS: stac3_decomp: end token 0x%02X\n",
+ (unsigned)token);
+ break;
+ };
+
+ if(token>=256)
+ {
+ token=token & 0xFF;
+ if(token==0x81)
+ {
+ repN=ReadNC(&bb)+2;
+#ifdef dcflDebugInfo
+ printk(KERN_DEBUG "DMSDOS: stac3_decomp: Rep:(%dx) ",repN);
+#endif
+ if(DataSize<repN)
+ {
+ repN=DataSize;
+ printk(KERN_ERR "DMSDOS: stac3_decomp: char repeat overrun!\n");
+ return(0);
+ }
+ memset((void *)Decomp,*(Decomp-1),repN);
+ Decomp+=repN;
+ DataSize-=repN;
+ continue;
+ }
+
+ if (token >= 0x80)
+ {
+ token=token & 0x7F;
+ if(!token) break;
+ }
+ else
+ {
+ if (token<8)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: Unknown token %d on pos 0x%X->0x%X\n",
+ token, bb.ptr-(__u8*)data, Decomp-(__u8*)DecompData);
+ return(0);
+ }
+ token=16*token+Read4BitC(&bb);
+ }
+ repN=ReadNC(&bb)+2;
+#ifdef dcflDebugInfo
+ printk(KERN_DEBUG "DMSDOS: stac3_decomp: Multi rep:(%dx %d) ",token,repN);
+#endif
+ if(DataSize<repN)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: Multi rep overrun 0x%x at pos 0x%x->0x%x\n",
+ repN,bb.ptr-(__u8*)data,Decomp-(__u8*)DecompData);
+ repN=DataSize;
+ return(0);
+ }
+/* memmove(Decomp,Decomp-token,repN); Decomp+=repN; */
+ DataSize-=repN;
+
+ P=Decomp-token;
+ /* this prevents segfaults in case of strange error */
+ if(P<(__u8*)DecompData)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: Illegal back pointer length 0x%x at pos 0x%x->0x%x\n",
+ token,bb.ptr-(__u8*)data,Decomp-(__u8*)DecompData);
+ break;
+ };
+ while(repN--) *(Decomp++)=*(P++);
+
+ }
+ else
+ {
+ *Decomp=token; /*ReadnBitC(&bb,8);*/
+/* printk(" %c",*Decomp,*Decomp);*/
+ Decomp++;
+ if(DataSize!=0) DataSize--;
+ }
+ }
+
+ if(bb.pos!=8) {bb.x++;bb.ptr++;};
+ if(CompSize>bb.x)
+ {
+ /* Check data xor sum */
+ __u8 sum;
+ sum=sd3_xorsum_D((__u8*)DecompData,DecompSize-DataSize);
+ if(sum^*bb.ptr)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: xor sum error!\n");
+ return(0);
+ };
+ };
+
+ return(DecompSize-DataSize);
+}
+
+
+/**************** end code from sd3_bs0.c ********************************/
+
+/*************************************************************************/
+/*************************************************************************/
+/*************** begin code from sd4_bs1.c *******************************/
+
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* not read bits count in buffer */
+ __u16 *pd; /* first not readed input data */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+typedef
+ struct {
+ __u8 ch[0x400]; /* characters codes */
+ __u8 ln[0x400]; /* characters lens .. if >=0x80 controll */
+ __u8 ch1[0x200]; /* for codes vith more than bn bits */
+ __u8 ln1[0x200];
+ int bn; /* ch,ln array max convert bits, longer use ch1,cl1 */
+ __u16 cd_ln[16]; /* distribution of bits */
+ __u16 cd_ch[16]; /* distribution of codes codes */
+ }huf_t;
+
+const unsigned sd4b_bmsk[]=
+ {0x0,0x1,0x3,0x7,0xF,0x1F,0x3F,0x7F,0xFF,
+ 0x1FF,0x3FF,0x7FF,0xFFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
+
+#define RDN_G16(bits) \
+ { \
+ (bits).buf<<=16; \
+ (bits).pb+=16; \
+ if((bits).pd<(bits).pe) \
+ { \
+ (bits).buf|=le16_to_cpu(*((bits).pd++)); \
+ }; \
+ }
+
+#define RDN_PR(i,bits,n,G16) \
+ { \
+ if((bits).pb<16) G16(bits); \
+ i=(bits).buf>>((bits).pb-=(n)); \
+ }
+
+INLINE void sd4b_rdi(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->pb=0;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+INLINE unsigned sd4b_rdn(bits_t *pbits,int n)
+{
+ unsigned i;
+ RDN_PR(i,*pbits,n,RDN_G16);
+ i&=sd4b_bmsk[n];
+ return i;
+};
+
+#define OUT_OVER 0x100
+
+/* read and huffman decode of characters, stops on tokens or buffer ends */
+INLINE unsigned sd4b_rdh(bits_t *pbits,const huf_t *phuf,__u8 **pout,__u8 *pend)
+{
+
+ unsigned ch;
+ unsigned bmsk=sd4b_bmsk[phuf->bn];
+
+ while(1)
+ {while(1)
+ {if(pbits->pb<16)
+ RDN_G16(*pbits);
+ if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>(pbits->pb-phuf->bn))&bmsk;
+ if((pbits->pb-=phuf->ln[ch])<0) break;
+ *((*pout)++)=phuf->ch[ch];
+
+ if(pbits->pb>=16)
+ {if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>(pbits->pb-phuf->bn))&bmsk;
+ if((pbits->pb-=phuf->ln[ch])<0) break;
+ *((*pout)++)=phuf->ch[ch];
+
+ if(pbits->pb>=16)
+ {if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>(pbits->pb-phuf->bn))&bmsk;
+ if((pbits->pb-=phuf->ln[ch])<0) break;
+ *((*pout)++)=phuf->ch[ch];
+ };
+ };
+ };
+
+ ch=phuf->ch[ch];
+ pbits->pb+=0x40; if(ch) return ch;
+ /* code longer than phuf->bn */
+ if(pbits->pb<16) RDN_G16(*pbits);
+ ch=(pbits->buf>>(pbits->pb-16))&0xFFFF;
+ {
+ int i;
+ i=phuf->bn;
+ do
+ i++;
+ while(phuf->cd_ch[i]<=(ch>>(16-i))&&(i<15));
+ ch=(ch>>(16-i))-phuf->cd_ch[i]+phuf->cd_ln[i];
+ };
+ if((pbits->pb-=phuf->ln1[ch])<0)
+ {pbits->pb+=0x40;
+ return phuf->ch1[ch];
+ };
+ *((*pout)++)=phuf->ch1[ch];
+ };
+};
+
+INLINE int sd4b_rdhufi(huf_t *phuf,int m,int bn,__u8 *ca)
+{
+ if(bn>10) bn=10;
+ phuf->bn=bn;
+ {
+ int i;
+ unsigned u,us,ut;
+ memset(phuf->cd_ln,0,sizeof(phuf->cd_ln));i=0;
+ while((u=ca[i++])<16) phuf->cd_ln[u]++;
+ memset(phuf->cd_ch,0,sizeof(phuf->cd_ch));
+ phuf->cd_ln[0]=0;us=0;ut=0;
+ for(i=1;i<16;i++)
+ {
+ u=phuf->cd_ln[i];phuf->cd_ln[i]=ut;
+ phuf->cd_ch[i]=us;ut+=u;us+=u;us<<=1;
+ };
+ /* if suceed us should be 0x10000 */
+ if (us&0xFFFF) return(0);
+ };
+ {
+ int i,ln,ch,sh,cod;
+ for(i=0;(ln=ca[i])<16;i++) if(ln)
+ {
+ sh=(bn-ln);
+ cod=(phuf->cd_ch[ln])++;
+ if(i<m) ch=i; else {ch=i-m+1;ln+=0x40;};
+ if (sh>0)
+ {
+ memset(phuf->ch+(cod<<sh),ch,1<<sh);
+ memset(phuf->ln+(cod<<sh),ln,1<<sh);
+ } else if (sh==0) {
+ phuf->ch[cod]=ch;
+ phuf->ln[cod]=ln;
+ } else {
+ cod>>=-sh;
+ phuf->ch[cod]=0x00;
+ phuf->ln[cod]=0x40;
+ cod=(phuf->cd_ln[ln&0xF])++;
+ phuf->ch1[cod]=ch;
+ phuf->ln1[cod]=ln;
+ };
+ };
+ /* if suceed ln should be 0xFF */
+ };
+ return(1);
+};
+
+#if 0
+/* token decoding tables */
+ const unsigned int sd4b_prog_len[]={ 5, 7, 9, 11};
+ const unsigned int sd4b_prog_add[]={ 0x1,0x21,0xA1,0x2A1};
+ const signed char sd4b_reps_div3[]={0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,
+ 6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,
+ 15,15,15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20};
+ const signed char sd4b_reps_n[] = {3-1,3-4,3-7,3-10,3-13,3-16,3-19,3-22,
+ 3-25,3-28,3-31,3-34,3-37,3-40,3-43,3-46,3-49,3-52,3-55,3-58,3-61};
+ const unsigned char sd4b_reps_b[] = {0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9};
+ const unsigned int sd4b_reps_m[] = {1,2,3,4,6,8,12,16,24,32,48,64,96,128,
+ 192,256,384,512,768,1024,1536};
+#endif
+#if 1
+ extern const unsigned int sd4b_prog_len[];
+ extern const unsigned int sd4b_prog_add[];
+ extern const signed char sd4b_reps_div3[];
+ extern const signed char sd4b_reps_n[];
+ extern const unsigned char sd4b_reps_b[];
+ extern const unsigned int sd4b_reps_m[];
+#endif
+#if 0
+ static const unsigned int sd4b_prog_len[]={ 5, 7, 9, 11};
+ static const unsigned int sd4b_prog_add[]={ 0x1,0x21,0xA1,0x2A1};
+ static const signed char sd4b_reps_div3[]={0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,
+ 6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,
+ 15,15,15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20};
+ static const signed char sd4b_reps_n[] = {3-1,3-4,3-7,3-10,3-13,3-16,3-19,3-22,
+ 3-25,3-28,3-31,3-34,3-37,3-40,3-43,3-46,3-49,3-52,3-55,3-58,3-61};
+ static const unsigned char sd4b_reps_b[] = {0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9};
+ static const unsigned int sd4b_reps_m[] = {1,2,3,4,6,8,12,16,24,32,48,64,96,128,
+ 192,256,384,512,768,1024,1536};
+#endif
+
+int sd4_decomp(void* pin,int lin, void* pout, int lout, int flg)
+{
+ bits_t bits;
+ huf_t *huf;
+ unsigned u;
+ __u8 len_150;
+
+ sd4b_rdi(&bits,pin,lin);
+ u=sd4b_rdn(&bits,16);
+ if(u!=0x81) {printk(KERN_ERR "DMSDOS: sd4_decomp: Magic = %X => error!\n",u);return 0;};
+
+ huf=(huf_t*)MALLOC(sizeof(huf_t));
+ if(!huf) {printk(KERN_ERR "DMSDOS: sd4_decomp: no memory!\n");return 0;};
+
+ {
+ int i;
+ int ie;
+ int bmax1,bmax2;
+ unsigned u;
+ __u8 ca[0x180];/* 12B4 */
+ __u8 *pca;
+ __u8 *pcae;
+
+ memset(ca,0,22);
+ i=sd4b_rdn(&bits,3)+1;
+ ie=bmax2=sd4b_rdn(&bits,5);
+ if(i>ie) {printk(KERN_ERR "DMSDOS: sd4_decomp: Table 1 error\n");goto error;};
+ ca[0]=bmax1=sd4b_rdn(&bits,4);
+ while(1)
+ {
+ while(i<=ie)
+ {
+ u=sd4b_rdn(&bits,4);
+ ca[i++]=u;if(u>bmax1) bmax1=u;
+ };
+ if(ie==0x15) break;
+ i=0x10;ie=0x15;
+ };
+ ca[22]=0xFF;
+
+ if(!sd4b_rdhufi(huf,0x10,7<bmax1?7:bmax1,ca))
+ {printk(KERN_ERR "DMSDOS: sd4_decomp: Table 1 consistency check !!!!\n");goto error;};
+
+ pca=ca;
+ pcae=ca+0x150;
+ while((u=sd4b_rdh(&bits,huf,&pca,pcae))<=6)
+ {
+ switch (u)
+ {
+ unsigned n;
+ case 1: /* 2 times zerro */
+ pca[1]=pca[0]=0;pca+=2;
+ break;
+ case 2: /* 3 times zerro */
+ pca[2]=pca[1]=pca[0]=0;pca+=3;
+ break;
+ case 3: /* zerro fill */
+ n=4+(u=sd4b_rdn(&bits,3));
+ if (u==7) do {u=sd4b_rdn(&bits,7);n+=u;} while (u==0x7F);
+ if ((pca+n)>pcae) n=pcae-pca;
+ memset(pca,0,n);
+ pca+=n;
+ break;
+ case 4: /* 2 times last char */
+ pca[1]=pca[0]=*(pca-1);pca+=2;
+ break;
+ case 5: /* 3 times last char */
+ u=*(pca-1);
+ if (pca<pcae) {pca[0]=pca[1]=pca[2]=u;pca+=3;};
+ break;
+ case 6: /* repeat last chr */
+ n=4;
+ do {u=sd4b_rdn(&bits,3);n+=u;} while (u==7);
+ if ((pca+n)>pcae) n=pcae-pca;
+ memset(pca,*(pca-1),n);
+ pca+=n;
+ break;
+ };
+ };
+ ca[0x150]=0xFF;
+ len_150=ca[0x14F];
+
+ if(!sd4b_rdhufi(huf,0x100,bmax2,ca))
+ {printk(KERN_ERR "DMSDOS: sd4_decomp: Table 2 consistency check !!!!\n");goto error;};
+
+ };
+ {
+ __u8 *p,*r,*pe;
+ p=(__u8*)pout;pe=p+lout;
+ while((u=sd4b_rdh(&bits,huf,&p,pe))<0x50)
+ {
+ {
+ unsigned n,m;
+
+ if (u<0x40) {
+ m=sd4b_reps_div3[u]; /* short repeat tokens */
+ n=u+sd4b_reps_n[m];
+ u=sd4b_reps_b[m];
+ m=sd4b_reps_m[m];
+ if (u) m+=sd4b_rdn(&bits,u);
+ } else {
+ m=sd4b_rdn(&bits,2); /* Repeat n times last m characters */
+ m=sd4b_rdn(&bits,sd4b_prog_len[m])+sd4b_prog_add[m];
+ if((n=u-0x40+6)==0x15)
+ if((n+=sd4b_rdn(&bits,4))==0x15+0xF)
+ if((n+=sd4b_rdn(&bits,8))==0x15+0xF+0xFF)
+ if((n+=sd4b_rdn(&bits,12))==0x15+0xF+0xFF+0xFFF)
+ n+=sd4b_rdn(&bits,16);
+ };
+ if ((__u8*)pout+m>p)
+ {m=p-(__u8*)pout;printk(KERN_ERR "DMSDOS: sd4_decomp: Under !!!\n");};
+ if (p+n>pe)
+ {n=pe-p;printk(KERN_ERR "DMSDOS: sd4_decomp: Over !!!!\n");};
+ /*memcpy(p,p-m,n);p+=n;*/
+ r=p-m;M_MOVSB(p,r,n); /* copy/repeat function */
+ };
+ };
+ if((u==OUT_OVER)&&len_150)
+ {
+ int i;
+ if((i=sd4b_rdn(&bits,len_150))==huf->cd_ch[len_150]-1) u=0x50;
+ else printk(KERN_ERR "DMSDOS: sd4_decomp: End read %X and should be %X\n",i,(int)huf->cd_ch[len_150]-1);
+ };
+ if(u==0x50)
+ {
+ FREE(huf);
+ return(p-(__u8*)pout);
+ }
+ else {printk(KERN_ERR "DMSDOS: sd4_decomp: Error end token %X\n",u);};
+ };
+
+ error:
+ FREE(huf);
+ return 0;
+};
+
+/*************** end code from sd4_bs1.c *********************************/
+
+int stac_decompress(unsigned char*buf_in, int len_in,
+ unsigned char*buf_out, int len_out)
+{ int alg_info;
+
+ alg_info=le16_to_cpu(*(__u16*)buf_in);
+ switch(alg_info)
+ { case 0x0081:
+ return(sd4_decomp(buf_in,len_in,buf_out,len_out,0));
+ case 0x5344:
+ /* call DS decompression from dmsdos_dec */
+ /* mde.size_hi_minus_1=(len_out-1)/SECTOR_SIZE; */
+ /* return(dbl_decompress(buf_out,buf_in,&mde)); */
+ return(ds_dec(buf_in,len_in,buf_out,len_out,0x4000));
+ default:
+ return(sd3_decomp(buf_in,len_in,buf_out,len_out,0));
+ };
+}
+
+/* Specification:
+ This function reads a stacker cluster into clusterd.
+ It must take care of fragmentation and decompression.
+ In case of failure it must return a negative error code,
+ otherwise it returns number of used bytes in cluster.
+*/
+int stac_read_cluster(struct super_block*sb,unsigned char*clusterd,
+ int clusternr)
+{
+ int sect;
+ int count,val,bytesperclust;
+ struct buffer_head*bh;
+ __u8 * clusterk;
+ Stac_cwalk cw;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ /* Prepare buffers for next read of cluster */
+ if(clusterd==NULL)
+ { if((val=stac_cwalk_init(&cw,sb,clusternr,0))>0)
+ { while((sect=stac_cwalk_sector(&cw))>0)
+ { dblspace_reada(sb,sect,cw.flen+1);
+ cw.flen=0;
+ };
+ };
+ stac_cwalk_done(&cw);
+ return 0;
+ }
+
+ /* Regular start of cluster read */
+
+ val=stac_cwalk_init(&cw,sb,clusternr,2);
+ if (val<0)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: alloc error in cluster %d\n",
+ clusternr);
+ return -EIO;
+ };
+
+ bytesperclust=dblsb->s_sectperclust*SECTOR_SIZE;
+ if(val==0)
+ { memset(clusterd,0,bytesperclust);
+ /* I am not happy, that I cannot consider this as error (printk),
+ but dblspace_getblk must fill rest of cluster and cannot
+ call noread, some cases in dblspace_file_write are problematic too,
+ Pavel */
+ LOG_CLUST("DMSDOS: stac_read_cluster: lost cluster (cluster %d)\n",
+ clusternr);
+ return 0;
+ }
+
+ if(cw.compressed)
+ { clusterk=(unsigned char*)MALLOC(cw.bytes_in_clust);
+ if(clusterk==NULL)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: no memory!\n");
+ stac_cwalk_done(&cw);
+ return -EIO;
+ }
+ }
+ else clusterk=clusterd;
+ count=0;
+
+ while((sect=stac_cwalk_sector(&cw))>0)
+ { bh=raw_bread(sb,sect);
+ if(bh==NULL)
+ { error1:
+ if(cw.compressed) FREE(clusterk);
+ stac_cwalk_done(&cw);
+ return -EIO;
+ }
+ if(count+cw.bytes>cw.bytes_in_clust)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: internal cw error 1 cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ goto error1;
+ };
+ memcpy(clusterk+count,bh->b_data+cw.offset,cw.bytes);
+ count+=cw.bytes;
+ raw_brelse(sb,bh);
+ };
+ if(count!=cw.bytes_in_clust)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: internal cw error 2 cluster=%d\n",
+ clusternr);
+ goto error1;
+ };
+ if(cw.compressed)
+ { count=stac_decompress(clusterk,count,clusterd,bytesperclust);
+ FREE(clusterk);
+ if(!count)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: decompression error cluster=%d\n",
+ clusternr);
+ };
+ };
+ stac_cwalk_done(&cw);
+ if(count<=0) return -EIO;
+ if(bytesperclust-count>0) memset(clusterd+count,0,bytesperclust-count);
+ return (count);
+}
+
+#endif /* DMSDOS_CONFIG_STAC */
diff --git a/src/lib/lib_interface.c b/src/lib/lib_interface.c
new file mode 100644
index 0000000..3ff6796
--- /dev/null
+++ b/src/lib/lib_interface.c
@@ -0,0 +1,726 @@
+/*
+lib_interface.c
+
+DMSDOS library: interface functions, hacks, dummies, and fakes.
+
+******************************************************************************
+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.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include<string.h>
+#include<malloc.h>
+#ifdef USE_FLOCK
+#include<unistd.h>
+#include<sys/file.h>
+#endif
+#ifdef USE_SOPEN
+#include<share.h>
+#endif
+#include<fcntl.h>
+#include<errno.h>
+
+#define fat_boot_sector msdos_boot_sector
+
+/* some interface hacks */
+#include"lib_interface.h"
+#undef MALLOC
+#undef FREE
+#undef CURRENT_TIME
+#undef memcpy
+#undef memset
+#define MALLOC malloc
+#define FREE free
+#define kmalloc(x,y) malloc(x)
+#define kfree free
+#define CURRENT_TIME time(NULL)
+
+#include"dmsdos.h"
+
+#ifndef cpu_to_le16
+/* works only for old kernels and little endian architecture */
+#define cpu_to_le16(v) (v)
+#define cpu_to_le32(v) (v)
+#endif
+
+#define MSDOS_FAT12 4078 /* maximum number of clusters in a 12 bit FAT */
+
+long int blk_size[1][1];
+
+extern Acache mdfat[];
+extern Acache dfat[];
+extern Acache bitfat[];
+
+/* hacks for the low-level interface */
+
+#include <stdarg.h>
+int printk(const char *fmt, ...)
+{ va_list ap;
+ char buf[500];
+ char*p=buf;
+ int i;
+
+ va_start(ap, fmt);
+ i=vsprintf(buf,fmt,ap);
+ va_end(ap);
+
+ if(p[0]=='<'&&p[1]>='0'&&p[1]<='7'&&p[2]=='>')p+=3;
+ if(strncmp(p,"DMSDOS: ",8)==0)p+=8;
+ fprintf(stderr,"libdmsdos: %s",p);
+
+ return i;
+}
+
+void panic(const char *fmt, ...)
+{ va_list ap;
+ char buf[500];
+ int i;
+
+ va_start(ap, fmt);
+ i=vsprintf(buf,fmt,ap);
+ va_end(ap);
+
+ fprintf(stderr,"libdmsdos panic: %s",buf);
+
+ exit(1);
+}
+
+int translate_direct(struct super_block*sb,int block)
+{ int i;
+
+ if(block>=sb->directsize)
+ { printk("DMSDOS: access beyond end of CVF in direct mode (wanted=%d limit=%d)\n",
+ block,sb->directsize-1);
+ return 0;
+ }
+
+ /* calculate physical sector */
+ i=0;
+ do
+ { block-=sb->directlen[i];
+ ++i;
+ }
+ while(block>=0&&i<MAXFRAGMENT);
+ --i;
+ block+=sb->directlen[i]+sb->directlist[i];
+ return block;
+}
+
+struct buffer_head* raw_bread(struct super_block*sb,int block)
+{ struct buffer_head*bh;
+ int fd=sb->s_dev;
+
+ if(sb->directlist)
+ { block=translate_direct(sb,block);
+ if(!block)
+ { printk("raw_bread: translate_direct failed\n");
+ return NULL;
+ }
+ }
+
+ if(lseek(fd,block*512,SEEK_SET)<0)
+ { printk("raw_bread: lseek block %d failed: %s\n",block,strerror(errno));
+ return NULL;
+ }
+ bh=malloc(sizeof(struct buffer_head));
+ if(bh==NULL)
+ { printk("raw_bread: malloc(%d) failed\n",sizeof(struct buffer_head));
+ return NULL;
+ }
+
+ bh->b_data=malloc(512);
+ if(bh->b_data==NULL)
+ { free(bh);
+ printk("raw_bread: malloc(512) failed\n");
+ return NULL;
+ }
+
+ bh->b_blocknr=block;
+
+ if(read(fd,bh->b_data,512)>=0)return bh;
+
+ printk("raw_bread: read failed: %s\n",strerror(errno));
+ free(bh->b_data);
+ free(bh);
+
+ return NULL;
+}
+
+struct buffer_head* raw_getblk(struct super_block*sb,int block)
+{ struct buffer_head*bh;
+ int fd=sb->s_dev;
+
+ if(sb->directlist)
+ { block=translate_direct(sb,block);
+ if(!block)return NULL;
+ }
+
+ if(lseek(fd,block*512,SEEK_SET)<0)
+ { printk("raw_getblk: lseek block %d failed: %s\n",block,strerror(errno));
+ return NULL;
+ }
+ bh=malloc(sizeof(struct buffer_head));
+ if(bh==NULL)return NULL;
+
+ bh->b_data=malloc(512);
+ if(bh->b_data==NULL)
+ { free(bh);
+ return NULL;
+ }
+
+ bh->b_blocknr=block;
+ return bh;
+}
+
+void raw_brelse(struct super_block*sb,struct buffer_head*bh)
+{ if(bh==NULL)return;
+ free(bh->b_data);
+ free(bh);
+}
+
+void raw_set_uptodate(struct super_block*sb,struct buffer_head*bh,int v)
+{ /* dummy */
+}
+
+void raw_mark_buffer_dirty(struct super_block*sb,struct buffer_head*bh,int dirty_val)
+{ int fd=sb->s_dev;
+
+ if(dirty_val==0)return;
+ if(bh==NULL)return;
+
+#ifdef DBL_WRITEACCESS
+
+ if(lseek(fd,bh->b_blocknr*512,SEEK_SET)<0)
+ { printk("can't seek block %ld: %s\n",bh->b_blocknr,strerror(errno));
+ return;
+ }
+
+ if(write(fd,bh->b_data,512)<0)
+ printk("writing block %ld failed: %s\n",bh->b_blocknr,strerror(errno));
+
+#else
+ printk("DMSDOS: write access not compiled in, ignored\n");
+#endif
+}
+
+void dblspace_reada(struct super_block*sb, int sector,int count)
+{ /* dummy */
+}
+
+int try_daemon(struct super_block*sb,int clusternr, int length, int method)
+{ return 0;
+}
+
+int host_fat_lookup(struct super_block *sb,int nr)
+{
+ struct buffer_head *bh,*bh2;
+ unsigned char *p_first,*p_last;
+ int first,last,next,b;
+
+ if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
+ return 0;
+ if (MSDOS_SB(sb)->fat_bits == 16) {
+ first = last = nr*2;
+ } else {
+ first = nr*3/2;
+ last = first+1;
+ }
+ b = MSDOS_SB(sb)->fat_start + (first >> SECTOR_BITS);
+ if (!(bh = raw_bread(sb, b))) {
+ printk("DMSDOS: bread in host_fat_access failed\n");
+ return 0;
+ }
+ if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS)) {
+ bh2 = bh;
+ } else {
+ if (!(bh2 = raw_bread(sb, b+1))) {
+ raw_brelse(sb, bh);
+ printk("DMSDOS: 2nd bread in host_fat_lookup failed\n"); return 0;
+ }
+ }
+ if (MSDOS_SB(sb)->fat_bits == 16) {
+ p_first = p_last = NULL; /* GCC needs that stuff */
+ next = cpu_to_le16(((unsigned short *) bh->b_data)[(first &
+ (SECTOR_SIZE-1)) >> 1]);
+ if (next >= 0xfff7) next = -1;
+ }
+ else {
+ p_first = &((unsigned char *) bh->b_data)[first & (SECTOR_SIZE-1)];
+ p_last = &((unsigned char *) bh2->b_data)[(first+1) &
+ (SECTOR_SIZE-1)];
+ if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
+ else next = (*p_first+(*p_last << 8)) & 0xfff;
+ if (next >= 0xff7) next = -1;
+ }
+
+ raw_brelse(sb, bh);
+ if (bh != bh2)
+ raw_brelse(sb, bh2);
+ return next;
+}
+
+int dos_cluster2sector(struct super_block * sb,int clusternr)
+{ return (clusternr-2)*MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start;
+}
+
+int setup_fragment(struct super_block*sb, int startcluster)
+{ int fragmentzaehler;
+ int clusterzaehler;
+ int akt_cluster;
+ int folge_cluster;
+ int i;
+ unsigned long* directlist;
+ unsigned long* directlen;
+
+ LOG_REST("DMSDOS: setup_fragment\n");
+
+ directlist=malloc(sizeof(unsigned long)*(MAXFRAGMENT+1));
+ if(directlist==NULL)
+ { printk("DMSDOS: out of memory (directlist)\n");
+ return -1;
+ }
+ directlen=malloc(sizeof(unsigned long)*(MAXFRAGMENT+1));
+ if(directlen==NULL)
+ { printk("DMSDOS: out of memory (directlen)\n");
+ free(directlist);
+ return -1;
+ }
+
+ fragmentzaehler=0;
+
+ folge_cluster=startcluster;
+
+ do
+ {
+ clusterzaehler=0;
+ directlist[fragmentzaehler]=folge_cluster;
+ do
+ { akt_cluster=folge_cluster;
+ folge_cluster=host_fat_lookup(sb,akt_cluster);
+ ++clusterzaehler;
+ }
+ while(folge_cluster==akt_cluster+1);
+
+ directlen[fragmentzaehler]=clusterzaehler;
+ LOG_REST("DMSDOS: firstclust=%d anz=%d\n",
+ directlist[fragmentzaehler],
+ directlen[fragmentzaehler]);
+
+ ++fragmentzaehler;
+ }
+ while(folge_cluster>0&&fragmentzaehler<MAXFRAGMENT);
+ if(fragmentzaehler==MAXFRAGMENT&&folge_cluster>0)
+ { /* zu fragmentiert, raus */
+ free(directlist);
+ free(directlen);
+ printk("DMSDOS: CVF too fragmented, not mounted.\n");
+ printk("Increase MAXFRAGMENT in lib_interface.h and recompile.\n");
+ return -1;
+ }
+ printk("DMSDOS: CVF has %d fragment(s)\n",fragmentzaehler);
+
+ /* convert cluster-oriented numbers into sector-oriented ones */
+ for(i=0;i<fragmentzaehler;++i)
+ { /*printk("DMSDOS: umrechnen 1\n");*/
+ directlist[i]=dos_cluster2sector(sb,directlist[i]);
+ /*printk("DMSDOS: umrechnen 2\n");*/
+ directlen[i]*=MSDOS_SB(sb)->cluster_size;
+ /*printk("DMSDOS: umrechnen 3\n");*/
+ }
+
+ /* hang in */
+ sb->directlist=directlist;
+ sb->directlen=directlen;
+
+ return 0;
+}
+
+int setup_translation(struct super_block*sb,char*ext)
+{ int i,j,testvers;
+ struct buffer_head* bh;
+ struct msdos_dir_entry* data;
+ char cvfname[20];
+
+ /* scan the root directory for a CVF */
+
+ for(i=0;i<MSDOS_SB(sb)->dir_entries/MSDOS_DPS;++i)
+ { bh=raw_bread(sb,MSDOS_SB(sb)->dir_start+i);
+ if(bh==NULL)
+ { printk("DMSDOS: unable to read msdos root directory\n");
+ return -1;
+ }
+ data=(struct msdos_dir_entry*) bh->b_data;
+
+ for(j=0;j<MSDOS_DPS;++j)
+ { testvers=0;
+ if(strncmp(data[j].name,"DRVSPACE",8)==0)testvers=1;
+ if(strncmp(data[j].name,"DBLSPACE",8)==0)testvers=1;
+ if(strncmp(data[j].name,"STACVOL ",8)==0)testvers=2;
+ if(testvers)
+ { if( ( data[j].name[8]>='0'&&data[j].name[8]<='9'
+ &&data[j].name[9]>='0'&&data[j].name[9]<='9'
+ &&data[j].name[10]>='0'&&data[j].name[10]<='9'
+ ) | (testvers==2&&strncmp(data[j].name+8,"DSK",3)==0)
+ )
+ { /* it is a CVF */
+ strncpy(cvfname,data[j].name,9-testvers);
+ cvfname[9-testvers]='\0';
+ strcat(cvfname,".");
+ strncat(cvfname,data[j].ext,3);
+ printk("DMSDOS: CVF %s in root directory found.\n",cvfname);
+ if(ext)
+ { if(strncmp(ext,data[j].ext,3)!=0)continue;
+ }
+ if(setup_fragment(sb,data[j].start)==0)
+ { sb->directsize=data[j].size/SECTOR_SIZE;
+ blk_size[0][0]=(data[j].size%1024)?(data[j].size/1024)+1:
+ data[j].size/1024;
+ raw_brelse(sb,bh);
+ printk("DMSDOS: using CVF %s.\n",cvfname);
+ return 0;
+ }
+ }
+ }
+ }
+ raw_brelse(sb,bh);
+ }
+ return -1;
+}
+
+/*okay, first thing is setup super block*/
+/* stolen from fatfs */
+/* Read the super block of an MS-DOS FS. */
+
+struct super_block *read_super(struct super_block *sb,char*ext)
+{
+ struct buffer_head *bh;
+ struct fat_boot_sector *b;
+ int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
+ int debug=0,error,fat=0;
+ int blksize = 512;
+ int i=-1;
+ int mt=0;
+ char cvf_options[101]="bitfaterrs=nocheck";
+
+ MSDOS_SB(sb)->cvf_format=NULL;
+ MSDOS_SB(sb)->private_data=NULL;
+
+ retry:
+ blksize = 512;
+
+ bh = raw_bread(sb, 0);
+ if (bh == NULL) {
+ raw_brelse (sb, bh);
+ sb->s_dev = 0;
+ printk("FAT bread failed\n");
+ return NULL;
+ }
+ b = (struct fat_boot_sector *) bh->b_data;
+/*
+ * The DOS3 partition size limit is *not* 32M as many people think.
+ * Instead, it is 64K sectors (with the usual sector size being
+ * 512 bytes, leading to a 32M limit).
+ *
+ * DOS 3 partition managers got around this problem by faking a
+ * larger sector size, ie treating multiple physical sectors as
+ * a single logical sector.
+ *
+ * We can accommodate this scheme by adjusting our cluster size,
+ * fat_start, and data_start by an appropriate value.
+ *
+ * (by Drew Eckhardt)
+ */
+
+#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
+ /* don't divide by zero */
+
+ logical_sector_size =
+ cpu_to_le16(get_unaligned((unsigned short *) &b->sector_size));
+ sector_mult = logical_sector_size >> SECTOR_BITS;
+ MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult;
+ MSDOS_SB(sb)->fats = b->fats;
+ MSDOS_SB(sb)->fat_start = cpu_to_le16(b->reserved)*sector_mult;
+ MSDOS_SB(sb)->fat_length = cpu_to_le16(b->fat_length)*sector_mult;
+ MSDOS_SB(sb)->dir_start = (cpu_to_le16(b->reserved)+b->fats*cpu_to_le16(
+ b->fat_length))*sector_mult;
+ MSDOS_SB(sb)->dir_entries =
+ cpu_to_le16(get_unaligned((unsigned short *) &b->dir_entries));
+ MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE((
+ MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
+ sector_mult);
+ data_sectors = cpu_to_le16(get_unaligned((unsigned short *) &b->sectors));
+ if (!data_sectors) {
+ data_sectors = cpu_to_le32(b->total_sect);
+ }
+ data_sectors = data_sectors * sector_mult - MSDOS_SB(sb)->data_start;
+ error = !b->cluster_size || !sector_mult;
+ if (!error) {
+ MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
+ b->cluster_size/sector_mult : 0;
+ MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters >
+ MSDOS_FAT12 ? 16 : 12;
+ fat_clusters = MSDOS_SB(sb)->fat_length*SECTOR_SIZE*8/
+ MSDOS_SB(sb)->fat_bits;
+ /* this doesn't compile. I don't understand it either...
+ error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries &
+ (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > fat_clusters+
+ MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
+ || !b->secs_track || !b->heads;
+ */
+ }
+ raw_brelse(sb, bh);
+
+ if(error)goto c_err;
+
+ /*
+ This must be done after the brelse because the bh is a dummy
+ allocated by fat_bread (see buffer.c)
+ */
+ sb->s_blocksize = blksize; /* Using this small block size solves */
+ /* the misfit with buffer cache and cluster */
+ /* because clusters (DOS) are often aligned */
+ /* on odd sectors. */
+ sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
+ i=0;
+
+#ifdef DMSDOS_CONFIG_DBL
+ if(i==0)
+ { i=detect_dblspace(sb);
+ if(i>0){mt++;i=mount_dblspace(sb,cvf_options);}
+ }
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ if(i==0)
+ { i=detect_stacker(sb);
+ if(i>0){mt++;i=mount_stacker(sb,cvf_options);}
+ }
+#endif
+ if(mt==0)
+ { /* looks like a real msdos filesystem */
+ printk("DMSDOS: trying to find CVF inside host MSDOS filesystem...\n");
+ i=setup_translation(sb,ext);
+ ++mt;
+ if(i==0)goto retry;
+ }
+ error=i;
+ c_err:
+ if (error || debug) {
+ /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
+ printk("MS-DOS FS Rel. 12 (hacked for libdmsdos), FAT %d\n",
+ MSDOS_SB(sb)->fat_bits);
+ printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
+ "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size,
+ MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->fat_length,
+ MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
+ MSDOS_SB(sb)->data_start,
+ cpu_to_le16(*(unsigned short *) &b->sectors),
+ (unsigned long)b->total_sect,logical_sector_size);
+ printk ("Transaction block size = %d\n",blksize);
+ }
+ if(!error&&i<0) if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+ MSDOS_SB(sb)->clusters = fat_clusters-2;
+ if (error) {
+ printk("Can't find a valid MSDOS CVF filesystem\n");
+ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
+ MSDOS_SB(sb)->private_data=NULL;
+ return NULL;
+ }
+ sb->s_magic = MSDOS_SUPER_MAGIC;
+ /* set up enough so that it can read an inode */
+ MSDOS_SB(sb)->free_clusters = -1; /* don't know yet */
+ MSDOS_SB(sb)->fat_wait = NULL;
+ MSDOS_SB(sb)->fat_lock = 0;
+ MSDOS_SB(sb)->prev_free = 0;
+ return sb;
+}
+
+void init_daemon(void)
+{ /*dummy*/
+}
+
+void exit_daemon(void)
+{ /*dummy*/
+}
+
+void clear_list_dev(struct super_block*sb)
+{ /*dummy*/
+}
+
+void free_ccache_dev(struct super_block*sb)
+{ /*dummy*/
+}
+
+void remove_from_daemon_list(struct super_block*sb,int clusternr)
+{ /* dummy */
+}
+
+static int _wascalled=0;
+void do_lib_init(void)
+{ int i;
+
+ if(_wascalled)return;
+ _wascalled=1;
+
+ /* first call of DMSDOS library, initialising variables */
+
+ printk("DMSDOS library version %d.%d.%d" DMSDOS_VLT
+ " compiled " __DATE__ " " __TIME__ " with options:"
+#ifndef DBL_WRITEACCESS
+ " read-only"
+#else
+ " read-write"
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ ", doublespace/drivespace(<3)"
+#endif
+#ifdef DMSDOS_CONFIG_DRVSP3
+ ", drivespace 3"
+#endif
+#ifdef DMSDOS_CONFIG_STAC3
+ ", stacker 3"
+#endif
+#ifdef DMSDOS_CONFIG_STAC4
+ ", stacker 4"
+#endif
+ "\n",
+ DMSDOS_MAJOR,DMSDOS_MINOR,DMSDOS_ACT_REL);
+
+ for(i=0;i<MDFATCACHESIZE;++i)
+ { mdfat[i].a_time=0;
+ mdfat[i].a_acc=0;
+ mdfat[i].a_buffer=NULL;
+ }
+ for(i=0;i<DFATCACHESIZE;++i)
+ { dfat[i].a_time=0;
+ dfat[i].a_acc=0;
+ dfat[i].a_buffer=NULL;
+ }
+ for(i=0;i<BITFATCACHESIZE;++i)
+ { bitfat[i].a_time=0;
+ bitfat[i].a_acc=0;
+ bitfat[i].a_buffer=NULL;
+ }
+}
+
+struct super_block* open_cvf(char*filename,int rwflag)
+{ struct super_block *sb;
+ int fd;
+ long int s;
+ char*ext=NULL;
+
+ do_lib_init();
+
+ ext=strrchr(filename,':');
+ if(ext)
+ { if(strlen(ext)==4)
+ { *ext='\0';
+ ++ext;
+ }
+ else
+ ext=NULL;
+ }
+
+ reopen:
+#ifndef USE_SOPEN
+ fd=open(filename,rwflag?O_RDWR:O_RDONLY);
+ if(fd<0)
+ { printk("unable to open CVF read-write: %s\n",strerror(errno));
+ if(rwflag==0)return NULL;
+ printk("trying again in read-only mode\n");
+ rwflag=0;
+ goto reopen;
+ }
+
+#ifdef USE_FLOCK
+ if(rwflag)
+ { if(flock(fd,LOCK_EX|LOCK_NB))
+ { printk("unable to lock CVF exclusively: %s",strerror(errno));
+ printk("trying again in read-only mode\n");
+ rwflag=0;
+ close(fd);
+ goto reopen;
+ }
+ }
+ else
+ { if(flock(fd,LOCK_SH|LOCK_NB))
+ { printk("unable to lock CVF with shared flag: %s",strerror(errno));
+ printk("probably some other process has opened the CVF read-write.\n");
+ close(fd);
+ return NULL;
+ }
+ }
+#endif /* USE_FLOCK */
+#else
+ /* open with win32 locking */
+ fd=sopen(filename,rwflag?O_RDWR:O_RDONLY,rwflag?SH_DENYRW:SH_DENYWR);
+ if(fd<0)
+ { printk("unable to open CVF read-write: %s\n",strerror(errno));
+ if(rwflag==0)return NULL;
+ printk("trying again in read-only mode\n");
+ rwflag=0;
+ goto reopen;
+ }
+#endif
+
+ s=lseek(fd,0,SEEK_END);
+ blk_size[0][0]=(s%1024)?(s/1024)+1:s/1024;
+ sb=malloc(sizeof(struct super_block));
+ if(sb==NULL)
+ { printk("malloc failed\n");
+#ifdef USE_FLOCK
+ flock(fd,LOCK_UN);
+#endif
+ close(fd);
+ return NULL;
+ }
+
+ sb->s_dev=fd;
+ sb->s_flags=0;
+ if(rwflag==0)sb->s_flags|=MS_RDONLY;
+ sb->directlist=NULL;
+ sb->directlen=NULL;
+
+ if(read_super(sb,ext)==NULL)
+ {
+#ifdef USE_FLOCK
+ flock(fd,LOCK_UN);
+#endif
+ close(fd);
+ free(sb);
+ return NULL;
+ }
+
+ return sb;
+}
+
+void close_cvf(struct super_block*sb)
+{ int fd=sb->s_dev;
+
+ unmount_dblspace(sb);
+#ifdef USE_FLOCK
+ flock(fd,LOCK_UN);
+#endif
+ close(fd);
+ if(sb->directlist)free(sb->directlist);
+ if(sb->directlen)free(sb->directlen);
+ free(sb);
+}
diff --git a/src/lib/lib_interface.h b/src/lib/lib_interface.h
new file mode 100644
index 0000000..ed70e1e
--- /dev/null
+++ b/src/lib/lib_interface.h
@@ -0,0 +1,204 @@
+/*
+lib_interface.h
+
+DMSDOS library: headers for interface functions, hacks, dummies, and fakes.
+
+******************************************************************************
+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.
+******************************************************************************
+
+*/
+
+/* These are copied from the kernel include files in order to avoid
+ including those files. They are not 100% identical to the kernel types.
+ Most of them needn't be the same as in the kernel.
+ This has been done for libc6 support.
+*/
+
+/* machine and system dependent hacks */
+
+/* Linux section -- no problems here... :)) */
+//#include<asm/types.h>
+#if 0
+//#ifdef __linux__
+/* this defines machine-dependent __u8, __s8 etc. types */
+#include<asm/types.h>
+/* this defines get_unaligned and put_unaligned */
+#include<asm-generic/unaligned.h>
+//#include<asm/unaligned.h>
+/* this defines cpu_to_le16 etc. in 2.1 kernels - a kind of nop for 2.0 */
+#include<asm/byteorder.h>
+
+/* Other systems usually do not have the asm include files */
+#else
+/* emulate asm/types.h */
+typedef unsigned char __u8;
+typedef signed char __s8;
+typedef unsigned short int __u16;
+typedef signed short int __s16;
+typedef unsigned int __u32;
+typedef signed int __s32;
+/* emulate asm/unaligned.h */
+/* edit these lines if your system cannot do unaligned access */
+#define get_unaligned(ptr) (*(ptr))
+#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) ))
+/* emulate asm/byteorder.h */
+/* edit these lines if your system is non-linux and big endian */
+/* the examples are commented out; they are valid for a little endian cpu */
+/* #define cpu_to_le16(v) (v) */
+/* #define cpu_to_be16(v) ( (((v)&0xff)<<8) | (((v)&0xff00)>>8) ) */
+/* #define cpu_to_le32(v) (v) */
+/* hack: sometimes NULL is missing */
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+#endif
+
+#define C_ST_u16(p,v) { \
+ put_unaligned(v,((__u16*)p)); \
+ p+=2; \
+}
+//inline void c_st_u16(__u8* p, unsigned v) {
+// put_unaligned(v,(__u16*)p);
+//}
+#define C_LD_u16(p,v) { \
+ v=get_unaligned((__u16*)p); \
+ p+=2; \
+}
+
+int printk(const char *fmt, ...);
+void panic(const char * fmt, ...);
+
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+
+#define SECTOR_SIZE 512
+#define SECTOR_BITS 9
+#define MSDOS_DPB (MSDOS_DPS) /* dir entries per block */
+#define MSDOS_DPB_BITS 4 /* log2(MSDOS_DPB) */
+#define MSDOS_DPS (SECTOR_SIZE/sizeof(struct msdos_dir_entry))
+#define MSDOS_DPS_BITS 4 /* log2(MSDOS_DPS) */
+#define MSDOS_DIR_BITS 5 /* log2(sizeof(struct msdos_dir_entry)) */
+
+#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
+
+#define MSDOS_MAX_EXTRA 3 /* tolerate up to that number of clusters which are
+ inaccessible because the FAT is too short */
+
+#define KERN_EMERG "<0>" /* system is unusable */
+#define KERN_ALERT "<1>" /* action must be taken immediately */
+#define KERN_CRIT "<2>" /* critical conditions */
+#define KERN_ERR "<3>" /* error conditions */
+#define KERN_WARNING "<4>" /* warning conditions */
+#define KERN_NOTICE "<5>" /* normal but significant condition */
+#define KERN_INFO "<6>" /* informational */
+#define KERN_DEBUG "<7>" /* debug-level messages */
+
+struct buffer_head {
+ unsigned long b_blocknr; /* block number */
+ char * b_data; /* pointer to data block */
+};
+
+#define MS_RDONLY 1 /* Mount read-only */
+#define MSDOS_SB(s) (&((s)->u.msdos_sb))
+
+struct msdos_dir_entry {
+ __s8 name[8],ext[3]; /* name and extension */
+ __u8 attr; /* attribute bits */
+ __u8 lcase; /* Case for base and extension */
+ __u8 ctime_ms; /* Creation time, milliseconds */
+ __u16 ctime; /* Creation time */
+ __u16 cdate; /* Creation date */
+ __u16 adate; /* Last access date */
+ __u16 starthi; /* High 16 bits of cluster in FAT32 */
+ __u16 time,date,start;/* time, date and first cluster */
+ __u32 size; /* file size (in bytes) */
+};
+
+struct msdos_sb_info {
+ unsigned short cluster_size; /* sectors/cluster */
+ unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */
+ unsigned short fat_start,fat_length; /* FAT start & length (sec.) */
+ unsigned short dir_start,dir_entries; /* root dir start & entries */
+ unsigned short data_start; /* first data sector */
+ unsigned long clusters; /* number of clusters */
+ unsigned long root_cluster; /* first cluster of the root directory */
+ unsigned long fsinfo_offset; /* FAT32 fsinfo offset from start of disk */
+ void *fat_wait;
+ int fat_lock;
+ int prev_free; /* previously returned free cluster number */
+ int free_clusters; /* -1 if undefined */
+ /*struct fat_mount_options options;*/
+ struct nls_table *nls_disk; /* Codepage used on disk */
+ struct nls_table *nls_io; /* Charset used for input and display */
+ struct cvf_format* cvf_format;
+ void* private_data;
+};
+
+struct super_block {
+ int s_dev;
+ unsigned long s_blocksize;
+ unsigned char s_blocksize_bits;
+ unsigned long s_flags;
+ unsigned long s_magic;
+ unsigned long* directlist;
+ unsigned long* directlen;
+ unsigned long directsize;
+ union {
+ struct msdos_sb_info msdos_sb;
+ } u;
+
+};
+
+struct fat_boot_sector {
+ __s8 ignored[3]; /* Boot strap short or near jump */
+ __s8 system_id[8]; /* Name - can be used to special case
+ partition manager volumes */
+ __u8 sector_size[2]; /* bytes per logical sector */
+ __u8 cluster_size; /* sectors/cluster */
+ __u16 reserved; /* reserved sectors */
+ __u8 fats; /* number of FATs */
+ __u8 dir_entries[2]; /* root directory entries */
+ __u8 sectors[2]; /* number of sectors */
+ __u8 media; /* media code (unused) */
+ __u16 fat_length; /* sectors/FAT */
+ __u16 secs_track; /* sectors per track */
+ __u16 heads; /* number of heads */
+ __u32 hidden; /* hidden sectors (unused) */
+ __u32 total_sect; /* number of sectors (if sectors == 0) */
+ /* The following fields are only used by FAT32 */
+ __u32 fat32_length; /* sectors/FAT */
+ __u16 flags; /* bit 8: fat mirroring, low 4: active fat */
+ __u8 version[2]; /* major, minor filesystem version */
+ __u32 root_cluster; /* first cluster in root directory */
+ __u16 info_sector; /* filesystem info sector */
+ __u16 backup_boot; /* backup boot sector */
+ __u16 reserved2[6]; /* Unused */
+};
+
+#define MALLOC malloc
+#define FREE free
+#define kmalloc(x,y) malloc(x)
+#define kfree free
+#define CURRENT_TIME time(NULL)
+#define vmalloc malloc
+#define vfree free
+
+#define MAXFRAGMENT 300
diff --git a/src/utils/Makefile b/src/utils/Makefile
new file mode 100644
index 0000000..89d3d1b
--- /dev/null
+++ b/src/utils/Makefile
@@ -0,0 +1,22 @@
+CFLAGS := -c -Wall -D__DMSDOS_LIB__
+LDFLAGS :=
+SOURCES := mcdmsdos.c
+BUILD_DIR := ./build
+MAINSRC := mcdmsdos.c
+LIBSRC := $(shell find ./lib/ -name '*.c')
+SRC := $(MAINSRC) $(LIBSRC)
+OBJS := $(SRC:%=$(BUILD_DIR)/%.o)
+
+EXECUTABLE := mcdmsdosnext
+
+all: $(SOURCES) $(EXECUTABLE)
+
+$(EXECUTABLE): $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) -o $(BUILD_DIR)/$@
+
+$(BUILD_DIR)/%.c.o: %.c
+ mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -c $< -o $@
+
+clean:
+ rm -r $(BUILD_DIR)
diff --git a/src/utils/mcdmsdos.c b/src/utils/mcdmsdos.c
new file mode 100644
index 0000000..b2cb5d6
--- /dev/null
+++ b/src/utils/mcdmsdos.c
@@ -0,0 +1,409 @@
+/*
+
+mcdmsdos.c
+
+DMSDOS: external filesystem interface for Midnight Commander
+
+******************************************************************************
+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.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include<ctype.h>
+
+#include"lib/dmsdos.h"
+#include"lib/lib_interface.h"
+
+#define M_LIST 1
+#define M_OUT 2
+#define M_IN 3
+
+/*this is not good - but currently we have only one CVF open at a time*/
+struct super_block*sb;
+Dblsb*dblsb;
+
+int scan(char*text)
+{ int v=0;
+ if(strncmp(text,"0x",2)==0||strncmp(text,"0X",2)==0)
+ sscanf(text+2,"%x",&v);
+ else
+ sscanf(text,"%d",&v);
+ return v;
+}
+
+unsigned char* get_root_dir(void)
+{ unsigned char* data;
+ struct buffer_head*bh;
+ int i;
+
+ data=malloc(dblsb->s_rootdirentries*32);
+ if(data==NULL)return NULL;
+
+ for(i=0;i<dblsb->s_rootdirentries*32/512;++i)
+ { bh=raw_bread(sb,dblsb->s_rootdir+i);
+ if(bh==NULL){free(data);return NULL;}
+ memcpy(data+i*512,bh->b_data,512);
+ raw_brelse(sb,bh);
+ }
+ return data;
+}
+
+int copy_cluster_out(int nr, int len, FILE*f)
+{ unsigned char*data;
+ int i,j;
+
+ if(nr==0)
+ { data=get_root_dir();
+ if(data==NULL)return -1;
+ i=dblsb->s_rootdirentries*32;
+ }
+ else
+ { data=malloc(dblsb->s_sectperclust*512);
+ if(data==NULL)return -1;
+ i=dmsdos_read_cluster(sb,data,nr);
+ if(i<0){free(data);return -1;}
+ }
+ if(len<=0||len>i)len=i;
+
+ for(j=0;j<len;++j)fputc(data[j],f);
+
+ free(data);
+ return ferror(f);
+}
+
+int handle_dir_chain(int start,int rek,char*prefix);
+
+int display_dir_cluster(int nr, int rek, char*prefix)
+{ unsigned char*data;
+ int i,j;
+
+ /*printf("display_dir_cluster called with nr=%d rek=%d prefix=%s\n",
+ nr,rek,prefix);*/
+
+ if(nr==0)
+ { data=get_root_dir();
+ if(data==NULL)return -1;
+ i=dblsb->s_rootdirentries*32;
+ }
+ else
+ { data=malloc(dblsb->s_sectperclust*512);
+ if(data==NULL)return -1;
+ i=dmsdos_read_cluster(sb,data,nr);
+ if(i<0){free(data);return -1;}
+ }
+
+ for(j=0;j<dblsb->s_sectperclust*512;j+=32)
+ { unsigned char*pp;
+ unsigned int x;
+ char filename[15]="";
+ int nstart;
+ long size;
+ char datestr[16][4]={"?00","Jan","Feb","Mar","Apr","May","Jun",
+ "Jul","Aug","Sep","Oct","Nov","Dec",
+ "?13","?14","?15"};
+ if(data[j]==0)break;
+ if(data[j]==0xe5)continue;
+ if(data[j+11]&8)continue;
+ if(data[j]=='.')continue;
+
+ for(i=0;i<11;++i)
+ { if(i==8&&data[j+i]!=' ')strcat(filename,".");
+ if(data[j+i]!=' ')strncat(filename,&(data[j+i]),1);
+ }
+ for(i=0;i<strlen(filename);++i)filename[i]=tolower(filename[i]);
+
+ if(data[j+11]&16)printf("dr");else printf("-r");
+ if(data[j+11]&1)printf("-");else printf("w");
+ printf("xr-xr-x 1 0 0"); /* bogus values :) */
+
+ /*
+ printf(" ");
+ if(data[j+11]&1)printf("R");else printf(" ");
+ if(data[j+11]&2)printf("H");else printf(" ");
+ if(data[j+11]&4)printf("S");else printf(" ");
+ if(data[j+11]&8)printf("V");else printf(" ");
+ if(data[j+11]&16)printf("D");else printf(" ");
+ if(data[j+11]&32)printf("A");else printf(" ");
+ if(data[j+11]&64)printf("?");else printf(" ");
+ if(data[j+11]&128)printf("?");else printf(" ");
+ */
+
+ pp=&(data[j+28]);
+ size=CHL(pp);
+ printf(" %7lu",size);
+
+ pp=&(data[j+24]);
+ x=CHS(pp);
+ /*printf(" %02d.%02d.%02d",x&31,(x>>5)&15,(x>>9)+80);*/
+ printf(" %s",datestr[(x>>5)&15]);
+ printf(" %02d",x&31);
+ printf(" %04d",(x>>9)+1980); /* y2k compliant :) */
+
+ pp=&(data[j+22]);
+ x=CHS(pp);
+ /*printf(" %02d:%02d:%02d",x>>11,(x>>5)&63,(x&31)<<1);*/
+ printf(" %02d:%02d",x>>11,(x>>5)&63);
+
+ pp=&(data[j+26]);
+ nstart=CHS(pp);
+ /*printf(" %5d",nstart);*/
+
+ printf(" %s%s\n",prefix,filename);
+
+ if((data[j+11]&16)!=0&&rek!=0&&filename[0]!='.')
+ { char *nprefix;
+ nprefix=malloc(strlen(prefix)+20);
+ if(nprefix==NULL)
+ { fprintf(stderr,"out of memory\n");
+ exit(3);
+ }
+ strcpy(nprefix,prefix);
+ strcat(nprefix,filename);
+ strcat(nprefix,"/");
+ handle_dir_chain(nstart,rek,nprefix);
+ free(nprefix);
+ }
+
+ }
+
+ free(data);
+ return 0;
+}
+
+int handle_dir_chain(int start,int rek,char*prefix)
+{ int i,next;
+
+ if(start==0)return display_dir_cluster(0,rek,prefix);
+ if(start==1||start<0||start>dblsb->s_max_cluster)return -1;
+
+ do
+ {
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ i=display_dir_cluster(start,rek,prefix);
+ if(i<0)return i;
+ start=next;
+ }
+ while(next>1&&next<=dblsb->s_max_cluster);
+
+ if(next>=0)
+ { return -1;
+ }
+
+ return 0;
+}
+
+int handle_file_chain(int start, int len, FILE*f)
+{ int i,next;
+
+ if(start==0)return -1; /* never a file :) */
+ if(start==1||start<0||start>dblsb->s_max_cluster)return -1;
+
+ do
+ {
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ i=copy_cluster_out(start,len,f);
+ if(i<0)return i;
+ len-=dblsb->s_sectperclust*512;
+ if(len<=0)break;
+ start=next;
+ }
+ while(next>1&&next<=dblsb->s_max_cluster);
+
+ if(next>=0)
+ { return -1;
+ }
+
+ return 0;
+}
+
+int scan_dir(char*entry,int start,int*len)
+{ char buf[]=" ";
+ /*12345678EXT*/
+ int i;
+ int size;
+ unsigned char*data;
+ int next;
+
+ if(strcmp(entry,".")==0)return start;
+ else if(strcmp(entry,"..")==0)strncpy(buf,"..",2);
+ else if(*entry=='.') return -1;
+ else
+ for(i=0;i<11;++i)
+ { if(*entry=='.'&&i<=7){i=7;++entry;continue;}
+ if(*entry=='.'&&i==8){i=7;++entry;continue;}
+ if(*entry=='.')break;
+ if(*entry=='\0')break;
+ buf[i]=toupper(*entry);
+ ++entry;
+ }
+
+ do
+ {
+ /*printf("scan_dir: searching for %s in %d\n",buf,start);*/
+
+ if(start==0)
+ { data=get_root_dir();
+ size=dblsb->s_rootdirentries;
+ next=-1;
+ }
+ else
+ { data=malloc(dblsb->s_sectperclust*512);
+ if(data!=NULL)
+ { i=dmsdos_read_cluster(sb,data,start);
+ if(i<0){free(data);data=NULL;}
+ size=i/32;
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ if(next==0)
+ fprintf(stderr,"warning: cluster %d is marked as unused in FAT\n",
+ next);
+ }
+ }
+ if(data==NULL)return -1;
+
+ for(i=0;i<size;++i)
+ { if(strncmp(&(data[i*32]),buf,11)==0)
+ { unsigned char*pp;
+ int cluster;
+
+ pp=&(data[i*32+26]);
+ cluster=CHS(pp);
+ pp=&(data[i*32+28]);
+ if(len)*len=CHL(pp);
+ free(data);
+ return cluster;
+ }
+ }
+
+ free(data);
+ start=next;
+ }
+ while(next>0&&next<=dblsb->s_max_cluster);
+ return -1;
+}
+
+int scan_path(char*path,int start, int*len)
+{ int i;
+ char*p;
+
+ for(p=strtok(path,"/");p;p=strtok(NULL,"/"))
+ { i=scan_dir(p,start,len);
+ if(i<0)
+ { fprintf(stderr,"path component %s not found\n",p);
+ return -1;
+ }
+ start=i;
+ }
+
+ return start;
+}
+
+int main(int argc, char*argv[])
+{ int mode=0;
+ int cluster;
+ int i;
+ char*p;
+ int len;
+ FILE*f;
+
+ fprintf(stderr,"mcdmsdos version 0.2.0 (for libdmsdos 0.9.x and newer)\n");
+ if(argc<2)
+ { fprintf(stderr,"\nusage: mcdmsdos <mc-extfs-command> ...\n");
+ fprintf(stderr,"where <mc-extfs-command> can be:\n");
+ fprintf(stderr," list <CVF>\n");
+ fprintf(stderr," copyout <CVF> <path/name_in_CVF> <outfile>\n");
+ fprintf(stderr," copyin <CVF> <path/name_in_CVF> <infile> [*]\n");
+ fprintf(stderr," [*] currently not implemented\n");
+ return 1;
+ }
+
+ /* check syntax */
+ if(strcmp(argv[1],"list")==0)
+ { mode=M_LIST;
+ if(argc!=3)
+ { fprintf(stderr,"wrong number of arguments\n");
+ return 1;
+ }
+ }
+ else if(strcmp(argv[1],"copyout")==0)
+ { mode=M_OUT;
+ if(argc!=5)
+ { fprintf(stderr,"wrong number of arguments\n");
+ return 1;
+ }
+ }
+ else if(strcmp(argv[1],"copyin")==0)
+ { mode=M_IN;
+ if(argc!=5)
+ { fprintf(stderr,"wrong number of arguments\n");
+ return 1;
+ }
+ fprintf(stderr,"copyin command is not implemented\n");
+ return -2;
+ }
+ else
+ { fprintf(stderr,"unknown command\n");
+ return -1;
+ }
+
+ sb=open_cvf(argv[2],0/*read-only*/);
+ if(sb==NULL)
+ { printf("open CVF %s failed\n",argv[1]);
+ return 2;
+ }
+ dblsb=MSDOS_SB(sb)->private_data;
+
+
+ if(mode==M_LIST)
+ { i=handle_dir_chain(0,1,"");
+ }
+
+ else if(mode==M_OUT)
+ { p=malloc(strlen(argv[3])+1);
+ strcpy(p,argv[3]);
+#ifdef _WIN32
+ /* convert to Unix style path */
+ for(i=0;i<strlen(p);++i){if(p[i]=='\\')p[i]='/';}
+#endif
+ if(*p=='/')++p;
+ cluster=scan_path(p,0,&len);
+ if(cluster<0)
+ { fprintf(stderr,"%s not found\n",argv[3]);
+ return 1;
+ }
+
+ f=fopen(argv[4],"wb");
+ if(f==NULL)
+ { perror("open write failed");
+ i=-1;
+ }
+ else
+ { i=handle_file_chain(cluster,len,f);
+ fclose(f);
+ }
+ }
+
+ close_cvf(sb);
+
+ return i;
+}