diff options
author | cc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60> | 2023-05-12 08:39:37 +0000 |
---|---|---|
committer | cc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60> | 2023-07-19 21:05:39 +0000 |
commit | 177f9cba3e9813cf82ea764799a12128f65572cf (patch) | |
tree | 553e4086a588d18f0e27d5d607a34d5b9bcd0e04 /src | |
parent | 5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff) | |
download | dmsdosnow-177f9cba3e9813cf82ea764799a12128f65572cf.tar.gz |
Build mcdmsdos
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/dblspace_alloc.c | 711 | ||||
-rw-r--r-- | src/lib/dblspace_chk.c | 559 | ||||
-rw-r--r-- | src/lib/dblspace_compr.c | 711 | ||||
-rw-r--r-- | src/lib/dblspace_dec.c | 671 | ||||
-rw-r--r-- | src/lib/dblspace_interface.c | 1147 | ||||
-rw-r--r-- | src/lib/dblspace_methsq.c | 1256 | ||||
-rw-r--r-- | src/lib/dblspace_tables.c | 760 | ||||
-rw-r--r-- | src/lib/dmsdos-config.h | 24 | ||||
-rw-r--r-- | src/lib/dmsdos.h | 716 | ||||
-rw-r--r-- | src/lib/dstacker_alloc.c | 477 | ||||
-rw-r--r-- | src/lib/dstacker_compr.c | 1235 | ||||
-rw-r--r-- | src/lib/dstacker_dec.c | 824 | ||||
-rw-r--r-- | src/lib/lib_interface.c | 726 | ||||
-rw-r--r-- | src/lib/lib_interface.h | 204 | ||||
-rw-r--r-- | src/utils/Makefile | 22 | ||||
-rw-r--r-- | src/utils/mcdmsdos.c | 409 |
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&§ornr<=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&§or<=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; +} |